@clawos-dev/clawd 0.2.178 → 0.2.179-beta.359.7bf1eca
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 +120 -149
- package/dist/shift/mcp-server.cjs +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -253,11 +253,7 @@ var init_events = __esm({
|
|
|
253
253
|
"skill-hint",
|
|
254
254
|
"meta-text",
|
|
255
255
|
"attachment-skills",
|
|
256
|
-
"attachment-deferred-tools"
|
|
257
|
-
// clawd shift v1:scheduler fire 时 daemon 给新 cc 的首条 user message 用
|
|
258
|
-
// <clawd-shift-fire> wrap 起头;classifyUserMessage 识别后路由到 UserMetaLine 折叠渲染,
|
|
259
|
-
// 不显示成 user 气泡(避免 instruction 文本被当 owner 实时对话)。
|
|
260
|
-
"clawd-shift-fire"
|
|
256
|
+
"attachment-deferred-tools"
|
|
261
257
|
];
|
|
262
258
|
SKILL_SOURCE_VALUES = ["builtin", "global", "project", "plugin"];
|
|
263
259
|
AGENT_SOURCE_VALUES = [
|
|
@@ -11244,7 +11240,6 @@ function classifyUserMessage(input) {
|
|
|
11244
11240
|
if (SYSTEM_REMINDER_RE.test(text) || OPENSPEC_BLOCK_RE.test(text)) {
|
|
11245
11241
|
return { meta: "system-reminder" };
|
|
11246
11242
|
}
|
|
11247
|
-
if (CLAWD_SHIFT_FIRE_RE.test(text)) return { meta: "clawd-shift-fire" };
|
|
11248
11243
|
if (SKILL_HINT_RE.test(text)) return { meta: "skill-hint" };
|
|
11249
11244
|
const flags = raw;
|
|
11250
11245
|
if (flags.isMeta === true || flags.isSynthetic === true) return { meta: "meta-text" };
|
|
@@ -11459,7 +11454,7 @@ function readCurrentContent(filePath) {
|
|
|
11459
11454
|
return null;
|
|
11460
11455
|
}
|
|
11461
11456
|
}
|
|
11462
|
-
var import_node_fs10, import_node_os5, import_node_path9, TASK_NOTIFICATION_RE, TASK_ID_RE, TOOL_USE_ID_RE, SLASH_COMMAND_RE, LOCAL_COMMAND_RE, SYSTEM_REMINDER_RE, OPENSPEC_BLOCK_RE, SKILL_HINT_RE,
|
|
11457
|
+
var import_node_fs10, import_node_os5, import_node_path9, TASK_NOTIFICATION_RE, TASK_ID_RE, TOOL_USE_ID_RE, SLASH_COMMAND_RE, LOCAL_COMMAND_RE, SYSTEM_REMINDER_RE, OPENSPEC_BLOCK_RE, SKILL_HINT_RE, ATTACHMENT_SILENT_SUBTYPES, ClaudeHistoryReader;
|
|
11463
11458
|
var init_claude_history = __esm({
|
|
11464
11459
|
"src/tools/claude-history.ts"() {
|
|
11465
11460
|
"use strict";
|
|
@@ -11476,7 +11471,6 @@ var init_claude_history = __esm({
|
|
|
11476
11471
|
SYSTEM_REMINDER_RE = /^\s*<system-reminder\b/i;
|
|
11477
11472
|
OPENSPEC_BLOCK_RE = /^\s*<!--\s*OPENSPEC\b/i;
|
|
11478
11473
|
SKILL_HINT_RE = /^\s*Base directory for this skill:\s/;
|
|
11479
|
-
CLAWD_SHIFT_FIRE_RE = /^\s*<clawd-shift-fire\b/i;
|
|
11480
11474
|
ATTACHMENT_SILENT_SUBTYPES = /* @__PURE__ */ new Set([
|
|
11481
11475
|
"hook_additional_context",
|
|
11482
11476
|
"hook_success",
|
|
@@ -41345,34 +41339,11 @@ function buildConnectionPrompt(args) {
|
|
|
41345
41339
|
}
|
|
41346
41340
|
if (args.workDir) {
|
|
41347
41341
|
result += `
|
|
41348
|
-
\u4F60\u7684\u6587\u4EF6\u5DE5\u4F5C\u76EE\u5F55\u662F ${args.workDir}\uFF0C\
|
|
41342
|
+
\u4F60\u7684\u6587\u4EF6\u5DE5\u4F5C\u76EE\u5F55\u662F ${args.workDir}\uFF0C\u6240\u6709\u6587\u4EF6\u8BFB\u5199\u90FD\u653E\u5728\u8FD9\u4E2A\u76EE\u5F55\u4E0B\u3002persona \u76EE\u5F55\u5BF9\u4F60\u53EA\u8BFB\uFF0C\u4E0D\u8981\u5F80\u91CC\u5199\u3002`;
|
|
41349
41343
|
}
|
|
41350
41344
|
return result;
|
|
41351
41345
|
}
|
|
41352
41346
|
|
|
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
|
-
|
|
41376
41347
|
// src/dispatch/system-prompt.ts
|
|
41377
41348
|
var DISPATCH_SYSTEM_PROMPT_HINT = `## \u59D4\u6D3E\u7ED9\u53E6\u4E00\u4E2A persona\uFF08dispatch\uFF09
|
|
41378
41349
|
|
|
@@ -41538,9 +41509,6 @@ function buildSpawnContext(state, deps) {
|
|
|
41538
41509
|
if (dispatchMcpConfigPath2) {
|
|
41539
41510
|
ctx.extraSystemPrompt = (ctx.extraSystemPrompt ? ctx.extraSystemPrompt + "\n\n" : "") + DISPATCH_SYSTEM_PROMPT_HINT;
|
|
41540
41511
|
}
|
|
41541
|
-
if (daemonUrl) {
|
|
41542
|
-
ctx.extraSystemPrompt = (ctx.extraSystemPrompt ? ctx.extraSystemPrompt + "\n\n" : "") + ATTACHMENT_SHARING_HINT;
|
|
41543
|
-
}
|
|
41544
41512
|
if (meta?.extraSettings) {
|
|
41545
41513
|
ctx.extraSettings = meta.extraSettings;
|
|
41546
41514
|
}
|
|
@@ -49275,67 +49243,6 @@ function removeSubscription(client, sessionId) {
|
|
|
49275
49243
|
else client.subscribedSessions.set(sessionId, next);
|
|
49276
49244
|
}
|
|
49277
49245
|
|
|
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
|
-
|
|
49339
49246
|
// src/transport/auth.ts
|
|
49340
49247
|
init_protocol();
|
|
49341
49248
|
var AuthGate = class {
|
|
@@ -50578,6 +50485,60 @@ async function importZip(buf, root) {
|
|
|
50578
50485
|
}
|
|
50579
50486
|
}
|
|
50580
50487
|
|
|
50488
|
+
// src/transport/rpc-adapter.ts
|
|
50489
|
+
var EXPOSED_RPC_METHODS = [
|
|
50490
|
+
"appBuilder.createProject",
|
|
50491
|
+
"appBuilder.listProjects",
|
|
50492
|
+
"appBuilder.updateProjectPort",
|
|
50493
|
+
"appBuilder.setProdUrl",
|
|
50494
|
+
// 单栏 refactor §5.6.2 stage fix:scaffold + install 完成调 startDevServer 起 dev server;
|
|
50495
|
+
// 中间用 reportStage 上报阶段(scaffolding / installing / failed),UI 据 stage 推帧切 PreviewPane。
|
|
50496
|
+
"appBuilder.startDevServer",
|
|
50497
|
+
"appBuilder.reportStage",
|
|
50498
|
+
// 单栏 §5.6.5 fix: UI 切 session 时调(assistant 不需要主动调,但暴露白名单避免误猜成 forbidden)
|
|
50499
|
+
"appBuilder.setActiveSession",
|
|
50500
|
+
// dataclaw-builder:persona 建页时取建页人身份(含 union_id)写进生成 app 的配置。
|
|
50501
|
+
// 路由到 handlers['auth:getIdentity'],只返 identity、绝不带 ttcToken(见 handlers/feishu-auth.ts)。
|
|
50502
|
+
"auth.getIdentity",
|
|
50503
|
+
// Persona dispatch (Task 4): A 端 dispatch MCP server 调 run(hold-and-wait 一次);
|
|
50504
|
+
// B 端 dispatch MCP server 调 complete(带 outcome 触发 run 端 resolve)。详见
|
|
50505
|
+
// protocol/src/dispatch.ts。
|
|
50506
|
+
"personaDispatch.run",
|
|
50507
|
+
"personaDispatch.complete",
|
|
50508
|
+
// clawd-shift v1 (spec 2026-06-24-clawd-shift):cc 子进程内的 shift MCP server 调
|
|
50509
|
+
// 这 5 个 method 管理 owner 的 shift(fire-and-forget 排班)。鉴权靠 body.personaId
|
|
50510
|
+
// (mcp-server 从 process.env.CLAWD_PERSONA_ID 读,源 = SessionFile.ownerPersonaId)。
|
|
50511
|
+
// handler 内 cancel/update/history 校验 createdByPersona === personaId 防越权。
|
|
50512
|
+
"shift-internal.add",
|
|
50513
|
+
"shift-internal.list",
|
|
50514
|
+
"shift-internal.cancel",
|
|
50515
|
+
"shift-internal.update",
|
|
50516
|
+
"shift-internal.history"
|
|
50517
|
+
];
|
|
50518
|
+
async function handleRpcRequest(input) {
|
|
50519
|
+
const { method, body, sessionId, dispatcher } = input;
|
|
50520
|
+
if (!sessionId) {
|
|
50521
|
+
return { status: 400, body: { ok: false, error: "MISSING_SESSION_ID", message: "X-Clawd-Session-Id header required" } };
|
|
50522
|
+
}
|
|
50523
|
+
if (!EXPOSED_RPC_METHODS.includes(method)) {
|
|
50524
|
+
return { status: 403, body: { ok: false, error: "METHOD_NOT_EXPOSED", message: `method "${method}" not exposed via HTTP RPC` } };
|
|
50525
|
+
}
|
|
50526
|
+
const internalMethod = method.replace(".", ":");
|
|
50527
|
+
const augmentedBody = {
|
|
50528
|
+
...typeof body === "object" && body != null ? body : {},
|
|
50529
|
+
sessionId
|
|
50530
|
+
};
|
|
50531
|
+
try {
|
|
50532
|
+
const result = await dispatcher.dispatch(internalMethod, augmentedBody, { sessionId });
|
|
50533
|
+
return { status: 200, body: { ok: true, result } };
|
|
50534
|
+
} catch (err) {
|
|
50535
|
+
const e = err;
|
|
50536
|
+
const code = e?.code ?? "INTERNAL";
|
|
50537
|
+
const message = e?.message ?? String(err);
|
|
50538
|
+
return { status: 400, body: { ok: false, error: code, message } };
|
|
50539
|
+
}
|
|
50540
|
+
}
|
|
50541
|
+
|
|
50581
50542
|
// src/transport/http-router.ts
|
|
50582
50543
|
var RPC_ROUTE_PREFIX = "/api/rpc/";
|
|
50583
50544
|
var RPC_BODY_MAX_BYTES = 1 * 1024 * 1024;
|
|
@@ -52968,16 +52929,6 @@ function personaIdFromPath(absPath, personaRoot) {
|
|
|
52968
52929
|
const id = rel.split(path44.sep)[0];
|
|
52969
52930
|
return id || null;
|
|
52970
52931
|
}
|
|
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
|
-
}
|
|
52981
52932
|
|
|
52982
52933
|
// src/handlers/history.ts
|
|
52983
52934
|
init_protocol();
|
|
@@ -53789,31 +53740,6 @@ function assertGuestAttachmentPath(ctx, absPath, personaRoot, method, usersRoot)
|
|
|
53789
53740
|
}
|
|
53790
53741
|
}
|
|
53791
53742
|
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
|
-
}
|
|
53817
53743
|
const signUrl = async (frame, _client, ctx) => {
|
|
53818
53744
|
const parsed = AttachmentSignUrlArgs.safeParse(frame);
|
|
53819
53745
|
if (!parsed.success) {
|
|
@@ -53856,7 +53782,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
53856
53782
|
}
|
|
53857
53783
|
const cwdAbs = import_node_path36.default.resolve(sessionFile.cwd);
|
|
53858
53784
|
const candidateAbs = import_node_path36.default.isAbsolute(args.relPath) ? import_node_path36.default.resolve(args.relPath) : import_node_path36.default.resolve(cwdAbs, args.relPath);
|
|
53859
|
-
|
|
53785
|
+
assertGuestAttachmentPath(ctx, candidateAbs, deps.personaRoot, "attachment.signUrl", deps.usersRoot);
|
|
53860
53786
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
53861
53787
|
const entry = entries.find((e) => {
|
|
53862
53788
|
const storedAbs = import_node_path36.default.isAbsolute(e.relPath) ? import_node_path36.default.resolve(e.relPath) : import_node_path36.default.resolve(cwdAbs, e.relPath);
|
|
@@ -53895,18 +53821,15 @@ function buildAttachmentHandlers(deps) {
|
|
|
53895
53821
|
throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, parsed.error.message);
|
|
53896
53822
|
}
|
|
53897
53823
|
const args = parsed.data;
|
|
53824
|
+
guardGuestBySessionCwd(ctx, args.sessionId, "attachment.groupAdd");
|
|
53898
53825
|
const scope = deps.getSessionScope(args.sessionId);
|
|
53899
53826
|
if (!scope) {
|
|
53900
53827
|
throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
|
|
53901
53828
|
}
|
|
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";
|
|
53906
53829
|
const size = 0;
|
|
53907
53830
|
const entry = deps.groupFileStore.upsert(scope, args.sessionId, {
|
|
53908
53831
|
relPath: args.relPath,
|
|
53909
|
-
from,
|
|
53832
|
+
from: "owner",
|
|
53910
53833
|
label: args.label,
|
|
53911
53834
|
size,
|
|
53912
53835
|
mime: lookupMime(args.relPath)
|
|
@@ -55731,11 +55654,22 @@ function buildAppBuilderHandlers(deps) {
|
|
|
55731
55654
|
}
|
|
55732
55655
|
|
|
55733
55656
|
// src/handlers/shift.ts
|
|
55734
|
-
|
|
55735
|
-
|
|
55736
|
-
|
|
55737
|
-
|
|
55738
|
-
|
|
55657
|
+
var UI_DEFAULT_DURATION_MS = 30 * 6e4;
|
|
55658
|
+
var UI_MAX_DURATION_MS = 60 * 6e4;
|
|
55659
|
+
function pickUiDurationMs(timeoutMs) {
|
|
55660
|
+
return Math.min(timeoutMs ?? UI_DEFAULT_DURATION_MS, UI_MAX_DURATION_MS);
|
|
55661
|
+
}
|
|
55662
|
+
function everyMsToCron(everyMs) {
|
|
55663
|
+
if (everyMs <= 6e4) return "* * * * *";
|
|
55664
|
+
const minutes = Math.floor(everyMs / 6e4);
|
|
55665
|
+
if (minutes < 60) return `*/${minutes} * * * *`;
|
|
55666
|
+
const hours = Math.floor(minutes / 60);
|
|
55667
|
+
if (hours < 24) return `0 */${hours} * * *`;
|
|
55668
|
+
const days = Math.max(1, Math.floor(hours / 24));
|
|
55669
|
+
return `0 0 */${days} * *`;
|
|
55670
|
+
}
|
|
55671
|
+
function daemonShiftToUiShift(d, nowMs) {
|
|
55672
|
+
const durationMs = pickUiDurationMs(d.timeoutMs);
|
|
55739
55673
|
const noteParts = [d.name];
|
|
55740
55674
|
if (d.prompt && d.prompt.length > 0) {
|
|
55741
55675
|
const promptPreview = d.prompt.length > 120 ? `${d.prompt.slice(0, 117)}\u2026` : d.prompt;
|
|
@@ -55743,12 +55677,49 @@ function daemonShiftToUiShift(d, _nowMs) {
|
|
|
55743
55677
|
}
|
|
55744
55678
|
if (!d.enabled) noteParts.push("(disabled)");
|
|
55745
55679
|
const note = noteParts.join(" \u2014 ");
|
|
55746
|
-
|
|
55747
|
-
|
|
55748
|
-
|
|
55749
|
-
|
|
55750
|
-
|
|
55751
|
-
|
|
55680
|
+
switch (d.schedule.kind) {
|
|
55681
|
+
case "at": {
|
|
55682
|
+
const startMs = Date.parse(d.schedule.at);
|
|
55683
|
+
if (Number.isNaN(startMs)) return null;
|
|
55684
|
+
if (startMs + durationMs <= nowMs) return null;
|
|
55685
|
+
return {
|
|
55686
|
+
id: d.id,
|
|
55687
|
+
agentId: d.targetPersona,
|
|
55688
|
+
schedule: {
|
|
55689
|
+
kind: "single",
|
|
55690
|
+
start: new Date(startMs).toISOString(),
|
|
55691
|
+
end: new Date(startMs + durationMs).toISOString()
|
|
55692
|
+
},
|
|
55693
|
+
note
|
|
55694
|
+
};
|
|
55695
|
+
}
|
|
55696
|
+
case "cron": {
|
|
55697
|
+
return {
|
|
55698
|
+
id: d.id,
|
|
55699
|
+
agentId: d.targetPersona,
|
|
55700
|
+
schedule: {
|
|
55701
|
+
kind: "recurring",
|
|
55702
|
+
cron: d.schedule.expr,
|
|
55703
|
+
...d.schedule.tz ? { tz: d.schedule.tz } : {},
|
|
55704
|
+
durationMs
|
|
55705
|
+
},
|
|
55706
|
+
note
|
|
55707
|
+
};
|
|
55708
|
+
}
|
|
55709
|
+
case "every": {
|
|
55710
|
+
const cron = everyMsToCron(d.schedule.everyMs);
|
|
55711
|
+
return {
|
|
55712
|
+
id: d.id,
|
|
55713
|
+
agentId: d.targetPersona,
|
|
55714
|
+
schedule: {
|
|
55715
|
+
kind: "recurring",
|
|
55716
|
+
cron,
|
|
55717
|
+
durationMs
|
|
55718
|
+
},
|
|
55719
|
+
note
|
|
55720
|
+
};
|
|
55721
|
+
}
|
|
55722
|
+
}
|
|
55752
55723
|
}
|
|
55753
55724
|
function buildShiftHandlers(deps) {
|
|
55754
55725
|
return {
|
|
@@ -57476,7 +57447,7 @@ async function startDaemon(config) {
|
|
|
57476
57447
|
});
|
|
57477
57448
|
const rpcDispatcher = {
|
|
57478
57449
|
async dispatch(method, args, _ctx) {
|
|
57479
|
-
const handler =
|
|
57450
|
+
const handler = handlers[method];
|
|
57480
57451
|
if (!handler) {
|
|
57481
57452
|
throw Object.assign(new Error(`unknown RPC method "${method}"`), { code: "METHOD_NOT_IMPLEMENTED" });
|
|
57482
57453
|
}
|
|
@@ -21252,7 +21252,7 @@ async function main() {
|
|
|
21252
21252
|
name: external_exports.string().describe("short label shown in shift.json + future UI"),
|
|
21253
21253
|
schedule: ScheduleSchema,
|
|
21254
21254
|
prompt: external_exports.string().describe(
|
|
21255
|
-
"instruction the spawned cc executes on startup (NOT a chat message from the owner). Write what you want the persona to DO or SAY at fire time, in instruction form \u2014 daemon wraps it in a
|
|
21255
|
+
"instruction the spawned cc executes on startup (NOT a chat message from the owner). Write what you want the persona to DO or SAY at fire time, in instruction form \u2014 daemon wraps it in a `<clawd-shift-fire>` tag so the spawned cc treats it as a scheduled action and responds as an assistant turn (the wrap is stripped from the owner's UI bubble)."
|
|
21256
21256
|
),
|
|
21257
21257
|
targetPersona: external_exports.string().optional().describe("which persona to spawn; defaults to the calling persona"),
|
|
21258
21258
|
timeoutMs: external_exports.number().optional().describe("hard timeout per fire; default 30 minutes")
|
package/package.json
CHANGED