@clawos-dev/clawd 0.2.177 → 0.2.178

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.
Files changed (2) hide show
  1. package/dist/cli.cjs +129 -58
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -41350,6 +41350,29 @@ function buildConnectionPrompt(args) {
41350
41350
  return result;
41351
41351
  }
41352
41352
 
41353
+ // src/persona/sharing-prompt.ts
41354
+ var ATTACHMENT_SHARING_HINT = `## \u628A\u4EA7\u51FA\u6587\u4EF6\u5206\u4EAB\u7ED9\u5BF9\u65B9\uFF08attachment\uFF09
41355
+
41356
+ \u5F53\u4F60\u4EA7\u51FA\u4E86\u6587\u4EF6\u3001\u9700\u8981\u8BA9\u5BF9\u65B9\u62FF\u5230\u65F6\uFF08\u5C24\u5176 Bash \u89E3\u538B/\u751F\u6210\u7684\u6587\u4EF6\uFF0C\u6216\u8DE8\u8BBE\u5907\u88AB\u59D4\u6D3E\u65F6\u5BF9\u65B9\u5728\u53E6\u4E00\u53F0\u673A\u5668\u3001\u770B\u4E0D\u5230\u4F60\u7684\u672C\u5730\u8DEF\u5F84\uFF09\uFF0C\u7528 daemon \u7684 HTTP RPC \u628A\u6587\u4EF6\u8F6C\u6210\u4E00\u4E2A\u5E26\u7B7E\u540D\u7684\u4E0B\u8F7D URL\uFF1A
41357
+
41358
+ 1. \u5165\u7FA4\uFF08\u767B\u8BB0\u5230\u672C session \u7684\u5171\u4EAB\u6E05\u5355\uFF09\uFF1A
41359
+ \`\`\`bash
41360
+ curl -sS -X POST "$CLAWD_DAEMON_URL/api/rpc/attachment.groupAdd" \\
41361
+ -H "X-Clawd-Session-Id: $CLAWD_SESSION_ID" -H 'Content-Type: application/json' \\
41362
+ -d '{"relPath":"<\u6587\u4EF6\u7684\u7EDD\u5BF9\u8DEF\u5F84>"}'
41363
+ \`\`\`
41364
+ 2. \u7B7E\u540D\u62FF URL\uFF1A
41365
+ \`\`\`bash
41366
+ curl -sS -X POST "$CLAWD_DAEMON_URL/api/rpc/attachment.signUrl" \\
41367
+ -H "X-Clawd-Session-Id: $CLAWD_SESSION_ID" -H 'Content-Type: application/json' \\
41368
+ -d '{"relPath":"<\u540C\u4E00\u4E2A\u7EDD\u5BF9\u8DEF\u5F84>"}'
41369
+ \`\`\`
41370
+ \u8FD4\u56DE\u7684 \`url\` \u5C31\u662F\u5BF9\u65B9\u53EF\u76F4\u63A5\u6253\u5F00/\u4E0B\u8F7D\u7684\u94FE\u63A5\u3002
41371
+
41372
+ - \`relPath\` \u5FC5\u987B\u4F20**\u7EDD\u5BF9\u8DEF\u5F84**\uFF08\u4F60\u4EA7\u51FA\u6587\u4EF6\u7684\u771F\u5B9E\u8DEF\u5F84\uFF09\uFF0C\u4E0D\u8981\u4F20\u76F8\u5BF9\u8DEF\u5F84\u3002
41373
+ - \u53EA\u80FD\u5206\u4EAB\u4F60\u81EA\u5DF1\u5DE5\u4F5C\u76EE\u5F55\u5185\u7684\u6587\u4EF6\uFF0C\u8D8A\u754C\u4F1A\u88AB\u62D2\u3002
41374
+ - \u88AB dispatch \u63A5\u5230\u4EFB\u52A1\u3001\u8981\u56DE\u4F20\u4EA7\u7269\u65F6\uFF1A\u628A\u7B7E\u540D URL \u5199\u8FDB \`personaDispatchComplete\` \u7684 \`text\` \u91CC\uFF08\`filePaths\` \u53EA\u662F\u4F60\u672C\u673A\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u8DE8\u8BBE\u5907\u5BF9\u65B9\u6253\u4E0D\u5F00\uFF0CURL \u624D\u80FD\u6253\u5F00\uFF09\u3002`;
41375
+
41353
41376
  // src/dispatch/system-prompt.ts
41354
41377
  var DISPATCH_SYSTEM_PROMPT_HINT = `## \u59D4\u6D3E\u7ED9\u53E6\u4E00\u4E2A persona\uFF08dispatch\uFF09
41355
41378
 
@@ -41515,6 +41538,9 @@ function buildSpawnContext(state, deps) {
41515
41538
  if (dispatchMcpConfigPath2) {
41516
41539
  ctx.extraSystemPrompt = (ctx.extraSystemPrompt ? ctx.extraSystemPrompt + "\n\n" : "") + DISPATCH_SYSTEM_PROMPT_HINT;
41517
41540
  }
41541
+ if (daemonUrl) {
41542
+ ctx.extraSystemPrompt = (ctx.extraSystemPrompt ? ctx.extraSystemPrompt + "\n\n" : "") + ATTACHMENT_SHARING_HINT;
41543
+ }
41518
41544
  if (meta?.extraSettings) {
41519
41545
  ctx.extraSettings = meta.extraSettings;
41520
41546
  }
@@ -49249,6 +49275,67 @@ function removeSubscription(client, sessionId) {
49249
49275
  else client.subscribedSessions.set(sessionId, next);
49250
49276
  }
49251
49277
 
49278
+ // src/transport/rpc-adapter.ts
49279
+ var EXPOSED_RPC_METHODS = [
49280
+ "appBuilder.createProject",
49281
+ "appBuilder.listProjects",
49282
+ "appBuilder.updateProjectPort",
49283
+ "appBuilder.setProdUrl",
49284
+ // 单栏 refactor §5.6.2 stage fix:scaffold + install 完成调 startDevServer 起 dev server;
49285
+ // 中间用 reportStage 上报阶段(scaffolding / installing / failed),UI 据 stage 推帧切 PreviewPane。
49286
+ "appBuilder.startDevServer",
49287
+ "appBuilder.reportStage",
49288
+ // 单栏 §5.6.5 fix: UI 切 session 时调(assistant 不需要主动调,但暴露白名单避免误猜成 forbidden)
49289
+ "appBuilder.setActiveSession",
49290
+ // dataclaw-builder:persona 建页时取建页人身份(含 union_id)写进生成 app 的配置。
49291
+ // 路由到 handlers['auth:getIdentity'],只返 identity、绝不带 ttcToken(见 handlers/feishu-auth.ts)。
49292
+ "auth.getIdentity",
49293
+ // Persona dispatch (Task 4): A 端 dispatch MCP server 调 run(hold-and-wait 一次);
49294
+ // B 端 dispatch MCP server 调 complete(带 outcome 触发 run 端 resolve)。详见
49295
+ // protocol/src/dispatch.ts。
49296
+ "personaDispatch.run",
49297
+ "personaDispatch.complete",
49298
+ // clawd-shift v1 (spec 2026-06-24-clawd-shift):cc 子进程内的 shift MCP server 调
49299
+ // 这 5 个 method 管理 owner 的 shift(fire-and-forget 排班)。鉴权靠 body.personaId
49300
+ // (mcp-server 从 process.env.CLAWD_PERSONA_ID 读,源 = SessionFile.ownerPersonaId)。
49301
+ // handler 内 cancel/update/history 校验 createdByPersona === personaId 防越权。
49302
+ "shift-internal.add",
49303
+ "shift-internal.list",
49304
+ "shift-internal.cancel",
49305
+ "shift-internal.update",
49306
+ "shift-internal.history",
49307
+ // file-sharing:persona guest agent 把自己工作目录内的产物入群+签名分享。
49308
+ // 路径越界由 handlers/attachment.ts guardAttachmentPath(scope-based)拦截。
49309
+ "attachment.groupAdd",
49310
+ "attachment.signUrl"
49311
+ ];
49312
+ async function handleRpcRequest(input) {
49313
+ const { method, body, sessionId, dispatcher } = input;
49314
+ if (!sessionId) {
49315
+ return { status: 400, body: { ok: false, error: "MISSING_SESSION_ID", message: "X-Clawd-Session-Id header required" } };
49316
+ }
49317
+ if (!EXPOSED_RPC_METHODS.includes(method)) {
49318
+ return { status: 403, body: { ok: false, error: "METHOD_NOT_EXPOSED", message: `method "${method}" not exposed via HTTP RPC` } };
49319
+ }
49320
+ const internalMethod = method.replace(".", ":");
49321
+ const augmentedBody = {
49322
+ ...typeof body === "object" && body != null ? body : {},
49323
+ sessionId
49324
+ };
49325
+ try {
49326
+ const result = await dispatcher.dispatch(internalMethod, augmentedBody, { sessionId });
49327
+ return { status: 200, body: { ok: true, result } };
49328
+ } catch (err) {
49329
+ const e = err;
49330
+ const code = e?.code ?? "INTERNAL";
49331
+ const message = e?.message ?? String(err);
49332
+ return { status: 400, body: { ok: false, error: code, message } };
49333
+ }
49334
+ }
49335
+ function resolveExposedHandler(handlers, internalMethod) {
49336
+ return handlers[internalMethod] ?? handlers[internalMethod.replace(":", ".")];
49337
+ }
49338
+
49252
49339
  // src/transport/auth.ts
49253
49340
  init_protocol();
49254
49341
  var AuthGate = class {
@@ -50491,60 +50578,6 @@ async function importZip(buf, root) {
50491
50578
  }
50492
50579
  }
50493
50580
 
50494
- // src/transport/rpc-adapter.ts
50495
- var EXPOSED_RPC_METHODS = [
50496
- "appBuilder.createProject",
50497
- "appBuilder.listProjects",
50498
- "appBuilder.updateProjectPort",
50499
- "appBuilder.setProdUrl",
50500
- // 单栏 refactor §5.6.2 stage fix:scaffold + install 完成调 startDevServer 起 dev server;
50501
- // 中间用 reportStage 上报阶段(scaffolding / installing / failed),UI 据 stage 推帧切 PreviewPane。
50502
- "appBuilder.startDevServer",
50503
- "appBuilder.reportStage",
50504
- // 单栏 §5.6.5 fix: UI 切 session 时调(assistant 不需要主动调,但暴露白名单避免误猜成 forbidden)
50505
- "appBuilder.setActiveSession",
50506
- // dataclaw-builder:persona 建页时取建页人身份(含 union_id)写进生成 app 的配置。
50507
- // 路由到 handlers['auth:getIdentity'],只返 identity、绝不带 ttcToken(见 handlers/feishu-auth.ts)。
50508
- "auth.getIdentity",
50509
- // Persona dispatch (Task 4): A 端 dispatch MCP server 调 run(hold-and-wait 一次);
50510
- // B 端 dispatch MCP server 调 complete(带 outcome 触发 run 端 resolve)。详见
50511
- // protocol/src/dispatch.ts。
50512
- "personaDispatch.run",
50513
- "personaDispatch.complete",
50514
- // clawd-shift v1 (spec 2026-06-24-clawd-shift):cc 子进程内的 shift MCP server 调
50515
- // 这 5 个 method 管理 owner 的 shift(fire-and-forget 排班)。鉴权靠 body.personaId
50516
- // (mcp-server 从 process.env.CLAWD_PERSONA_ID 读,源 = SessionFile.ownerPersonaId)。
50517
- // handler 内 cancel/update/history 校验 createdByPersona === personaId 防越权。
50518
- "shift-internal.add",
50519
- "shift-internal.list",
50520
- "shift-internal.cancel",
50521
- "shift-internal.update",
50522
- "shift-internal.history"
50523
- ];
50524
- async function handleRpcRequest(input) {
50525
- const { method, body, sessionId, dispatcher } = input;
50526
- if (!sessionId) {
50527
- return { status: 400, body: { ok: false, error: "MISSING_SESSION_ID", message: "X-Clawd-Session-Id header required" } };
50528
- }
50529
- if (!EXPOSED_RPC_METHODS.includes(method)) {
50530
- return { status: 403, body: { ok: false, error: "METHOD_NOT_EXPOSED", message: `method "${method}" not exposed via HTTP RPC` } };
50531
- }
50532
- const internalMethod = method.replace(".", ":");
50533
- const augmentedBody = {
50534
- ...typeof body === "object" && body != null ? body : {},
50535
- sessionId
50536
- };
50537
- try {
50538
- const result = await dispatcher.dispatch(internalMethod, augmentedBody, { sessionId });
50539
- return { status: 200, body: { ok: true, result } };
50540
- } catch (err) {
50541
- const e = err;
50542
- const code = e?.code ?? "INTERNAL";
50543
- const message = e?.message ?? String(err);
50544
- return { status: 400, body: { ok: false, error: code, message } };
50545
- }
50546
- }
50547
-
50548
50581
  // src/transport/http-router.ts
50549
50582
  var RPC_ROUTE_PREFIX = "/api/rpc/";
50550
50583
  var RPC_BODY_MAX_BYTES = 1 * 1024 * 1024;
@@ -52935,6 +52968,16 @@ function personaIdFromPath(absPath, personaRoot) {
52935
52968
  const id = rel.split(path44.sep)[0];
52936
52969
  return id || null;
52937
52970
  }
52971
+ function isPathWithin(dir, absPath) {
52972
+ const d = path44.resolve(dir);
52973
+ const t = path44.resolve(absPath);
52974
+ const sep3 = d.endsWith(path44.sep) ? "" : path44.sep;
52975
+ return t === d || t.startsWith(d + sep3);
52976
+ }
52977
+ function isPathInGuestBoundary(personaRoot, personaId, userWorkDir, absPath) {
52978
+ if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
52979
+ return personaIdFromPath(path44.resolve(absPath), personaRoot) === personaId;
52980
+ }
52938
52981
 
52939
52982
  // src/handlers/history.ts
52940
52983
  init_protocol();
@@ -53746,6 +53789,31 @@ function assertGuestAttachmentPath(ctx, absPath, personaRoot, method, usersRoot)
53746
53789
  }
53747
53790
  }
53748
53791
  function buildAttachmentHandlers(deps) {
53792
+ function guardAttachmentPath(ctx, sessionId, candidateAbs, method, nonGuestPolicy) {
53793
+ if (ctx?.principal.kind === "owner") return;
53794
+ const scope = deps.getSessionScope?.(sessionId);
53795
+ if (!scope) return;
53796
+ if (scope.kind === "persona" && scope.mode === "guest") {
53797
+ if (!deps.personaRoot) return;
53798
+ const userWorkDir = deps.usersRoot ? deriveUserWorkDir(scope.capId, deps.usersRoot) : void 0;
53799
+ if (!isPathInGuestBoundary(deps.personaRoot, scope.personaId, userWorkDir, candidateAbs)) {
53800
+ throw new ClawdError(
53801
+ ERROR_CODES.UNAUTHORIZED,
53802
+ `${method}: path outside guest work dir: ${candidateAbs}`
53803
+ );
53804
+ }
53805
+ return;
53806
+ }
53807
+ if (nonGuestPolicy === "group-acl") return;
53808
+ const cwd = deps.sessionStore?.read(sessionId)?.cwd;
53809
+ if (!cwd) return;
53810
+ if (!isPathWithin(cwd, candidateAbs)) {
53811
+ throw new ClawdError(
53812
+ ERROR_CODES.UNAUTHORIZED,
53813
+ `${method}: path outside session work dir: ${candidateAbs}`
53814
+ );
53815
+ }
53816
+ }
53749
53817
  const signUrl = async (frame, _client, ctx) => {
53750
53818
  const parsed = AttachmentSignUrlArgs.safeParse(frame);
53751
53819
  if (!parsed.success) {
@@ -53788,7 +53856,7 @@ function buildAttachmentHandlers(deps) {
53788
53856
  }
53789
53857
  const cwdAbs = import_node_path36.default.resolve(sessionFile.cwd);
53790
53858
  const candidateAbs = import_node_path36.default.isAbsolute(args.relPath) ? import_node_path36.default.resolve(args.relPath) : import_node_path36.default.resolve(cwdAbs, args.relPath);
53791
- assertGuestAttachmentPath(ctx, candidateAbs, deps.personaRoot, "attachment.signUrl", deps.usersRoot);
53859
+ guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
53792
53860
  const entries = deps.groupFileStore.list(scope, args.sessionId);
53793
53861
  const entry = entries.find((e) => {
53794
53862
  const storedAbs = import_node_path36.default.isAbsolute(e.relPath) ? import_node_path36.default.resolve(e.relPath) : import_node_path36.default.resolve(cwdAbs, e.relPath);
@@ -53827,15 +53895,18 @@ function buildAttachmentHandlers(deps) {
53827
53895
  throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, parsed.error.message);
53828
53896
  }
53829
53897
  const args = parsed.data;
53830
- guardGuestBySessionCwd(ctx, args.sessionId, "attachment.groupAdd");
53831
53898
  const scope = deps.getSessionScope(args.sessionId);
53832
53899
  if (!scope) {
53833
53900
  throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
53834
53901
  }
53902
+ const cwdAbs = import_node_path36.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
53903
+ const candidateAbs = import_node_path36.default.isAbsolute(args.relPath) ? import_node_path36.default.resolve(args.relPath) : import_node_path36.default.resolve(cwdAbs, args.relPath);
53904
+ guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
53905
+ const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
53835
53906
  const size = 0;
53836
53907
  const entry = deps.groupFileStore.upsert(scope, args.sessionId, {
53837
53908
  relPath: args.relPath,
53838
- from: "owner",
53909
+ from,
53839
53910
  label: args.label,
53840
53911
  size,
53841
53912
  mime: lookupMime(args.relPath)
@@ -57405,7 +57476,7 @@ async function startDaemon(config) {
57405
57476
  });
57406
57477
  const rpcDispatcher = {
57407
57478
  async dispatch(method, args, _ctx) {
57408
- const handler = handlers[method];
57479
+ const handler = resolveExposedHandler(handlers, method);
57409
57480
  if (!handler) {
57410
57481
  throw Object.assign(new Error(`unknown RPC method "${method}"`), { code: "METHOD_NOT_IMPLEMENTED" });
57411
57482
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawos-dev/clawd",
3
- "version": "0.2.177",
3
+ "version": "0.2.178",
4
4
  "description": "Standalone clawd daemon — Claude Code (and future Codex) session server over WebSocket",
5
5
  "type": "module",
6
6
  "license": "MIT",