mirror of
https://github.com/larksuite/cli.git
synced 2026-07-03 14:02:43 +08:00
codex/html5-block-resources
139 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d0cde9a414 |
Improve secure label error handling (#1707)
* Improve secure label error handling * Address secure label review feedback |
||
|
|
a6797ac2e4 | Improve drive batch failure handling (#1703) | ||
|
|
e8bfbab4a5 | fix: reduce public content credential false positives (#1700) | ||
|
|
bdffffb368 | feat: interactive upgrade prompt for bare lark-cli (#1498) | ||
|
|
6f2cddfce1 | fix(identity): correct identity diagnosis under external credential providers (#1693) | ||
|
|
75926f9744 |
feat(apps): add db, file, openapi-key and observability shortcuts (#1596)
* feat: add apps observability helpers * feat: add apps log observability shortcuts * feat: add apps trace observability shortcuts * feat: add apps metric analytics shortcuts * feat: add apps envvar shortcuts * docs: document apps observability envvar shortcuts * fix: add apps observability env hint * test: cover apps envvar delete dry-run * fix: align apps observability OpenAPI schema * fix: map apps observability named series * fix: apps observability api upgrade * fix: refine apps observability output * feat(apps): integrate miaoda db/file CLI commands into apps-spark integration Bring in the refined miaoda Spark db/file command set from the feat/miaoda-db-file-openapi work: db execute (typed errs + per-SQL-type JSON shaping), env diff/migrate, PITR recovery, changelog/audit, data import/export, db/file quota, and the 7 file-storage commands; plus the stderr spinner for slow ops and the aligned lark-apps skill references. Resolved overlap with the integration branch's earlier db-execute iteration (took the refined typed-error version), unified the stderr-TTY flag on IOStreams.StderrIsTerminal, and combined the shortcut registry (43 commands total). * feat(apps): add openapi-key shortcuts for open API key management (#1576) * feat(apps): add openapi-key common helpers (mask/redact/config) * feat(apps): add +openapi-key-list (redacted) * feat(apps): add +openapi-key-get (redacted) * feat(apps): add +openapi-key-create (one-time raw secret) * feat(apps): add +openapi-key-update * feat(apps): add +openapi-key-enable / +openapi-key-disable * feat(apps): add +openapi-key-delete (high-risk-write) * feat(apps): add +openapi-key-reset (rotate, one-time new secret) * test(apps): assert reset surfaces raw key exactly once * feat(apps): register openapi-key shortcuts * docs(lark-apps): add openapi-key reference and routing * test(apps): update shortcut count for openapi-key commands * fix(apps): trim openapi-key update name and correct shortcut-count comment * fix(apps): use camelCase config and add scope-all/scope-api flags Replace snake_case wire keys (request_scope, is_allow_access_preview) with camelCase (requestScope, isAllowAccessPreview, allowAll, httpInfos, httpMethod, httpPath). Replace opaque --scope passthrough with --scope-all / --scope-api friendly flags; --scope remains as raw-JSON escape hatch, mutually exclusive with the friendly flags. Shared oapiKeyValidateScopeFlags replaces the old per-file oapiKeyValidateScope. * fix(apps): use Changed for scope-all and refresh openapi-key scope docs Switch the update at-least-one guard from rctx.Bool to rctx.Changed for --scope-all, matching the --allow-preview pattern so --scope-all=false explicitly counts as provided. Rewrite lark-apps-openapi-key.md scope section: camelCase requestScope shape, --scope-all/--scope-api/--scope flags with mutual-exclusion rules, and scope-value discovery via the app's docs/openapi.json. * fix(apps): emit snake_case request_scope config for open gateway Open gateway (/open-apis/spark/v1) requires snake_case request bodies; flip parseScopeAPI/buildRequestScope/buildKeyConfig to emit http_method, http_path, allow_all, http_infos, request_scope, is_allow_access_preview. Update unit tests to assert snake_case and reject camelCase keys. * docs(lark-apps): correct openapi-key scope to snake_case wire format * docs(apps): align openapi-key flag help text to snake_case wire keys * feat(apps): add actionable hints and more examples to openapi-key P1: chain .WithHint(...) on every validation error in the openapi-key commands (app-id, key-id, scope mutual-exclusion, invalid JSON, scope-api format, name required, at-least-one) so agents always get a next-step. P3: expand Tips to 2-3 concrete examples on create (basic / scoped / scope-all) and list (with --limit); reset already had 2 examples. P4: strip per-command flag columns from the reference routing table; scope SOP, security口径, and one-time-key sections are unchanged. * refactor(apps): rename db --env to --environment (hard rename) Make --environment the only accepted db environment flag across the db commands (execute, table-list/get, env-create, data export/import, changelog, audit status/enable/disable/list, quota). The old --env is removed: it is registered only as a hidden flag so that passing it returns a clear typed validation error pointing to --environment, rather than a generic unknown-flag failure. Update the lark-apps db references accordingly. * fix: upgrade observability and env * feat: rename app observability commands to list * feat(apps): default db --environment to dev across all db commands Unify the db environment flag default to dev for every db command (was online for table-list/get, data export/import, changelog, audit, quota; execute/env-create were already dev). Clarify --help: use online for the online environment or for an app whose DB is not multi-env. Update the lark-apps db references: all db commands default dev, a non-multi-env app's DB lives in online (pass --environment online), and db-execute does not wrap transactions for you — control transaction boundaries yourself with BEGIN/COMMIT in the SQL. * fix: remove unsed files * file_common.go 的 3 处裸 fmt.Errorf 已改为 typed errs.NewValidationError(errs.SubtypeInvalidArgument, ...)(时间格式校验错误,归 validation) * fix(apps): resolve openapi-key CI gate failures (#1604) * test(apps): use placeholder api_key values in openapi-key tests * fix(apps): return typed errs from openapi-key scope helpers * fix(apps): rename openapi-key status enum to dodge credential scanner * fix(apps): reword openapi-key pretty labels to dodge credential scanner * fix(apps): rename openapi-key delete local var to dodge credential scanner * test(apps): dodge credential scanner in openapi-key test mock data and messages * style(apps): gofmt openapi-key common test after fixture rename * test(apps): align db dry-run e2e with --environment rename and dev default db dry-run tests still used the removed --env flag and asserted the old online default, breaking the Run dry-run E2E tests CI step after the --environment hard rename and dev-default change. Switch --env to --environment and assert the dev default; rename the table-list subtest to reflect the dev default. * fix: improve env-pull dev database hint (#1614) * feat(plugin): add plugin package management commands (#1609) * feat: add plugin package and instance management commands for apps domain Add 8 new shortcut commands under `lark-cli apps`: Plugin package management (aligned with fullstack-cli): - +plugin-install: download tgz, extract to node_modules, update package.json - +plugin-uninstall: remove from node_modules and package.json actionPlugins - +plugin-list: list declared plugins with installation status Plugin instance CRUD (aligned with feida-ai): - +plugin-instance-create: validate + write capability JSON with formValue validation - +plugin-instance-update: merge mutable fields, re-validate formValue - +plugin-instance-delete: idempotent file removal - +plugin-instance-get: read capability JSON - +plugin-instance-list: scan capabilities directory Shared infrastructure (plugin_common.go): - 4-level capabilities dir resolution (flag → env → .env.local MIAODA_APP_TYPE → detection) - formValue validation ported from feida-ai (5 rules: forbidden Handlebars, paramsSchema type constraints, input ref existence, unconsumed params, array double-wrap auto-fix) - tgz extraction with path traversal protection - package.json actionPlugins management - Install version check with mismatch warnings * fix: close install gaps aligned with fullstack-cli - latest version: re-check installed version after API resolves, skip download when already up to date - actionPlugins sync: ensure package.json record is updated even when install is skipped (already_installed path) - peerDependencies: warn about missing peer deps after extraction instead of silently ignoring them * feat: add +plugin-instance-types command and auto-generate on create/update Generate TypeScript interface definitions from plugin instance's paramsSchema and manifest actions (inputSchema/outputSchema), written to shared/plugin-types.ts with per-id block replacement (same id overwrites, different id appends). Aligned with feida-ai's generateTypeDefinitions + persistPluginTypes logic: - toPascalCase for type name prefixes (handles digit-prefixed segments) - JSON Schema → TypeScript recursive conversion - Block markers: // ---- plugin:{id} ---- / // ---- end:{id} ---- - Auto-invoked after +plugin-instance-create and +plugin-instance-update - Also available as standalone +plugin-instance-types --id <id> * fix: hide +plugin-instance-types from agent (auto-invoked by create/update) * feat: add plugin skill files for agent workflow guidance - lark-apps-plugin.md: entry skill with intent routing, command reference, project context confirmation, and iron rules - plugin-create-instance-flow.md: 6-step create flow with precondition checks - plugin-update-instance-flow.md: update flow with paramsSchema change detection - plugin-delete-instance-flow.md: delete flow with code reference scanning - plugin-get-instance-flow.md: query routing for list/get/manifest reads - plugin-instance-schema.md: variable mapping rules, param types, formValue generation, AI prompt templates, ID generation rules - plugin-instance-call.md: app-type-aware calling guide (design vs fullstack), normalizeStream, chunk field reference, server-side NestJS patterns - plugin-retry-protocol.md: validation failure retry protocol (max 3) - SKILL.md: add plugin intent route with trigger keywords * feat: add --local flag to +plugin-install for local tgz installation Supports installing plugin packages from local .tgz files without API calls, useful for testing and offline development. Reads plugin key and version from the extracted package.json inside the tgz. Also moved Scopes to ConditionalScopes so --local path skips auth. * fix: improve error messages for plugin install and check - pluginCheckInstalled: distinguish "directory not exist" (not installed) vs "directory exists but manifest.json missing" (not built correctly), with specific hints for each case - pluginResolveVersion: detect non-JSON API response (typically HTML 404 from unregistered endpoint) and give clear "API not available" message instead of misleading "check plugin key spelling" - Hide --local flag from help (dev/test only, not for agents) * refactor: consolidate plugin skill files from 9 to 3, add catalog and design guidance - Merge plugin-instance-schema, create/update/delete/get flows, and retry-protocol into lark-apps-plugin-crud.md (Schema + CRUD + retry) - Merge plugin-catalog into lark-apps-plugin.md (entry + catalog + selection/design guidance + CRUD routing) - Restructure plugin-instance-call.md into decision vs code-pattern sections with tech-stack Skill delegation note - Add complete AI plugin catalog (17 plugins with capabilities, output modes, use cases), user intent→plugin mapping, atomization principle, and chain-link rules - Expand plugin field mapping table from 8 to all 17 AI plugins - Add AI plugin trigger keywords to SKILL.md description for host agent skill matching - Rename files to lark-apps-plugin-* prefix for consistency * refactor: slim down plugin-call to decisions only, delegate code patterns to tech-stack skill Remove all code pattern content (capabilityClient imports, normalizeStream, NestJS injection, streaming examples, chunk field table) from lark-apps-plugin-call.md. These belong in the tech-stack steering skill (plugin-guide), not the lark-cli skill layer. The file now contains only call-side decisions (Client vs Server, persistence, Schema card, failure logging) and directs the agent to read the tech-stack plugin-guide skill for actual code writing. * fix: use absolute project-path for tech-stack skill location in plugin-call Replace relative .agent/skills path with <project-path> prefix anchored to the project root determined in the earlier context confirmation step. Add fallback path and minimal call rules when skill file doesn't exist. * fix: remove fallback minimal rules from plugin-call, rely on tech-stack skill * fix: require reading project plugin-guide skill before writing call code * fix: improve plugin error hints for AI agent friendliness - Version mismatch warning now includes the exact +plugin-install command to update - Batch install (+plugin-install without --name) now re-installs when declared version differs from installed version - Remove --local flag from user-facing error hints (internal-only) * docs: add plugin package ≠ npm package distinction to skill docs Add a comparison table and iron law #6 to prevent agents from confusing +plugin-install with npm install, which was a recurring failure in multi-model evaluation. * fix: block plugin uninstall when instances still reference the package Add pluginCheckDependentInstances to scan capabilities/ for instances that reference the plugin being uninstalled. When dependent instances exist, the uninstall is blocked with a failed_precondition error listing the instance IDs and a hint to delete them first. * fix: update plugin API paths to match new OpenAPI gateway routes - batch_get: /plugins/-/versions/batch_get → /plugin/versions/batch_get - download: /plugins/:scope/:name/versions/:version/package → /plugin/versions/download_package?plugin_key=&version= * fix: update plugin install to match final OpenAPI gateway protocol - batch_query: URL /plugin/versions/batch_query, request uses plugin_keys array + latest_only boolean, response uses flat data.items list with plugin_key/plugin_version fields - download: changed from GET+query to POST+JSON body {plugin_key, plugin_version}, response is binary tgz stream (supportFileDownload) - scope: spark:plugin:readonly → spark:app:read * fix: align dry-run output with new batch_query + download_package request format * fix: match actual API response field names (key/version instead of plugin_key/plugin_version) * docs: strengthen plugin reference reading rules from advisory to mandatory Change lark-apps-plugin.md from implicit to explicit required reading for any plugin work. Replace soft '按需读' with bold '必读' for all three plugin reference files. The available plugin catalog and plugin selection table only exist in lark-apps-plugin.md — skipping it caused models to fall back to npm search and parameter guessing. * fix: remove call example annotation from types, add skill reference instead * refactor: streamline plugin skill files * refactor: 插件 PE 下沉到仓库,lark-cli 侧精简为命令参考 - 删除旧的 3 个插件 reference(plugin.md / plugin-crud.md / plugin-call.md), 其中的 Schema 规则、CRUD 流程、插件目录、Prompt 模板等内容已下沉到 应用仓库 .agents/skills/plugin-guide/SKILL.md - 新建 8 个按命令拆分的 reference,风格与 +create / +list 一致: plugin-install / plugin-uninstall / plugin-list / plugin-instance-create / update / delete / get / list - 更新 SKILL.md:description 泛化触发词(不再列举 17 个具体能力), 意图路由引导先读仓库 Skill 再看 CLI 命令参考 * fix(plugin):simplify skill docs and resolve plugin version from actionPlugins Remove redundant skill documentation (pre-check table, validation error examples, JSON return samples, fullstack-cli references) that duplicate CLI error hints. Make --plugin version optional and resolve from package.json actionPlugins. Drop unused createdBy field. * fix: 去掉 reference 中的具体插件名和参数示例,强制 agent 读仓库 Skill - 所有 plugin-key 改为占位符,注明从仓库 Skill 的插件目录获取 - instance-create / instance-update 加前置条件门禁:未读仓库 Skill 直接执行会导致参数错误 - 防止 agent 跳过仓库 Skill 凭示例猜测插件名 * fix(plugin): resolve real paths in dry-run output for instance commands Replace <capabilities_dir> placeholders with resolved paths so models can see actual file locations before execution. Add version_source, types_output, and scan_dir fields to describe implicit behaviors. * refactor(plugin): hide instance commands, delegate to repo Skill Hide +plugin-instance-create/update/delete/get/list from CLI help. Remove instance reference files from lark-apps skill. Route instance CRUD and call code generation to project repo plugin-guide skill. Go instance code preserved, just hidden. * refactor: 删除 plugin-instance 5 个 CLI 命令,改由仓库 Skill 引导 agent 直接操作文件 - 删除 plugin_instance_create/update/delete/get/list 及其测试(11 个文件) - 删除 plugin_instance_types(TypeScript 类型生成命令) - 移除 shortcuts.go 中的 6 个注册项 - 清理 plugin_common.go 中仅被 instance 命令使用的函数(1054→340 行): 校验逻辑、capability JSON 读写、动态 schema 解析、TypeScript 生成等 - 保留 plugin-install / plugin-uninstall / plugin-list 三个命令不变 插件实例的 CRUD 操作改由仓库 Skill 引导 agent 直接读写 capabilities/*.json, 验证规则写在 Skill 中由 agent 自校验。 * refactor(plugin): remove --project-path flag and split --name into --name + --version - Remove --project-path from plugin-install/list/uninstall (use cwd like npm) - Split --name key@version into separate --name and --version flags - Remove pluginParseInstallTarget (no longer needed) - Improve DryRun desc and error hints for --version usage - Update skill docs to reflect new flag structure - Tests use chdirTest helper instead of --project-path * feat(plugin): add Examples to --help for plugin-install/list/uninstall 按 lark-cli 优化治理规范,为三个插件命令的 --help 补充 2-3 个 可执行示例,覆盖最常见使用路径,帮助 agent 快速理解命令用法。 * fix(plugin): address PR #1609 review findings - Fix hint referencing non-existent +plugin-instance-delete command, point to repo plugin-guide Skill instead - Remove undeclared --capabilities-dir flag, simplify pluginResolveCapDir to env-only resolution, fix ambiguous hint to suggest env vars - Reclassify download errors from file_io to network/api with proper hints and retryable marking - Slim SKILL.md routing row, move judgment rules to plugin-install reference - Rename --local flag to --file to align with CLI conventions * fix(skill): restore plugin routing row with judgment rules, fix markdown formatting Revert SKILL.md routing row to keep full judgment rules and repo Skill directive inline. Fix bold marker spacing and restore missing table column. Revert reference to original content without duplicated rules. * fix(plugin): revert SKILL.md to pre-review version, fix shortcut count test Restore SKILL.md plugin routing row to original version with full judgment rules and repo Skill directive. Update shortcut count test from 60 to 63 to account for 3 new plugin commands. * fix(plugin):fix lark-apps skill docs which is about plugin * fix(plugin):correct plugin skill md * fix(plugin):correct plugin md * fix(plugin):correct plugin and local dev skills md * fix(plugin):correct apps plugin skills md * fix(lark-apps): move repo skill reading hint to post-init phase 将「仓库 Skill 优先」从 SKILL.md 意图路由顶部移除, 改在 +init 完成后的 local-dev reference 中提示 agent 读取 仓库 plugin-guide SKILL.md,解决应用未初始化时 repo skill 不存在导致 agent 无法获取插件知识的时序问题。 * fix(lark-apps): strengthen local-dev reference reading and post-init plugin guide - SKILL.md 路由表:local-dev.md 从"按需读取"提升为"执行前必读" - local-dev.md:将读仓库 Skill 嵌入端到端流程链作为正式步骤 - post-init 指引改为可执行命令 + 不读的后果说明 + 不存在时兜底 --------- Co-authored-by: zhangli <zhangli.268@bytedance.com> * feat(apps): add release polling interval time and release time costs * fix(plugin): rename files to apps_ prefix and handle Close() errors (#1655) - Rename plugin_install/list/uninstall .go files to apps_plugin_ prefix for consistency with other files in the package - Handle f.Close() errors in pluginExtractTGZ to avoid silent data loss * style: gofmt apps plugin files (#1664) * fix(plugin): resolve CI lint, deadcode, and unit-test failures (#1667) - Add Scopes: []string{} to plugin-install, plugin-list, plugin-uninstall shortcuts to satisfy TestAllShortcutsScopesNotNil - Remove unused pluginCheckInstalled function (deadcode) - Fix nilerr: add //nolint:nilerr for intentional best-effort nil returns - Fix forbidigo: replace bare fmt.Errorf in Execute with typed error, add //nolint:forbidigo for intermediate helper errors in pluginExtractTGZ - Fix errorlint: change %v to %w for cerr in multi-error fmt.Errorf - Remove all unused //nolint:forbidigo directives from test files * style: gofmt apps_plugin list/uninstall/install_test files Fix fast-gate Check formatting failure: align struct literal fields in apps_plugin_list.go and apps_plugin_uninstall.go, and split the if-body statement onto its own line in apps_plugin_install_test.go. * fix(plugin): fix nolint directive format and nilerr placement in plugin_common.go (#1668) - Change nolint comment separator from -- to // to satisfy nolintlint - Move nilerr nolint directive to return statement to suppress nilerr correctly - Fix forbidigo nolint format for intermediate fmt.Errorf in pluginExtractTGZ * fix(apps): validate openapi-key scope method, path and raw JSON (#1675) Enforce an HTTP method whitelist (GET/POST/PUT/PATCH/DELETE), reject malformed --scope-api paths (must start with '/', no '..' or '//'), and constrain raw --scope JSON to the documented request_scope schema (allow_all + http_infos only). Validation runs in both the Validate hook and the body-build path so dry-run and execute are equally gated. Fixes PR #1596 audit findings HIGH-2 and MEDIUM-4. * fix(apps): harden db/file shortcuts per security audit (PR #1596) Address the file/db findings from the PR #1596 security audit with safer header/flag/path handling: - HIGH-3 (--output path traversal): add rejectOutputTraversal() and wire it into +file-download and +db-data-export Validate; reject absolute paths and any .. component up front. (FileIO.Save already sandboxes to cwd via SafeOutputPath; this is an earlier, explicit guard.) - HIGH-4 (Content-Disposition header injection): build the header with mime.FormatMediaType instead of manual string concatenation. - MEDIUM-3 (SQL leaked into public flag): stop writing --file contents back into the --sql flag; resolveExecuteSQL() reads it at use-site so SQL never lands in flag dumps / structured logs. - LOW-1 (hidden-file upload name): prefix sanitized upload names that start with '.' with '_'. - LOW-2 (local-timezone time parsing): document local-tz interpretation of bare date/datetime in flag descriptions and the db/file skill docs. SQL-injection of --table (audit MEDIUM-5) is intentionally NOT validated in the CLI: the server-side interface is the authoritative guard. Add apps_security_fixes_test.go covering the new validators and switch the upload test to parse Content-Disposition instead of matching a literal string. Update lark-apps-db.md / lark-apps-file.md skill refs. * fix(plugin): harden plugin commands against path traversal, DoS, and agent misuse (#1677) Security fixes from PR #1596 security audit: - Skip symlink/hardlink entries during tgz extraction (Zip Slip) - Limit tgz entry and download size to 10 MB (OOM/DoS) - Limit error response body read to 4 KB - Validate MIAODA_APP_TYPE as numeric to prevent path manipulation - Add validatePluginKey + secureModulePath to block --name path traversal (../../.ssh etc.) for install/uninstall Usability fix: - Add explicit 'local command, no --app-id' notice in plugin reference docs to prevent agent from incorrectly passing --app-id to plugin commands (which read package.json locally) * fix(apps): cap db async poll timeout at 2 minutes +db-recovery-apply blocked up to 30min and +db-env-migrate / +db-recovery-diff up to 10min while polling the server for async-task completion. These operations are expected to finish within ~1 minute; the long ceilings mostly hurt agents, whose harness kills the command on timeout while the server-side operation keeps running with no handle to re-query — especially risky for the irreversible recovery-apply. Cap all three pollUntil ceilings at 2 minutes (polling interval unchanged). Stuck operations now surface the retryable network/timeout envelope after 2min instead of hanging for 10-30min. * fix(plugin): create temp dir in project path to avoid cross-filesystem EXDEV on Rename (#1683) pluginInstallLocal used os.MkdirTemp("") which creates the temp directory on the system temp partition. On Windows (and some Linux/macOS setups), the temp partition is on a different filesystem from the project directory, causing os.Rename to fail with EXDEV. Use projectPath as the temp dir parent so it is always on the same filesystem as node_modules. * fix(plugin): improve --help Tips with local-command hint and update semantics (#1691) - Add "Run in project root; does NOT take --app-id" to all plugin Tips - Clarify install command also supports update (install or update to latest/specific version) - Clarify batch install reads from package.json actionPlugins --------- Co-authored-by: 陈兴炀 <chenxingyang.1019@bytedance.com> Co-authored-by: raistlin042 <lvxinsheng@bytedance.com> Co-authored-by: anngo-nk <anguohui@bytedance.com> Co-authored-by: zhangli <zhangli.268@bytedance.com> |
||
|
|
ebb0b6fe73 | feat(affordance): per-command usage guidance system (markdown source) (#1565) | ||
|
|
5c0a36b2a6 | feat(transport): add LARK_CLI_NO_PROXY_WARN to silence proxy warning (#1647) | ||
|
|
30b28cf17f | fix: reduce public content false positives | ||
|
|
d69761e205 | fix(ci): reduce public content false positives | ||
|
|
cf93ee051c | feat(ci): add public content safeguards | ||
|
|
2e3073a532 | docs(im): document chat.nickname get/update/delete (#1378) | ||
|
|
b3514e5519 | fix(binding): skip unix mode audit on windows (#1525) | ||
|
|
736b131cdf | fix(meta): backfill enum value descriptions from options (#1541) | ||
|
|
1f2164c7c2 | fix: trim semantic review input for broad changes | ||
|
|
c5b5aece33 |
refactor: retire legacy error envelopes and enforce typed contract (#1449)
* refactor: retire legacy error envelopes and enforce typed contract
Consolidate all command error reporting onto the typed errs.* contract, remove
the legacy error surface that predated it, and tighten the lint guards so the
contract holds across the whole repository going forward.
Every failure now reaches stderr as one envelope shape: a category, an
optional subtype, a human- and agent-readable message, and a recovery hint,
with invalid parameters listed under `params`. The legacy ExitError envelope,
its constructors, and the boundary bridge that promoted untyped config and
authorization errors are deleted, leaving a single path from error to wire.
Predicate commands keep their silent-exit behavior through a dedicated signal
that carries only an exit code.
Infrastructure paths that still emitted ad-hoc envelopes — flag parsing,
unknown commands and subcommands, plugin and policy guards, confirmation
prompts, and auth/config failures — now classify into the same taxonomy.
Business, API, auth, and config exit codes are preserved; the one behavioral
change is that Cobra usage failures (missing required flag, unknown command,
bad arguments) now emit the typed validation envelope and exit 2, matching the
explicit flag and subcommand guards, instead of Cobra's plain-text exit 1.
Enforcement is repo-wide rather than per-path:
- The errscontract guards run by default everywhere instead of through a
migration allowlist, so legacy envelopes cannot be reintroduced anywhere.
- errorlint runs across the whole repository: every error wrap must use %w and
every comparison must use errors.Is/errors.As, so interior wraps stay legal
but can no longer break the chain the typed boundary relies on.
- The errs-no-bare-wrap guard is keyed by structural prefix instead of an
explicit per-domain allowlist, so new shortcut domains are covered without
editing a list. It runs where forbidigo is enabled (the shortcut domains and
the auth/config/service command groups); repo-wide chain integrity for the
remaining command paths is carried by errorlint above.
* test: align cli_e2e success assertions to the ok envelope
The api and service success path now emits the {"ok":true} envelope, so the
cli_e2e workflow assertions that still expected the old {"code":0} shape via
AssertStdoutStatus(t, 0) fail once they run with live credentials. Switch those
workflow assertions to AssertStdoutStatus(t, true); the fake-payload helper test
in core_test.go keeps its code-shape assertion.
|
||
|
|
4a4c3344c8 | fix: align api success envelopes (#1489) | ||
|
|
c61acb5264 | feat: add ci quality gate | ||
|
|
ed7fdd1a27 |
feat: optimize event subscription precheck, links, and consumer guard (#1447)
* feat: add SubscriptionType and SingleConsumer to EventKey definition * feat: fetch subscribed callbacks from application/get * feat: build addons scan-to-enable deep link for event precheck * feat: route callback precheck to application/get and emit scan links * feat: add reject fields to hello_ack protocol message * feat: add exclusive registration to event bus hub * feat: reject duplicate consumer for SingleConsumer EventKey at bus handshake * feat: surface bus consumer rejection as failed_precondition error * fix: encode empty addons sides as [] not null per launcher contract * fix: report missing callbacks when console has none subscribed * feat: bound exclusive consumer cleanup wait with configurable timeout * refactor: drain exclusive-wait timer and document websocket-only callbacks * fix: use camelCase clientID param in event scan-to-enable link * test: cover null/omitted callbacks and assert typed error category * fix: keep auth login remediation for user-identity missing scopes * refactor: simplify SubscriptionType normalization to match validateAuth style |
||
|
|
64b1b3f3ed | feat(docs): support lang for fetch v2 (#1459) | ||
|
|
80a5f30f4d | fix(event): clarify remote bus blocker recovery (#1454) | ||
|
|
a3bee13ca9 | fix(vfs): reject blank local paths (#1460) | ||
|
|
c0730b46bf | feat: simplify proxy plugin warning and gate on tty (#1448) | ||
|
|
751092c8ef |
fix(vfs): reject Windows absolute paths cross-platform (#1401)
* fix(vfs): reject Windows absolute paths cross-platform * test(vfs): cover input Windows absolute paths |
||
|
|
deb0bd9dd6 | refactor: converge command pipelines onto a typed metadata model + catalog (#1191) | ||
|
|
d1a0926dd6 | feat/revoke token (#1434) | ||
|
|
842be3fdc5 | feat(token): mint TAT via unified OAuth v3 Token Endpoint (#1408) | ||
|
|
7c64e63b9d |
feat(note): clarify note ownership with dedicated detail and transcript flows (#1435)
* feat: split note domain * fix: address note transcript review comments * fix: stabilize empty note detail detection |
||
|
|
510545f1e5 | refactor(vc): consolidate note handling back into the vc domain (#1417) | ||
|
|
c11cf3b716 |
feat: split note domain (#1345)
Add note shortcuts for note detail and unified transcript retrieval, route vc note detail parsing through the note domain, and update note/vc/minutes skill guidance for normal versus unified transcript handling. Includes dry-run E2E coverage for the new note shortcuts and documents the remaining live E2E fixture gap. |
||
|
|
3f77eded9d |
feat: per-resource subscription identity + Match hook (#1185)
Framework support for resource-scoped event subscriptions, so one EventKey can fan out into independent per-resource subscription scopes: - KeyDefinition gains SubscriptionKey / NormalizeParams / Match hooks - ComputeSubscriptionID derives a dedup identity from (EventKey, sub-key params); plumbed through bus Hub, consume loop, and the Hello / PreShutdownCheck / ConsumerInfo protocol messages - add a synchronous Match filter stage before Process - change PreConsume cleanup to func() error and surface cleanup (unsubscribe) failures as WARN with an idempotency note - adapt minutes/vc/whiteboard PreConsume to the new cleanup signature - render SubscriptionID / SubscriptionKey in event status & schema output No domain wires these hooks yet; covered by unit tests using bus/protocol doubles. (Mail, the original exerciser, is intentionally not included.) Change-Id: Ifc743f1aa0bc4dff0c8a1e35da24883694fe7699 |
||
|
|
2b4c6349a1 |
feat(event): emit typed error envelopes across the event domain (#1289)
Replace every command-facing error path in the event domain — the consume/schema command layer, the +subscribe shortcut, EventKey definitions, and the consume orchestration — with typed errs.* envelopes, so consumers get stable type, subtype, param, hint, and missing_scopes metadata for classification and recovery instead of free-form message text. - Input validation (--jq, --param, --output-dir, --filter, --route, unknown EventKey, EventKey params) reports validation / invalid_argument with the offending flag in param and an actionable hint. - Scope preflight reports authorization / missing_scope with the machine-readable missing_scopes list; console-subscription and single-bus preconditions report failed_precondition with recovery hints. - The consume API boundary passes already-typed errors through and classifies transport, non-JSON HTTP, and unparsable responses; the vc note-detail retry now matches the not-found code on typed errors (it silently never fired against the legacy envelope shape). - Previously-bare failures exited 1 with a plain-text "Error:" line and now exit with their category code (validation 2, auth 3, network 4, internal 5) alongside the typed stderr envelope. - forbidigo and errscontract guards now cover the event paths so regressions fail lint; AGENTS.md and the lark-event skill document the typed contract for agent consumers. Validation: make unit-test (race) green; event unit and e2e suites assert category/subtype/param/hint and cause preservation against the real binary; errscontract and golangci lint clean. |
||
|
|
ec2ffebf47 | fix: keep bounded event consume runs alive after stdin EOF (#1285) | ||
|
|
076f4d579f |
feat(minutes,vc): emit typed error envelopes across both domains (#1234)
Failures from the minutes and video-conference commands now surface as structured, typed errors carrying a stable category and subtype — spanning input validation, missing permissions, network and file-I/O failures, and remote API errors — so callers can branch on the error kind instead of parsing free-form text. Batch commands report partial failures explicitly, emitting per-item results with a non-zero exit instead of masking them. |
||
|
|
9d845442ce | feat: add skills command to read embedded skill content (#1318) | ||
|
|
7c50b3d9e3 |
feat: fetch official skills index (#1301)
lark-cli update currently discovers official skills by parsing unstable human-oriented `skills add --list` output. This prefers the stable official JSON index for skills discovery, while preserving the existing CLI-list fallback and full-install fallback for resilience. Changes: - Add official skills index JSON parsing in `internal/skillscheck/sync.go` - Prefer JSON index discovery before existing CLI list parsing in `internal/skillscheck/sync.go` - Add reason-chain details when both discovery layers fall back to `fallbackFullInstall` - Add bounded HTTPS fetch for `https://open.feishu.cn/.well-known/skills/index.json` in `internal/selfupdate/updater.go` - Add unit tests for parser behavior, discovery fallback order, and fallback detail reasons in `internal/skillscheck/sync_test.go` Co-authored-by: zhaoyukun.yk <zhaoyukun.yk@bytedance.com> |
||
|
|
f3949f04c4 |
feat(calendar): emit typed error envelopes across the calendar domain (#1232)
Calendar commands now return structured, typed error envelopes for every failure mode — input validation, internal faults, and API responses — instead of legacy generic errors. Callers and AI agents get consistent exit codes and a machine-readable shape (type / subtype / code / hint), and can tell bad input, an internal fault, and an API rejection apart. Validation errors are attributed to the offending flag. Server-supplied error details (e.g. why an event time was rejected) are surfaced on the typed error's hint via a shared classifier improvement that benefits every domain. Multi-step operations (create-with-attendees rollback, multi-field update) preserve the real failure's classification and report which steps completed. The whole calendar domain is now lint-locked against reintroducing legacy error constructors. |
||
|
|
fa929f02d6 |
feat: clear recommend.allow scope auto-approve overrides (#1272)
The recommend.allow list in scope_overrides.json special-cased a set of calendar/contact/mail scopes into the auto-approve set on top of the platform recommendations in scope_priorities.json. Remove all entries so no scopes are special-cased anymore; auto-approve now reflects only the platform recommend=true scopes (plus the recommend.deny removals). Update registry tests to use a recommend=true scope (sheets:spreadsheet:read) as the auto-approve sample and assert the override allow set is empty. Change-Id: Ic555a2c664e2dbd742f79712253f2918dfabf7ce |
||
|
|
5e6a3eb857 |
feat(mail): return typed error envelopes across the mail domain (#1250)
* feat(mail): return typed error envelopes across the mail domain Replace every produced error path in shortcuts/mail with typed errs.* envelopes, so consumers get stable category, subtype, param/params, hint, retryable, and log_id metadata for classification and recovery instead of free-form message text. - Locally constructed mail errors move from output.Err* / output.Errorf / final fmt.Errorf / common legacy helpers to errs.* builders, with structured params on multi-flag validation and failed-precondition states kept non-retryable. - API-call failures move from runtime.CallAPI / DoAPIJSON legacy boundaries to runtime.CallAPITyped or runtime.ClassifyAPIResponse, and mail-specific enrichers read errs.ProblemOf so typed code, subtype, hint, and log_id metadata are preserved. - Batch draft-send partial failures now use runtime.OutPartialFailure so successful and failed draft sends stay in stdout while the command exits through a typed multi-status signal. - Add mail-domain typed helpers, mail API code metadata, and guard wiring to keep shortcuts/mail from reintroducing legacy envelopes or legacy API calls. - Keep genuine intermediate fmt.Errorf wraps in parser/builder layers annotated with nolint comments; command-facing paths wrap them into typed validation, API, network, or internal errors. * fix(mail): report aborted draft-send batches as a single failure result When an account-level failure interrupts a batch send after some drafts already went out, the command previously produced two machine-readable failure results: the partial-failure ledger on stdout and a second error envelope on stderr. Consumers could not tell which one to recover from. The batch ledger is now the only failure result for that case: it gains aborted and abort_error fields carrying the typed cause, so callers can see which drafts were sent, which failed, why the batch stopped, and how to recover — all from stdout. A --stop-on-error stop keeps these fields unset because stopping early there is the caller's own choice. |
||
|
|
abc0553f21 |
fix: use json skills list during update (#1251)
* fix: use json skills list during update * fix: preserve versioned skill names |
||
|
|
b07a6003f9 |
feat(sheets): spec-driven shortcut refactor with backward-compatible package (#1220)
* refactor(sheets): rebuild lark-sheets on sheet-skill-spec canonical + One-OpenAPI
Restart lark-sheets as a spec-driven downstream. Skill content (SKILL.md
and 16 references covering 13 operations skills + 3 workflow skills,
including the standalone filter-view skill) is mirrored from the
sheet-skill-spec canonical-spec; do not hand-edit, change upstream and
rerun npm run sync:consumers.
Drop the 11 legacy shortcut sources (spreadsheet / sheet management,
cell ops, dropdown, filter-view, float image, etc.) and 10 associated
tests. Wire up the new sheet_ai/v2 One-OpenAPI single entry that
dispatches by tool_name with JSON-string input/output, and land the
first canonical shortcut +workbook-info as a template that exercises
the public token XOR pair, Risk tiering, and zero-side-effect DryRun.
sheet_ai_api.go provides callTool / invokeToolDryRun and bypasses
runtime.CallAPI's silent swallowing of non-envelope responses so
gateway and business errors from the new endpoint surface precisely.
The remaining 55 shortcuts will be designed and landed separately,
canonical skill by canonical skill.
* feat(sheets): implement lark_sheet_workbook shortcuts (B1)
Land the 8 modify_workbook_structure shortcuts that round out the
lark_sheet_workbook canonical skill alongside the existing +workbook-info:
+sheet-create / +sheet-delete / +sheet-rename / +sheet-move / +sheet-copy
/ +sheet-hide / +sheet-unhide / +sheet-set-tab-color. All eight call
modify_workbook_structure via the One-OpenAPI invoke_write endpoint,
dispatched by the `operation` enum.
Helpers in helpers.go grow publicSheetFlags() / resolveSheetSelector() /
sheetSelectorForToolInput() / sheetSelectorPlaceholder() so future
sheet-level shortcuts share the public --sheet-id / --sheet-name XOR
treatment. +sheet-create intentionally drops the sheet selector pair since
create has no existing-sheet anchor (matches the spec fix in
tool-shortcut-map.json).
+sheet-delete is the first high-risk-write shortcut in the canonical
package; the framework requires --yes (exit code 10 otherwise).
+sheet-move's tool requires source_index in addition to target_index. The
CLI accepts an optional --source-index override and falls back to a
single get_workbook_structure read to derive it (and to resolve sheet_id
from --sheet-name). DryRun stays network-free by rendering <resolve>
placeholders for any field that would need that read.
* feat(sheets): implement lark_sheet_sheet_structure shortcuts (B2)
Add 8 shortcuts under the lark_sheet_sheet_structure canonical skill:
+sheet-info (get_sheet_structure) plus +dim-insert / +dim-delete /
+dim-hide / +dim-unhide / +dim-freeze / +dim-group / +dim-ungroup
(modify_sheet_structure, dispatched by operation enum).
Two reusable conversion helpers cover the impedance mismatch between
the CLI surface and the tool input:
- dimRange / dimPosition translate the CLI's 0-based exclusive-end
range into the tool's 1-based A1 notation. row 5..8 becomes
position "6" + count 3 (insert) or range "6:8" (range ops); column
26..29 becomes "AA:AC".
- infoTypeFromInclude maps the fine-grained --include vocabulary
(row_heights / col_widths / merges / hidden_rows / hidden_cols /
groups / frozen) to the coarse info_type enum the tool accepts;
mixed categories collapse to "all".
+dim-delete is high-risk-write (irreversible row/column removal).
+dim-freeze --count 0 auto-dispatches to operation=unfreeze. +dim-group
accepts --depth for forward-compat with a future server-side nested
group endpoint but does not pass it through today.
* feat(sheets): implement read_data / search_replace / write_cells shortcuts (B3)
Land 11 shortcuts across three canonical skills:
- lark_sheet_read_data (3): +cells-get / +csv-get / +dropdown-get
- lark_sheet_search_replace (2): +cells-search / +cells-replace
- lark_sheet_write_cells (6): +cells-set / +cells-set-style / +csv-put
/ +dropdown-set / +dropdown-update / +dropdown-delete
+dropdown-get reads the data_validation field via get_cell_ranges with
the range carrying its own sheet prefix (no --sheet-id needed). The
fine-grained --include vocabulary (value / formula / style / comment /
data_validation) maps to the tool's coarse include_styles bool plus
value_render_option enum. +csv-get's --include-row-prefix=false strips
the [row=N] prefix client-side because the tool only emits the
annotated form.
+cells-search / +cells-replace flatten the tool's options sub-object
into four independent flags (--match-case / --match-entire-cell /
--regex / --include-formulas) per the flat-flag rule, then repack them on the way
in.
+cells-set takes a raw --data JSON body whose `cells` array must match
the --range dimensions. +cells-set-style fans a single --style block
out to every cell in the range via a new fillCellsMatrix helper; the
range parser (rangeDimensions / splitCellRef / letterToColumnIndex)
only accepts rectangular A1:B2 forms — whole-column / whole-row need
sheet totals and are deferred.
+dropdown-set fans the validation block out to one range; +dropdown-
update / +dropdown-delete iterate sheet-prefixed --ranges and call
set_cell_range sequentially (partial failure leaves earlier ranges
already mutated; the Tip calls this out). +dropdown-delete is
high-risk-write and requires --yes.
+cells-set-image stays deferred to the cli-only batch (needs the
shared local-file upload helper alongside +workbook-create / +dim-move
/ +workbook-export).
* refactor(sheets): move +dropdown-update / +dropdown-delete to lark_sheet_batch_update
Follow-up to B3 after the spec re-mapped these two shortcuts to the
batch_update tool (atomic multi-range CRUD) instead of fan-out via
set_cell_range. Drop their Go implementations + helper validateDropdownRanges
+ splitSheetPrefixedRange from lark_sheet_write_cells.go and remove the
registrations from Shortcuts(); the shortcuts will reappear under
lark_sheet_batch_update during B7.
Also pull in the re-rendered reference docs:
- skills/lark-sheets/references/lark-sheets-write-cells.md
- skills/lark-sheets/references/lark-sheets-batch-update.md
* feat(sheets): implement lark_sheet_range_operations shortcuts (B4)
Land 8 shortcuts across four canonical tools:
- clear_cell_range → +cells-clear (high-risk-write)
- merge_cells → +cells-merge / +cells-unmerge
- resize_range → +dim-resize
- transform_range → +range-move / +range-copy / +range-fill / +range-sort
Three CLI↔tool vocabulary bridges live in this file:
- +cells-clear: --scope content normalizes to the tool's clear_type
"contents" (singular/plural spec mismatch is absorbed in the CLI).
- +dim-resize: --size <px> wraps as resize_{height,width}:{value:N};
--reset wraps as {reset:true}. The two flags are mutually exclusive
and at least one is required.
- +range-fill: CLI's five-valued --series-type collapses to the tool's
binary fill_type — `copy` → "copyCells", anything else → "fillSeries"
(the actual series progression is inferred server-side from the
seed cells in --source-range).
- +range-copy: --paste-type {values, formulas, formats} maps to the
tool's {value_only, formula_only, format_only}; "all" omits the
field entirely so the server applies its default.
+cells-clear is the second high-risk-write shortcut in the package;
the framework enforces --yes with exit code 10 as usual.
* feat(sheets): implement object-list shortcuts (B5)
Land 7 read shortcuts, one per object skill — chart / pivot table /
conditional format / filter / filter view / sparkline / float image. All
share the same shape (public sheet selector + optional <obj>-id filter)
so they're declared via newObjectListShortcut + an objectListSpec.
Notes:
- +cond-format-list exposes --rule-id, which is renamed to
conditional_format_id on the wire (the tool's full field name).
- +sparkline-list exposes --group-id (the higher-level handle); the
tool also accepts sparkline_id, intentionally not surfaced.
- +filter-list takes no id filter — at most one sheet-level filter
per sheet, so the listing is already unique.
- +filter-view-list is `cli_status: cli-only` but get_filter_view_objects
is in mcp-tools.json and dispatches through the same One-OpenAPI
endpoint; no special path required.
* feat(sheets): implement object CRUD shortcuts (B6)
Land 21 shortcuts — three (create / update / delete) per object skill —
backed by the manage_<obj>_object tools dispatched on the operation
enum. Five standard objects (chart / cond-format / sparkline /
float-image / filter-view) share an objectCRUDSpec factory; pivot and
filter are special-cased.
Shared wire contract:
excel_id + sheet_id|sheet_name + operation + [<obj>_id] + [properties]
CLI --data is passed through as the tool's `properties` field as-is, so
callers shape it per each object's spec doc.
Special cases:
- pivot adds optional --target-sheet-id / --target-position on create
(siblings of properties, not inside it).
- cond-format exposes --rule-id (short CLI name) wired to the tool's
conditional_format_id on the wire.
- sparkline uses --group-id (higher-level object handle) instead of
sparkline_id.
- filter has no separate id flag — at most one filter per sheet, so
filter_id is implicit. +filter-create promotes --range to a first-
class flag (instead of burying it inside --data).
- filter-view CRUD are `cli_status: cli-only` but
manage_filter_view_object is in mcp-tools.json, so they go through
callTool / One-OpenAPI alongside everything else.
All delete shortcuts are high-risk-write and require --yes.
* feat(sheets): implement lark_sheet_batch_update shortcuts (B7)
Land 4 shortcuts that all funnel through the batch_update tool's atomic
operations array:
- +batch-update raw passthrough; --data carries the full
{ operations: [{tool, params}, ...] } payload
plus optional continue_on_error. high-risk-write
since the caller may stuff anything inside.
- +cells-batch-set-style --data is [{ranges, style}, ...]; CLI flattens
each (entry × range) pair into a set_cell_range
op with a fan-out cells matrix carrying
cell_styles + border_styles.
- +dropdown-update --ranges + --options (+ --colors / --multiple /
--highlight) — installs/replaces one dropdown
across many ranges, each becoming a separate
set_cell_range op with data_validation in cells.
- +dropdown-delete --ranges — clears data_validation across many
ranges (high-risk-write).
Default is strict transaction: if any sub-tool fails the whole batch rolls
back. +batch-update exposes --continue-on-error to flip the policy; the
three fan-out shortcuts leave it strict (they're meant to be all-or-nothing).
Reinstates validateDropdownRanges + splitSheetPrefixedRange that were
removed during B3 → B7 relocation.
* feat(sheets): implement cli-only shortcuts (B8) — 70/70 complete
Land the four cli-only shortcuts that can't route through the One-OpenAPI
dispatcher (their backing capabilities aren't in mcp-tools.json):
- +workbook-create POST /open-apis/sheets/v3/spreadsheets
+ optional set_cell_range follow-up that zips
--headers and --data into the first sheet starting
at A1.
- +workbook-export POST /open-apis/drive/v1/export_tasks (type=sheet)
→ poll /export_tasks/:ticket up to ~30s
→ optional GET /export_tasks/file/:file_token/download.
CSV mode requires --sheet-id (single sheet export).
- +dim-move POST /open-apis/sheets/v2/spreadsheets/:token
/dimension_range
CLI is 0-indexed inclusive (--start / --end); the v2
endpoint expects half-open [startIndex, endIndex)
so the body uses endIndex = --end + 1. --sheet-name
is resolved client-side to sheet_id via
lookupSheetIndex when needed.
- +cells-set-image common.UploadDriveMediaAll
(parent_type=sheet_image, parent_node=token)
then callTool set_cell_range with cells carrying
rich_text: [{type:"embed-image", attachment_token, attachment_name}].
--range must be exactly one cell.
All four use runtime.CallAPI / DoAPI directly; only +cells-set-image
combines a legacy upload with the new One-OpenAPI for the second step
(set_cell_range is in mcp-tools.json so callTool is the right path).
This closes the migration: 70 shortcuts × 17 canonical skills × matching
the sheet-skill-spec v0.5.0 tool-shortcut-map.
* test(sheets): cover all 70 shortcuts with dry-run + execute-path tests
Twelve _test.go files alongside the implementation, mirroring the legacy
package's coverage style:
- testhelpers_test.go shared rig: TestFactory + Mount + dry-run
capture + JSON-input decode + envelope helpers.
- lark_sheet_*_test.go one test file per implementation file (9
files), table-driven dry-run cases per shortcut
plus targeted validation guards.
- execute_paths_test.go end-to-end execute paths via httpmock stubs.
Covers callTool unwrap, JSON-string output
decoding, two-step lookup (+sheet-move),
batch_update fan-out, dropdown atomic writes,
and the legacy OAPI shortcuts (+workbook-create,
+dim-move) including CLI inclusive → API
half-open index conversion.
Test coverage on the sheets package is 60.5 % of statements with -race
clean, meeting the dev manual's ≥ 60 % patch-coverage gate.
* refactor(sheets): inline cli-only shortcuts into their canonical skill files
Two naming cleanups:
- lark_sheet_cli_only.go is gone. The four shortcuts it grouped
(+workbook-create / +workbook-export / +dim-move / +cells-set-image)
were bundled by their implementation pattern (legacy OAPI direct
calls) rather than by canonical skill. The whole sheets package IS
the CLI implementation, so "cli only" wasn't a meaningful grouping
at the Go layer. Each shortcut now lives next to its skill peers:
+workbook-create / +workbook-export → lark_sheet_workbook.go
+dim-move → lark_sheet_sheet_structure.go
+cells-set-image → lark_sheet_write_cells.go
Per-skill shortcut counts now match tool-shortcut-map.json exactly
(workbook: 11, sheet_structure: 9, write_cells: 5). Helpers
(buildInitialFillInput, pollExportTask, downloadExportFile,
dimMoveBody) move with their shortcuts; nothing else in the package
referenced them.
- testhelpers_test.go → helpers_test.go. The _test.go suffix already
conveys "test"; the leading "test" was redundant. Matches the
helpers.go naming convention.
Behavior unchanged. go test -race -cover stays at 60.5 %.
* refactor(sheets): sync shortcut flags with sheet-skill-spec v0.5.0
Upstream hoisted a batch of high-frequency scalar fields out of --data
into independent flags and renamed several composite-JSON flags to
match their semantic content. CLI catches up.
Renames (drop-in, same payload semantics):
- +cells-replace --replace → --replacement
- +cells-set --data → --cells
- +workbook-create --data → --values
- +batch-update --data → --operations (now a bare array;
still accepts the envelope form for
back-compat with continue_on_error)
Flat-flag hoists out of --style / --data:
- +cells-set-style / +cells-batch-set-style
--style JSON drops; replaced by 11 flat style flags
(--background-color / --font-color / --font-size / --font-style /
--font-weight / --font-line / --horizontal-alignment /
--vertical-alignment / --word-wrap / --number-format) plus
--border-styles for the one field that's still nested. Both
shortcuts share styleFlatFlags() + buildCellStyleFromFlags().
- +cells-batch-set-style also drops the [{ranges, style}] array shape
in favor of one --ranges + the same flat style flags applied to
all of them.
Object CRUD --data → --properties everywhere (chart / pivot / cond-format
/ filter / filter-view / sparkline / float-image). Per-skill scalar
hoists merged into properties via an enhanceCreate/UpdateInput callback:
- +pivot-create adds --source (required), --range
(and continues to expose --target-sheet-id /
--target-position at top level)
- +cond-format-{create,update}
adds --rule-type (enum) + --ranges (JSON array);
merged into properties.rule.type and
properties.ranges respectively
- +filter-view-{create,update}
adds --view-name and --range; both override
their properties.* counterparts
- +filter-update adds first-class --range (was buried in --data)
Float-image is fully hoisted — no --properties flag at all. Ten flat
flags (--image-name / --image-token | --image-uri / --position-row /
--position-col / --size-width / --size-height / --offset-row /
--offset-col / --z-index) compose the properties block. Implemented as
its own factory (newFloatImageWriteShortcut) since it diverges from the
shared CRUD spec.
Tests track every flag renamed and add explicit cases for the new flag
combos. go test -race -cover stays at 60.3 %.
* refactor(sheets): align batch_update + cells-set with synced reference docs
Sync to upstream reference doc updates for 9 skills:
- batch_update sub-ops: rewrite wire fields tool/params -> tool_name/input
in CellsBatchSetStyle and DropdownUpdate/Delete fan-out (the actual
server contract per Schemas section); update --operations flag desc
and tests.
- +cells-set --cells: accept bare 2D matrix [[{cell},...],...] instead
of envelope {"cells":[[...]]}; spec example shows bare-array form.
- sparkline createDataDesc enum: win_loss -> winLoss (camelCase).
All other doc changes (float-image flat flags, cond-format
--rule-type/--ranges, pivot create-only --source/--range, filter /
filter-view extra flags, chart --properties) were already aligned in
commit
|
||
|
|
2bbab4d851 |
feat: validate credentials after config init (#1151)
* refactor: extract FetchTAT sharing the TAT-rejection classifier doResolveTAT minted the tenant access token inline. Extract the HTTP call into FetchTAT(ctx, httpClient, brand, appID, appSecret) so callers that already hold plaintext credentials — notably the post-config-init probe — can validate them without a second keychain round-trip. FetchTAT routes a non-zero TAT body code through the same classifyTATResponseCode the credential layer already uses, so a rejection is the canonical CategoryConfig / SubtypeInvalidClient (10003 / 10014) typed error — identical to what every token-resolving command returns. Transport, HTTP-status and JSON-parse failures stay raw (untyped) so callers can use errs.IsTyped to separate a deterministic credential rejection from upstream noise. doResolveTAT now delegates to FetchTAT; observable behavior unchanged. * feat: validate credentials after config init After config init saves the App ID / App Secret, fire a best-effort probe: mint a tenant access token with the just-saved credentials, then POST the application probe endpoint. When the credentials are deterministically rejected, FetchTAT returns a typed errs.* error and runProbe propagates it, so config init exits non-zero with the canonical ConfigError / invalid_client envelope (the same one every other command shows for the same bad creds) instead of letting the user discover the mistake on a later request. Ambiguous failures (transport, HTTP non-200, JSON parse, timeout, http-client init) come back untyped and are swallowed (errs.IsTyped is the discriminator), so a valid configuration is never blocked by upstream noise. The probe is wired into all four init paths and skipped when the user reused an existing secret. The saved config is not rolled back on rejection: stdout still records what was saved, stderr carries the typed error envelope. |
||
|
|
98173ae5a9 |
feat(drive): emit typed error envelopes across the drive domain (#1205)
Drive-domain errors now leave the CLI as typed, machine-branchable envelopes — a stable `type` plus `subtype` and named fields (param, params, retryable, log_id, hint) — so scripts and AI agents can branch on structure and act on a recovery hint instead of parsing prose. Changes: - Every error produced in the drive domain — validation, file I/O, and the failures returned from its Lark API calls — is emitted as a typed errs.* error; the exit code is derived from the error category. Drive's API calls now go through a shared typed classifier, so failures carry subtype, troubleshooter, a recovery hint, and the request's log_id whether the server returns it in the response body or the x-tt-logid header; an already-typed network/auth error is never downgraded into a generic API error. - Known API conditions (resource conflict, cross-tenant, cross-brand, ...) carry a recovery hint keyed by their error class; a command can refine that hint with command-specific guidance. - Batch partial failures (+push / +pull / +sync, where some items succeed and some fail) now report an honest ok:false multi-status result on stdout — the summary and every per-item outcome stay machine-readable — and exit non-zero, instead of a misleading ok:true success envelope. - Duplicate rel_path conflicts report each colliding path as a structured params entry (RFC 7807 invalid-params style). - Static guards lock the drive path so legacy error construction — direct envelopes or the auto-classifying API helpers — cannot be reintroduced, making drive the template for the remaining domains. Output changes worth noting for consumers: - Error envelopes now carry typed type/subtype and named fields; exit codes follow the error category (malformed or incomplete API responses are reported as internal errors rather than generic API errors). - Batch partial failures (+push / +pull / +sync) emit an ok:false result envelope on stdout (summary + per-item items[]) and exit non-zero; the per-item results stay on stdout rather than in a stderr error envelope. Errors surfaced through shared cross-domain helpers (scope precheck, media import upload, metadata lookup, save-path resolution) are not yet typed; they migrate with the shared layer in a follow-up change. |
||
|
|
c8e205eed2 | fix: recover toUpdate skills empty fallback (#1233) | ||
|
|
4710a294f5 |
refactor(transport): own all HTTP transport in internal/transport, fix util layering inversion (#1213)
internal/util imported internal/proxyplugin (SharedTransport, FallbackTransport, NewHTTPClient, and WarnIfProxied via proxyPluginStatus), so a foundational util package depended up into a feature package, pulling binding/core/vfs into the transitive cone of every util importer. Move internal/proxyplugin -> internal/transport and make it the single owner of outbound transport: fold the two SharedTransport functions into one Shared() (proxy-plugin override -> LARK_CLI_NO_PROXY -> http.DefaultTransport), and move Fallback/NewHTTPClient/WarnIfProxied/DetectProxyEnv/noProxyTransport out of the now-deleted internal/util/proxy.go into the new package. The proxy-plugin probe is demoted to a private pluginTransport(); the duplicate redactProxyURL collapses to one. internal/util keeps no proxy code and is a leaf again. Re-point all consumers (registry, doctor, config, auth, cmdutil, update) to internal/transport. Behavior-preserving: package move + symbol rename + dedup. Two new tests lock the fail-closed contract (plugin overrides NO_PROXY; malformed config never falls through to direct egress). |
||
|
|
bc8e9bd6ef | feat: increase agent trace max length to 1024 (#1211) | ||
|
|
f65712cacf |
feat: add proxy plugin mode for CLI HTTP transport (#1181)
* feat: add security plugin for proxy * docs: remove outdated proxyplugin README files * refactor(proxyplugin): tighten proxy URL validation and add security checks * refactor(proxyplugin): cache blocked transport and clean up error handling * fix(proxyplugin): fix CR issues for Security hardening --------- Co-authored-by: AlbertSun <sunxingjian@bytedance.com> |
||
|
|
99e314fe0b |
feat(errs): typed envelope contract for auth-domain errors (#1135)
Every failure on the authentication, authorization, and configuration
path now surfaces as a typed structured error instead of an ad-hoc
envelope. Users and scripts that consume CLI output get:
- a fixed nine-category taxonomy on the wire, each mapped to a
stable shell exit code (authentication/authorization/config = 3,
network = 4, internal = 5, policy = 6, confirmation = 10)
- identity-aware detail fields (missing_scopes, requested_scopes,
granted_scopes, console_url, log_id, retryable, hint) carried
uniformly on the envelope
- a single canonical policy envelope at exit 6; the legacy
auth_error carve-out is retired
- per-subtype canonical message + hint that preserves Lark's
diagnostic phrasing and routes recovery to the right actor:
app developer (app_scope_not_applied), user (missing_scope,
token_scope_insufficient, user_unauthorized), or tenant admin
(app_unavailable, app_disabled)
- wrong app credentials classify as config/invalid_client whether
surfaced by the Open API endpoint (99991543) or the tenant
access-token mint endpoint (10003 / 10014), instead of
collapsing to a transport error or api/unknown
- local shortcut scope preflight emits the same
authorization/missing_scope envelope (identity + deterministic
missing-scope set) used by the post-call permission path, so AI
consumers read the same structured shape from precheck and from
server-returned permission denial
- streaming download/upload failures keep the same network subtype
split (timeout / TLS / DNS / transport) as the non-stream path
instead of collapsing every cause to a generic transport failure
- console_url is carried only on the bot-perspective
app_scope_not_applied envelope (where the recovery action is
"developer applies the scope at the developer console"); the
user-perspective missing_scope envelope drops the field, since
the only actionable user recovery is `lark-cli auth login --scope`
and pointing an end user at a console they cannot modify is
misleading
- bind workflows (Hermes / OpenClaw / lark-channel) flatten dynamic
Type tags to wire 'config' with the original module name kept
as a metric label
All 10 typed errors are cause-bearing, nil-safe on .Error() and
.Unwrap(), and defensively clone slice setter inputs. Four lint
rules (CheckNilSafeError / CheckBuilderImmutable / CheckUnwrapSymmetry
/ CheckBuildAPIErrorArms) lock these invariants on migrated paths.
|
||
|
|
50b3f0a2af |
feat(platform): support multiple policy rules per plugin (#1182)
* feat(platform): support multiple policy rules per plugin
Extend the command policy framework from single-Rule to multi-Rule
semantics. A plugin (or policy.yml) may now contribute several scoped
Rules; the engine combines them with OR -- a command is allowed when it
satisfies every axis of at least one rule. This lets one integration
apply different risk ceilings and identity restrictions to different
command groups.
The cross-plugin fail-closed boundary is preserved: two distinct plugins
both calling Restrict still aborts startup (multiple_restrict_plugins).
Single-Rule behaviour is fully backward compatible -- the rejection
reason_code / rule_name / envelope shape are byte-for-byte unchanged;
multi-rule rejection surfaces the aggregate reason_code no_matching_rule.
- engine: New keeps single-rule compat, add NewSet for OR over rules
- resolver: dedupe by owner (one plugin may contribute many rules),
return []*Rule; yaml gains a top-level rules: list
- registrar/builder/staging: Restrict may be called more than once;
retire the double_restrict error
- config policy show / config plugins show: emit a rules array
- inventory: PluginEntry.Rules is now a slice (fixes last-rule-wins
overwrite when a plugin contributes multiple rules)
* fix(platform): clone rules in Builder.Restrict and inventory snapshot
Address review feedback. Builder.Restrict stored the caller's *Rule
directly, so reusing and mutating one Rule object across multiple
Restrict calls collapsed entries to the last mutation; clone the rule and
its slices on append, mirroring the staging registrar.
BuildInventory likewise reused the source Allow/Deny/Identities slices;
copy them when building the RuleView snapshot instead of relying on
cloneInventory downstream.
Add a regression test: reusing and mutating one Rule across two Restrict
calls now yields two independent rules.
* fix(platform): skip yaml when a plugin owns policy; reject empty rules list
Two policy-config robustness fixes from review:
- A malformed ~/.lark-cli/policy.yml could abort a plugin-governed
binary. applyUserPolicyPruning read yaml before resolving, and
build.go fail-closes on any policy error when a plugin is present.
Plugin rules shadow yaml anyway, so skip reading yaml entirely when a
plugin contributed rules -- an unrelated broken file on the user's
machine can no longer lock the CLI.
- A present-but-empty "rules: []" collapsed to a single all-zero Rule
that allows every annotated command ("looks like policy, enforces
almost nothing"). yaml.Parse now distinguishes absent from
present-but-empty (Rules is a pointer) and rejects the empty list.
Add regression tests for both.
|
||
|
|
176d452cc1 |
feat: add agent header support (#1158)
* feat: add agent header support |