@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.
- package/dist/cli.cjs +129 -58
- 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
|
-
|
|
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
|
|
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
|
|
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
|
}
|