@clawos-dev/clawd 0.2.260 → 0.2.261

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/dist/cli.cjs CHANGED
@@ -33,322 +33,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
33
33
  mod
34
34
  ));
35
35
 
36
- // ../protocol/src/methods.ts
37
- var METHOD_NAMES;
38
- var init_methods = __esm({
39
- "../protocol/src/methods.ts"() {
40
- "use strict";
41
- METHOD_NAMES = [
42
- "session:create",
43
- "session:list",
44
- "session:get",
45
- "session:update",
46
- "session:delete",
47
- "session:send",
48
- // per-turn sender 2026-07-08 spec §4.6:MCP servers (ticket / inbox ...) 走 HTTP RPC
49
- // /rpc/session:currentTurnSender(Bearer)拉当前 turn caller;WS handler 同名
50
- "session:currentTurnSender",
51
- "session:stop",
52
- // 温柔中断当前轮次,不杀进程(走 CC IPC control_request.subtype=interrupt)
53
- "session:interrupt",
54
- // 回滚到指定 user message,并撤销其后 CC 的文件编辑(走 CC IPC control_request.subtype=rewind_files)
55
- "session:rewind",
56
- // Rewind 预览:daemon 读 ~/.claude/file-history 快照 + 磁盘现状算真实 diff,不依赖 CC 进程
57
- "session:rewind-diff",
58
- // 打开 session 时一次性拿到"有真实 diff"的 user message id 集合,UI heuristic 据此过滤按钮显示
59
- "session:rewindable-message-ids",
60
- // 从指定 message uuid 处 fork 出新 transcript(截断 + 重写 sessionId/parentUuid + forkedFrom)
61
- // daemon 只生成新 jsonl 文件并返回新 toolSessionId;UI 自行决定是否走 session:create 拉起 resume
62
- "session:fork",
63
- "session:new",
64
- "session:resume",
65
- "session:observe",
66
- "session:events",
67
- "session:subscribe",
68
- "session:unsubscribe",
69
- "session:pin",
70
- // 清共享未读标记(SessionFile.unreadAt → null)+ 广播 session:info;幂等
71
- "session:markRead",
72
- "session:reorderPins",
73
- // mirror peer sessions (2026-05-26): owner-only RPC,写/删本机 mirror SessionFile —
74
- // mirror 是「**我**在朋友 daemon 上自己创建的 session」的本机持久化副本(不是朋友的会话),
75
- // 方便 sidebar 跨设备聚合。daemon dumb 写删,**不广播任何 push 帧** (不发 session:info /
76
- // session:deleted);UI 是写入唯一源,双写完成后通过 cache.setData('sessions', ...) 同步
77
- // 本地 sidebar state。native session:* handler 完全不感知 mirror (UI 操作 mirror 不复用
78
- // native 路径)。
79
- "peerSession:upsert",
80
- "peerSession:remove",
81
- "permission:respond",
82
- // AskUserQuestion 表单回写:UI 答完所有 question 后调用,daemon 把答案合并进 updated_input
83
- // 写一条 control_response 到 CC stdin(详见 events.ts session:question JSDoc)
84
- "session:answerQuestion",
85
- // AskUserQuestion 用户跳过:daemon 向 CC 回 allow + answers={} 让 tool 走空答案路径,
86
- // 同时广播 cleared 帧(无 answers)让所有客户端 UI 卡片转 Ended
87
- "session:cancelQuestion",
88
- "history:projects",
89
- "history:list",
90
- "history:read",
91
- "history:subagents",
92
- "history:subagent-read",
93
- "history:recentDirs",
94
- "workspace:list",
95
- "workspace:read",
96
- "shift:list",
97
- // ---- shift-internal:* (cc 内 shift MCP server 管理 owner 排班,spec 2026-06-24-clawd-shift) ----
98
- // 统一 RPC 鉴权(spec 2026-07-28)后经 /rpc + Bearer owner token 调;personaId 归因
99
- // 从 body 来(env CLAWD_PERSONA_ID),handler 内 createdByPersona 自检防跨 persona 误操作。
100
- "shift-internal:add",
101
- "shift-internal:list",
102
- "shift-internal:cancel",
103
- "shift-internal:update",
104
- "shift-internal:history",
105
- "skills:list",
106
- "agents:list",
107
- "git:root",
108
- "git:branch",
109
- "git:branches",
110
- "capabilities:get",
111
- // ---- persona:* (persona 管理) ----
112
- // persona:list 响应 shape 不分流(统一返 PersonaFile[]),但 daemon handler 按
113
- // ConnectionContext 投影不同子集:
114
- // owner ctx → 返全量 personas(含 private)
115
- // guest ctx → persona.public === true ∧ assertGrant(ctx.grants, {persona,p.id}, 'read')
116
- // method-grants.ts: persona:list 是 CAPABILITY_SCOPED;其它 persona:* 仍 ADMIN_ANY。
117
- "persona:create",
118
- "persona:list",
119
- "persona:get",
120
- "persona:update",
121
- "persona:delete",
122
- // ---- personaDispatch:* (dispatch 全家,spec 2026-07-28-unify-rpc-auth 统一登记) ----
123
- // run:本地 MCP server 经 /rpc(Bearer owner token);跨设备 A 的 peer daemon 经 /rpc
124
- //(Bearer connect token)。capability-scoped(handler 自检 persona.public + assertGrant(send))。
125
- // complete / list:B / A 本机 MCP server 调(owner token → ADMIN_ANY)。
126
- // get:跨设备轮询——A 端 daemon 凭 connect token 查 B 台账终态;handler 校验行归属。
127
- "personaDispatch:run",
128
- "personaDispatch:complete",
129
- "personaDispatch:list",
130
- "personaDispatch:get",
131
- // ---- peerExec:run (跨设备 exec,spec 2026-07-16-cross-device-exec-mcp) ----
132
- // A 侧本机 MCP server 经 /rpc(Bearer owner token)带 peerDeviceId → daemon forward;
133
- // B 侧对端经 /rpc(Bearer connect token)无 peerDeviceId → 本地 spawn。METHOD_GRANT_MAP
134
- // 设 public,handler 内判 remoteAccessAllowed(跟 inbox:postMessage 同款)。
135
- "peerExec:run",
136
- // ---- session:pty 双向透传(仅 CLAWD_CC_MODE=tui 才生效,对应 session:pty push 帧的上行) ----
137
- // session:pty:input — UI 把用户键盘字节(base64 UTF-8)发给 daemon,daemon 直写 pty stdin。
138
- // 高频帧(每次按键 1 帧),不入 reducer / 不广播。response 是 fire-and-forget 风格 ack。
139
- // session:pty:resize — UI 端 xterm 尺寸(cols/rows)变化时同步给 daemon 调 pty resize;
140
- // 触发频率低(仅 window resize),response 也是 ack。
141
- "session:pty:input",
142
- "session:pty:resize",
143
- // session:pty:snapshot — UI XtermPanel 重新挂载(切 session 回来 / 刷新)时主动拉一次当前
144
- // 屏幕全量快照,再开始消费 incremental session:pty。daemon 调 manager.getPtyReplay(sessionId)
145
- // 先 drain detector parse queue 再 serialize,保证 atSeq 与 buffer 内容严格对齐。
146
- // response 字段:
147
- // - data:当前屏幕 + scrollback 的 ANSI 字节串(base64-utf8),与 session:pty push 帧同格式
148
- // - atSeq:此 snapshot 覆盖到的最后一帧 session:pty 的 seq(即 detector 已 parse 完成的最大 seq)
149
- // UI 端 flush 时丢弃 pending 中 seq ≤ atSeq 的帧
150
- // - popupKind:当前 detector 检测到的 popup 类型(null = 无 popup)
151
- // session 不存在 / 非 tui / pty 已 exit 时 data=null, atSeq=0。
152
- "session:pty:snapshot",
153
- // ---- attachment.* file-sharing(详见 attachment-schemas.ts) ----
154
- // 命名警告:这里的 `attachment.*` RPC 与 CC v2.x 上行 `type:"attachment"` 系统行
155
- // (attachment-skills / attachment-deferred-tools / attachment_memories)是不同概念。
156
- // 全部管理类 RPC dispatcher grant 限 owner(capability admin-only);
157
- // 实际文件传输走 HTTP 路由(GET /files?p=&e=&s=,HMAC 签名自包含,不在白名单内)。
158
- "attachment.signUrl",
159
- "attachment.groupAdd",
160
- "attachment.groupRemove",
161
- "attachment.groupList",
162
- // ---- inbox:* (capability platform Phase 3 跨用户通知 + Phase 4 DM) ----
163
- // owner 接 guest 的 cross-principal 消息事件 + DM 双向私聊.
164
- // inbox:list / markRead: admin-only (Phase 3); inbox:postMessage: DM 自带能力,
165
- // 任何 principal (owner/guest) 可调 (Phase 4).
166
- "inbox:list",
167
- "inbox:markRead",
168
- "inbox:postMessage",
169
- // inbox:sendDm——persona 代发 DM 编排(inbox MCP server 经 /rpc + Bearer owner token 调)。
170
- // sender/mode 由 handler 按 body.sessionId 反查 session scope 归因(业务逻辑非鉴权)。
171
- "inbox:sendDm",
172
- // ---- contact:* (联系人列表) ----
173
- // 飞书统一身份 Phase 3 (决策 #14): received-capability:* 正名为 contact:*——Phase 2 后
174
- // 联系人身份由中心 server introspect 背书,不再是"对方颁发的 capability"。
175
- // 写入路径:device:connect 落 store + 自动反向(决策 #11);list / remove 作联系人列表读删。
176
- "contact:list",
177
- "contact:pin",
178
- "contact:remove",
179
- // ---- contact:setRemoteAccess (远程访问授权,spec 2026-07-16) ----
180
- // owner UI 授权 contact 反向操作我的设备(跨设备 exec 开关 remoteAccessAllowed)。
181
- // 详见 protocol/src/contact-remote-access.ts.
182
- "contact:setRemoteAccess",
183
- // ---- contact:handshake (双向确认建联握手, spec 2026-07-22) ----
184
- // device:connect 临时连接调用;被调方同步落库+broadcast 后回 ok(携带身份载荷)。
185
- // 替代旧 whoami+auto-reverse 组合;whoami 对 connect-token guest 已封(旧客户端拦截)。
186
- "contact:handshake",
187
- // ---- visitor:* (web 访客 · persona web 分享,spec 2026-06-24-persona-web-share-design) ----
188
- // owner-only:列出登录访问过本机 public persona 的 web 访客(零安装、飞书登录、daemon 自签
189
- // visitor token)。存储 ~/.clawd/visitors.json(VisitorStore),登录(exchange)即 upsert;
190
- // 访客身份用 TTC unique_id,会话按 creatorPrincipalId='visitor-<id>' 进现有会话用户分组。
191
- // 访客换 token 走 HTTP-only 公开路由 POST /share/exchange(非 WS method)。
192
- "visitor:list",
193
- // ---- whoami (v2 capability platform) ----
194
- // 任何 authed connection 都能调;返回 owner 显示名 + 当前 capability wire 形态 +
195
- // grants 对应的 persona 元数据 (id + displayName)。UI 端 AddRemotePersonaDialog
196
- // 的 preview 一次性临时 client 用它判定身份和可用 persona。
197
- "whoami",
198
- "info",
199
- "ping",
200
- // ---- extension runtime (v1: local mode only, no host-bridge —
201
- // see clawd/superpowers/specs/2026-05-27-clawd-extensions-v1-design.md) ----
202
- "extension.list",
203
- "extension.open",
204
- "extension.close",
205
- "extension.uninstall",
206
- // ---- extension sharing v1 (spec 2026-05-28-clawd-extension-sharing-design) ----
207
- // owner-side: 公开 / 撤回 / 查 publish 状态(owner-only ADMIN_ANY)
208
- "extension.publish",
209
- "extension.unpublish",
210
- "extension.publish:status",
211
- // cross-tunnel: guest 从 owner 拉 published list + bundle(CAPABILITY_SCOPED)
212
- "extension.list-published",
213
- "extension.fetchBundle",
214
- // guest-self: guest 在自己 daemon 上做安装与更新(ADMIN_ANY,guest 是 owner of self daemon).
215
- // 无 subscription 持久化:guest 端身份完全来自文件系统 + reconciled publishedExtensions.
216
- "extension.install-from-channel",
217
- "extension.update-from-channel",
218
- // ---- auth:* 飞书统一身份 Phase 1 (spec 2026-06-01-feishu-identity-design) ----
219
- // owner-only:daemon loopback 登录流程(auth:login:start 起 state → HTTP /auth/callback
220
- // 回调落盘 → auth:login:done 事件)。中央字面量见 protocol/feishu-auth.ts FEISHU_AUTH_METHODS。
221
- "auth:login:start",
222
- "auth:getIdentity",
223
- "auth:logout",
224
- // ---- device:* 设备目录 + 连接(决策 #15:deviceId + 自包含 token) ----
225
- // owner-only:公共设备目录 + audience 绑定设备的签名 token 连接(daemon 代理中心 server)。
226
- // 中央字面量见 protocol/feishu-auth.ts DEVICE_METHODS。
227
- "device:list",
228
- "device:connect",
229
- // ---- app-builder Project (spec 2026-06-01 picker → 2026-06-02 单栏默认 refactor) ----
230
- // persona-app-builder Project 管理;schemas: ProjectMetadataSchema + 5 个 args/result 对。
231
- // listProjects: readdir projects/,按目录读 .clawd-project.json
232
- // createProject: 校验 personaId/未绑 + 校验名 + 从 6173-6182 段分配端口 + 写元数据 + 创建空目录
233
- // + daemon 内部 mutate session.appBuilderProject + 起 dev server(单栏 refactor 后唯一绑定入口)
234
- // deleteProject: 停 dev server + rm -rf 目录(绑定 session 由 session:delete 联动 / 不在这里删)
235
- // updateProjectPort: 校验新端口段内 + 未被别的 project 占 + 重启 dev server
236
- // setProdUrl: 发布上线后 assistant 调一次写入 .clawd-project.json.prodUrl(取代 prod marker)
237
- "appBuilder:listProjects",
238
- // getProject:按 sessionId 取单个绑定 project(含 status)。people/mirror 跨 daemon 场景:
239
- // 本机 owner 在远端是 guest,listProjects 被 per-user 隔离拿不到别人的 project;getProject
240
- // 按 session 可见性授权(ensureSessionAccess read)放行。本地也复用(省掉拉全量再 find)。
241
- "appBuilder:getProject",
242
- "appBuilder:createProject",
243
- "appBuilder:deleteProject",
244
- "appBuilder:updateProjectPort",
245
- "appBuilder:setProdUrl",
246
- // 单栏 refactor stage fix (spec 2026-06-02-app-builder-single-pane-default-design §5.6.2):
247
- // createProject 时不再立即起 dev server(fresh 目录必崩)。assistant scaffold + pnpm install
248
- // 完成后调 startDevServer 显式触发 supervisor spawn;中间用 reportStage 上报进度(scaffolding
249
- // / installing 等),daemon broadcast appBuilder:project-updated 帧让 UI PreviewPane 切阶段。
250
- "appBuilder:startDevServer",
251
- "appBuilder:reportStage",
252
- // 显式控制 fix (spec 2026-06-02-app-builder-explicit-dev-server-control):用户点"收起预览"按钮触发,
253
- // 对称 startDevServer。砍掉了 setActiveSession(dev server 不再跟用户 focus 走),切 session 不动 dev server。
254
- "appBuilder:stopDevServer",
255
- // 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design):
256
- // publish:PreviewPane「发布上线」按钮 onClick 调本 RPC,daemon 内串行化 + spawn publish.sh +
257
- // 广播阶段进度帧 (appBuilder:publish-progress / publish-failed)。同 project 已有 in-flight job
258
- // 返回 already-publishing + 既有 jobId(UI 不报错继续订阅)。失败时 daemon 调 manager.send()
259
- // 注入 user_text 到 chat,由 assistant 接管完成部署。
260
- // dismissPublishJob:取消 in-flight publish(SIGTERM 子进程 + 清字段)或清掉 daemon 崩溃留下的
261
- // 中断残骸 publishJob 字段。UI 端 [取消] / [interrupted 取消] 都走这个 RPC。
262
- "appBuilder:publish",
263
- "appBuilder:dismissPublishJob"
264
- ];
265
- }
266
- });
267
-
268
- // ../protocol/src/events.ts
269
- var DAEMON_MODE_VALUES, SESSION_STATUS_VALUES, HISTORY_USER_META_VALUES, SKILL_SOURCE_VALUES, AGENT_SOURCE_VALUES, ASK_USER_QUESTION_TOOL_NAME;
270
- var init_events = __esm({
271
- "../protocol/src/events.ts"() {
272
- "use strict";
273
- DAEMON_MODE_VALUES = ["sdk", "tui"];
274
- SESSION_STATUS_VALUES = [
275
- "idle",
276
- "running",
277
- "running-idle",
278
- "stopped",
279
- "error",
280
- "observing"
281
- ];
282
- HISTORY_USER_META_VALUES = [
283
- "task-notification",
284
- "slash-command",
285
- "local-command",
286
- "system-reminder",
287
- "skill-hint",
288
- "meta-text",
289
- "attachment-skills",
290
- "attachment-deferred-tools"
291
- ];
292
- SKILL_SOURCE_VALUES = ["builtin", "global", "project", "plugin"];
293
- AGENT_SOURCE_VALUES = [
294
- "builtin",
295
- "global",
296
- "project",
297
- "policy",
298
- "plugin"
299
- ];
300
- ASK_USER_QUESTION_TOOL_NAME = "AskUserQuestion";
301
- }
302
- });
303
-
304
- // ../protocol/src/errors.ts
305
- var ERROR_CODES, ClawdError;
306
- var init_errors = __esm({
307
- "../protocol/src/errors.ts"() {
308
- "use strict";
309
- ERROR_CODES = {
310
- SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
311
- SESSION_BUSY: "SESSION_BUSY",
312
- INVALID_CWD: "INVALID_CWD",
313
- INVALID_PATH: "INVALID_PATH",
314
- TOOL_NOT_SUPPORTED: "TOOL_NOT_SUPPORTED",
315
- CLAUDE_NOT_FOUND: "CLAUDE_NOT_FOUND",
316
- SPAWN_FAILED: "SPAWN_FAILED",
317
- INVALID_PERMISSION_MODE: "INVALID_PERMISSION_MODE",
318
- METHOD_NOT_IMPLEMENTED: "METHOD_NOT_IMPLEMENTED",
319
- METHOD_NOT_ALLOWED: "METHOD_NOT_ALLOWED",
320
- VALIDATION_ERROR: "VALIDATION_ERROR",
321
- PERMISSION_REQUEST_STALE: "PERMISSION_REQUEST_STALE",
322
- FILE_TOO_LARGE: "FILE_TOO_LARGE",
323
- INTERNAL: "INTERNAL",
324
- UNAUTHORIZED: "UNAUTHORIZED",
325
- FORBIDDEN: "FORBIDDEN",
326
- INVALID_PARAM: "INVALID_PARAM",
327
- // 飞书统一身份(spec 2026-06-01 决策 #9):People/远程体系强制飞书登录。
328
- // daemon 进程身份未激活为飞书 unionId 时,FEISHU_GATED_METHODS 内的 RPC 一律返回此错误;
329
- // UI 据此显示登录引导(区别于 UNAUTHORIZED 的越权语义)。
330
- FEISHU_LOGIN_REQUIRED: "FEISHU_LOGIN_REQUIRED",
331
- // app-builder 单栏默认 refactor (spec 2026-06-02-app-builder-single-pane-default-design):
332
- // appBuilder:createProject 加 personaId + session 未绑校验后抛的两个错码。
333
- WRONG_PERSONA: "WRONG_PERSONA",
334
- SESSION_ALREADY_BOUND: "SESSION_ALREADY_BOUND",
335
- // 2026-07-02 unified people sidebar: contact:pin 时 deviceId 不在 store(可能被别处删了)
336
- CONTACT_NOT_FOUND: "CONTACT_NOT_FOUND",
337
- // 统一 RPC 鉴权(spec 2026-07-28):personaDispatch:get 查不到台账行,或 guest ctx
338
- // 查非自己发起的行(越权与未知 id 同码,不泄露存在性)。A 端轮询遇此码立即判失败。
339
- DISPATCH_NOT_FOUND: "DISPATCH_NOT_FOUND"
340
- };
341
- ClawdError = class extends Error {
342
- constructor(code, message) {
343
- super(message);
344
- this.code = code;
345
- this.name = "ClawdError";
346
- }
347
- code;
348
- };
349
- }
350
- });
351
-
352
36
  // ../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js
353
37
  var util, objectUtil, ZodParsedType, getParsedType;
354
38
  var init_util = __esm({
@@ -731,7 +415,7 @@ function getErrorMap() {
731
415
  return overrideErrorMap;
732
416
  }
733
417
  var overrideErrorMap;
734
- var init_errors2 = __esm({
418
+ var init_errors = __esm({
735
419
  "../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/errors.js"() {
736
420
  "use strict";
737
421
  init_en();
@@ -763,7 +447,7 @@ var makeIssue, EMPTY_PATH, ParseStatus, INVALID, DIRTY, OK, isAborted, isDirty,
763
447
  var init_parseUtil = __esm({
764
448
  "../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js"() {
765
449
  "use strict";
766
- init_errors2();
450
+ init_errors();
767
451
  init_en();
768
452
  makeIssue = (params) => {
769
453
  const { data, path: path75, errorMaps, issueData } = params;
@@ -1073,7 +757,7 @@ var init_types = __esm({
1073
757
  "../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js"() {
1074
758
  "use strict";
1075
759
  init_ZodError();
1076
- init_errors2();
760
+ init_errors();
1077
761
  init_errorUtil();
1078
762
  init_parseUtil();
1079
763
  init_util();
@@ -4447,7 +4131,7 @@ __export(external_exports, {
4447
4131
  var init_external = __esm({
4448
4132
  "../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js"() {
4449
4133
  "use strict";
4450
- init_errors2();
4134
+ init_errors();
4451
4135
  init_parseUtil();
4452
4136
  init_typeAliases();
4453
4137
  init_util();
@@ -4465,6 +4149,346 @@ var init_zod = __esm({
4465
4149
  }
4466
4150
  });
4467
4151
 
4152
+ // ../protocol/src/methods.ts
4153
+ var METHOD_NAMES, RPC_METHOD_STATUSES, RpcMethodStatusSchema, RpcMethodInfoSchema, MetaMethodsResultSchema;
4154
+ var init_methods = __esm({
4155
+ "../protocol/src/methods.ts"() {
4156
+ "use strict";
4157
+ init_zod();
4158
+ METHOD_NAMES = [
4159
+ "session:create",
4160
+ "session:list",
4161
+ "session:get",
4162
+ "session:update",
4163
+ "session:delete",
4164
+ "session:send",
4165
+ // per-turn sender 2026-07-08 spec §4.6:MCP servers (ticket / inbox ...) 走 HTTP RPC
4166
+ // /rpc/session:currentTurnSender(Bearer)拉当前 turn caller;WS handler 同名
4167
+ "session:currentTurnSender",
4168
+ "session:stop",
4169
+ // 温柔中断当前轮次,不杀进程(走 CC IPC control_request.subtype=interrupt)
4170
+ "session:interrupt",
4171
+ // 回滚到指定 user message,并撤销其后 CC 的文件编辑(走 CC IPC control_request.subtype=rewind_files)
4172
+ "session:rewind",
4173
+ // Rewind 预览:daemon 读 ~/.claude/file-history 快照 + 磁盘现状算真实 diff,不依赖 CC 进程
4174
+ "session:rewind-diff",
4175
+ // 打开 session 时一次性拿到"有真实 diff"的 user message id 集合,UI heuristic 据此过滤按钮显示
4176
+ "session:rewindable-message-ids",
4177
+ // 从指定 message uuid 处 fork 出新 transcript(截断 + 重写 sessionId/parentUuid + forkedFrom)
4178
+ // daemon 只生成新 jsonl 文件并返回新 toolSessionId;UI 自行决定是否走 session:create 拉起 resume
4179
+ "session:fork",
4180
+ "session:new",
4181
+ "session:resume",
4182
+ "session:observe",
4183
+ "session:events",
4184
+ "session:subscribe",
4185
+ "session:unsubscribe",
4186
+ "session:pin",
4187
+ // 清共享未读标记(SessionFile.unreadAt → null)+ 广播 session:info;幂等
4188
+ "session:markRead",
4189
+ "session:reorderPins",
4190
+ // mirror peer sessions (2026-05-26): owner-only RPC,写/删本机 mirror SessionFile —
4191
+ // mirror 是「**我**在朋友 daemon 上自己创建的 session」的本机持久化副本(不是朋友的会话),
4192
+ // 方便 sidebar 跨设备聚合。daemon dumb 写删,**不广播任何 push 帧** (不发 session:info /
4193
+ // session:deleted);UI 是写入唯一源,双写完成后通过 cache.setData('sessions', ...) 同步
4194
+ // 本地 sidebar state。native session:* handler 完全不感知 mirror (UI 操作 mirror 不复用
4195
+ // native 路径)。
4196
+ "peerSession:upsert",
4197
+ "peerSession:remove",
4198
+ "permission:respond",
4199
+ // AskUserQuestion 表单回写:UI 答完所有 question 后调用,daemon 把答案合并进 updated_input
4200
+ // 写一条 control_response 到 CC stdin(详见 events.ts session:question JSDoc)
4201
+ "session:answerQuestion",
4202
+ // AskUserQuestion 用户跳过:daemon 向 CC 回 allow + answers={} 让 tool 走空答案路径,
4203
+ // 同时广播 cleared 帧(无 answers)让所有客户端 UI 卡片转 Ended
4204
+ "session:cancelQuestion",
4205
+ "history:projects",
4206
+ "history:list",
4207
+ "history:read",
4208
+ "history:subagents",
4209
+ "history:subagent-read",
4210
+ "history:recentDirs",
4211
+ "workspace:list",
4212
+ "workspace:read",
4213
+ "shift:list",
4214
+ // ---- shift-internal:* (cc 内 shift MCP server 管理 owner 排班,spec 2026-06-24-clawd-shift) ----
4215
+ // 统一 RPC 鉴权(spec 2026-07-28)后经 /rpc + Bearer owner token 调;personaId 归因
4216
+ // 从 body 来(env CLAWD_PERSONA_ID),handler 内 createdByPersona 自检防跨 persona 误操作。
4217
+ "shift-internal:add",
4218
+ "shift-internal:list",
4219
+ "shift-internal:cancel",
4220
+ "shift-internal:update",
4221
+ "shift-internal:history",
4222
+ "skills:list",
4223
+ "agents:list",
4224
+ "git:root",
4225
+ "git:branch",
4226
+ "git:branches",
4227
+ "capabilities:get",
4228
+ // ---- persona:* (persona 管理) ----
4229
+ // persona:list 响应 shape 不分流(统一返 PersonaFile[]),但 daemon handler 按
4230
+ // ConnectionContext 投影不同子集:
4231
+ // owner ctx → 返全量 personas(含 private)
4232
+ // guest ctx → persona.public === true ∧ assertGrant(ctx.grants, {persona,p.id}, 'read')
4233
+ // method-grants.ts: persona:list 是 CAPABILITY_SCOPED;其它 persona:* 仍 ADMIN_ANY。
4234
+ "persona:create",
4235
+ "persona:list",
4236
+ "persona:get",
4237
+ "persona:update",
4238
+ "persona:delete",
4239
+ // ---- personaDispatch:* (dispatch 全家,spec 2026-07-28-unify-rpc-auth 统一登记) ----
4240
+ // run:本地 MCP server 经 /rpc(Bearer owner token);跨设备 A 的 peer daemon 经 /rpc
4241
+ //(Bearer connect token)。capability-scoped(handler 自检 persona.public + assertGrant(send))。
4242
+ // complete / list:B / A 本机 MCP server 调(owner token → ADMIN_ANY)。
4243
+ // get:跨设备轮询——A 端 daemon 凭 connect token 查 B 台账终态;handler 校验行归属。
4244
+ "personaDispatch:run",
4245
+ "personaDispatch:complete",
4246
+ "personaDispatch:list",
4247
+ "personaDispatch:get",
4248
+ // ---- peerExec:run (跨设备 exec,spec 2026-07-16-cross-device-exec-mcp) ----
4249
+ // A 侧本机 MCP server 经 /rpc(Bearer owner token)带 peerDeviceId → daemon forward;
4250
+ // B 侧对端经 /rpc(Bearer connect token)无 peerDeviceId → 本地 spawn。METHOD_GRANT_MAP
4251
+ // 设 public,handler 内判 remoteAccessAllowed(跟 inbox:postMessage 同款)。
4252
+ "peerExec:run",
4253
+ // ---- session:pty 双向透传(仅 CLAWD_CC_MODE=tui 才生效,对应 session:pty push 帧的上行) ----
4254
+ // session:pty:input — UI 把用户键盘字节(base64 UTF-8)发给 daemon,daemon 直写 pty stdin。
4255
+ // 高频帧(每次按键 1 帧),不入 reducer / 不广播。response 是 fire-and-forget 风格 ack。
4256
+ // session:pty:resize — UI 端 xterm 尺寸(cols/rows)变化时同步给 daemon 调 pty resize;
4257
+ // 触发频率低(仅 window resize),response 也是 ack。
4258
+ "session:pty:input",
4259
+ "session:pty:resize",
4260
+ // session:pty:snapshot — UI XtermPanel 重新挂载(切 session 回来 / 刷新)时主动拉一次当前
4261
+ // 屏幕全量快照,再开始消费 incremental session:pty。daemon 调 manager.getPtyReplay(sessionId)
4262
+ // 先 drain detector parse queue 再 serialize,保证 atSeq 与 buffer 内容严格对齐。
4263
+ // response 字段:
4264
+ // - data:当前屏幕 + scrollback 的 ANSI 字节串(base64-utf8),与 session:pty push 帧同格式
4265
+ // - atSeq:此 snapshot 覆盖到的最后一帧 session:pty 的 seq(即 detector 已 parse 完成的最大 seq)
4266
+ // UI 端 flush 时丢弃 pending 中 seq ≤ atSeq 的帧
4267
+ // - popupKind:当前 detector 检测到的 popup 类型(null = 无 popup)
4268
+ // session 不存在 / 非 tui / pty 已 exit 时 data=null, atSeq=0。
4269
+ "session:pty:snapshot",
4270
+ // ---- attachment.* file-sharing(详见 attachment-schemas.ts) ----
4271
+ // 命名警告:这里的 `attachment.*` RPC 与 CC v2.x 上行 `type:"attachment"` 系统行
4272
+ // (attachment-skills / attachment-deferred-tools / attachment_memories)是不同概念。
4273
+ // 全部管理类 RPC dispatcher grant 限 owner(capability admin-only);
4274
+ // 实际文件传输走 HTTP 路由(GET /files?p=&e=&s=,HMAC 签名自包含,不在白名单内)。
4275
+ "attachment.signUrl",
4276
+ "attachment.groupAdd",
4277
+ "attachment.groupRemove",
4278
+ "attachment.groupList",
4279
+ // ---- inbox:* (capability platform Phase 3 跨用户通知 + Phase 4 DM) ----
4280
+ // owner 接 guest 的 cross-principal 消息事件 + DM 双向私聊.
4281
+ // inbox:list / markRead: admin-only (Phase 3); inbox:postMessage: DM 自带能力,
4282
+ // 任何 principal (owner/guest) 可调 (Phase 4).
4283
+ "inbox:list",
4284
+ "inbox:markRead",
4285
+ "inbox:postMessage",
4286
+ // inbox:sendDm——persona 代发 DM 编排(inbox MCP server 经 /rpc + Bearer owner token 调)。
4287
+ // sender/mode 由 handler 按 body.sessionId 反查 session scope 归因(业务逻辑非鉴权)。
4288
+ "inbox:sendDm",
4289
+ // ---- contact:* (联系人列表) ----
4290
+ // 飞书统一身份 Phase 3 (决策 #14): received-capability:* 正名为 contact:*——Phase 2 后
4291
+ // 联系人身份由中心 server introspect 背书,不再是"对方颁发的 capability"。
4292
+ // 写入路径:device:connect 落 store + 自动反向(决策 #11);list / remove 作联系人列表读删。
4293
+ "contact:list",
4294
+ "contact:pin",
4295
+ "contact:remove",
4296
+ // ---- contact:setRemoteAccess (远程访问授权,spec 2026-07-16) ----
4297
+ // owner UI 授权 contact 反向操作我的设备(跨设备 exec 开关 remoteAccessAllowed)。
4298
+ // 详见 protocol/src/contact-remote-access.ts.
4299
+ "contact:setRemoteAccess",
4300
+ // ---- contact:handshake (双向确认建联握手, spec 2026-07-22) ----
4301
+ // device:connect 临时连接调用;被调方同步落库+broadcast 后回 ok(携带身份载荷)。
4302
+ // 替代旧 whoami+auto-reverse 组合;whoami 对 connect-token guest 已封(旧客户端拦截)。
4303
+ "contact:handshake",
4304
+ // ---- visitor:* (web 访客 · persona web 分享,spec 2026-06-24-persona-web-share-design) ----
4305
+ // owner-only:列出登录访问过本机 public persona 的 web 访客(零安装、飞书登录、daemon 自签
4306
+ // visitor token)。存储 ~/.clawd/visitors.json(VisitorStore),登录(exchange)即 upsert;
4307
+ // 访客身份用 TTC unique_id,会话按 creatorPrincipalId='visitor-<id>' 进现有会话用户分组。
4308
+ // 访客换 token 走 HTTP-only 公开路由 POST /share/exchange(非 WS method)。
4309
+ "visitor:list",
4310
+ // ---- whoami (v2 capability platform) ----
4311
+ // 任何 authed connection 都能调;返回 owner 显示名 + 当前 capability wire 形态 +
4312
+ // grants 对应的 persona 元数据 (id + displayName)。UI 端 AddRemotePersonaDialog
4313
+ // 的 preview 一次性临时 client 用它判定身份和可用 persona。
4314
+ "whoami",
4315
+ "info",
4316
+ "ping",
4317
+ // ---- meta:methods (RPC 自省) ----
4318
+ // 任何 authed connection 都能调(同 whoami):返回 METHOD_NAMES 全集 + **对本连接**
4319
+ // 的可调用性三态。agent 侧 clawd-rpc 的 list tool 是唯一消费方——统一鉴权删掉白名单后
4320
+ // agent 面前是 104 个 method,没有自省就只能靠试错。
4321
+ "meta:methods",
4322
+ // ---- extension runtime (v1: local mode only, no host-bridge —
4323
+ // see clawd/superpowers/specs/2026-05-27-clawd-extensions-v1-design.md) ----
4324
+ "extension.list",
4325
+ "extension.open",
4326
+ "extension.close",
4327
+ "extension.uninstall",
4328
+ // ---- extension sharing v1 (spec 2026-05-28-clawd-extension-sharing-design) ----
4329
+ // owner-side: 公开 / 撤回 / 查 publish 状态(owner-only ADMIN_ANY)
4330
+ "extension.publish",
4331
+ "extension.unpublish",
4332
+ "extension.publish:status",
4333
+ // cross-tunnel: guest 从 owner 拉 published list + bundle(CAPABILITY_SCOPED)
4334
+ "extension.list-published",
4335
+ "extension.fetchBundle",
4336
+ // guest-self: guest 在自己 daemon 上做安装与更新(ADMIN_ANY,guest 是 owner of self daemon).
4337
+ // 无 subscription 持久化:guest 端身份完全来自文件系统 + reconciled publishedExtensions.
4338
+ "extension.install-from-channel",
4339
+ "extension.update-from-channel",
4340
+ // ---- auth:* 飞书统一身份 Phase 1 (spec 2026-06-01-feishu-identity-design) ----
4341
+ // owner-only:daemon loopback 登录流程(auth:login:start 起 state → HTTP /auth/callback
4342
+ // 回调落盘 → auth:login:done 事件)。中央字面量见 protocol/feishu-auth.ts FEISHU_AUTH_METHODS。
4343
+ "auth:login:start",
4344
+ "auth:getIdentity",
4345
+ "auth:logout",
4346
+ // ---- device:* 设备目录 + 连接(决策 #15:deviceId + 自包含 token) ----
4347
+ // owner-only:公共设备目录 + audience 绑定设备的签名 token 连接(daemon 代理中心 server)。
4348
+ // 中央字面量见 protocol/feishu-auth.ts DEVICE_METHODS。
4349
+ "device:list",
4350
+ "device:connect",
4351
+ // ---- app-builder Project (spec 2026-06-01 picker → 2026-06-02 单栏默认 refactor) ----
4352
+ // persona-app-builder Project 管理;schemas: ProjectMetadataSchema + 5 个 args/result 对。
4353
+ // listProjects: readdir projects/,按目录读 .clawd-project.json
4354
+ // createProject: 校验 personaId/未绑 + 校验名 + 从 6173-6182 段分配端口 + 写元数据 + 创建空目录
4355
+ // + daemon 内部 mutate session.appBuilderProject + 起 dev server(单栏 refactor 后唯一绑定入口)
4356
+ // deleteProject: 停 dev server + rm -rf 目录(绑定 session 由 session:delete 联动 / 不在这里删)
4357
+ // updateProjectPort: 校验新端口段内 + 未被别的 project 占 + 重启 dev server
4358
+ // setProdUrl: 发布上线后 assistant 调一次写入 .clawd-project.json.prodUrl(取代 prod marker)
4359
+ "appBuilder:listProjects",
4360
+ // getProject:按 sessionId 取单个绑定 project(含 status)。people/mirror 跨 daemon 场景:
4361
+ // 本机 owner 在远端是 guest,listProjects 被 per-user 隔离拿不到别人的 project;getProject
4362
+ // 按 session 可见性授权(ensureSessionAccess read)放行。本地也复用(省掉拉全量再 find)。
4363
+ "appBuilder:getProject",
4364
+ "appBuilder:createProject",
4365
+ "appBuilder:deleteProject",
4366
+ "appBuilder:updateProjectPort",
4367
+ "appBuilder:setProdUrl",
4368
+ // 单栏 refactor stage fix (spec 2026-06-02-app-builder-single-pane-default-design §5.6.2):
4369
+ // createProject 时不再立即起 dev server(fresh 目录必崩)。assistant scaffold + pnpm install
4370
+ // 完成后调 startDevServer 显式触发 supervisor spawn;中间用 reportStage 上报进度(scaffolding
4371
+ // / installing 等),daemon broadcast appBuilder:project-updated 帧让 UI PreviewPane 切阶段。
4372
+ "appBuilder:startDevServer",
4373
+ "appBuilder:reportStage",
4374
+ // 显式控制 fix (spec 2026-06-02-app-builder-explicit-dev-server-control):用户点"收起预览"按钮触发,
4375
+ // 对称 startDevServer。砍掉了 setActiveSession(dev server 不再跟用户 focus 走),切 session 不动 dev server。
4376
+ "appBuilder:stopDevServer",
4377
+ // 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design):
4378
+ // publish:PreviewPane「发布上线」按钮 onClick 调本 RPC,daemon 内串行化 + spawn publish.sh +
4379
+ // 广播阶段进度帧 (appBuilder:publish-progress / publish-failed)。同 project 已有 in-flight job
4380
+ // 返回 already-publishing + 既有 jobId(UI 不报错继续订阅)。失败时 daemon 调 manager.send()
4381
+ // 注入 user_text 到 chat,由 assistant 接管完成部署。
4382
+ // dismissPublishJob:取消 in-flight publish(SIGTERM 子进程 + 清字段)或清掉 daemon 崩溃留下的
4383
+ // 中断残骸 publishJob 字段。UI 端 [取消] / [interrupted 取消] 都走这个 RPC。
4384
+ "appBuilder:publish",
4385
+ "appBuilder:dismissPublishJob"
4386
+ ];
4387
+ RPC_METHOD_STATUSES = ["allowed", "conditional", "denied"];
4388
+ RpcMethodStatusSchema = external_exports.enum(RPC_METHOD_STATUSES);
4389
+ RpcMethodInfoSchema = external_exports.object({
4390
+ /** canonical method 名,直接可作为 clawd-rpc call 的 method 参数 */
4391
+ name: external_exports.string().min(1),
4392
+ status: RpcMethodStatusSchema,
4393
+ /**
4394
+ * `denied` 必带:daemon error code(`UNAUTHORIZED` / `FEISHU_LOGIN_REQUIRED` /
4395
+ * `METHOD_NOT_IMPLEMENTED`)。`conditional` 必带:谁来做二次校验。
4396
+ * `allowed` 恒无。
4397
+ */
4398
+ reason: external_exports.string().optional()
4399
+ });
4400
+ MetaMethodsResultSchema = external_exports.object({
4401
+ type: external_exports.literal("meta:methods"),
4402
+ /** METHOD_NAMES 全集,顺序与之一致 */
4403
+ methods: external_exports.array(RpcMethodInfoSchema)
4404
+ });
4405
+ }
4406
+ });
4407
+
4408
+ // ../protocol/src/events.ts
4409
+ var DAEMON_MODE_VALUES, SESSION_STATUS_VALUES, HISTORY_USER_META_VALUES, SKILL_SOURCE_VALUES, AGENT_SOURCE_VALUES, ASK_USER_QUESTION_TOOL_NAME;
4410
+ var init_events = __esm({
4411
+ "../protocol/src/events.ts"() {
4412
+ "use strict";
4413
+ DAEMON_MODE_VALUES = ["sdk", "tui"];
4414
+ SESSION_STATUS_VALUES = [
4415
+ "idle",
4416
+ "running",
4417
+ "running-idle",
4418
+ "stopped",
4419
+ "error",
4420
+ "observing"
4421
+ ];
4422
+ HISTORY_USER_META_VALUES = [
4423
+ "task-notification",
4424
+ "slash-command",
4425
+ "local-command",
4426
+ "system-reminder",
4427
+ "skill-hint",
4428
+ "meta-text",
4429
+ "attachment-skills",
4430
+ "attachment-deferred-tools"
4431
+ ];
4432
+ SKILL_SOURCE_VALUES = ["builtin", "global", "project", "plugin"];
4433
+ AGENT_SOURCE_VALUES = [
4434
+ "builtin",
4435
+ "global",
4436
+ "project",
4437
+ "policy",
4438
+ "plugin"
4439
+ ];
4440
+ ASK_USER_QUESTION_TOOL_NAME = "AskUserQuestion";
4441
+ }
4442
+ });
4443
+
4444
+ // ../protocol/src/errors.ts
4445
+ var ERROR_CODES, ClawdError;
4446
+ var init_errors2 = __esm({
4447
+ "../protocol/src/errors.ts"() {
4448
+ "use strict";
4449
+ ERROR_CODES = {
4450
+ SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
4451
+ SESSION_BUSY: "SESSION_BUSY",
4452
+ INVALID_CWD: "INVALID_CWD",
4453
+ INVALID_PATH: "INVALID_PATH",
4454
+ TOOL_NOT_SUPPORTED: "TOOL_NOT_SUPPORTED",
4455
+ CLAUDE_NOT_FOUND: "CLAUDE_NOT_FOUND",
4456
+ SPAWN_FAILED: "SPAWN_FAILED",
4457
+ INVALID_PERMISSION_MODE: "INVALID_PERMISSION_MODE",
4458
+ METHOD_NOT_IMPLEMENTED: "METHOD_NOT_IMPLEMENTED",
4459
+ METHOD_NOT_ALLOWED: "METHOD_NOT_ALLOWED",
4460
+ VALIDATION_ERROR: "VALIDATION_ERROR",
4461
+ PERMISSION_REQUEST_STALE: "PERMISSION_REQUEST_STALE",
4462
+ FILE_TOO_LARGE: "FILE_TOO_LARGE",
4463
+ INTERNAL: "INTERNAL",
4464
+ UNAUTHORIZED: "UNAUTHORIZED",
4465
+ FORBIDDEN: "FORBIDDEN",
4466
+ INVALID_PARAM: "INVALID_PARAM",
4467
+ // 飞书统一身份(spec 2026-06-01 决策 #9):People/远程体系强制飞书登录。
4468
+ // daemon 进程身份未激活为飞书 unionId 时,FEISHU_GATED_METHODS 内的 RPC 一律返回此错误;
4469
+ // UI 据此显示登录引导(区别于 UNAUTHORIZED 的越权语义)。
4470
+ FEISHU_LOGIN_REQUIRED: "FEISHU_LOGIN_REQUIRED",
4471
+ // app-builder 单栏默认 refactor (spec 2026-06-02-app-builder-single-pane-default-design):
4472
+ // appBuilder:createProject 加 personaId + session 未绑校验后抛的两个错码。
4473
+ WRONG_PERSONA: "WRONG_PERSONA",
4474
+ SESSION_ALREADY_BOUND: "SESSION_ALREADY_BOUND",
4475
+ // 2026-07-02 unified people sidebar: contact:pin 时 deviceId 不在 store(可能被别处删了)
4476
+ CONTACT_NOT_FOUND: "CONTACT_NOT_FOUND",
4477
+ // 统一 RPC 鉴权(spec 2026-07-28):personaDispatch:get 查不到台账行,或 guest ctx
4478
+ // 查非自己发起的行(越权与未知 id 同码,不泄露存在性)。A 端轮询遇此码立即判失败。
4479
+ DISPATCH_NOT_FOUND: "DISPATCH_NOT_FOUND"
4480
+ };
4481
+ ClawdError = class extends Error {
4482
+ constructor(code, message) {
4483
+ super(message);
4484
+ this.code = code;
4485
+ this.name = "ClawdError";
4486
+ }
4487
+ code;
4488
+ };
4489
+ }
4490
+ });
4491
+
4468
4492
  // ../protocol/src/attachment-schemas.ts
4469
4493
  var TOKEN_ROLES, GROUP_FILE_SOURCES, GroupFileEntrySchema, AttachmentSignUrlArgs, AttachmentSignUrlResponseSchema, AttachmentGroupAddArgs, AttachmentGroupAddResponseSchema, AttachmentGroupRemoveArgs, AttachmentGroupRemoveResponseSchema, AttachmentGroupListArgs, AttachmentGroupListResponseSchema, AttachmentUploadResponseSchema;
4470
4494
  var init_attachment_schemas = __esm({
@@ -6371,7 +6395,7 @@ var init_runtime = __esm({
6371
6395
  "use strict";
6372
6396
  init_methods();
6373
6397
  init_events();
6374
- init_errors();
6398
+ init_errors2();
6375
6399
  init_schemas();
6376
6400
  init_frames();
6377
6401
  init_persona_schemas();
@@ -55647,6 +55671,235 @@ function buildDeviceHandlers(deps) {
55647
55671
  var import_node_os17 = __toESM(require("os"), 1);
55648
55672
  init_protocol();
55649
55673
 
55674
+ // src/handlers/method-grants.ts
55675
+ var ADMIN_ANY = {
55676
+ kind: "fixed",
55677
+ resource: { type: "*" },
55678
+ action: "admin"
55679
+ };
55680
+ var CAPABILITY_SCOPED = { kind: "capability-scoped" };
55681
+ var METHOD_GRANT_MAP = {
55682
+ // ---- public(meta-only,guest 也能调) ----
55683
+ "info": { kind: "public" },
55684
+ "ping": { kind: "public" },
55685
+ // v2 Phase 6: whoami 是身份反查,任何 authed connection (owner / guest) 都能调;
55686
+ // handler 内部按 ctx.principal.kind 决定返回 "合成 owner self capability" 还是
55687
+ // "查 capabilityManager 拿 caller 的 guest capability"。
55688
+ "whoami": { kind: "public" },
55689
+ // meta:methods 是自省,与 whoami 同族:任何 authed connection 都能问「我能调什么」。
55690
+ // 返回全集而非只返可调的——guest 看到 denied 条目才知道边界在哪,否则只能反复试错;
55691
+ // method 名本身不是秘密(protocol/src/methods.ts 就是公开源),泄漏面为零。
55692
+ "meta:methods": { kind: "public" },
55693
+ // ---- inbox: 双投 P2P IM ----
55694
+ // 三条都 'public' — 能连上 = 已通过本地验票. handler 内额外校验 guest 的
55695
+ // args.peerDeviceId 与 ctx.principal.id(=deviceId) 一致, 防 guest 越权操作别的 DM 桶.
55696
+ "inbox:list": { kind: "public" },
55697
+ "inbox:markRead": { kind: "public" },
55698
+ "inbox:postMessage": { kind: "public" },
55699
+ // ---- contact:* (联系人列表) ----
55700
+ // 飞书统一身份 Phase 3 (决策 #14): received-capability:* 正名 contact:*。
55701
+ // 写入路径在 device:connect / 自动反向;list / remove 作联系人读 / 删,owner-only。
55702
+ "contact:list": ADMIN_ANY,
55703
+ "contact:pin": ADMIN_ANY,
55704
+ "contact:remove": ADMIN_ANY,
55705
+ // contact:setRemoteAccess (owner UI 配远程访问授权):ADMIN_ANY
55706
+ // (对齐 inbox:postMessage 的"能连上=有 auth,业务在 handler 校"模式)
55707
+ "contact:setRemoteAccess": ADMIN_ANY,
55708
+ // contact:handshake: 能连上 = 已通过本地验票;handler 内校验 tokenKind==='connect'
55709
+ // (visitor / owner 不允许触发反向落库)。
55710
+ "contact:handshake": { kind: "public" },
55711
+ // ---- visitor:* (访客名单,owner-only) ----
55712
+ // owner 看完整访客名单(含没开会话的);guest 不可调(handler 内再 assertOwner 兜底)。
55713
+ "visitor:list": ADMIN_ANY,
55714
+ // ---- session:* / chat:* 业务方法(v2 Phase 8 两层模型)----
55715
+ // dispatcher 不验资源,handler 内按 ctx + frame.args 反查 ownerPersonaId 调 assertGrant。
55716
+ // owner 自动通过(ctx 自带 '*':'admin' grant 一切 match);guest 在被授权 persona 内可调。
55717
+ "session:create": CAPABILITY_SCOPED,
55718
+ "session:list": CAPABILITY_SCOPED,
55719
+ "session:get": CAPABILITY_SCOPED,
55720
+ "session:update": CAPABILITY_SCOPED,
55721
+ "session:delete": CAPABILITY_SCOPED,
55722
+ "session:send": CAPABILITY_SCOPED,
55723
+ // per-turn sender 2026-07-08 spec §4.6:dispatcher 层走 'public'(HTTP RPC path 需 dispatcher 放行);
55724
+ // WS path 的隐私边界(防 guest 猜 sid 枚举 owner feishuUnionId)由 handler 内 ensureSessionAccess
55725
+ // 显式兜底——见 handlers/session.ts:currentTurnSender。dispatcher 'public' ≠ handler 免检。
55726
+ "session:currentTurnSender": { kind: "public" },
55727
+ "session:stop": CAPABILITY_SCOPED,
55728
+ "session:interrupt": CAPABILITY_SCOPED,
55729
+ "session:rewind": CAPABILITY_SCOPED,
55730
+ "session:rewind-diff": CAPABILITY_SCOPED,
55731
+ "session:rewindable-message-ids": CAPABILITY_SCOPED,
55732
+ "session:fork": CAPABILITY_SCOPED,
55733
+ "session:new": CAPABILITY_SCOPED,
55734
+ "session:resume": CAPABILITY_SCOPED,
55735
+ "session:observe": CAPABILITY_SCOPED,
55736
+ "session:events": CAPABILITY_SCOPED,
55737
+ "session:subscribe": CAPABILITY_SCOPED,
55738
+ "session:unsubscribe": CAPABILITY_SCOPED,
55739
+ "session:pin": CAPABILITY_SCOPED,
55740
+ "session:markRead": CAPABILITY_SCOPED,
55741
+ "session:reorderPins": ADMIN_ANY,
55742
+ // owner 全局操作,无 personaId 维度
55743
+ // mirror peer sessions (2026-05-26):owner-only — guest 不应能写/删本机 mirror SessionFile
55744
+ // (会绕过 #739 隔离规则把伪造身份的 SessionFile 写进 default store)。handler 端内部 ctx
55745
+ // 校验,dispatcher 层一刀切 admin 也兜底。
55746
+ "peerSession:upsert": ADMIN_ANY,
55747
+ "peerSession:remove": ADMIN_ANY,
55748
+ "permission:respond": CAPABILITY_SCOPED,
55749
+ "session:answerQuestion": CAPABILITY_SCOPED,
55750
+ "session:cancelQuestion": CAPABILITY_SCOPED,
55751
+ // 2026-05-25 老板拍板:guest 用别人 daemon 的 chat 必须能读 history / workspace /
55752
+ // attachment / skills / agents / git 元信息,下沉到 capability-scoped;handler 内
55753
+ // 用 isGuestPathAllowed (persona-paths.ts) 限制路径必须在 personaRoot/<allowed-pid>/** 下,
55754
+ // 防止跨 persona 越权读 owner 私有文件。
55755
+ "history:projects": CAPABILITY_SCOPED,
55756
+ "history:list": CAPABILITY_SCOPED,
55757
+ // history:read 取一条 session 的 CC JSONL 历史。guest 进 persona VM 后必须能读自己
55758
+ // 创建的 session 历史(chat view 加载历史消息走这条),所以下沉到 capability-scoped;
55759
+ // handler 内按 ctx.principal.id === file.creatorPrincipalId 拦越权。
55760
+ "history:read": CAPABILITY_SCOPED,
55761
+ "history:subagents": CAPABILITY_SCOPED,
55762
+ "history:subagent-read": CAPABILITY_SCOPED,
55763
+ "history:recentDirs": CAPABILITY_SCOPED,
55764
+ "workspace:list": CAPABILITY_SCOPED,
55765
+ "workspace:read": CAPABILITY_SCOPED,
55766
+ // ---- shift:* (排班 mock,owner-only 读) ----
55767
+ // mock 阶段只 owner 用;接 guest 后再考虑分级
55768
+ "shift:list": ADMIN_ANY,
55769
+ // ---- shift-internal:* + inbox:sendDm + personaDispatch complete/list ----
55770
+ // 统一 RPC 鉴权(spec 2026-07-28):原 /api/rpc 白名单 method 收编。调用方 = 本机
55771
+ // MCP server 持 owner token → ADMIN_ANY 即「owner token 即可」;业务归因(sendDm 的
55772
+ // sender、shift 的 personaId)在 handler 内按 body 字段处理,与鉴权无关。
55773
+ "shift-internal:add": ADMIN_ANY,
55774
+ "shift-internal:list": ADMIN_ANY,
55775
+ "shift-internal:cancel": ADMIN_ANY,
55776
+ "shift-internal:update": ADMIN_ANY,
55777
+ "shift-internal:history": ADMIN_ANY,
55778
+ "inbox:sendDm": ADMIN_ANY,
55779
+ "personaDispatch:complete": ADMIN_ANY,
55780
+ "personaDispatch:list": ADMIN_ANY,
55781
+ // 跨设备轮询(spec §5):A 端 daemon 凭 connect token 查 outcome;handler 内校验
55782
+ // 台账行 sourceSessionId === ctx.principal.id,防 guest 查别人的 dispatch。
55783
+ "personaDispatch:get": CAPABILITY_SCOPED,
55784
+ "skills:list": CAPABILITY_SCOPED,
55785
+ "agents:list": CAPABILITY_SCOPED,
55786
+ "git:root": CAPABILITY_SCOPED,
55787
+ "git:branch": CAPABILITY_SCOPED,
55788
+ "git:branches": CAPABILITY_SCOPED,
55789
+ "capabilities:get": ADMIN_ANY,
55790
+ "persona:create": ADMIN_ANY,
55791
+ // persona:list / persona:get 下沉到 CAPABILITY_SCOPED:guest 调用合法
55792
+ // (list 用于 People 面板展开对方分享的 personas;get 用于 guest 视角下
55793
+ // PersonaDrawer 展示 persona 详情 readOnly),handler 端按 ctx + persona.public
55794
+ // + assertGrant 过滤,两者口径一致。
55795
+ // persona:update / persona:delete 保持 ADMIN_ANY:UI 端 guest viewerRole 下
55796
+ // 已干掉编辑入口,dispatcher 这层是双保险——非法 RPC 401 兜底。
55797
+ "persona:list": CAPABILITY_SCOPED,
55798
+ "persona:get": CAPABILITY_SCOPED,
55799
+ "persona:update": ADMIN_ANY,
55800
+ "persona:delete": ADMIN_ANY,
55801
+ // 跨设备 dispatch:A 的 peer daemon 凭 connect token 经 /rpc 调进来;dispatcher 放行,
55802
+ // run handler 内做 persona.public + assertGrant(send) 双重校验(guest grants 是 wildcard,
55803
+ // 单 assertGrant 不够)。本地 MCP server 经 /rpc + Bearer 进来,同过此表(owner 恒过)。
55804
+ "personaDispatch:run": CAPABILITY_SCOPED,
55805
+ // 跨设备 exec(spec 2026-07-16):public——"能连上=有 auth",业务在 handler 校
55806
+ // (ensureGuest + remoteAccessAllowed),跟 inbox:postMessage 同模式。
55807
+ "peerExec:run": { kind: "public" },
55808
+ // 2026-05-26 修 clawd people TUI 渲染 mix 串扰回归(参考 f1133ff0 原 owner 路径
55809
+ // snapshot/buffer/flush 修复):之前三件套 ADMIN_ANY 把 guest 拦在 dispatcher 401,
55810
+ // 导致 XtermPanel 重挂载拉 snapshot 在 guest 路径不生效,老的字节流断裂串扰回来。
55811
+ // 同一个 session 永远只有一个操作者(owner 不进 guest 创建的 session),并发字节流
55812
+ // 冲突不存在;guest 在 chat 模式下已能 session:send/interrupt,PTY 域不开放纯属
55813
+ // 历史遗留的不对称。handler 端通过 ensureSessionAccess 验权对齐其他 session:* RPC。
55814
+ "session:pty:input": CAPABILITY_SCOPED,
55815
+ "session:pty:resize": CAPABILITY_SCOPED,
55816
+ "session:pty:snapshot": CAPABILITY_SCOPED,
55817
+ // file-sharing attachment.* RPC:dispatcher 放行;handler 内按 sessionId 反查
55818
+ // session.ownerPersonaId + ctx.grants 跑 assertGrant,限制 guest 只能签 / 操作
55819
+ // 自己被授权 persona 下的 session 文件。HTTP Bearer 仍然只识别 owner,所以 guest
55820
+ // 拿到 signed URL 后还要 HTTP 端校验通过才能取到文件——这里 RPC 层 ACL 是第一道。
55821
+ "attachment.signUrl": CAPABILITY_SCOPED,
55822
+ "attachment.groupAdd": CAPABILITY_SCOPED,
55823
+ "attachment.groupRemove": CAPABILITY_SCOPED,
55824
+ "attachment.groupList": CAPABILITY_SCOPED,
55825
+ // ---- extension runtime(owner-only;handler 内再 assertOwner 兜底) ----
55826
+ "extension.list": ADMIN_ANY,
55827
+ "extension.open": ADMIN_ANY,
55828
+ "extension.close": ADMIN_ANY,
55829
+ "extension.uninstall": ADMIN_ANY,
55830
+ // ---- extension sharing v1(spec 2026-05-28-clawd-extension-sharing-design) ----
55831
+ // owner-side:owner 自己管理 publish 状态
55832
+ "extension.publish": ADMIN_ANY,
55833
+ "extension.unpublish": ADMIN_ANY,
55834
+ "extension.publish:status": ADMIN_ANY,
55835
+ // cross-tunnel:guest 从 owner 拉 published list + bundle
55836
+ "extension.list-published": CAPABILITY_SCOPED,
55837
+ "extension.fetchBundle": CAPABILITY_SCOPED,
55838
+ // guest-self:guest 在自己 daemon 上触发安装与更新(无持久化 subscription 概念)
55839
+ "extension.install-from-channel": ADMIN_ANY,
55840
+ "extension.update-from-channel": ADMIN_ANY,
55841
+ // ---- auth:* 飞书统一身份 Phase 1(owner-only) ----
55842
+ // 登录/登出/查身份都是 owner 本机操作,guest 不可调
55843
+ "auth:login:start": ADMIN_ANY,
55844
+ "auth:getIdentity": ADMIN_ANY,
55845
+ "auth:logout": ADMIN_ANY,
55846
+ // ---- device:* 设备目录 + 连接(决策 #15,owner-only) ----
55847
+ // 设备目录 / 连接都是 owner 本机操作(用 owner 的 ttcJwt 找中心 server),guest 不可调
55848
+ "device:list": ADMIN_ANY,
55849
+ "device:connect": ADMIN_ANY,
55850
+ // ---- app-builder Project picker(spec 2026-06-01-app-builder-project-picker-design) ----
55851
+ // owner-only:picker UI 给 owner 管理本机 build 中的 project(fs + 端口分配)。
55852
+ // 即使 persona-app-builder 是 PreinstalledPersona,project 管理也属本机 owner 行为,
55853
+ // 不暴露给 guest(远端接入者)。
55854
+ "appBuilder:listProjects": ADMIN_ANY,
55855
+ // getProject 走 capability-scoped(区别于其它 appBuilder 的 owner-only ADMIN_ANY):mirror/people
55856
+ // 的本机 owner 在远端是 guest,需能调 getProject 拿它 mirror 的 session 的 project。dispatcher
55857
+ // 放行后由 handler 内 canAccessSession(read) 按 session 可见性授权(防 guest 越权拿别人 project)。
55858
+ "appBuilder:getProject": CAPABILITY_SCOPED,
55859
+ "appBuilder:createProject": ADMIN_ANY,
55860
+ "appBuilder:deleteProject": ADMIN_ANY,
55861
+ "appBuilder:updateProjectPort": ADMIN_ANY,
55862
+ // people/mirror 协同写操作(spec 2026-06-04-people-mode-app-builder-write-and-push):以下 5 个写
55863
+ // method 走 CAPABILITY_SCOPED(不再 owner-only ADMIN_ANY),handler 内 ensureProjectWriteAccess
55864
+ // 按 session 可见性(canAccessSession 'send')授权——mirror 的本机 owner 在远端是 guest,能 mirror
55865
+ // 该 session 就能操作它绑定的 project(发布凭证是 persona 共享资产,老板决策"统一 session 授权")。
55866
+ "appBuilder:setProdUrl": CAPABILITY_SCOPED,
55867
+ "appBuilder:startDevServer": CAPABILITY_SCOPED,
55868
+ // reportStage 仍 owner-only:assistant 走 HTTP RPC 上报(不过 WS admin grant),UI 不调它。
55869
+ "appBuilder:reportStage": ADMIN_ANY,
55870
+ "appBuilder:stopDevServer": CAPABILITY_SCOPED,
55871
+ "appBuilder:publish": CAPABILITY_SCOPED,
55872
+ "appBuilder:dismissPublishJob": CAPABILITY_SCOPED
55873
+ };
55874
+ function computeGrant(method) {
55875
+ const rule = METHOD_GRANT_MAP[method];
55876
+ if (!rule) return { kind: "public" };
55877
+ if (rule.kind === "public") return { kind: "public" };
55878
+ if (rule.kind === "capability-scoped") return { kind: "public" };
55879
+ return { kind: "check", resource: rule.resource, action: rule.action };
55880
+ }
55881
+ function computeMethodAccess(args) {
55882
+ const { method, grants, feishuActive, isImplemented } = args;
55883
+ if (!isImplemented(method)) {
55884
+ return { name: method, status: "denied", reason: "METHOD_NOT_IMPLEMENTED" };
55885
+ }
55886
+ if (!feishuActive && FEISHU_GATED_METHODS.includes(method)) {
55887
+ return { name: method, status: "denied", reason: "FEISHU_LOGIN_REQUIRED" };
55888
+ }
55889
+ if (METHOD_GRANT_MAP[method]?.kind === "capability-scoped") {
55890
+ return {
55891
+ name: method,
55892
+ status: "conditional",
55893
+ reason: "handler checks your grants for the target resource"
55894
+ };
55895
+ }
55896
+ const verdict = computeGrant(method);
55897
+ if (verdict.kind === "check" && !assertGrant(grants, verdict.resource, verdict.action)) {
55898
+ return { name: method, status: "denied", reason: "UNAUTHORIZED" };
55899
+ }
55900
+ return { name: method, status: "allowed" };
55901
+ }
55902
+
55650
55903
  // src/version.ts
55651
55904
  var version = "0.2.6".length > 0 ? "0.2.6" : "dev";
55652
55905
 
@@ -55699,6 +55952,20 @@ function buildMetaHandlers(deps) {
55699
55952
  ping
55700
55953
  };
55701
55954
  }
55955
+ function buildMetaMethodsHandler(deps) {
55956
+ return async (_frame, _client, ctx) => {
55957
+ const grants = ctx?.grants ?? [];
55958
+ const methods = METHOD_NAMES.map(
55959
+ (method) => computeMethodAccess({
55960
+ method,
55961
+ grants,
55962
+ feishuActive: deps.feishuActive(),
55963
+ isImplemented: deps.isImplemented
55964
+ })
55965
+ );
55966
+ return { response: { type: "meta:methods", methods } };
55967
+ };
55968
+ }
55702
55969
 
55703
55970
  // src/handlers/persona.ts
55704
55971
  init_protocol();
@@ -58024,7 +58291,7 @@ function ensureGuest(ctx) {
58024
58291
 
58025
58292
  // src/handlers/index.ts
58026
58293
  function buildMethodHandlers(deps) {
58027
- return {
58294
+ const handlers = {
58028
58295
  ...buildSessionHandlers({
58029
58296
  ...deps,
58030
58297
  devServerLifecycle: deps.devServerLifecycle,
@@ -58123,6 +58390,11 @@ function buildMethodHandlers(deps) {
58123
58390
  }),
58124
58391
  ...buildVisitorHandlers({ visitorStore: deps.visitorStore })
58125
58392
  };
58393
+ handlers["meta:methods"] = buildMetaMethodsHandler({
58394
+ feishuActive: deps.feishuActive ?? (() => true),
58395
+ isImplemented: (m2) => Boolean((deps.getAllHandlers?.() ?? handlers)[m2])
58396
+ });
58397
+ return handlers;
58126
58398
  }
58127
58399
 
58128
58400
  // src/app-builder/dev-server-supervisor.ts
@@ -58381,217 +58653,6 @@ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
58381
58653
 
58382
58654
  // src/handlers/dispatch-rpc.ts
58383
58655
  init_protocol();
58384
-
58385
- // src/handlers/method-grants.ts
58386
- var ADMIN_ANY = {
58387
- kind: "fixed",
58388
- resource: { type: "*" },
58389
- action: "admin"
58390
- };
58391
- var CAPABILITY_SCOPED = { kind: "capability-scoped" };
58392
- var METHOD_GRANT_MAP = {
58393
- // ---- public(meta-only,guest 也能调) ----
58394
- "info": { kind: "public" },
58395
- "ping": { kind: "public" },
58396
- // v2 Phase 6: whoami 是身份反查,任何 authed connection (owner / guest) 都能调;
58397
- // handler 内部按 ctx.principal.kind 决定返回 "合成 owner self capability" 还是
58398
- // "查 capabilityManager 拿 caller 的 guest capability"。
58399
- "whoami": { kind: "public" },
58400
- // ---- inbox: 双投 P2P IM ----
58401
- // 三条都 'public' — 能连上 = 已通过本地验票. handler 内额外校验 guest 的
58402
- // args.peerDeviceId 与 ctx.principal.id(=deviceId) 一致, 防 guest 越权操作别的 DM 桶.
58403
- "inbox:list": { kind: "public" },
58404
- "inbox:markRead": { kind: "public" },
58405
- "inbox:postMessage": { kind: "public" },
58406
- // ---- contact:* (联系人列表) ----
58407
- // 飞书统一身份 Phase 3 (决策 #14): received-capability:* 正名 contact:*。
58408
- // 写入路径在 device:connect / 自动反向;list / remove 作联系人读 / 删,owner-only。
58409
- "contact:list": ADMIN_ANY,
58410
- "contact:pin": ADMIN_ANY,
58411
- "contact:remove": ADMIN_ANY,
58412
- // contact:setRemoteAccess (owner UI 配远程访问授权):ADMIN_ANY
58413
- // (对齐 inbox:postMessage 的"能连上=有 auth,业务在 handler 校"模式)
58414
- "contact:setRemoteAccess": ADMIN_ANY,
58415
- // contact:handshake: 能连上 = 已通过本地验票;handler 内校验 tokenKind==='connect'
58416
- // (visitor / owner 不允许触发反向落库)。
58417
- "contact:handshake": { kind: "public" },
58418
- // ---- visitor:* (访客名单,owner-only) ----
58419
- // owner 看完整访客名单(含没开会话的);guest 不可调(handler 内再 assertOwner 兜底)。
58420
- "visitor:list": ADMIN_ANY,
58421
- // ---- session:* / chat:* 业务方法(v2 Phase 8 两层模型)----
58422
- // dispatcher 不验资源,handler 内按 ctx + frame.args 反查 ownerPersonaId 调 assertGrant。
58423
- // owner 自动通过(ctx 自带 '*':'admin' grant 一切 match);guest 在被授权 persona 内可调。
58424
- "session:create": CAPABILITY_SCOPED,
58425
- "session:list": CAPABILITY_SCOPED,
58426
- "session:get": CAPABILITY_SCOPED,
58427
- "session:update": CAPABILITY_SCOPED,
58428
- "session:delete": CAPABILITY_SCOPED,
58429
- "session:send": CAPABILITY_SCOPED,
58430
- // per-turn sender 2026-07-08 spec §4.6:dispatcher 层走 'public'(HTTP RPC path 需 dispatcher 放行);
58431
- // WS path 的隐私边界(防 guest 猜 sid 枚举 owner feishuUnionId)由 handler 内 ensureSessionAccess
58432
- // 显式兜底——见 handlers/session.ts:currentTurnSender。dispatcher 'public' ≠ handler 免检。
58433
- "session:currentTurnSender": { kind: "public" },
58434
- "session:stop": CAPABILITY_SCOPED,
58435
- "session:interrupt": CAPABILITY_SCOPED,
58436
- "session:rewind": CAPABILITY_SCOPED,
58437
- "session:rewind-diff": CAPABILITY_SCOPED,
58438
- "session:rewindable-message-ids": CAPABILITY_SCOPED,
58439
- "session:fork": CAPABILITY_SCOPED,
58440
- "session:new": CAPABILITY_SCOPED,
58441
- "session:resume": CAPABILITY_SCOPED,
58442
- "session:observe": CAPABILITY_SCOPED,
58443
- "session:events": CAPABILITY_SCOPED,
58444
- "session:subscribe": CAPABILITY_SCOPED,
58445
- "session:unsubscribe": CAPABILITY_SCOPED,
58446
- "session:pin": CAPABILITY_SCOPED,
58447
- "session:markRead": CAPABILITY_SCOPED,
58448
- "session:reorderPins": ADMIN_ANY,
58449
- // owner 全局操作,无 personaId 维度
58450
- // mirror peer sessions (2026-05-26):owner-only — guest 不应能写/删本机 mirror SessionFile
58451
- // (会绕过 #739 隔离规则把伪造身份的 SessionFile 写进 default store)。handler 端内部 ctx
58452
- // 校验,dispatcher 层一刀切 admin 也兜底。
58453
- "peerSession:upsert": ADMIN_ANY,
58454
- "peerSession:remove": ADMIN_ANY,
58455
- "permission:respond": CAPABILITY_SCOPED,
58456
- "session:answerQuestion": CAPABILITY_SCOPED,
58457
- "session:cancelQuestion": CAPABILITY_SCOPED,
58458
- // 2026-05-25 老板拍板:guest 用别人 daemon 的 chat 必须能读 history / workspace /
58459
- // attachment / skills / agents / git 元信息,下沉到 capability-scoped;handler 内
58460
- // 用 isGuestPathAllowed (persona-paths.ts) 限制路径必须在 personaRoot/<allowed-pid>/** 下,
58461
- // 防止跨 persona 越权读 owner 私有文件。
58462
- "history:projects": CAPABILITY_SCOPED,
58463
- "history:list": CAPABILITY_SCOPED,
58464
- // history:read 取一条 session 的 CC JSONL 历史。guest 进 persona VM 后必须能读自己
58465
- // 创建的 session 历史(chat view 加载历史消息走这条),所以下沉到 capability-scoped;
58466
- // handler 内按 ctx.principal.id === file.creatorPrincipalId 拦越权。
58467
- "history:read": CAPABILITY_SCOPED,
58468
- "history:subagents": CAPABILITY_SCOPED,
58469
- "history:subagent-read": CAPABILITY_SCOPED,
58470
- "history:recentDirs": CAPABILITY_SCOPED,
58471
- "workspace:list": CAPABILITY_SCOPED,
58472
- "workspace:read": CAPABILITY_SCOPED,
58473
- // ---- shift:* (排班 mock,owner-only 读) ----
58474
- // mock 阶段只 owner 用;接 guest 后再考虑分级
58475
- "shift:list": ADMIN_ANY,
58476
- // ---- shift-internal:* + inbox:sendDm + personaDispatch complete/list ----
58477
- // 统一 RPC 鉴权(spec 2026-07-28):原 /api/rpc 白名单 method 收编。调用方 = 本机
58478
- // MCP server 持 owner token → ADMIN_ANY 即「owner token 即可」;业务归因(sendDm 的
58479
- // sender、shift 的 personaId)在 handler 内按 body 字段处理,与鉴权无关。
58480
- "shift-internal:add": ADMIN_ANY,
58481
- "shift-internal:list": ADMIN_ANY,
58482
- "shift-internal:cancel": ADMIN_ANY,
58483
- "shift-internal:update": ADMIN_ANY,
58484
- "shift-internal:history": ADMIN_ANY,
58485
- "inbox:sendDm": ADMIN_ANY,
58486
- "personaDispatch:complete": ADMIN_ANY,
58487
- "personaDispatch:list": ADMIN_ANY,
58488
- // 跨设备轮询(spec §5):A 端 daemon 凭 connect token 查 outcome;handler 内校验
58489
- // 台账行 sourceSessionId === ctx.principal.id,防 guest 查别人的 dispatch。
58490
- "personaDispatch:get": CAPABILITY_SCOPED,
58491
- "skills:list": CAPABILITY_SCOPED,
58492
- "agents:list": CAPABILITY_SCOPED,
58493
- "git:root": CAPABILITY_SCOPED,
58494
- "git:branch": CAPABILITY_SCOPED,
58495
- "git:branches": CAPABILITY_SCOPED,
58496
- "capabilities:get": ADMIN_ANY,
58497
- "persona:create": ADMIN_ANY,
58498
- // persona:list / persona:get 下沉到 CAPABILITY_SCOPED:guest 调用合法
58499
- // (list 用于 People 面板展开对方分享的 personas;get 用于 guest 视角下
58500
- // PersonaDrawer 展示 persona 详情 readOnly),handler 端按 ctx + persona.public
58501
- // + assertGrant 过滤,两者口径一致。
58502
- // persona:update / persona:delete 保持 ADMIN_ANY:UI 端 guest viewerRole 下
58503
- // 已干掉编辑入口,dispatcher 这层是双保险——非法 RPC 401 兜底。
58504
- "persona:list": CAPABILITY_SCOPED,
58505
- "persona:get": CAPABILITY_SCOPED,
58506
- "persona:update": ADMIN_ANY,
58507
- "persona:delete": ADMIN_ANY,
58508
- // 跨设备 dispatch:A 的 peer daemon 凭 connect token 经 /rpc 调进来;dispatcher 放行,
58509
- // run handler 内做 persona.public + assertGrant(send) 双重校验(guest grants 是 wildcard,
58510
- // 单 assertGrant 不够)。本地 MCP server 经 /rpc + Bearer 进来,同过此表(owner 恒过)。
58511
- "personaDispatch:run": CAPABILITY_SCOPED,
58512
- // 跨设备 exec(spec 2026-07-16):public——"能连上=有 auth",业务在 handler 校
58513
- // (ensureGuest + remoteAccessAllowed),跟 inbox:postMessage 同模式。
58514
- "peerExec:run": { kind: "public" },
58515
- // 2026-05-26 修 clawd people TUI 渲染 mix 串扰回归(参考 f1133ff0 原 owner 路径
58516
- // snapshot/buffer/flush 修复):之前三件套 ADMIN_ANY 把 guest 拦在 dispatcher 401,
58517
- // 导致 XtermPanel 重挂载拉 snapshot 在 guest 路径不生效,老的字节流断裂串扰回来。
58518
- // 同一个 session 永远只有一个操作者(owner 不进 guest 创建的 session),并发字节流
58519
- // 冲突不存在;guest 在 chat 模式下已能 session:send/interrupt,PTY 域不开放纯属
58520
- // 历史遗留的不对称。handler 端通过 ensureSessionAccess 验权对齐其他 session:* RPC。
58521
- "session:pty:input": CAPABILITY_SCOPED,
58522
- "session:pty:resize": CAPABILITY_SCOPED,
58523
- "session:pty:snapshot": CAPABILITY_SCOPED,
58524
- // file-sharing attachment.* RPC:dispatcher 放行;handler 内按 sessionId 反查
58525
- // session.ownerPersonaId + ctx.grants 跑 assertGrant,限制 guest 只能签 / 操作
58526
- // 自己被授权 persona 下的 session 文件。HTTP Bearer 仍然只识别 owner,所以 guest
58527
- // 拿到 signed URL 后还要 HTTP 端校验通过才能取到文件——这里 RPC 层 ACL 是第一道。
58528
- "attachment.signUrl": CAPABILITY_SCOPED,
58529
- "attachment.groupAdd": CAPABILITY_SCOPED,
58530
- "attachment.groupRemove": CAPABILITY_SCOPED,
58531
- "attachment.groupList": CAPABILITY_SCOPED,
58532
- // ---- extension runtime(owner-only;handler 内再 assertOwner 兜底) ----
58533
- "extension.list": ADMIN_ANY,
58534
- "extension.open": ADMIN_ANY,
58535
- "extension.close": ADMIN_ANY,
58536
- "extension.uninstall": ADMIN_ANY,
58537
- // ---- extension sharing v1(spec 2026-05-28-clawd-extension-sharing-design) ----
58538
- // owner-side:owner 自己管理 publish 状态
58539
- "extension.publish": ADMIN_ANY,
58540
- "extension.unpublish": ADMIN_ANY,
58541
- "extension.publish:status": ADMIN_ANY,
58542
- // cross-tunnel:guest 从 owner 拉 published list + bundle
58543
- "extension.list-published": CAPABILITY_SCOPED,
58544
- "extension.fetchBundle": CAPABILITY_SCOPED,
58545
- // guest-self:guest 在自己 daemon 上触发安装与更新(无持久化 subscription 概念)
58546
- "extension.install-from-channel": ADMIN_ANY,
58547
- "extension.update-from-channel": ADMIN_ANY,
58548
- // ---- auth:* 飞书统一身份 Phase 1(owner-only) ----
58549
- // 登录/登出/查身份都是 owner 本机操作,guest 不可调
58550
- "auth:login:start": ADMIN_ANY,
58551
- "auth:getIdentity": ADMIN_ANY,
58552
- "auth:logout": ADMIN_ANY,
58553
- // ---- device:* 设备目录 + 连接(决策 #15,owner-only) ----
58554
- // 设备目录 / 连接都是 owner 本机操作(用 owner 的 ttcJwt 找中心 server),guest 不可调
58555
- "device:list": ADMIN_ANY,
58556
- "device:connect": ADMIN_ANY,
58557
- // ---- app-builder Project picker(spec 2026-06-01-app-builder-project-picker-design) ----
58558
- // owner-only:picker UI 给 owner 管理本机 build 中的 project(fs + 端口分配)。
58559
- // 即使 persona-app-builder 是 PreinstalledPersona,project 管理也属本机 owner 行为,
58560
- // 不暴露给 guest(远端接入者)。
58561
- "appBuilder:listProjects": ADMIN_ANY,
58562
- // getProject 走 capability-scoped(区别于其它 appBuilder 的 owner-only ADMIN_ANY):mirror/people
58563
- // 的本机 owner 在远端是 guest,需能调 getProject 拿它 mirror 的 session 的 project。dispatcher
58564
- // 放行后由 handler 内 canAccessSession(read) 按 session 可见性授权(防 guest 越权拿别人 project)。
58565
- "appBuilder:getProject": CAPABILITY_SCOPED,
58566
- "appBuilder:createProject": ADMIN_ANY,
58567
- "appBuilder:deleteProject": ADMIN_ANY,
58568
- "appBuilder:updateProjectPort": ADMIN_ANY,
58569
- // people/mirror 协同写操作(spec 2026-06-04-people-mode-app-builder-write-and-push):以下 5 个写
58570
- // method 走 CAPABILITY_SCOPED(不再 owner-only ADMIN_ANY),handler 内 ensureProjectWriteAccess
58571
- // 按 session 可见性(canAccessSession 'send')授权——mirror 的本机 owner 在远端是 guest,能 mirror
58572
- // 该 session 就能操作它绑定的 project(发布凭证是 persona 共享资产,老板决策"统一 session 授权")。
58573
- "appBuilder:setProdUrl": CAPABILITY_SCOPED,
58574
- "appBuilder:startDevServer": CAPABILITY_SCOPED,
58575
- // reportStage 仍 owner-only:assistant 走 HTTP RPC 上报(不过 WS admin grant),UI 不调它。
58576
- "appBuilder:reportStage": ADMIN_ANY,
58577
- "appBuilder:stopDevServer": CAPABILITY_SCOPED,
58578
- "appBuilder:publish": CAPABILITY_SCOPED,
58579
- "appBuilder:dismissPublishJob": CAPABILITY_SCOPED
58580
- };
58581
- function computeGrantForFrame(method, frame) {
58582
- const rule = METHOD_GRANT_MAP[method];
58583
- if (!rule) return { kind: "public" };
58584
- if (rule.kind === "public") return { kind: "public" };
58585
- if (rule.kind === "capability-scoped") return { kind: "public" };
58586
- if (rule.kind === "fixed") {
58587
- return { kind: "check", resource: rule.resource, action: rule.action };
58588
- }
58589
- const picked = rule.pick(frame);
58590
- if (!picked) return { kind: "public" };
58591
- return { kind: "check", resource: picked.resource, action: picked.action };
58592
- }
58593
-
58594
- // src/handlers/dispatch-rpc.ts
58595
58656
  async function dispatchRpc(method, frame, client, ctx, deps) {
58596
58657
  if (!METHOD_NAMES.includes(method)) {
58597
58658
  throw new ClawdError(ERROR_CODES.METHOD_NOT_ALLOWED, `unknown method: ${method}`);
@@ -58604,7 +58665,7 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
58604
58665
  `${method} requires feishu login (People/remote identity)`
58605
58666
  );
58606
58667
  }
58607
- const verdict = computeGrantForFrame(method, frame);
58668
+ const verdict = computeGrant(method);
58608
58669
  if (verdict.kind === "check") {
58609
58670
  const ok = assertGrant(ctx.grants, verdict.resource, verdict.action);
58610
58671
  if (!ok) {
@@ -59562,6 +59623,10 @@ async function startDaemon(config) {
59562
59623
  /* @__PURE__ */ new Set([...config.previewPorts ?? [], ...appBuilderPortRange])
59563
59624
  );
59564
59625
  const makeHandlers = () => buildMethodHandlers({
59626
+ // meta:methods 自省:飞书 gate getter + 最终 handler 表(下面还会 spread 进
59627
+ // dispatch / shift-internal 两组,只看 buildMethodHandlers 内部表会误报死方法)
59628
+ feishuActive: () => feishuActive,
59629
+ getAllHandlers: () => handlers,
59565
59630
  manager,
59566
59631
  workspace,
59567
59632
  skills,