@clawos-dev/clawd 0.2.177-beta.357.f436213 → 0.2.178-beta.358.7f6c694
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 +139 -123
- package/dist/shift/mcp-server.cjs +1 -3
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -253,12 +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 — ...]` plain text marker 起头任务包(不是 XML 标签,避开
|
|
259
|
-
// reducer 双源 dedup 失效雷,见 daemon shift/wrap.ts 注释);classifyUserMessage
|
|
260
|
-
// 识别后路由到 UserMetaLine 折叠渲染,不显示成 user 气泡。
|
|
261
|
-
"clawd-shift-fire"
|
|
256
|
+
"attachment-deferred-tools"
|
|
262
257
|
];
|
|
263
258
|
SKILL_SOURCE_VALUES = ["builtin", "global", "project", "plugin"];
|
|
264
259
|
AGENT_SOURCE_VALUES = [
|
|
@@ -4789,16 +4784,6 @@ var init_schemas = __esm({
|
|
|
4789
4784
|
* 反查路径走错;按 #750 一族防线处理,跟 ownerPersonaId 同地点 strip)。
|
|
4790
4785
|
*/
|
|
4791
4786
|
dispatchedFromSessionId: external_exports.string().min(1).optional(),
|
|
4792
|
-
/**
|
|
4793
|
-
* Shift fire 派生的 session 记录调 addShift 的 source cc session id(稳定 key);
|
|
4794
|
-
* sidebar 据此把 fire 出的 session 折叠在 source session 下,跟 dispatchedFromSessionId
|
|
4795
|
-
* 平行但渲染不同 icon(AlarmClock 区别于 dispatch 的 CornerDownRight),让 owner 一眼
|
|
4796
|
-
* 分清"派生于人为委派"还是"派生于定时唤起"。非 shift fire session 缺省。
|
|
4797
|
-
* 跟 dispatchedFromSessionId / forkedFromSessionId 三者互斥(同一 session 不会兼任)。
|
|
4798
|
-
* mirror SessionFile 同 dispatchedFromSessionId 处理(shift fire 本地概念,理论上不会
|
|
4799
|
-
* mirror,留 strip 防御)。
|
|
4800
|
-
*/
|
|
4801
|
-
shiftFiredFromSessionId: external_exports.string().min(1).optional(),
|
|
4802
4787
|
createdAt: external_exports.string().min(1),
|
|
4803
4788
|
updatedAt: external_exports.string().min(1)
|
|
4804
4789
|
});
|
|
@@ -11245,7 +11230,6 @@ function classifyUserMessage(input) {
|
|
|
11245
11230
|
if (SYSTEM_REMINDER_RE.test(text) || OPENSPEC_BLOCK_RE.test(text)) {
|
|
11246
11231
|
return { meta: "system-reminder" };
|
|
11247
11232
|
}
|
|
11248
|
-
if (CLAWD_SHIFT_FIRE_RE.test(text)) return { meta: "clawd-shift-fire" };
|
|
11249
11233
|
if (SKILL_HINT_RE.test(text)) return { meta: "skill-hint" };
|
|
11250
11234
|
const flags = raw;
|
|
11251
11235
|
if (flags.isMeta === true || flags.isSynthetic === true) return { meta: "meta-text" };
|
|
@@ -11460,7 +11444,7 @@ function readCurrentContent(filePath) {
|
|
|
11460
11444
|
return null;
|
|
11461
11445
|
}
|
|
11462
11446
|
}
|
|
11463
|
-
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,
|
|
11447
|
+
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;
|
|
11464
11448
|
var init_claude_history = __esm({
|
|
11465
11449
|
"src/tools/claude-history.ts"() {
|
|
11466
11450
|
"use strict";
|
|
@@ -11477,7 +11461,6 @@ var init_claude_history = __esm({
|
|
|
11477
11461
|
SYSTEM_REMINDER_RE = /^\s*<system-reminder\b/i;
|
|
11478
11462
|
OPENSPEC_BLOCK_RE = /^\s*<!--\s*OPENSPEC\b/i;
|
|
11479
11463
|
SKILL_HINT_RE = /^\s*Base directory for this skill:\s/;
|
|
11480
|
-
CLAWD_SHIFT_FIRE_RE = /^\s*\[clawd-shift-fire\b/i;
|
|
11481
11464
|
ATTACHMENT_SILENT_SUBTYPES = /* @__PURE__ */ new Set([
|
|
11482
11465
|
"hook_additional_context",
|
|
11483
11466
|
"hook_success",
|
|
@@ -41278,22 +41261,6 @@ function composeGuestSandbox(base, userWorkDir, spawnCwd) {
|
|
|
41278
41261
|
return s;
|
|
41279
41262
|
}
|
|
41280
41263
|
|
|
41281
|
-
// src/shift/wrap.ts
|
|
41282
|
-
function wrapShiftFirePrompt(args) {
|
|
41283
|
-
const personaLabel = sanitizeMarkerValue(args.targetPersona);
|
|
41284
|
-
return [
|
|
41285
|
-
`[clawd-shift-fire target-persona=${personaLabel} \u2014 scheduled shift fire, not a live user message from the owner]`,
|
|
41286
|
-
"",
|
|
41287
|
-
"Respond as an assistant turn (start your output directly).",
|
|
41288
|
-
"",
|
|
41289
|
-
"Instruction:",
|
|
41290
|
-
args.prompt
|
|
41291
|
-
].join("\n");
|
|
41292
|
-
}
|
|
41293
|
-
function sanitizeMarkerValue(v2) {
|
|
41294
|
-
return v2.replace(/[\]\r\n\t]/g, "_");
|
|
41295
|
-
}
|
|
41296
|
-
|
|
41297
41264
|
// src/session/reducer.ts
|
|
41298
41265
|
init_runtime();
|
|
41299
41266
|
|
|
@@ -41351,6 +41318,29 @@ function buildConnectionPrompt(args) {
|
|
|
41351
41318
|
return result;
|
|
41352
41319
|
}
|
|
41353
41320
|
|
|
41321
|
+
// src/persona/sharing-prompt.ts
|
|
41322
|
+
var ATTACHMENT_SHARING_HINT = `## \u628A\u4EA7\u51FA\u6587\u4EF6\u5206\u4EAB\u7ED9\u5BF9\u65B9\uFF08attachment\uFF09
|
|
41323
|
+
|
|
41324
|
+
\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
|
|
41325
|
+
|
|
41326
|
+
1. \u5165\u7FA4\uFF08\u767B\u8BB0\u5230\u672C session \u7684\u5171\u4EAB\u6E05\u5355\uFF09\uFF1A
|
|
41327
|
+
\`\`\`bash
|
|
41328
|
+
curl -sS -X POST "$CLAWD_DAEMON_URL/api/rpc/attachment.groupAdd" \\
|
|
41329
|
+
-H "X-Clawd-Session-Id: $CLAWD_SESSION_ID" -H 'Content-Type: application/json' \\
|
|
41330
|
+
-d '{"relPath":"<\u6587\u4EF6\u7684\u7EDD\u5BF9\u8DEF\u5F84>"}'
|
|
41331
|
+
\`\`\`
|
|
41332
|
+
2. \u7B7E\u540D\u62FF URL\uFF1A
|
|
41333
|
+
\`\`\`bash
|
|
41334
|
+
curl -sS -X POST "$CLAWD_DAEMON_URL/api/rpc/attachment.signUrl" \\
|
|
41335
|
+
-H "X-Clawd-Session-Id: $CLAWD_SESSION_ID" -H 'Content-Type: application/json' \\
|
|
41336
|
+
-d '{"relPath":"<\u540C\u4E00\u4E2A\u7EDD\u5BF9\u8DEF\u5F84>"}'
|
|
41337
|
+
\`\`\`
|
|
41338
|
+
\u8FD4\u56DE\u7684 \`url\` \u5C31\u662F\u5BF9\u65B9\u53EF\u76F4\u63A5\u6253\u5F00/\u4E0B\u8F7D\u7684\u94FE\u63A5\u3002
|
|
41339
|
+
|
|
41340
|
+
- \`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
|
|
41341
|
+
- \u53EA\u80FD\u5206\u4EAB\u4F60\u81EA\u5DF1\u5DE5\u4F5C\u76EE\u5F55\u5185\u7684\u6587\u4EF6\uFF0C\u8D8A\u754C\u4F1A\u88AB\u62D2\u3002
|
|
41342
|
+
- \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`;
|
|
41343
|
+
|
|
41354
41344
|
// src/dispatch/system-prompt.ts
|
|
41355
41345
|
var DISPATCH_SYSTEM_PROMPT_HINT = `## \u59D4\u6D3E\u7ED9\u53E6\u4E00\u4E2A persona\uFF08dispatch\uFF09
|
|
41356
41346
|
|
|
@@ -41516,6 +41506,9 @@ function buildSpawnContext(state, deps) {
|
|
|
41516
41506
|
if (dispatchMcpConfigPath2) {
|
|
41517
41507
|
ctx.extraSystemPrompt = (ctx.extraSystemPrompt ? ctx.extraSystemPrompt + "\n\n" : "") + DISPATCH_SYSTEM_PROMPT_HINT;
|
|
41518
41508
|
}
|
|
41509
|
+
if (daemonUrl) {
|
|
41510
|
+
ctx.extraSystemPrompt = (ctx.extraSystemPrompt ? ctx.extraSystemPrompt + "\n\n" : "") + ATTACHMENT_SHARING_HINT;
|
|
41511
|
+
}
|
|
41519
41512
|
if (meta?.extraSettings) {
|
|
41520
41513
|
ctx.extraSettings = meta.extraSettings;
|
|
41521
41514
|
}
|
|
@@ -43275,14 +43268,10 @@ var SessionManager = class {
|
|
|
43275
43268
|
// 强制走 deps.store(即 default scope),不走 scopeForFile:mirror 物理落 default 即可。
|
|
43276
43269
|
// 远端 creator / origin 字段原值保留(不被本机 ctx 派生覆写)。
|
|
43277
43270
|
//
|
|
43278
|
-
// strip 远端 personaId / dispatchedFromSessionId
|
|
43279
|
-
//
|
|
43280
|
-
//
|
|
43281
|
-
//
|
|
43282
|
-
// sub-session)走错分支。写入前清掉。#750 一族防线。
|
|
43283
|
-
//
|
|
43284
|
-
// shift fire 是本地 daemon scheduler 行为,理论上不产生 mirror SessionFile,但留 strip
|
|
43285
|
-
// 防御(同 dispatchedFromSessionId 处理姿势对齐)。
|
|
43271
|
+
// strip 远端 personaId / dispatchedFromSessionId:mirror SessionFile 上这两个字段都指向
|
|
43272
|
+
// 朋友 daemon 上的本地 id,本机不持有对应 persona / 源 session。留着会让按这些字段反查的
|
|
43273
|
+
// 本机消费者(MyPersonasSection 按 ownerPersonaId 反查 persona、sidebar 按
|
|
43274
|
+
// dispatchedFromSessionId 折叠 sub-session)走错分支。写入前清掉。#750 一族防线。
|
|
43286
43275
|
//
|
|
43287
43276
|
// 与 manager.create / manager.update 区别:
|
|
43288
43277
|
// - 不走 newRunner / attachObserver / cwd 校验(mirror 是纯 metadata 层)
|
|
@@ -43291,7 +43280,6 @@ var SessionManager = class {
|
|
|
43291
43280
|
const {
|
|
43292
43281
|
ownerPersonaId: _strippedPid,
|
|
43293
43282
|
dispatchedFromSessionId: _strippedDsp,
|
|
43294
|
-
shiftFiredFromSessionId: _strippedSft,
|
|
43295
43283
|
...rest
|
|
43296
43284
|
} = file;
|
|
43297
43285
|
return this.deps.store.write(rest);
|
|
@@ -43994,7 +43982,6 @@ var SessionManager = class {
|
|
|
43994
43982
|
cwd,
|
|
43995
43983
|
tool: "claude",
|
|
43996
43984
|
ownerPersonaId: args.targetPersona,
|
|
43997
|
-
...args.sourceSessionId ? { shiftFiredFromSessionId: args.sourceSessionId } : {},
|
|
43998
43985
|
createdAt: now,
|
|
43999
43986
|
updatedAt: now
|
|
44000
43987
|
};
|
|
@@ -44030,16 +44017,7 @@ var SessionManager = class {
|
|
|
44030
44017
|
});
|
|
44031
44018
|
const runner = this.ensureRunnerForScope(file, scope);
|
|
44032
44019
|
this.deps.logger?.info("shift.createShiftSession.runner-ready", { sessionId });
|
|
44033
|
-
runner.input({
|
|
44034
|
-
kind: "command",
|
|
44035
|
-
command: {
|
|
44036
|
-
kind: "send",
|
|
44037
|
-
text: wrapShiftFirePrompt({
|
|
44038
|
-
prompt: args.prompt,
|
|
44039
|
-
targetPersona: args.targetPersona
|
|
44040
|
-
})
|
|
44041
|
-
}
|
|
44042
|
-
});
|
|
44020
|
+
runner.input({ kind: "command", command: { kind: "send", text: args.prompt } });
|
|
44043
44021
|
const kill = () => {
|
|
44044
44022
|
const r = this.runners.get(sessionId);
|
|
44045
44023
|
if (!r) return;
|
|
@@ -47561,8 +47539,7 @@ function createShiftScheduler(deps) {
|
|
|
47561
47539
|
shiftId: shift.id,
|
|
47562
47540
|
targetPersona: shift.targetPersona,
|
|
47563
47541
|
prompt: shift.prompt,
|
|
47564
|
-
timeoutMs
|
|
47565
|
-
...shift.createdBySessionId ? { sourceSessionId: shift.createdBySessionId } : {}
|
|
47542
|
+
timeoutMs
|
|
47566
47543
|
});
|
|
47567
47544
|
sessionId = spawned.sessionId;
|
|
47568
47545
|
const startRun = {
|
|
@@ -47616,12 +47593,8 @@ function createShiftScheduler(deps) {
|
|
|
47616
47593
|
|
|
47617
47594
|
// src/shift/spawn.ts
|
|
47618
47595
|
function createShiftSpawnFn(deps) {
|
|
47619
|
-
return async ({ targetPersona, prompt, timeoutMs
|
|
47620
|
-
const created = deps.sessionManager.createShiftSession({
|
|
47621
|
-
targetPersona,
|
|
47622
|
-
prompt,
|
|
47623
|
-
...sourceSessionId ? { sourceSessionId } : {}
|
|
47624
|
-
});
|
|
47596
|
+
return async ({ targetPersona, prompt, timeoutMs }) => {
|
|
47597
|
+
const created = deps.sessionManager.createShiftSession({ targetPersona, prompt });
|
|
47625
47598
|
let resolveOuter = () => {
|
|
47626
47599
|
};
|
|
47627
47600
|
const ended = new Promise((r) => {
|
|
@@ -47675,14 +47648,12 @@ function validateSchedule(schedule) {
|
|
|
47675
47648
|
function buildShiftInternalHandlers(deps) {
|
|
47676
47649
|
const add = async (frame) => {
|
|
47677
47650
|
const persona = readPersonaId(frame);
|
|
47678
|
-
const callerSessionId = typeof frame.sessionId === "string" ? frame.sessionId : void 0;
|
|
47679
47651
|
const { type: _t, requestId: _r, personaId: _p, sessionId: _s, ...rest } = frame;
|
|
47680
47652
|
const args = rest;
|
|
47681
47653
|
validateSchedule(args.schedule);
|
|
47682
47654
|
const shift = deps.store.add({
|
|
47683
47655
|
ownerId: deps.ownerIdProvider(),
|
|
47684
47656
|
createdByPersona: persona,
|
|
47685
|
-
...callerSessionId ? { createdBySessionId: callerSessionId } : {},
|
|
47686
47657
|
name: args.name,
|
|
47687
47658
|
enabled: true,
|
|
47688
47659
|
schedule: args.schedule,
|
|
@@ -49250,6 +49221,67 @@ function removeSubscription(client, sessionId) {
|
|
|
49250
49221
|
else client.subscribedSessions.set(sessionId, next);
|
|
49251
49222
|
}
|
|
49252
49223
|
|
|
49224
|
+
// src/transport/rpc-adapter.ts
|
|
49225
|
+
var EXPOSED_RPC_METHODS = [
|
|
49226
|
+
"appBuilder.createProject",
|
|
49227
|
+
"appBuilder.listProjects",
|
|
49228
|
+
"appBuilder.updateProjectPort",
|
|
49229
|
+
"appBuilder.setProdUrl",
|
|
49230
|
+
// 单栏 refactor §5.6.2 stage fix:scaffold + install 完成调 startDevServer 起 dev server;
|
|
49231
|
+
// 中间用 reportStage 上报阶段(scaffolding / installing / failed),UI 据 stage 推帧切 PreviewPane。
|
|
49232
|
+
"appBuilder.startDevServer",
|
|
49233
|
+
"appBuilder.reportStage",
|
|
49234
|
+
// 单栏 §5.6.5 fix: UI 切 session 时调(assistant 不需要主动调,但暴露白名单避免误猜成 forbidden)
|
|
49235
|
+
"appBuilder.setActiveSession",
|
|
49236
|
+
// dataclaw-builder:persona 建页时取建页人身份(含 union_id)写进生成 app 的配置。
|
|
49237
|
+
// 路由到 handlers['auth:getIdentity'],只返 identity、绝不带 ttcToken(见 handlers/feishu-auth.ts)。
|
|
49238
|
+
"auth.getIdentity",
|
|
49239
|
+
// Persona dispatch (Task 4): A 端 dispatch MCP server 调 run(hold-and-wait 一次);
|
|
49240
|
+
// B 端 dispatch MCP server 调 complete(带 outcome 触发 run 端 resolve)。详见
|
|
49241
|
+
// protocol/src/dispatch.ts。
|
|
49242
|
+
"personaDispatch.run",
|
|
49243
|
+
"personaDispatch.complete",
|
|
49244
|
+
// clawd-shift v1 (spec 2026-06-24-clawd-shift):cc 子进程内的 shift MCP server 调
|
|
49245
|
+
// 这 5 个 method 管理 owner 的 shift(fire-and-forget 排班)。鉴权靠 body.personaId
|
|
49246
|
+
// (mcp-server 从 process.env.CLAWD_PERSONA_ID 读,源 = SessionFile.ownerPersonaId)。
|
|
49247
|
+
// handler 内 cancel/update/history 校验 createdByPersona === personaId 防越权。
|
|
49248
|
+
"shift-internal.add",
|
|
49249
|
+
"shift-internal.list",
|
|
49250
|
+
"shift-internal.cancel",
|
|
49251
|
+
"shift-internal.update",
|
|
49252
|
+
"shift-internal.history",
|
|
49253
|
+
// file-sharing:persona guest agent 把自己工作目录内的产物入群+签名分享。
|
|
49254
|
+
// 路径越界由 handlers/attachment.ts guardAttachmentPath(scope-based)拦截。
|
|
49255
|
+
"attachment.groupAdd",
|
|
49256
|
+
"attachment.signUrl"
|
|
49257
|
+
];
|
|
49258
|
+
async function handleRpcRequest(input) {
|
|
49259
|
+
const { method, body, sessionId, dispatcher } = input;
|
|
49260
|
+
if (!sessionId) {
|
|
49261
|
+
return { status: 400, body: { ok: false, error: "MISSING_SESSION_ID", message: "X-Clawd-Session-Id header required" } };
|
|
49262
|
+
}
|
|
49263
|
+
if (!EXPOSED_RPC_METHODS.includes(method)) {
|
|
49264
|
+
return { status: 403, body: { ok: false, error: "METHOD_NOT_EXPOSED", message: `method "${method}" not exposed via HTTP RPC` } };
|
|
49265
|
+
}
|
|
49266
|
+
const internalMethod = method.replace(".", ":");
|
|
49267
|
+
const augmentedBody = {
|
|
49268
|
+
...typeof body === "object" && body != null ? body : {},
|
|
49269
|
+
sessionId
|
|
49270
|
+
};
|
|
49271
|
+
try {
|
|
49272
|
+
const result = await dispatcher.dispatch(internalMethod, augmentedBody, { sessionId });
|
|
49273
|
+
return { status: 200, body: { ok: true, result } };
|
|
49274
|
+
} catch (err) {
|
|
49275
|
+
const e = err;
|
|
49276
|
+
const code = e?.code ?? "INTERNAL";
|
|
49277
|
+
const message = e?.message ?? String(err);
|
|
49278
|
+
return { status: 400, body: { ok: false, error: code, message } };
|
|
49279
|
+
}
|
|
49280
|
+
}
|
|
49281
|
+
function resolveExposedHandler(handlers, internalMethod) {
|
|
49282
|
+
return handlers[internalMethod] ?? handlers[internalMethod.replace(":", ".")];
|
|
49283
|
+
}
|
|
49284
|
+
|
|
49253
49285
|
// src/transport/auth.ts
|
|
49254
49286
|
init_protocol();
|
|
49255
49287
|
var AuthGate = class {
|
|
@@ -50492,60 +50524,6 @@ async function importZip(buf, root) {
|
|
|
50492
50524
|
}
|
|
50493
50525
|
}
|
|
50494
50526
|
|
|
50495
|
-
// src/transport/rpc-adapter.ts
|
|
50496
|
-
var EXPOSED_RPC_METHODS = [
|
|
50497
|
-
"appBuilder.createProject",
|
|
50498
|
-
"appBuilder.listProjects",
|
|
50499
|
-
"appBuilder.updateProjectPort",
|
|
50500
|
-
"appBuilder.setProdUrl",
|
|
50501
|
-
// 单栏 refactor §5.6.2 stage fix:scaffold + install 完成调 startDevServer 起 dev server;
|
|
50502
|
-
// 中间用 reportStage 上报阶段(scaffolding / installing / failed),UI 据 stage 推帧切 PreviewPane。
|
|
50503
|
-
"appBuilder.startDevServer",
|
|
50504
|
-
"appBuilder.reportStage",
|
|
50505
|
-
// 单栏 §5.6.5 fix: UI 切 session 时调(assistant 不需要主动调,但暴露白名单避免误猜成 forbidden)
|
|
50506
|
-
"appBuilder.setActiveSession",
|
|
50507
|
-
// dataclaw-builder:persona 建页时取建页人身份(含 union_id)写进生成 app 的配置。
|
|
50508
|
-
// 路由到 handlers['auth:getIdentity'],只返 identity、绝不带 ttcToken(见 handlers/feishu-auth.ts)。
|
|
50509
|
-
"auth.getIdentity",
|
|
50510
|
-
// Persona dispatch (Task 4): A 端 dispatch MCP server 调 run(hold-and-wait 一次);
|
|
50511
|
-
// B 端 dispatch MCP server 调 complete(带 outcome 触发 run 端 resolve)。详见
|
|
50512
|
-
// protocol/src/dispatch.ts。
|
|
50513
|
-
"personaDispatch.run",
|
|
50514
|
-
"personaDispatch.complete",
|
|
50515
|
-
// clawd-shift v1 (spec 2026-06-24-clawd-shift):cc 子进程内的 shift MCP server 调
|
|
50516
|
-
// 这 5 个 method 管理 owner 的 shift(fire-and-forget 排班)。鉴权靠 body.personaId
|
|
50517
|
-
// (mcp-server 从 process.env.CLAWD_PERSONA_ID 读,源 = SessionFile.ownerPersonaId)。
|
|
50518
|
-
// handler 内 cancel/update/history 校验 createdByPersona === personaId 防越权。
|
|
50519
|
-
"shift-internal.add",
|
|
50520
|
-
"shift-internal.list",
|
|
50521
|
-
"shift-internal.cancel",
|
|
50522
|
-
"shift-internal.update",
|
|
50523
|
-
"shift-internal.history"
|
|
50524
|
-
];
|
|
50525
|
-
async function handleRpcRequest(input) {
|
|
50526
|
-
const { method, body, sessionId, dispatcher } = input;
|
|
50527
|
-
if (!sessionId) {
|
|
50528
|
-
return { status: 400, body: { ok: false, error: "MISSING_SESSION_ID", message: "X-Clawd-Session-Id header required" } };
|
|
50529
|
-
}
|
|
50530
|
-
if (!EXPOSED_RPC_METHODS.includes(method)) {
|
|
50531
|
-
return { status: 403, body: { ok: false, error: "METHOD_NOT_EXPOSED", message: `method "${method}" not exposed via HTTP RPC` } };
|
|
50532
|
-
}
|
|
50533
|
-
const internalMethod = method.replace(".", ":");
|
|
50534
|
-
const augmentedBody = {
|
|
50535
|
-
...typeof body === "object" && body != null ? body : {},
|
|
50536
|
-
sessionId
|
|
50537
|
-
};
|
|
50538
|
-
try {
|
|
50539
|
-
const result = await dispatcher.dispatch(internalMethod, augmentedBody, { sessionId });
|
|
50540
|
-
return { status: 200, body: { ok: true, result } };
|
|
50541
|
-
} catch (err) {
|
|
50542
|
-
const e = err;
|
|
50543
|
-
const code = e?.code ?? "INTERNAL";
|
|
50544
|
-
const message = e?.message ?? String(err);
|
|
50545
|
-
return { status: 400, body: { ok: false, error: code, message } };
|
|
50546
|
-
}
|
|
50547
|
-
}
|
|
50548
|
-
|
|
50549
50527
|
// src/transport/http-router.ts
|
|
50550
50528
|
var RPC_ROUTE_PREFIX = "/api/rpc/";
|
|
50551
50529
|
var RPC_BODY_MAX_BYTES = 1 * 1024 * 1024;
|
|
@@ -52936,6 +52914,16 @@ function personaIdFromPath(absPath, personaRoot) {
|
|
|
52936
52914
|
const id = rel.split(path44.sep)[0];
|
|
52937
52915
|
return id || null;
|
|
52938
52916
|
}
|
|
52917
|
+
function isPathWithin(dir, absPath) {
|
|
52918
|
+
const d = path44.resolve(dir);
|
|
52919
|
+
const t = path44.resolve(absPath);
|
|
52920
|
+
const sep3 = d.endsWith(path44.sep) ? "" : path44.sep;
|
|
52921
|
+
return t === d || t.startsWith(d + sep3);
|
|
52922
|
+
}
|
|
52923
|
+
function isPathInGuestBoundary(personaRoot, personaId, userWorkDir, absPath) {
|
|
52924
|
+
if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
|
|
52925
|
+
return personaIdFromPath(path44.resolve(absPath), personaRoot) === personaId;
|
|
52926
|
+
}
|
|
52939
52927
|
|
|
52940
52928
|
// src/handlers/history.ts
|
|
52941
52929
|
init_protocol();
|
|
@@ -53747,6 +53735,31 @@ function assertGuestAttachmentPath(ctx, absPath, personaRoot, method, usersRoot)
|
|
|
53747
53735
|
}
|
|
53748
53736
|
}
|
|
53749
53737
|
function buildAttachmentHandlers(deps) {
|
|
53738
|
+
function guardAttachmentPath(ctx, sessionId, candidateAbs, method, nonGuestPolicy) {
|
|
53739
|
+
if (ctx?.principal.kind === "owner") return;
|
|
53740
|
+
const scope = deps.getSessionScope?.(sessionId);
|
|
53741
|
+
if (!scope) return;
|
|
53742
|
+
if (scope.kind === "persona" && scope.mode === "guest") {
|
|
53743
|
+
if (!deps.personaRoot) return;
|
|
53744
|
+
const userWorkDir = deps.usersRoot ? deriveUserWorkDir(scope.capId, deps.usersRoot) : void 0;
|
|
53745
|
+
if (!isPathInGuestBoundary(deps.personaRoot, scope.personaId, userWorkDir, candidateAbs)) {
|
|
53746
|
+
throw new ClawdError(
|
|
53747
|
+
ERROR_CODES.UNAUTHORIZED,
|
|
53748
|
+
`${method}: path outside guest work dir: ${candidateAbs}`
|
|
53749
|
+
);
|
|
53750
|
+
}
|
|
53751
|
+
return;
|
|
53752
|
+
}
|
|
53753
|
+
if (nonGuestPolicy === "group-acl") return;
|
|
53754
|
+
const cwd = deps.sessionStore?.read(sessionId)?.cwd;
|
|
53755
|
+
if (!cwd) return;
|
|
53756
|
+
if (!isPathWithin(cwd, candidateAbs)) {
|
|
53757
|
+
throw new ClawdError(
|
|
53758
|
+
ERROR_CODES.UNAUTHORIZED,
|
|
53759
|
+
`${method}: path outside session work dir: ${candidateAbs}`
|
|
53760
|
+
);
|
|
53761
|
+
}
|
|
53762
|
+
}
|
|
53750
53763
|
const signUrl = async (frame, _client, ctx) => {
|
|
53751
53764
|
const parsed = AttachmentSignUrlArgs.safeParse(frame);
|
|
53752
53765
|
if (!parsed.success) {
|
|
@@ -53789,7 +53802,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
53789
53802
|
}
|
|
53790
53803
|
const cwdAbs = import_node_path36.default.resolve(sessionFile.cwd);
|
|
53791
53804
|
const candidateAbs = import_node_path36.default.isAbsolute(args.relPath) ? import_node_path36.default.resolve(args.relPath) : import_node_path36.default.resolve(cwdAbs, args.relPath);
|
|
53792
|
-
|
|
53805
|
+
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
|
|
53793
53806
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
53794
53807
|
const entry = entries.find((e) => {
|
|
53795
53808
|
const storedAbs = import_node_path36.default.isAbsolute(e.relPath) ? import_node_path36.default.resolve(e.relPath) : import_node_path36.default.resolve(cwdAbs, e.relPath);
|
|
@@ -53828,15 +53841,18 @@ function buildAttachmentHandlers(deps) {
|
|
|
53828
53841
|
throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, parsed.error.message);
|
|
53829
53842
|
}
|
|
53830
53843
|
const args = parsed.data;
|
|
53831
|
-
guardGuestBySessionCwd(ctx, args.sessionId, "attachment.groupAdd");
|
|
53832
53844
|
const scope = deps.getSessionScope(args.sessionId);
|
|
53833
53845
|
if (!scope) {
|
|
53834
53846
|
throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
|
|
53835
53847
|
}
|
|
53848
|
+
const cwdAbs = import_node_path36.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
|
|
53849
|
+
const candidateAbs = import_node_path36.default.isAbsolute(args.relPath) ? import_node_path36.default.resolve(args.relPath) : import_node_path36.default.resolve(cwdAbs, args.relPath);
|
|
53850
|
+
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
|
|
53851
|
+
const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
|
|
53836
53852
|
const size = 0;
|
|
53837
53853
|
const entry = deps.groupFileStore.upsert(scope, args.sessionId, {
|
|
53838
53854
|
relPath: args.relPath,
|
|
53839
|
-
from
|
|
53855
|
+
from,
|
|
53840
53856
|
label: args.label,
|
|
53841
53857
|
size,
|
|
53842
53858
|
mime: lookupMime(args.relPath)
|
|
@@ -57454,7 +57470,7 @@ async function startDaemon(config) {
|
|
|
57454
57470
|
});
|
|
57455
57471
|
const rpcDispatcher = {
|
|
57456
57472
|
async dispatch(method, args, _ctx) {
|
|
57457
|
-
const handler = handlers
|
|
57473
|
+
const handler = resolveExposedHandler(handlers, method);
|
|
57458
57474
|
if (!handler) {
|
|
57459
57475
|
throw Object.assign(new Error(`unknown RPC method "${method}"`), { code: "METHOD_NOT_IMPLEMENTED" });
|
|
57460
57476
|
}
|
|
@@ -21251,9 +21251,7 @@ async function main() {
|
|
|
21251
21251
|
inputSchema: {
|
|
21252
21252
|
name: external_exports.string().describe("short label shown in shift.json + future UI"),
|
|
21253
21253
|
schedule: ScheduleSchema,
|
|
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 `[clawd-shift-fire \u2014 ...]` task pack so the spawned cc treats it as a scheduled action and responds as an assistant turn (no user-bubble appears in the owner's UI)."
|
|
21256
|
-
),
|
|
21254
|
+
prompt: external_exports.string().describe("first user message injected when the new cc starts"),
|
|
21257
21255
|
targetPersona: external_exports.string().optional().describe("which persona to spawn; defaults to the calling persona"),
|
|
21258
21256
|
timeoutMs: external_exports.number().optional().describe("hard timeout per fire; default 30 minutes")
|
|
21259
21257
|
}
|
package/package.json
CHANGED