@acosmi/sdk-ts 2.0.0 → 2.1.0

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.
@@ -0,0 +1,831 @@
1
+ # @acosmi/sdk-ts 开发与发布手册
2
+
3
+ > 适用版本: v1.0.0+
4
+ > 最后更新: 2026-05-25
5
+
6
+ ## 1. 项目概览
7
+
8
+ `@acosmi/sdk-ts` 是 Acosmi 模型网关、SDK-facing Agent Run Gateway 与 compliance API 的 TypeScript SDK。**自 2026-05-22 起 TS SDK 成为主实现 / 事实标准**:Go SDK `acosmi-sdk-go` 暂停维护,本仓专注把 TS 做好做稳;待 TS 稳定后再从 TS 反向翻译补齐 Go。早期版本由 Go 端口而来,但布局与演进已不再受 Go 约束。
9
+
10
+ | 项目 | 值 |
11
+ | -------- | -- |
12
+ | 实现地位 | **主实现 / 事实标准**;Go SDK 暂停维护,后续从 TS 反向翻译 |
13
+ | 当前版本 | **2.0.1**(packaging fix — `package.json.files` 数组补齐两个 docs;上游 v2.0.0 为 Phase 3 复核 + 全量根治 BREAKING,含商品化 P1-P7 namespace 全量 + 律师 / 企业 OWNER 自查端点 + PII 真落盘加密链 + admin 写端点 HTTP 错误码契约) |
14
+ | 版本策略 | TS 独立演进;wire-format / API 不兼容变更升 major(v2.0.0 已发,见 §10 / §18) |
15
+ | 主消费者 | `crabcode` (Anthropic 格式)、`crabdesign` / `crabclaw` 等下游产品 |
16
+ | 长期保留 | `OpenAIAdapter` (P0 红线,`crabclaw` 暂不用 TS 但保留双格式等地位) |
17
+ | LICENSE | MIT — Copyright (c) 2026 Acosmi |
18
+
19
+ ## 2. 仓库与发布拓扑
20
+
21
+ ```
22
+ SDK 独立仓 (public) npm 公开包
23
+ /Users/fushihua/Desktop/Acosmi/acosmi-sdk-ts → npmjs.com/@acosmi/sdk-ts
24
+ └── github.com/acosmi/sdk-ts └── tag v* 触发 release workflow
25
+ ```
26
+
27
+ **铁律**:
28
+ - SDK 是独立发布物,提交与 tag 只在 `acosmi-sdk-ts` 仓库内完成。
29
+ - 不把外层 monorepo 的 Java / Go / CFCA real provider 改动带入 SDK commit。
30
+ - 公开仓 history 和提交信息必须保持产品/SDK 语义,不包含协作工具或开发过程元数据。
31
+ - `docs/` 可放公开用户文档;内部 audit、证书材料、provider runbook 不进公开仓。
32
+
33
+ ## 3. 开发环境
34
+
35
+ - Node.js **≥18**(package.json `engines.node` 公开承诺,不动);推荐本地 24 Active LTS;CI 用 24(含 JS-actions runtime, `FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true`,**CI 端 release workflow 配置详见 §9.1.D**)
36
+ - npm ≥8
37
+ - 推荐 IDE:VSCode + ESLint + Prettier 扩展
38
+
39
+ ## 4. 目录结构
40
+
41
+ `src/` 按**业务域文件夹**组织:每个域一个文件夹 + 一个 `index.ts` barrel,`core/`
42
+ 收口运行时基座,`shared/` 收口跨域基础设施。根 `src/index.ts` 只从各域 barrel
43
+ re-export。新增业务域时落对应文件夹,规则唯一明确。
44
+
45
+ ```
46
+ acosmi-sdk-ts/
47
+ ├── src/ # 源码(按域文件夹组织)
48
+ │ ├── index.ts # 公共入口 barrel(从各域 re-export)
49
+ │ ├── browser.ts # 浏览器构建入口
50
+ │ ├── core/ # 运行时基座,无业务域
51
+ │ │ ├── client.ts # 主 Client class
52
+ │ │ ├── store.ts # FileTokenStore + LocalStorageTokenStore + InMemoryTokenStore
53
+ │ │ ├── retry.ts # 重试策略
54
+ │ │ ├── http.ts # HTTP / SSE helper
55
+ │ │ └── sanitize-bridge.ts # Client × sanitize 胶水 (declaration merging)
56
+ │ ├── shared/ # 跨域基础设施 + 跨域共享 DTO(v1.5.0)
57
+ │ │ ├── api-response.ts # APIResponse / YudaoPageResult
58
+ │ │ ├── errors.ts # HTTPError / NetworkError / StreamError / BusinessError / ...
59
+ │ │ ├── pagination.ts # PageRequest / PageResult(= YudaoPageResult 别名)/ SortDirection
60
+ │ │ ├── operation.ts # OperationId / Source / Status / VerifyStatus / IdempotencyKey(Header)
61
+ │ │ ├── retry-advice.ts # RetryAdvice + reason 映射(叠加层,不替换 RetryPolicy)
62
+ │ │ ├── principal.ts # PrincipalRef / TenantRef / ApiClientRef 轻量引用
63
+ │ │ └── gate.ts # FeatureGateStatus / StepUpStatus / BillingPreflightResult
64
+ │ ├── auth/ # 鉴权 / 身份域
65
+ │ │ ├── auth.ts # OAuth 2.1 PKCE / 动态注册 / token refresh / revoke
66
+ │ │ ├── scopes.ts # 分组 scope
67
+ │ │ └── types.ts # ServerMetadata / TokenResponse / TokenSet / ClientRegistration
68
+ │ ├── models/ # 模型网关域
69
+ │ │ ├── types.ts # ManagedModel / Chat* / StreamEvent / ...
70
+ │ │ ├── wire-anthropic.ts wire-openai.ts # 两套 wire-format DTO
71
+ │ │ ├── adapters/ # AnthropicAdapter + OpenAIAdapter (P0 双格式红线)
72
+ │ │ └── model-helpers.ts stream-meta.ts betas.ts
73
+ │ ├── billing/ # 计费域(entitlements / packages / wallet)
74
+ │ ├── casehall/ # 法律案件咨询(v1.8.0;v2.0.0 +getMyLawyerCredentialStatus 律师自查)
75
+ │ ├── certification/ # 预留 — 实名 / 人脸 / 活体 / 企业认证 / CA
76
+ │ ├── compliance/ # 合规域(重点扩张:evidence / timestamp / signing / ...)
77
+ │ ├── enterprise/ # 企业席位(v1.8.1;v2.0.0 +getMyEnterpriseKycStatus 企业 OWNER 自查)
78
+ │ ├── finance/ # 财务域(v1.9.0)— 发票 / 退款 / 对公转账(决策 14/15 + R12;v2.0.0 PII Javadoc 升级含 keyVersion v1/v2)
79
+ │ ├── notifications/ # 通知 / 推送 / WebSocket
80
+ │ ├── agent-runs/ # SDK-facing Agent Run Gateway(client + 公开协议类型)
81
+ │ │ └── remote-control.ts # v2.1 远控类型 + parseRemoteControlEvent / 11 事件 union
82
+ │ ├── chatbridge/ # v2.1 第三方聊天平台桥接类型骨架(types-only,无 client)
83
+ │ ├── pricing/ # 公开业务参数(v1.7.0)+ csign 合规 SKU 报价(v1.7.0)
84
+ │ ├── products/ # 商品中心(v1.7.0)— productFamily / audience / billingMode 索引
85
+ │ ├── sanitize/ # 历史消息清理子包
86
+ │ ├── skills/ # 技能商店 + 工具列表
87
+ │ ├── subscription/ # 订阅档位(v1.7.0)— SubscriptionPlan / UserSubscription
88
+ │ ├── support/ # bug-report 等
89
+ │ └── apiClients/ tenant/ iam/ audit/ operations/ mcp/ gateway/
90
+ │ # 8 占位命名空间,当前 `export {}` 不真实导出(含本行 7 个 + certification);
91
+ │ # 对应后端端点未就绪,落地准则见 §7 v1.5.0「跨域共享 DTO 契约」段
92
+ ├── test/ # vitest 单元测试
93
+ ├── examples/ # npm 包随附示例
94
+ │ ├── core-chat.ts
95
+ │ ├── auth-oauth-flow.ts
96
+ │ ├── agent-runs-stream.ts
97
+ │ ├── compliance-read.ts
98
+ │ ├── compliance-evidence-timestamp.ts
99
+ │ └── compliance-envelope.ts
100
+ ├── docs/
101
+ │ ├── compliance.md # npm 包随附 compliance API 指南
102
+ │ ├── api/ # TypeDoc 生成的 API 参考(npm run docs)
103
+ │ └── 开发与发布手册.md # 仓库维护手册,不随 npm 包发布
104
+ ├── dist/ # 构建产物 (gitignored, CI 编译)
105
+ ├── package.json
106
+ ├── tsup.config.ts # 多端构建
107
+ ├── tsconfig.json
108
+ ├── typedoc.json # TypeDoc API 参考文档配置
109
+ ├── .eslintrc.json
110
+ ├── .npmignore # 排除 src/test/configs/.github
111
+ ├── README.md # 用户文档(API 总览 + 示例)
112
+ ├── LICENSE # MIT
113
+ └── .github/workflows/
114
+ └── release.yml # push tag v* → npm publish + GitHub Release
115
+ ```
116
+
117
+ > 重组为**非破坏性**:对外导出符号、`package.json` 的 `exports`、`dist/` 产物路径
118
+ > 全部不变;下游 `crabcode` / `crabclaw` / `csign` 零感知。`node.ts` 因是未接线的
119
+ > 死入口(无 `./node` 子路径导出、非任何 build entry)已删除。
120
+
121
+ ## 5. 编码规范
122
+
123
+ ### TypeScript 风格(硬规则)
124
+
125
+ | 类别 | 规则 | 例 |
126
+ |------|------|----|
127
+ | 函数/方法 | **camelCase** | `discover`, `getAdapterForModel` |
128
+ | 类 / 接口 / 类型 | **PascalCase** | `Client`, `ServerMetadata`, `AnthropicAdapter` |
129
+ | Error 子类 | **PascalCase + Error 后缀** | `HTTPError`, `BusinessError`, `OrderTerminalError` |
130
+ | 常量 | **PascalCase**(前缀型)或 **UPPER_CASE** | `ErrDiscovery`, `ScopeAI`, `EventComplete` |
131
+ | 文件名 | **kebab-case** | `stream-meta.ts`, `bug-report.ts` |
132
+ | Wire-format 字段 | **snake_case**(与 Go json tag 严格对齐) | `preferred_format`, `created_at`, `max_tokens` |
133
+
134
+ ### 不要使用的 Go-isms(硬禁止)
135
+
136
+ - ❌ **PascalCase 函数** — TS 公开 API 必须 camelCase(`AllScopes()` 是 Go 风格,改 `allScopes()`)
137
+ - ❌ **多返回值 `[T, Error]` 元组** — 用 `throw` + 类型化 Error 子类
138
+ - ❌ **`nil` 字面量** — 用 `null` / `undefined`
139
+ - ❌ **`: any`** — 优先 `: unknown` 或精确类型
140
+ - ❌ **Go 关键字识别符**:`func`, `package`, `chan`, `defer`, `goroutine`
141
+ - ❌ **指针风格 `T | null`** — 用 optional `?: T`(除非 wire-format 必须)
142
+ - ❌ **`fmt.Sprintf` / `fmt.Errorf` / `log.Println`** — 用模板字符串 / `new Error()` / `console`
143
+
144
+ ### 跨语言契约印记(TS→Go 反向翻译对照基准,不许改)
145
+
146
+ TS 现为主实现,**文件级与 Go 1:1 对齐的约束已作废** —— `src/` 布局完全自由(见 §4
147
+ 域文件夹结构)。但以下跨语言契约仍是将来把 TS 反向翻译为 Go 时的对照基准,必须保留:
148
+
149
+ - ✅ **snake_case wire-format 字段名** — wire 协议契约(50+ struct 已对齐),不可漂移
150
+ - ✅ **符号名跨语言一致** — 导出的类型 / 方法 / 错误名是 TS↔Go 对照锚点
151
+ - ✅ **bug-for-bug 行为对齐**(e.g. retry POST=false 默认,sanitize thinking 硬豁免)
152
+ - ✅ **ISO 8601 字符串时间** — 与 Go RFC3339 wire-compat
153
+ - ✅ **string 表示金额 / json.Number 字段** — 避免 JS number 精度损失(金融安全)
154
+ - ✅ **双 adapter 等地位** — `AnthropicAdapter` + `OpenAIAdapter` P0 红线,不可降级
155
+ - ✅ **已有端口代码的 "端口自 X.go" 溯源注释** — 历史对照保留;compliance、对外鉴权层等
156
+ 新域本就无 Go 对应物,不写该注释
157
+
158
+ ### TS 不可避免的偏移(与 Go 不 1:1)
159
+
160
+ | 维度 | Go | TS |
161
+ |------|----|----|
162
+ | 错误处理 | `(val, err)` 多返回值 | `throw` + 类型化 Error 子类 |
163
+ | 上下文取消 | `context.Context` | `AbortSignal` |
164
+ | 流返回 | `(eventCh, errCh chan)` | `AsyncIterable<StreamEvent>` |
165
+ | 锁 | `sync.Mutex` | Promise chain (`this.mu`) |
166
+ | IO | 同步 `os.ReadFile` | `fs/promises` async |
167
+ | Token Store 持久化 | 同步 file IO | `async` Save/Load/Clear |
168
+
169
+ ## 6. 开发流程
170
+
171
+ ### 改源码 → 提交
172
+
173
+ ```bash
174
+ cd /Users/fushihua/Desktop/Acosmi/acosmi-sdk-ts
175
+
176
+ # 改完后必须通过发布前验证
177
+ npm run typecheck # 0 errors
178
+ npm run lint # 0 errors
179
+ npm test # all pass
180
+ npm run build # 多端 dist/ 全部生成
181
+ npm run test:pack # packed tarball consumer smoke
182
+
183
+ # commit (仅 SDK 独立仓)
184
+ git status --short
185
+ git diff --check
186
+ git add <files>
187
+ git commit -m "fix: ..."
188
+ ```
189
+
190
+ ### Commit message 规范
191
+
192
+ `type(scope): subject` 风格(与 Go SDK 仓库一致):
193
+
194
+ - `feat(sdk-ts): ...` 新功能
195
+ - `fix(sdk-ts): ...` bug fix
196
+ - `refactor(sdk-ts): ...` 重构(无功能变化)
197
+ - `docs(sdk-ts): ...` 文档
198
+ - `test(sdk-ts): ...` 测试
199
+ - `release(sdk-ts): vX.Y.Z` 版本发布
200
+
201
+ 公开仓提交信息只写产品/SDK 变更,不写协作工具、临时计划或开发过程元数据。
202
+
203
+ ## 7. 测试
204
+
205
+ - 框架:**vitest**(Node + jsdom 兼容)
206
+ - 位置:`test/`(随功能增长,发版前以 `npm test` 实际输出为准)
207
+ - 命令:`npm test` 单跑 / `npm run test:watch` watch
208
+
209
+ ### 必测 P0 红线(改了就必跑)
210
+
211
+ - `test/adapters/routing.test.ts` — 双格式四级路由 8 case(**P0 红线**)
212
+ - `test/adapters/anthropic-build.test.ts` — buildRequestBody 14 case
213
+ - `test/sanitize/history.test.ts` — StripEphemeral / DropBlocks / thinking 豁免 7 case
214
+ - `test/scopes.test.ts` — 分组 scope 5 case
215
+ - `test/agent-runs.test.ts` — Agent Runs create/stream/401/local-tool/artifact 公开协议
216
+ - `test/model-helpers.test.ts` — v1.2 InputModality catalog helpers + sidecar 选择规则
217
+ - `test/list-models-input-modalities.test.ts` — v1.2 snake_case → camelCase 归一化 + undefined 保留
218
+ - `test/compliance.test.ts` — v1.3 compliance URL / 401 / no-retry / idempotency / error classify / polling / privacy boundary
219
+ - `test/compliance-scopes.test.ts` — **15 个** compliance scope 常量与 `complianceScopes()`(v1.3.0 首发 12 个 → v1.3.2 +`compliance:reports:write` → v1.5.0 S5 +`compliance:contract_template:{read,write}` 合计 15;含 `compliance:reports:publish` 等 step-up gated 写 scope)
220
+ - `test/shared.test.ts` — v1.5 跨域共享 DTO:`PageResult` 别名等价 / retryAdvice reason 映射 / 叠加投影只读性 / `classifyComplianceError` 零回归红线 17 case
221
+
222
+ > **v2.0.0 新方法测试位** — Phase 3 复核新增 `casehall.getMyLawyerCredentialStatus()` 与 `enterprise.getMyEnterpriseKycStatus()` 两个自查端点;wire-format 走既有 `doJSON` + `APIResponse` 路径,覆盖在 typecheck + smoke pack 层(无新业务红线,**不进 P0 必跑列表**)。若将来这两个端点的鉴权 / 字段策略发生变化,须在此列表追加显式测试。
223
+
224
+ ### Agent Runs 公开 API 范围
225
+
226
+ 从 v1.1.0 起,SDK 不再只是模型网关客户端,还包含下游产品可用的云端智能体任务协议:
227
+
228
+ - `client.agentRuns.create(req, signal?)`
229
+ - `client.agentRuns.stream(runId, opts?, signal?)`
230
+ - `client.agentRuns.run(req, opts?, signal?)`
231
+ - `client.agentRuns.cancel(runId, signal?)`
232
+ - `client.agentRuns.get(runId, signal?)`
233
+ - `client.agentRuns.listArtifacts(runId, signal?)`
234
+ - `client.agentRuns.downloadArtifact(runId, artifactId, signal?)`
235
+ - `client.agentRuns.submitLocalToolResult(runId, result, signal?)`
236
+ - `client.agentRuns.runWithLocalTools(req, handlers, opts?, signal?)`
237
+
238
+ **远程控制(v2.1,CrabCode remote-control)** — 同属 `agentRuns` 命名空间,但事件协议独立(契约 §4 的 11 事件,不复用 `stream` 旧 union):
239
+
240
+ - `client.agentRuns.createRemoteRun(req, signal?)` — `req.runtime` 固定 `'crabcode_remote'`,`runner` + `adapter` 必填。
241
+ - `client.agentRuns.streamRemoteControl(runId, signal?)` — **无 options 参数**(`error` 恒非终结、`done`/`settle` 终结、从不抛异常)。
242
+ - helper:`parseRemoteControlEvent(raw)`(wire→强类型,未知 type 返回 null)、`isTerminalRemoteEvent(ev)`。
243
+ - `chatbridge`(v2.1,Phase 7 types-only):仅导出类型 + 守卫(`isPlatform` / `isRegion` / `isChannelInboundEvent` / `asCredentialRef`),**无 `client.chatBridge.*` 方法**;平台 webhook/凭证/桥接 handler 是 Phase 7B 后端工作,平台 SDK 依赖留在独立 adapter 包,禁进主包。
244
+
245
+ 红线:
246
+ - 下游产品禁止直连 Nexus 内部 `/api/v4/chat/completions` 或 `/api/v4/managed-models/:id/...` 来实现智能体循环。
247
+ - Agent Runs wire-format 使用 snake_case;SDK public API 使用 camelCase。
248
+ - **远控 wire 约定按平面分(契约 §12)**:remote-control 平面 = snake_case + 时长整数毫秒(`approval_timeout_ms`,**禁** Go `time.Duration` 上 wire——会被当纳秒);chatbridge 资源视图平面 = camelCase。两平面不可混用。
249
+ - **远控事件唯一序列化出口**:后端 `RemoteSessionEvent.ToWire()` 出扁平 snake_case 帧(`type`+`seq`+字段同级);改动远控事件字段/单位/形状,必须同步更新跨语言金标 fixtures(后端 `remotecontrol/testdata/wire_golden.json` ⇄ SDK `test/remote-control-wire-golden.test.ts`),任一端漂移即红——这是 P1-1 同类分裂的护栏。
250
+ - **远控专用 scope**:`remote_control`(+ 3 子 scope)绝不复用 `models:chat`/`ai`,且 **不进 `allScopes()`**;`ai` 展开列表禁加入任何 `remote_control` 子项(隐式获权后门)。
251
+ - **secret 边界(契约 §16)**:chatbridge 平台 secret 只入上游 vault;SDK 公共面只见 `CredentialRef` + fingerprint + 脱敏 metadata,`ChatCredentialPublic` 编译期无密文字段。
252
+ - Agent Runs 服务端状态必须按 `tenantId + userId` 隔离并持久化;run/SSE event/artifact/local-tool-result 不能只放进进程内存。
253
+ - Agent Runs 执行必须接入统一 entitlement hold/settle/release 链路;settle 失败必须进入既有 pending settlement 补偿机制。
254
+ - Agent Runs 结算只能使用 provider/ADK 透传的 `exact: true` usage;不得用字符数、输入长度或其他估算 token 扣费。provider usage 缺失时必须 release hold,并向 stream 返回稳定的 `usage_missing_released` settlement 状态。
255
+ - `create`、`submitLocalToolResult` 等可能产生副作用的 POST 遇到 401 不自动 refresh 后重放,避免重复创建或重复计费;GET/stream/download 这类安全查询允许单次 refresh 重试。
256
+ - SDK 不内置 CrabDesign/CrabCode/CrabClaw 专属本地文件读取逻辑;`local_tool_request` 只定义协议,handler 必须由下游显式传入;`allowedTools` 使用 ASCII function name。
257
+
258
+ ### InputModality + 桌面视觉理解 sidecar 契约(v1.2.0)
259
+
260
+ 从 v1.2.0 起,SDK 暴露上游 `ManagedModel` 的输入模态字段 + 桌面视觉理解 sidecar capability,供 CrabCode desktop automation / computer-use 通过 catalog(而非模型名 substring)选模型:
261
+
262
+ **类型契约**:
263
+
264
+ - `InputModality = 'text' | 'image'`(严格白名单)
265
+ - `ManagedModel.inputModalities?: InputModality[]` — 模型可接收的用户输入模态;`undefined` = 未声明(保守按 text-only / unknown 处理)
266
+ - `ModelCapabilities.supports_desktop_visual_understanding?: boolean` — 模型是否被运营标记为桌面截图解析 sidecar(输入 screenshot,输出 UI 结构化描述)
267
+
268
+ **4 个 catalog helper**(`src/models/model-helpers.ts`):
269
+
270
+ - `modelSupportsInputModality(model, modality): boolean`
271
+ - `modelSupportsImageInput(model): boolean`
272
+ - `findFirstModelByInputModality(models, modality): ManagedModel | null` — 按 catalog 顺序,跳过 `isEnabled === false`
273
+ - `findDesktopVisualUnderstandingModel(models): ManagedModel | null` — 选择规则:`isEnabled !== false` + `supports_desktop_visual_understanding === true` + `inputModalities` 含 `image` + `isDefault` 优先 / 否则 catalog 顺序第一
274
+
275
+ **协议归一化**:
276
+
277
+ - `listModels` / `listModelsWithStatus` 会归一化上游 snake_case `input_modalities` → camelCase `inputModalities`(兼容老网关)
278
+ - 同时存在 camelCase + snake_case 时 camelCase 胜
279
+ - `zeroModelCapabilities()` 显式置 `supports_desktop_visual_understanding: false`,避免 `undefined` 误判
280
+
281
+ **红线(严禁违反)**:
282
+
283
+ - 客户端**不得**在本地硬编码模型名做能力推断(如检测 modelId 含 "vision");一切视觉模型选择必须来自 SDK catalog 字段
284
+ - 上游 `ManagedModel` 缺失 `inputModalities` 时,SDK 保留 `undefined`,**不**自动补 `['text']` — 调用方必须保守按 text-only / unknown 处理,**严禁默认假设支持 image**
285
+ - 网关后端强约束:`supports_desktop_visual_understanding=true` 必须 `inputModalities` 含 `image`(handler 400 拒绝错配置)
286
+ - `inputModalities` 与 `capabilities.supports_desktop_visual_understanding` 是正交两件事:前者描述"模型能不能吃图",后者描述"运营是否把该模型标为桌面 UI 解析专用 sidecar"
287
+
288
+ **新增单测**:
289
+
290
+ - `test/list-models-input-modalities.test.ts` — listModels snake/camel 归一化 + undefined 保留 8 case
291
+ - `test/model-helpers.test.ts` — 4 个 helper + isEnabled/isDefault 优先级 15 case
292
+
293
+ 加测试规则:
294
+ - 端口自 Go SDK 的测试用例必须保留 bug-for-bug
295
+ - TS-only 测试(e.g. AbortSignal 行为)单独写文件,不混进端口测试
296
+
297
+ ### Compliance SDK 契约(v1.3.0)
298
+
299
+ 从 v1.3.0 起,SDK 暴露 `client.compliance` 子客户端,覆盖时间章、电子证据、
300
+ 证据包、报告、合同签署 envelope、用印审批和 provider request 脱敏状态轮询。
301
+
302
+ **公开 API 边界**:
303
+
304
+ - `Config.complianceBaseURL` 默认 `${serverURL}/admin-api`,不复用 `/api/v4`。
305
+ - `src/compliance/client.ts` 只发送 Acosmi 公共 DTO,不发送 provider 选择字段。
306
+ - compliance 类型文件(`src/compliance/**/types.ts` —— 各子域 `evidence/timestamp/signing/...`
307
+ 的 `types.ts` 及共享 `src/compliance/types.ts`)不得暴露 provider
308
+ product/user/transaction/project/seal-provider 字段。
309
+ - `src/compliance/errors.ts` 只按 Java numeric `BusinessError.code` 分类,不从 message 文案做正则。
310
+ - `src/index.ts` 以 `export * from './compliance'` 导出 compliance 域;`src/compliance/index.ts`
311
+ barrel 从 `./client` re-export `ComplianceClient`,该 re-export 即加载 `client.ts`,
312
+ 其 `Client.prototype` 的 `compliance` getter declaration-merging 随 barrel 一并生效。
313
+
314
+ **写操作红线**:
315
+
316
+ - POST/PUT/DELETE 不自动 retry。
317
+ - 写操作 401 不 refresh + replay;调用方重新认证后用同一 `Idempotency-Key` 恢复。
318
+ - GET 读路径可以做单次 401 refresh retry。
319
+ - 所有写方法必须允许传递 `Idempotency-Key`。
320
+
321
+ **安全红线**:
322
+
323
+ - SDK / docs / examples / tests / dist / tarball 不得包含 provider endpoint、证书、私钥、
324
+ JKS/PFX/P12、P7、zip、jar、口令、provider raw payload 或 callback billing commit payload。
325
+ - Java compliance 后端负责 provider 集成、受控材料、local verify 和 billing 状态机。
326
+ - Go OAuth/JWKS 层负责 token 签发、scope 与 step-up/introspection 语义。
327
+ - TS SDK 只申请 scope、发送公共 DTO、分类公开错误码并轮询脱敏状态。
328
+
329
+ **必测**:
330
+
331
+ - `npm test -- --run test/compliance.test.ts test/compliance-scopes.test.ts`
332
+ - `npm run test:pack` 必须覆盖 `client.compliance` consumer 视角类型调用。
333
+ - `npm pack --dry-run` 必须确认 tarball 仅包含 dist、README、CHANGELOG、LICENSE、
334
+ `docs/compliance.md` 和 `examples/` 等预期文件。
335
+
336
+ **v1.3.2 增补**(生产闭环实施计划 Phase 1 / 5 / 7):
337
+
338
+ - `verifyEvidencePublic` 收口为匿名公开验真:未 `login()` 时直接发匿名请求,不再抛
339
+ `not authorized`;已持 token 则附 `Authorization` 保留审计上下文;public 端点 `401`
340
+ 不触发 `forceRefresh`、不做 refresh replay。
341
+ - 新增第 13 个 compliance scope `compliance:reports:write`:`createReport` 对应的服务端
342
+ scope 从 read 切到独立写 scope;`complianceScopes()` 已含;Go/Java/TS 三端字面量一致。
343
+ - `docs/compliance.md` 新增「Method Status」一节,把每个 `client.compliance.*` 方法标注为
344
+ `production-ready` / `gated` / `draft contract` / `internal-only` 四档。`gated`
345
+ (`publishReport` / `signEnvelope` / `createH5SigningUrl` / `approveSealApproval`)在服务端
346
+ step-up / 闸门未闭合前 fail-closed,SDK 不重试、不伪成功;distribution billing 等
347
+ `internal-only` 能力不进入 SDK 调用面。
348
+
349
+ ### 跨域共享 DTO 契约(v1.5.0)
350
+
351
+ 从 v1.5.0 起,`src/shared/` 除 `errors.ts` / `api-response.ts` 外,新增 5 个跨域
352
+ 共享 DTO 文件,为后续平台控制面(`tenant` / `iam` / `operations` / `gateway`
353
+ 等占位命名空间)与 `compliance` 分页 / gate 能力预沉淀【共享原语】。依据:能力
354
+ 缺口总账 `docs/audit/saas-sdk-backend-capability-gap-register-2026-05-22` §9.4 /
355
+ §9.5(Phase 0.3 / 0.5)。**注:该总账归档在主仓 `docs/audit/`,不进公开 SDK 仓。**
356
+
357
+ **落位规则**:
358
+
359
+ - 共享 DTO 一律落 `src/shared/`,按【关注点】分文件(`pagination` / `operation` /
360
+ `retry-advice` / `principal` / `gate`),不堆进 `shared/index.ts`,不在 `src/`
361
+ 根新增散文件,不回退巨型 `types.ts`。
362
+ - `shared/index.ts` barrel 汇总 re-export;根 `src/index.ts` 经 `export * from
363
+ './shared'` 自动导出,新增文件无需改根入口。
364
+
365
+ **冲突避免红线(严禁违反)**:
366
+
367
+ - ✅ **`PageResult<T>` 是 `YudaoPageResult<T>` 的别名**,不引入第二套
368
+ `{list,total}` 分页结果结构——避免 billing / compliance / skills /
369
+ notifications 之间双分页标准。`PageRequest` 为新增【可选】类型,**不回填改写**
370
+ 既有 4 处内联分页签名(破坏性变更)。
371
+ - ✅ **`RetryAdvice` 是叠加层**——独立类型、独立字段,**不修改也不替换**
372
+ `core/retry.ts` 的 `RetryPolicy`(决定 SDK 传输层是否自动重试)与
373
+ `compliance/errors.ts` 的 `ComplianceErrorInfo`(`retryable`/`terminal`/
374
+ `stepUpRequired`)。否则 `csign sdk-client.ts:classifyVerifyError` 破裂。
375
+ - ✅ **`RetryAdviceReason` 不开第四套错误码登记表**——它是既有三套登记表
376
+ (Java 数值码 `1_031_xxx` / SDK 符号 key / Go OAuth 标准字符串)的小写归一化
377
+ 映射;`retry-advice.ts` 内 `Record<ComplianceErrorKey, RetryAdviceReason>`
378
+ 以编译期穷举强约束映射表不漏。
379
+ - ✅ **`ProviderRequestStatus` 复用**既有 `ComplianceProviderRequestStatus`,
380
+ 不另造同名近似类型。
381
+ - ✅ **`IdempotencyKeyHeader`(`'Idempotency-Key'`)是写接口幂等键 header 的
382
+ 单一真相源**,新写接口一律引用该常量。
383
+
384
+ **关键约束**:使 8 个占位命名空间(`tenant` / `iam` / `apiClients` /
385
+ `operations` / `audit` / `gateway` / `mcp` / `certification`)变成真实导出 = 写出
386
+ 调用后端端点的 SDK 方法;对应后端端点**当前不存在**。在后端端点与契约就绪前,
387
+ 这些命名空间保持 `export {}` 占位,**不实现**——提前写空转方法属编造契约。共享
388
+ DTO 是纯类型 / 纯函数沉淀,不构成对后端契约的预设,故可先行落地。
389
+
390
+ **必测**:`test/shared.test.ts`(17 case)。
391
+
392
+ ### Compliance gateway S1-S6 rollup(v1.5.0)
393
+
394
+ v1.5.0 同时把 compliance gateway S1-S6(roadmap 原 v1.6.0-v1.11.0)全量 rollup 进
395
+ 当前版本,**新增 SDK 公开方法 25+ 个**,全部走 `client.compliance.*`,红线(写不
396
+ 重试 / 写 401 不 replay / GET 单次 401 refresh)不变。CHANGELOG 各节用 "原 1.X.0"
397
+ 注解标出来源。
398
+
399
+ - **S1(原 v1.6.0 / U-1 / 后端 G1)**——6 个分页列表读端点:`listEvidenceAssets`
400
+ / `listTimestamps` / `listEvidencePackages` / `listReports` /
401
+ `listSigningEnvelopes` / `listSealApprovals`。统一走 `GET .../page`,返回 yudao
402
+ `PageResult<T>`(`{ total, list }`),请求参数继承共享 `PageRequest`。
403
+ - **S2(原 v1.7.0 / U-5 + U-6 / 后端 G2)**——capability gate + 操作投影读:
404
+ `getCapabilities` / `getFeatureGate(action)` / `listOperations` / `getOperation`。
405
+ 能力闸门拿不到时必须 fail-closed。
406
+ - **S3(原 v1.8.0 / U-7 / 后端 G3)**——TSA 只读视图:`listTsaProviders` /
407
+ `getTsaStats`。
408
+ - **S4(原 v1.9.0 / U-10 + U-12 子集 / 后端 G4)**——envelope 收尾:
409
+ `listEnvelopeContracts` / `listEnvelopeProviderRequests`(GET 读,返回普通数组
410
+ 而非 `PageResult`),`voidEnvelope`(写,带 `Idempotency-Key`,`reason` 随 body
411
+ 提交)。envelope 的 send / remind / authorize / download / token 等 W3 闸门动作
412
+ 仍后端推迟。
413
+ - **S5(原 v1.10.0 / U-2 / 后端 G5)**——合同模板 9 个方法 DRAFT → PUBLISHED →
414
+ ARCHIVED 全生命周期:`createContractTemplate` / `updateContractTemplate` /
415
+ `deleteContractTemplate` / `getContractTemplate` / `listContractTemplates` /
416
+ `uploadContractTemplatePdf` / `publishContractTemplate` /
417
+ `archiveContractTemplate` / `listContractTemplateVersions`。新增 2 个 scope
418
+ `compliance:contract_template:{read,write}`(不要求 step-up)。
419
+ - **S6(原 v1.11.0 / U-4 / 后端 G6)**——用印执行分页:`listSealUses`。复用既有
420
+ `compliance:contract_signing:read` scope,不引入新 scope。
421
+
422
+ > 当前 compliance scope 总数 **15** 个(首发 12 + v1.3.2 `compliance:reports:write`
423
+ > + S5 两个 contract_template = 15;其中 `compliance:reports:publish` 在首发批次
424
+ > 内)。`complianceScopes()` 返回全部 15 个;生产建议按最小集合申请。
425
+
426
+ ## 8. 构建
427
+
428
+ - 工具:**tsup**
429
+ - 命令:`npm run build`
430
+ - 产物结构:
431
+ - `dist/node/` — Node ESM (`.mjs`) + CJS (`.cjs`) + `.d.ts`
432
+ - `dist/browser/` — Browser ESM + `.d.ts`
433
+ - `dist/` — Deno/Bun ESM + `.d.ts`
434
+ - 大小:~120KB(压缩前)
435
+ - `package.json.files`(v2.0.1 起 8 项;任何调整必须同步本节,违反即触发 §9.3 公开仓清洁度检查失败):
436
+ - `dist`
437
+ - `README.md`
438
+ - `CHANGELOG.md`
439
+ - `LICENSE`
440
+ - `docs/compliance.md`(v1.3.0 起 — 合规域 API 指南)
441
+ - `docs/pii-role-matrix.md`(v2.0.0 起 — 4 角色 × 3 PII 级矩阵)
442
+ - `docs/开发与发布手册.md`(v2.0.1 起 — 本文件随包发布给下游集成方)
443
+ - `examples/`
444
+ - `prepublishOnly` 钩子:`typecheck && lint && test && build && test:pack && docs`(npm publish 前自动跑;含 TypeDoc 文档生成)
445
+
446
+ ## 9. 发布流程
447
+
448
+ ### 9.1 一次性配置(首次发布前)
449
+
450
+ #### A. 公开仓 GitHub Repo
451
+
452
+ ```
453
+ github.com/acosmi/sdk-ts (public, MIT)
454
+ ```
455
+
456
+ 由企业账号 `acosmi`(lowercase)持有。
457
+
458
+ #### B. 获取独立 SDK 仓
459
+
460
+ ```bash
461
+ git clone https://github.com/acosmi/sdk-ts.git
462
+ cd sdk-ts
463
+ git config user.name "acosmi-fushihua"
464
+ git config user.email "fushihua@acosmi.com"
465
+ ```
466
+
467
+ > 用 HTTPS(不是 SSH),避免 host key verification 失败。
468
+
469
+ #### C. npm Token(**关键**:bypass 2FA)
470
+
471
+ | Token 类型 | bypass 2FA | 推荐度 |
472
+ |------------|------------|--------|
473
+ | **Classic Publish token** | ❌(账号开 2FA 时不能用 CI) | 不推荐 |
474
+ | **Classic Automation token** | ✅(天然 bypass) | 推荐(最简单) |
475
+ | **Granular Access Token** | ⚠️ 默认 ❌,**必须勾选 "Bypass two-factor authentication"** | 推荐(颗粒度细) |
476
+
477
+ **Granular Token 配置步骤**:
478
+ 1. npmjs.com → Account → Granular Access Tokens → Generate New Token
479
+ 2. Name:`acosmi-sdk-ts-ci`
480
+ 3. Expiration:建议 1 年
481
+ 4. Permissions:Read & Write
482
+ 5. Packages:选 `@acosmi/sdk-ts`
483
+ 6. **勾选 "Bypass two-factor authentication when publishing"** ← 漏勾就 E403
484
+ 7. 复制 token
485
+
486
+ **GitHub repo Secret**:
487
+ - repo → Settings → Secrets and variables → Actions → New repository secret
488
+ - Name: `NPM_TOKEN`
489
+ - Value: 上一步 token
490
+
491
+ #### D. 验证 release.yml
492
+
493
+ 公开仓 `.github/workflows/release.yml` 关键内容:
494
+
495
+ ```yaml
496
+ on:
497
+ push:
498
+ tags: ['v*']
499
+
500
+ jobs:
501
+ publish:
502
+ runs-on: ubuntu-latest
503
+ # JS-based actions (checkout / setup-node / action-gh-release) 走 Node 24,
504
+ # 提前进入 2026-06-02 GitHub 默认状态,避免 Node 20 弃用 annotation。
505
+ env:
506
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
507
+ steps:
508
+ - uses: actions/checkout@v4
509
+ - uses: actions/setup-node@v4
510
+ with:
511
+ # CI 跑 Node 24(Active LTS),与 JS-action 运行时一致;
512
+ # SDK consumer 仍按 package.json engines.node >=18 兼容承诺。
513
+ node-version: '24'
514
+ registry-url: 'https://registry.npmjs.org'
515
+ - run: npm ci
516
+ - run: npm run typecheck
517
+ - run: npm run lint
518
+ - run: npm test
519
+ - run: npm run build
520
+ - run: npm publish --provenance --access public
521
+ env:
522
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
523
+ - uses: softprops/action-gh-release@v2
524
+ with:
525
+ generate_release_notes: true
526
+ ```
527
+
528
+ ### 9.2 每次发布
529
+
530
+ ```bash
531
+ # === Step 1:确认 SDK 独立仓 ===
532
+ cd /Users/fushihua/Desktop/Acosmi/acosmi-sdk-ts
533
+ git rev-parse --show-toplevel
534
+ git remote -v
535
+ git status --short
536
+
537
+ # === Step 2:改源码 + 文档 + 升 version ===
538
+ npm run typecheck
539
+ npm run lint
540
+ npm test
541
+ npm run build
542
+ npm run test:pack
543
+ npm pack --dry-run
544
+
545
+ # === Step 3:审计 ===
546
+ git diff --check
547
+ git diff --stat
548
+ # 额外执行敏感材料扫描,确认没有证书、私钥、keystore、真实 provider endpoint、raw payload。
549
+
550
+ # === Step 4:提交并推送当前分支 ===
551
+ git add <sdk files>
552
+ git commit -m "feat: add compliance SDK client"
553
+ git push
554
+
555
+ # === Step 5:发布 tag(会触发 npm publish)===
556
+ # 只有在确认要触发 npm/GitHub Release 时才执行:
557
+ git tag vX.Y.Z
558
+ git push origin vX.Y.Z
559
+ ```
560
+
561
+ ### 9.3 公开仓清洁度检查(push 前必看)
562
+
563
+ - [ ] 作者 = `acosmi-fushihua <fushihua@acosmi.com>`,提交信息干净
564
+ - [ ] commit 范围只包含 SDK 发布相关文件
565
+ - [ ] 没有 Java / Go / CFCA real provider 改动
566
+ - [ ] `docs/` 不包含内部 audit 文档或 provider runbook
567
+ - [ ] `.github/workflows/release.yml` 存在
568
+ - [ ] `package.json` `"version"` 已升
569
+ - [ ] `package.json` `"private"` 字段不存在或为 `false`
570
+ - [ ] 不含 `dist/` `node_modules/` `.eslintcache`
571
+ - [ ] 不含证书、私钥、JKS/PFX/P12、zip、jar、口令、真实 provider endpoint、provider raw payload
572
+
573
+ ## 10. 版本号策略
574
+
575
+ **自 2026-05-22 起版本策略以 TS 为准**:TS SDK 是主实现,独立演进;Go SDK `acosmi-sdk-go`
576
+ 暂停维护,原"Go + TS 主版本号联动"约束已挂起,TS 不再等 Go 对齐版本号。
577
+
578
+ - **minor**(x.y.0):新业务方法 / 新域 / 向后兼容的协议扩展
579
+ - **patch**(x.y.z):bug fix、文档、内部 refactor、packaging fix
580
+ - **major**(x.0.0):wire-format 不兼容变更、API 移除、TokenStore 接口变化、后端契约 BREAKING
581
+
582
+ **实例 — v2.0.0(2026-05-25)**:商品化 P1-P7 Phase 3 复核后 SDK 首次 major bump。SDK 公开类型 / 方法签名零移除、零改名,但**网关后端契约 BREAKING**:
583
+ 1. `SensitiveSerializer` 取消 `ROLE_ADMIN` → `platform_admin` 别名 fail-OPEN,统一收敛为 4 角色严格白名单(`platform_admin` / `s2s` / `lawyer` / `consumer`),违反即返回脱敏值。
584
+ 2. admin 写端点错误码从 `200 + {ok:false, code}` 改为 HTTP 状态码(`403` 鉴权不足 / `404` 资源不存在 / `501` `NOT_CONFIGURED_CODE`)。
585
+ 3. PII 字段(如 `Invoice.taxNumber` / 律师执照号)落盘从"应用层脱敏"升级为"AES-GCM 真加密 + AAD field binding + `keyVersion` v1/v2 协议",运行时 transparent,但消费方读取行为受 §1 角色严格化影响。
586
+
587
+ **实例 — v2.0.1(2026-05-25)**:纯 packaging fix。`package.json.files` 数组补 `docs/pii-role-matrix.md` + `docs/开发与发布手册.md`,让 v2.0.0 引入的两个 docs 随 npm tarball 下发。无源码改动,从 v2.0.0 升级无需 review。教训:**任何 v.major.0 BREAKING 发版同时必须自查 `files` 数组**——见 §18 v2.0.0 复盘。
588
+
589
+ > 历史背景:v1.0.0 时曾约定 Go + TS 主版本号同步、patch 各自独立。该联动机制因 Go SDK
590
+ > 暂停维护已挂起。将来 Go SDK 从 TS 反向翻译重启时(见 §11、计划 §3),版本对齐策略需
591
+ > 重新评估,本节届时一并修订。
592
+
593
+ ## 11. Go SDK 反向翻译流程(当前暂停)
594
+
595
+ > **状态:暂停**。本节描述的是将来 Go SDK 重启时的方向,目前不执行。
596
+
597
+ 历史上本节是「Go SDK 升级 → 镜像到 TS」的同步流程;自 2026-05-22 起 TS 成为主实现、
598
+ Go SDK 暂停维护,该 Go→TS 镜像流程已不再适用。
599
+
600
+ 未来方向(计划 §3):待 TS SDK 稳定后,从 **TS 反向翻译补齐 Go**,方向是 TS→Go。
601
+ 反向翻译以 §5「跨语言契约印记」为基准:
602
+
603
+ - 导出的类型 / 方法 / 错误**符号名**跨语言一致,作为 TS↔Go 对照锚点
604
+ - **snake_case wire-format 字段名**严格一致(wire 协议契约不可漂移)
605
+ - **bug-for-bug 行为对齐**(retry POST=false 默认、sanitize thinking 硬豁免等)
606
+ - 翻译时引入 Go 端不可避免的偏移(`(val, err)` 多返回值、`context.Context`、channel 流等,见 §5 偏移表的反向)
607
+
608
+ 在 Go SDK 重启反向翻译之前,TS SDK 的发版只走 §9.2,不需要任何 Go 侧同步动作。
609
+
610
+ ## 12. 故障排查
611
+
612
+ ### 12.1 npm publish 失败
613
+
614
+ | 错误 | 原因 | 排查 |
615
+ |------|------|------|
616
+ | `403 Forbidden` "Two-factor authentication or granular access token with bypass 2fa enabled is required" | Token 没开 bypass 2FA | 重建 Granular token,**勾选 "Bypass two-factor authentication"**;或换 Classic Automation token |
617
+ | `403 Forbidden`(其他) | `NPM_TOKEN` secret 失效 / 包名权限不够 | 重建 token,确保 packages 选了 `@acosmi/sdk-ts` |
618
+ | `EPUBLISHCONFLICT` / `cannot publish over the previously published versions` | 没升 version | 升 `package.json` `"version"` |
619
+ | `package private` | `package.json` 还有 `"private": true` | 删掉或改 `false` |
620
+ | `provenance attestation failed` | 仓库 visibility / OIDC 配置不对 | workflow 里**去掉** `--provenance` flag |
621
+ | `404 Not Found` 在 publish 时 | 包名拼错或 `publishConfig.access` 缺 | `package.json` 加 `"publishConfig": { "access": "public" }` |
622
+
623
+ ### 12.2 typecheck 失败
624
+
625
+ - 改了某个域的 wire-format 类型(`src/models/types.ts` / `src/compliance/**/types.ts` 等)→ 检查所有 caller
626
+ - import 路径错 → `npm run typecheck` 输出文件:行号
627
+ - TS strict 默认开 → 看具体 error,不要图方便加 `as any`
628
+
629
+ ### 12.3 test 失败
630
+
631
+ - `routing.test.ts` 红 = **P0 红线破坏,严重停车**,立刻回滚
632
+ - `history.test.ts` 红 = sanitize 行为漂移(检查 thinking 豁免 / tool_use_id 联动)
633
+ - `anthropic-build.test.ts` 红 = adapter buildRequestBody 行为变了(确认是有意的契约变更,而非回归)
634
+
635
+ ### 12.4 公开仓 push 失败
636
+
637
+ | 错误 | 排查 |
638
+ |------|------|
639
+ | `Host key verification failed` | 把 remote URL 从 `git@github.com:` 改成 `https://github.com/` |
640
+ | `Updates were rejected because the remote contains work` | 先 `git fetch` + `git pull --ff-only`;只有维护者明确要重写历史时才 force push |
641
+ | `This repository moved. Please use the new location` | 仓库被 transfer 了,更新 remote URL(见原始 enterprise repo) |
642
+ | GitHub Contributors 显示异常作者 | 检查本地 `git config user.name/user.email`,必要时修正后重新提交 |
643
+
644
+ ### 12.5 下游 crabcode 报 type / 模块 缺失
645
+
646
+ - 检查 `package.json` `exports` 字段 `types` 是否齐
647
+ - `dist/node/index.d.ts` 必须存在(build 产物)
648
+ - `npm pack --dry-run` 看实际 publish 内容
649
+ - 用户问"缺 X" → 先核 SDK 仓 `src/` 是否已实现该能力 → 多数是 README 未文档化(更新 README);若确实未实现则按需新增
650
+
651
+ ## 13. 下游消费指引
652
+
653
+ 下游产品(crabcode / crabclaw / 任意第三方)使用方式:
654
+
655
+ ```bash
656
+ npm install @acosmi/sdk-ts
657
+ ```
658
+
659
+ ```ts
660
+ import { Client } from '@acosmi/sdk-ts';
661
+ const client = new Client({ serverURL: process.env.ACOSMI_SERVER_URL! });
662
+ ```
663
+
664
+ 完整 API 文档见公开仓 [README.md](../README.md)。
665
+
666
+ 下游报"缺 X"时先按 §12.5 排查;如确实未实现,新增 issue → 主仓改 → 走发布流程。
667
+
668
+ ## 14. 发布前严格审计(5-phase)
669
+
670
+ 每次重大发版(含首次发版 / API 重构)必走:
671
+
672
+ | Phase | 内容 | 输出 |
673
+ |-------|------|------|
674
+ | **A. TS 代码审计** | `src/core/client.ts` + 各业务域 barrel 与子模块逐文件 + 红线扫描 | P0/P1/P2 issue 列表 |
675
+ | **B. Wire-format 对齐** | 50+ struct 的 snake_case 字段名 + 类型 + optional 标记守住跨语言契约(§5)| 0 偏移确认 |
676
+ | **C. 脚手架审计** | package.json / tsup / .npmignore / release.yml / README | 0 漏配置 |
677
+ | **D. 开发手册自审** | 本文件各节是否覆盖实际流程 | 修订列表 |
678
+ | **E. Go SDK 自审** | **暂停** —— Go SDK `acosmi-sdk-go` 已停止维护,无须同步自审;待 Go 从 TS 反向翻译重启后再恢复本 Phase | —(当前不执行)|
679
+
680
+ 四绿 PASS 后再走 §9.2。审计报告归档主仓 `docs/audit/acosmi-sdk-ts-发布前严格复核审计-YYYY-MM-DD.md`,**不进公开仓**。
681
+
682
+ **已审版本档案(最近)**:
683
+
684
+ | 版本 | 5-phase audit 完成日期 | 归档(主仓) |
685
+ |------|---------------------|------------|
686
+ | v2.0.0 / v2.0.1 | 2026-05-25 | 主仓 `memory/commercialization-p1p7-phase3-deep-review-handoff.md`(Phase 3 复核闭环:主仓 9 commit + SDK 9 commit + npm publish + CN DB V42-V67 24 迁移)|
687
+ | v1.5.1 | 2026-05-23 | 主仓 `docs/audit/acosmi-sdk-ts-发布前严格复核审计-2026-05-23.md`(docs / examples / 源码注释全量复核 — 无 API 变化)|
688
+ | v1.0.1 | 2026-05-01 | 主仓 `docs/audit/sdk-ts-1.0.0-fix-plan-2026-05-01.md`(v1.0.0 双层 broken packaging 修复 + 烟测脚本引入,详见 §17)|
689
+
690
+ ## 15. 关键文档与链接
691
+
692
+ - 用户文档:[README.md](../README.md)(公开仓 + 主仓同步)
693
+ - PII 角色矩阵:[docs/pii-role-matrix.md](./pii-role-matrix.md)(v2.0.0 起,4 角色 × 3 PII 级访问授权)
694
+ - Compliance API 指南:[docs/compliance.md](./compliance.md)
695
+ - 端口完成档:主仓 `docs/audit/TS-SDK-端口完成-2026-05-01.md`(**主仓非公开**)
696
+ - 端口初稿计划:主仓 `docs/audit/acosmi-sdk-ts-port-初稿计划-2026-05-01.md`(**主仓非公开**)
697
+ - 严格审计报告:主仓 `docs/audit/acosmi-sdk-ts-发布前严格复核审计-2026-05-01.md`(**主仓非公开**;新近审计档案见 §14)
698
+ - npm 包:https://www.npmjs.com/package/@acosmi/sdk-ts
699
+ - npm 旧包名(待 deprecate):https://www.npmjs.com/package/acosmi-sdk-ts — 1.0.0 已发布占位;下次新版发布时单独窗口执行 `npm deprecate acosmi-sdk-ts@1.0.0 "Renamed to @acosmi/sdk-ts"`
700
+ - 公开仓:https://github.com/acosmi/sdk-ts
701
+ - Go SDK 端口源:https://github.com/acosmi/acosmi-sdk-go(**暂停维护**,自 2026-05-22 起 TS 为主实现,详见 §1 / §11;将来 Go SDK 重启时从 TS 反向翻译)
702
+
703
+ ## 16. 维护者
704
+
705
+ - 源码改动:SDK 独立仓维护者 — `acosmi-fushihua <fushihua@acosmi.com>`(git config 见 §9.1.B)
706
+ - 公开仓:通过 §9.2 流程提交与发布,不接受无维护者确认的外部发布 PR
707
+ - npm 包:GitHub Actions 自动发布(push tag v* 触发;workflow 见 §9.1.D)
708
+ - 安全 Issue:通过 GitHub Security Advisory 私下沟通
709
+
710
+ ## 17. v1.0.0 翻车教训 + 烟测加固(2026-05-01)
711
+
712
+ ### 时间线
713
+
714
+ - 2026-05-01 07:51:35Z — `npm publish @acosmi/sdk-ts@1.0.0`(首发,CI 自动)
715
+ - 同日 — 下游 crabcode(Anthropic 兼容格式消费方)反馈 P0 双层 broken
716
+ - 同日 — 源码侧 7 commit 修复落地(C0~C6: 4d53585 → 885c08a)
717
+ - 同日 — SDK 公开仓同步并重写为干净发布提交(commit `e60a88e → 0d8c0a9`)
718
+ - 同日 — `git tag v1.0.1 && git push origin v1.0.1` 触发 release.yml CI 自动 npm publish
719
+ - 同日 — `npm publish @acosmi/sdk-ts@1.0.1` 完成(tarball 594.4 kB / 36 文件 / shasum `f8805a04..` / SLSA v1 provenance)
720
+ - 同日 — audit Part 2: `npm i @acosmi/sdk-ts@1.0.1` consumer 视角实拉 + smoke `tsc --noEmit` 全绿,9 处 declare module 在 dist/node/index.d.ts 行 548/571/592/601/645/654/677/720/761 全包名
721
+ - 同日 — `npm deprecate @acosmi/sdk-ts@1.0.0 'broken packaging, use 1.0.1+'`(手动单独跑,CI 不含)
722
+
723
+ ### 翻车的两层根因
724
+
725
+ | 层 | 现象 | 根因 |
726
+ | --- | --- | --- |
727
+ | Layer 1 — packaging | bun / Node ESM `Cannot find module '@acosmi/sdk-ts'` | `tsup.config.ts` 无 `outExtension`(默认输出 `.js + .cjs`),`package.json.exports` 8 处写死 `.mjs` 引用,两侧无对账 |
728
+ | Layer 2 — d.ts augmentation | consumer 项目 `getBalance` / `submitBugReport` 等 50+ 方法 TS2339 | 9 处 `declare module` 用相对路径(`'../client'` / `'./client'`),tsup 打包 d.ts 不做 path rewrite,consumer 视角下相对路径指向不存在的文件 |
729
+ | 流程根因 | publish 前没拦截 | `prepublishOnly` 仅跑源码侧(typecheck / lint / vitest / build),不验证 **packed product 在 consumer 视角能否解析** |
730
+
731
+ ### 修复点
732
+
733
+ - **Layer 1**:`tsup.config.ts` 三 entry 显式 `outExtension: ({ format }) => ({ js: format === 'esm' ? '.mjs' : '.cjs' })`
734
+ - **Layer 2**:9 处 `declare module '../client'` / `'./client'` → `declare module '@acosmi/sdk-ts'`;附加 `tsconfig.json` 的 `paths` 让源码 typecheck self-reference
735
+ - **流程**:新增 `scripts/smoke-pack.mjs` + `prepublishOnly` 末尾 `&& npm run test:pack`
736
+
737
+ ### 烟测脚本设计要点
738
+
739
+ - **跨平台**:用 `spawnSync` 不用 `exec/execSync`(无 shell injection);`shell: isWin`(Node 20.12+ Windows 安全限制 CVE-2024-27980 修复后跑 `.cmd` 必需)
740
+ - **隔离 consumer**:`mkdtempSync(os.tmpdir())` 临时目录,不污染 caller 的 `node_modules`
741
+ - **覆盖 9 处 augmentation**:`smoke.ts` 内调用每个 `declare module` 文件至少一个 method(`getBalance` / `getWalletStats` / `listTokenPackages` / `listNotifications` / `listTools` / `browseSkillStore` / `submitBugReport` / `applyRequestSanitizers` / WS `connect` typeof 验证)
742
+ - **失败保留**:smoke 失败时保留临时目录供调试;成功才清理
743
+
744
+ ### 未来发版前必读
745
+
746
+ 1. **任何改 `tsup.config.ts` / `package.json.exports` / `declare module` 的 PR**,merge 前必须本地跑 `npm run test:pack`
747
+ 2. **publish 流程**:`npm publish` 会自动跑 `prepublishOnly`(含 `test:pack`);不可手动跳过
748
+ 3. **新增 augmentation 时**:`smoke.ts` 内补对应 method 调用,确保 consumer 视角验证覆盖
749
+ 4. **手动 deprecate 已发布版本**:`npm deprecate @acosmi/sdk-ts@<version> '<reason>'`(无法撤回但可加警告)
750
+
751
+ ### 详细变更
752
+
753
+ **历史修复 commits**:
754
+ - `f4972d3` C1 Layer 1 — tsup outExtension
755
+ - `1597d61` C2 Layer 2a — 6 处 `src/client/*.ts` declare module 绑包名 + tsconfig paths self-reference
756
+ - `47e7307` C3 Layer 2b — 3 处 `src/{ws,sanitize-bridge,bug-report}.ts` declare module 绑包名
757
+ - `9f94561` C4 Layer 3 — 新建 `scripts/smoke-pack.mjs` + `prepublishOnly` 加 `test:pack`
758
+ - `b8feb5b` C5 版本同步 1.0.0 → 1.0.1
759
+ - `885c08a` C6 文档闭环 — README + CHANGELOG + 本节 + `package.json.files` 加 CHANGELOG.md
760
+
761
+ **SDK 公开仓发布历史**:
762
+ - `0d8c0a9` release: v1.0.1 — 19 文件 745+/192-(amend + reset-author 自 `e60a88e`)
763
+ - tag: `v1.0.1` → 触发 release.yml CI
764
+
765
+ **npm registry 实拉验证** (audit Part 2 PASS):
766
+ - 包: `@acosmi/sdk-ts@1.0.1`
767
+ - tarball: `https://registry.npmjs.org/@acosmi/sdk-ts/-/sdk-ts-1.0.1.tgz`
768
+ - shasum: `f8805a0443c9b36ce7d559bd333a472ecb8fcef4`
769
+ - integrity: `sha512-CwJMxyCRULQCZT34O/dACSR65bjkRDY2T357oc0q31YFEw8LDEA/+r1Kuy7u/MmuZlsXc0q85cxFjcVcn5RgkA==`
770
+ - 包大小: 594.4 kB / 解包 2.42 MB / 36 文件
771
+ - provenance: SLSA v1(CI 自动签 attestation)
772
+ - 0 production vulnerabilities(vitest devDep 链 esbuild GHSA-67mh-4wv8-2f99 不进 production,作技术债务下个 patch 处理)
773
+ - consumer 视角 smoke `tsc --noEmit` 全绿;dist/node/index.d.ts 9 处 declare module 全 `'@acosmi/sdk-ts'`(line 548/571/592/601/645/654/677/720/761)
774
+
775
+ **完整执行档**:主仓 `docs/audit/sdk-ts-1.0.0-fix-plan-2026-05-01.md`
776
+ **CHANGELOG**:`./CHANGELOG.md`
777
+
778
+ ## 18. v2.0.0 BREAKING 复盘(2026-05-25)
779
+
780
+ ### 时间线
781
+
782
+ - 2026-05-25 — 主仓商品化 P1-P7 Phase 3 复核启动;用户钉死「不要遗漏和延迟」
783
+ - 同日 — 5-domain 并行 audit 识别 20 P0 问题(RBAC 表达式 / PII 真落盘加密链 / K7 K8 K9 集成 / admin 写端点错误码 / 跨域 sidecar)
784
+ - 同日 — 6-agent 并行实施闭环全部 20 P0(主仓 9 commit 含 K10AdminController 19 端点改 `@ss.hasRole` / SensitiveSerializer 角色严格化 / MockKmsProvider RFC 3394 AES-Wrap / AesGcmFieldCryptor v2 payload 协议)
785
+ - 同日 — CN tk_dist DB schema 补齐 V42-V67 全 24 迁移(含 3 SQL bug 热修:V47/V52/V59 `deleted=FALSE` → `deleted=0` 与 V59 `||` 拼接)
786
+ - 同日 — SDK 仓 9 commit:v2.0.0 BREAKING bump(`getMyLawyerCredentialStatus` + `getMyEnterpriseKycStatus` 2 新方法 + `finance/types.ts` PII Javadoc + `pii-role-matrix.md`)
787
+ - 同日 — `npm publish @acosmi/sdk-ts@2.0.0` via release.yml CI 自动发布
788
+ - 同日 — 用户实测验证 npm tarball **遗漏 `docs/pii-role-matrix.md` 与 `docs/开发与发布手册.md`**(`package.json.files` 数组未补齐)
789
+ - 同日 — v2.0.1 packaging fix(commit `063b379` / tag `v2.0.1`),`files` 数组改 8 项,npm tarball 实拉确认 3 个 docs 全在
790
+ - 2026-05-25 后续窗口 — 本手册 + README 深度复核审计,识别 19 项漂移与遗漏(P0×7 + P1×9 + P2×3),一窗口全量修订闭环
791
+
792
+ ### 翻车的根因
793
+
794
+ | 层 | 现象 | 根因 |
795
+ |---|------|------|
796
+ | Layer 1 — packaging | `npm install @acosmi/sdk-ts@2.0.0` 后下游找不到 `docs/pii-role-matrix.md` | v2.0.0 新建该文件,但 `package.json.files` 数组未同步追加 → tarball 不含 |
797
+ | Layer 2 — 流程 | 5-phase audit Phase C「脚手架审计」未抓到 | 上轮审计模板只关注 `release.yml` + `tsup.config.ts` + `.npmignore`,**未把"新建公开 docs 必须同步 `files` 数组"列为硬检查项** |
798
+ | Layer 3 — 文档 | 本手册 §8 `files` 清单本身就过时(v1.3.0 写到 6 项后未维护) | 文档作为「单一真相源」失效;BREAKING 发版时维护者读手册作准会反向回滚 v2.0.1 的 packaging fix |
799
+
800
+ ### 修复点
801
+
802
+ - **Layer 1**:`package.json.files` 数组改 8 项,显式列出每个 docs;任何新建公开 docs 必须同 commit 追加.
803
+ - **Layer 2**:§14 Phase C 检查项扩张「`package.json.files` 数组 vs `docs/*.md` 与 `examples/*.ts` 实际文件清单逐项核对」(详见下方红线段).
804
+ - **Layer 3**:本手册 §8 改为"任何调整必须同步本节,违反即触发 §9.3 公开仓清洁度检查失败" + §10 版本号策略增加 v2.0.0 / v2.0.1 实例段,文档自身闭环.
805
+
806
+ ### 未来发版前必读
807
+
808
+ 1. **BREAKING 发版(major bump)**专用复盘检查表:
809
+ - [ ] `package.json.files` 数组与 `docs/*.md` 实际清单逐项核对
810
+ - [ ] `package.json.files` 数组与 `examples/*.ts` 实际清单逐项核对
811
+ - [ ] `npm pack --dry-run` 输出 vs 期望文件列表比对
812
+ - [ ] 本手册 §1 当前版本、§4 目录结构、§8 `files` 清单、§10 版本号策略、§14 已审版本档案、§18 复盘节同步更新
813
+ - [ ] README §状态、§"v2.0.0 升级指引"对应节、§"API 总览"、§"更新历史"表同步更新
814
+ - [ ] `docs/pii-role-matrix.md` 等 BREAKING 引入的新公开 docs 必须随 npm tarball 下发
815
+ - [ ] 升级指引段必须给出受影响 callsite 清单与"零改动 / 必须 review"二分判定
816
+ 2. **新建公开 docs 时**:同 commit 必须改 3 处 — 新文件本身 + `package.json.files` 追加 + 本手册 §8 与 §15 链接段同步.
817
+ 3. **手动 deprecate broken BREAKING 版本**:`npm deprecate @acosmi/sdk-ts@<version> '<reason>'`(无法撤回但可加警告;v2.0.0 未走该步因 v2.0.1 在同日内发布修复,consumer 视角 `npm i @acosmi/sdk-ts` 自动跳到 2.0.1).
818
+
819
+ ### npm registry 实拉验证(v2.0.1)
820
+
821
+ - 包: `@acosmi/sdk-ts@2.0.1`(latest tag)
822
+ - tarball 含: `dist/` + `README.md` + `CHANGELOG.md` + `LICENSE` + `docs/compliance.md` + `docs/pii-role-matrix.md` + `docs/开发与发布手册.md` + `examples/`
823
+ - 主仓 HEAD(Phase 3 复核闭环): `e510f68a`
824
+ - SDK 仓 HEAD: `063b379` / tag `v2.0.1`
825
+
826
+ ### 关联文档
827
+
828
+ - 主仓 Phase 3 复核 handoff:`memory/commercialization-p1p7-phase3-deep-review-handoff.md`
829
+ - 本手册深度复核审计:本次会话即为复盘成果,无独立 audit 报告(成果直接落入 README + 本手册)
830
+ - v2.0.0 升级指引:[README.md §v2.0.0 升级指引](../README.md#v200-升级指引)
831
+ - PII 角色矩阵:[docs/pii-role-matrix.md](./pii-role-matrix.md)