@fateforge/wechat-mp-cli 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.agent/AGENT.md CHANGED
@@ -34,7 +34,7 @@ Run in order; close out each step against the matching spec's checklist:
34
34
  2. **Define the contract** (→ CLI-SPEC.md): implement the envelope, exit-code mapping, and error taxonomy *before* the first command. This is the foundation, not an afterthought.
35
35
  3. **Build the self-description set** (→ CLI-SPEC.md §11): `reference` / `context` / `doctor` / `changelog`. `changelog` is derived from CHANGELOG.md and embedded at build time.
36
36
  4. **Implement commands**: query commands support `--fields` / `--compact` / pagination; write commands go through dry-run/confirm.
37
- 5. **Evaluate optional patterns** (→ CLI-SPEC.md §15, as needed): tokens expire? → credential lifecycle; long-running jobs? → async jobs; QR/captcha/approval? → human-in-the-loop. Do it only if you need it.
37
+ 5. **Evaluate optional patterns** (→ CLI-SPEC.md §16, as needed): tokens expire? → credential lifecycle; long-running jobs? → async jobs; QR/captcha/approval? → human-in-the-loop. Do it only if you need it.
38
38
  6. **Set the security tier** (→ SEC-SPEC.md): classify T0/T1/T2, then apply injection defense, least privilege, credential-at-rest, and supply chain by tier.
39
39
  7. **Write the Skill** (→ SKILL-SPEC.md): frontmatter (with `requires.bins` + `min_version`), trigger list, error decision tree, usage playbooks.
40
40
  8. **Set up distribution** (→ shared REPO-SPEC.md §4b): npm wrapper (`scripts/{run,prepare-npm-platform-packages}.js`), binary not committed.
@@ -34,7 +34,7 @@
34
34
  2. **定契约**(→ CLI-SPEC):先实现 envelope、exit code 映射、错误分类,再写第一个命令。这是地基,不要后补。
35
35
  3. **建自描述四件套**(→ CLI-SPEC §11):`reference` / `context` / `doctor` / `changelog`。`changelog` 从 CHANGELOG.md 派生、构建时嵌入。
36
36
  4. **实现命令**:查询命令支持 `--fields` / `--compact` / 分页;写命令走 dry-run/confirm。
37
- 5. **评估可选模式**(→ CLI-SPEC §15,按需):令牌会过期?→ 凭证生命周期;有长任务?→ 异步 job;要扫码/验证码/审批?→ 人工介入。用得上才做,用不上跳过。
37
+ 5. **评估可选模式**(→ CLI-SPEC §16,按需):令牌会过期?→ 凭证生命周期;有长任务?→ 异步 job;要扫码/验证码/审批?→ 人工介入。用得上才做,用不上跳过。
38
38
  6. **定安全档**(→ SEC-SPEC):先判 T0/T1/T2 风险档,按档套用注入防护、最小权限、凭证落盘、供应链。
39
39
  7. **写 Skill**(→ SKILL-SPEC):frontmatter(含 `requires.bins` + `min_version`)、触发清单、错误决策树、用法剧本。
40
40
  8. **配分发**(→ 共享 REPO-SPEC §4b):npm 壳(`scripts/{run,prepare-npm-platform-packages}.js`),二进制不入库。
@@ -123,7 +123,7 @@ Conventions:
123
123
  | 6 | Precondition conflict or invalid token | re-read state, then retry |
124
124
  | 7 | Retryable transient error (network/rate-limit/server) | back off and retry |
125
125
  | 8 | Timeout | back off and retry |
126
- | 9 | Human action required (see §15.3, optional) | relay to the user, run `resume` once done |
126
+ | 9 | Human action required (see §16.3, optional) | relay to the user, run `resume` once done |
127
127
 
128
128
  Error codes and exit codes must align:
129
129
 
@@ -134,7 +134,8 @@ Error codes and exit codes must align:
134
134
  - `E_CONFLICT` -> 6
135
135
  - `E_NETWORK` / `E_RATE_LIMITED` / `E_SERVER` -> 7
136
136
  - `E_TIMEOUT` -> 8
137
- - `E_HUMAN_REQUIRED` -> 9 (optional, only when §15.3 is enabled)
137
+ - `E_INTEGRITY` -> 1 (release integrity failure: missing/invalid signature or checksum mismatch; **non-retryable**, see §14)
138
+ - `E_HUMAN_REQUIRED` -> 9 (optional, only when §16.3 is enabled)
138
139
 
139
140
  When the failure comes from an upstream HTTP call, map the status onto the
140
141
  taxonomy so the agent can tell failure modes apart from `error.code` +
@@ -635,27 +636,150 @@ Version notification contract:
635
636
 
636
637
  Release verification baseline:
637
638
 
638
- - Verify the archive/package against `checksums.txt`; checksum mismatch, missing
639
- checksum file, or missing archive entry fails closed.
640
- - Signed releases should sign `checksums.txt` with Sigstore/Cosign keyless
641
- signing from the tagged GitHub Actions release workflow. Verifiers should
642
- validate the bundle against the expected repository workflow identity and the
643
- GitHub OIDC issuer.
644
- - Update results carry `signature_status` (a short string describing where
645
- release integrity verification happened: e.g. `verified`, `not_checked`,
646
- `handled_by_npm_installer`, `manual_release_verification_required`) and
647
- `signature_verified` (true only when local Sigstore verification actually
648
- ran and succeeded). Never imply checksum verification is a signature.
639
+ - **Mandatory signature verification, no skip path**: the binary self-update path
640
+ MUST verify the Sigstore signature on `checksums.txt` in-process, then verify
641
+ the archive SHA256 against it. A missing signature bundle, a signature that does
642
+ not verify, or a checksum mismatch all fail closed — there is no "can't verify,
643
+ proceed anyway" degradation. The whole chain surfaces `E_INTEGRITY` (exit 1,
644
+ non-retryable): a forged or corrupt release is not a transient blip to retry.
645
+ - **Verifier embedded, no user-environment dependency**: verification happens
646
+ inside the tool binary (Go via `sigstore-go`, Python inside the frozen binary
647
+ via `sigstore`) with **no external cosign** and nothing pre-installed on the
648
+ machine. The TUF trust root is bootstrapped from the library's embedded
649
+ `root.json`, not fetched on first-use trust (TOFU).
650
+ - **New bundle format**: the signing side produces a Sigstore protobuf bundle
651
+ (`checksums.txt.sigstore.json`) via `cosign sign-blob --new-bundle-format`, which
652
+ the in-process verifier consumes; the legacy cosign bundle format is not accepted.
653
+ - **Identity binding**: verifiers bind the certificate SAN to this repo's tagged
654
+ release workflow (`…/release.yml@refs/tags/v*`, anchored `^…$`) and validate the
655
+ GitHub OIDC issuer. When the target tag is known, pin the exact identity (stronger
656
+ than a regexp).
657
+ - **Cross-language parity**: Go binaries and Python frozen binaries follow the same
658
+ self-update contract — download archive → in-process signature verify → checksum
659
+ → replace binary. Package managers do not own integrity.
660
+ - Update results carry `signature_status` (`verified` on success; any failure exits
661
+ via the error envelope) and `signature_verified` (true only when in-process
662
+ Sigstore verification actually ran and succeeded). Never imply checksum
663
+ verification is a signature.
649
664
 
650
665
  - After `update --confirm <token>` succeeds, return `previous_version` and `current_version` in `data`.
651
666
  - Also hint in the result: `run "changelog --since <previous_version>" to see what changed`.
652
667
  - Agent convention: after self-update, before continuing, read `changelog --since <old version>` (see the SKILL-SPEC recipe).
653
668
 
654
- ## 15. Optional patterns (enable as needed)
669
+ ## 15. Batch operations
670
+
671
+ Many write workflows need to act on a batch of objects in one call (close many issues, send to many openids, run one SQL across many instances). A batch command is still **one** agent-facing command with **one** envelope, **one** confirm token, and **one** aggregated result — never a loop the agent has to drive. The contract below is identical whether the batch is served by a native upstream bulk endpoint (class A) or by a client-side loop (class B); the agent must not be able to tell which.
672
+
673
+ ### 15.1 Plural inputs
674
+
675
+ - Batch targets use a plural flag: `--ids`, `--symbols`, `--instances`, `--openids`, etc.
676
+ - Each plural flag accepts both **comma-separated** (`--ids 1,2,3`) and **repeatable** (`--ids 1 --ids 2 --ids 3`) forms; the two are equivalent and may be mixed.
677
+ - A single value degrades gracefully: `--ids 1` is a valid batch of one, same envelope as a batch of many. Where a singular flag (`--symbol`) already exists, keep it as a compatibility alias of the plural and declare it deprecated in reference; do not run two divergent code paths.
678
+ - De-duplicate targets before executing and preserve input order in the result `items[]` so the agent can zip results back to inputs deterministically.
679
+ - An empty target list is a usage error (`E_VALIDATION`, exit 2), not a silent no-op.
680
+
681
+ ### 15.2 Dry-run summary for a batch
682
+
683
+ `--dry-run` on a batch returns **what will happen to N objects** before any write, plus a single `confirm_token` that covers the whole batch:
684
+
685
+ ```json
686
+ {
687
+ "ok": true,
688
+ "schema_version": "1.0",
689
+ "data": {
690
+ "preview": {
691
+ "action": "close",
692
+ "total": 3,
693
+ "targets": ["1042", "1043", "1044"],
694
+ "changes": [
695
+ { "action": "close", "resource": "issue", "id": "1042" },
696
+ { "action": "close", "resource": "issue", "id": "1043" },
697
+ { "action": "close", "resource": "issue", "id": "1044" }
698
+ ]
699
+ },
700
+ "confirm_token": "ct_9f2a...",
701
+ "expires_at": "2026-06-15T12:00:00Z"
702
+ },
703
+ "meta": { "duration_ms": 0 }
704
+ }
705
+ ```
706
+
707
+ - The preview must state the operation and the full target set, so a human or agent can audit the blast radius before confirming.
708
+ - The token binds the **whole resolved target set** (plus command path, args, account, permission context per §7), so adding or removing a target invalidates it with `E_CONFLICT`.
709
+
710
+ ### 15.3 One confirm token covers the whole batch, consumed once
711
+
712
+ - A single `--confirm <token>` from the batch dry-run authorizes the entire batch; the agent does not confirm per item.
713
+ - The token is **single-use** exactly as in §9: it is fingerprinted and marked consumed before the write runs, and any replay is rejected with `E_CONFLICT` ("token already used; re-run `--dry-run`"). This reuses each repo's existing single-consumption confirm infrastructure — batch adds no new token mechanism.
714
+ - On a partial batch failure the token stays consumed; the agent re-runs `--dry-run` (which now resolves to the still-pending targets) rather than replaying the old token.
715
+
716
+ ### 15.4 Dangerous batches: `--dangerous` two-step gate
717
+
718
+ Irreversible or high-blast-radius batches — bulk `delete`, MR `merge`, mass send / broadcast — require an extra gate **on top of** dry-run → confirm:
719
+
720
+ - The command must be invoked with `--dangerous`; without it the command fails with `E_CONFIRMATION_REQUIRED` (exit 5) even when a valid confirm token is present.
721
+ - This is a two-step human-intent gate: `--dangerous` declares intent, the confirm token authorizes the specific resolved batch. Both are required; neither alone executes.
722
+ - Reference marks these commands `dangerous: true`, and their `examples[]` show the `--dangerous` form.
723
+ - Tools may layer stricter local policy on a dangerous batch (e.g. per-item confirm, default `--continue-on-error false`, or night-time dry-run-only); such overrides must be declared in reference, not hidden.
724
+
725
+ ### 15.5 Per-item aggregated result, no whole-batch rollback
726
+
727
+ Batch results aggregate per item. A partial failure does **not** roll back succeeded items:
728
+
729
+ ```json
730
+ {
731
+ "ok": true,
732
+ "schema_version": "1.0",
733
+ "data": {
734
+ "items": [
735
+ { "target": "1042", "ok": true },
736
+ { "target": "1043", "ok": true },
737
+ {
738
+ "target": "1044",
739
+ "ok": false,
740
+ "error": { "code": "E_NOT_FOUND", "retryable": false }
741
+ }
742
+ ],
743
+ "summary": { "total": 3, "succeeded": 2, "failed": 1 }
744
+ },
745
+ "meta": { "duration_ms": 0 }
746
+ }
747
+ ```
748
+
749
+ - Each `items[]` entry carries `target` (the input identifier — `id`, `symbol`, `instance`, …; use the natural key, not an array index), `ok`, and on failure `error` with the same `{ code, retryable }` taxonomy as the top-level envelope (§6).
750
+ - `summary` always reports `{ total, succeeded, failed }`. The counts must equal the item tally.
751
+ - Top-level `ok` is `true` when the batch executed and produced a result, even with per-item failures; per-item status lives in `items[]`. Reserve top-level `ok: false` for a batch that could not run at all (bad args, auth, no targets). Do not hide other items' status because one failed (consistent with §9).
752
+ - `--continue-on-error` controls whether the batch keeps going after the first item failure; **default `true`** (best-effort, finish the batch). Set `--continue-on-error false` to stop at the first failure — already-applied items stay applied (no rollback), and `summary` reflects only attempted items, with the unattempted remainder reported (e.g. `skipped`) so the agent can resume. Dangerous batches may flip the default to `false`; declare it in reference.
753
+
754
+ ### 15.6 Upstream caps: client-side auto-chunking
755
+
756
+ When the native bulk endpoint has a per-call limit, the command splits the batch into chunks and submits them sequentially, presenting a single command to the agent:
757
+
758
+ - Known caps: Jira `/issue/bulk`, agile `backlog/issue` and `sprint/{id}/issue` ≤ 50; WeChat openid batches ≤ 100, mass-send openid lists per upstream limit. The command must chunk to the cap; it must not pass a too-large batch straight through and let the upstream 400.
759
+ - Chunking is invisible in the contract: still one envelope, one `confirm_token`, one aggregated `items[]`/`summary` across all chunks.
760
+ - A chunk-level failure is mapped back onto the affected `items[]`; one failed chunk does not fail the whole command (subject to `--continue-on-error`).
761
+ - Keep the chunk size in one shared helper per tool so the cap cannot drift between commands sharing an endpoint.
762
+
763
+ ### 15.7 A/B classes, one external contract
764
+
765
+ - **Class A** uses a native upstream bulk endpoint (true server-side batch, possibly atomic per the upstream).
766
+ - **Class B** loops client-side over single-target calls because no native bulk exists.
767
+ - The external contract is identical for both: plural inputs, dry-run summary, single confirm token, dangerous gate, aggregated `items[]`/`summary`, and `--continue-on-error`. The agent cannot and need not tell A from B.
768
+ - Atomicity is **not** part of the external contract. A class-B (or capped, chunked class-A) batch must not claim upstream atomicity; where the upstream genuinely is non-atomic or order-unstable, say so in the output schema / reference rather than implying a transaction.
769
+
770
+ ### 15.8 Self-description for batch commands
771
+
772
+ Every batch command carries a real `output_schema` and runnable `examples[]` (per §11):
773
+
774
+ - The schema declares the `items[]` shape (`target`, `ok`, `error{code,retryable}`) and the `summary{total,succeeded,failed}` shape, with attacker-controllable keys listed in `untrusted_fields` (`_untrusted`).
775
+ - `examples[]` show the plural-input dry-run then confirm pair; dangerous batches include `--dangerous`.
776
+ - These must pass the reference guard (non-empty schema + at least one example per leaf command) and count toward FCC (§13) like any other public behavior.
777
+
778
+ ## 16. Optional patterns (enable as needed)
655
779
 
656
780
  These three patterns are **not for everyone**: implement them if your tool needs them, ignore them otherwise — zero overhead. They let the spec scale with tool complexity — a simple tool stays light, a complex tool need not reinvent the wheel. Each is marked "when applicable."
657
781
 
658
- ### 15.1 Credential lifecycle (when tokens expire)
782
+ ### 16.1 Credential lifecycle (when tokens expire)
659
783
 
660
784
  **When applicable**: credentials are not static but expire / need refresh — OAuth access_token (WeChat Official Account ~2h), cookie / session (Xiaohongshu), temporary STS credentials, etc. Tools with static username/password skip this section.
661
785
 
@@ -677,7 +801,7 @@ These three patterns are **not for everyone**: implement them if your tool needs
677
801
  - `doctor` adds a `check: "credentials"` item; for near-expiry give `warn` + a renew `fix`.
678
802
  - Refresh tokens and secrets are always redacted — never in stdout / stderr / details.
679
803
 
680
- ### 15.2 Async job lifecycle (long jobs: submit -> poll -> fetch result)
804
+ ### 16.2 Async job lifecycle (long jobs: submit -> poll -> fetch result)
681
805
 
682
806
  **When applicable**: the operation can't return a result synchronously — async SQL execution / approval (Archery), bulk send, scrape/crawl jobs, large exports. Commands that return results synchronously skip this section.
683
807
 
@@ -702,7 +826,7 @@ These three patterns are **not for everyone**: implement them if your tool needs
702
826
  - A `failed` result uses the standard error envelope; `retryable` indicates whether the whole job can be retried.
703
827
  - Submission of a write-type long job still goes through `dry-run → confirm`; the `job_id` is created only after confirm.
704
828
 
705
- ### 15.3 Human-in-the-loop checkpoints (when a human must scan / solve captcha / approve)
829
+ ### 16.3 Human-in-the-loop checkpoints (when a human must scan / solve captcha / approve)
706
830
 
707
831
  **When applicable**: a step mid-flow must be completed by a human — QR login / captcha (Xiaohongshu), approver sign-off (Archery), secondary confirmation. Fully automated tools skip this section.
708
832
 
@@ -726,7 +850,7 @@ These three patterns are **not for everyone**: implement them if your tool needs
726
850
  - `details.action` is a stable enum describing what the human must do; `details.resume` gives the command to continue after the human is done.
727
851
  - Agent convention: on `E_HUMAN_REQUIRED` → relay `message` and the required action to the user → wait for them → run `resume`; do not auto-retry.
728
852
 
729
- ## 16. Design checklist
853
+ ## 17. Design checklist
730
854
 
731
855
  > Items marked `(optional)` only apply when the corresponding optional pattern is enabled.
732
856
 
@@ -748,6 +872,11 @@ These three patterns are **not for everyone**: implement them if your tool needs
748
872
  - [ ] (with self-update) post-update returns previous/current version and hints to read changelog
749
873
  - [ ] Query commands support `fields` / `compact`
750
874
  - [ ] List commands support pagination or explicitly state none is needed
875
+ - [ ] Batch commands take plural inputs (`--ids`/`--symbols`/…), comma-separated or repeatable, single value degrades
876
+ - [ ] Batch dry-run summarizes the full target set; one confirm token covers and is consumed once for the whole batch
877
+ - [ ] Dangerous batches (delete / merge / mass-send) require the `--dangerous` two-step gate
878
+ - [ ] Batch results aggregate `items[].{target,ok,error{code,retryable}}` + `summary{total,succeeded,failed}`, no whole-batch rollback, `--continue-on-error` default true
879
+ - [ ] Capped upstream bulk auto-chunks client-side under one command; A/B classes share one external contract; batch commands ship real `output_schema` + `examples`
751
880
  - [ ] Functional Contract Coverage is 100% for public README / Skill / reference / help / context / doctor / changelog / update behavior
752
881
  - [ ] Stable releases have recorded live smoke/E2E evidence; otherwise the tool declares `beta`
753
882
  - [ ] All times ISO 8601 UTC
@@ -120,7 +120,7 @@
120
120
  | 6 | 前置条件冲突或 token 失效 | 重新读取状态后重试 |
121
121
  | 7 | 可重试瞬时错误(网络/限流/服务端) | 退避后重试 |
122
122
  | 8 | 超时 | 退避后重试 |
123
- | 9 | 需人工介入(见 §15.3,可选) | 转述给用户,待其完成后跑 `resume` |
123
+ | 9 | 需人工介入(见 §16.3,可选) | 转述给用户,待其完成后跑 `resume` |
124
124
 
125
125
  错误码与 exit code 必须一致:
126
126
 
@@ -131,7 +131,8 @@
131
131
  - `E_CONFLICT` -> 6
132
132
  - `E_NETWORK` / `E_RATE_LIMITED` / `E_SERVER` -> 7
133
133
  - `E_TIMEOUT` -> 8
134
- - `E_HUMAN_REQUIRED` -> 9(可选,仅启用 §15.3 时)
134
+ - `E_INTEGRITY` -> 1(发布完整性失败:签名缺失/无效或 checksum 不符;**非重试**,见 §14)
135
+ - `E_HUMAN_REQUIRED` -> 9(可选,仅启用 §16.3 时)
135
136
 
136
137
  当错误来自上游 HTTP 调用时,按状态码映射到错误码,让 agent 能从 `error.code` +
137
138
  `retryable` 区分失败类型——不要把所有 4xx 都塌缩成 `E_NETWORK`:
@@ -583,19 +584,131 @@ update 必须遵守的契约:
583
584
 
584
585
  release 校验基线:
585
586
 
586
- - `checksums.txt` 校验归档/包;checksum 不匹配、缺失 checksum 文件、或缺少当前归档条目,都必须失败关闭。
587
- - 已签名 release 应由 tagged GitHub Actions release workflow 使用 Sigstore/Cosign keyless 模式签署 `checksums.txt`。验证端应绑定到预期仓库 workflow 身份和 GitHub OIDC issuer。
588
- - update 结果携带 `signature_status`(一个短字符串说明发布完整性在哪里被验证:如 `verified`、`not_checked`、`handled_by_npm_installer`、`manual_release_verification_required`)与 `signature_verified`(仅当本地 Sigstore 验证真实执行且成功时为 true)。不能把 checksum 校验伪装成签名校验。
587
+ - **强制签名验证,无跳过路径**:二进制自更新路径必须在进程内验证 `checksums.txt` Sigstore 签名,再用它校验归档 SHA256。签名 bundle 缺失、签名验不过、checksum 不符,一律**失败关闭**,不存在"验不了就放行"的降级。整条链对外返回 `E_INTEGRITY`(exit 1,非重试)——伪造或损坏的发布不该被当成可重试的瞬时错误。
588
+ - **验证器内置、不依赖用户环境**:验证在工具二进制内完成(Go `sigstore-go`,Python 冻结二进制内用 `sigstore`),**不外挂 cosign**,不依赖机器上预装任何东西。TUF 信任根从库内嵌的 `root.json` 引导(不是 TOFU 现拉现信)。
589
+ - **新版 bundle 格式**:签名侧用 `cosign sign-blob --new-bundle-format` 产出 Sigstore protobuf bundle(`checksums.txt.sigstore.json`),与进程内验证器对齐;旧版 cosign bundle 格式不被接受。
590
+ - **身份绑定**:验证端把证书 SAN 绑定到本仓库的 tagged release workflow(`…/release.yml@refs/tags/v*`,锚定 `^…$`)并校验 GitHub OIDC issuer。已知目标 tag 时可绑精确身份,强于正则。
591
+ - **跨语言统一**:Go 二进制与 Python 冻结二进制走同一套自更新契约——都是"下载归档 → 进程内验签 → checksum → 替换二进制",包管理器不参与完整性。
592
+ - update 结果携带 `signature_status`(成功即 `verified`;异常一律走 error envelope 中止)与 `signature_verified`(仅当进程内 Sigstore 验证真实执行且成功时为 true)。不能把 checksum 校验伪装成签名校验。
589
593
 
590
594
  - `update --confirm <token>` 成功后,结果 `data` 中返回 `previous_version` 与 `current_version`。
591
595
  - 同时在结果中提示:`run "changelog --since <previous_version>" to see what changed`。
592
596
  - Agent 约定:自更新后、继续干活前,先读 `changelog --since <旧版本>`(见 SKILL-SPEC 配方)。
593
597
 
594
- ## 15. 可选模式(按需启用)
598
+ ## 15. 批量操作(Batch operations)
599
+
600
+ 很多写操作的现实工作流需要一次对**一批**对象执行(关一批 issue、群发给一批 openid、对一批实例跑同一条 SQL)。批量命令对 Agent 仍是**一条**命令、**一个** envelope、**一个** confirm token、**一份**聚合结果——绝不是要 Agent 自己驱动的循环。下面的契约无论该批量由上游原生 bulk 端点服务(A 类)还是客户端循环实现(B 类)都完全一致;Agent 不应、也不需要分辨是哪一类。
601
+
602
+ ### 15.1 复数入参
603
+
604
+ - 批量目标用复数参数:`--ids`、`--symbols`、`--instances`、`--openids` 等。
605
+ - 每个复数参数同时接受 **comma-separated**(`--ids 1,2,3`)与 **repeatable**(`--ids 1 --ids 2 --ids 3`)两种写法;二者等价,可混用。
606
+ - 单值优雅退化:`--ids 1` 是合法的「批量一个」,与批量多个同形 envelope。已存在单数参数(`--symbol`)的,保留为复数的兼容别名并在 reference 标 deprecated,不维护两条分叉代码路径。
607
+ - 执行前对目标去重,并在结果 `items[]` 中保持输入顺序,使 Agent 能确定性地把结果对回输入。
608
+ - 空目标列表是用法错误(`E_VALIDATION`,exit 2),不是静默 no-op。
609
+
610
+ ### 15.2 批量的 dry-run 汇总
611
+
612
+ 批量的 `--dry-run` 在任何写之前返回**将对 N 个对象做什么**,并给出一个覆盖整批的 `confirm_token`:
613
+
614
+ ```json
615
+ {
616
+ "ok": true,
617
+ "schema_version": "1.0",
618
+ "data": {
619
+ "preview": {
620
+ "action": "close",
621
+ "total": 3,
622
+ "targets": ["1042", "1043", "1044"],
623
+ "changes": [
624
+ { "action": "close", "resource": "issue", "id": "1042" },
625
+ { "action": "close", "resource": "issue", "id": "1043" },
626
+ { "action": "close", "resource": "issue", "id": "1044" }
627
+ ]
628
+ },
629
+ "confirm_token": "ct_9f2a...",
630
+ "expires_at": "2026-06-15T12:00:00Z"
631
+ },
632
+ "meta": { "duration_ms": 0 }
633
+ }
634
+ ```
635
+
636
+ - preview 必须说明操作与完整目标集合,让人或 Agent 在确认前审计影响范围(blast radius)。
637
+ - token 绑定**整个已解析的目标集合**(外加命令路径、参数、账号、权限上下文,见 §7),增减任一目标即令其失效并返回 `E_CONFLICT`。
638
+
639
+ ### 15.3 单个 confirm token 覆盖整批,且单次消费
640
+
641
+ - 批量 dry-run 给出的单个 `--confirm <token>` 授权整批;Agent 不逐项确认。
642
+ - token **单次消费**,与 §9 完全一致:写执行前记录指纹并标记已消费,任何重放以 `E_CONFLICT` 拒绝(「token 已用过,请重新 `--dry-run`」)。这沿用各仓现有的 confirm 单次消费基础设施——批量不新增任何 token 机制。
643
+ - 批量部分失败后,token 仍保持已消费;Agent 重新 `--dry-run`(此时只解析到仍待处理的目标),而不是重放旧 token。
644
+
645
+ ### 15.4 危险批量:`--dangerous` 两步闸门
646
+
647
+ 不可逆或高影响范围的批量——批量 `delete`、MR `merge`、群发 / 广播——在 dry-run → confirm **之上**再加一道闸门:
648
+
649
+ - 命令必须带 `--dangerous` 调用;不带时即使持有有效 confirm token,命令也以 `E_CONFIRMATION_REQUIRED`(exit 5)失败。
650
+ - 这是两步人意闸门:`--dangerous` 声明意图,confirm token 授权具体已解析的那一批。两者缺一不可,任何一个单独都不执行。
651
+ - reference 把这些命令标 `dangerous: true`,其 `examples[]` 展示 `--dangerous` 形式。
652
+ - 工具可在危险批量上叠加更严格的本地策略(如逐项 confirm、默认 `--continue-on-error false`、夜间仅 dry-run);此类覆盖必须在 reference 声明,不得暗藏。
653
+
654
+ ### 15.5 逐项聚合结果,不整体回滚
655
+
656
+ 批量结果按项聚合。部分失败**不**回滚已成功的项:
657
+
658
+ ```json
659
+ {
660
+ "ok": true,
661
+ "schema_version": "1.0",
662
+ "data": {
663
+ "items": [
664
+ { "target": "1042", "ok": true },
665
+ { "target": "1043", "ok": true },
666
+ {
667
+ "target": "1044",
668
+ "ok": false,
669
+ "error": { "code": "E_NOT_FOUND", "retryable": false }
670
+ }
671
+ ],
672
+ "summary": { "total": 3, "succeeded": 2, "failed": 1 }
673
+ },
674
+ "meta": { "duration_ms": 0 }
675
+ }
676
+ ```
677
+
678
+ - 每个 `items[]` 项带 `target`(输入标识——`id`、`symbol`、`instance`……;用自然键,不用数组下标)、`ok`,失败时带 `error`,其 `{ code, retryable }` 分类与顶层 envelope(§6)一致。
679
+ - `summary` 始终报告 `{ total, succeeded, failed }`,计数必须与 item 实际数量相等。
680
+ - 只要批量执行并产出结果,顶层 `ok` 即为 `true`,即便存在逐项失败;逐项状态在 `items[]` 里。顶层 `ok: false` 仅保留给整批根本无法执行的情况(参数错、鉴权、无目标)。不要因为单项失败而隐藏其他项状态(与 §9 一致)。
681
+ - `--continue-on-error` 控制首个失败后是否继续;**默认 `true`**(尽力而为,跑完整批)。设 `--continue-on-error false` 在首个失败处停止——已应用的项保持已应用(不回滚),`summary` 只反映已尝试的项,未尝试的剩余项要报告出来(如 `skipped`),让 Agent 可续跑。危险批量可把默认翻成 `false`,需在 reference 声明。
682
+
683
+ ### 15.6 上游有上限:客户端自动分批
684
+
685
+ 当原生 bulk 端点有单次调用上限时,命令把批量切成多块顺序提交,对 Agent 仍呈现为一条命令:
686
+
687
+ - 已知上限:Jira `/issue/bulk`、agile `backlog/issue` 与 `sprint/{id}/issue` ≤ 50;微信 openid 批量 ≤ 100,群发 openid 列表按上游上限。命令必须按上限分批,不得把超限批量直接透传让上游 400。
688
+ - 分批在契约里不可见:跨所有块仍是一个 envelope、一个 `confirm_token`、一份聚合 `items[]`/`summary`。
689
+ - 块级失败映射回受影响的 `items[]`;单块失败不令整条命令失败(受 `--continue-on-error` 约束)。
690
+ - 把分批大小收敛到每个工具的一个共享 helper 里,避免共用同一端点的命令间上限漂移。
691
+
692
+ ### 15.7 A/B 两类,对外契约一致
693
+
694
+ - **A 类**用上游原生 bulk 端点(真服务端批量,上游可能原子)。
695
+ - **B 类**因无原生 bulk,客户端对单目标调用做循环。
696
+ - 对外契约对两类完全一致:复数入参、dry-run 汇总、单 confirm token、危险闸门、聚合 `items[]`/`summary`、`--continue-on-error`。Agent 无法也无需分辨 A 与 B。
697
+ - 原子性**不**是对外契约的一部分。B 类(或被分批的、有上限的 A 类)批量不得宣称上游原子;上游确实非原子或顺序不稳定时,在 output schema / reference 里如实说明,而不是暗示事务语义。
698
+
699
+ ### 15.8 批量命令的自描述
700
+
701
+ 每个批量命令带真实的 `output_schema` 与可运行 `examples[]`(见 §11):
702
+
703
+ - schema 声明 `items[]` 形状(`target`、`ok`、`error{code,retryable}`)与 `summary{total,succeeded,failed}` 形状,攻击者可控的键列入 `untrusted_fields`(`_untrusted`)。
704
+ - `examples[]` 展示复数入参的 dry-run 再 confirm 这一对;危险批量带 `--dangerous`。
705
+ - 这些必须通过 reference 守卫(每个叶子命令非空 schema + 至少一条 example),并像其他公开行为一样计入 FCC(§13)。
706
+
707
+ ## 16. 可选模式(按需启用)
595
708
 
596
709
  以下三种模式**不是人人必做**:工具用得上就照这里实现,用不上就忽略,零负担。它们让规范随工具复杂度伸缩——简单工具保持轻,复杂工具不必重新发明轮子。每条都标了「何时适用」。
597
710
 
598
- ### 15.1 凭证生命周期(令牌会过期时)
711
+ ### 16.1 凭证生命周期(令牌会过期时)
599
712
 
600
713
  **何时适用**:凭证不是静态的,而是会过期 / 需刷新的——OAuth access_token(微信公众号约 2h)、cookie / session(小红书)、临时 STS 凭证等。静态用户名密码的工具跳过本节。
601
714
 
@@ -617,7 +730,7 @@ release 校验基线:
617
730
  - `doctor` 增一项 `check: "credentials"`,对临近过期给 `warn` + 续期 `fix`。
618
731
  - 刷新令牌、secret 一律脱敏,绝不出现在 stdout / stderr / details。
619
732
 
620
- ### 15.2 异步任务生命周期(长任务:提交 -> 轮询 -> 取结果)
733
+ ### 16.2 异步任务生命周期(长任务:提交 -> 轮询 -> 取结果)
621
734
 
622
735
  **何时适用**:操作不能同步返回结果——SQL 异步执行 / 审批(Archery)、批量群发、采集 / 爬取任务、大导出。同步即得结果的命令跳过本节。
623
736
 
@@ -642,7 +755,7 @@ release 校验基线:
642
755
  - `failed` 的结果用标准 error envelope,`retryable` 指明能否重试整个任务。
643
756
  - 写类长任务的提交仍走 `dry-run → confirm`;confirm 后才落 `job_id`。
644
757
 
645
- ### 15.3 人工介入检查点(需要人来扫码 / 验证码 / 审批时)
758
+ ### 16.3 人工介入检查点(需要人来扫码 / 验证码 / 审批时)
646
759
 
647
760
  **何时适用**:流程中途必须由人完成某步——扫码登录 / 验证码(小红书)、审批人放行(Archery)、二次确认等。全自动工具跳过本节。
648
761
 
@@ -666,7 +779,7 @@ release 校验基线:
666
779
  - `details.action` 用稳定枚举说明需要人做什么,`details.resume` 给出人工完成后的续跑命令。
667
780
  - Agent 约定:收到 `E_HUMAN_REQUIRED` → 向用户转述 `message` 与所需动作 → 等用户完成 → 跑 `resume`,不自动重试。
668
781
 
669
- ## 16. 设计检查清单
782
+ ## 17. 设计检查清单
670
783
 
671
784
  > 标 `(可选)` 的仅当对应可选模式启用时才需勾。
672
785
 
@@ -688,6 +801,11 @@ release 校验基线:
688
801
  - [ ] (含 self-update 时)更新后回传 previous/current 版本并提示读 changelog
689
802
  - [ ] 查询命令支持 `fields` / `compact`
690
803
  - [ ] 列表命令支持分页或明确说明无需分页
804
+ - [ ] 批量命令用复数入参(`--ids`/`--symbols`/…),comma-separated 或 repeatable,单值退化
805
+ - [ ] 批量 dry-run 汇总完整目标集;单个 confirm token 覆盖整批且单次消费
806
+ - [ ] 危险批量(delete / merge / 群发)需 `--dangerous` 两步闸门
807
+ - [ ] 批量结果聚合 `items[].{target,ok,error{code,retryable}}` + `summary{total,succeeded,failed}`,不整体回滚,`--continue-on-error` 默认 true
808
+ - [ ] 有上限的原生 bulk 由客户端自动分批、对外单条命令;A/B 两类对外契约一致;批量命令带真实 `output_schema` + `examples`
691
809
  - [ ] README / Skill / reference / help / context / doctor / changelog / update 中声明的公开行为达到 100% 功能契约覆盖率
692
810
  - [ ] Stable release 有真实环境 smoke/E2E 记录;否则工具声明为 `beta`
693
811
  - [ ] 所有时间为 ISO 8601 UTC
@@ -93,19 +93,24 @@ Fallback and channel rules:
93
93
  explicitly.
94
94
  - **Minimal memory residency**: discard after use, don't log, don't put in
95
95
  stdout/stderr.
96
- - Token acquire / refresh / expiry lifecycle is in `CLI-SPEC.md §15.1`; this section only covers "how to store static data at rest safely."
96
+ - Token acquire / refresh / expiry lifecycle is in `CLI-SPEC.md §16.1`; this section only covers "how to store static data at rest safely."
97
97
 
98
98
  ## 5. Supply chain (applies to anything distributed)
99
99
 
100
- - **Integrity verification**: install scripts and self-update commands pulling a
101
- binary must verify checksums, **hard-fail on mismatch**, and report signature
102
- verification status explicitly. A checksum proves bytes match a checksum file;
103
- it does not prove the checksum file came from the publisher.
104
- - **Signed release material**: release pipelines should sign `checksums.txt`
105
- with Sigstore/Cosign keyless signing from the tagged GitHub Actions release
106
- workflow, publishing the bundle alongside the checksum file. Verification must
107
- bind the signature to the expected repository workflow identity and GitHub OIDC
108
- issuer.
100
+ - **Integrity verification, mandatory and no-skip**: binary self-update MUST verify
101
+ the Sigstore signature on `checksums.txt` **in-process** (the verifier is embedded
102
+ in the tool binary Go via `sigstore-go`, Python inside the frozen binary via
103
+ `sigstore` with **no external cosign** and no user-environment dependency), then
104
+ verify the archive SHA256. A missing/invalid signature or a checksum mismatch
105
+ **fails closed** with no "can't verify, proceed anyway" degradation, surfacing
106
+ `E_INTEGRITY` (non-retryable). A checksum proves bytes match a checksum file; only
107
+ the signature proves the checksum file came from the publisher.
108
+ - **Signed release material**: release pipelines sign `checksums.txt` with
109
+ Sigstore/Cosign keyless signing from the tagged GitHub Actions release workflow
110
+ using `--new-bundle-format` (a Sigstore protobuf bundle the in-process verifier
111
+ consumes). Verification binds the signature to the expected repository workflow
112
+ identity (anchored `^…$`) and GitHub OIDC issuer; the TUF trust root is
113
+ bootstrapped from the library's embedded root, not TOFU.
109
114
  - **Dependency locking + audit**: commit a lockfile; CI runs `npm audit` / `pip-audit` and blocks high-severity dependencies.
110
115
  - **Traceable builds**: release artifacts are built by CI from tagged source, no hand-uploaded unknown binaries.
111
116
  - **No remote scripts in postinstall**: don't execute code freshly pulled from the network at install time.
@@ -116,8 +121,8 @@ Fallback and channel rules:
116
121
  |----------------|---------------|
117
122
  | Output redaction (password / token / cookie out of stdout·stderr·details·audit) | `CLI-SPEC.md §10` |
118
123
  | Write dry-run → confirm, token bound to operation | `CLI-SPEC.md §7` |
119
- | Credential acquire / refresh / expiry lifecycle | `CLI-SPEC.md §15.1` |
120
- | Human-in-the-loop (QR / captcha / approval) | `CLI-SPEC.md §15.3` |
124
+ | Credential acquire / refresh / expiry lifecycle | `CLI-SPEC.md §16.1` |
125
+ | Human-in-the-loop (QR / captcha / approval) | `CLI-SPEC.md §16.3` |
121
126
  | Skill permission tiers, only trusted-source Skills | `SKILL-SPEC.md` |
122
127
  | No committed secrets, third-party trademark notice, pre-publish check | `REPO-SPEC.md` (OPEN_SOURCE_CHECKLIST / NOTICE) |
123
128
 
@@ -84,12 +84,12 @@ Agent 侧约定(同时写进 SKILL-SPEC 的用法):
84
84
  的保护来自用户目录的默认 ACL,或者干脆没有秘密文件(即 keyring 模式)。除非
85
85
  显式设置 ACL,否则不要在 Windows 上声称「仅属主可读」。
86
86
  - **内存最小驻留**:用完即弃,不写日志、不进 stdout/stderr。
87
- - 令牌的获取 / 刷新 / 过期生命周期见 `CLI-SPEC.md §15.1`,本节只管「静态落盘怎么存才安全」。
87
+ - 令牌的获取 / 刷新 / 过期生命周期见 `CLI-SPEC.md §16.1`,本节只管「静态落盘怎么存才安全」。
88
88
 
89
89
  ## 5. 供应链(凡分发即适用)
90
90
 
91
- - **完整性校验**:安装脚本和自更新命令拉取二进制时必须校验 checksum,**不匹配硬失败**,并显式返回签名校验状态。checksum 只能证明字节与 checksum 文件一致,不能证明 checksum 文件来自发布者。
92
- - **签名发布材料**:release pipeline 应由 tagged GitHub Actions release workflow 使用 Sigstore/Cosign keyless 模式签署 `checksums.txt`,并把 bundle checksum 一起发布。验证时必须绑定到预期仓库 workflow 身份和 GitHub OIDC issuer。
91
+ - **完整性校验,强制且无跳过**:二进制自更新必须**在进程内**验证 `checksums.txt` 的 Sigstore 签名(验证器内置工具二进制,Go 用 `sigstore-go`、Python 冻结二进制内用 `sigstore`,**不外挂 cosign**、不依赖用户环境),再用它校验归档 SHA256。签名缺失/验不过/checksum 不符一律**失败关闭**,没有"验不了就放行"的降级;对外返回 `E_INTEGRITY`(非重试)。checksum 只能证明字节与 checksum 文件一致,签名才能证明 checksum 文件来自发布者。
92
+ - **签名发布材料**:release pipeline tagged GitHub Actions release workflow Sigstore/Cosign keyless 模式以 `--new-bundle-format` 签署 `checksums.txt`(产出 Sigstore protobuf bundle),与进程内验证器对齐。验证时绑定到预期仓库 workflow 身份(锚定 `^…$`)和 GitHub OIDC issuer;TUF 信任根从库内嵌 root 引导,不 TOFU
93
93
  - **依赖锁定 + 审计**:提交 lockfile;CI 跑 `npm audit` / `pip-audit` 一类,高危依赖阻断。
94
94
  - **构建可追溯**:发布产物由 CI 从打了 tag 的源码构建,不手工上传不明二进制。
95
95
  - **不在 postinstall 跑远程脚本**:安装期不执行从网络现拉的代码。
@@ -100,8 +100,8 @@ Agent 侧约定(同时写进 SKILL-SPEC 的用法):
100
100
  |--------|---------|
101
101
  | 输出脱敏(密码 / token / cookie 不入 stdout·stderr·details·audit) | `CLI-SPEC.md §10` |
102
102
  | 写操作 dry-run → confirm,token 绑定操作内容 | `CLI-SPEC.md §7` |
103
- | 凭证获取 / 刷新 / 过期生命周期 | `CLI-SPEC.md §15.1` |
104
- | 人工介入(扫码 / 验证码 / 审批) | `CLI-SPEC.md §15.3` |
103
+ | 凭证获取 / 刷新 / 过期生命周期 | `CLI-SPEC.md §16.1` |
104
+ | 人工介入(扫码 / 验证码 / 审批) | `CLI-SPEC.md §16.3` |
105
105
  | Skill 权限分层、仅用可信来源 Skill | `SKILL-SPEC.md` |
106
106
  | 不提交密钥、第三方商标声明、首推前体检 | `REPO-SPEC.md`(OPEN_SOURCE_CHECKLIST / NOTICE) |
107
107
 
package/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.0.4] - 2026-06-16
11
+
12
+ ### Changed
13
+
14
+ - `update` is rewritten from package-manager delegation (npm/go install) to a self-contained verified binary self-update: download the release archive + `checksums.txt` + Sigstore bundle, verify the signature **in-process** (embedded `sigstore-go`, embedded TUF root) against this repo's tagged release-workflow identity, verify the archive SHA256, and replace the running binary — no dependency on npm/go/pip being installed. Releases are signed with `cosign sign-blob --new-bundle-format`.
15
+
16
+ ### Security
17
+
18
+ - Verification is mandatory and fail-closed (no skip path); release-integrity failures return the non-retryable `E_INTEGRITY` code (exit 1) instead of a retryable network code.
19
+
10
20
  ## [1.0.3] - 2026-06-15
11
21
 
12
22
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fateforge/wechat-mp-cli",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "AI-native CLI for WeChat Official Account drafting, publishing, assets, comments, analytics, menus, users, and webhooks",
5
5
  "keywords": [
6
6
  "wechat",
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: wechat-mp-cli
3
- version: "1.0.3"
3
+ version: "1.0.4"
4
4
  description: "Use wechat-mp-cli when the user needs to configure, draft, upload assets for, or publish WeChat Official Account content through a stable AI-native CLI contract."
5
5
  license: MIT
6
6
  user-invocable: true
7
- metadata: {"requires":{"bins":["wechat-mp-cli"],"min_version":"1.0.3"}}
7
+ metadata: {"requires":{"bins":["wechat-mp-cli"],"min_version":"1.0.4"}}
8
8
  ---
9
9
 
10
10
  # wechat-mp-cli
@@ -13,6 +13,19 @@ Use this Skill for WeChat Official Account API workflows: account setup, API pro
13
13
 
14
14
  Do not use this Skill for personal WeChat chat automation, Mini Program development, browser-only manual publishing, or any attempt to bypass WeChat permissions, IP allowlists, credential gates, or user approval.
15
15
 
16
+ ```bash
17
+ # Please install wechat-mp-cli and use it for all WeChat Official Account operations going forward (JSON is the default output).
18
+ # Install CLI
19
+ npm install -g @fateforge/wechat-mp-cli
20
+
21
+ # Install CLI Skill (required) — copies into your agent-supported skills directory
22
+ npx skills add fatecannotbealtered/wechat-mp-cli -y -g
23
+
24
+ # Configure account and verify
25
+ wechat-mp-cli setup account add
26
+ wechat-mp-cli doctor --compact
27
+ ```
28
+
16
29
  ## First Step
17
30
 
18
31
  Before task commands, inspect the live binary and environment: