diff --git a/README.md b/README.md index a01bcb96..0a2551a2 100644 --- a/README.md +++ b/README.md @@ -247,6 +247,17 @@ For commands that may have side effects, preview the request with --dry-run firs lark-cli im +messages-send --chat-id oc_xxx --text "hello" --dry-run ``` +### Shell Completion + +`lark-cli` ships a working `completion` subcommand, but it is intentionally hidden from the default help output because the CLI is primarily optimized for agent workflows. + +```bash +lark-cli completion zsh +lark-cli completion bash +lark-cli completion fish +lark-cli completion powershell +``` + ### Schema Introspection Use schema to inspect any API method's parameters, request body, response structure, supported identities, and scopes: diff --git a/shortcuts/base/base_execute_test.go b/shortcuts/base/base_execute_test.go index 46ec996d..78bc1fab 100644 --- a/shortcuts/base/base_execute_test.go +++ b/shortcuts/base/base_execute_test.go @@ -907,6 +907,8 @@ func TestBaseFieldExecuteSearchOptions(t *testing.T) { } if got := stdout.String(); !strings.Contains(got, `"options"`) || !strings.Contains(got, `"已完成"`) { t.Fatalf("stdout=%s", got) + } else if strings.Contains(got, `"opt_1"`) { + t.Fatalf("stdout should not expose option ids: %s", got) } } diff --git a/shortcuts/base/field_ops.go b/shortcuts/base/field_ops.go index 0976c90d..dabd72f6 100644 --- a/shortcuts/base/field_ops.go +++ b/shortcuts/base/field_ops.go @@ -215,8 +215,31 @@ func executeFieldSearchOptions(runtime *common.RuntimeContext) error { "field_id": fieldRef, "field_name": fieldRef, "keyword": strings.TrimSpace(runtime.Str("keyword")), - "options": options, + "options": stripFieldOptionIDs(options), "total": total, }, nil) return nil } + +func stripFieldOptionIDs(options []interface{}) []interface{} { + if len(options) == 0 { + return options + } + normalized := make([]interface{}, 0, len(options)) + for _, option := range options { + record, ok := option.(map[string]interface{}) + if !ok { + normalized = append(normalized, option) + continue + } + copied := make(map[string]interface{}, len(record)) + for key, value := range record { + if key == "id" { + continue + } + copied[key] = value + } + normalized = append(normalized, copied) + } + return normalized +} diff --git a/skills/lark-im/SKILL.md b/skills/lark-im/SKILL.md index d5b182ef..96a82f62 100644 --- a/skills/lark-im/SKILL.md +++ b/skills/lark-im/SKILL.md @@ -34,7 +34,7 @@ Chat (oc_xxx) ### Identity and Token Mapping -- `--as user` means **user identity** and uses `user_access_token`. Calls run as the authorized end user, so permissions depend on both the app scopes and that user's own access to the target chat/message/resource. +- `--as user` means **user identity** and uses `user_access_token`. Calls run as the authorized end user, so permissions depend on both the app scopes and that user's own access to the target chat/message/resource. For direct messages to users, the app also still needs to be visible to the target user; user token auth does not bypass app visibility or availability settings. - `--as bot` means **bot identity** and uses `tenant_access_token`. Calls run as the app bot, so behavior depends on the bot's membership, app visibility, availability range, and bot-specific scopes. - If an IM API says it supports both `user` and `bot`, the token type changes who the operator is. The same API can succeed with one identity and fail with the other because owner/admin status, chat membership, tenant boundary, or app availability are checked against the current caller. diff --git a/skills/lark-im/references/lark-im-messages-send.md b/skills/lark-im/references/lark-im-messages-send.md index d9a57816..f50062e8 100644 --- a/skills/lark-im/references/lark-im-messages-send.md +++ b/skills/lark-im/references/lark-im-messages-send.md @@ -20,6 +20,8 @@ When using `--as bot`, the message is sent in the app's name, so make sure the a When using `--as user`, the message is sent as the authorized end user and requires the `im:message.send_as_user` and `im:message` scopes. +For direct messages to users, `--as user` still does **not** bypass app visibility. If the target user is outside the app's visible or available range, the send can still fail even though the operator already completed `auth login`. + ## Choose The Right Content Flag | Need | Recommended flag | Why | @@ -175,6 +177,7 @@ lark-cli im +messages-send --chat-id oc_xxx --markdown $'## Test\n\nhello' --dry - Using `--content` without making the JSON match the effective `--msg-type`. - Explicitly setting `--msg-type` to something that conflicts with `--text`, `--markdown`, or media flags. - Mixing `--text`, `--markdown`, or `--content` with media flags in one command. +- Assuming `--as user` can message any target user once the sender has logged in. The app still needs to be visible to the recipient, or Feishu will reject the send with visibility / authorization errors. ## `content` Format Reference @@ -218,6 +221,7 @@ lark-cli im +messages-send --chat-id oc_xxx --markdown $'## Test\n\nhello' --dry - When using `--video`, `--video-cover` is required as the video cover - `--dry-run` uses placeholder image keys for remote Markdown images and placeholder media keys for local uploads - Failures return an error code and message +- For `--as user` direct messages, make sure the app's visible range includes the target user. A logged-in sender token alone is not enough when the app is not visible to that recipient. - `--as user` uses a user access token (UAT) and requires the `im:message.send_as_user` and `im:message` scopes; the message is sent as the authorized end user - `--as bot` uses a tenant access token (TAT) and requires the `im:message:send_as_bot` scope - When sending as a bot, the app must already be in the target group or already have a direct-message relationship with the target user