Feature expansion: raw API passthrough + 10 new command groups, full CRUD, WebSocket events - #22
Open
seahyc wants to merge 90 commits into
Open
Feature expansion: raw API passthrough + 10 new command groups, full CRUD, WebSocket events#22seahyc wants to merge 90 commits into
seahyc wants to merge 90 commits into
Conversation
- lark sheet style: apply bold formatting to a cell range - lark sheet resize: set pixel widths per column (JSON map or --all/--cols) - lark sheet add-tab: add a new tab to an existing spreadsheet Also refactors the repeated first-sheet resolution logic into a shared resolveSheetID helper, used by read, write, style, and resize commands. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds task list, create, complete, and delete commands for Lark Tasks v2 API. Includes tasks scope group and skill documentation.
… flags - sheets: add write, create, add-tab, style, resize command docs - documents: remove duplicate sheets section, add doc image command - calendar: add missing --no-notify flag to cal update - contacts: add missing --page-size flag to list-dept
Add `lark doc upload` for uploading files to Lark Drive via POST /drive/v1/files/upload_all (multipart, 20MB limit). Add drive:drive write scope to documents scope group.
- sheet write --auto-type: auto-convert date strings (YYYY-MM-DD) to Excel serial numbers and numeric strings to numbers - sheet style --format: apply number/date format to cell ranges (e.g., "yyyy-MM-dd", "#,##0", "0.00%") - doc info: get file/folder metadata (owner, timestamps, URL) - doc mkdir: create folders in Lark Drive - doc list: now includes created_time, modified_time, owner_id - Add development workflow to CLAUDE.md - Update skills for all new capabilities
# Conflicts: # internal/cmd/sheet.go # skills/sheets/SKILL.md
- Add --link flag to doc append and doc update for creating hyperlinks - Add doc trash command to move files to Drive trash - Add doc delete command for removing blocks by ID - Add doc update command for modifying existing block content - Add DeleteDriveFile and DeleteWithBody API methods - Update documents skill with new commands and auth docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Lark PATCH block API has strict format requirements and may return "invalid param" errors. Document the delete + append workaround. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add BaseResponse.Err() method, replacing 61 inline error checks across 14 API files - Add PaginateWith[T] generic helper, replacing 8 pagination loops - Add ClampPageSize helper, replacing 7 page size clamping patterns - Add config.LoadTimezone() helper, replacing 4 duplicate timezone loading blocks - Extract buildBlocks/setHeadingField in doc.go, deduplicating append/update commands Net reduction of ~140 lines while improving consistency. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…delete - Add `doc outline` command showing heading structure with block IDs - Add `doc move` command to relocate blocks within a document - Add `--after <block-id>` flag to `doc append` for stable positioning - Fix `doc update` for all block types (use update_text_elements field) - Fix `--index 0` not prepending (remove omitempty from JSON tag) - Fix bulk delete failing when deleting parent+child blocks together Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…trol Default render mode is 'value' (computed values via valueRenderOption=ToString). Options: value (default), formula (raw =formulas), formatted (display strings).
…ills Add Lark API error code table (99991663, 99991664, 99991400, etc.) with exact remediation steps. Agent now tells users who needs to act and what to do instead of fumbling with workarounds like CSV export or copy-paste.
Agent now checks auth status first to distinguish tool-missing vs not-authenticated vs no-file-access before attempting any command.
- Add `lark msg edit` command (PUT /im/v1/messages/:id) for in-place
message editing instead of recall+resend
- Replace fragile strconv.Unquote in unescapeString with manual
replacement for \n, \t, \r, \\ - the old approach silently failed
on the entire string if any unrecognized escape was present (e.g.
fish shell's \!)
- Strip fish shell's \! escape injection
- Auto-upgrade --msg-type text to post when mentions (@{), bold (**)
or links ([) are detected, preventing broken formatting
- Add PatchWithTenantToken and PutWithTenantToken to API client
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Table creation now automatically fills cells with header and row content via follow-up API calls, instead of requiring manual per-cell append commands. Includes rate limiting (200ms delay) and retry logic for API reliability.
- Table --table-header/--table-row now auto-fills cell content after creation instead of leaving cells empty - Add move and outline commands to SKILL.md - Add --after flag, callout/table-cell block types, and markdown/table entries to "Which Command to Use" table
- Change GetUser from tenant_access_token to user_access_token (fixes API error 41050 "no user authority error") - Add Name field to OutputMessageSender - Resolve sender names via GetUser in msg history with deduplication cache (one API call per unique sender, not per message)
Adds --file flag to lark msg send for sending file messages (PDF, PPTX, DOCX, XLSX, MP4, etc.) via the Lark IM file upload API (/im/v1/files). File type is inferred from extension; falls back to stream for unknown types. Multiple files send as separate messages. Cannot be combined with --text or --image. Updates skill docs with usage and 30MB size limit.
…ttedValue sheet write: auto-calculate end range from values dimensions instead of bare "A1" which Lark v2 API rejects. Convert bool values to 1/0 since Lark API doesn't accept bool cell type. sheet read: default render option to UnformattedValue for raw numeric values instead of ToString which loses precision. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Try common types (docx, sheet, bitable, folder, file, doc, mindnote, slides, wiki) in order instead of defaulting to "file" which fails for most document tokens. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add --include-bodies flag to sync command for fetching full email bodies during sync. Improve cache schema and search to support body content. Update email skill docs with new capabilities. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- chat dm scans recent messages to find the P2P chat_id with the target user, not just the open_id (Lark's API has no direct user→P2P lookup endpoint) - chat list-dms enumerates recent DMs by scanning message history, filtering on is_p2p_chat=true and deduping by chat_id (Lark's /im/v1/chats endpoint only returns group chats, never P2P) - Workaround for Lark's missing P2P chat list/lookup APIs
Replace the long-polling stub in SubscribeEvents with a real implementation that speaks Lark's long-connection event protocol (pbbp2 binary frames over wss://). The endpoint URL is fetched via POST /callback/ws/endpoint using the configured AppID/AppSecret; heartbeats, multi-frame message reassembly, and exponential-backoff reconnect (max 60s) are handled inline. The Frame/Header protobuf types are vendored from github.com/larksuite/oapi-sdk-go/v3/ws/pbbp2.pb.go so no runtime dependency on the full SDK is required — only gorilla/websocket and gogo/protobuf. Events are emitted as-is (one JSON object per line). Filters (--type, --match) are applied to the raw payload; --compact remains accepted for back-compat but is a no-op since frames already arrive compact.
New top-level 'lark approval' command wraps the /approval/v4/ API: - list: query the current user's pending approval tasks - get <instance-code>: fetch full instance detail (form, timeline, tasks) - approve|reject <task-id>: act on a pending task (with optional --comment) - transfer <task-id> --to <user>: reassign a task to another approver - cancel <instance-code>: cancel an instance the user started - cc <instance-code> --to <user,user>: cc additional users Adds 'approval' scope group (approval:approval, approval:instance:write, approval:task:write) and registers the command in root.go. Includes the skills/lark-approval/SKILL.md guide.
New top-level 'lark slides' command wraps the /slides/v1/ API: - create: new empty presentation with optional --folder - get <id>: fetch XML presentation metadata - slide create <id>: append a slide via --xml or --xml-file (supports stdin) - slide delete <id> --slide-id: remove a slide - media upload <file>: upload media to Drive, returns file_token for referencing in slide XML Adds 'slides' scope group covering slides:presentation:* and docs:document.media:upload + drive:drive for uploads. Registers the command in root.go. Includes skills/lark-slides/SKILL.md guide.
New persistent --format flag on the root command, validated in PersistentPreRun. output.JSON() switches on output.Format at emit time so every subcommand picks it up automatically. Formats: - pretty (default) / json: indented JSON - ndjson: compact one-line JSON (good for streaming and jq pipelines) - table: ASCII table; detects array-of-objects, or a single object containing exactly one array-valued field, and renders that as rows - csv: same detection as table, emits RFC 4180 CSV Falls back to pretty JSON when the data shape can't be tabulated. Errors always render as pretty JSON regardless of --format so tooling gets a stable error envelope.
Introspects any Lark API path or dotted name by fetching the relevant llms-docs file from open.larksuite.com and printing the matching section(s). - 'lark schema <path-or-name>' — fetch + grep + print section - 'lark schema modules' — list supported modules Module map covers messaging, docs, calendar, meetings, approval, attendance, tasks, mail, bot, bitable, contacts, feed, and group-chat. Section matching splits on '##' headings so callers get the full reference block for the matched endpoint.
New 'lark attendance list' fetches user_task records from /attendance/v1/user_tasks/query. Defaults to the caller (resolved via /authen/v1/user_info) and the last 7 days; --user, --after, --before, --overtime cover targeted lookups. Adds the 'attendance' scope group (attendance:task[:readonly]) and registers the command in root. Includes a skill file with usage and error-handling reference.
Extracts three pure helpers to make them testable and covers them with 25 table-driven tests: - api.NormalizeAPIPath (new): handles /open-apis prefix collapse, relative path append, and absolute URL passthrough. raw.go now delegates to it, so the doubled-prefix regression is caught by a test. - cmd.classifyMemberIdent (new): shape check extracted from resolveMembers (ou_* vs @ vs name). - cmd.parseTimeArgE (new): errorable twin of parseTimeArg that covers unix, RFC3339, ISO-without-tz, date-only, and invalid inputs. Also tests api.InferModule / searchNeedles / extractSections for the schema command, and scopes.GetScopesForGroups / ParseGroups / GetGroupForCommand / AllGroupNames.
Lark's docs sometimes list approval:instance:read / approval:task:read instead of approval:approval[:readonly]. Add a pointer so the next caller who sees SCOPE_ERROR knows where to adjust scopes.go.
One command does resolve person -> fetch recent history -> optional reply. Adds --compact mode that returns a parsed plain-text transcript with role (me/them), sender name, and message text, optimized for agent consumption.
findP2PChatIDForUser matches the first P2P chat where FromID equals the target open_id. When the target is the authenticated user, every DM they sent matches, so the command would route the reply into an arbitrary recent counterpart's chat. Fail fast with VALIDATION_ERROR instead.
Four ergonomic gaps that broke bulk-reorder of a Lark doc, all from one real-world failure (reordering ~30 blocks across a single doc): 1. doc move now returns the NEW block_id, not the input. Lark's "move" is delete+recreate so the moved block always gets a fresh id; chaining `doc move ... --after $just-moved` always failed before because the saved id was stale. OutputDocumentMove gains `old_block_id` for callers that still want the original. 2. doc move now preserves table content. Previously the block-clone path copied a fixed list of leaf field pointers (Heading1..9, Bullet, Code, Quote, Image, etc.) and silently dropped Table + Children, so moving a table re-inserted an empty same-typed block with zero rows. Now the move detects table source blocks, extracts each cell's text before delete, and re-populates the cells of the freshly-created table after insert. Same path used for Callout. 3. New `doc move-range <doc> <start-block-id> <end-block-id> --after <target>` for bulk reordering of a contiguous slice of root children. Internally moves blocks one-by-one re-anchoring on each freshly-returned id, so a multi-block move is one command instead of N+ stale-id error chains. 4. doc append --markdown now auto-chunks markdown tables larger than 6 rows into multiple consecutive tables (each repeating the header). Appending a 14-row table previously failed with an opaque `invalid param` from Lark's API; the chunked output renders indistinguishably to the reader and works reliably. Cross-cutting: the HTTP client now retries Lark's rate-limit error (error_code 99991663 / `99991400` / HTTP 429) on POST/PUT/PATCH/DELETE with exponential backoff (1s, 2s, 4s, 8s, 16s; 5 attempts). Every existing write call benefits — large doc rebuilds no longer trip the rate limiter. Verified end-to-end against the live Lark API on a throwaway doc: all four bugs reproduce-and-fix demonstrated, plus a 20-call rapid loop completes with zero failures.
- Add contact:user.email:readonly to the contacts scope bundle so lark contact get returns email (was previously gated by tenant admin policy, scope confirmed via Lark dev console). - lark msg resource: infer --type from file-key prefix (img_*/file_*) so callers don't need to know whether something is image vs file vs video vs audio. --type is now an optional override. - Sync lark-messages and lark-contacts skills to current CLI shape: document chat list-dms, chat delete, chat create --to shortcut; drop stale fields (job_title/department) from contact get example; note Glints tenant restrictions on list-dept. - USAGE.md app-permission checklist: add the new email scope.
- msg history Long help previously said "default reads as bot" while the flag default has been "user" for a while; rewrite the prose to match the actual default. - skill: split the upload cap (30MB files, 10MB images per Lark API) and cite the 234037 error code for the 100MB download cap so the reader can attribute limits to Lark rather than the CLI.
Lark's IMAP server occasionally returns 0 from SELECT in some auth/idle states even when the server holds messages, causing sync to silently no-op for days. Always go through GetAllUIDs() — a truly empty mailbox returns an empty list and hits the up-to-date branch cleanly.
Default to user identity. P2P chat resources require user token since the bot isn't a chat member.
nameResolver batches+caches open_id->display-name lookups within a run, falling back to the open_id when the contacts lookup fails or is empty. DMCache persists a person->chat_id map in the config dir (dm_cache.json). Lark exposes no read-only way to get a 1:1 chat_id from an open_id, so once one becomes known (send response / history read / list-dms) we remember it, letting later reads skip the send-to-discover step that would notify the counterpart.
Decodes Lark message content deterministically into one clean plain-text block per message so an agent can read a conversation without writing a recursive JSON decoder: text -> text, a -> label (url), at -> @name (resolved), img/media/file -> [image|video|file <key>], post -> title+paragraphs, interactive -> [card] <title/summary>, recalled -> [recalled]. Senders and @-mentions resolve to display names; replies are marked with a leading marker + indent and thread messages carry a thread:<short> tag. Adds output.FormatText; pretty/json/ndjson/table/csv are unchanged.
Lark's search API silently ignores its from_ids/chat_ids parameters and returns global results, so the CLI now matches meta_data.from_id / meta_data.chat_id after fetching and prints a one-line stderr note when it does. Scans up to 50 pages to fill --limit against the global stream, and sets client_side_filtered in the JSON output. Also renders search hits as a compact transcript under --format text.
…commands - msg history / dm / msg search emit the transcript under --format text and resolve sender + mention names via the shared resolver. - dm / chat dm consult the persistent DM cache before the recent-activity scan, cache any chat_id they learn (send response, history, list-dms), and return an actionable open_id + send hint when one is still unknown — never sending implicitly. - Enable cobra.EnableTraverseRunHooks so the root --format hook is no longer shadowed by each group's scope-validation PersistentPreRun. Without this, output.Format stayed "pretty" for every grouped subcommand, so --format json/ndjson/text were silently dropped on e.g. msg history. Guarded by a regression test. - SKILL.md documents the transcript format, the cache-backed DM read, and the client-side search-filter note.
Agent-readable UX: transcript format, DM cache, client-side search filters, name resolution
`lark msg send --file` sent every attachment as msg_type=file. Lark
rejects that for video/audio with code 230055 and stores them as
"nonsupport" ("This message type is currently not supported").
Map the upload file_type to the msg_type Lark expects:
mp4/mov/m4v -> media (video) opus -> audio (voice) else -> file
Video also needs:
- a cover image_key, or the message is still stored as nonsupport.
Use --cover if given, else generate a plain placeholder (Lark draws
its own play button over it).
- its duration in the upload's `duration` form field, or the client
renders 00:00. Parse it from the mp4 moov/mvhd box (pure Go).
Adds the --cover flag and threads durationMs through UploadFile(AsUser).
Adds 'lark mail filter' (list/create/delete) and 'lark mail folder' (list/create) wrapping the mail-v1 user_mailbox rule and folder endpoints. Filter create matches by sender — full addresses match exactly (operator: is), @Domain entries match by contains — and moves matched mail to a folder. The Open API has no auto-forward action, so move-to-folder is the supported primitive; fan-out to a chat is left to a separate folder-watching forwarder. New 'mailrules' scope group (mail:user_mailbox.rule:write, mail:user_mailbox.folder:write) kept separate from the IMAP 'mail' group so read-only IMAP usage is unaffected.
The --add flag was defined but never used: login requested only the named scope groups and relied on Lark's incremental authorization to preserve existing grants, which unreliably dropped groups (observed dropping the messages/im group when adding mailrules). Now --add explicitly requests the union of currently-granted groups and the new ones, so no permission is silently lost.
filter create now supports forward actions in addition to move-to-folder: --forward-email (action type 12) and --forward-chat (type 13), both repeatable and combinable with --folder. At least one action is now required and --folder is optional. Although the published API docs mark forwarding unsupported, the rule engine honours types 12/13. Also adds 'mail folder delete' and documents the filter/folder commands and action-type map in the lark-email skill.
lark auth login --manual: browser-free login for headless hosts. Prints the consent URL, human approves on any device and pastes the localhost redirect URL (or bare code) back on stdin; reuses the registered localhost:PORT/callback redirect_uri and verifies OAuth state. No xdg-open, no reachable callback needed. lark auth refresh: non-interactive access-token refresh for a keep-alive timer, so Lark's short refresh-token window never lapses into a full re-login.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Major expansion of the lark-cli with 20 top-level commands (up from 12), covering the full Lark Open Platform surface.
New command groups
lark api— Raw API passthrough for all 2500+ Lark endpoints (GET/POST/PUT/PATCH/DELETE + discovery vialark api list)lark approval— Approval workflows (list/get/approve/reject/transfer/cancel/cc)lark attendance— Clock-in/attendance recordslark events— Real-time WebSocket event subscription (NDJSON streaming with type/regex filters)lark meetings— Video conferencing (search, notes, transcript, recording)lark schema— API schema introspection via llms-docslark slides— Presentation management (create/get/slide create-delete/media upload)lark wiki— Wiki space and node management (spaces/create/move/delete/get/children)Expanded existing commands
lark task— Full CRUD: create, update, complete, reopen, assign, subtasks, comments, reminders, tasklistslark bitable— Full CRUD: table/field/record management, batch ops, searchlark chat— Group management: create, update, delete, members add/remove, pin/unpin, dm lookup, list-dmslark msg— Search across chats, batch get, forward, merge-forward, edit/recall with --as userInfrastructure
--as usersupport across all message/chat commands (send, history, recall as your identity, not bot)--formatglobal flag:pretty(default),json,ndjson,table,csvauth loginretry (no more manual kill on port 9999)chat dm,chat list-dms) since Lark's API doesn't expose P2P chat listsLark API limitations documented
msg editandmsg forwardare bot-only (tenant token required by Lark)chat createrequires bot identity (im:chat:createis tenant-scope only)approval:approval) may need adjustment per tenantTest plan
go build ./cmd/lark/builds cleanlygo test ./... -short— 25 tests passlark api listreturns Lark API module indexlark task list --format tablerenders ASCII tablelark msg search "test" --limit 3returns search resultslark chat dm "Francis Goh"returns open_id + P2P chat_idlark chat list-dmsenumerates recent DMslark wiki spacesreturns wiki space listlark approval --help/lark slides --help/lark attendance --helpshow subcommandslark events --helpshows subscribe subcommandlark schema modules --format tablerenders module list