@sema-agent/sdk 6.12.0 → 6.14.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,381 @@
1
+ /**
2
+ * rules — **持久权限规则**的导入面 + 撤销面(#154 车二 / design/179 §7 + design/203 §2;
3
+ * server `src/http/routes/rules.ts`)。
4
+ *
5
+ * `ccImportPrepare(layers)` → POST /v1/rules/cc-import/prepare — 读交上来的 CC settings 层,回**预览** +
6
+ * 一张一次性票(还没有任何规则落库)
7
+ * `ccImportRedeem(ticket)` → POST /v1/rules/cc-import/redeem — 原子消费票 → 确认 → 批量兑付进店
8
+ * `list(params?)` → GET /v1/rules — 列自己名下**活着**的规则(keyset 分页,
9
+ * 游标绑 rev)
10
+ * `revoke(input)` → DELETE /v1/rules — 按 `(rule, scope)` **内容**撤销
11
+ * (**路径上没有 `:id` 段**)
12
+ *
13
+ * 🔴 **两步不是仪式,是同意协议的形**:第一步只产生「如果确认会发生什么」的预览;只有第二步够得着写面。
14
+ * 中间那张票是 **principal 绑定 / 有期(10 分钟)/ 一次性原子消费 / 载荷绑定** 的 —— 它不是可缓存的凭据,
15
+ * 也不该跨用户传递。
16
+ *
17
+ * ⚠️ **prepare 不是零写入、也不幂等**(codex 交叉复审 [medium],亲读 server `rules-consent.ts` 验真):
18
+ * 没有任何**规则**落库,但每一次成功的 prepare 都会落一条 **pending 审批记录**并**再铸一张有效票**。
19
+ * 所以别把它当可随手重放的预览接口:预览一次、呈给人看、然后兑付或丢弃。(超候选帽的拒绝会 best-effort
20
+ * 收掉刚落的那条记录 —— 收不掉只是留一条不影响任何判决的孤儿行,但那也不是「零残留」的承诺。)
21
+ *
22
+ * 🔴 **导入两口 lane = principal,不是 operator**:用户导的是**他自己**的规则,一条规则的语义就是「这个人
23
+ * 自己说过 yes」。运维在这条路上没有位置(他既不该替租户扩权,也不该被要求代人点确认);一个 operator 要
24
+ * 给自己导规则,用他自己的 principal 走同一条路即可。连 operator 旁路都不给。
25
+ *
26
+ * 🔴 **撤销面两口多一条 operator 越权域,而这不是上面那句话的例外、是它的对偶**:`principal` 让一个显式
27
+ * 列名的 operator 读**或**收回任一租户的规则。**方向决定授权** —— 收回是**收紧**(core `removePersistedRule`
28
+ * 头注逐字:a host may narrow on a user's behalf, it may not widen),而多租户管理员能回收权限正是撤销面的
29
+ * 本义。读那一半同样只开给 operator(规则文本含命令样式,跨 principal 读是信息面泄露),且**不开**第三方
30
+ * principal 相互读。判据是**显式名单**:`OPERATOR_PRINCIPALS` 为空 = **谁都不是** operator(不是「全员」),
31
+ * 也绝不从 service token 推断身份 —— 一个共享的部署凭证不是一个人。
32
+ *
33
+ * 🔴 **只读 allow 桶**:deny/ask 桶是**收紧**方向,有自己的通道 —— 从一条放松的车道导进来是走错门。
34
+ * 预览的 `uncovered` 把「本版没读的两层」写进 TYPE(两键记录,不是列表):一份只报了自己读过的层的预览,
35
+ * 是在冒充它没有的完整性。
36
+ *
37
+ * 🔴 **redeem 的 404 是同形的**:伪造的票 / 别人的票 / 过期 / 已用过 —— 四个票面成因在 wire 上**一个字都
38
+ * 不差**(返回不同的码就是一台存在性 oracle)。⚠️ 而且它**不止**这四类:记录不见了、载荷摘要对不上、
39
+ * 确认步的裁定性拒绝 —— 这些「服务端状态/完整性」失败**也**折进同一个 404。所以 404 的含义只有一句
40
+ * 「这张票在这里不可用,重来一遍」,**禁按成因分支**,也别把它一律读成「用户拿错票了」(分类只进服务端
41
+ * 日志)。唯一可分的是 503 `state.rule_import_retry`(→ {@link import("../errors.js").ServiceStateError}):
42
+ * 那一支意味着**票还在**,原样重试即可,与「这张票没了」是相反的处置。
43
+ *
44
+ * 🔴 **部署位分两段读**(codex 对抗复审 [high],亲读 server `config.ts` 的 `boolEnv` 默认值验真 ——
45
+ * 旧文只写「默认关」,自 server 7.12.0 起是**假话**):`PERMISSION_RULES_ENABLED` 在 **server ≤7.11.0
46
+ * 默认 `false`(opt-in)**,自 **7.12.0 起默认 `true`(opt-out)**。所以「运维没表态」在新旧两代 server 上
47
+ * 是**相反**的结局 —— 显式写 `false` 才是关。⚠️ 连带后果(server boot 期为此专门加了休眠行审计):
48
+ * 一台旧部署里因为旋钮关着而**躺着没生效**的持久规则,升到 7.12.0 之后会随默认 ON **一起激活**。
49
+ * 车道还需真装配规则店 —— 旋钮与店缺一 ⇒ **四口全部** 501 `capability.rule_store_required`(→
50
+ * {@link import("../errors.js").CapabilityUnavailableError}),同一条谓词也让审批帧上不带 `ruleSuggestions`。
51
+ * 🔴 **更正(6.14.0):旧文写「`/v1/capabilities` 上今天没有这一族的能力位」,已作废** ——
52
+ * `capabilities.permissionRules` 就是这一位,且它与这台 server **所有**规则口的 501 是**同一条**谓词
53
+ * (说 yes ⟺ 它有的那些口全都活,不是「有一口活」)。**按它 gate,别 trial-by-501**,更别按版本号猜。
54
+ * ⚠️ 面的条数随 server 版本长(撤销面两口自 7.12.0 起才有),本位说的是「车道在不在」而非「有几条口」。
55
+ */
56
+ import type { Transport } from "../transport.js";
57
+ /** 本版读的三个用户可编辑 settings 层(core `ImportedSettingsLayer`)。 */
58
+ export type CcImportSettingsLayer = "userSettings" | "projectSettings" | "localSettings";
59
+ /** 一条持久规则的作用域(core `RuleScope`)。`project` 的 `root` 是这条规则将来在哪些 cwd 下生效的前缀。 */
60
+ export type RuleScope = {
61
+ kind: "global";
62
+ } | {
63
+ kind: "project";
64
+ root: string;
65
+ };
66
+ /** 一条候选规则:规范文本 + 它会落在哪个作用域(core `RuleCandidate`)。 */
67
+ export interface RuleCandidate {
68
+ rule: string;
69
+ scope: RuleScope;
70
+ }
71
+ /**
72
+ * 一条没进候选的条目。
73
+ *
74
+ * 🔴 **`reason` 是给人看的散文,不是机读码**(codex 交叉复审 [medium],亲读 core `prepareCcImport` 验真):
75
+ * 真值形有三种 —— 语法拒是 `` `${code}: ${message}` ``(**前缀**是 `RuleRejectCode`,如
76
+ * `"invalid.bare_interpreter_prefix: …"`,不是裸码)、整层 JSON 解不开是
77
+ * `` `settings file is not valid JSON (${err})` ``、条目不是串是 `"settings entry is not a string"`。
78
+ * 想分类就**只**按 `:` 前那截前缀,而且要容得下没有前缀的两种;别做全串等值匹配,更别指望裸码。
79
+ *
80
+ * ⚠️ `rule` 也不总是一条规则文本:整层 JSON 解不开的那一支,这里放的是**那一层的 path**(条目还没解析出来,
81
+ * 没有规则可放)。渲染时别假定它一定是 `Bash(...)` 形。
82
+ */
83
+ export interface CcImportSkippedRule {
84
+ rule: string;
85
+ reason: string;
86
+ }
87
+ /**
88
+ * 预览里的逐层读取报告。
89
+ *
90
+ * ⚠️ **在这条 HTTP 路上 `found` 恒 `true`**(codex 交叉复审 [medium],亲读验真):core 只在它那个注入的
91
+ * 读口返回 `undefined` 或抛错时才写 `false`,而 server 的适配器把请求体里**必填**的 `content` 原样交回去
92
+ * ⇒ 永不缺席。空文本、`{}`、没有 allow 桶都照样是 `found: true`(它们只是不产候选)。所以**别**把
93
+ * `found` 当「这一层有没有规则」的判据 —— 那要看 `candidates` 里有没有这一层作用域的条目。
94
+ * 字段保留是因为它是 core 预览形的一部分(别的宿主用真文件系统读口时它才有判别力)。
95
+ */
96
+ export interface CcImportLayerReport {
97
+ path: string;
98
+ layer: CcImportSettingsLayer;
99
+ found: boolean;
100
+ }
101
+ /**
102
+ * 一层交上来的 settings(server 侧 zod 是 `.strict()` —— 多一个键 = 400,把 `content` 拼成 `contents`
103
+ * 的客户端应当场知道,而不是拿到一份「零候选」的预览去纳闷)。
104
+ *
105
+ * 🔴 `root` **非空是硬的**:`root: ""` 在 core 里会变成 `{kind:"project", root:""}`,而空前缀**包含每一个
106
+ * cwd** —— 一条项目内规则就此悄悄升成全局规则。server 侧当场 400。
107
+ * ⚠️ `root` 的**真实性**不校验:云端 server 上没有那棵树可对照,假装校验过才是谎;它只是「这条规则在哪些
108
+ * cwd 下生效」的用户自陈标签。
109
+ * 上限:单层 `content` ≤ 16 KiB,`layers` 一次 1–3 层(超出 ⇒ 400 / 413)。
110
+ */
111
+ export interface CcImportLayer {
112
+ layer: CcImportSettingsLayer;
113
+ path: string;
114
+ root: string;
115
+ content: string;
116
+ }
117
+ /**
118
+ * 导入**会做什么**(在任何人确认之前)。
119
+ *
120
+ * `uncovered` 是**两键记录而不是列表**(core 原形):本版不读的两层写在 TYPE 里,于是「空列表 / 少一员 /
121
+ * 重复一员」都不可拼写。
122
+ */
123
+ export interface CcImportPreview {
124
+ /** 会被持久化的候选(顺序即兑付顺序)。 */
125
+ candidates: RuleCandidate[];
126
+ /** 没进候选的条目(`reason` 是散文,不是码 —— 见 {@link CcImportSkippedRule})。 */
127
+ skipped: CcImportSkippedRule[];
128
+ /** 逐层读取报告。 */
129
+ layers: CcImportLayerReport[];
130
+ /** 本版**不读**的两层,写死在类型里(不是运行期列表)。 */
131
+ uncovered: {
132
+ flagSettings: "not-imported-v1";
133
+ policySettings: "not-imported-v1";
134
+ };
135
+ }
136
+ /** `prepare` 的 200 体。**此刻还没有任何规则落库** —— 落库要等 `redeem`;但 pending 记录与这张票已经是
137
+ * durable 的(prepare 不幂等,见模块顶注)。 */
138
+ export interface CcImportPrepareResult {
139
+ preview: CcImportPreview;
140
+ /** 一次性、principal 绑定、有期、载荷绑定的兑付票。不可缓存复用,不该跨用户传递。 */
141
+ ticket: string;
142
+ /** 票的绝对过期时刻(server 时钟毫秒;窗 = 10 分钟)。 */
143
+ expiresAtMs: number;
144
+ }
145
+ /**
146
+ * 导入**真做了什么** —— 与预览是**不同时刻、不同契约**(去重、并发、兑付期校验都可能让一条在两者之间
147
+ * 换位置)。
148
+ *
149
+ * ⚠️ `skippedAtRedeem` 在本车道的成功臂上恒为空:server 把「非空」一律当**不确定**处理并放回认领
150
+ * (于是你看到的是 503 可重试,而不是一个「一半没落地」的 200)。它留在形上是为了 core 契约同形。
151
+ */
152
+ export interface CcImportResult {
153
+ /** 本次真落库的规则。 */
154
+ persisted: RuleCandidate[];
155
+ /** 已经在店里、这次不重复添加的(不是失败)。 */
156
+ deduped: RuleCandidate[];
157
+ /** 兑付期被逐条拒绝的。 */
158
+ skippedAtRedeem: CcImportSkippedRule[];
159
+ /** 规则店的单调修订号(兑付后的读数)。 */
160
+ rev: number;
161
+ }
162
+ /** `redeem` 的 200 体。 */
163
+ export interface CcImportRedeemResult {
164
+ result: CcImportResult;
165
+ }
166
+ /**
167
+ * 一次 add 的因果身份(core `RuleDot`)。
168
+ *
169
+ * 🔴 **按身份比,不按因果序比**:不同副本铸的单点之间没有序可比。`counter` 只在**同一个 `actor` 内**
170
+ * 单调,跨 actor 比大小是无意义的;它也不是时间戳、不是排序键 —— 当不透明二元组用。
171
+ */
172
+ export interface RuleDot {
173
+ /** 铸出这次 add 的副本身份。 */
174
+ actor: string;
175
+ /** 该副本铸这一点时的单调计数(**仅**同 actor 内可比)。 */
176
+ counter: number;
177
+ }
178
+ /**
179
+ * 一次 add 的来源(core `RuleAddOrigin`)。**按 add 存,绝不跨 dot 折叠** —— 折过的来源没法跟着某一个
180
+ * 单独被删掉的 dot 走。
181
+ *
182
+ * `user` = 审批卡上点的「不再询问」;`imported-cc` = 走 CC settings 导入道兑付进来的;`starter` = 部署
183
+ * 预置。wire 上是闭集,读的时候仍然带 default 臂(词表属主在引擎侧)。
184
+ */
185
+ export type RuleAddOrigin = "user" | "imported-cc" | "starter";
186
+ /**
187
+ * 一条规则的**一次** add(core `RuleAdd`)。
188
+ *
189
+ * 🔴 **这是一个真集合,不是标量**:同一个 `(rule, scope)` 可以被批准过多次(不同 dot、不同来源、不同
190
+ * 时刻),而一条规则**活着**的判据是它至少有一次 add 没被墓碑盖掉。折成一个 `addedAt` 会让「这条规则是
191
+ * 我导进来的还是我点卡点出来的」在治理面上不可分 —— 而那恰恰是唯一需要分清的地方。
192
+ */
193
+ export interface RuleAdd {
194
+ dot: RuleDot;
195
+ origin: RuleAddOrigin;
196
+ /** 这次 **add** 的记录时刻(引擎盖的 ISO-8601)。它记的是 add 不是规则:多 add 的规则有多个这种时刻。 */
197
+ createdAt: string;
198
+ }
199
+ /**
200
+ * `GET /v1/rules` 列出来的一条**活着**的持久 allow 规则(server `PersistedRuleWireRow`)。墓碑已折算 ——
201
+ * 这里的每一行在 {@link RuleListResult.rev} 那一刻都是活的。
202
+ *
203
+ * 🔴 `scope` 是**判别式串**(`global` / `project:<root>`),**不是**导入道说的 {@link RuleScope} 对象:
204
+ * 它就是撤销体想要回去的那串字节,所以**原样回传**,别拿解析后的形重新序列化(拼法等价但不逐字相同的
205
+ * root 什么都匹配不上,而且会得到一个笑呵呵的 `no-op`)。
206
+ *
207
+ * 🔴 规则车道**只有 allow**:deny/ask 是收紧方向,有自己的通道,永远不会出现在这里。
208
+ */
209
+ export interface PersistedRule {
210
+ /** 规范规则文本 —— `Bash(git status)`(exact)或 `Bash(git status:*)`(prefix)。撤销时**原样**回传。 */
211
+ rule: string;
212
+ /** 作用域判别式串:`global`,或 `project:<root>`(root **非空**)。撤销时**原样**回传。 */
213
+ scope: string;
214
+ /** v1 规则车道只说这一个工具。字段留着是为了下一版能不改形地加员 —— **读它**,别假定。 */
215
+ tool: "Bash";
216
+ /** `exact` = 只这一条命令;`prefix` = 词边界前缀。(core 给 v2 预留了 `wildcard`,现役任何版本都不产。) */
217
+ match: "exact" | "prefix";
218
+ /** 匹配器**真正**拿去比的东西:铸规则时从 `rule` 里解出来存下的,省得每个消费端各自重解一遍。
219
+ * 信它 + `match`,别信你自己对 `rule` 的解析。 */
220
+ command: string;
221
+ /** 这条规则**全部活着的** add(见 {@link RuleAdd} —— 真集合,刻意不折叠)。 */
222
+ adds: RuleAdd[];
223
+ }
224
+ /**
225
+ * `rules.list()` 的 200 体。
226
+ *
227
+ * `nextCursor` **缺席 = 这是最后一页**(没有空串哨兵)。`rev` 是规则桶的 OCC 版本,游标绑着它 ——
228
+ * 拿旧游标翻页会被**拒**,而不是静默从头开始。
229
+ */
230
+ export interface RuleListResult {
231
+ /** 活着的规则,按 `(scope, rule)` 字典序 —— 由 server 定的确定性序(keyset 分页全靠它)。 */
232
+ rules: PersistedRule[];
233
+ /** 规则桶的 OCC 版本。🔴 **桶局部**:两位租户各有一条规则时两边都读到 `rev: 1` ——
234
+ * 它不是全局时钟,跨 principal 之间不可比。 */
235
+ rev: number;
236
+ /** 下一页的不透明 base64url 游标。**缺席 ⇒ 没有下一页**。原样回传,别解析、别自己铸。 */
237
+ nextCursor?: string;
238
+ }
239
+ /**
240
+ * `rules.list()` 的查询入参(纯客户端调用参数 —— wire 上它们是 path 的 query parameters)。
241
+ */
242
+ export interface RuleListParams {
243
+ /**
244
+ * 🔴 **operator 越权域**:读**别人**名下的规则。缺席(或等于自己的 principal)= 读自己的,零授权提问。
245
+ * 给别人的名字则**必须**是 `OPERATOR_PRINCIPALS` 里显式列名的身份,否则 403 `auth.operator_only`
246
+ * (**空名单 = 谁都不是**,不是「全员」;也绝不从 service token 推断 —— 一个共享部署凭证不是一个人)。
247
+ * 规则文本里含命令样式,跨 principal 读是信息面泄露,所以**不开**第三方 principal 相互读。
248
+ */
249
+ principal?: string;
250
+ /** 只看一个作用域:`global` 或 `project:<root>`(root **非空**)。读不出形 ⇒ 400
251
+ * `request.query_invalid`,**不会**被当成「没过滤」—— 那会把一次「只看这个项目」悄悄变成全量列举,
252
+ * 而调用方会照单全收当成该项目的清单。过滤按**规范形**比对。 */
253
+ scope?: string;
254
+ /** 页大小。**夹取**(非数/越界一律夹进 1..200)而不是拒绝 —— 一个分页参数写错不该让治理面整个打不开。
255
+ * 缺省 50。 */
256
+ limit?: number;
257
+ /** 上一页的 {@link RuleListResult.nextCursor},**原样**。见 {@link RulesResource.list} 的游标段。 */
258
+ cursor?: string;
259
+ }
260
+ /** `rules.revoke()` 的请求体。规则**按内容**寻址 —— 这条面上下没有任何 id。 */
261
+ export interface RuleRevokeRequest {
262
+ /** 规范规则文本,**逐字**照 {@link PersistedRule.rule}。 */
263
+ rule: string;
264
+ /** 作用域判别式串,**逐字**照 {@link PersistedRule.scope}(`global` / `project:<非空 root>`)。 */
265
+ scope: string;
266
+ /** operator 越权域:替**别人**收回。缺席(或自己的 principal)= 收自己的。判据同读半场。 */
267
+ principal?: string;
268
+ }
269
+ /**
270
+ * `rules.revoke()` 的 200 体。
271
+ *
272
+ * `removed` = 落了墓碑;`no-op` = 没匹配上(幂等重删,或本来就没有 —— **刻意不是 404**,那会是一台
273
+ * 存在性 oracle)。**两支都带 `stillLive`**,键集刻意同形,消费端一套分支读得通。
274
+ */
275
+ export interface RuleRevokeResult {
276
+ /** 这次调用有没有落墓碑。`no-op` 对「这条规则曾经存在过吗」**什么都没说** —— 别渲成「找不到」。 */
277
+ status: "removed" | "no-op";
278
+ /** 操作后规则桶的 OCC 版本。 */
279
+ rev: number;
280
+ /**
281
+ * 🔴 `true` = 这条规则**仍然活着**:add-wins 之下,本次调用期间又落了一次批准。
282
+ * **没读它之前别对人说「规则已经没了」。**
283
+ * `no-op` 那一支也带它,理由同族:引擎按初始快照就回 `no-op`、不做读回,所以并发批准会让规则活着而
284
+ * 动词报「没什么可删」。
285
+ *
286
+ * 🔴 **两个真值的证明力不对称,而这条不对称在引擎里、不在这里**(codex 对抗复审 [high],亲读 core
287
+ * `removePersistedRule` 验真):`true` 是**正证据**(真的观察到它还活着);`false` 只是「没有它还活着
288
+ * 的证据」—— `removed` 那一支的确认读回被引擎包在一个 `catch { stillLive = false }` 里,于是**店抖动
289
+ * 时**回出来的 `{status:"removed", stillLive:false}` 与一次真验证过的撤销**逐字节相同**。
290
+ * (`no-op` 支没有这个洞:那次读回是 server 自己做的,失败就让它抛 500,不编答案。)
291
+ * 处置:见 `true` 就当真;而当「它到底没了没有」**要紧**时(运维收回一条常驻放行),用
292
+ * {@link RulesResource.list} 对账,别把 `false` 当成证明。已作为上游请托登记(不确定该是它自己的一个
293
+ * 状态,而不是折进 `false`)。
294
+ */
295
+ stillLive: boolean;
296
+ }
297
+ export declare class RulesResource {
298
+ private readonly t;
299
+ constructor(t: Transport);
300
+ /** POST /v1/rules/cc-import/prepare — 交 1–3 层 CC settings,拿**预览 + 一张票**(零规则落库,但**有**
301
+ * durable 副作用:一条 pending 记录 + 一张票 ⇒ 不幂等,别循环重放,见模块顶注)。
302
+ *
303
+ * 典型用法:壳读用户机器上的 `~/.claude/settings.json` / `<repo>/.claude/settings.json` /
304
+ * `<repo>/.claude/settings.local.json`,把**内容**交上来(云端 server 上没有用户的文件系统 —— 这是部署形,
305
+ * 不是绕过:读的仍然只有 allow 桶)。把 `preview.candidates` 呈给人看,人点确认再调 {@link ccImportRedeem}。
306
+ *
307
+ * 413 `request.payload_too_large`(→ {@link import("../errors.js").PayloadTooLargeError})= 全部层合计候选
308
+ * 超上限(200 条):**缩短后重发**,重试同一份没有意义。501 = 本部署没接规则店。 */
309
+ ccImportPrepare(layers: CcImportLayer[], opts?: {
310
+ signal?: AbortSignal;
311
+ }): Promise<CcImportPrepareResult>;
312
+ /** POST /v1/rules/cc-import/redeem — 用 {@link ccImportPrepare} 给的票原子兑付整批。
313
+ *
314
+ * 🔴 **不是 submit 类,SDK 不自动重试**:票是一次性消费的,一次自动重放就等于把人的确认烧掉两次。
315
+ * 要重试请**自己**在拿到 503 `state.rule_import_retry` 之后重发(那一支明说票还在;体里 `retryAfterSec`
316
+ * 与 `Retry-After` 同值)。
317
+ * 🔴 404 `not_found.rule_ticket` 是一个**同形信封**:四类票面拒绝(伪造/别人的/过期/已用过)**加上**
318
+ * 非可重试的记录/载荷失败(记录不见了、载荷摘要对不上、确认步裁定性拒绝)全折在这一格。按它一丢了之
319
+ * 即可 —— 别试图分辨成因(wire 上分不出来是设计),也别把它一律读成「用户拿错票了」。 */
320
+ ccImportRedeem(ticket: string, opts?: {
321
+ signal?: AbortSignal;
322
+ }): Promise<CcImportRedeemResult>;
323
+ /**
324
+ * `GET /v1/rules` —— 列出一位 principal 名下**活着**的规则(墓碑已折算),keyset 分页。
325
+ *
326
+ * 序 = `(scope, rule)` 字典序,由 **server** 定死(core 的 `list()` 不承诺顺序)—— keyset 分页的全部
327
+ * 前提就是「同一份数据每次以同一个顺序出现」。
328
+ *
329
+ * 🔴 **游标绑 `(rev, principal, scope)`,对不上是「拒」不是「重来」。** 规则集是**整份读**的,所以一页
330
+ * 之内没有撕裂;撕裂发生在**两页之间** —— 第一页之后有人加了或删了一条,第二页的 keyset 起点就落在
331
+ * 一份不同的集合上,结果是静默漏行或重行。拿一个在别的 rev / 别的 principal / 别的 scope 过滤上铸的
332
+ * 游标来翻页 ⇒ 400 `request.query_invalid`(体里带当前 `rev`),**丢掉游标从头列**即可。静默重置的代价
333
+ * 是客户端把第二页当成「接着上一页」,治理面上于是出现一份既漏行又重行的清单,而没有任何人知道。
334
+ *
335
+ * 🔴 **翻页要翻完**:`nextCursor` 缺席才是终点。半途停下拿到的是**一份不全的清单**,而这条面的消费者
336
+ * (治理视图)恰恰最不能拿不全的清单当全量。
337
+ *
338
+ * 越权读见 {@link RuleListParams.principal}(403 `auth.operator_only`);未接规则店 ⇒ 501
339
+ * `capability.rule_store_required`(→ {@link import("../errors.js").CapabilityUnavailableError})。
340
+ * GET ⇒ SDK 的重试矩阵**会**自动重试瞬时失败(纯读,无副作用)。
341
+ *
342
+ * 🔴 **版本偏斜:gate 用 `capabilities.permissionRulesRevoke`,不是 `permissionRules`**(codex 对抗复审
343
+ * R2 [medium] 提出,上游采纳后 server 7.12.0 加位)。`permissionRules` 比撤销面**早出生** —— 一台
344
+ * 7.11.0 worker 完全可以报 `true`(导入两口真的活着)而这两口根本没被路由;那个位已发布的成文语义
345
+ * 修不动,所以上游**加了一位**而不是追改旧位。判据两段:
346
+ * · `permissionRulesRevoke` **在场** ⇒ 路由已铸(接着答不答仍看 `permissionRules`);
347
+ * · **缺席** ⇒ 这台 worker 比撤销面老 ⇒ 直接藏起治理入口。真打了也能认:server 的未命中路由臂是
348
+ * **404 `not_found.route`**(已鉴权的调用方;未鉴权会先撞全局 401 `auth.unauthorized`)⇒ 拿到
349
+ * {@link import("../errors.js").NotFoundError} 且 `errorCode` 为 `not_found.route`,与 501(没接店)
350
+ * 和本族其它 404 都可分。
351
+ */
352
+ list(params?: RuleListParams, opts?: {
353
+ signal?: AbortSignal;
354
+ }): Promise<RuleListResult>;
355
+ /**
356
+ * `DELETE /v1/rules` —— 按**内容** `(rule, scope)` 撤销一条持久规则。**路径上没有 `:id` 段**:一条规则
357
+ * 的身份就是那个内容对,server 不铸行 id,引擎的 `removePersistedRule` 原语按 observed-remove 形在 add
358
+ * 的 dot 上工作。
359
+ *
360
+ * 🔴 **幂等,而且刻意不是 404**:重复删 / 本来就没有 ⇒ `200 {status:"no-op"}`。一个「这条规则存在过吗」
361
+ * 的 404 就是一台存在性 oracle(在 operator 越权域下更是跨租户的那种),而删除对「有没有过」本来就不
362
+ * 该作 oracle。要判「我删到东西了吗」,读 `status`。
363
+ *
364
+ * 🔴 **读 {@link RuleRevokeResult.stillLive} 再对人下结论**:它为真意味着墓碑落了但规则**仍然活着**
365
+ * (add-wins;本次调用期间又落了一次批准)。两支 200 都带这一位,所以一套分支读得通。
366
+ * ⚠️ 但**两个真值证明力不对称**:`false` 不是「已经没了」的证明(`removed` 支的确认读回在引擎里被
367
+ * `catch` 吞成 `false`)—— 要紧的场合用 {@link list} 对账,逐条理由见该字段的注。
368
+ *
369
+ * 🔴 **SDK 不自动重试**(非 submit 类的 DELETE):503 `state.rule_remove_failed`(→
370
+ * {@link import("../errors.js").ServiceStateError})的语义是「读回确认什么都没写,**或者**读回本身也
371
+ * 失败、结局不定」—— 两种都是重试可改判的,所以**自己**重试,然后用 {@link list} 对账。
372
+ *
373
+ * 越权撤销见 {@link RuleRevokeRequest.principal}(403 `auth.operator_only`);未接规则店 ⇒ 501。
374
+ * 🔴 **版本偏斜判据同 {@link list}**:gate 用 `capabilities.permissionRulesRevoke`(缺席 = 这台 worker
375
+ * 比撤销面老,真打会拿 404 `not_found.route`);`permissionRules` 为 true **不蕴含**这口在。
376
+ */
377
+ revoke(input: RuleRevokeRequest, opts?: {
378
+ signal?: AbortSignal;
379
+ }): Promise<RuleRevokeResult>;
380
+ }
381
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/resources/rules.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,120 @@
1
+ export class RulesResource {
2
+ t;
3
+ constructor(t) {
4
+ this.t = t;
5
+ }
6
+ /** POST /v1/rules/cc-import/prepare — 交 1–3 层 CC settings,拿**预览 + 一张票**(零规则落库,但**有**
7
+ * durable 副作用:一条 pending 记录 + 一张票 ⇒ 不幂等,别循环重放,见模块顶注)。
8
+ *
9
+ * 典型用法:壳读用户机器上的 `~/.claude/settings.json` / `<repo>/.claude/settings.json` /
10
+ * `<repo>/.claude/settings.local.json`,把**内容**交上来(云端 server 上没有用户的文件系统 —— 这是部署形,
11
+ * 不是绕过:读的仍然只有 allow 桶)。把 `preview.candidates` 呈给人看,人点确认再调 {@link ccImportRedeem}。
12
+ *
13
+ * 413 `request.payload_too_large`(→ {@link import("../errors.js").PayloadTooLargeError})= 全部层合计候选
14
+ * 超上限(200 条):**缩短后重发**,重试同一份没有意义。501 = 本部署没接规则店。 */
15
+ async ccImportPrepare(layers, opts) {
16
+ return this.t.request({
17
+ method: "POST",
18
+ path: "/v1/rules/cc-import/prepare",
19
+ body: { layers },
20
+ signal: opts?.signal,
21
+ });
22
+ }
23
+ /** POST /v1/rules/cc-import/redeem — 用 {@link ccImportPrepare} 给的票原子兑付整批。
24
+ *
25
+ * 🔴 **不是 submit 类,SDK 不自动重试**:票是一次性消费的,一次自动重放就等于把人的确认烧掉两次。
26
+ * 要重试请**自己**在拿到 503 `state.rule_import_retry` 之后重发(那一支明说票还在;体里 `retryAfterSec`
27
+ * 与 `Retry-After` 同值)。
28
+ * 🔴 404 `not_found.rule_ticket` 是一个**同形信封**:四类票面拒绝(伪造/别人的/过期/已用过)**加上**
29
+ * 非可重试的记录/载荷失败(记录不见了、载荷摘要对不上、确认步裁定性拒绝)全折在这一格。按它一丢了之
30
+ * 即可 —— 别试图分辨成因(wire 上分不出来是设计),也别把它一律读成「用户拿错票了」。 */
31
+ async ccImportRedeem(ticket, opts) {
32
+ return this.t.request({
33
+ method: "POST",
34
+ path: "/v1/rules/cc-import/redeem",
35
+ body: { ticket },
36
+ signal: opts?.signal,
37
+ });
38
+ }
39
+ /**
40
+ * `GET /v1/rules` —— 列出一位 principal 名下**活着**的规则(墓碑已折算),keyset 分页。
41
+ *
42
+ * 序 = `(scope, rule)` 字典序,由 **server** 定死(core 的 `list()` 不承诺顺序)—— keyset 分页的全部
43
+ * 前提就是「同一份数据每次以同一个顺序出现」。
44
+ *
45
+ * 🔴 **游标绑 `(rev, principal, scope)`,对不上是「拒」不是「重来」。** 规则集是**整份读**的,所以一页
46
+ * 之内没有撕裂;撕裂发生在**两页之间** —— 第一页之后有人加了或删了一条,第二页的 keyset 起点就落在
47
+ * 一份不同的集合上,结果是静默漏行或重行。拿一个在别的 rev / 别的 principal / 别的 scope 过滤上铸的
48
+ * 游标来翻页 ⇒ 400 `request.query_invalid`(体里带当前 `rev`),**丢掉游标从头列**即可。静默重置的代价
49
+ * 是客户端把第二页当成「接着上一页」,治理面上于是出现一份既漏行又重行的清单,而没有任何人知道。
50
+ *
51
+ * 🔴 **翻页要翻完**:`nextCursor` 缺席才是终点。半途停下拿到的是**一份不全的清单**,而这条面的消费者
52
+ * (治理视图)恰恰最不能拿不全的清单当全量。
53
+ *
54
+ * 越权读见 {@link RuleListParams.principal}(403 `auth.operator_only`);未接规则店 ⇒ 501
55
+ * `capability.rule_store_required`(→ {@link import("../errors.js").CapabilityUnavailableError})。
56
+ * GET ⇒ SDK 的重试矩阵**会**自动重试瞬时失败(纯读,无副作用)。
57
+ *
58
+ * 🔴 **版本偏斜:gate 用 `capabilities.permissionRulesRevoke`,不是 `permissionRules`**(codex 对抗复审
59
+ * R2 [medium] 提出,上游采纳后 server 7.12.0 加位)。`permissionRules` 比撤销面**早出生** —— 一台
60
+ * 7.11.0 worker 完全可以报 `true`(导入两口真的活着)而这两口根本没被路由;那个位已发布的成文语义
61
+ * 修不动,所以上游**加了一位**而不是追改旧位。判据两段:
62
+ * · `permissionRulesRevoke` **在场** ⇒ 路由已铸(接着答不答仍看 `permissionRules`);
63
+ * · **缺席** ⇒ 这台 worker 比撤销面老 ⇒ 直接藏起治理入口。真打了也能认:server 的未命中路由臂是
64
+ * **404 `not_found.route`**(已鉴权的调用方;未鉴权会先撞全局 401 `auth.unauthorized`)⇒ 拿到
65
+ * {@link import("../errors.js").NotFoundError} 且 `errorCode` 为 `not_found.route`,与 501(没接店)
66
+ * 和本族其它 404 都可分。
67
+ */
68
+ async list(params, opts) {
69
+ const q = new URLSearchParams();
70
+ if (params?.principal !== undefined)
71
+ q.set("principal", params.principal);
72
+ if (params?.scope !== undefined)
73
+ q.set("scope", params.scope);
74
+ if (params?.limit !== undefined)
75
+ q.set("limit", String(params.limit));
76
+ if (params?.cursor !== undefined)
77
+ q.set("cursor", params.cursor);
78
+ const qs = q.toString();
79
+ return this.t.request({
80
+ method: "GET",
81
+ path: qs === "" ? "/v1/rules" : `/v1/rules?${qs}`,
82
+ signal: opts?.signal,
83
+ });
84
+ }
85
+ /**
86
+ * `DELETE /v1/rules` —— 按**内容** `(rule, scope)` 撤销一条持久规则。**路径上没有 `:id` 段**:一条规则
87
+ * 的身份就是那个内容对,server 不铸行 id,引擎的 `removePersistedRule` 原语按 observed-remove 形在 add
88
+ * 的 dot 上工作。
89
+ *
90
+ * 🔴 **幂等,而且刻意不是 404**:重复删 / 本来就没有 ⇒ `200 {status:"no-op"}`。一个「这条规则存在过吗」
91
+ * 的 404 就是一台存在性 oracle(在 operator 越权域下更是跨租户的那种),而删除对「有没有过」本来就不
92
+ * 该作 oracle。要判「我删到东西了吗」,读 `status`。
93
+ *
94
+ * 🔴 **读 {@link RuleRevokeResult.stillLive} 再对人下结论**:它为真意味着墓碑落了但规则**仍然活着**
95
+ * (add-wins;本次调用期间又落了一次批准)。两支 200 都带这一位,所以一套分支读得通。
96
+ * ⚠️ 但**两个真值证明力不对称**:`false` 不是「已经没了」的证明(`removed` 支的确认读回在引擎里被
97
+ * `catch` 吞成 `false`)—— 要紧的场合用 {@link list} 对账,逐条理由见该字段的注。
98
+ *
99
+ * 🔴 **SDK 不自动重试**(非 submit 类的 DELETE):503 `state.rule_remove_failed`(→
100
+ * {@link import("../errors.js").ServiceStateError})的语义是「读回确认什么都没写,**或者**读回本身也
101
+ * 失败、结局不定」—— 两种都是重试可改判的,所以**自己**重试,然后用 {@link list} 对账。
102
+ *
103
+ * 越权撤销见 {@link RuleRevokeRequest.principal}(403 `auth.operator_only`);未接规则店 ⇒ 501。
104
+ * 🔴 **版本偏斜判据同 {@link list}**:gate 用 `capabilities.permissionRulesRevoke`(缺席 = 这台 worker
105
+ * 比撤销面老,真打会拿 404 `not_found.route`);`permissionRules` 为 true **不蕴含**这口在。
106
+ */
107
+ async revoke(input, opts) {
108
+ return this.t.request({
109
+ method: "DELETE",
110
+ path: "/v1/rules",
111
+ body: {
112
+ rule: input.rule,
113
+ scope: input.scope,
114
+ ...(input.principal !== undefined ? { principal: input.principal } : {}),
115
+ },
116
+ signal: opts?.signal,
117
+ });
118
+ }
119
+ }
120
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/resources/rules.ts"],"names":[],"mappings":"AAqTA,MAAM,OAAO,aAAa;IACK;IAA7B,YAA6B,CAAY;QAAZ,MAAC,GAAD,CAAC,CAAW;IAAG,CAAC;IAE7C;;;;;;;;yDAQqD;IACrD,KAAK,CAAC,eAAe,CAAC,MAAuB,EAAE,IAA+B;QAC5E,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO,CAAwB;YAC3C,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,6BAA6B;YACnC,IAAI,EAAE,EAAE,MAAM,EAAE;YAChB,MAAM,EAAE,IAAI,EAAE,MAAM;SACrB,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;yDAOqD;IACrD,KAAK,CAAC,cAAc,CAAC,MAAc,EAAE,IAA+B;QAClE,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO,CAAuB;YAC1C,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,4BAA4B;YAClC,IAAI,EAAE,EAAE,MAAM,EAAE;YAChB,MAAM,EAAE,IAAI,EAAE,MAAM;SACrB,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,KAAK,CAAC,IAAI,CAAC,MAAuB,EAAE,IAA+B;QACjE,MAAM,CAAC,GAAG,IAAI,eAAe,EAAE,CAAC;QAChC,IAAI,MAAM,EAAE,SAAS,KAAK,SAAS;YAAE,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;QAC1E,IAAI,MAAM,EAAE,KAAK,KAAK,SAAS;YAAE,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAC9D,IAAI,MAAM,EAAE,KAAK,KAAK,SAAS;YAAE,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACtE,IAAI,MAAM,EAAE,MAAM,KAAK,SAAS;YAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QACjE,MAAM,EAAE,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO,CAAiB;YACpC,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,EAAE,EAAE;YACjD,MAAM,EAAE,IAAI,EAAE,MAAM;SACrB,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,KAAK,CAAC,MAAM,CAAC,KAAwB,EAAE,IAA+B;QACpE,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO,CAAmB;YACtC,MAAM,EAAE,QAAQ;YAChB,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE;gBACJ,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,GAAG,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACzE;YACD,MAAM,EAAE,IAAI,EAAE,MAAM;SACrB,CAAC,CAAC;IACL,CAAC;CACF"}