@clawos-dev/clawd 0.2.257 → 0.2.258
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 +757 -771
- package/dist/dispatch/mcp-server.cjs +36 -17
- package/dist/inbox/mcp-server.cjs +9 -7
- package/dist/peer-ops/mcp-server.cjs +7 -5
- package/dist/persona-defaults/persona-app-builder/CLAUDE.md +36 -56
- package/dist/persona-defaults/persona-clawd-butler/.claude/skills/clawd-orchestration/SKILL.md +2 -2
- package/dist/persona-defaults/persona-clawd-butler/CLAUDE.md +2 -2
- package/dist/persona-defaults/persona-dataclaw-builder/CLAUDE.md +9 -20
- package/dist/rpc-tool/mcp-server.cjs +21209 -0
- package/dist/share-ui/assets/{guest-D7iXbNdh.js → guest-SFe_5tKr.js} +65 -65
- package/dist/share-ui/guest.html +1 -1
- package/dist/shift/mcp-server.cjs +15 -16
- package/dist/ticket/mcp-server.cjs +10 -5
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -46,7 +46,7 @@ var init_methods = __esm({
|
|
|
46
46
|
"session:delete",
|
|
47
47
|
"session:send",
|
|
48
48
|
// per-turn sender 2026-07-08 spec §4.6:MCP servers (ticket / inbox ...) 走 HTTP RPC
|
|
49
|
-
// /
|
|
49
|
+
// /rpc/session:currentTurnSender(Bearer)拉当前 turn caller;WS handler 同名
|
|
50
50
|
"session:currentTurnSender",
|
|
51
51
|
"session:stop",
|
|
52
52
|
// 温柔中断当前轮次,不杀进程(走 CC IPC control_request.subtype=interrupt)
|
|
@@ -94,6 +94,14 @@ var init_methods = __esm({
|
|
|
94
94
|
"workspace:list",
|
|
95
95
|
"workspace:read",
|
|
96
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",
|
|
97
105
|
"skills:list",
|
|
98
106
|
"agents:list",
|
|
99
107
|
"git:root",
|
|
@@ -111,14 +119,17 @@ var init_methods = __esm({
|
|
|
111
119
|
"persona:get",
|
|
112
120
|
"persona:update",
|
|
113
121
|
"persona:delete",
|
|
114
|
-
// ---- personaDispatch:* (
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
-
//
|
|
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 校验行归属。
|
|
119
127
|
"personaDispatch:run",
|
|
128
|
+
"personaDispatch:complete",
|
|
129
|
+
"personaDispatch:list",
|
|
130
|
+
"personaDispatch:get",
|
|
120
131
|
// ---- peerExec:run (跨设备 exec,spec 2026-07-16-cross-device-exec-mcp) ----
|
|
121
|
-
// A
|
|
132
|
+
// A 侧本机 MCP server 经 /rpc(Bearer owner token)带 peerDeviceId → daemon forward;
|
|
122
133
|
// B 侧对端经 /rpc(Bearer connect token)无 peerDeviceId → 本地 spawn。METHOD_GRANT_MAP
|
|
123
134
|
// 设 public,handler 内判 remoteAccessAllowed(跟 inbox:postMessage 同款)。
|
|
124
135
|
"peerExec:run",
|
|
@@ -155,6 +166,9 @@ var init_methods = __esm({
|
|
|
155
166
|
"inbox:list",
|
|
156
167
|
"inbox:markRead",
|
|
157
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",
|
|
158
172
|
// ---- contact:* (联系人列表) ----
|
|
159
173
|
// 飞书统一身份 Phase 3 (决策 #14): received-capability:* 正名为 contact:*——Phase 2 后
|
|
160
174
|
// 联系人身份由中心 server introspect 背书,不再是"对方颁发的 capability"。
|
|
@@ -319,7 +333,10 @@ var init_errors = __esm({
|
|
|
319
333
|
WRONG_PERSONA: "WRONG_PERSONA",
|
|
320
334
|
SESSION_ALREADY_BOUND: "SESSION_ALREADY_BOUND",
|
|
321
335
|
// 2026-07-02 unified people sidebar: contact:pin 时 deviceId 不在 store(可能被别处删了)
|
|
322
|
-
CONTACT_NOT_FOUND: "CONTACT_NOT_FOUND"
|
|
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"
|
|
323
340
|
};
|
|
324
341
|
ClawdError = class extends Error {
|
|
325
342
|
constructor(code, message) {
|
|
@@ -749,8 +766,8 @@ var init_parseUtil = __esm({
|
|
|
749
766
|
init_errors2();
|
|
750
767
|
init_en();
|
|
751
768
|
makeIssue = (params) => {
|
|
752
|
-
const { data, path:
|
|
753
|
-
const fullPath = [...
|
|
769
|
+
const { data, path: path75, errorMaps, issueData } = params;
|
|
770
|
+
const fullPath = [...path75, ...issueData.path || []];
|
|
754
771
|
const fullIssue = {
|
|
755
772
|
...issueData,
|
|
756
773
|
path: fullPath
|
|
@@ -1061,11 +1078,11 @@ var init_types = __esm({
|
|
|
1061
1078
|
init_parseUtil();
|
|
1062
1079
|
init_util();
|
|
1063
1080
|
ParseInputLazyPath = class {
|
|
1064
|
-
constructor(parent, value,
|
|
1081
|
+
constructor(parent, value, path75, key) {
|
|
1065
1082
|
this._cachedPath = [];
|
|
1066
1083
|
this.parent = parent;
|
|
1067
1084
|
this.data = value;
|
|
1068
|
-
this._path =
|
|
1085
|
+
this._path = path75;
|
|
1069
1086
|
this._key = key;
|
|
1070
1087
|
}
|
|
1071
1088
|
get path() {
|
|
@@ -4496,7 +4513,12 @@ var init_attachment_schemas = __esm({
|
|
|
4496
4513
|
sessionId: external_exports.string().min(1),
|
|
4497
4514
|
relPath: external_exports.string().min(1),
|
|
4498
4515
|
/** owner 手动加入时可选备注;agent 自动入清单不走此 RPC */
|
|
4499
|
-
label: external_exports.string().optional()
|
|
4516
|
+
label: external_exports.string().optional(),
|
|
4517
|
+
/**
|
|
4518
|
+
* 统一 RPC 鉴权(spec 2026-07-28 §8 决策 B):agent 经 clawd-rpc 调用持 owner token,
|
|
4519
|
+
* ctx 已推不出"这是 agent";body 显式标 origin:'agent' 保留群清单 from 的展示语义。
|
|
4520
|
+
*/
|
|
4521
|
+
origin: external_exports.literal("agent").optional()
|
|
4500
4522
|
});
|
|
4501
4523
|
AttachmentGroupAddResponseSchema = external_exports.object({
|
|
4502
4524
|
entry: GroupFileEntrySchema
|
|
@@ -6207,7 +6229,7 @@ var init_feishu_auth = __esm({
|
|
|
6207
6229
|
});
|
|
6208
6230
|
|
|
6209
6231
|
// ../protocol/src/dispatch.ts
|
|
6210
|
-
var DispatchOutcomeSchema, DispatchRunArgsSchema, DispatchCompleteArgsSchema, DISPATCH_STATUSES, DispatchStatusSchema, DispatchRecordSchema, DispatchListArgsSchema, DispatchListResultSchema;
|
|
6232
|
+
var DispatchOutcomeSchema, DispatchRunArgsSchema, DispatchCompleteArgsSchema, DISPATCH_STATUSES, DispatchStatusSchema, DispatchRecordSchema, DispatchListArgsSchema, DispatchListResultSchema, DispatchGetArgsSchema, DispatchGetOkSchema;
|
|
6211
6233
|
var init_dispatch = __esm({
|
|
6212
6234
|
"../protocol/src/dispatch.ts"() {
|
|
6213
6235
|
"use strict";
|
|
@@ -6280,6 +6302,14 @@ var init_dispatch = __esm({
|
|
|
6280
6302
|
DispatchListResultSchema = external_exports.object({
|
|
6281
6303
|
records: external_exports.array(DispatchRecordSchema)
|
|
6282
6304
|
});
|
|
6305
|
+
DispatchGetArgsSchema = external_exports.object({
|
|
6306
|
+
dispatchId: external_exports.string().min(1)
|
|
6307
|
+
});
|
|
6308
|
+
DispatchGetOkSchema = external_exports.object({
|
|
6309
|
+
type: external_exports.literal("personaDispatch:get:ok"),
|
|
6310
|
+
status: DispatchStatusSchema,
|
|
6311
|
+
outcome: DispatchOutcomeSchema.optional()
|
|
6312
|
+
});
|
|
6283
6313
|
}
|
|
6284
6314
|
});
|
|
6285
6315
|
|
|
@@ -6633,8 +6663,8 @@ var require_req = __commonJS({
|
|
|
6633
6663
|
if (req.originalUrl) {
|
|
6634
6664
|
_req.url = req.originalUrl;
|
|
6635
6665
|
} else {
|
|
6636
|
-
const
|
|
6637
|
-
_req.url = typeof
|
|
6666
|
+
const path75 = req.path;
|
|
6667
|
+
_req.url = typeof path75 === "string" ? path75 : req.url ? req.url.path || req.url : void 0;
|
|
6638
6668
|
}
|
|
6639
6669
|
if (req.query) {
|
|
6640
6670
|
_req.query = req.query;
|
|
@@ -6799,14 +6829,14 @@ var require_redact = __commonJS({
|
|
|
6799
6829
|
}
|
|
6800
6830
|
return obj;
|
|
6801
6831
|
}
|
|
6802
|
-
function parsePath(
|
|
6832
|
+
function parsePath(path75) {
|
|
6803
6833
|
const parts = [];
|
|
6804
6834
|
let current = "";
|
|
6805
6835
|
let inBrackets = false;
|
|
6806
6836
|
let inQuotes = false;
|
|
6807
6837
|
let quoteChar = "";
|
|
6808
|
-
for (let i = 0; i <
|
|
6809
|
-
const char =
|
|
6838
|
+
for (let i = 0; i < path75.length; i++) {
|
|
6839
|
+
const char = path75[i];
|
|
6810
6840
|
if (!inBrackets && char === ".") {
|
|
6811
6841
|
if (current) {
|
|
6812
6842
|
parts.push(current);
|
|
@@ -6937,10 +6967,10 @@ var require_redact = __commonJS({
|
|
|
6937
6967
|
return current;
|
|
6938
6968
|
}
|
|
6939
6969
|
function redactPaths(obj, paths, censor, remove = false) {
|
|
6940
|
-
for (const
|
|
6941
|
-
const parts = parsePath(
|
|
6970
|
+
for (const path75 of paths) {
|
|
6971
|
+
const parts = parsePath(path75);
|
|
6942
6972
|
if (parts.includes("*")) {
|
|
6943
|
-
redactWildcardPath(obj, parts, censor,
|
|
6973
|
+
redactWildcardPath(obj, parts, censor, path75, remove);
|
|
6944
6974
|
} else {
|
|
6945
6975
|
if (remove) {
|
|
6946
6976
|
removeKey(obj, parts);
|
|
@@ -7025,8 +7055,8 @@ var require_redact = __commonJS({
|
|
|
7025
7055
|
}
|
|
7026
7056
|
} else {
|
|
7027
7057
|
if (afterWildcard.includes("*")) {
|
|
7028
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
7029
|
-
const fullPath = [...pathArray.slice(0, pathLength), ...
|
|
7058
|
+
const wrappedCensor = typeof censor === "function" ? (value, path75) => {
|
|
7059
|
+
const fullPath = [...pathArray.slice(0, pathLength), ...path75];
|
|
7030
7060
|
return censor(value, fullPath);
|
|
7031
7061
|
} : censor;
|
|
7032
7062
|
redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
|
|
@@ -7061,8 +7091,8 @@ var require_redact = __commonJS({
|
|
|
7061
7091
|
return null;
|
|
7062
7092
|
}
|
|
7063
7093
|
const pathStructure = /* @__PURE__ */ new Map();
|
|
7064
|
-
for (const
|
|
7065
|
-
const parts = parsePath(
|
|
7094
|
+
for (const path75 of pathsToClone) {
|
|
7095
|
+
const parts = parsePath(path75);
|
|
7066
7096
|
let current = pathStructure;
|
|
7067
7097
|
for (let i = 0; i < parts.length; i++) {
|
|
7068
7098
|
const part = parts[i];
|
|
@@ -7114,24 +7144,24 @@ var require_redact = __commonJS({
|
|
|
7114
7144
|
}
|
|
7115
7145
|
return cloneSelectively(obj, pathStructure);
|
|
7116
7146
|
}
|
|
7117
|
-
function validatePath(
|
|
7118
|
-
if (typeof
|
|
7147
|
+
function validatePath(path75) {
|
|
7148
|
+
if (typeof path75 !== "string") {
|
|
7119
7149
|
throw new Error("Paths must be (non-empty) strings");
|
|
7120
7150
|
}
|
|
7121
|
-
if (
|
|
7151
|
+
if (path75 === "") {
|
|
7122
7152
|
throw new Error("Invalid redaction path ()");
|
|
7123
7153
|
}
|
|
7124
|
-
if (
|
|
7125
|
-
throw new Error(`Invalid redaction path (${
|
|
7154
|
+
if (path75.includes("..")) {
|
|
7155
|
+
throw new Error(`Invalid redaction path (${path75})`);
|
|
7126
7156
|
}
|
|
7127
|
-
if (
|
|
7128
|
-
throw new Error(`Invalid redaction path (${
|
|
7157
|
+
if (path75.includes(",")) {
|
|
7158
|
+
throw new Error(`Invalid redaction path (${path75})`);
|
|
7129
7159
|
}
|
|
7130
7160
|
let bracketCount = 0;
|
|
7131
7161
|
let inQuotes = false;
|
|
7132
7162
|
let quoteChar = "";
|
|
7133
|
-
for (let i = 0; i <
|
|
7134
|
-
const char =
|
|
7163
|
+
for (let i = 0; i < path75.length; i++) {
|
|
7164
|
+
const char = path75[i];
|
|
7135
7165
|
if ((char === '"' || char === "'") && bracketCount > 0) {
|
|
7136
7166
|
if (!inQuotes) {
|
|
7137
7167
|
inQuotes = true;
|
|
@@ -7145,20 +7175,20 @@ var require_redact = __commonJS({
|
|
|
7145
7175
|
} else if (char === "]" && !inQuotes) {
|
|
7146
7176
|
bracketCount--;
|
|
7147
7177
|
if (bracketCount < 0) {
|
|
7148
|
-
throw new Error(`Invalid redaction path (${
|
|
7178
|
+
throw new Error(`Invalid redaction path (${path75})`);
|
|
7149
7179
|
}
|
|
7150
7180
|
}
|
|
7151
7181
|
}
|
|
7152
7182
|
if (bracketCount !== 0) {
|
|
7153
|
-
throw new Error(`Invalid redaction path (${
|
|
7183
|
+
throw new Error(`Invalid redaction path (${path75})`);
|
|
7154
7184
|
}
|
|
7155
7185
|
}
|
|
7156
7186
|
function validatePaths(paths) {
|
|
7157
7187
|
if (!Array.isArray(paths)) {
|
|
7158
7188
|
throw new TypeError("paths must be an array");
|
|
7159
7189
|
}
|
|
7160
|
-
for (const
|
|
7161
|
-
validatePath(
|
|
7190
|
+
for (const path75 of paths) {
|
|
7191
|
+
validatePath(path75);
|
|
7162
7192
|
}
|
|
7163
7193
|
}
|
|
7164
7194
|
function slowRedact(options = {}) {
|
|
@@ -7326,8 +7356,8 @@ var require_redaction = __commonJS({
|
|
|
7326
7356
|
if (shape[k2] === null) {
|
|
7327
7357
|
o[k2] = (value) => topCensor(value, [k2]);
|
|
7328
7358
|
} else {
|
|
7329
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
7330
|
-
return censor(value, [k2, ...
|
|
7359
|
+
const wrappedCensor = typeof censor === "function" ? (value, path75) => {
|
|
7360
|
+
return censor(value, [k2, ...path75]);
|
|
7331
7361
|
} : censor;
|
|
7332
7362
|
o[k2] = Redact({
|
|
7333
7363
|
paths: shape[k2],
|
|
@@ -7545,10 +7575,10 @@ var require_atomic_sleep = __commonJS({
|
|
|
7545
7575
|
var require_sonic_boom = __commonJS({
|
|
7546
7576
|
"../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
|
|
7547
7577
|
"use strict";
|
|
7548
|
-
var
|
|
7578
|
+
var fs66 = require("fs");
|
|
7549
7579
|
var EventEmitter3 = require("events");
|
|
7550
7580
|
var inherits = require("util").inherits;
|
|
7551
|
-
var
|
|
7581
|
+
var path75 = require("path");
|
|
7552
7582
|
var sleep2 = require_atomic_sleep();
|
|
7553
7583
|
var assert = require("assert");
|
|
7554
7584
|
var BUSY_WRITE_TIMEOUT = 100;
|
|
@@ -7602,20 +7632,20 @@ var require_sonic_boom = __commonJS({
|
|
|
7602
7632
|
const mode = sonic.mode;
|
|
7603
7633
|
if (sonic.sync) {
|
|
7604
7634
|
try {
|
|
7605
|
-
if (sonic.mkdir)
|
|
7606
|
-
const fd =
|
|
7635
|
+
if (sonic.mkdir) fs66.mkdirSync(path75.dirname(file), { recursive: true });
|
|
7636
|
+
const fd = fs66.openSync(file, flags, mode);
|
|
7607
7637
|
fileOpened(null, fd);
|
|
7608
7638
|
} catch (err) {
|
|
7609
7639
|
fileOpened(err);
|
|
7610
7640
|
throw err;
|
|
7611
7641
|
}
|
|
7612
7642
|
} else if (sonic.mkdir) {
|
|
7613
|
-
|
|
7643
|
+
fs66.mkdir(path75.dirname(file), { recursive: true }, (err) => {
|
|
7614
7644
|
if (err) return fileOpened(err);
|
|
7615
|
-
|
|
7645
|
+
fs66.open(file, flags, mode, fileOpened);
|
|
7616
7646
|
});
|
|
7617
7647
|
} else {
|
|
7618
|
-
|
|
7648
|
+
fs66.open(file, flags, mode, fileOpened);
|
|
7619
7649
|
}
|
|
7620
7650
|
}
|
|
7621
7651
|
function SonicBoom(opts) {
|
|
@@ -7656,8 +7686,8 @@ var require_sonic_boom = __commonJS({
|
|
|
7656
7686
|
this.flush = flushBuffer;
|
|
7657
7687
|
this.flushSync = flushBufferSync;
|
|
7658
7688
|
this._actualWrite = actualWriteBuffer;
|
|
7659
|
-
fsWriteSync = () =>
|
|
7660
|
-
fsWrite = () =>
|
|
7689
|
+
fsWriteSync = () => fs66.writeSync(this.fd, this._writingBuf);
|
|
7690
|
+
fsWrite = () => fs66.write(this.fd, this._writingBuf, this.release);
|
|
7661
7691
|
} else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
|
|
7662
7692
|
this._writingBuf = "";
|
|
7663
7693
|
this.write = write;
|
|
@@ -7666,15 +7696,15 @@ var require_sonic_boom = __commonJS({
|
|
|
7666
7696
|
this._actualWrite = actualWrite;
|
|
7667
7697
|
fsWriteSync = () => {
|
|
7668
7698
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7669
|
-
return
|
|
7699
|
+
return fs66.writeSync(this.fd, this._writingBuf);
|
|
7670
7700
|
}
|
|
7671
|
-
return
|
|
7701
|
+
return fs66.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7672
7702
|
};
|
|
7673
7703
|
fsWrite = () => {
|
|
7674
7704
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
7675
|
-
return
|
|
7705
|
+
return fs66.write(this.fd, this._writingBuf, this.release);
|
|
7676
7706
|
}
|
|
7677
|
-
return
|
|
7707
|
+
return fs66.write(this.fd, this._writingBuf, "utf8", this.release);
|
|
7678
7708
|
};
|
|
7679
7709
|
} else {
|
|
7680
7710
|
throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
|
|
@@ -7731,7 +7761,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7731
7761
|
}
|
|
7732
7762
|
}
|
|
7733
7763
|
if (this._fsync) {
|
|
7734
|
-
|
|
7764
|
+
fs66.fsyncSync(this.fd);
|
|
7735
7765
|
}
|
|
7736
7766
|
const len = this._len;
|
|
7737
7767
|
if (this._reopening) {
|
|
@@ -7845,7 +7875,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7845
7875
|
const onDrain = () => {
|
|
7846
7876
|
if (!this._fsync) {
|
|
7847
7877
|
try {
|
|
7848
|
-
|
|
7878
|
+
fs66.fsync(this.fd, (err) => {
|
|
7849
7879
|
this._flushPending = false;
|
|
7850
7880
|
cb(err);
|
|
7851
7881
|
});
|
|
@@ -7947,7 +7977,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7947
7977
|
const fd = this.fd;
|
|
7948
7978
|
this.once("ready", () => {
|
|
7949
7979
|
if (fd !== this.fd) {
|
|
7950
|
-
|
|
7980
|
+
fs66.close(fd, (err) => {
|
|
7951
7981
|
if (err) {
|
|
7952
7982
|
return this.emit("error", err);
|
|
7953
7983
|
}
|
|
@@ -7996,7 +8026,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7996
8026
|
buf = this._bufs[0];
|
|
7997
8027
|
}
|
|
7998
8028
|
try {
|
|
7999
|
-
const n = Buffer.isBuffer(buf) ?
|
|
8029
|
+
const n = Buffer.isBuffer(buf) ? fs66.writeSync(this.fd, buf) : fs66.writeSync(this.fd, buf, "utf8");
|
|
8000
8030
|
const releasedBufObj = releaseWritingBuf(buf, this._len, n);
|
|
8001
8031
|
buf = releasedBufObj.writingBuf;
|
|
8002
8032
|
this._len = releasedBufObj.len;
|
|
@@ -8012,7 +8042,7 @@ var require_sonic_boom = __commonJS({
|
|
|
8012
8042
|
}
|
|
8013
8043
|
}
|
|
8014
8044
|
try {
|
|
8015
|
-
|
|
8045
|
+
fs66.fsyncSync(this.fd);
|
|
8016
8046
|
} catch {
|
|
8017
8047
|
}
|
|
8018
8048
|
}
|
|
@@ -8033,7 +8063,7 @@ var require_sonic_boom = __commonJS({
|
|
|
8033
8063
|
buf = mergeBuf(this._bufs[0], this._lens[0]);
|
|
8034
8064
|
}
|
|
8035
8065
|
try {
|
|
8036
|
-
const n =
|
|
8066
|
+
const n = fs66.writeSync(this.fd, buf);
|
|
8037
8067
|
buf = buf.subarray(n);
|
|
8038
8068
|
this._len = Math.max(this._len - n, 0);
|
|
8039
8069
|
if (buf.length <= 0) {
|
|
@@ -8061,13 +8091,13 @@ var require_sonic_boom = __commonJS({
|
|
|
8061
8091
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
|
|
8062
8092
|
if (this.sync) {
|
|
8063
8093
|
try {
|
|
8064
|
-
const written = Buffer.isBuffer(this._writingBuf) ?
|
|
8094
|
+
const written = Buffer.isBuffer(this._writingBuf) ? fs66.writeSync(this.fd, this._writingBuf) : fs66.writeSync(this.fd, this._writingBuf, "utf8");
|
|
8065
8095
|
release(null, written);
|
|
8066
8096
|
} catch (err) {
|
|
8067
8097
|
release(err);
|
|
8068
8098
|
}
|
|
8069
8099
|
} else {
|
|
8070
|
-
|
|
8100
|
+
fs66.write(this.fd, this._writingBuf, release);
|
|
8071
8101
|
}
|
|
8072
8102
|
}
|
|
8073
8103
|
function actualWriteBuffer() {
|
|
@@ -8076,7 +8106,7 @@ var require_sonic_boom = __commonJS({
|
|
|
8076
8106
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
|
|
8077
8107
|
if (this.sync) {
|
|
8078
8108
|
try {
|
|
8079
|
-
const written =
|
|
8109
|
+
const written = fs66.writeSync(this.fd, this._writingBuf);
|
|
8080
8110
|
release(null, written);
|
|
8081
8111
|
} catch (err) {
|
|
8082
8112
|
release(err);
|
|
@@ -8085,7 +8115,7 @@ var require_sonic_boom = __commonJS({
|
|
|
8085
8115
|
if (kCopyBuffer) {
|
|
8086
8116
|
this._writingBuf = Buffer.from(this._writingBuf);
|
|
8087
8117
|
}
|
|
8088
|
-
|
|
8118
|
+
fs66.write(this.fd, this._writingBuf, release);
|
|
8089
8119
|
}
|
|
8090
8120
|
}
|
|
8091
8121
|
function actualClose(sonic) {
|
|
@@ -8101,12 +8131,12 @@ var require_sonic_boom = __commonJS({
|
|
|
8101
8131
|
sonic._lens = [];
|
|
8102
8132
|
assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
|
|
8103
8133
|
try {
|
|
8104
|
-
|
|
8134
|
+
fs66.fsync(sonic.fd, closeWrapped);
|
|
8105
8135
|
} catch {
|
|
8106
8136
|
}
|
|
8107
8137
|
function closeWrapped() {
|
|
8108
8138
|
if (sonic.fd !== 1 && sonic.fd !== 2) {
|
|
8109
|
-
|
|
8139
|
+
fs66.close(sonic.fd, done);
|
|
8110
8140
|
} else {
|
|
8111
8141
|
done();
|
|
8112
8142
|
}
|
|
@@ -11241,11 +11271,11 @@ var init_lib = __esm({
|
|
|
11241
11271
|
}
|
|
11242
11272
|
}
|
|
11243
11273
|
},
|
|
11244
|
-
addToPath: function addToPath(
|
|
11245
|
-
var last =
|
|
11274
|
+
addToPath: function addToPath(path75, added, removed, oldPosInc, options) {
|
|
11275
|
+
var last = path75.lastComponent;
|
|
11246
11276
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
11247
11277
|
return {
|
|
11248
|
-
oldPos:
|
|
11278
|
+
oldPos: path75.oldPos + oldPosInc,
|
|
11249
11279
|
lastComponent: {
|
|
11250
11280
|
count: last.count + 1,
|
|
11251
11281
|
added,
|
|
@@ -11255,7 +11285,7 @@ var init_lib = __esm({
|
|
|
11255
11285
|
};
|
|
11256
11286
|
} else {
|
|
11257
11287
|
return {
|
|
11258
|
-
oldPos:
|
|
11288
|
+
oldPos: path75.oldPos + oldPosInc,
|
|
11259
11289
|
lastComponent: {
|
|
11260
11290
|
count: 1,
|
|
11261
11291
|
added,
|
|
@@ -11764,10 +11794,10 @@ function attachmentToHistoryMessage(o, ts) {
|
|
|
11764
11794
|
const memories = raw.map((m2) => {
|
|
11765
11795
|
if (!m2 || typeof m2 !== "object") return null;
|
|
11766
11796
|
const rec3 = m2;
|
|
11767
|
-
const
|
|
11797
|
+
const path75 = typeof rec3.path === "string" ? rec3.path : null;
|
|
11768
11798
|
const content = typeof rec3.content === "string" ? rec3.content : null;
|
|
11769
|
-
if (!
|
|
11770
|
-
const entry = { path:
|
|
11799
|
+
if (!path75 || content == null) return null;
|
|
11800
|
+
const entry = { path: path75, content };
|
|
11771
11801
|
if (typeof rec3.mtimeMs === "number") entry.mtimeMs = rec3.mtimeMs;
|
|
11772
11802
|
return entry;
|
|
11773
11803
|
}).filter((m2) => m2 !== null);
|
|
@@ -12570,10 +12600,10 @@ function parseAttachment(obj) {
|
|
|
12570
12600
|
const memories = raw.map((m2) => {
|
|
12571
12601
|
if (!m2 || typeof m2 !== "object") return null;
|
|
12572
12602
|
const rec3 = m2;
|
|
12573
|
-
const
|
|
12603
|
+
const path75 = typeof rec3.path === "string" ? rec3.path : null;
|
|
12574
12604
|
const content = typeof rec3.content === "string" ? rec3.content : null;
|
|
12575
|
-
if (!
|
|
12576
|
-
const out = { path:
|
|
12605
|
+
if (!path75 || content == null) return null;
|
|
12606
|
+
const out = { path: path75, content };
|
|
12577
12607
|
if (typeof rec3.mtimeMs === "number") out.mtimeMs = rec3.mtimeMs;
|
|
12578
12608
|
return out;
|
|
12579
12609
|
}).filter((m2) => m2 !== null);
|
|
@@ -33542,8 +33572,8 @@ var require_utils = __commonJS({
|
|
|
33542
33572
|
var result = transform[inputType][outputType](input);
|
|
33543
33573
|
return result;
|
|
33544
33574
|
};
|
|
33545
|
-
exports2.resolve = function(
|
|
33546
|
-
var parts =
|
|
33575
|
+
exports2.resolve = function(path75) {
|
|
33576
|
+
var parts = path75.split("/");
|
|
33547
33577
|
var result = [];
|
|
33548
33578
|
for (var index = 0; index < parts.length; index++) {
|
|
33549
33579
|
var part = parts[index];
|
|
@@ -39396,18 +39426,18 @@ var require_object = __commonJS({
|
|
|
39396
39426
|
var object = new ZipObject(name, zipObjectContent, o);
|
|
39397
39427
|
this.files[name] = object;
|
|
39398
39428
|
};
|
|
39399
|
-
var parentFolder = function(
|
|
39400
|
-
if (
|
|
39401
|
-
|
|
39429
|
+
var parentFolder = function(path75) {
|
|
39430
|
+
if (path75.slice(-1) === "/") {
|
|
39431
|
+
path75 = path75.substring(0, path75.length - 1);
|
|
39402
39432
|
}
|
|
39403
|
-
var lastSlash =
|
|
39404
|
-
return lastSlash > 0 ?
|
|
39433
|
+
var lastSlash = path75.lastIndexOf("/");
|
|
39434
|
+
return lastSlash > 0 ? path75.substring(0, lastSlash) : "";
|
|
39405
39435
|
};
|
|
39406
|
-
var forceTrailingSlash = function(
|
|
39407
|
-
if (
|
|
39408
|
-
|
|
39436
|
+
var forceTrailingSlash = function(path75) {
|
|
39437
|
+
if (path75.slice(-1) !== "/") {
|
|
39438
|
+
path75 += "/";
|
|
39409
39439
|
}
|
|
39410
|
-
return
|
|
39440
|
+
return path75;
|
|
39411
39441
|
};
|
|
39412
39442
|
var folderAdd = function(name, createFolders) {
|
|
39413
39443
|
createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
|
|
@@ -40409,7 +40439,7 @@ var require_lib3 = __commonJS({
|
|
|
40409
40439
|
// src/run-case/recorder.ts
|
|
40410
40440
|
function startRunCaseRecorder(opts) {
|
|
40411
40441
|
const now = opts.now ?? Date.now;
|
|
40412
|
-
const dir =
|
|
40442
|
+
const dir = import_node_path63.default.dirname(opts.recordPath);
|
|
40413
40443
|
let stream = null;
|
|
40414
40444
|
let closing = false;
|
|
40415
40445
|
let closedSettled = false;
|
|
@@ -40423,8 +40453,8 @@ function startRunCaseRecorder(opts) {
|
|
|
40423
40453
|
});
|
|
40424
40454
|
const ensureStream = () => {
|
|
40425
40455
|
if (stream) return stream;
|
|
40426
|
-
|
|
40427
|
-
stream =
|
|
40456
|
+
import_node_fs48.default.mkdirSync(dir, { recursive: true });
|
|
40457
|
+
stream = import_node_fs48.default.createWriteStream(opts.recordPath, { flags: "a" });
|
|
40428
40458
|
stream.on("close", () => closedResolve());
|
|
40429
40459
|
return stream;
|
|
40430
40460
|
};
|
|
@@ -40449,12 +40479,12 @@ function startRunCaseRecorder(opts) {
|
|
|
40449
40479
|
};
|
|
40450
40480
|
return { tap, close, closed };
|
|
40451
40481
|
}
|
|
40452
|
-
var
|
|
40482
|
+
var import_node_fs48, import_node_path63;
|
|
40453
40483
|
var init_recorder = __esm({
|
|
40454
40484
|
"src/run-case/recorder.ts"() {
|
|
40455
40485
|
"use strict";
|
|
40456
|
-
|
|
40457
|
-
|
|
40486
|
+
import_node_fs48 = __toESM(require("fs"), 1);
|
|
40487
|
+
import_node_path63 = __toESM(require("path"), 1);
|
|
40458
40488
|
}
|
|
40459
40489
|
});
|
|
40460
40490
|
|
|
@@ -40497,7 +40527,7 @@ var init_wire = __esm({
|
|
|
40497
40527
|
// src/run-case/controller.ts
|
|
40498
40528
|
async function runController(opts) {
|
|
40499
40529
|
const now = opts.now ?? Date.now;
|
|
40500
|
-
const cwd = opts.cwd ?? (0,
|
|
40530
|
+
const cwd = opts.cwd ?? (0, import_node_fs49.mkdtempSync)(import_node_path64.default.join(import_node_os23.default.tmpdir(), "clawd-runcase-"));
|
|
40501
40531
|
const ownsCwd = opts.cwd === void 0;
|
|
40502
40532
|
const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
|
|
40503
40533
|
const spawnCtx = { cwd };
|
|
@@ -40658,19 +40688,19 @@ async function runController(opts) {
|
|
|
40658
40688
|
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
40659
40689
|
if (ownsCwd) {
|
|
40660
40690
|
try {
|
|
40661
|
-
(0,
|
|
40691
|
+
(0, import_node_fs49.rmSync)(cwd, { recursive: true, force: true });
|
|
40662
40692
|
} catch {
|
|
40663
40693
|
}
|
|
40664
40694
|
}
|
|
40665
40695
|
return exitCode ?? 0;
|
|
40666
40696
|
}
|
|
40667
|
-
var
|
|
40697
|
+
var import_node_fs49, import_node_os23, import_node_path64;
|
|
40668
40698
|
var init_controller = __esm({
|
|
40669
40699
|
"src/run-case/controller.ts"() {
|
|
40670
40700
|
"use strict";
|
|
40671
|
-
|
|
40701
|
+
import_node_fs49 = require("fs");
|
|
40672
40702
|
import_node_os23 = __toESM(require("os"), 1);
|
|
40673
|
-
|
|
40703
|
+
import_node_path64 = __toESM(require("path"), 1);
|
|
40674
40704
|
init_claude();
|
|
40675
40705
|
init_stdout_splitter();
|
|
40676
40706
|
init_permission_stdio();
|
|
@@ -40945,8 +40975,8 @@ Env (advanced):
|
|
|
40945
40975
|
`;
|
|
40946
40976
|
|
|
40947
40977
|
// src/index.ts
|
|
40948
|
-
var
|
|
40949
|
-
var
|
|
40978
|
+
var import_node_path62 = __toESM(require("path"), 1);
|
|
40979
|
+
var import_node_fs47 = __toESM(require("fs"), 1);
|
|
40950
40980
|
var import_node_os22 = __toESM(require("os"), 1);
|
|
40951
40981
|
|
|
40952
40982
|
// ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
|
|
@@ -41933,24 +41963,16 @@ function buildConnectionPrompt(args) {
|
|
|
41933
41963
|
// src/persona/sharing-prompt.ts
|
|
41934
41964
|
var ATTACHMENT_SHARING_HINT = `## \u628A\u4EA7\u51FA\u6587\u4EF6\u5206\u4EAB\u7ED9\u5BF9\u65B9\uFF08attachment\uFF09
|
|
41935
41965
|
|
|
41936
|
-
\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
|
|
41966
|
+
\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 \`clawd-rpc\` MCP \u7684 \`call\` tool \u628A\u6587\u4EF6\u8F6C\u6210\u4E00\u4E2A\u5E26\u7B7E\u540D\u7684\u4E0B\u8F7D URL\uFF1A
|
|
41937
41967
|
|
|
41938
41968
|
1. \u5165\u7FA4\uFF08\u767B\u8BB0\u5230\u672C session \u7684\u5171\u4EAB\u6E05\u5355\uFF09\uFF1A
|
|
41939
|
-
|
|
41940
|
-
curl -sS -X POST "$CLAWD_DAEMON_URL/api/rpc/attachment.groupAdd" \\
|
|
41941
|
-
-H "X-Clawd-Session-Id: $CLAWD_SESSION_ID" -H 'Content-Type: application/json' \\
|
|
41942
|
-
-d '{"relPath":"<\u6587\u4EF6\u7684\u7EDD\u5BF9\u8DEF\u5F84>"}'
|
|
41943
|
-
\`\`\`
|
|
41969
|
+
call({ method: "attachment.groupAdd", args: { relPath: "<\u6587\u4EF6\u7684\u7EDD\u5BF9\u8DEF\u5F84>", origin: "agent" } })
|
|
41944
41970
|
2. \u7B7E\u540D\u62FF URL\uFF1A
|
|
41945
|
-
|
|
41946
|
-
curl -sS -X POST "$CLAWD_DAEMON_URL/api/rpc/attachment.signUrl" \\
|
|
41947
|
-
-H "X-Clawd-Session-Id: $CLAWD_SESSION_ID" -H 'Content-Type: application/json' \\
|
|
41948
|
-
-d '{"relPath":"<\u540C\u4E00\u4E2A\u7EDD\u5BF9\u8DEF\u5F84>"}'
|
|
41949
|
-
\`\`\`
|
|
41971
|
+
call({ method: "attachment.signUrl", args: { relPath: "<\u540C\u4E00\u4E2A\u7EDD\u5BF9\u8DEF\u5F84>" } })
|
|
41950
41972
|
\u8FD4\u56DE\u7684 \`url\` \u5C31\u662F\u5BF9\u65B9\u53EF\u76F4\u63A5\u6253\u5F00/\u4E0B\u8F7D\u7684\u94FE\u63A5\u3002
|
|
41951
41973
|
|
|
41952
41974
|
- \`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
|
|
41953
|
-
- \u53EA\u80FD\u5206\u4EAB\
|
|
41975
|
+
- \u8DE8\u8BBE\u5907\u88AB\u59D4\u6D3E\uFF08guest\uFF09\u7684\u4F1A\u8BDD\u53EA\u80FD\u5206\u4EAB\u81EA\u5DF1\u5DE5\u4F5C\u76EE\u5F55\u5185\u7684\u6587\u4EF6\uFF0C\u8D8A\u754C\u4F1A\u88AB\u62D2\u3002
|
|
41954
41976
|
- \u88AB dispatch \u63A5\u5230\u4EFB\u52A1\u3001\u8981\u56DE\u4F20\u4EA7\u7269\u65F6\uFF1A\u628A\u7B7E\u540D URL \u5199\u8FDB \`personaDispatchComplete\` tool\uFF08\`clawd-dispatch\` MCP\uFF09\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`;
|
|
41955
41977
|
|
|
41956
41978
|
// src/persona/no-ask-user-question-prompt.ts
|
|
@@ -42950,7 +42972,7 @@ var SessionRunner = class {
|
|
|
42950
42972
|
ownerFeishuUnionId: this.hooks.ownerFeishuUnionId,
|
|
42951
42973
|
personaRoot: this.hooks.personaRoot,
|
|
42952
42974
|
// 单栏 refactor (spec 2026-06-02 §5.1): cc 子进程 env 注入 CLAWD_DAEMON_URL,让 assistant
|
|
42953
|
-
//
|
|
42975
|
+
// MCP server 拼 /rpc/<method>(Bearer)调 daemon。null = HTTP RPC 未启。
|
|
42954
42976
|
getDaemonUrl: this.hooks.getDaemonUrl,
|
|
42955
42977
|
// clawd 管家 (spec 2026-07-22-clawd-butler-persona §3.1): cc 子进程 env 注入 CLAWOS_API,
|
|
42956
42978
|
// 让 clawd 管家 skill fetch `${CLAWOS_API}/docs/**` 线上文档。
|
|
@@ -46880,8 +46902,8 @@ function turnStartInput(text) {
|
|
|
46880
46902
|
const items = [];
|
|
46881
46903
|
let leftover = text;
|
|
46882
46904
|
for (const m2 of text.matchAll(SKILL_RE)) {
|
|
46883
|
-
const [marker, name,
|
|
46884
|
-
items.push({ type: "skill", name, path:
|
|
46905
|
+
const [marker, name, path75] = m2;
|
|
46906
|
+
items.push({ type: "skill", name, path: path75 });
|
|
46885
46907
|
leftover = leftover.replace(marker, "");
|
|
46886
46908
|
}
|
|
46887
46909
|
for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
|
|
@@ -48470,13 +48492,6 @@ function jsonlExistsForCtx(ctx) {
|
|
|
48470
48492
|
init_claude_history();
|
|
48471
48493
|
|
|
48472
48494
|
// src/dispatch/manager.ts
|
|
48473
|
-
function defer() {
|
|
48474
|
-
let resolve6;
|
|
48475
|
-
const promise = new Promise((r) => {
|
|
48476
|
-
resolve6 = r;
|
|
48477
|
-
});
|
|
48478
|
-
return { promise, resolve: resolve6 };
|
|
48479
|
-
}
|
|
48480
48495
|
var PersonaDispatchManager = class {
|
|
48481
48496
|
constructor(deps) {
|
|
48482
48497
|
this.deps = deps;
|
|
@@ -48488,8 +48503,7 @@ var PersonaDispatchManager = class {
|
|
|
48488
48503
|
this.map.set(dispatchId, {
|
|
48489
48504
|
dispatchId,
|
|
48490
48505
|
sourceSessionId: args.sourceSessionId,
|
|
48491
|
-
targetPersona: args.targetPersona
|
|
48492
|
-
waiters: []
|
|
48506
|
+
targetPersona: args.targetPersona
|
|
48493
48507
|
});
|
|
48494
48508
|
this.deps.store.add({
|
|
48495
48509
|
dispatchId,
|
|
@@ -48511,45 +48525,21 @@ var PersonaDispatchManager = class {
|
|
|
48511
48525
|
outcome,
|
|
48512
48526
|
completedAt: new Date(this.deps.now()).toISOString()
|
|
48513
48527
|
});
|
|
48514
|
-
if (state)
|
|
48515
|
-
const waiters = state.waiters;
|
|
48516
|
-
state.waiters = [];
|
|
48517
|
-
for (const w2 of waiters) w2.resolve(outcome);
|
|
48518
|
-
this.map.delete(dispatchId);
|
|
48519
|
-
}
|
|
48528
|
+
if (state) this.map.delete(dispatchId);
|
|
48520
48529
|
const sourceSessionId = updated?.sourceSessionId ?? state?.sourceSessionId;
|
|
48521
48530
|
if (sourceSessionId) this.deps.onCompleted?.(sourceSessionId);
|
|
48522
48531
|
}
|
|
48523
|
-
/**
|
|
48524
|
-
* mesh B 角色 hold 专用(跨设备 dispatch 的 peer 侧):guest B-role handler 用它
|
|
48525
|
-
* 挂住 /rpc HTTP 响应直到 complete,把 outcome 回传给 A 侧 daemon 的 forwardToPeer
|
|
48526
|
-
* (A 侧后台 await,A 的 tool call 不受影响照样秒回)。
|
|
48527
|
-
* 本地 dispatch 链路**不用**这个——本地 outcome 只走台账 + deliverPending。
|
|
48528
|
-
*/
|
|
48529
|
-
async waitForOutcome(dispatchId) {
|
|
48530
|
-
const state = this.map.get(dispatchId);
|
|
48531
|
-
if (!state) {
|
|
48532
|
-
const rec3 = this.deps.store.get(dispatchId);
|
|
48533
|
-
if (rec3?.outcome) return rec3.outcome;
|
|
48534
|
-
throw new Error(`unknown dispatchId: ${dispatchId}`);
|
|
48535
|
-
}
|
|
48536
|
-
const d = defer();
|
|
48537
|
-
state.waiters.push(d);
|
|
48538
|
-
return d.promise;
|
|
48539
|
-
}
|
|
48540
48532
|
/**
|
|
48541
48533
|
* 续派(spec 2026-07-21):complete 已清掉 map 条目,续派前重建——bSessionId 映射
|
|
48542
48534
|
* 保证 worker 进程死后 respawn 时 reducer lookupDispatchByBSessionId 仍注回同一
|
|
48543
48535
|
* CLAWD_DISPATCH_ID env。台账行的复位由 handler 直接走 store(本方法只管内存态)。
|
|
48544
48536
|
*/
|
|
48545
48537
|
reopen(dispatchId, args) {
|
|
48546
|
-
const existing = this.map.get(dispatchId);
|
|
48547
48538
|
this.map.set(dispatchId, {
|
|
48548
48539
|
dispatchId,
|
|
48549
48540
|
sourceSessionId: args.sourceSessionId,
|
|
48550
48541
|
targetPersona: args.targetPersona,
|
|
48551
|
-
bSessionId: args.workerSessionId
|
|
48552
|
-
waiters: existing?.waiters ?? []
|
|
48542
|
+
bSessionId: args.workerSessionId
|
|
48553
48543
|
});
|
|
48554
48544
|
}
|
|
48555
48545
|
/** SessionManager 在 createDispatchedSession 时调,建立 dispatchId → B sessionId 映射 */
|
|
@@ -48678,13 +48668,17 @@ function dispatchMcpConfigPath(dataDir) {
|
|
|
48678
48668
|
function writeDispatchMcpConfig(args) {
|
|
48679
48669
|
const cfgPath = dispatchMcpConfigPath(args.dataDir);
|
|
48680
48670
|
const content = {
|
|
48681
|
-
_comment: "daemon \u542F\u52A8\u65F6\u81EA\u52A8\u751F\u6210\uFF1Bcc spawn \u901A\u8FC7 --mcp-config \u6CE8\u5165\u3002dispatch-mcp-server.cjs \u662F daemon \u81EA\u5E26\u7684 stdio MCP server\u3002env.CLAWD_DISPATCH_LOG \u6307 server \u5199\u65E5\u5FD7\u7684 append \u6587\u4EF6\u8DEF\u5F84\uFF08dispatch \u6D41\u7A0B\u8282\u70B9 + fetch \u9519\u8BEF\u7EC6\u8282\uFF09\u3002",
|
|
48671
|
+
_comment: "daemon \u542F\u52A8\u65F6\u81EA\u52A8\u751F\u6210\uFF1Bcc spawn \u901A\u8FC7 --mcp-config \u6CE8\u5165\u3002dispatch-mcp-server.cjs \u662F daemon \u81EA\u5E26\u7684 stdio MCP server\uFF0C\u7ECF /rpc + Bearer\uFF08env CLAWD_DAEMON_TOKEN\uFF0C\u4EC5\u672C server \u8FDB\u7A0B\u53EF\u89C1\uFF09\u8C03 daemon\u3002env.CLAWD_DISPATCH_LOG \u6307 server \u5199\u65E5\u5FD7\u7684 append \u6587\u4EF6\u8DEF\u5F84\uFF08dispatch \u6D41\u7A0B\u8282\u70B9 + fetch \u9519\u8BEF\u7EC6\u8282\uFF09\u3002",
|
|
48682
48672
|
mcpServers: {
|
|
48683
48673
|
"clawd-dispatch": {
|
|
48684
48674
|
type: "stdio",
|
|
48685
48675
|
command: "node",
|
|
48686
48676
|
args: [args.serverScriptPath],
|
|
48687
|
-
|
|
48677
|
+
env: {
|
|
48678
|
+
CLAWD_DAEMON_TOKEN: args.daemonToken,
|
|
48679
|
+
CLAWD_DAEMON_URL: args.daemonUrl,
|
|
48680
|
+
...args.logPath ? { CLAWD_DISPATCH_LOG: args.logPath } : {}
|
|
48681
|
+
}
|
|
48688
48682
|
}
|
|
48689
48683
|
}
|
|
48690
48684
|
};
|
|
@@ -48702,7 +48696,7 @@ function ticketMcpConfigPath(dataDir) {
|
|
|
48702
48696
|
function writeTicketMcpConfig(args) {
|
|
48703
48697
|
const cfgPath = ticketMcpConfigPath(args.dataDir);
|
|
48704
48698
|
const content = {
|
|
48705
|
-
_comment: "daemon \u542F\u52A8\u65F6\u81EA\u52A8\u751F\u6210\uFF1Bcc spawn \u901A\u8FC7 --mcp-config \u6CE8\u5165\u3002ticket-mcp-server.cjs \u662F daemon \u81EA\u5E26\u7684 stdio MCP server\uFF0CHTTP client \u8C03 clawos api /api/tickets/*\u3002env \u542B api url\u3001\u5199\u6B7B API key\u3001scene\uFF1Bdid/name \u6BCF\u6B21 tool call \u8D70
|
|
48699
|
+
_comment: "daemon \u542F\u52A8\u65F6\u81EA\u52A8\u751F\u6210\uFF1Bcc spawn \u901A\u8FC7 --mcp-config \u6CE8\u5165\u3002ticket-mcp-server.cjs \u662F daemon \u81EA\u5E26\u7684 stdio MCP server\uFF0CHTTP client \u8C03 clawos api /api/tickets/*\u3002env \u542B api url\u3001\u5199\u6B7B API key\u3001scene\uFF1Bdid/name \u6BCF\u6B21 tool call \u8D70 /rpc/session:currentTurnSender (Bearer CLAWD_DAEMON_TOKEN) \u62FF\u5F53\u524D turn caller (2026-07-08 spec \xA74.7)\u3002CLAWD_TICKET_LOG \u6307 server \u5199\u65E5\u5FD7\u7684 append \u6587\u4EF6\u8DEF\u5F84\u3002cc \u573A\u666F scene \u56FA\u5B9A persona\u3002",
|
|
48706
48700
|
mcpServers: {
|
|
48707
48701
|
"clawd-ticket": {
|
|
48708
48702
|
type: "stdio",
|
|
@@ -48712,6 +48706,8 @@ function writeTicketMcpConfig(args) {
|
|
|
48712
48706
|
CLAWD_TICKET_API_URL: args.apiBaseUrl,
|
|
48713
48707
|
CLAWD_TICKET_API_KEY: args.apiKey,
|
|
48714
48708
|
CLAWD_TICKET_SCENE: "persona",
|
|
48709
|
+
CLAWD_DAEMON_TOKEN: args.daemonToken,
|
|
48710
|
+
CLAWD_DAEMON_URL: args.daemonUrl,
|
|
48715
48711
|
...args.logPath ? { CLAWD_TICKET_LOG: args.logPath } : {}
|
|
48716
48712
|
}
|
|
48717
48713
|
}
|
|
@@ -48737,7 +48733,11 @@ async function writeShiftMcpConfig(args) {
|
|
|
48737
48733
|
type: "stdio",
|
|
48738
48734
|
command: "node",
|
|
48739
48735
|
args: [args.serverScriptPath],
|
|
48740
|
-
|
|
48736
|
+
env: {
|
|
48737
|
+
CLAWD_DAEMON_TOKEN: args.daemonToken,
|
|
48738
|
+
CLAWD_DAEMON_URL: args.daemonUrl,
|
|
48739
|
+
...args.logPath ? { CLAWD_SHIFT_LOG: args.logPath } : {}
|
|
48740
|
+
}
|
|
48741
48741
|
}
|
|
48742
48742
|
}
|
|
48743
48743
|
};
|
|
@@ -48761,7 +48761,11 @@ async function writeInboxMcpConfig(args) {
|
|
|
48761
48761
|
type: "stdio",
|
|
48762
48762
|
command: "node",
|
|
48763
48763
|
args: [args.serverScriptPath],
|
|
48764
|
-
|
|
48764
|
+
env: {
|
|
48765
|
+
CLAWD_DAEMON_TOKEN: args.daemonToken,
|
|
48766
|
+
CLAWD_DAEMON_URL: args.daemonUrl,
|
|
48767
|
+
...args.logPath ? { CLAWD_INBOX_LOG: args.logPath } : {}
|
|
48768
|
+
}
|
|
48765
48769
|
}
|
|
48766
48770
|
}
|
|
48767
48771
|
};
|
|
@@ -48779,11 +48783,15 @@ function peerOpsMcpConfigPath(dataDir) {
|
|
|
48779
48783
|
function writePeerOpsMcpConfig(args) {
|
|
48780
48784
|
const cfgPath = peerOpsMcpConfigPath(args.dataDir);
|
|
48781
48785
|
const cfg = {
|
|
48782
|
-
_comment: "daemon \u542F\u52A8\u81EA\u52A8\u751F\u6210\uFF1Bcc spawn \u901A\u8FC7 --mcp-config \u6CE8\u5165\u3002peer-ops-mcp-server.cjs \u662F daemon \u81EA\u5E26 stdio MCP server\uFF08\u8DE8\u8BBE\u5907 exec\uFF09\u3002",
|
|
48786
|
+
_comment: "daemon \u542F\u52A8\u81EA\u52A8\u751F\u6210\uFF1Bcc spawn \u901A\u8FC7 --mcp-config \u6CE8\u5165\u3002peer-ops-mcp-server.cjs \u662F daemon \u81EA\u5E26 stdio MCP server\uFF08\u8DE8\u8BBE\u5907 exec\uFF09\uFF0C\u7ECF /rpc + Bearer\uFF08env CLAWD_DAEMON_TOKEN\uFF09\u8C03 daemon\u3002",
|
|
48783
48787
|
mcpServers: {
|
|
48784
48788
|
"peer-ops": {
|
|
48785
48789
|
command: "node",
|
|
48786
|
-
args: [args.serverScriptPath]
|
|
48790
|
+
args: [args.serverScriptPath],
|
|
48791
|
+
env: {
|
|
48792
|
+
CLAWD_DAEMON_TOKEN: args.daemonToken,
|
|
48793
|
+
CLAWD_DAEMON_URL: args.daemonUrl
|
|
48794
|
+
}
|
|
48787
48795
|
}
|
|
48788
48796
|
}
|
|
48789
48797
|
};
|
|
@@ -48792,9 +48800,37 @@ function writePeerOpsMcpConfig(args) {
|
|
|
48792
48800
|
return cfgPath;
|
|
48793
48801
|
}
|
|
48794
48802
|
|
|
48803
|
+
// src/rpc-tool/mcp-config.ts
|
|
48804
|
+
var import_node_fs25 = __toESM(require("fs"), 1);
|
|
48805
|
+
var import_node_path24 = __toESM(require("path"), 1);
|
|
48806
|
+
function rpcToolMcpConfigPath(dataDir) {
|
|
48807
|
+
return import_node_path24.default.join(dataDir, "rpc-tool.mcp.json");
|
|
48808
|
+
}
|
|
48809
|
+
function writeRpcToolMcpConfig(args) {
|
|
48810
|
+
const cfgPath = rpcToolMcpConfigPath(args.dataDir);
|
|
48811
|
+
const content = {
|
|
48812
|
+
_comment: "daemon \u542F\u52A8\u65F6\u81EA\u52A8\u751F\u6210\uFF1Bcc spawn \u901A\u8FC7 --mcp-config \u6CE8\u5165\u3002rpc-tool/mcp-server.cjs \u662F daemon \u81EA\u5E26\u7684 stdio MCP server\uFF0C\u66B4\u9732\u6CDB\u5316 call(method, args) tool\u2014\u2014agent \u8C03 daemon RPC \u7684\u552F\u4E00\u6536\u53E3\uFF08/rpc + Bearer\uFF0Ctoken \u4EC5\u672C server \u8FDB\u7A0B\u53EF\u89C1\uFF09\u3002",
|
|
48813
|
+
mcpServers: {
|
|
48814
|
+
"clawd-rpc": {
|
|
48815
|
+
type: "stdio",
|
|
48816
|
+
command: "node",
|
|
48817
|
+
args: [args.serverScriptPath],
|
|
48818
|
+
env: {
|
|
48819
|
+
CLAWD_DAEMON_TOKEN: args.daemonToken,
|
|
48820
|
+
CLAWD_DAEMON_URL: args.daemonUrl,
|
|
48821
|
+
...args.logPath ? { CLAWD_RPC_LOG: args.logPath } : {}
|
|
48822
|
+
}
|
|
48823
|
+
}
|
|
48824
|
+
}
|
|
48825
|
+
};
|
|
48826
|
+
import_node_fs25.default.mkdirSync(args.dataDir, { recursive: true });
|
|
48827
|
+
import_node_fs25.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
|
|
48828
|
+
return cfgPath;
|
|
48829
|
+
}
|
|
48830
|
+
|
|
48795
48831
|
// src/shift/store.ts
|
|
48796
48832
|
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
48797
|
-
var
|
|
48833
|
+
var import_node_path25 = __toESM(require("path"), 1);
|
|
48798
48834
|
var import_node_crypto6 = require("crypto");
|
|
48799
48835
|
|
|
48800
48836
|
// src/shift/schedule.ts
|
|
@@ -48869,7 +48905,7 @@ function createShiftStore(deps) {
|
|
|
48869
48905
|
flushTimer = null;
|
|
48870
48906
|
}
|
|
48871
48907
|
const content = { version: 1, shifts };
|
|
48872
|
-
await import_promises2.default.mkdir(
|
|
48908
|
+
await import_promises2.default.mkdir(import_node_path25.default.dirname(deps.filePath), { recursive: true });
|
|
48873
48909
|
const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
|
|
48874
48910
|
await import_promises2.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
|
|
48875
48911
|
await import_promises2.default.rename(tmp, deps.filePath);
|
|
@@ -49322,7 +49358,7 @@ function buildPersonaDispatchHandlers(deps) {
|
|
|
49322
49358
|
}
|
|
49323
49359
|
if (!sourceSessionId) {
|
|
49324
49360
|
throw new Error(
|
|
49325
|
-
"personaDispatch:run requires sessionId
|
|
49361
|
+
"personaDispatch:run requires sessionId in the request body"
|
|
49326
49362
|
);
|
|
49327
49363
|
}
|
|
49328
49364
|
const rec3 = deps.store.get(args.dispatchId);
|
|
@@ -49395,7 +49431,7 @@ function buildPersonaDispatchHandlers(deps) {
|
|
|
49395
49431
|
}
|
|
49396
49432
|
if (!sourceSessionId) {
|
|
49397
49433
|
throw new Error(
|
|
49398
|
-
"personaDispatch:run requires sessionId
|
|
49434
|
+
"personaDispatch:run requires sessionId in the request body"
|
|
49399
49435
|
);
|
|
49400
49436
|
}
|
|
49401
49437
|
const { dispatchId: dispatchId2 } = mgr.start({
|
|
@@ -49462,14 +49498,13 @@ function buildPersonaDispatchHandlers(deps) {
|
|
|
49462
49498
|
reason: `failed to spawn B: ${reason}`
|
|
49463
49499
|
});
|
|
49464
49500
|
});
|
|
49465
|
-
const outcome = await mgr.waitForOutcome(dispatchId2);
|
|
49466
49501
|
return {
|
|
49467
|
-
response: { type: "personaDispatch:run:ok",
|
|
49502
|
+
response: { type: "personaDispatch:run:ok", dispatchId: dispatchId2 }
|
|
49468
49503
|
};
|
|
49469
49504
|
}
|
|
49470
49505
|
if (!sourceSessionId) {
|
|
49471
49506
|
throw new Error(
|
|
49472
|
-
"personaDispatch:run requires sessionId
|
|
49507
|
+
"personaDispatch:run requires sessionId in the request body"
|
|
49473
49508
|
);
|
|
49474
49509
|
}
|
|
49475
49510
|
const { dispatchId } = mgr.start({
|
|
@@ -49517,6 +49552,22 @@ function buildPersonaDispatchHandlers(deps) {
|
|
|
49517
49552
|
response: { type: "personaDispatch:complete:ok" }
|
|
49518
49553
|
};
|
|
49519
49554
|
};
|
|
49555
|
+
const get = async (frame, _client, ctx) => {
|
|
49556
|
+
const { type: _t, requestId: _r, ...rest } = frame;
|
|
49557
|
+
const args = DispatchGetArgsSchema.parse(rest);
|
|
49558
|
+
const rec3 = deps.store.get(args.dispatchId);
|
|
49559
|
+
const denied = ctx?.principal.kind === "guest" && rec3?.sourceSessionId !== ctx.principal.id;
|
|
49560
|
+
if (!rec3 || denied) {
|
|
49561
|
+
throw new ClawdError(ERROR_CODES.DISPATCH_NOT_FOUND, `unknown dispatchId: ${args.dispatchId}`);
|
|
49562
|
+
}
|
|
49563
|
+
return {
|
|
49564
|
+
response: {
|
|
49565
|
+
type: "personaDispatch:get:ok",
|
|
49566
|
+
status: rec3.status,
|
|
49567
|
+
...rec3.outcome ? { outcome: rec3.outcome } : {}
|
|
49568
|
+
}
|
|
49569
|
+
};
|
|
49570
|
+
};
|
|
49520
49571
|
const list = async (frame) => {
|
|
49521
49572
|
const { type: _t, requestId: _r, ...rest } = frame;
|
|
49522
49573
|
const args = DispatchListArgsSchema.parse(rest);
|
|
@@ -49530,7 +49581,8 @@ function buildPersonaDispatchHandlers(deps) {
|
|
|
49530
49581
|
return {
|
|
49531
49582
|
"personaDispatch:run": run,
|
|
49532
49583
|
"personaDispatch:complete": complete,
|
|
49533
|
-
"personaDispatch:list": list
|
|
49584
|
+
"personaDispatch:list": list,
|
|
49585
|
+
"personaDispatch:get": get
|
|
49534
49586
|
};
|
|
49535
49587
|
}
|
|
49536
49588
|
|
|
@@ -49541,8 +49593,13 @@ function wsUrlToHttp(url) {
|
|
|
49541
49593
|
if (url.startsWith("ws://")) return "http://" + url.slice("ws://".length);
|
|
49542
49594
|
return url;
|
|
49543
49595
|
}
|
|
49596
|
+
var DISPATCH_POLL_INTERVAL_MS = 5e3;
|
|
49597
|
+
var DISPATCH_POLL_UNREACHABLE_MS = 6e5;
|
|
49598
|
+
var DISPATCH_POLL_TOTAL_MS = 864e5;
|
|
49599
|
+
var defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
49544
49600
|
async function forwardDispatchToPeer(args) {
|
|
49545
49601
|
const f = args.fetchImpl ?? fetch;
|
|
49602
|
+
const sleep2 = args.sleepImpl ?? defaultSleep;
|
|
49546
49603
|
const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
|
|
49547
49604
|
const url = `${base}/rpc/personaDispatch:run`;
|
|
49548
49605
|
let res;
|
|
@@ -49577,7 +49634,58 @@ async function forwardDispatchToPeer(args) {
|
|
|
49577
49634
|
if (json.ok === false) {
|
|
49578
49635
|
return { kind: "failure", reason: `peer rejected: ${json.error}: ${json.message}` };
|
|
49579
49636
|
}
|
|
49580
|
-
return json.result.outcome;
|
|
49637
|
+
if ("outcome" in json.result) return json.result.outcome;
|
|
49638
|
+
const dispatchId = json.result.dispatchId;
|
|
49639
|
+
let elapsed = 0;
|
|
49640
|
+
let unreachableFor = 0;
|
|
49641
|
+
while (elapsed < DISPATCH_POLL_TOTAL_MS) {
|
|
49642
|
+
await sleep2(DISPATCH_POLL_INTERVAL_MS);
|
|
49643
|
+
elapsed += DISPATCH_POLL_INTERVAL_MS;
|
|
49644
|
+
const markUnreachable = () => {
|
|
49645
|
+
unreachableFor += DISPATCH_POLL_INTERVAL_MS;
|
|
49646
|
+
return unreachableFor >= DISPATCH_POLL_UNREACHABLE_MS ? {
|
|
49647
|
+
kind: "failure",
|
|
49648
|
+
reason: "peer unreachable while polling dispatch outcome (TARGET_UNREACHABLE)"
|
|
49649
|
+
} : null;
|
|
49650
|
+
};
|
|
49651
|
+
let getRes;
|
|
49652
|
+
try {
|
|
49653
|
+
getRes = await f(`${base}/rpc/personaDispatch:get`, {
|
|
49654
|
+
method: "POST",
|
|
49655
|
+
headers: {
|
|
49656
|
+
"content-type": "application/json",
|
|
49657
|
+
authorization: `Bearer ${args.contact.connectToken}`
|
|
49658
|
+
},
|
|
49659
|
+
body: JSON.stringify({ dispatchId })
|
|
49660
|
+
});
|
|
49661
|
+
} catch {
|
|
49662
|
+
const dead = markUnreachable();
|
|
49663
|
+
if (dead) return dead;
|
|
49664
|
+
continue;
|
|
49665
|
+
}
|
|
49666
|
+
let getJson;
|
|
49667
|
+
try {
|
|
49668
|
+
getJson = await getRes.json();
|
|
49669
|
+
} catch {
|
|
49670
|
+
const dead = markUnreachable();
|
|
49671
|
+
if (dead) return dead;
|
|
49672
|
+
continue;
|
|
49673
|
+
}
|
|
49674
|
+
if (getJson.ok === false) {
|
|
49675
|
+
if (getJson.error === ERROR_CODES.DISPATCH_NOT_FOUND) {
|
|
49676
|
+
return { kind: "failure", reason: `peer lost dispatch ${dispatchId} (daemon restarted?)` };
|
|
49677
|
+
}
|
|
49678
|
+
const dead = markUnreachable();
|
|
49679
|
+
if (dead) return dead;
|
|
49680
|
+
continue;
|
|
49681
|
+
}
|
|
49682
|
+
unreachableFor = 0;
|
|
49683
|
+
const { status, outcome } = getJson.result;
|
|
49684
|
+
if (status === "completed" || status === "failed") {
|
|
49685
|
+
return outcome ?? { kind: "failure", reason: "peer returned terminal status without outcome" };
|
|
49686
|
+
}
|
|
49687
|
+
}
|
|
49688
|
+
return { kind: "failure", reason: "dispatch polling exceeded 24h (TIMEOUT)" };
|
|
49581
49689
|
}
|
|
49582
49690
|
async function forwardInboxPostToPeer(args) {
|
|
49583
49691
|
const f = args.fetchImpl ?? fetch;
|
|
@@ -49821,7 +49929,7 @@ var CodexHistoryReader = class {
|
|
|
49821
49929
|
|
|
49822
49930
|
// src/tools/codex-skills.ts
|
|
49823
49931
|
var import_node_child_process7 = require("child_process");
|
|
49824
|
-
var
|
|
49932
|
+
var import_node_path26 = __toESM(require("path"), 1);
|
|
49825
49933
|
var DEFAULT_TIMEOUT_MS3 = 8e3;
|
|
49826
49934
|
function str3(v2) {
|
|
49827
49935
|
return typeof v2 === "string" ? v2 : void 0;
|
|
@@ -49837,13 +49945,13 @@ function mapSkillsListResponse(res) {
|
|
|
49837
49945
|
const r = s ?? {};
|
|
49838
49946
|
const name = str3(r.name);
|
|
49839
49947
|
if (!name) continue;
|
|
49840
|
-
const
|
|
49948
|
+
const path75 = str3(r.path);
|
|
49841
49949
|
const description = str3(r.description);
|
|
49842
49950
|
const isPlugin = name.includes(":");
|
|
49843
49951
|
out.push({
|
|
49844
49952
|
name,
|
|
49845
49953
|
source: isPlugin ? "plugin" : "project",
|
|
49846
|
-
...
|
|
49954
|
+
...path75 ? { path: path75 } : {},
|
|
49847
49955
|
...description ? { description } : {},
|
|
49848
49956
|
...isPlugin ? { plugin: name.split(":")[0] } : {}
|
|
49849
49957
|
});
|
|
@@ -49873,7 +49981,7 @@ async function listCodexSkills(cwd, deps = {}) {
|
|
|
49873
49981
|
})(),
|
|
49874
49982
|
timeout
|
|
49875
49983
|
]);
|
|
49876
|
-
void client.request("skills/extraRoots/set", { extraRoots: [
|
|
49984
|
+
void client.request("skills/extraRoots/set", { extraRoots: [import_node_path26.default.join(cwd, ".claude", "skills")] }).catch(() => void 0);
|
|
49877
49985
|
const res = await Promise.race([client.request("skills/list", { cwds: [cwd] }), timeout]);
|
|
49878
49986
|
return mapSkillsListResponse(res);
|
|
49879
49987
|
} finally {
|
|
@@ -49883,23 +49991,23 @@ async function listCodexSkills(cwd, deps = {}) {
|
|
|
49883
49991
|
}
|
|
49884
49992
|
|
|
49885
49993
|
// src/workspace/browser.ts
|
|
49886
|
-
var
|
|
49994
|
+
var import_node_fs26 = __toESM(require("fs"), 1);
|
|
49887
49995
|
var import_node_os8 = __toESM(require("os"), 1);
|
|
49888
|
-
var
|
|
49996
|
+
var import_node_path27 = __toESM(require("path"), 1);
|
|
49889
49997
|
init_protocol();
|
|
49890
49998
|
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
49891
49999
|
function resolveInsideCwd(cwd, subpath) {
|
|
49892
|
-
const absCwd =
|
|
49893
|
-
const joined =
|
|
49894
|
-
const rel =
|
|
49895
|
-
if (rel.startsWith("..") ||
|
|
50000
|
+
const absCwd = import_node_path27.default.resolve(cwd);
|
|
50001
|
+
const joined = import_node_path27.default.resolve(absCwd, subpath ?? ".");
|
|
50002
|
+
const rel = import_node_path27.default.relative(absCwd, joined);
|
|
50003
|
+
if (rel.startsWith("..") || import_node_path27.default.isAbsolute(rel)) {
|
|
49896
50004
|
throw new ClawdError(ERROR_CODES.INVALID_PATH, `path escapes cwd: ${subpath}`);
|
|
49897
50005
|
}
|
|
49898
50006
|
return joined;
|
|
49899
50007
|
}
|
|
49900
50008
|
function ensureCwd(cwd) {
|
|
49901
50009
|
try {
|
|
49902
|
-
const stat =
|
|
50010
|
+
const stat = import_node_fs26.default.statSync(cwd);
|
|
49903
50011
|
if (!stat.isDirectory()) {
|
|
49904
50012
|
throw new ClawdError(ERROR_CODES.INVALID_CWD, `not a directory: ${cwd}`);
|
|
49905
50013
|
}
|
|
@@ -49913,7 +50021,7 @@ var WorkspaceBrowser = class {
|
|
|
49913
50021
|
const cwd = args.cwd && args.cwd.length > 0 ? args.cwd : import_node_os8.default.homedir();
|
|
49914
50022
|
ensureCwd(cwd);
|
|
49915
50023
|
const full = resolveInsideCwd(cwd, args.path);
|
|
49916
|
-
const dirents =
|
|
50024
|
+
const dirents = import_node_fs26.default.readdirSync(full, { withFileTypes: true });
|
|
49917
50025
|
const entries = [];
|
|
49918
50026
|
for (const d of dirents) {
|
|
49919
50027
|
if (!args.showHidden && d.name.startsWith(".")) continue;
|
|
@@ -49923,7 +50031,7 @@ var WorkspaceBrowser = class {
|
|
|
49923
50031
|
mtime: ""
|
|
49924
50032
|
};
|
|
49925
50033
|
try {
|
|
49926
|
-
const st =
|
|
50034
|
+
const st = import_node_fs26.default.statSync(import_node_path27.default.join(full, d.name));
|
|
49927
50035
|
entry.mtime = new Date(st.mtimeMs).toISOString();
|
|
49928
50036
|
if (d.isFile()) entry.size = st.size;
|
|
49929
50037
|
} catch {
|
|
@@ -49939,14 +50047,14 @@ var WorkspaceBrowser = class {
|
|
|
49939
50047
|
read(args) {
|
|
49940
50048
|
ensureCwd(args.cwd);
|
|
49941
50049
|
const full = resolveInsideCwd(args.cwd, args.path);
|
|
49942
|
-
const st =
|
|
50050
|
+
const st = import_node_fs26.default.statSync(full);
|
|
49943
50051
|
if (!st.isFile()) {
|
|
49944
50052
|
throw new ClawdError(ERROR_CODES.INVALID_PATH, `not a file: ${args.path}`);
|
|
49945
50053
|
}
|
|
49946
50054
|
if (st.size > MAX_FILE_BYTES) {
|
|
49947
50055
|
throw new ClawdError(ERROR_CODES.FILE_TOO_LARGE, `file > ${MAX_FILE_BYTES} bytes`);
|
|
49948
50056
|
}
|
|
49949
|
-
const buf =
|
|
50057
|
+
const buf = import_node_fs26.default.readFileSync(full);
|
|
49950
50058
|
const isBinary = buf.includes(0);
|
|
49951
50059
|
if (isBinary) {
|
|
49952
50060
|
return {
|
|
@@ -49968,20 +50076,20 @@ var WorkspaceBrowser = class {
|
|
|
49968
50076
|
};
|
|
49969
50077
|
|
|
49970
50078
|
// src/skills/agents-scanner.ts
|
|
49971
|
-
var
|
|
50079
|
+
var import_node_fs27 = __toESM(require("fs"), 1);
|
|
49972
50080
|
var import_node_os9 = __toESM(require("os"), 1);
|
|
49973
|
-
var
|
|
50081
|
+
var import_node_path28 = __toESM(require("path"), 1);
|
|
49974
50082
|
var DEFAULT_POLICY_DIR_DARWIN = "/Library/Application Support/ClaudeCode/.claude/agents";
|
|
49975
50083
|
function isDirLikeSync2(p2) {
|
|
49976
50084
|
try {
|
|
49977
|
-
return
|
|
50085
|
+
return import_node_fs27.default.statSync(p2).isDirectory();
|
|
49978
50086
|
} catch {
|
|
49979
50087
|
return false;
|
|
49980
50088
|
}
|
|
49981
50089
|
}
|
|
49982
50090
|
function fileExistsSync(p2) {
|
|
49983
50091
|
try {
|
|
49984
|
-
return
|
|
50092
|
+
return import_node_fs27.default.statSync(p2).isFile();
|
|
49985
50093
|
} catch {
|
|
49986
50094
|
return false;
|
|
49987
50095
|
}
|
|
@@ -49989,7 +50097,7 @@ function fileExistsSync(p2) {
|
|
|
49989
50097
|
function parseAgentFile(filePath) {
|
|
49990
50098
|
let content;
|
|
49991
50099
|
try {
|
|
49992
|
-
content =
|
|
50100
|
+
content = import_node_fs27.default.readFileSync(filePath, "utf8");
|
|
49993
50101
|
} catch {
|
|
49994
50102
|
return {};
|
|
49995
50103
|
}
|
|
@@ -50002,16 +50110,16 @@ function parseAgentFile(filePath) {
|
|
|
50002
50110
|
function scanAgentsDir(dir, source, seen, out) {
|
|
50003
50111
|
let entries;
|
|
50004
50112
|
try {
|
|
50005
|
-
entries =
|
|
50113
|
+
entries = import_node_fs27.default.readdirSync(dir, { withFileTypes: true });
|
|
50006
50114
|
} catch {
|
|
50007
50115
|
return;
|
|
50008
50116
|
}
|
|
50009
50117
|
for (const ent of entries) {
|
|
50010
50118
|
if (!ent.name.endsWith(".md")) continue;
|
|
50011
|
-
if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(
|
|
50119
|
+
if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path28.default.join(dir, ent.name)))) {
|
|
50012
50120
|
continue;
|
|
50013
50121
|
}
|
|
50014
|
-
const filePath =
|
|
50122
|
+
const filePath = import_node_path28.default.join(dir, ent.name);
|
|
50015
50123
|
const baseName = ent.name.replace(/\.md$/, "");
|
|
50016
50124
|
if (seen.has(baseName)) continue;
|
|
50017
50125
|
seen.add(baseName);
|
|
@@ -50029,12 +50137,12 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
|
|
|
50029
50137
|
function walk2(dir, namespaces) {
|
|
50030
50138
|
let entries;
|
|
50031
50139
|
try {
|
|
50032
|
-
entries =
|
|
50140
|
+
entries = import_node_fs27.default.readdirSync(dir, { withFileTypes: true });
|
|
50033
50141
|
} catch {
|
|
50034
50142
|
return;
|
|
50035
50143
|
}
|
|
50036
50144
|
for (const ent of entries) {
|
|
50037
|
-
const childPath =
|
|
50145
|
+
const childPath = import_node_path28.default.join(dir, ent.name);
|
|
50038
50146
|
if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync2(childPath)) {
|
|
50039
50147
|
walk2(childPath, [...namespaces, ent.name]);
|
|
50040
50148
|
continue;
|
|
@@ -50059,13 +50167,13 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
|
|
|
50059
50167
|
walk2(root, []);
|
|
50060
50168
|
}
|
|
50061
50169
|
function readInstalledPlugins2(home) {
|
|
50062
|
-
const pluginsDir =
|
|
50063
|
-
const v2 =
|
|
50064
|
-
const v1 =
|
|
50170
|
+
const pluginsDir = import_node_path28.default.join(home, ".claude", "plugins");
|
|
50171
|
+
const v2 = import_node_path28.default.join(pluginsDir, "installed_plugins_v2.json");
|
|
50172
|
+
const v1 = import_node_path28.default.join(pluginsDir, "installed_plugins.json");
|
|
50065
50173
|
let raw = null;
|
|
50066
50174
|
for (const candidate of [v2, v1]) {
|
|
50067
50175
|
try {
|
|
50068
|
-
raw =
|
|
50176
|
+
raw = import_node_fs27.default.readFileSync(candidate, "utf8");
|
|
50069
50177
|
break;
|
|
50070
50178
|
} catch {
|
|
50071
50179
|
}
|
|
@@ -50090,19 +50198,19 @@ function readInstalledPlugins2(home) {
|
|
|
50090
50198
|
return out;
|
|
50091
50199
|
}
|
|
50092
50200
|
function walkUpProjectAgentsDirs(startCwd, home, seen, out) {
|
|
50093
|
-
let cur =
|
|
50094
|
-
const fsRoot =
|
|
50201
|
+
let cur = import_node_path28.default.resolve(startCwd);
|
|
50202
|
+
const fsRoot = import_node_path28.default.parse(cur).root;
|
|
50095
50203
|
while (true) {
|
|
50096
|
-
scanAgentsDir(
|
|
50204
|
+
scanAgentsDir(import_node_path28.default.join(cur, ".claude", "agents"), "project", seen, out);
|
|
50097
50205
|
let hasGit = false;
|
|
50098
50206
|
try {
|
|
50099
|
-
hasGit =
|
|
50207
|
+
hasGit = import_node_fs27.default.existsSync(import_node_path28.default.join(cur, ".git"));
|
|
50100
50208
|
} catch {
|
|
50101
50209
|
}
|
|
50102
50210
|
if (hasGit) return;
|
|
50103
50211
|
if (cur === home) return;
|
|
50104
50212
|
if (cur === fsRoot) return;
|
|
50105
|
-
const parent =
|
|
50213
|
+
const parent = import_node_path28.default.dirname(cur);
|
|
50106
50214
|
if (parent === cur) return;
|
|
50107
50215
|
cur = parent;
|
|
50108
50216
|
}
|
|
@@ -50137,7 +50245,7 @@ var AgentsScanner = class {
|
|
|
50137
50245
|
}
|
|
50138
50246
|
const fsBlock = [];
|
|
50139
50247
|
scanAgentsDir(
|
|
50140
|
-
|
|
50248
|
+
import_node_path28.default.join(this.home, ".claude", "agents"),
|
|
50141
50249
|
"global",
|
|
50142
50250
|
seen,
|
|
50143
50251
|
fsBlock
|
|
@@ -50151,7 +50259,7 @@ var AgentsScanner = class {
|
|
|
50151
50259
|
...this.extraPluginRoots
|
|
50152
50260
|
];
|
|
50153
50261
|
for (const { name, root } of plugins) {
|
|
50154
|
-
const agentsRoot =
|
|
50262
|
+
const agentsRoot = import_node_path28.default.join(root, "agents");
|
|
50155
50263
|
scanPluginAgentsTree(agentsRoot, name, seen, fsBlock);
|
|
50156
50264
|
}
|
|
50157
50265
|
return [...builtinBlock, ...fsBlock];
|
|
@@ -50159,15 +50267,15 @@ var AgentsScanner = class {
|
|
|
50159
50267
|
};
|
|
50160
50268
|
|
|
50161
50269
|
// src/observer/session-observer.ts
|
|
50162
|
-
var
|
|
50270
|
+
var import_node_fs29 = __toESM(require("fs"), 1);
|
|
50163
50271
|
var import_node_os11 = __toESM(require("os"), 1);
|
|
50164
|
-
var
|
|
50272
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
50165
50273
|
init_claude_history();
|
|
50166
50274
|
|
|
50167
50275
|
// src/observer/subagent-meta-observer.ts
|
|
50168
|
-
var
|
|
50276
|
+
var import_node_fs28 = __toESM(require("fs"), 1);
|
|
50169
50277
|
var import_node_os10 = __toESM(require("os"), 1);
|
|
50170
|
-
var
|
|
50278
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
50171
50279
|
init_claude_history();
|
|
50172
50280
|
var META_RE = /^agent-([A-Za-z0-9_-]+)\.meta\.json$/;
|
|
50173
50281
|
var SubagentMetaObserver = class {
|
|
@@ -50180,7 +50288,7 @@ var SubagentMetaObserver = class {
|
|
|
50180
50288
|
watches = /* @__PURE__ */ new Map();
|
|
50181
50289
|
// public for spec only:测试直接拼路径写假 meta.json;生产 start() 内部自己解析
|
|
50182
50290
|
resolveSubagentDir(cwd, toolSessionId) {
|
|
50183
|
-
return
|
|
50291
|
+
return import_node_path29.default.join(
|
|
50184
50292
|
this.home,
|
|
50185
50293
|
".claude",
|
|
50186
50294
|
"projects",
|
|
@@ -50210,7 +50318,7 @@ var SubagentMetaObserver = class {
|
|
|
50210
50318
|
attachWatcher(w2) {
|
|
50211
50319
|
if (w2.watcher) return;
|
|
50212
50320
|
try {
|
|
50213
|
-
w2.watcher =
|
|
50321
|
+
w2.watcher = import_node_fs28.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
|
|
50214
50322
|
if (!name) return;
|
|
50215
50323
|
const m2 = META_RE.exec(String(name));
|
|
50216
50324
|
if (!m2) return;
|
|
@@ -50222,7 +50330,7 @@ var SubagentMetaObserver = class {
|
|
|
50222
50330
|
scan(w2) {
|
|
50223
50331
|
let entries;
|
|
50224
50332
|
try {
|
|
50225
|
-
entries =
|
|
50333
|
+
entries = import_node_fs28.default.readdirSync(w2.dirPath);
|
|
50226
50334
|
} catch {
|
|
50227
50335
|
return;
|
|
50228
50336
|
}
|
|
@@ -50236,10 +50344,10 @@ var SubagentMetaObserver = class {
|
|
|
50236
50344
|
if (!m2) return;
|
|
50237
50345
|
const agentId = m2[1];
|
|
50238
50346
|
if (w2.emitted.has(agentId)) return;
|
|
50239
|
-
const file =
|
|
50347
|
+
const file = import_node_path29.default.join(w2.dirPath, name);
|
|
50240
50348
|
let raw;
|
|
50241
50349
|
try {
|
|
50242
|
-
raw =
|
|
50350
|
+
raw = import_node_fs28.default.readFileSync(file, "utf8");
|
|
50243
50351
|
} catch {
|
|
50244
50352
|
return;
|
|
50245
50353
|
}
|
|
@@ -50296,14 +50404,14 @@ var SessionObserver = class {
|
|
|
50296
50404
|
metaObserver;
|
|
50297
50405
|
resolveJsonlPath(cwd, toolSessionId, override) {
|
|
50298
50406
|
if (override) return override;
|
|
50299
|
-
return
|
|
50407
|
+
return import_node_path30.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
|
|
50300
50408
|
}
|
|
50301
50409
|
start(args) {
|
|
50302
50410
|
this.stop(args.sessionId);
|
|
50303
50411
|
const filePath = this.resolveJsonlPath(args.cwd, args.toolSessionId, args.jsonlPath);
|
|
50304
50412
|
let size = 0;
|
|
50305
50413
|
try {
|
|
50306
|
-
size =
|
|
50414
|
+
size = import_node_fs29.default.statSync(filePath).size;
|
|
50307
50415
|
} catch {
|
|
50308
50416
|
}
|
|
50309
50417
|
const w2 = {
|
|
@@ -50316,16 +50424,16 @@ var SessionObserver = class {
|
|
|
50316
50424
|
adapter: args.adapter,
|
|
50317
50425
|
prevIsRejectSentinel: false,
|
|
50318
50426
|
toolSessionId: args.toolSessionId,
|
|
50319
|
-
sawFile:
|
|
50427
|
+
sawFile: import_node_fs29.default.existsSync(filePath),
|
|
50320
50428
|
lastRelocateScanMs: 0,
|
|
50321
50429
|
pendingCwdDerive: false,
|
|
50322
50430
|
lastDeriveMs: 0
|
|
50323
50431
|
};
|
|
50324
50432
|
try {
|
|
50325
|
-
|
|
50433
|
+
import_node_fs29.default.mkdirSync(import_node_path30.default.dirname(filePath), { recursive: true });
|
|
50326
50434
|
} catch {
|
|
50327
50435
|
}
|
|
50328
|
-
w2.watcher =
|
|
50436
|
+
w2.watcher = import_node_fs29.default.watch(import_node_path30.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
|
|
50329
50437
|
if (!changedName || !w2.filePath.endsWith(changedName)) return;
|
|
50330
50438
|
this.poll(w2);
|
|
50331
50439
|
});
|
|
@@ -50348,7 +50456,7 @@ var SessionObserver = class {
|
|
|
50348
50456
|
// 异常静默吞,不阻塞 watcher 启动
|
|
50349
50457
|
hydrateMetaTail(w2, maxLines = 200) {
|
|
50350
50458
|
try {
|
|
50351
|
-
const raw =
|
|
50459
|
+
const raw = import_node_fs29.default.readFileSync(w2.filePath, "utf8");
|
|
50352
50460
|
if (!raw) return;
|
|
50353
50461
|
const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
50354
50462
|
if (allLines.length === 0) return;
|
|
@@ -50372,7 +50480,7 @@ var SessionObserver = class {
|
|
|
50372
50480
|
poll(w2) {
|
|
50373
50481
|
let size = 0;
|
|
50374
50482
|
try {
|
|
50375
|
-
size =
|
|
50483
|
+
size = import_node_fs29.default.statSync(w2.filePath).size;
|
|
50376
50484
|
} catch {
|
|
50377
50485
|
if (w2.sawFile) this.maybeRelocate(w2);
|
|
50378
50486
|
return;
|
|
@@ -50383,11 +50491,11 @@ var SessionObserver = class {
|
|
|
50383
50491
|
w2.buf = "";
|
|
50384
50492
|
}
|
|
50385
50493
|
if (size === w2.lastSize) return;
|
|
50386
|
-
const fd =
|
|
50494
|
+
const fd = import_node_fs29.default.openSync(w2.filePath, "r");
|
|
50387
50495
|
try {
|
|
50388
50496
|
const len = size - w2.lastSize;
|
|
50389
50497
|
const buf = Buffer.alloc(len);
|
|
50390
|
-
|
|
50498
|
+
import_node_fs29.default.readSync(fd, buf, 0, len, w2.lastSize);
|
|
50391
50499
|
w2.lastSize = size;
|
|
50392
50500
|
w2.buf += buf.toString("utf8");
|
|
50393
50501
|
let newlineIndex;
|
|
@@ -50410,7 +50518,7 @@ var SessionObserver = class {
|
|
|
50410
50518
|
}
|
|
50411
50519
|
}
|
|
50412
50520
|
} finally {
|
|
50413
|
-
|
|
50521
|
+
import_node_fs29.default.closeSync(fd);
|
|
50414
50522
|
}
|
|
50415
50523
|
if (w2.pendingCwdDerive) this.maybeDeriveCwd(w2);
|
|
50416
50524
|
}
|
|
@@ -50422,7 +50530,7 @@ var SessionObserver = class {
|
|
|
50422
50530
|
const now = Date.now();
|
|
50423
50531
|
if (now - w2.lastRelocateScanMs < 1e3) return;
|
|
50424
50532
|
w2.lastRelocateScanMs = now;
|
|
50425
|
-
const projectsRoot =
|
|
50533
|
+
const projectsRoot = import_node_path30.default.join(this.home, ".claude", "projects");
|
|
50426
50534
|
const found = findTranscriptByToolSessionId(projectsRoot, w2.toolSessionId);
|
|
50427
50535
|
if (!found || found === w2.filePath) return;
|
|
50428
50536
|
try {
|
|
@@ -50431,7 +50539,7 @@ var SessionObserver = class {
|
|
|
50431
50539
|
}
|
|
50432
50540
|
w2.filePath = found;
|
|
50433
50541
|
try {
|
|
50434
|
-
w2.watcher =
|
|
50542
|
+
w2.watcher = import_node_fs29.default.watch(import_node_path30.default.dirname(found), { persistent: false }, (_event, changedName) => {
|
|
50435
50543
|
if (!changedName || !w2.filePath.endsWith(changedName)) return;
|
|
50436
50544
|
this.poll(w2);
|
|
50437
50545
|
});
|
|
@@ -50998,79 +51106,6 @@ function removeSubscription(client, sessionId) {
|
|
|
50998
51106
|
else client.subscribedSessions.set(sessionId, next);
|
|
50999
51107
|
}
|
|
51000
51108
|
|
|
51001
|
-
// src/transport/rpc-adapter.ts
|
|
51002
|
-
var EXPOSED_RPC_METHODS = [
|
|
51003
|
-
"appBuilder.createProject",
|
|
51004
|
-
"appBuilder.listProjects",
|
|
51005
|
-
"appBuilder.updateProjectPort",
|
|
51006
|
-
"appBuilder.setProdUrl",
|
|
51007
|
-
// 单栏 refactor §5.6.2 stage fix:scaffold + install 完成调 startDevServer 起 dev server;
|
|
51008
|
-
// 中间用 reportStage 上报阶段(scaffolding / installing / failed),UI 据 stage 推帧切 PreviewPane。
|
|
51009
|
-
"appBuilder.startDevServer",
|
|
51010
|
-
"appBuilder.reportStage",
|
|
51011
|
-
// 单栏 §5.6.5 fix: UI 切 session 时调(assistant 不需要主动调,但暴露白名单避免误猜成 forbidden)
|
|
51012
|
-
"appBuilder.setActiveSession",
|
|
51013
|
-
// dataclaw-builder:persona 建页时取建页人身份(含 union_id)写进生成 app 的配置。
|
|
51014
|
-
// 路由到 handlers['auth:getIdentity'],只返 identity、绝不带 ttcToken(见 handlers/feishu-auth.ts)。
|
|
51015
|
-
"auth.getIdentity",
|
|
51016
|
-
// Persona dispatch(异步,spec 2026-07-20):A 端 dispatch MCP server 调 run(秒回 dispatchId);
|
|
51017
|
-
// B 端调 complete(带 outcome → 台账 + 投递);发起方调 list 查台账。详见 protocol/src/dispatch.ts。
|
|
51018
|
-
"personaDispatch.run",
|
|
51019
|
-
// 跨设备 exec(spec 2026-07-16):A 侧本地 cc 经 /api/rpc 调,带 peerDeviceId → daemon forward。
|
|
51020
|
-
"peerExec.run",
|
|
51021
|
-
"personaDispatch.complete",
|
|
51022
|
-
"personaDispatch.list",
|
|
51023
|
-
// persona 清单(spec 2026-07-21):两级路由第一级——发起方 cc(loopback owner 语义)
|
|
51024
|
-
// 用 curl 拿全量 personaId+label 紧凑清单,再按需读候选 CLAUDE.md 首段做终选。
|
|
51025
|
-
"persona.list",
|
|
51026
|
-
// clawd-shift v1 (spec 2026-06-24-clawd-shift):cc 子进程内的 shift MCP server 调
|
|
51027
|
-
// 这 5 个 method 管理 owner 的 shift(fire-and-forget 排班)。鉴权靠 body.personaId
|
|
51028
|
-
// (mcp-server 从 process.env.CLAWD_PERSONA_ID 读,源 = SessionFile.ownerPersonaId)。
|
|
51029
|
-
// handler 内 cancel/update/history 校验 createdByPersona === personaId 防越权。
|
|
51030
|
-
"shift-internal.add",
|
|
51031
|
-
"shift-internal.list",
|
|
51032
|
-
"shift-internal.cancel",
|
|
51033
|
-
"shift-internal.update",
|
|
51034
|
-
"shift-internal.history",
|
|
51035
|
-
// file-sharing:persona guest agent 把自己工作目录内的产物入群+签名分享。
|
|
51036
|
-
// 路径越界由 handlers/attachment.ts guardAttachmentPath(scope-based)拦截。
|
|
51037
|
-
"attachment.groupAdd",
|
|
51038
|
-
"attachment.signUrl",
|
|
51039
|
-
// persona 受控发 P2P IM DM(sender 由 session scope 推,driver 分级授权见 handlers/inbox.ts)
|
|
51040
|
-
"inbox.sendDm",
|
|
51041
|
-
// per-turn sender 2026-07-08 spec §4.1:MCP servers (ticket / inbox / ...) 每 tool call
|
|
51042
|
-
// 查当前 turn caller —— cc 长驻 + owner/guest 交替驱动同一 session 时不能靠启动时 env
|
|
51043
|
-
// 写死身份,必须运行时 fetch。header X-Clawd-Session-Id 作 session 定位,handler 内查
|
|
51044
|
-
// manager.tryGetState(sid).currentTurnSender 返 SenderInfo | null。
|
|
51045
|
-
"session.currentTurnSender"
|
|
51046
|
-
];
|
|
51047
|
-
async function handleRpcRequest(input) {
|
|
51048
|
-
const { method, body, sessionId, dispatcher } = input;
|
|
51049
|
-
if (!sessionId) {
|
|
51050
|
-
return { status: 400, body: { ok: false, error: "MISSING_SESSION_ID", message: "X-Clawd-Session-Id header required" } };
|
|
51051
|
-
}
|
|
51052
|
-
if (!EXPOSED_RPC_METHODS.includes(method)) {
|
|
51053
|
-
return { status: 403, body: { ok: false, error: "METHOD_NOT_EXPOSED", message: `method "${method}" not exposed via HTTP RPC` } };
|
|
51054
|
-
}
|
|
51055
|
-
const internalMethod = method.replace(".", ":");
|
|
51056
|
-
const augmentedBody = {
|
|
51057
|
-
...typeof body === "object" && body != null ? body : {},
|
|
51058
|
-
sessionId
|
|
51059
|
-
};
|
|
51060
|
-
try {
|
|
51061
|
-
const result = await dispatcher.dispatch(internalMethod, augmentedBody, { sessionId });
|
|
51062
|
-
return { status: 200, body: { ok: true, result } };
|
|
51063
|
-
} catch (err) {
|
|
51064
|
-
const e = err;
|
|
51065
|
-
const code = e?.code ?? "INTERNAL";
|
|
51066
|
-
const message = e?.message ?? String(err);
|
|
51067
|
-
return { status: 400, body: { ok: false, error: code, message } };
|
|
51068
|
-
}
|
|
51069
|
-
}
|
|
51070
|
-
function resolveExposedHandler(handlers, internalMethod) {
|
|
51071
|
-
return handlers[internalMethod] ?? handlers[internalMethod.replace(":", ".")];
|
|
51072
|
-
}
|
|
51073
|
-
|
|
51074
51109
|
// src/transport/auth.ts
|
|
51075
51110
|
init_protocol();
|
|
51076
51111
|
var AuthGate = class {
|
|
@@ -51287,8 +51322,8 @@ async function authenticate(token, deps) {
|
|
|
51287
51322
|
}
|
|
51288
51323
|
|
|
51289
51324
|
// src/legacy-cleanup.ts
|
|
51290
|
-
var
|
|
51291
|
-
var
|
|
51325
|
+
var fs34 = __toESM(require("fs/promises"), 1);
|
|
51326
|
+
var path35 = __toESM(require("path"), 1);
|
|
51292
51327
|
var LEGACY_FILES = [
|
|
51293
51328
|
"personal-capability.json",
|
|
51294
51329
|
"received-capabilities.json",
|
|
@@ -51297,9 +51332,9 @@ var LEGACY_FILES = [
|
|
|
51297
51332
|
async function cleanupLegacyCapabilityFiles(dataDir, logger) {
|
|
51298
51333
|
const removed = [];
|
|
51299
51334
|
for (const name of LEGACY_FILES) {
|
|
51300
|
-
const p2 =
|
|
51335
|
+
const p2 = path35.join(dataDir, name);
|
|
51301
51336
|
try {
|
|
51302
|
-
await
|
|
51337
|
+
await fs34.unlink(p2);
|
|
51303
51338
|
removed.push(name);
|
|
51304
51339
|
logger.debug("legacy capability file removed", { file: name });
|
|
51305
51340
|
} catch (err) {
|
|
@@ -51311,13 +51346,13 @@ async function cleanupLegacyCapabilityFiles(dataDir, logger) {
|
|
|
51311
51346
|
}
|
|
51312
51347
|
|
|
51313
51348
|
// src/inbox/inbox-store.ts
|
|
51314
|
-
var
|
|
51315
|
-
var
|
|
51349
|
+
var fs35 = __toESM(require("fs"), 1);
|
|
51350
|
+
var path36 = __toESM(require("path"), 1);
|
|
51316
51351
|
var INBOX_SUBDIR = "inbox";
|
|
51317
51352
|
var InboxStore = class {
|
|
51318
51353
|
constructor(dataDir) {
|
|
51319
51354
|
this.dataDir = dataDir;
|
|
51320
|
-
|
|
51355
|
+
fs35.mkdirSync(this.dirPath(), { recursive: true });
|
|
51321
51356
|
}
|
|
51322
51357
|
dataDir;
|
|
51323
51358
|
/**
|
|
@@ -51329,7 +51364,7 @@ var InboxStore = class {
|
|
|
51329
51364
|
const file = this.filePath(peerDeviceId);
|
|
51330
51365
|
let raw;
|
|
51331
51366
|
try {
|
|
51332
|
-
raw =
|
|
51367
|
+
raw = fs35.readFileSync(file, "utf8");
|
|
51333
51368
|
} catch (err) {
|
|
51334
51369
|
if (err?.code === "ENOENT") return [];
|
|
51335
51370
|
return [];
|
|
@@ -51345,7 +51380,7 @@ var InboxStore = class {
|
|
|
51345
51380
|
const dir = this.dirPath();
|
|
51346
51381
|
let entries;
|
|
51347
51382
|
try {
|
|
51348
|
-
entries =
|
|
51383
|
+
entries = fs35.readdirSync(dir);
|
|
51349
51384
|
} catch (err) {
|
|
51350
51385
|
if (err?.code === "ENOENT") return [];
|
|
51351
51386
|
return [];
|
|
@@ -51361,9 +51396,9 @@ var InboxStore = class {
|
|
|
51361
51396
|
if (existing.some((m2) => m2.id === message.id)) return;
|
|
51362
51397
|
const file = this.filePath(message.peerDeviceId);
|
|
51363
51398
|
const line = JSON.stringify(message) + "\n";
|
|
51364
|
-
|
|
51399
|
+
fs35.appendFileSync(file, line, { mode: 384 });
|
|
51365
51400
|
try {
|
|
51366
|
-
|
|
51401
|
+
fs35.chmodSync(file, 384);
|
|
51367
51402
|
} catch {
|
|
51368
51403
|
}
|
|
51369
51404
|
}
|
|
@@ -51393,7 +51428,7 @@ var InboxStore = class {
|
|
|
51393
51428
|
removeByPeerDeviceId(peerDeviceId) {
|
|
51394
51429
|
const file = this.filePath(peerDeviceId);
|
|
51395
51430
|
try {
|
|
51396
|
-
|
|
51431
|
+
fs35.unlinkSync(file);
|
|
51397
51432
|
} catch (err) {
|
|
51398
51433
|
if (err?.code === "ENOENT") return;
|
|
51399
51434
|
}
|
|
@@ -51402,18 +51437,18 @@ var InboxStore = class {
|
|
|
51402
51437
|
const file = this.filePath(peerDeviceId);
|
|
51403
51438
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
51404
51439
|
const content = messages.map((m2) => JSON.stringify(m2)).join("\n") + (messages.length > 0 ? "\n" : "");
|
|
51405
|
-
|
|
51406
|
-
|
|
51440
|
+
fs35.writeFileSync(tmp, content, { mode: 384 });
|
|
51441
|
+
fs35.renameSync(tmp, file);
|
|
51407
51442
|
try {
|
|
51408
|
-
|
|
51443
|
+
fs35.chmodSync(file, 384);
|
|
51409
51444
|
} catch {
|
|
51410
51445
|
}
|
|
51411
51446
|
}
|
|
51412
51447
|
dirPath() {
|
|
51413
|
-
return
|
|
51448
|
+
return path36.join(this.dataDir, INBOX_SUBDIR);
|
|
51414
51449
|
}
|
|
51415
51450
|
filePath(peerDeviceId) {
|
|
51416
|
-
return
|
|
51451
|
+
return path36.join(this.dirPath(), `${peerDeviceId}.jsonl`);
|
|
51417
51452
|
}
|
|
51418
51453
|
};
|
|
51419
51454
|
function parseAllLines(raw) {
|
|
@@ -51501,8 +51536,8 @@ var InboxManager = class {
|
|
|
51501
51536
|
};
|
|
51502
51537
|
|
|
51503
51538
|
// src/state/contact-store.ts
|
|
51504
|
-
var
|
|
51505
|
-
var
|
|
51539
|
+
var fs36 = __toESM(require("fs"), 1);
|
|
51540
|
+
var path37 = __toESM(require("path"), 1);
|
|
51506
51541
|
var FILE_NAME = "contacts.json";
|
|
51507
51542
|
var ContactStore = class {
|
|
51508
51543
|
constructor(dataDir) {
|
|
@@ -51512,10 +51547,10 @@ var ContactStore = class {
|
|
|
51512
51547
|
contacts = /* @__PURE__ */ new Map();
|
|
51513
51548
|
load() {
|
|
51514
51549
|
this.contacts.clear();
|
|
51515
|
-
const file =
|
|
51550
|
+
const file = path37.join(this.dataDir, FILE_NAME);
|
|
51516
51551
|
let raw;
|
|
51517
51552
|
try {
|
|
51518
|
-
raw =
|
|
51553
|
+
raw = fs36.readFileSync(file, "utf8");
|
|
51519
51554
|
} catch (err) {
|
|
51520
51555
|
if (err?.code !== "ENOENT") this.renameBak(file);
|
|
51521
51556
|
return;
|
|
@@ -51584,20 +51619,20 @@ var ContactStore = class {
|
|
|
51584
51619
|
return true;
|
|
51585
51620
|
}
|
|
51586
51621
|
flush() {
|
|
51587
|
-
const file =
|
|
51622
|
+
const file = path37.join(this.dataDir, FILE_NAME);
|
|
51588
51623
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
51589
51624
|
const content = JSON.stringify(
|
|
51590
51625
|
{ contacts: Array.from(this.contacts.values()) },
|
|
51591
51626
|
null,
|
|
51592
51627
|
2
|
|
51593
51628
|
);
|
|
51594
|
-
|
|
51595
|
-
|
|
51596
|
-
|
|
51629
|
+
fs36.mkdirSync(this.dataDir, { recursive: true });
|
|
51630
|
+
fs36.writeFileSync(tmp, content, { mode: 384 });
|
|
51631
|
+
fs36.renameSync(tmp, file);
|
|
51597
51632
|
}
|
|
51598
51633
|
renameBak(file) {
|
|
51599
51634
|
try {
|
|
51600
|
-
|
|
51635
|
+
fs36.renameSync(file, `${file}.bak`);
|
|
51601
51636
|
} catch {
|
|
51602
51637
|
}
|
|
51603
51638
|
}
|
|
@@ -51705,61 +51740,61 @@ async function connectRemote(args) {
|
|
|
51705
51740
|
}
|
|
51706
51741
|
|
|
51707
51742
|
// src/migrations/2026-05-20-flatten-sessions.ts
|
|
51708
|
-
var
|
|
51709
|
-
var
|
|
51743
|
+
var fs37 = __toESM(require("fs"), 1);
|
|
51744
|
+
var path38 = __toESM(require("path"), 1);
|
|
51710
51745
|
var MIGRATION_FLAG_NAME = ".migration.v1.done";
|
|
51711
51746
|
function migrateFlattenSessions(opts) {
|
|
51712
51747
|
const dataDir = opts.dataDir;
|
|
51713
51748
|
const now = opts.now ?? Date.now;
|
|
51714
|
-
const sessionsDir =
|
|
51715
|
-
const flagPath =
|
|
51749
|
+
const sessionsDir = path38.join(dataDir, "sessions");
|
|
51750
|
+
const flagPath = path38.join(sessionsDir, MIGRATION_FLAG_NAME);
|
|
51716
51751
|
if (existsSync3(flagPath)) {
|
|
51717
51752
|
return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
|
|
51718
51753
|
}
|
|
51719
51754
|
let movedBare = 0;
|
|
51720
51755
|
let movedVmOwner = 0;
|
|
51721
51756
|
let archivedListener = 0;
|
|
51722
|
-
const defaultDir =
|
|
51757
|
+
const defaultDir = path38.join(sessionsDir, "default");
|
|
51723
51758
|
if (existsSync3(defaultDir)) {
|
|
51724
51759
|
for (const entry of readdirSafe(defaultDir)) {
|
|
51725
51760
|
if (!entry.endsWith(".json")) continue;
|
|
51726
|
-
const src =
|
|
51727
|
-
const dst =
|
|
51728
|
-
|
|
51761
|
+
const src = path38.join(defaultDir, entry);
|
|
51762
|
+
const dst = path38.join(sessionsDir, entry);
|
|
51763
|
+
fs37.renameSync(src, dst);
|
|
51729
51764
|
movedBare += 1;
|
|
51730
51765
|
}
|
|
51731
51766
|
rmdirIfEmpty(defaultDir);
|
|
51732
51767
|
}
|
|
51733
51768
|
for (const pid of readdirSafe(sessionsDir)) {
|
|
51734
|
-
const personaDir =
|
|
51769
|
+
const personaDir = path38.join(sessionsDir, pid);
|
|
51735
51770
|
if (!isDir(personaDir)) continue;
|
|
51736
51771
|
if (pid === "default") continue;
|
|
51737
|
-
const ownerSrc =
|
|
51772
|
+
const ownerSrc = path38.join(personaDir, "owner");
|
|
51738
51773
|
if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
|
|
51739
|
-
const ownerDst =
|
|
51740
|
-
|
|
51774
|
+
const ownerDst = path38.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
|
|
51775
|
+
fs37.mkdirSync(ownerDst, { recursive: true });
|
|
51741
51776
|
for (const file of readdirSafe(ownerSrc)) {
|
|
51742
51777
|
if (!file.endsWith(".json")) continue;
|
|
51743
|
-
|
|
51778
|
+
fs37.renameSync(path38.join(ownerSrc, file), path38.join(ownerDst, file));
|
|
51744
51779
|
movedVmOwner += 1;
|
|
51745
51780
|
}
|
|
51746
51781
|
rmdirIfEmpty(ownerSrc);
|
|
51747
51782
|
}
|
|
51748
|
-
const listenerSrc =
|
|
51783
|
+
const listenerSrc = path38.join(personaDir, "listener");
|
|
51749
51784
|
if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
|
|
51750
|
-
const archiveDst =
|
|
51751
|
-
|
|
51785
|
+
const archiveDst = path38.join(dataDir, ".legacy", `listener-${pid}`);
|
|
51786
|
+
fs37.mkdirSync(archiveDst, { recursive: true });
|
|
51752
51787
|
for (const file of readdirSafe(listenerSrc)) {
|
|
51753
51788
|
if (!file.endsWith(".json")) continue;
|
|
51754
|
-
|
|
51789
|
+
fs37.renameSync(path38.join(listenerSrc, file), path38.join(archiveDst, file));
|
|
51755
51790
|
archivedListener += 1;
|
|
51756
51791
|
}
|
|
51757
51792
|
rmdirIfEmpty(listenerSrc);
|
|
51758
51793
|
}
|
|
51759
51794
|
rmdirIfEmpty(personaDir);
|
|
51760
51795
|
}
|
|
51761
|
-
|
|
51762
|
-
|
|
51796
|
+
fs37.mkdirSync(sessionsDir, { recursive: true });
|
|
51797
|
+
fs37.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
|
|
51763
51798
|
return {
|
|
51764
51799
|
skipped: false,
|
|
51765
51800
|
flagWritten: true,
|
|
@@ -51770,7 +51805,7 @@ function migrateFlattenSessions(opts) {
|
|
|
51770
51805
|
}
|
|
51771
51806
|
function existsSync3(p2) {
|
|
51772
51807
|
try {
|
|
51773
|
-
|
|
51808
|
+
fs37.statSync(p2);
|
|
51774
51809
|
return true;
|
|
51775
51810
|
} catch {
|
|
51776
51811
|
return false;
|
|
@@ -51778,31 +51813,31 @@ function existsSync3(p2) {
|
|
|
51778
51813
|
}
|
|
51779
51814
|
function isDir(p2) {
|
|
51780
51815
|
try {
|
|
51781
|
-
return
|
|
51816
|
+
return fs37.statSync(p2).isDirectory();
|
|
51782
51817
|
} catch {
|
|
51783
51818
|
return false;
|
|
51784
51819
|
}
|
|
51785
51820
|
}
|
|
51786
51821
|
function readdirSafe(p2) {
|
|
51787
51822
|
try {
|
|
51788
|
-
return
|
|
51823
|
+
return fs37.readdirSync(p2);
|
|
51789
51824
|
} catch {
|
|
51790
51825
|
return [];
|
|
51791
51826
|
}
|
|
51792
51827
|
}
|
|
51793
51828
|
function rmdirIfEmpty(p2) {
|
|
51794
51829
|
try {
|
|
51795
|
-
|
|
51830
|
+
fs37.rmdirSync(p2);
|
|
51796
51831
|
} catch {
|
|
51797
51832
|
}
|
|
51798
51833
|
}
|
|
51799
51834
|
|
|
51800
51835
|
// src/transport/http-router.ts
|
|
51801
|
-
var
|
|
51802
|
-
var
|
|
51836
|
+
var import_node_fs31 = __toESM(require("fs"), 1);
|
|
51837
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
51803
51838
|
|
|
51804
51839
|
// src/attachment/mime.ts
|
|
51805
|
-
var
|
|
51840
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
51806
51841
|
var TEXT_PLAIN = "text/plain; charset=utf-8";
|
|
51807
51842
|
var EXT_TO_NATIVE_MIME = {
|
|
51808
51843
|
// 图片
|
|
@@ -51909,7 +51944,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
51909
51944
|
".mk"
|
|
51910
51945
|
]);
|
|
51911
51946
|
function lookupMime(filePathOrName) {
|
|
51912
|
-
const ext =
|
|
51947
|
+
const ext = import_node_path31.default.extname(filePathOrName).toLowerCase();
|
|
51913
51948
|
if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
|
|
51914
51949
|
if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
|
|
51915
51950
|
return "application/octet-stream";
|
|
@@ -51978,8 +52013,8 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
|
|
|
51978
52013
|
}
|
|
51979
52014
|
|
|
51980
52015
|
// src/attachment/upload.ts
|
|
51981
|
-
var
|
|
51982
|
-
var
|
|
52016
|
+
var import_node_fs30 = __toESM(require("fs"), 1);
|
|
52017
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
51983
52018
|
var import_node_crypto8 = __toESM(require("crypto"), 1);
|
|
51984
52019
|
var import_promises3 = require("stream/promises");
|
|
51985
52020
|
var UploadError = class extends Error {
|
|
@@ -51991,22 +52026,22 @@ var UploadError = class extends Error {
|
|
|
51991
52026
|
code;
|
|
51992
52027
|
};
|
|
51993
52028
|
function assertValidFileName(fileName) {
|
|
51994
|
-
if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !==
|
|
52029
|
+
if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path32.default.basename(fileName)) {
|
|
51995
52030
|
throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
|
|
51996
52031
|
}
|
|
51997
52032
|
}
|
|
51998
52033
|
var HASH_PREFIX_LEN = 16;
|
|
51999
52034
|
async function writeUploadedAttachment(args) {
|
|
52000
52035
|
assertValidFileName(args.fileName);
|
|
52001
|
-
const attachmentsRoot =
|
|
52036
|
+
const attachmentsRoot = import_node_path32.default.join(args.sessionDir, ".attachments");
|
|
52002
52037
|
try {
|
|
52003
|
-
|
|
52038
|
+
import_node_fs30.default.mkdirSync(attachmentsRoot, { recursive: true });
|
|
52004
52039
|
} catch (err) {
|
|
52005
52040
|
throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
|
|
52006
52041
|
}
|
|
52007
52042
|
const hasher = import_node_crypto8.default.createHash("sha256");
|
|
52008
52043
|
let actualSize = 0;
|
|
52009
|
-
const tmpPath =
|
|
52044
|
+
const tmpPath = import_node_path32.default.join(
|
|
52010
52045
|
attachmentsRoot,
|
|
52011
52046
|
`.upload-${process.pid}-${Date.now()}-${import_node_crypto8.default.randomBytes(4).toString("hex")}`
|
|
52012
52047
|
);
|
|
@@ -52021,18 +52056,18 @@ async function writeUploadedAttachment(args) {
|
|
|
52021
52056
|
yield buf;
|
|
52022
52057
|
}
|
|
52023
52058
|
},
|
|
52024
|
-
|
|
52059
|
+
import_node_fs30.default.createWriteStream(tmpPath, { mode: 384 })
|
|
52025
52060
|
);
|
|
52026
52061
|
} catch (err) {
|
|
52027
52062
|
try {
|
|
52028
|
-
|
|
52063
|
+
import_node_fs30.default.unlinkSync(tmpPath);
|
|
52029
52064
|
} catch {
|
|
52030
52065
|
}
|
|
52031
52066
|
throw new UploadError("STORAGE_ERROR", `write failed: ${err.message}`);
|
|
52032
52067
|
}
|
|
52033
52068
|
if (actualSize !== args.contentLength) {
|
|
52034
52069
|
try {
|
|
52035
|
-
|
|
52070
|
+
import_node_fs30.default.unlinkSync(tmpPath);
|
|
52036
52071
|
} catch {
|
|
52037
52072
|
}
|
|
52038
52073
|
throw new UploadError(
|
|
@@ -52041,35 +52076,35 @@ async function writeUploadedAttachment(args) {
|
|
|
52041
52076
|
);
|
|
52042
52077
|
}
|
|
52043
52078
|
const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
|
|
52044
|
-
const hashDir =
|
|
52079
|
+
const hashDir = import_node_path32.default.join(attachmentsRoot, attachmentId);
|
|
52045
52080
|
let finalFileName;
|
|
52046
52081
|
let hashDirExists = false;
|
|
52047
52082
|
try {
|
|
52048
|
-
hashDirExists =
|
|
52083
|
+
hashDirExists = import_node_fs30.default.statSync(hashDir).isDirectory();
|
|
52049
52084
|
} catch {
|
|
52050
52085
|
}
|
|
52051
52086
|
if (hashDirExists) {
|
|
52052
|
-
const existing =
|
|
52087
|
+
const existing = import_node_fs30.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
|
|
52053
52088
|
finalFileName = existing[0] ?? args.fileName;
|
|
52054
52089
|
try {
|
|
52055
|
-
|
|
52090
|
+
import_node_fs30.default.unlinkSync(tmpPath);
|
|
52056
52091
|
} catch {
|
|
52057
52092
|
}
|
|
52058
52093
|
} else {
|
|
52059
52094
|
try {
|
|
52060
|
-
|
|
52095
|
+
import_node_fs30.default.mkdirSync(hashDir, { recursive: true });
|
|
52061
52096
|
finalFileName = args.fileName;
|
|
52062
|
-
|
|
52097
|
+
import_node_fs30.default.renameSync(tmpPath, import_node_path32.default.join(hashDir, finalFileName));
|
|
52063
52098
|
} catch (err) {
|
|
52064
52099
|
try {
|
|
52065
|
-
|
|
52100
|
+
import_node_fs30.default.unlinkSync(tmpPath);
|
|
52066
52101
|
} catch {
|
|
52067
52102
|
}
|
|
52068
52103
|
throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
|
|
52069
52104
|
}
|
|
52070
52105
|
}
|
|
52071
|
-
const absPath =
|
|
52072
|
-
const relPath =
|
|
52106
|
+
const absPath = import_node_path32.default.join(hashDir, finalFileName);
|
|
52107
|
+
const relPath = import_node_path32.default.relative(args.sessionDir, absPath);
|
|
52073
52108
|
args.groupFileStore.upsert(args.scope, args.sessionId, {
|
|
52074
52109
|
relPath: absPath,
|
|
52075
52110
|
// 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
|
|
@@ -52083,7 +52118,7 @@ async function writeUploadedAttachment(args) {
|
|
|
52083
52118
|
|
|
52084
52119
|
// src/extension/import.ts
|
|
52085
52120
|
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
52086
|
-
var
|
|
52121
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
52087
52122
|
var import_node_os12 = __toESM(require("os"), 1);
|
|
52088
52123
|
var import_jszip = __toESM(require_lib3(), 1);
|
|
52089
52124
|
var ImportError = class extends Error {
|
|
@@ -52101,7 +52136,7 @@ async function importZip(buf, root) {
|
|
|
52101
52136
|
throw new ImportError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
52102
52137
|
}
|
|
52103
52138
|
for (const name of Object.keys(zip.files)) {
|
|
52104
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
52139
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path33.default.isAbsolute(name)) {
|
|
52105
52140
|
throw new ImportError("ZIP_INVALID", `unsafe zip entry path: ${name}`);
|
|
52106
52141
|
}
|
|
52107
52142
|
}
|
|
@@ -52134,7 +52169,7 @@ async function importZip(buf, root) {
|
|
|
52134
52169
|
);
|
|
52135
52170
|
}
|
|
52136
52171
|
}
|
|
52137
|
-
const destDir =
|
|
52172
|
+
const destDir = import_node_path33.default.join(root, manifest.id);
|
|
52138
52173
|
let destExists = false;
|
|
52139
52174
|
try {
|
|
52140
52175
|
await import_promises4.default.access(destDir);
|
|
@@ -52144,15 +52179,15 @@ async function importZip(buf, root) {
|
|
|
52144
52179
|
if (destExists) {
|
|
52145
52180
|
throw new ImportError("ALREADY_EXISTS", `extension ${manifest.id} already installed`);
|
|
52146
52181
|
}
|
|
52147
|
-
const stage = await import_promises4.default.mkdtemp(
|
|
52182
|
+
const stage = await import_promises4.default.mkdtemp(import_node_path33.default.join(import_node_os12.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
|
|
52148
52183
|
try {
|
|
52149
52184
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
52150
|
-
const dest =
|
|
52185
|
+
const dest = import_node_path33.default.join(stage, name);
|
|
52151
52186
|
if (entry.dir) {
|
|
52152
52187
|
await import_promises4.default.mkdir(dest, { recursive: true });
|
|
52153
52188
|
continue;
|
|
52154
52189
|
}
|
|
52155
|
-
await import_promises4.default.mkdir(
|
|
52190
|
+
await import_promises4.default.mkdir(import_node_path33.default.dirname(dest), { recursive: true });
|
|
52156
52191
|
const content = await entry.async("nodebuffer");
|
|
52157
52192
|
await import_promises4.default.writeFile(dest, content);
|
|
52158
52193
|
}
|
|
@@ -52167,7 +52202,6 @@ async function importZip(buf, root) {
|
|
|
52167
52202
|
}
|
|
52168
52203
|
|
|
52169
52204
|
// src/transport/http-router.ts
|
|
52170
|
-
var RPC_ROUTE_PREFIX = "/api/rpc/";
|
|
52171
52205
|
var RPC_BODY_MAX_BYTES = 1 * 1024 * 1024;
|
|
52172
52206
|
var SHARE_UI_ASSETS_PREFIX = "/share-ui/assets/";
|
|
52173
52207
|
var SHARE_UI_ASSET_MIME = {
|
|
@@ -52183,11 +52217,9 @@ var SHARE_UI_ASSET_MIME = {
|
|
|
52183
52217
|
".wasm": "application/wasm"
|
|
52184
52218
|
};
|
|
52185
52219
|
function shareUiAssetMime(filePath) {
|
|
52186
|
-
const ext =
|
|
52220
|
+
const ext = import_node_path34.default.extname(filePath).toLowerCase();
|
|
52187
52221
|
return SHARE_UI_ASSET_MIME[ext] ?? lookupMime(filePath);
|
|
52188
52222
|
}
|
|
52189
|
-
var DISPATCH_HEARTBEAT_MS = 25e3;
|
|
52190
|
-
var DISPATCH_HOLD_METHOD = "personaDispatch:run";
|
|
52191
52223
|
var AUTHED_RPC_ROUTE_PREFIX = "/rpc/";
|
|
52192
52224
|
var AUTHED_RPC_CORS_HEADERS = {
|
|
52193
52225
|
"Access-Control-Allow-Origin": "*",
|
|
@@ -52210,10 +52242,6 @@ function authedRpcErrorStatus(code) {
|
|
|
52210
52242
|
return 400;
|
|
52211
52243
|
}
|
|
52212
52244
|
}
|
|
52213
|
-
function isLoopbackAddress(addr) {
|
|
52214
|
-
if (!addr) return false;
|
|
52215
|
-
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1" || addr.startsWith("127.");
|
|
52216
|
-
}
|
|
52217
52245
|
async function readJsonBody(req, max) {
|
|
52218
52246
|
return await new Promise((resolve6, reject) => {
|
|
52219
52247
|
let size = 0;
|
|
@@ -52270,7 +52298,7 @@ function isValidUploadFileName(fileName) {
|
|
|
52270
52298
|
if (fileName === "." || fileName === "..") return false;
|
|
52271
52299
|
if (fileName.startsWith(".")) return false;
|
|
52272
52300
|
if (fileName.includes("/") || fileName.includes("\\")) return false;
|
|
52273
|
-
return fileName ===
|
|
52301
|
+
return fileName === import_node_path34.default.basename(fileName);
|
|
52274
52302
|
}
|
|
52275
52303
|
function createHttpRouter(deps) {
|
|
52276
52304
|
return async (req, res) => {
|
|
@@ -52342,7 +52370,7 @@ function createHttpRouter(deps) {
|
|
|
52342
52370
|
}
|
|
52343
52371
|
let buf;
|
|
52344
52372
|
try {
|
|
52345
|
-
buf = await
|
|
52373
|
+
buf = await import_node_fs31.default.promises.readFile(abs);
|
|
52346
52374
|
} catch {
|
|
52347
52375
|
sendJson(res, 404, { code: "NOT_FOUND", message: "asset not found" });
|
|
52348
52376
|
return true;
|
|
@@ -52390,57 +52418,6 @@ function createHttpRouter(deps) {
|
|
|
52390
52418
|
return true;
|
|
52391
52419
|
}
|
|
52392
52420
|
const dispatchArgs = typeof body === "object" && body != null ? body : {};
|
|
52393
|
-
if (method === DISPATCH_HOLD_METHOD) {
|
|
52394
|
-
const heartbeatMs = deps.dispatchHeartbeatMs ?? DISPATCH_HEARTBEAT_MS;
|
|
52395
|
-
let headSent = false;
|
|
52396
|
-
let hbInterval;
|
|
52397
|
-
const startStreaming = () => {
|
|
52398
|
-
headSent = true;
|
|
52399
|
-
req.socket?.setTimeout?.(0);
|
|
52400
|
-
res.socket?.setTimeout?.(0);
|
|
52401
|
-
res.writeHead(200, {
|
|
52402
|
-
"Content-Type": "application/json; charset=utf-8",
|
|
52403
|
-
"Transfer-Encoding": "chunked",
|
|
52404
|
-
// nginx 专用:禁用该响应缓冲,让心跳字节即时 flush 到上游 read(否则被攒着不重置计时)
|
|
52405
|
-
"X-Accel-Buffering": "no",
|
|
52406
|
-
...AUTHED_RPC_CORS_HEADERS
|
|
52407
|
-
});
|
|
52408
|
-
res.write(" ");
|
|
52409
|
-
hbInterval = setInterval(() => {
|
|
52410
|
-
try {
|
|
52411
|
-
res.write(" ");
|
|
52412
|
-
} catch {
|
|
52413
|
-
}
|
|
52414
|
-
}, heartbeatMs);
|
|
52415
|
-
};
|
|
52416
|
-
const graceTimer = setTimeout(startStreaming, heartbeatMs);
|
|
52417
|
-
const cleanup = () => {
|
|
52418
|
-
clearTimeout(graceTimer);
|
|
52419
|
-
if (hbInterval) clearInterval(hbInterval);
|
|
52420
|
-
};
|
|
52421
|
-
const safeEnd = (payload) => {
|
|
52422
|
-
if (res.writableEnded || res.destroyed) return;
|
|
52423
|
-
try {
|
|
52424
|
-
res.end(payload);
|
|
52425
|
-
} catch {
|
|
52426
|
-
}
|
|
52427
|
-
};
|
|
52428
|
-
res.on("close", cleanup);
|
|
52429
|
-
try {
|
|
52430
|
-
const result = await deps.authedRpc.dispatch(method, dispatchArgs, auth.context);
|
|
52431
|
-
cleanup();
|
|
52432
|
-
if (headSent) safeEnd(JSON.stringify({ ok: true, result: result.response }));
|
|
52433
|
-
else sendJson(res, 200, { ok: true, result: result.response }, AUTHED_RPC_CORS_HEADERS);
|
|
52434
|
-
} catch (err) {
|
|
52435
|
-
cleanup();
|
|
52436
|
-
const e = err;
|
|
52437
|
-
const code = e?.code ?? "INTERNAL";
|
|
52438
|
-
const payload = { ok: false, error: code, message: e?.message ?? String(err) };
|
|
52439
|
-
if (headSent) safeEnd(JSON.stringify(payload));
|
|
52440
|
-
else sendJson(res, authedRpcErrorStatus(code), payload, AUTHED_RPC_CORS_HEADERS);
|
|
52441
|
-
}
|
|
52442
|
-
return true;
|
|
52443
|
-
}
|
|
52444
52421
|
try {
|
|
52445
52422
|
const result = await deps.authedRpc.dispatch(method, dispatchArgs, auth.context);
|
|
52446
52423
|
sendJson(res, 200, { ok: true, result: result.response }, AUTHED_RPC_CORS_HEADERS);
|
|
@@ -52451,34 +52428,6 @@ function createHttpRouter(deps) {
|
|
|
52451
52428
|
}
|
|
52452
52429
|
return true;
|
|
52453
52430
|
}
|
|
52454
|
-
if (url.pathname.startsWith(RPC_ROUTE_PREFIX)) {
|
|
52455
|
-
if (!deps.rpcDispatcher) {
|
|
52456
|
-
sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "HTTP RPC adapter not wired" });
|
|
52457
|
-
return true;
|
|
52458
|
-
}
|
|
52459
|
-
if (req.method !== "POST") {
|
|
52460
|
-
sendJson(res, 405, { code: "METHOD_NOT_ALLOWED", message: "POST only" });
|
|
52461
|
-
return true;
|
|
52462
|
-
}
|
|
52463
|
-
if (!isLoopbackAddress(req.socket.remoteAddress)) {
|
|
52464
|
-
sendJson(res, 403, { code: "NOT_LOOPBACK", message: "RPC adapter only accepts loopback connections" });
|
|
52465
|
-
return true;
|
|
52466
|
-
}
|
|
52467
|
-
req.socket.setTimeout(0);
|
|
52468
|
-
res.socket?.setTimeout(0);
|
|
52469
|
-
const method = url.pathname.slice(RPC_ROUTE_PREFIX.length).split("?")[0] ?? "";
|
|
52470
|
-
const sessionId = req.headers["x-clawd-session-id"] ?? void 0;
|
|
52471
|
-
let body;
|
|
52472
|
-
try {
|
|
52473
|
-
body = await readJsonBody(req, RPC_BODY_MAX_BYTES);
|
|
52474
|
-
} catch (err) {
|
|
52475
|
-
sendJson(res, 400, { code: "INVALID_BODY", message: err instanceof Error ? err.message : String(err) });
|
|
52476
|
-
return true;
|
|
52477
|
-
}
|
|
52478
|
-
const result = await handleRpcRequest({ method, body, sessionId, dispatcher: deps.rpcDispatcher });
|
|
52479
|
-
sendJson(res, result.status, result.body);
|
|
52480
|
-
return true;
|
|
52481
|
-
}
|
|
52482
52431
|
if (url.pathname.startsWith("/preview/")) {
|
|
52483
52432
|
const port = matchPreviewPort(url.pathname);
|
|
52484
52433
|
if (port != null && isPreviewPortAllowed(port, { allowedPorts: deps.previewPorts ?? [] })) {
|
|
@@ -52566,7 +52515,7 @@ function createHttpRouter(deps) {
|
|
|
52566
52515
|
sendHtml(res, statusByCode[r.code], loader.renderErrorHtml(r.code, msgByCode[r.code]));
|
|
52567
52516
|
return true;
|
|
52568
52517
|
}
|
|
52569
|
-
sendHtml(res, 200, loader.renderViewerHtml(
|
|
52518
|
+
sendHtml(res, 200, loader.renderViewerHtml(import_node_path34.default.basename(r.absPath)));
|
|
52570
52519
|
return true;
|
|
52571
52520
|
}
|
|
52572
52521
|
const ctx = deps.authResolver.resolveFromHeader(
|
|
@@ -52687,7 +52636,7 @@ function createHttpRouter(deps) {
|
|
|
52687
52636
|
return true;
|
|
52688
52637
|
}
|
|
52689
52638
|
let absPath;
|
|
52690
|
-
if (
|
|
52639
|
+
if (import_node_path34.default.isAbsolute(pathParam)) {
|
|
52691
52640
|
absPath = pathParam;
|
|
52692
52641
|
} else if (deps.sessionStore) {
|
|
52693
52642
|
const file = deps.sessionStore.read(sid);
|
|
@@ -52695,7 +52644,7 @@ function createHttpRouter(deps) {
|
|
|
52695
52644
|
sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sid} not found` });
|
|
52696
52645
|
return true;
|
|
52697
52646
|
}
|
|
52698
|
-
absPath =
|
|
52647
|
+
absPath = import_node_path34.default.join(file.cwd, pathParam);
|
|
52699
52648
|
} else {
|
|
52700
52649
|
sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "sessionStore not wired" }));
|
|
52701
52650
|
return true;
|
|
@@ -52782,7 +52731,7 @@ function withCtx(ctx, body) {
|
|
|
52782
52731
|
function streamFile(res, absPath, logger) {
|
|
52783
52732
|
let stat;
|
|
52784
52733
|
try {
|
|
52785
|
-
stat =
|
|
52734
|
+
stat = import_node_fs31.default.statSync(absPath);
|
|
52786
52735
|
} catch (err) {
|
|
52787
52736
|
const code = err?.code;
|
|
52788
52737
|
if (code === "ENOENT") {
|
|
@@ -52797,7 +52746,7 @@ function streamFile(res, absPath, logger) {
|
|
|
52797
52746
|
return;
|
|
52798
52747
|
}
|
|
52799
52748
|
const mime = lookupMime(absPath);
|
|
52800
|
-
const basename =
|
|
52749
|
+
const basename = import_node_path34.default.basename(absPath);
|
|
52801
52750
|
res.writeHead(200, {
|
|
52802
52751
|
"Content-Type": mime,
|
|
52803
52752
|
"Content-Length": String(stat.size),
|
|
@@ -52805,7 +52754,7 @@ function streamFile(res, absPath, logger) {
|
|
|
52805
52754
|
// 防止浏览器把任意 mime 当 html 渲染
|
|
52806
52755
|
"X-Content-Type-Options": "nosniff"
|
|
52807
52756
|
});
|
|
52808
|
-
const stream =
|
|
52757
|
+
const stream = import_node_fs31.default.createReadStream(absPath);
|
|
52809
52758
|
stream.on("error", (err) => {
|
|
52810
52759
|
logger?.warn("streamFile read error", { absPath, err: err.message });
|
|
52811
52760
|
res.destroy();
|
|
@@ -52814,8 +52763,8 @@ function streamFile(res, absPath, logger) {
|
|
|
52814
52763
|
}
|
|
52815
52764
|
|
|
52816
52765
|
// src/attachment/gc.ts
|
|
52817
|
-
var
|
|
52818
|
-
var
|
|
52766
|
+
var import_node_fs32 = __toESM(require("fs"), 1);
|
|
52767
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
52819
52768
|
var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
|
|
52820
52769
|
function runAttachmentGc(args) {
|
|
52821
52770
|
const now = (args.now ?? Date.now)();
|
|
@@ -52824,38 +52773,38 @@ function runAttachmentGc(args) {
|
|
|
52824
52773
|
for (const { scope, sessionId } of args.sessionScopes) {
|
|
52825
52774
|
for (const entry of args.groupFileStore.list(scope, sessionId)) {
|
|
52826
52775
|
if (entry.stale) continue;
|
|
52827
|
-
if (
|
|
52776
|
+
if (import_node_path35.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
|
|
52828
52777
|
}
|
|
52829
52778
|
}
|
|
52830
52779
|
for (const { scope, sessionId } of args.sessionScopes) {
|
|
52831
|
-
const sessionDir = args.getSessionCwd?.(sessionId) ??
|
|
52780
|
+
const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path35.default.join(
|
|
52832
52781
|
args.dataDir,
|
|
52833
52782
|
"sessions",
|
|
52834
52783
|
...scopeSubPath(scope).map(safeFileName),
|
|
52835
52784
|
safeFileName(sessionId)
|
|
52836
52785
|
);
|
|
52837
|
-
const attRoot =
|
|
52786
|
+
const attRoot = import_node_path35.default.join(sessionDir, ".attachments");
|
|
52838
52787
|
let hashDirs;
|
|
52839
52788
|
try {
|
|
52840
|
-
hashDirs =
|
|
52789
|
+
hashDirs = import_node_fs32.default.readdirSync(attRoot);
|
|
52841
52790
|
} catch (err) {
|
|
52842
52791
|
if (err.code === "ENOENT") continue;
|
|
52843
52792
|
args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
|
|
52844
52793
|
continue;
|
|
52845
52794
|
}
|
|
52846
52795
|
for (const hashDir of hashDirs) {
|
|
52847
|
-
const hashDirAbs =
|
|
52796
|
+
const hashDirAbs = import_node_path35.default.join(attRoot, hashDir);
|
|
52848
52797
|
let files;
|
|
52849
52798
|
try {
|
|
52850
|
-
files =
|
|
52799
|
+
files = import_node_fs32.default.readdirSync(hashDirAbs);
|
|
52851
52800
|
} catch {
|
|
52852
52801
|
continue;
|
|
52853
52802
|
}
|
|
52854
52803
|
for (const name of files) {
|
|
52855
|
-
const file =
|
|
52804
|
+
const file = import_node_path35.default.join(hashDirAbs, name);
|
|
52856
52805
|
let stat;
|
|
52857
52806
|
try {
|
|
52858
|
-
stat =
|
|
52807
|
+
stat = import_node_fs32.default.statSync(file);
|
|
52859
52808
|
} catch {
|
|
52860
52809
|
continue;
|
|
52861
52810
|
}
|
|
@@ -52864,26 +52813,26 @@ function runAttachmentGc(args) {
|
|
|
52864
52813
|
if (age < ttlMs) continue;
|
|
52865
52814
|
if (liveAbs.has(file)) continue;
|
|
52866
52815
|
try {
|
|
52867
|
-
|
|
52816
|
+
import_node_fs32.default.unlinkSync(file);
|
|
52868
52817
|
} catch (err) {
|
|
52869
52818
|
args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
|
|
52870
52819
|
}
|
|
52871
52820
|
}
|
|
52872
52821
|
try {
|
|
52873
|
-
if (
|
|
52822
|
+
if (import_node_fs32.default.readdirSync(hashDirAbs).length === 0) import_node_fs32.default.rmdirSync(hashDirAbs);
|
|
52874
52823
|
} catch {
|
|
52875
52824
|
}
|
|
52876
52825
|
}
|
|
52877
52826
|
try {
|
|
52878
|
-
if (
|
|
52827
|
+
if (import_node_fs32.default.readdirSync(attRoot).length === 0) import_node_fs32.default.rmdirSync(attRoot);
|
|
52879
52828
|
} catch {
|
|
52880
52829
|
}
|
|
52881
52830
|
}
|
|
52882
52831
|
}
|
|
52883
52832
|
|
|
52884
52833
|
// src/attachment/group.ts
|
|
52885
|
-
var
|
|
52886
|
-
var
|
|
52834
|
+
var import_node_fs33 = __toESM(require("fs"), 1);
|
|
52835
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
52887
52836
|
var import_node_crypto9 = __toESM(require("crypto"), 1);
|
|
52888
52837
|
init_protocol();
|
|
52889
52838
|
var GroupFileStore = class {
|
|
@@ -52895,11 +52844,11 @@ var GroupFileStore = class {
|
|
|
52895
52844
|
this.logger = opts.logger;
|
|
52896
52845
|
}
|
|
52897
52846
|
rootForScope(scope) {
|
|
52898
|
-
return
|
|
52847
|
+
return import_node_path36.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
|
|
52899
52848
|
}
|
|
52900
52849
|
/** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
|
|
52901
52850
|
filePath(scope, sessionId) {
|
|
52902
|
-
return
|
|
52851
|
+
return import_node_path36.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
|
|
52903
52852
|
}
|
|
52904
52853
|
cacheKey(scope, sessionId) {
|
|
52905
52854
|
return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
|
|
@@ -52908,7 +52857,7 @@ var GroupFileStore = class {
|
|
|
52908
52857
|
readFile(scope, sessionId) {
|
|
52909
52858
|
const file = this.filePath(scope, sessionId);
|
|
52910
52859
|
try {
|
|
52911
|
-
const raw =
|
|
52860
|
+
const raw = import_node_fs33.default.readFileSync(file, "utf8");
|
|
52912
52861
|
const parsed = JSON.parse(raw);
|
|
52913
52862
|
if (!Array.isArray(parsed)) {
|
|
52914
52863
|
this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
|
|
@@ -52934,10 +52883,10 @@ var GroupFileStore = class {
|
|
|
52934
52883
|
}
|
|
52935
52884
|
writeFile(scope, sessionId, entries) {
|
|
52936
52885
|
const file = this.filePath(scope, sessionId);
|
|
52937
|
-
|
|
52886
|
+
import_node_fs33.default.mkdirSync(import_node_path36.default.dirname(file), { recursive: true });
|
|
52938
52887
|
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
52939
|
-
|
|
52940
|
-
|
|
52888
|
+
import_node_fs33.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
|
|
52889
|
+
import_node_fs33.default.renameSync(tmp, file);
|
|
52941
52890
|
}
|
|
52942
52891
|
/** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
|
|
52943
52892
|
list(scope, sessionId) {
|
|
@@ -53023,10 +52972,10 @@ var GroupFileStore = class {
|
|
|
53023
52972
|
};
|
|
53024
52973
|
|
|
53025
52974
|
// src/discovery/state-file.ts
|
|
53026
|
-
var
|
|
53027
|
-
var
|
|
52975
|
+
var import_node_fs34 = __toESM(require("fs"), 1);
|
|
52976
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
53028
52977
|
function defaultStateFilePath(dataDir) {
|
|
53029
|
-
return
|
|
52978
|
+
return import_node_path37.default.join(dataDir, "state.json");
|
|
53030
52979
|
}
|
|
53031
52980
|
function isPidAlive(pid) {
|
|
53032
52981
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
@@ -53048,7 +52997,7 @@ var StateFileManager = class {
|
|
|
53048
52997
|
}
|
|
53049
52998
|
read() {
|
|
53050
52999
|
try {
|
|
53051
|
-
const raw =
|
|
53000
|
+
const raw = import_node_fs34.default.readFileSync(this.file, "utf8");
|
|
53052
53001
|
const parsed = JSON.parse(raw);
|
|
53053
53002
|
return parsed;
|
|
53054
53003
|
} catch {
|
|
@@ -53062,20 +53011,20 @@ var StateFileManager = class {
|
|
|
53062
53011
|
return { status: "stale", existing };
|
|
53063
53012
|
}
|
|
53064
53013
|
write(state) {
|
|
53065
|
-
|
|
53014
|
+
import_node_fs34.default.mkdirSync(import_node_path37.default.dirname(this.file), { recursive: true });
|
|
53066
53015
|
const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
|
|
53067
|
-
|
|
53068
|
-
|
|
53016
|
+
import_node_fs34.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
|
|
53017
|
+
import_node_fs34.default.renameSync(tmp, this.file);
|
|
53069
53018
|
if (process.platform !== "win32") {
|
|
53070
53019
|
try {
|
|
53071
|
-
|
|
53020
|
+
import_node_fs34.default.chmodSync(this.file, 384);
|
|
53072
53021
|
} catch {
|
|
53073
53022
|
}
|
|
53074
53023
|
}
|
|
53075
53024
|
}
|
|
53076
53025
|
delete() {
|
|
53077
53026
|
try {
|
|
53078
|
-
|
|
53027
|
+
import_node_fs34.default.unlinkSync(this.file);
|
|
53079
53028
|
} catch {
|
|
53080
53029
|
}
|
|
53081
53030
|
}
|
|
@@ -53089,14 +53038,14 @@ function readSpawnedByDesktopFromEnv(env = process.env) {
|
|
|
53089
53038
|
}
|
|
53090
53039
|
|
|
53091
53040
|
// src/tunnel/tunnel-manager.ts
|
|
53092
|
-
var
|
|
53093
|
-
var
|
|
53041
|
+
var import_node_fs38 = __toESM(require("fs"), 1);
|
|
53042
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
53094
53043
|
var import_node_crypto10 = __toESM(require("crypto"), 1);
|
|
53095
53044
|
var import_node_child_process10 = require("child_process");
|
|
53096
53045
|
|
|
53097
53046
|
// src/tunnel/tunnel-store.ts
|
|
53098
|
-
var
|
|
53099
|
-
var
|
|
53047
|
+
var import_node_fs35 = __toESM(require("fs"), 1);
|
|
53048
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
53100
53049
|
var TunnelStore = class {
|
|
53101
53050
|
constructor(filePath) {
|
|
53102
53051
|
this.filePath = filePath;
|
|
@@ -53104,7 +53053,7 @@ var TunnelStore = class {
|
|
|
53104
53053
|
filePath;
|
|
53105
53054
|
async get() {
|
|
53106
53055
|
try {
|
|
53107
|
-
const raw = await
|
|
53056
|
+
const raw = await import_node_fs35.default.promises.readFile(this.filePath, "utf8");
|
|
53108
53057
|
const obj = JSON.parse(raw);
|
|
53109
53058
|
if (!isPersistedTunnel(obj)) return null;
|
|
53110
53059
|
return obj;
|
|
@@ -53115,22 +53064,22 @@ var TunnelStore = class {
|
|
|
53115
53064
|
}
|
|
53116
53065
|
}
|
|
53117
53066
|
async set(v2) {
|
|
53118
|
-
const dir =
|
|
53119
|
-
await
|
|
53067
|
+
const dir = import_node_path38.default.dirname(this.filePath);
|
|
53068
|
+
await import_node_fs35.default.promises.mkdir(dir, { recursive: true });
|
|
53120
53069
|
const data = JSON.stringify(v2, null, 2);
|
|
53121
53070
|
const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
|
|
53122
|
-
await
|
|
53071
|
+
await import_node_fs35.default.promises.writeFile(tmp, data, { mode: 384 });
|
|
53123
53072
|
if (process.platform !== "win32") {
|
|
53124
53073
|
try {
|
|
53125
|
-
await
|
|
53074
|
+
await import_node_fs35.default.promises.chmod(tmp, 384);
|
|
53126
53075
|
} catch {
|
|
53127
53076
|
}
|
|
53128
53077
|
}
|
|
53129
|
-
await
|
|
53078
|
+
await import_node_fs35.default.promises.rename(tmp, this.filePath);
|
|
53130
53079
|
}
|
|
53131
53080
|
async clear() {
|
|
53132
53081
|
try {
|
|
53133
|
-
await
|
|
53082
|
+
await import_node_fs35.default.promises.unlink(this.filePath);
|
|
53134
53083
|
} catch (err) {
|
|
53135
53084
|
const code = err?.code;
|
|
53136
53085
|
if (code !== "ENOENT") throw err;
|
|
@@ -53225,9 +53174,9 @@ function escape(v2) {
|
|
|
53225
53174
|
}
|
|
53226
53175
|
|
|
53227
53176
|
// src/tunnel/frpc-binary.ts
|
|
53228
|
-
var
|
|
53177
|
+
var import_node_fs36 = __toESM(require("fs"), 1);
|
|
53229
53178
|
var import_node_os13 = __toESM(require("os"), 1);
|
|
53230
|
-
var
|
|
53179
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
53231
53180
|
var import_node_child_process8 = require("child_process");
|
|
53232
53181
|
var import_node_stream3 = require("stream");
|
|
53233
53182
|
var import_promises5 = require("stream/promises");
|
|
@@ -53259,20 +53208,20 @@ function frpcDownloadUrl(version2, p2) {
|
|
|
53259
53208
|
}
|
|
53260
53209
|
async function ensureFrpcBinary(opts) {
|
|
53261
53210
|
if (opts.override) {
|
|
53262
|
-
if (!
|
|
53211
|
+
if (!import_node_fs36.default.existsSync(opts.override)) {
|
|
53263
53212
|
throw new Error(`frpc binary not found at override path: ${opts.override}`);
|
|
53264
53213
|
}
|
|
53265
53214
|
return opts.override;
|
|
53266
53215
|
}
|
|
53267
53216
|
const version2 = opts.version ?? FRPC_VERSION;
|
|
53268
53217
|
const platform = opts.platform ?? detectPlatform();
|
|
53269
|
-
const binDir =
|
|
53270
|
-
|
|
53218
|
+
const binDir = import_node_path39.default.join(opts.dataDir, "bin");
|
|
53219
|
+
import_node_fs36.default.mkdirSync(binDir, { recursive: true });
|
|
53271
53220
|
cleanupStaleArtifacts(binDir);
|
|
53272
|
-
const stableBin =
|
|
53273
|
-
if (
|
|
53221
|
+
const stableBin = import_node_path39.default.join(binDir, "frpc");
|
|
53222
|
+
if (import_node_fs36.default.existsSync(stableBin)) return stableBin;
|
|
53274
53223
|
const partialBin = `${stableBin}.partial`;
|
|
53275
|
-
const tarballPath =
|
|
53224
|
+
const tarballPath = import_node_path39.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
|
|
53276
53225
|
try {
|
|
53277
53226
|
const url = frpcDownloadUrl(version2, platform);
|
|
53278
53227
|
await downloadToFile(url, tarballPath, opts.fetchImpl);
|
|
@@ -53281,8 +53230,8 @@ async function ensureFrpcBinary(opts) {
|
|
|
53281
53230
|
} else {
|
|
53282
53231
|
await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
|
|
53283
53232
|
}
|
|
53284
|
-
|
|
53285
|
-
|
|
53233
|
+
import_node_fs36.default.chmodSync(partialBin, 493);
|
|
53234
|
+
import_node_fs36.default.renameSync(partialBin, stableBin);
|
|
53286
53235
|
} finally {
|
|
53287
53236
|
safeUnlink(tarballPath);
|
|
53288
53237
|
safeUnlink(partialBin);
|
|
@@ -53292,15 +53241,15 @@ async function ensureFrpcBinary(opts) {
|
|
|
53292
53241
|
function cleanupStaleArtifacts(binDir) {
|
|
53293
53242
|
let entries;
|
|
53294
53243
|
try {
|
|
53295
|
-
entries =
|
|
53244
|
+
entries = import_node_fs36.default.readdirSync(binDir);
|
|
53296
53245
|
} catch {
|
|
53297
53246
|
return;
|
|
53298
53247
|
}
|
|
53299
53248
|
for (const name of entries) {
|
|
53300
53249
|
if (name.endsWith(".partial") || name.startsWith("extract-")) {
|
|
53301
|
-
const full =
|
|
53250
|
+
const full = import_node_path39.default.join(binDir, name);
|
|
53302
53251
|
try {
|
|
53303
|
-
|
|
53252
|
+
import_node_fs36.default.rmSync(full, { recursive: true, force: true });
|
|
53304
53253
|
} catch {
|
|
53305
53254
|
}
|
|
53306
53255
|
}
|
|
@@ -53308,7 +53257,7 @@ function cleanupStaleArtifacts(binDir) {
|
|
|
53308
53257
|
}
|
|
53309
53258
|
function safeUnlink(p2) {
|
|
53310
53259
|
try {
|
|
53311
|
-
|
|
53260
|
+
import_node_fs36.default.unlinkSync(p2);
|
|
53312
53261
|
} catch {
|
|
53313
53262
|
}
|
|
53314
53263
|
}
|
|
@@ -53319,13 +53268,13 @@ async function downloadToFile(url, dest, fetchImpl) {
|
|
|
53319
53268
|
if (!res.ok || !res.body) {
|
|
53320
53269
|
throw new Error(`download failed: ${res.status} ${res.statusText}`);
|
|
53321
53270
|
}
|
|
53322
|
-
const out =
|
|
53271
|
+
const out = import_node_fs36.default.createWriteStream(dest);
|
|
53323
53272
|
const nodeStream = import_node_stream3.Readable.fromWeb(res.body);
|
|
53324
53273
|
await (0, import_promises5.pipeline)(nodeStream, out);
|
|
53325
53274
|
}
|
|
53326
53275
|
async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
|
|
53327
|
-
const work =
|
|
53328
|
-
|
|
53276
|
+
const work = import_node_path39.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
|
|
53277
|
+
import_node_fs36.default.mkdirSync(work, { recursive: true });
|
|
53329
53278
|
try {
|
|
53330
53279
|
await new Promise((resolve6, reject) => {
|
|
53331
53280
|
const proc = (0, import_node_child_process8.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
|
|
@@ -53333,32 +53282,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
|
|
|
53333
53282
|
proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
|
|
53334
53283
|
});
|
|
53335
53284
|
const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
|
|
53336
|
-
const src =
|
|
53337
|
-
if (!
|
|
53285
|
+
const src = import_node_path39.default.join(work, dirName, "frpc");
|
|
53286
|
+
if (!import_node_fs36.default.existsSync(src)) {
|
|
53338
53287
|
throw new Error(`frpc not found inside tarball at ${src}`);
|
|
53339
53288
|
}
|
|
53340
|
-
|
|
53289
|
+
import_node_fs36.default.copyFileSync(src, destBin);
|
|
53341
53290
|
} finally {
|
|
53342
|
-
|
|
53291
|
+
import_node_fs36.default.rmSync(work, { recursive: true, force: true });
|
|
53343
53292
|
}
|
|
53344
53293
|
}
|
|
53345
53294
|
|
|
53346
53295
|
// src/tunnel/frpc-process.ts
|
|
53347
|
-
var
|
|
53348
|
-
var
|
|
53296
|
+
var import_node_fs37 = __toESM(require("fs"), 1);
|
|
53297
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
53349
53298
|
var import_node_child_process9 = require("child_process");
|
|
53350
53299
|
function frpcPidFilePath(dataDir) {
|
|
53351
|
-
return
|
|
53300
|
+
return import_node_path40.default.join(dataDir, "frpc.pid");
|
|
53352
53301
|
}
|
|
53353
53302
|
function writeFrpcPid(dataDir, pid) {
|
|
53354
53303
|
try {
|
|
53355
|
-
|
|
53304
|
+
import_node_fs37.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
|
|
53356
53305
|
} catch {
|
|
53357
53306
|
}
|
|
53358
53307
|
}
|
|
53359
53308
|
function clearFrpcPid(dataDir) {
|
|
53360
53309
|
try {
|
|
53361
|
-
|
|
53310
|
+
import_node_fs37.default.unlinkSync(frpcPidFilePath(dataDir));
|
|
53362
53311
|
} catch {
|
|
53363
53312
|
}
|
|
53364
53313
|
}
|
|
@@ -53374,7 +53323,7 @@ function defaultIsPidAlive(pid) {
|
|
|
53374
53323
|
}
|
|
53375
53324
|
function defaultReadPidFile(file) {
|
|
53376
53325
|
try {
|
|
53377
|
-
return
|
|
53326
|
+
return import_node_fs37.default.readFileSync(file, "utf8");
|
|
53378
53327
|
} catch {
|
|
53379
53328
|
return null;
|
|
53380
53329
|
}
|
|
@@ -53385,17 +53334,17 @@ function defaultKillPid(pid, signal) {
|
|
|
53385
53334
|
} catch {
|
|
53386
53335
|
}
|
|
53387
53336
|
}
|
|
53388
|
-
function
|
|
53337
|
+
function defaultSleep2(ms) {
|
|
53389
53338
|
return new Promise((r) => setTimeout(r, ms));
|
|
53390
53339
|
}
|
|
53391
53340
|
async function killStaleFrpc(deps) {
|
|
53392
53341
|
const pidFile = frpcPidFilePath(deps.dataDir);
|
|
53393
|
-
const tomlPath =
|
|
53342
|
+
const tomlPath = import_node_path40.default.join(deps.dataDir, "frpc.toml");
|
|
53394
53343
|
const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
|
|
53395
53344
|
const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
|
|
53396
53345
|
const killPid = deps.killPidImpl ?? defaultKillPid;
|
|
53397
53346
|
const scanPids = deps.scanFrpcPidsImpl ?? ((tp) => defaultScanFrpcPidsByCmdline(tp, deps.logger));
|
|
53398
|
-
const sleep2 = deps.sleepImpl ??
|
|
53347
|
+
const sleep2 = deps.sleepImpl ?? defaultSleep2;
|
|
53399
53348
|
const victims = /* @__PURE__ */ new Set();
|
|
53400
53349
|
const raw = readPidFile(pidFile);
|
|
53401
53350
|
if (raw) {
|
|
@@ -53414,7 +53363,7 @@ async function killStaleFrpc(deps) {
|
|
|
53414
53363
|
}
|
|
53415
53364
|
if (victims.size === 0) {
|
|
53416
53365
|
try {
|
|
53417
|
-
|
|
53366
|
+
import_node_fs37.default.unlinkSync(pidFile);
|
|
53418
53367
|
} catch {
|
|
53419
53368
|
}
|
|
53420
53369
|
return;
|
|
@@ -53425,7 +53374,7 @@ async function killStaleFrpc(deps) {
|
|
|
53425
53374
|
}
|
|
53426
53375
|
await sleep2(deps.reapWaitMs ?? 300);
|
|
53427
53376
|
try {
|
|
53428
|
-
|
|
53377
|
+
import_node_fs37.default.unlinkSync(pidFile);
|
|
53429
53378
|
} catch {
|
|
53430
53379
|
}
|
|
53431
53380
|
}
|
|
@@ -53462,7 +53411,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
|
53462
53411
|
var TunnelManager = class {
|
|
53463
53412
|
constructor(deps) {
|
|
53464
53413
|
this.deps = deps;
|
|
53465
|
-
this.store = deps.store ?? new TunnelStore(
|
|
53414
|
+
this.store = deps.store ?? new TunnelStore(import_node_path41.default.join(deps.dataDir, "tunnel.json"));
|
|
53466
53415
|
this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
|
|
53467
53416
|
this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
|
|
53468
53417
|
}
|
|
@@ -53589,7 +53538,7 @@ var TunnelManager = class {
|
|
|
53589
53538
|
dataDir: this.deps.dataDir,
|
|
53590
53539
|
override: this.deps.frpcBinaryOverride ?? void 0
|
|
53591
53540
|
});
|
|
53592
|
-
const tomlPath =
|
|
53541
|
+
const tomlPath = import_node_path41.default.join(this.deps.dataDir, "frpc.toml");
|
|
53593
53542
|
const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto10.default.randomBytes(3).toString("hex")}`;
|
|
53594
53543
|
const toml = buildFrpcToml({
|
|
53595
53544
|
serverAddr: t.frpsHost,
|
|
@@ -53600,14 +53549,14 @@ var TunnelManager = class {
|
|
|
53600
53549
|
localPort,
|
|
53601
53550
|
logLevel: "info"
|
|
53602
53551
|
});
|
|
53603
|
-
await
|
|
53552
|
+
await import_node_fs38.default.promises.writeFile(tomlPath, toml, { mode: 384 });
|
|
53604
53553
|
const proc = (this.deps.spawnImpl ?? import_node_child_process10.spawn)(frpcBin, ["-c", tomlPath], {
|
|
53605
53554
|
stdio: ["ignore", "pipe", "pipe"]
|
|
53606
53555
|
});
|
|
53607
|
-
const logDir =
|
|
53608
|
-
|
|
53609
|
-
const logFilePath =
|
|
53610
|
-
const logStream =
|
|
53556
|
+
const logDir = import_node_path41.default.join(this.deps.dataDir, "log");
|
|
53557
|
+
import_node_fs38.default.mkdirSync(logDir, { recursive: true });
|
|
53558
|
+
const logFilePath = import_node_path41.default.join(logDir, "frpc.log");
|
|
53559
|
+
const logStream = import_node_fs38.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
|
|
53611
53560
|
logStream.on("error", () => {
|
|
53612
53561
|
});
|
|
53613
53562
|
const tee = (chunk) => {
|
|
@@ -53690,27 +53639,27 @@ async function waitForFrpcReady(proc, timeoutMs) {
|
|
|
53690
53639
|
|
|
53691
53640
|
// src/tunnel/device-key.ts
|
|
53692
53641
|
var import_node_os14 = __toESM(require("os"), 1);
|
|
53693
|
-
var
|
|
53642
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
53694
53643
|
var import_node_crypto11 = __toESM(require("crypto"), 1);
|
|
53695
53644
|
var DERIVE_SALT = "clawd-tunnel-device-v1";
|
|
53696
53645
|
function deriveStableDeviceKey(opts = {}) {
|
|
53697
53646
|
const hostname = opts.hostname ?? import_node_os14.default.hostname();
|
|
53698
53647
|
const uid = opts.uid ?? (typeof import_node_os14.default.userInfo === "function" ? import_node_os14.default.userInfo().uid : 0);
|
|
53699
53648
|
const home = opts.home ?? import_node_os14.default.homedir();
|
|
53700
|
-
const defaultDataDir =
|
|
53701
|
-
const normalizedDataDir = opts.dataDir ?
|
|
53649
|
+
const defaultDataDir = import_node_path42.default.resolve(import_node_path42.default.join(home, ".clawd"));
|
|
53650
|
+
const normalizedDataDir = opts.dataDir ? import_node_path42.default.resolve(opts.dataDir) : null;
|
|
53702
53651
|
const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
|
|
53703
53652
|
const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
|
|
53704
53653
|
return import_node_crypto11.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
|
|
53705
53654
|
}
|
|
53706
53655
|
|
|
53707
53656
|
// src/auth-store.ts
|
|
53708
|
-
var
|
|
53709
|
-
var
|
|
53657
|
+
var import_node_fs39 = __toESM(require("fs"), 1);
|
|
53658
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
53710
53659
|
var import_node_crypto12 = __toESM(require("crypto"), 1);
|
|
53711
53660
|
var AUTH_FILE_NAME = "auth.json";
|
|
53712
53661
|
function authFilePath(dataDir) {
|
|
53713
|
-
return
|
|
53662
|
+
return import_node_path43.default.join(dataDir, AUTH_FILE_NAME);
|
|
53714
53663
|
}
|
|
53715
53664
|
function loadOrCreateAuthFile(opts) {
|
|
53716
53665
|
const file = authFilePath(opts.dataDir);
|
|
@@ -53746,7 +53695,7 @@ function defaultGenerateOwnerPrincipalId() {
|
|
|
53746
53695
|
}
|
|
53747
53696
|
function readAuthFile(file) {
|
|
53748
53697
|
try {
|
|
53749
|
-
const raw =
|
|
53698
|
+
const raw = import_node_fs39.default.readFileSync(file, "utf8");
|
|
53750
53699
|
const parsed = JSON.parse(raw);
|
|
53751
53700
|
if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
|
|
53752
53701
|
return null;
|
|
@@ -53766,25 +53715,25 @@ function readAuthFile(file) {
|
|
|
53766
53715
|
}
|
|
53767
53716
|
}
|
|
53768
53717
|
function writeAuthFile(file, content) {
|
|
53769
|
-
|
|
53770
|
-
|
|
53718
|
+
import_node_fs39.default.mkdirSync(import_node_path43.default.dirname(file), { recursive: true });
|
|
53719
|
+
import_node_fs39.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
|
|
53771
53720
|
try {
|
|
53772
|
-
|
|
53721
|
+
import_node_fs39.default.chmodSync(file, 384);
|
|
53773
53722
|
} catch {
|
|
53774
53723
|
}
|
|
53775
53724
|
}
|
|
53776
53725
|
|
|
53777
53726
|
// src/owner-profile.ts
|
|
53778
|
-
var
|
|
53727
|
+
var import_node_fs40 = __toESM(require("fs"), 1);
|
|
53779
53728
|
var import_node_os15 = __toESM(require("os"), 1);
|
|
53780
|
-
var
|
|
53729
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
53781
53730
|
var PROFILE_FILENAME = "profile.json";
|
|
53782
53731
|
function loadOwnerDisplayName(dataDir) {
|
|
53783
53732
|
const fallback = import_node_os15.default.userInfo().username;
|
|
53784
|
-
const profilePath =
|
|
53733
|
+
const profilePath = import_node_path44.default.join(dataDir, PROFILE_FILENAME);
|
|
53785
53734
|
let raw;
|
|
53786
53735
|
try {
|
|
53787
|
-
raw =
|
|
53736
|
+
raw = import_node_fs40.default.readFileSync(profilePath, "utf8");
|
|
53788
53737
|
} catch {
|
|
53789
53738
|
return fallback;
|
|
53790
53739
|
}
|
|
@@ -53807,18 +53756,18 @@ function loadOwnerDisplayName(dataDir) {
|
|
|
53807
53756
|
}
|
|
53808
53757
|
|
|
53809
53758
|
// src/feishu-auth/owner-identity-store.ts
|
|
53810
|
-
var
|
|
53811
|
-
var
|
|
53759
|
+
var import_node_fs41 = __toESM(require("fs"), 1);
|
|
53760
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
53812
53761
|
var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
|
|
53813
53762
|
var OwnerIdentityStore = class {
|
|
53814
53763
|
file;
|
|
53815
53764
|
constructor(dataDir) {
|
|
53816
|
-
this.file =
|
|
53765
|
+
this.file = import_node_path45.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
|
|
53817
53766
|
}
|
|
53818
53767
|
read() {
|
|
53819
53768
|
let raw;
|
|
53820
53769
|
try {
|
|
53821
|
-
raw =
|
|
53770
|
+
raw = import_node_fs41.default.readFileSync(this.file, "utf8");
|
|
53822
53771
|
} catch {
|
|
53823
53772
|
return null;
|
|
53824
53773
|
}
|
|
@@ -53846,16 +53795,16 @@ var OwnerIdentityStore = class {
|
|
|
53846
53795
|
};
|
|
53847
53796
|
}
|
|
53848
53797
|
write(record) {
|
|
53849
|
-
|
|
53850
|
-
|
|
53798
|
+
import_node_fs41.default.mkdirSync(import_node_path45.default.dirname(this.file), { recursive: true });
|
|
53799
|
+
import_node_fs41.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
|
|
53851
53800
|
try {
|
|
53852
|
-
|
|
53801
|
+
import_node_fs41.default.chmodSync(this.file, 384);
|
|
53853
53802
|
} catch {
|
|
53854
53803
|
}
|
|
53855
53804
|
}
|
|
53856
53805
|
clear() {
|
|
53857
53806
|
try {
|
|
53858
|
-
|
|
53807
|
+
import_node_fs41.default.unlinkSync(this.file);
|
|
53859
53808
|
} catch (err) {
|
|
53860
53809
|
const code = err?.code;
|
|
53861
53810
|
if (code !== "ENOENT") throw err;
|
|
@@ -53992,9 +53941,9 @@ var CentralClientError = class extends Error {
|
|
|
53992
53941
|
code;
|
|
53993
53942
|
cause;
|
|
53994
53943
|
};
|
|
53995
|
-
async function centralRequest(opts,
|
|
53944
|
+
async function centralRequest(opts, path75, init) {
|
|
53996
53945
|
const f = opts.fetchImpl ?? globalThis.fetch;
|
|
53997
|
-
const url = `${opts.api.replace(/\/+$/, "")}${
|
|
53946
|
+
const url = `${opts.api.replace(/\/+$/, "")}${path75}`;
|
|
53998
53947
|
const ctrl = new AbortController();
|
|
53999
53948
|
const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
|
|
54000
53949
|
let res;
|
|
@@ -54188,8 +54137,8 @@ function verifyConnectToken(args) {
|
|
|
54188
54137
|
}
|
|
54189
54138
|
|
|
54190
54139
|
// src/feishu-auth/server-key.ts
|
|
54191
|
-
var
|
|
54192
|
-
var
|
|
54140
|
+
var fs51 = __toESM(require("fs"), 1);
|
|
54141
|
+
var path54 = __toESM(require("path"), 1);
|
|
54193
54142
|
var FILE_NAME2 = "server-signing-key.json";
|
|
54194
54143
|
var ServerKeyStore = class {
|
|
54195
54144
|
constructor(dataDir) {
|
|
@@ -54197,12 +54146,12 @@ var ServerKeyStore = class {
|
|
|
54197
54146
|
}
|
|
54198
54147
|
dataDir;
|
|
54199
54148
|
filePath() {
|
|
54200
|
-
return
|
|
54149
|
+
return path54.join(this.dataDir, FILE_NAME2);
|
|
54201
54150
|
}
|
|
54202
54151
|
/** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
|
|
54203
54152
|
read() {
|
|
54204
54153
|
try {
|
|
54205
|
-
const raw =
|
|
54154
|
+
const raw = fs51.readFileSync(this.filePath(), "utf8");
|
|
54206
54155
|
const parsed = JSON.parse(raw);
|
|
54207
54156
|
if (typeof parsed.publicKeyPem === "string" && parsed.publicKeyPem.includes("PUBLIC KEY")) {
|
|
54208
54157
|
return parsed.publicKeyPem;
|
|
@@ -54217,12 +54166,12 @@ var ServerKeyStore = class {
|
|
|
54217
54166
|
publicKeyPem,
|
|
54218
54167
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
54219
54168
|
};
|
|
54220
|
-
|
|
54221
|
-
|
|
54169
|
+
fs51.mkdirSync(this.dataDir, { recursive: true });
|
|
54170
|
+
fs51.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
|
|
54222
54171
|
}
|
|
54223
54172
|
clear() {
|
|
54224
54173
|
try {
|
|
54225
|
-
|
|
54174
|
+
fs51.unlinkSync(this.filePath());
|
|
54226
54175
|
} catch {
|
|
54227
54176
|
}
|
|
54228
54177
|
}
|
|
@@ -54235,12 +54184,12 @@ init_protocol();
|
|
|
54235
54184
|
init_protocol();
|
|
54236
54185
|
|
|
54237
54186
|
// src/session/fork.ts
|
|
54238
|
-
var
|
|
54187
|
+
var import_node_fs42 = __toESM(require("fs"), 1);
|
|
54239
54188
|
var import_node_os16 = __toESM(require("os"), 1);
|
|
54240
|
-
var
|
|
54189
|
+
var import_node_path46 = __toESM(require("path"), 1);
|
|
54241
54190
|
init_claude_history();
|
|
54242
54191
|
function readJsonlEntries(file) {
|
|
54243
|
-
const raw =
|
|
54192
|
+
const raw = import_node_fs42.default.readFileSync(file, "utf8");
|
|
54244
54193
|
const out = [];
|
|
54245
54194
|
for (const line of raw.split("\n")) {
|
|
54246
54195
|
const t = line.trim();
|
|
@@ -54253,19 +54202,19 @@ function readJsonlEntries(file) {
|
|
|
54253
54202
|
return out;
|
|
54254
54203
|
}
|
|
54255
54204
|
function forkSession(input) {
|
|
54256
|
-
const baseDir = input.baseDir ??
|
|
54257
|
-
let projectDir =
|
|
54258
|
-
let sourceFile =
|
|
54259
|
-
if (!
|
|
54205
|
+
const baseDir = input.baseDir ?? import_node_path46.default.join(import_node_os16.default.homedir(), ".claude");
|
|
54206
|
+
let projectDir = import_node_path46.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
|
|
54207
|
+
let sourceFile = import_node_path46.default.join(projectDir, `${input.toolSessionId}.jsonl`);
|
|
54208
|
+
if (!import_node_fs42.default.existsSync(sourceFile)) {
|
|
54260
54209
|
const found = findTranscriptByToolSessionId(
|
|
54261
|
-
|
|
54210
|
+
import_node_path46.default.join(baseDir, "projects"),
|
|
54262
54211
|
input.toolSessionId
|
|
54263
54212
|
);
|
|
54264
54213
|
if (!found) {
|
|
54265
54214
|
throw new Error(`fork: source transcript not found: ${sourceFile}`);
|
|
54266
54215
|
}
|
|
54267
54216
|
sourceFile = found;
|
|
54268
|
-
projectDir =
|
|
54217
|
+
projectDir = import_node_path46.default.dirname(found);
|
|
54269
54218
|
}
|
|
54270
54219
|
const entries = readJsonlEntries(sourceFile);
|
|
54271
54220
|
const mainEntries = entries.filter((e) => e.isSidechain !== true);
|
|
@@ -54294,9 +54243,9 @@ function forkSession(input) {
|
|
|
54294
54243
|
}
|
|
54295
54244
|
forkedLines.push(JSON.stringify(forked));
|
|
54296
54245
|
}
|
|
54297
|
-
const forkedFilePath =
|
|
54298
|
-
|
|
54299
|
-
|
|
54246
|
+
const forkedFilePath = import_node_path46.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
|
|
54247
|
+
import_node_fs42.default.mkdirSync(projectDir, { recursive: true });
|
|
54248
|
+
import_node_fs42.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
|
|
54300
54249
|
return { forkedToolSessionId, forkedFilePath };
|
|
54301
54250
|
}
|
|
54302
54251
|
|
|
@@ -54708,7 +54657,7 @@ function buildPermissionHandlers(deps) {
|
|
|
54708
54657
|
}
|
|
54709
54658
|
|
|
54710
54659
|
// src/handlers/history.ts
|
|
54711
|
-
var
|
|
54660
|
+
var path57 = __toESM(require("path"), 1);
|
|
54712
54661
|
init_protocol();
|
|
54713
54662
|
|
|
54714
54663
|
// src/session/recent-dirs.ts
|
|
@@ -54726,7 +54675,7 @@ function listRecentDirs(store, limit = 50) {
|
|
|
54726
54675
|
}
|
|
54727
54676
|
|
|
54728
54677
|
// src/permission/persona-paths.ts
|
|
54729
|
-
var
|
|
54678
|
+
var path56 = __toESM(require("path"), 1);
|
|
54730
54679
|
function getAllowedPersonaIds(grants, action) {
|
|
54731
54680
|
const ids = /* @__PURE__ */ new Set();
|
|
54732
54681
|
for (const g2 of grants) {
|
|
@@ -54739,42 +54688,42 @@ function getAllowedPersonaIds(grants, action) {
|
|
|
54739
54688
|
return ids;
|
|
54740
54689
|
}
|
|
54741
54690
|
function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
|
|
54742
|
-
const target =
|
|
54691
|
+
const target = path56.resolve(absPath);
|
|
54743
54692
|
if (userWorkDir) {
|
|
54744
|
-
const u =
|
|
54745
|
-
const usep = u.endsWith(
|
|
54693
|
+
const u = path56.resolve(userWorkDir);
|
|
54694
|
+
const usep = u.endsWith(path56.sep) ? "" : path56.sep;
|
|
54746
54695
|
if (target === u || target.startsWith(u + usep)) return true;
|
|
54747
54696
|
}
|
|
54748
|
-
const root =
|
|
54749
|
-
const sep3 = root.endsWith(
|
|
54697
|
+
const root = path56.resolve(personaRoot);
|
|
54698
|
+
const sep3 = root.endsWith(path56.sep) ? "" : path56.sep;
|
|
54750
54699
|
if (!target.startsWith(root + sep3)) return false;
|
|
54751
|
-
const rel =
|
|
54700
|
+
const rel = path56.relative(root, target);
|
|
54752
54701
|
if (!rel || rel.startsWith("..")) return false;
|
|
54753
|
-
const personaId = rel.split(
|
|
54702
|
+
const personaId = rel.split(path56.sep)[0];
|
|
54754
54703
|
if (!personaId) return false;
|
|
54755
54704
|
const allowed = getAllowedPersonaIds(grants, action);
|
|
54756
54705
|
if (allowed === "*") return true;
|
|
54757
54706
|
return allowed.has(personaId);
|
|
54758
54707
|
}
|
|
54759
54708
|
function personaIdFromPath(absPath, personaRoot) {
|
|
54760
|
-
const root =
|
|
54761
|
-
const target =
|
|
54762
|
-
const sep3 = root.endsWith(
|
|
54709
|
+
const root = path56.resolve(personaRoot);
|
|
54710
|
+
const target = path56.resolve(absPath);
|
|
54711
|
+
const sep3 = root.endsWith(path56.sep) ? "" : path56.sep;
|
|
54763
54712
|
if (!target.startsWith(root + sep3)) return null;
|
|
54764
|
-
const rel =
|
|
54713
|
+
const rel = path56.relative(root, target);
|
|
54765
54714
|
if (!rel || rel.startsWith("..")) return null;
|
|
54766
|
-
const id = rel.split(
|
|
54715
|
+
const id = rel.split(path56.sep)[0];
|
|
54767
54716
|
return id || null;
|
|
54768
54717
|
}
|
|
54769
54718
|
function isPathWithin(dir, absPath) {
|
|
54770
|
-
const d =
|
|
54771
|
-
const t =
|
|
54772
|
-
const sep3 = d.endsWith(
|
|
54719
|
+
const d = path56.resolve(dir);
|
|
54720
|
+
const t = path56.resolve(absPath);
|
|
54721
|
+
const sep3 = d.endsWith(path56.sep) ? "" : path56.sep;
|
|
54773
54722
|
return t === d || t.startsWith(d + sep3);
|
|
54774
54723
|
}
|
|
54775
54724
|
function isPathInGuestBoundary(personaRoot, personaId, userWorkDir, absPath) {
|
|
54776
54725
|
if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
|
|
54777
|
-
return personaIdFromPath(
|
|
54726
|
+
return personaIdFromPath(path56.resolve(absPath), personaRoot) === personaId;
|
|
54778
54727
|
}
|
|
54779
54728
|
|
|
54780
54729
|
// src/handlers/history.ts
|
|
@@ -54826,7 +54775,7 @@ function buildHistoryHandlers(deps) {
|
|
|
54826
54775
|
if (!pid) return false;
|
|
54827
54776
|
return isGuestPathAllowed(
|
|
54828
54777
|
ctx.grants,
|
|
54829
|
-
|
|
54778
|
+
path57.join(personaRoot, pid),
|
|
54830
54779
|
personaRoot,
|
|
54831
54780
|
"read",
|
|
54832
54781
|
userWorkDir
|
|
@@ -54838,7 +54787,7 @@ function buildHistoryHandlers(deps) {
|
|
|
54838
54787
|
};
|
|
54839
54788
|
const list = async (frame, _client, ctx) => {
|
|
54840
54789
|
const args = HistoryListArgs.parse(frame);
|
|
54841
|
-
assertGuestPath(ctx,
|
|
54790
|
+
assertGuestPath(ctx, path57.resolve(args.projectPath), personaRoot, "history:list");
|
|
54842
54791
|
const sessions = await history.listSessions(args);
|
|
54843
54792
|
return { response: { type: "history:list", sessions } };
|
|
54844
54793
|
};
|
|
@@ -54871,13 +54820,13 @@ function buildHistoryHandlers(deps) {
|
|
|
54871
54820
|
};
|
|
54872
54821
|
const subagents = async (frame, _client, ctx) => {
|
|
54873
54822
|
const args = HistorySubagentsArgs.parse(frame);
|
|
54874
|
-
assertGuestPath(ctx,
|
|
54823
|
+
assertGuestPath(ctx, path57.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
|
|
54875
54824
|
const subs = await history.listSubagents(args);
|
|
54876
54825
|
return { response: { type: "history:subagents", subagents: subs } };
|
|
54877
54826
|
};
|
|
54878
54827
|
const subagentRead = async (frame, _client, ctx) => {
|
|
54879
54828
|
const args = HistorySubagentReadArgs.parse(frame);
|
|
54880
|
-
assertGuestPath(ctx,
|
|
54829
|
+
assertGuestPath(ctx, path57.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
|
|
54881
54830
|
const res = await history.readSubagent(args);
|
|
54882
54831
|
return { response: { type: "history:subagent-read", ...res } };
|
|
54883
54832
|
};
|
|
@@ -54886,7 +54835,7 @@ function buildHistoryHandlers(deps) {
|
|
|
54886
54835
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
54887
54836
|
const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
|
|
54888
54837
|
const filtered = dirs.filter(
|
|
54889
|
-
(d) => isGuestPathAllowed(ctx.grants,
|
|
54838
|
+
(d) => isGuestPathAllowed(ctx.grants, path57.resolve(d.cwd), personaRoot, "read", userWorkDir)
|
|
54890
54839
|
);
|
|
54891
54840
|
return { response: { type: "history:recentDirs", dirs: filtered } };
|
|
54892
54841
|
}
|
|
@@ -54903,7 +54852,7 @@ function buildHistoryHandlers(deps) {
|
|
|
54903
54852
|
}
|
|
54904
54853
|
|
|
54905
54854
|
// src/handlers/workspace.ts
|
|
54906
|
-
var
|
|
54855
|
+
var path58 = __toESM(require("path"), 1);
|
|
54907
54856
|
var os16 = __toESM(require("os"), 1);
|
|
54908
54857
|
init_protocol();
|
|
54909
54858
|
init_protocol();
|
|
@@ -54945,22 +54894,22 @@ function buildWorkspaceHandlers(deps) {
|
|
|
54945
54894
|
const args = WorkspaceListArgs.parse(frame);
|
|
54946
54895
|
const isGuest = ctx?.principal.kind === "guest";
|
|
54947
54896
|
const fallbackCwd = isGuest && personaRoot ? personaRoot : os16.homedir();
|
|
54948
|
-
const resolvedCwd =
|
|
54949
|
-
const target = args.path ?
|
|
54897
|
+
const resolvedCwd = path58.resolve(args.cwd ?? fallbackCwd);
|
|
54898
|
+
const target = args.path ? path58.resolve(resolvedCwd, args.path) : resolvedCwd;
|
|
54950
54899
|
assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
|
|
54951
54900
|
const res = workspace.list({ ...args, cwd: resolvedCwd });
|
|
54952
54901
|
return { response: { type: "workspace:list", ...res } };
|
|
54953
54902
|
};
|
|
54954
54903
|
const read = async (frame, _client, ctx) => {
|
|
54955
54904
|
const args = WorkspaceReadArgs.parse(frame);
|
|
54956
|
-
const target =
|
|
54905
|
+
const target = path58.isAbsolute(args.path) ? path58.resolve(args.path) : path58.resolve(args.cwd, args.path);
|
|
54957
54906
|
assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
|
|
54958
54907
|
const res = workspace.read(args);
|
|
54959
54908
|
return { response: { type: "workspace:read", ...res } };
|
|
54960
54909
|
};
|
|
54961
54910
|
const skillsList = async (frame, _client, ctx) => {
|
|
54962
54911
|
const args = SkillsListArgs.parse(frame);
|
|
54963
|
-
const cwdAbs =
|
|
54912
|
+
const cwdAbs = path58.resolve(args.cwd);
|
|
54964
54913
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
|
|
54965
54914
|
const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
|
|
54966
54915
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -54972,7 +54921,7 @@ function buildWorkspaceHandlers(deps) {
|
|
|
54972
54921
|
};
|
|
54973
54922
|
const agentsList = async (frame, _client, ctx) => {
|
|
54974
54923
|
const args = AgentsListArgs.parse(frame);
|
|
54975
|
-
const cwdAbs =
|
|
54924
|
+
const cwdAbs = path58.resolve(args.cwd);
|
|
54976
54925
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
|
|
54977
54926
|
if (args.tool === "codex") {
|
|
54978
54927
|
return { response: { type: "agents:list", agents: [] } };
|
|
@@ -54994,20 +54943,20 @@ function buildWorkspaceHandlers(deps) {
|
|
|
54994
54943
|
}
|
|
54995
54944
|
|
|
54996
54945
|
// src/handlers/git.ts
|
|
54997
|
-
var
|
|
54946
|
+
var path60 = __toESM(require("path"), 1);
|
|
54998
54947
|
init_protocol();
|
|
54999
54948
|
init_protocol();
|
|
55000
54949
|
|
|
55001
54950
|
// src/workspace/git.ts
|
|
55002
54951
|
var import_node_child_process11 = require("child_process");
|
|
55003
|
-
var
|
|
55004
|
-
var
|
|
54952
|
+
var import_node_fs43 = __toESM(require("fs"), 1);
|
|
54953
|
+
var import_node_path47 = __toESM(require("path"), 1);
|
|
55005
54954
|
var import_node_util = require("util");
|
|
55006
54955
|
var pexec = (0, import_node_util.promisify)(import_node_child_process11.execFile);
|
|
55007
54956
|
function normalizePath(p2) {
|
|
55008
|
-
const resolved =
|
|
54957
|
+
const resolved = import_node_path47.default.resolve(p2);
|
|
55009
54958
|
try {
|
|
55010
|
-
return
|
|
54959
|
+
return import_node_fs43.default.realpathSync(resolved);
|
|
55011
54960
|
} catch {
|
|
55012
54961
|
return resolved;
|
|
55013
54962
|
}
|
|
@@ -55081,7 +55030,7 @@ async function listGitBranches(cwd) {
|
|
|
55081
55030
|
function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
|
|
55082
55031
|
if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
|
|
55083
55032
|
const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
|
|
55084
|
-
if (!isGuestPathAllowed(ctx.grants,
|
|
55033
|
+
if (!isGuestPathAllowed(ctx.grants, path60.resolve(cwd), personaRoot, "read", userWorkDir)) {
|
|
55085
55034
|
throw new ClawdError(
|
|
55086
55035
|
ERROR_CODES.UNAUTHORIZED,
|
|
55087
55036
|
`guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
|
|
@@ -55826,7 +55775,7 @@ function buildPersonaHandlers(deps) {
|
|
|
55826
55775
|
}
|
|
55827
55776
|
|
|
55828
55777
|
// src/handlers/attachment.ts
|
|
55829
|
-
var
|
|
55778
|
+
var import_node_path48 = __toESM(require("path"), 1);
|
|
55830
55779
|
init_protocol();
|
|
55831
55780
|
init_protocol();
|
|
55832
55781
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -55842,10 +55791,8 @@ function assertGuestAttachmentPath(ctx, absPath, personaRoot, method, usersRoot)
|
|
|
55842
55791
|
}
|
|
55843
55792
|
function buildAttachmentHandlers(deps) {
|
|
55844
55793
|
function guardAttachmentPath(ctx, sessionId, candidateAbs, method, nonGuestPolicy) {
|
|
55845
|
-
if (ctx?.principal.kind === "owner") return;
|
|
55846
55794
|
const scope = deps.getSessionScope?.(sessionId);
|
|
55847
|
-
if (
|
|
55848
|
-
if (scope.kind === "persona" && scope.mode === "guest") {
|
|
55795
|
+
if (scope?.kind === "persona" && scope.mode === "guest") {
|
|
55849
55796
|
if (!deps.personaRoot) return;
|
|
55850
55797
|
const userWorkDir = deps.usersRoot ? deriveUserWorkDir(scope.capId, deps.usersRoot) : void 0;
|
|
55851
55798
|
if (!isPathInGuestBoundary(deps.personaRoot, scope.personaId, userWorkDir, candidateAbs)) {
|
|
@@ -55856,6 +55803,8 @@ function buildAttachmentHandlers(deps) {
|
|
|
55856
55803
|
}
|
|
55857
55804
|
return;
|
|
55858
55805
|
}
|
|
55806
|
+
if (ctx?.principal.kind === "owner") return;
|
|
55807
|
+
if (!scope) return;
|
|
55859
55808
|
if (nonGuestPolicy === "group-acl") return;
|
|
55860
55809
|
const cwd = deps.sessionStore?.read(sessionId)?.cwd;
|
|
55861
55810
|
if (!cwd) return;
|
|
@@ -55906,12 +55855,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
55906
55855
|
`session ${args.sessionId} scope unresolved`
|
|
55907
55856
|
);
|
|
55908
55857
|
}
|
|
55909
|
-
const cwdAbs =
|
|
55910
|
-
const candidateAbs =
|
|
55858
|
+
const cwdAbs = import_node_path48.default.resolve(sessionFile.cwd);
|
|
55859
|
+
const candidateAbs = import_node_path48.default.isAbsolute(args.relPath) ? import_node_path48.default.resolve(args.relPath) : import_node_path48.default.resolve(cwdAbs, args.relPath);
|
|
55911
55860
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
|
|
55912
55861
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
55913
55862
|
const entry = entries.find((e) => {
|
|
55914
|
-
const storedAbs =
|
|
55863
|
+
const storedAbs = import_node_path48.default.isAbsolute(e.relPath) ? import_node_path48.default.resolve(e.relPath) : import_node_path48.default.resolve(cwdAbs, e.relPath);
|
|
55915
55864
|
return storedAbs === candidateAbs && !e.stale;
|
|
55916
55865
|
});
|
|
55917
55866
|
if (!entry) {
|
|
@@ -55936,7 +55885,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
55936
55885
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
55937
55886
|
const f = deps.sessionStore.read(sessionId);
|
|
55938
55887
|
if (!f) return;
|
|
55939
|
-
assertGuestAttachmentPath(ctx,
|
|
55888
|
+
assertGuestAttachmentPath(ctx, import_node_path48.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
|
|
55940
55889
|
}
|
|
55941
55890
|
const groupAdd = async (frame, _client, ctx) => {
|
|
55942
55891
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -55951,10 +55900,10 @@ function buildAttachmentHandlers(deps) {
|
|
|
55951
55900
|
if (!scope) {
|
|
55952
55901
|
throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
|
|
55953
55902
|
}
|
|
55954
|
-
const cwdAbs =
|
|
55955
|
-
const candidateAbs =
|
|
55903
|
+
const cwdAbs = import_node_path48.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
|
|
55904
|
+
const candidateAbs = import_node_path48.default.isAbsolute(args.relPath) ? import_node_path48.default.resolve(args.relPath) : import_node_path48.default.resolve(cwdAbs, args.relPath);
|
|
55956
55905
|
guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
|
|
55957
|
-
const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
|
|
55906
|
+
const from = args.origin === "agent" ? "agent" : ctx?.principal.kind === "owner" ? "owner" : "agent";
|
|
55958
55907
|
const size = 0;
|
|
55959
55908
|
const entry = deps.groupFileStore.upsert(scope, args.sessionId, {
|
|
55960
55909
|
relPath: args.relPath,
|
|
@@ -56011,19 +55960,19 @@ function buildAttachmentHandlers(deps) {
|
|
|
56011
55960
|
|
|
56012
55961
|
// src/handlers/extension.ts
|
|
56013
55962
|
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
56014
|
-
var
|
|
55963
|
+
var import_node_path53 = __toESM(require("path"), 1);
|
|
56015
55964
|
init_protocol();
|
|
56016
55965
|
|
|
56017
55966
|
// src/extension/bundle-zip.ts
|
|
56018
55967
|
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
56019
|
-
var
|
|
55968
|
+
var import_node_path49 = __toESM(require("path"), 1);
|
|
56020
55969
|
var import_node_crypto14 = __toESM(require("crypto"), 1);
|
|
56021
55970
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
56022
55971
|
async function bundleExtensionDir(dir) {
|
|
56023
55972
|
const entries = await listFilesSorted(dir);
|
|
56024
55973
|
const zip = new import_jszip2.default();
|
|
56025
55974
|
for (const rel of entries) {
|
|
56026
|
-
const abs =
|
|
55975
|
+
const abs = import_node_path49.default.join(dir, rel);
|
|
56027
55976
|
const content = await import_promises6.default.readFile(abs);
|
|
56028
55977
|
zip.file(rel, content, { date: FIXED_DATE });
|
|
56029
55978
|
}
|
|
@@ -56044,7 +55993,7 @@ async function listFilesSorted(rootDir) {
|
|
|
56044
55993
|
return out;
|
|
56045
55994
|
}
|
|
56046
55995
|
async function walk(absRoot, relPrefix, out) {
|
|
56047
|
-
const dirAbs =
|
|
55996
|
+
const dirAbs = import_node_path49.default.join(absRoot, relPrefix);
|
|
56048
55997
|
const entries = await import_promises6.default.readdir(dirAbs, { withFileTypes: true });
|
|
56049
55998
|
for (const e of entries) {
|
|
56050
55999
|
if (IGNORE_BASENAMES.has(e.name)) continue;
|
|
@@ -56098,25 +56047,25 @@ function computePublishCheck(args) {
|
|
|
56098
56047
|
|
|
56099
56048
|
// src/extension/install-flow.ts
|
|
56100
56049
|
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
56101
|
-
var
|
|
56050
|
+
var import_node_path51 = __toESM(require("path"), 1);
|
|
56102
56051
|
var import_node_os19 = __toESM(require("os"), 1);
|
|
56103
56052
|
var import_node_crypto15 = __toESM(require("crypto"), 1);
|
|
56104
56053
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
56105
56054
|
|
|
56106
56055
|
// src/extension/paths.ts
|
|
56107
56056
|
var import_node_os18 = __toESM(require("os"), 1);
|
|
56108
|
-
var
|
|
56057
|
+
var import_node_path50 = __toESM(require("path"), 1);
|
|
56109
56058
|
function clawdHomeRoot(override) {
|
|
56110
|
-
return override ?? process.env.CLAWD_HOME ??
|
|
56059
|
+
return override ?? process.env.CLAWD_HOME ?? import_node_path50.default.join(import_node_os18.default.homedir(), ".clawd");
|
|
56111
56060
|
}
|
|
56112
56061
|
function extensionsRoot(override) {
|
|
56113
|
-
return
|
|
56062
|
+
return import_node_path50.default.join(clawdHomeRoot(override), "extensions");
|
|
56114
56063
|
}
|
|
56115
56064
|
function publishedChannelsFile(override) {
|
|
56116
|
-
return
|
|
56065
|
+
return import_node_path50.default.join(clawdHomeRoot(override), "extensions-published.json");
|
|
56117
56066
|
}
|
|
56118
56067
|
function bundleCacheRoot(override) {
|
|
56119
|
-
return
|
|
56068
|
+
return import_node_path50.default.join(clawdHomeRoot(override), "extension-bundles");
|
|
56120
56069
|
}
|
|
56121
56070
|
|
|
56122
56071
|
// src/extension/install-flow.ts
|
|
@@ -56143,7 +56092,7 @@ async function installFromChannel(args, deps) {
|
|
|
56143
56092
|
throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
56144
56093
|
}
|
|
56145
56094
|
for (const name of Object.keys(zip.files)) {
|
|
56146
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
56095
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path51.default.isAbsolute(name)) {
|
|
56147
56096
|
throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
56148
56097
|
}
|
|
56149
56098
|
}
|
|
@@ -56175,7 +56124,7 @@ async function installFromChannel(args, deps) {
|
|
|
56175
56124
|
);
|
|
56176
56125
|
}
|
|
56177
56126
|
const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
|
|
56178
|
-
const destDir =
|
|
56127
|
+
const destDir = import_node_path51.default.join(deps.extensionsRoot, localExtId);
|
|
56179
56128
|
let destExists = false;
|
|
56180
56129
|
try {
|
|
56181
56130
|
await import_promises7.default.access(destDir);
|
|
@@ -56189,16 +56138,16 @@ async function installFromChannel(args, deps) {
|
|
|
56189
56138
|
);
|
|
56190
56139
|
}
|
|
56191
56140
|
const stage = await import_promises7.default.mkdtemp(
|
|
56192
|
-
|
|
56141
|
+
import_node_path51.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
|
|
56193
56142
|
);
|
|
56194
56143
|
try {
|
|
56195
56144
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
56196
|
-
const dest =
|
|
56145
|
+
const dest = import_node_path51.default.join(stage, name);
|
|
56197
56146
|
if (entry.dir) {
|
|
56198
56147
|
await import_promises7.default.mkdir(dest, { recursive: true });
|
|
56199
56148
|
continue;
|
|
56200
56149
|
}
|
|
56201
|
-
await import_promises7.default.mkdir(
|
|
56150
|
+
await import_promises7.default.mkdir(import_node_path51.default.dirname(dest), { recursive: true });
|
|
56202
56151
|
if (name === "manifest.json") {
|
|
56203
56152
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
56204
56153
|
await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -56219,7 +56168,7 @@ async function installFromChannel(args, deps) {
|
|
|
56219
56168
|
|
|
56220
56169
|
// src/extension/update-flow.ts
|
|
56221
56170
|
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
56222
|
-
var
|
|
56171
|
+
var import_node_path52 = __toESM(require("path"), 1);
|
|
56223
56172
|
var import_node_os20 = __toESM(require("os"), 1);
|
|
56224
56173
|
var import_node_crypto16 = __toESM(require("crypto"), 1);
|
|
56225
56174
|
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
@@ -56236,11 +56185,11 @@ async function updateFromChannel(args, deps) {
|
|
|
56236
56185
|
channelRef.extId,
|
|
56237
56186
|
channelRef.ownerPrincipalId
|
|
56238
56187
|
);
|
|
56239
|
-
const liveDir =
|
|
56188
|
+
const liveDir = import_node_path52.default.join(deps.extensionsRoot, localExtId);
|
|
56240
56189
|
const prevDir = `${liveDir}.prev`;
|
|
56241
56190
|
let existingVersion;
|
|
56242
56191
|
try {
|
|
56243
|
-
const raw = await import_promises8.default.readFile(
|
|
56192
|
+
const raw = await import_promises8.default.readFile(import_node_path52.default.join(liveDir, "manifest.json"), "utf8");
|
|
56244
56193
|
const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
|
|
56245
56194
|
if (!parsed2.success) {
|
|
56246
56195
|
throw new UpdateError(
|
|
@@ -56273,7 +56222,7 @@ async function updateFromChannel(args, deps) {
|
|
|
56273
56222
|
throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
56274
56223
|
}
|
|
56275
56224
|
for (const name of Object.keys(zip.files)) {
|
|
56276
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
56225
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path52.default.isAbsolute(name)) {
|
|
56277
56226
|
throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
56278
56227
|
}
|
|
56279
56228
|
}
|
|
@@ -56308,16 +56257,16 @@ async function updateFromChannel(args, deps) {
|
|
|
56308
56257
|
await import_promises8.default.rm(prevDir, { recursive: true, force: true });
|
|
56309
56258
|
await import_promises8.default.rename(liveDir, prevDir);
|
|
56310
56259
|
const stage = await import_promises8.default.mkdtemp(
|
|
56311
|
-
|
|
56260
|
+
import_node_path52.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
|
|
56312
56261
|
);
|
|
56313
56262
|
try {
|
|
56314
56263
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
56315
|
-
const dest =
|
|
56264
|
+
const dest = import_node_path52.default.join(stage, name);
|
|
56316
56265
|
if (entry.dir) {
|
|
56317
56266
|
await import_promises8.default.mkdir(dest, { recursive: true });
|
|
56318
56267
|
continue;
|
|
56319
56268
|
}
|
|
56320
|
-
await import_promises8.default.mkdir(
|
|
56269
|
+
await import_promises8.default.mkdir(import_node_path52.default.dirname(dest), { recursive: true });
|
|
56321
56270
|
if (name === "manifest.json") {
|
|
56322
56271
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
56323
56272
|
await import_promises8.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -56410,7 +56359,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
56410
56359
|
);
|
|
56411
56360
|
}
|
|
56412
56361
|
}
|
|
56413
|
-
const manifestPath =
|
|
56362
|
+
const manifestPath = import_node_path53.default.join(root, extId, "manifest.json");
|
|
56414
56363
|
const manifest = await readManifest(root, extId);
|
|
56415
56364
|
const next = { ...manifest, version: newVersion };
|
|
56416
56365
|
const tmp = `${manifestPath}.tmp`;
|
|
@@ -56418,7 +56367,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
56418
56367
|
await import_promises9.default.rename(tmp, manifestPath);
|
|
56419
56368
|
}
|
|
56420
56369
|
async function readManifest(root, extId) {
|
|
56421
|
-
const file =
|
|
56370
|
+
const file = import_node_path53.default.join(root, extId, "manifest.json");
|
|
56422
56371
|
let raw;
|
|
56423
56372
|
try {
|
|
56424
56373
|
raw = await import_promises9.default.readFile(file, "utf8");
|
|
@@ -56509,7 +56458,7 @@ function buildExtensionHandlers(deps) {
|
|
|
56509
56458
|
};
|
|
56510
56459
|
async function buildSnapshotMeta(extId) {
|
|
56511
56460
|
const manifest = await readManifest(deps.root, extId);
|
|
56512
|
-
const { sha256, buffer } = await bundleExtensionDir(
|
|
56461
|
+
const { sha256, buffer } = await bundleExtensionDir(import_node_path53.default.join(deps.root, extId));
|
|
56513
56462
|
return { manifest, contentHash: sha256, buffer };
|
|
56514
56463
|
}
|
|
56515
56464
|
const publish = async (frame, _client, ctx) => {
|
|
@@ -56690,9 +56639,9 @@ function buildExtensionHandlers(deps) {
|
|
|
56690
56639
|
}
|
|
56691
56640
|
|
|
56692
56641
|
// src/app-builder/project-store.ts
|
|
56693
|
-
var
|
|
56642
|
+
var import_node_fs44 = require("fs");
|
|
56694
56643
|
var import_node_child_process12 = require("child_process");
|
|
56695
|
-
var
|
|
56644
|
+
var import_node_path54 = require("path");
|
|
56696
56645
|
init_protocol();
|
|
56697
56646
|
var PROJECTS_DIR = "projects";
|
|
56698
56647
|
var META_FILE = ".clawd-project.json";
|
|
@@ -56706,19 +56655,19 @@ var ProjectStore = class {
|
|
|
56706
56655
|
root;
|
|
56707
56656
|
/** projects/<name>/.clawd-project.json 路径 */
|
|
56708
56657
|
metaPath(name) {
|
|
56709
|
-
return (0,
|
|
56658
|
+
return (0, import_node_path54.join)(this.projectsRoot(), name, META_FILE);
|
|
56710
56659
|
}
|
|
56711
56660
|
/** projects/<name>/ 目录路径(cwd 用) */
|
|
56712
56661
|
projectDir(name) {
|
|
56713
|
-
return (0,
|
|
56662
|
+
return (0, import_node_path54.join)(this.projectsRoot(), name);
|
|
56714
56663
|
}
|
|
56715
56664
|
projectsRoot() {
|
|
56716
|
-
return (0,
|
|
56665
|
+
return (0, import_node_path54.join)(this.root, PROJECTS_DIR);
|
|
56717
56666
|
}
|
|
56718
56667
|
async list() {
|
|
56719
56668
|
let entries;
|
|
56720
56669
|
try {
|
|
56721
|
-
entries = await
|
|
56670
|
+
entries = await import_node_fs44.promises.readdir(this.projectsRoot());
|
|
56722
56671
|
} catch (err) {
|
|
56723
56672
|
if (err.code === "ENOENT") return [];
|
|
56724
56673
|
throw err;
|
|
@@ -56726,7 +56675,7 @@ var ProjectStore = class {
|
|
|
56726
56675
|
const out = [];
|
|
56727
56676
|
for (const name of entries) {
|
|
56728
56677
|
try {
|
|
56729
|
-
const raw = await
|
|
56678
|
+
const raw = await import_node_fs44.promises.readFile(this.metaPath(name), "utf8");
|
|
56730
56679
|
const json = JSON.parse(raw);
|
|
56731
56680
|
let migrated = false;
|
|
56732
56681
|
if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
|
|
@@ -56737,7 +56686,7 @@ var ProjectStore = class {
|
|
|
56737
56686
|
if (parsed.success) {
|
|
56738
56687
|
out.push(parsed.data);
|
|
56739
56688
|
if (migrated) {
|
|
56740
|
-
void
|
|
56689
|
+
void import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
|
|
56741
56690
|
});
|
|
56742
56691
|
}
|
|
56743
56692
|
}
|
|
@@ -56781,8 +56730,8 @@ var ProjectStore = class {
|
|
|
56781
56730
|
throw new Error(`invalid name "${name}": ${validated.error.message}`);
|
|
56782
56731
|
}
|
|
56783
56732
|
const dir = this.projectDir(name);
|
|
56784
|
-
await
|
|
56785
|
-
await
|
|
56733
|
+
await import_node_fs44.promises.mkdir(dir, { recursive: true });
|
|
56734
|
+
await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
|
|
56786
56735
|
return meta;
|
|
56787
56736
|
}
|
|
56788
56737
|
/**
|
|
@@ -56825,7 +56774,7 @@ var ProjectStore = class {
|
|
|
56825
56774
|
}
|
|
56826
56775
|
async delete(name) {
|
|
56827
56776
|
const dir = this.projectDir(name);
|
|
56828
|
-
await
|
|
56777
|
+
await import_node_fs44.promises.rm(dir, { recursive: true, force: true });
|
|
56829
56778
|
}
|
|
56830
56779
|
async updatePort(name, newPort) {
|
|
56831
56780
|
if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
|
|
@@ -56841,7 +56790,7 @@ var ProjectStore = class {
|
|
|
56841
56790
|
throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
|
|
56842
56791
|
}
|
|
56843
56792
|
const updated = { ...target, port: newPort };
|
|
56844
|
-
await
|
|
56793
|
+
await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
56845
56794
|
return updated;
|
|
56846
56795
|
}
|
|
56847
56796
|
/**
|
|
@@ -56858,7 +56807,7 @@ var ProjectStore = class {
|
|
|
56858
56807
|
if (!validated.success) {
|
|
56859
56808
|
throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
|
|
56860
56809
|
}
|
|
56861
|
-
await
|
|
56810
|
+
await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
56862
56811
|
return validated.data;
|
|
56863
56812
|
}
|
|
56864
56813
|
/**
|
|
@@ -56879,7 +56828,7 @@ var ProjectStore = class {
|
|
|
56879
56828
|
if (!validated.success) {
|
|
56880
56829
|
throw new Error(`invalid publishJob: ${validated.error.message}`);
|
|
56881
56830
|
}
|
|
56882
|
-
await
|
|
56831
|
+
await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
56883
56832
|
return validated.data;
|
|
56884
56833
|
}
|
|
56885
56834
|
/** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
|
|
@@ -56894,7 +56843,7 @@ var ProjectStore = class {
|
|
|
56894
56843
|
if (!validated.success) {
|
|
56895
56844
|
throw new Error(`failed to clear publishJob: ${validated.error.message}`);
|
|
56896
56845
|
}
|
|
56897
|
-
await
|
|
56846
|
+
await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
|
|
56898
56847
|
return validated.data;
|
|
56899
56848
|
}
|
|
56900
56849
|
};
|
|
@@ -57015,8 +56964,8 @@ var PublishJobRegistry = class {
|
|
|
57015
56964
|
|
|
57016
56965
|
// src/app-builder/publish-job-runner.ts
|
|
57017
56966
|
var import_node_child_process14 = require("child_process");
|
|
57018
|
-
var
|
|
57019
|
-
var
|
|
56967
|
+
var import_node_fs45 = require("fs");
|
|
56968
|
+
var import_node_path55 = require("path");
|
|
57020
56969
|
|
|
57021
56970
|
// src/app-builder/publish-stage-parser.ts
|
|
57022
56971
|
var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
|
|
@@ -57048,10 +56997,10 @@ async function startPublishJob(deps, args) {
|
|
|
57048
56997
|
return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
|
|
57049
56998
|
}
|
|
57050
56999
|
const projDir = projectDir(args.name);
|
|
57051
|
-
const logPath = (0,
|
|
57000
|
+
const logPath = (0, import_node_path55.join)(projDir, ".publish.log");
|
|
57052
57001
|
let logStream = null;
|
|
57053
57002
|
try {
|
|
57054
|
-
logStream = (0,
|
|
57003
|
+
logStream = (0, import_node_fs45.createWriteStream)(logPath, { flags: "w" });
|
|
57055
57004
|
} catch {
|
|
57056
57005
|
logStream = null;
|
|
57057
57006
|
}
|
|
@@ -57308,8 +57257,8 @@ async function recoverInterruptedJobs(deps) {
|
|
|
57308
57257
|
|
|
57309
57258
|
// src/handlers/app-builder.ts
|
|
57310
57259
|
init_protocol();
|
|
57311
|
-
var
|
|
57312
|
-
var
|
|
57260
|
+
var import_node_path56 = require("path");
|
|
57261
|
+
var import_node_fs46 = require("fs");
|
|
57313
57262
|
var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
|
|
57314
57263
|
var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
|
|
57315
57264
|
async function recoverInterruptedPublishJobs(store, logger) {
|
|
@@ -57390,7 +57339,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
57390
57339
|
async function listAllUsersProjects() {
|
|
57391
57340
|
if (!deps.usersRoot || !deps.getStore) return [];
|
|
57392
57341
|
const getStore = deps.getStore;
|
|
57393
|
-
const userIds = await
|
|
57342
|
+
const userIds = await import_node_fs46.promises.readdir(deps.usersRoot).catch(() => []);
|
|
57394
57343
|
const perUser = await Promise.all(
|
|
57395
57344
|
userIds.map((uid) => getStore(uid).list().catch(() => []))
|
|
57396
57345
|
);
|
|
@@ -57409,12 +57358,14 @@ function buildAppBuilderHandlers(deps) {
|
|
|
57409
57358
|
}
|
|
57410
57359
|
const listProjects = async (frame, _client, ctx) => {
|
|
57411
57360
|
const f = frame;
|
|
57361
|
+
const sessionId = typeof f.sessionId === "string" ? f.sessionId : void 0;
|
|
57412
57362
|
let metas;
|
|
57413
|
-
if (
|
|
57363
|
+
if (sessionId) {
|
|
57364
|
+
metas = await resolveStore({ ctx, sessionId }).list();
|
|
57365
|
+
} else if (ctx?.principal.kind === "owner" && deps.usersRoot && deps.getStore) {
|
|
57414
57366
|
metas = await listAllUsersProjects();
|
|
57415
57367
|
} else {
|
|
57416
|
-
|
|
57417
|
-
metas = await resolveStore({ ctx, sessionId }).list();
|
|
57368
|
+
metas = await resolveStore({ ctx }).list();
|
|
57418
57369
|
}
|
|
57419
57370
|
return { response: { projects: metas.map(toProjectWithStatus) } };
|
|
57420
57371
|
};
|
|
@@ -57466,8 +57417,8 @@ function buildAppBuilderHandlers(deps) {
|
|
|
57466
57417
|
const project = await userStore.create(f.name, reservedPorts);
|
|
57467
57418
|
try {
|
|
57468
57419
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
|
|
57469
|
-
const templateSrcDir = (0,
|
|
57470
|
-
const scaffoldScript = (0,
|
|
57420
|
+
const templateSrcDir = (0, import_node_path56.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
|
|
57421
|
+
const scaffoldScript = (0, import_node_path56.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
|
|
57471
57422
|
const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
|
|
57472
57423
|
deps.logger?.info("app-builder.scaffold.done", {
|
|
57473
57424
|
name: project.name,
|
|
@@ -57688,7 +57639,7 @@ function buildAppBuilderHandlers(deps) {
|
|
|
57688
57639
|
await userStore.clearPublishJob(args.name);
|
|
57689
57640
|
}
|
|
57690
57641
|
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
|
|
57691
|
-
const scriptPath = (0,
|
|
57642
|
+
const scriptPath = (0, import_node_path56.join)(deps.deployKitRoot, "scripts", "publish.sh");
|
|
57692
57643
|
deps.logger?.info("app-builder.publish.start", {
|
|
57693
57644
|
name: args.name,
|
|
57694
57645
|
sessionId: boundSession.sessionId,
|
|
@@ -57864,7 +57815,7 @@ function buildVisitorHandlers(deps) {
|
|
|
57864
57815
|
|
|
57865
57816
|
// src/extension/registry.ts
|
|
57866
57817
|
var import_promises10 = __toESM(require("fs/promises"), 1);
|
|
57867
|
-
var
|
|
57818
|
+
var import_node_path57 = __toESM(require("path"), 1);
|
|
57868
57819
|
async function loadAll(root) {
|
|
57869
57820
|
let entries;
|
|
57870
57821
|
try {
|
|
@@ -57877,13 +57828,13 @@ async function loadAll(root) {
|
|
|
57877
57828
|
for (const ent of entries) {
|
|
57878
57829
|
if (!ent.isDirectory()) continue;
|
|
57879
57830
|
if (ent.name.startsWith(".")) continue;
|
|
57880
|
-
records.push(await loadOne(
|
|
57831
|
+
records.push(await loadOne(import_node_path57.default.join(root, ent.name), ent.name));
|
|
57881
57832
|
}
|
|
57882
57833
|
records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
|
|
57883
57834
|
return records;
|
|
57884
57835
|
}
|
|
57885
57836
|
async function loadOne(dir, dirName) {
|
|
57886
|
-
const manifestPath =
|
|
57837
|
+
const manifestPath = import_node_path57.default.join(dir, "manifest.json");
|
|
57887
57838
|
let raw;
|
|
57888
57839
|
try {
|
|
57889
57840
|
raw = await import_promises10.default.readFile(manifestPath, "utf8");
|
|
@@ -57928,7 +57879,7 @@ async function loadOne(dir, dirName) {
|
|
|
57928
57879
|
|
|
57929
57880
|
// src/extension/uninstall.ts
|
|
57930
57881
|
var import_promises11 = __toESM(require("fs/promises"), 1);
|
|
57931
|
-
var
|
|
57882
|
+
var import_node_path58 = __toESM(require("path"), 1);
|
|
57932
57883
|
var UninstallError = class extends Error {
|
|
57933
57884
|
constructor(code, message) {
|
|
57934
57885
|
super(message);
|
|
@@ -57937,7 +57888,7 @@ var UninstallError = class extends Error {
|
|
|
57937
57888
|
code;
|
|
57938
57889
|
};
|
|
57939
57890
|
async function uninstall(deps) {
|
|
57940
|
-
const dir =
|
|
57891
|
+
const dir = import_node_path58.default.join(deps.root, deps.extId);
|
|
57941
57892
|
try {
|
|
57942
57893
|
await import_promises11.default.access(dir);
|
|
57943
57894
|
} catch {
|
|
@@ -58524,6 +58475,21 @@ var METHOD_GRANT_MAP = {
|
|
|
58524
58475
|
// ---- shift:* (排班 mock,owner-only 读) ----
|
|
58525
58476
|
// mock 阶段只 owner 用;接 guest 后再考虑分级
|
|
58526
58477
|
"shift:list": ADMIN_ANY,
|
|
58478
|
+
// ---- shift-internal:* + inbox:sendDm + personaDispatch complete/list ----
|
|
58479
|
+
// 统一 RPC 鉴权(spec 2026-07-28):原 /api/rpc 白名单 method 收编。调用方 = 本机
|
|
58480
|
+
// MCP server 持 owner token → ADMIN_ANY 即「owner token 即可」;业务归因(sendDm 的
|
|
58481
|
+
// sender、shift 的 personaId)在 handler 内按 body 字段处理,与鉴权无关。
|
|
58482
|
+
"shift-internal:add": ADMIN_ANY,
|
|
58483
|
+
"shift-internal:list": ADMIN_ANY,
|
|
58484
|
+
"shift-internal:cancel": ADMIN_ANY,
|
|
58485
|
+
"shift-internal:update": ADMIN_ANY,
|
|
58486
|
+
"shift-internal:history": ADMIN_ANY,
|
|
58487
|
+
"inbox:sendDm": ADMIN_ANY,
|
|
58488
|
+
"personaDispatch:complete": ADMIN_ANY,
|
|
58489
|
+
"personaDispatch:list": ADMIN_ANY,
|
|
58490
|
+
// 跨设备轮询(spec §5):A 端 daemon 凭 connect token 查 outcome;handler 内校验
|
|
58491
|
+
// 台账行 sourceSessionId === ctx.principal.id,防 guest 查别人的 dispatch。
|
|
58492
|
+
"personaDispatch:get": CAPABILITY_SCOPED,
|
|
58527
58493
|
"skills:list": CAPABILITY_SCOPED,
|
|
58528
58494
|
"agents:list": CAPABILITY_SCOPED,
|
|
58529
58495
|
"git:root": CAPABILITY_SCOPED,
|
|
@@ -58543,7 +58509,7 @@ var METHOD_GRANT_MAP = {
|
|
|
58543
58509
|
"persona:delete": ADMIN_ANY,
|
|
58544
58510
|
// 跨设备 dispatch:A 的 peer daemon 凭 connect token 经 /rpc 调进来;dispatcher 放行,
|
|
58545
58511
|
// run handler 内做 persona.public + assertGrant(send) 双重校验(guest grants 是 wildcard,
|
|
58546
|
-
// 单 assertGrant 不够)。本地
|
|
58512
|
+
// 单 assertGrant 不够)。本地 MCP server 经 /rpc + Bearer 进来,同过此表(owner 恒过)。
|
|
58547
58513
|
"personaDispatch:run": CAPABILITY_SCOPED,
|
|
58548
58514
|
// 跨设备 exec(spec 2026-07-16):public——"能连上=有 auth",业务在 handler 校
|
|
58549
58515
|
// (ensureGuest + remoteAccessAllowed),跟 inbox:postMessage 同模式。
|
|
@@ -58655,7 +58621,7 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
|
|
|
58655
58621
|
|
|
58656
58622
|
// src/extension/runtime.ts
|
|
58657
58623
|
var import_node_child_process17 = require("child_process");
|
|
58658
|
-
var
|
|
58624
|
+
var import_node_path59 = __toESM(require("path"), 1);
|
|
58659
58625
|
var import_promises12 = require("timers/promises");
|
|
58660
58626
|
|
|
58661
58627
|
// src/extension/port-allocator.ts
|
|
@@ -58756,7 +58722,7 @@ var Runtime = class {
|
|
|
58756
58722
|
/\$CLAWOS_EXT_PORT/g,
|
|
58757
58723
|
String(port)
|
|
58758
58724
|
);
|
|
58759
|
-
const dir =
|
|
58725
|
+
const dir = import_node_path59.default.join(this.root, extId);
|
|
58760
58726
|
const env = {
|
|
58761
58727
|
...process.env,
|
|
58762
58728
|
CLAWOS_EXT_PORT: String(port),
|
|
@@ -58868,7 +58834,7 @@ ${handle.stderrTail}`
|
|
|
58868
58834
|
|
|
58869
58835
|
// src/extension/published-channels.ts
|
|
58870
58836
|
var import_promises13 = __toESM(require("fs/promises"), 1);
|
|
58871
|
-
var
|
|
58837
|
+
var import_node_path60 = __toESM(require("path"), 1);
|
|
58872
58838
|
init_zod();
|
|
58873
58839
|
var PublishedChannelsError = class extends Error {
|
|
58874
58840
|
constructor(code, message) {
|
|
@@ -58967,7 +58933,7 @@ var PublishedChannelStore = class {
|
|
|
58967
58933
|
)
|
|
58968
58934
|
};
|
|
58969
58935
|
const tmp = `${this.filePath}.tmp`;
|
|
58970
|
-
await import_promises13.default.mkdir(
|
|
58936
|
+
await import_promises13.default.mkdir(import_node_path60.default.dirname(this.filePath), { recursive: true });
|
|
58971
58937
|
await import_promises13.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
58972
58938
|
await import_promises13.default.rename(tmp, this.filePath);
|
|
58973
58939
|
}
|
|
@@ -58975,7 +58941,7 @@ var PublishedChannelStore = class {
|
|
|
58975
58941
|
|
|
58976
58942
|
// src/extension/bundle-cache.ts
|
|
58977
58943
|
var import_promises14 = __toESM(require("fs/promises"), 1);
|
|
58978
|
-
var
|
|
58944
|
+
var import_node_path61 = __toESM(require("path"), 1);
|
|
58979
58945
|
var BundleCache = class {
|
|
58980
58946
|
constructor(rootDir) {
|
|
58981
58947
|
this.rootDir = rootDir;
|
|
@@ -58984,14 +58950,14 @@ var BundleCache = class {
|
|
|
58984
58950
|
/** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
|
|
58985
58951
|
async write(snapshotHash, buffer) {
|
|
58986
58952
|
await import_promises14.default.mkdir(this.rootDir, { recursive: true });
|
|
58987
|
-
const file =
|
|
58953
|
+
const file = import_node_path61.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
58988
58954
|
const tmp = `${file}.tmp`;
|
|
58989
58955
|
await import_promises14.default.writeFile(tmp, buffer, { mode: 384 });
|
|
58990
58956
|
await import_promises14.default.rename(tmp, file);
|
|
58991
58957
|
}
|
|
58992
58958
|
/** Returns the bundle bytes, or null when the file doesn't exist. */
|
|
58993
58959
|
async read(snapshotHash) {
|
|
58994
|
-
const file =
|
|
58960
|
+
const file = import_node_path61.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
58995
58961
|
try {
|
|
58996
58962
|
return await import_promises14.default.readFile(file);
|
|
58997
58963
|
} catch (e) {
|
|
@@ -59001,7 +58967,7 @@ var BundleCache = class {
|
|
|
59001
58967
|
}
|
|
59002
58968
|
/** Idempotent — missing file is not an error. */
|
|
59003
58969
|
async delete(snapshotHash) {
|
|
59004
|
-
const file =
|
|
58970
|
+
const file = import_node_path61.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
59005
58971
|
await import_promises14.default.rm(file, { force: true });
|
|
59006
58972
|
}
|
|
59007
58973
|
};
|
|
@@ -59024,24 +58990,24 @@ async function startDaemon(config) {
|
|
|
59024
58990
|
sampling: logShippingCfg.sampling,
|
|
59025
58991
|
homeDir: import_node_os22.default.homedir()
|
|
59026
58992
|
});
|
|
59027
|
-
const logDir =
|
|
59028
|
-
|
|
58993
|
+
const logDir = import_node_path62.default.join(config.dataDir, "log");
|
|
58994
|
+
import_node_fs47.default.mkdirSync(logDir, { recursive: true });
|
|
59029
58995
|
const logger = createLogger({
|
|
59030
58996
|
level: config.logLevel,
|
|
59031
|
-
file:
|
|
58997
|
+
file: import_node_path62.default.join(logDir, "clawd.log"),
|
|
59032
58998
|
logClient
|
|
59033
58999
|
});
|
|
59034
59000
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
59035
59001
|
const screenIdleProbeLogger = createFileOnlyLogger({
|
|
59036
|
-
file:
|
|
59002
|
+
file: import_node_path62.default.join(logDir, "screen-idle-probe.log"),
|
|
59037
59003
|
level: "debug"
|
|
59038
59004
|
});
|
|
59039
59005
|
logger.info("screen-idle probe logger enabled", {
|
|
59040
|
-
file:
|
|
59006
|
+
file: import_node_path62.default.join(logDir, "screen-idle-probe.log")
|
|
59041
59007
|
});
|
|
59042
59008
|
const CAP_TARGETS = [
|
|
59043
|
-
|
|
59044
|
-
|
|
59009
|
+
import_node_path62.default.join(logDir, "clawd.log"),
|
|
59010
|
+
import_node_path62.default.join(logDir, "screen-idle-probe.log")
|
|
59045
59011
|
];
|
|
59046
59012
|
const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
|
|
59047
59013
|
const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
|
|
@@ -59065,12 +59031,7 @@ async function startDaemon(config) {
|
|
|
59065
59031
|
if (pre.status === "stale") {
|
|
59066
59032
|
logger.warn("stale state file detected, overwriting", { pid: pre.existing.pid });
|
|
59067
59033
|
}
|
|
59068
|
-
|
|
59069
|
-
if (config.authToken && config.authToken.trim()) {
|
|
59070
|
-
resolvedAuthToken = config.authToken.trim();
|
|
59071
|
-
} else if (config.tunnel) {
|
|
59072
|
-
resolvedAuthToken = authFile.token;
|
|
59073
|
-
}
|
|
59034
|
+
const resolvedAuthToken = config.authToken && config.authToken.trim() ? config.authToken.trim() : authFile.token;
|
|
59074
59035
|
const ownerIdentityStore = new OwnerIdentityStore(config.dataDir);
|
|
59075
59036
|
const feishuIdentity = ownerIdentityStore.read();
|
|
59076
59037
|
let feishuActive = feishuIdentity !== null;
|
|
@@ -59079,7 +59040,7 @@ async function startDaemon(config) {
|
|
|
59079
59040
|
let ownerProvider = feishuIdentity?.identity.provider ?? "";
|
|
59080
59041
|
let ownerDisplayName = feishuIdentity?.identity.displayName ?? loadOwnerDisplayName(config.dataDir);
|
|
59081
59042
|
let ownerFeishuUnionId = feishuIdentity?.identity.unionId;
|
|
59082
|
-
const authMode =
|
|
59043
|
+
const authMode = "first-message";
|
|
59083
59044
|
const inboxStore = new InboxStore(config.dataDir);
|
|
59084
59045
|
const inboxManager = new InboxManager(inboxStore, (_peerDeviceId, frame) => {
|
|
59085
59046
|
wsServer?.broadcastToOwners(frame);
|
|
@@ -59143,8 +59104,8 @@ async function startDaemon(config) {
|
|
|
59143
59104
|
const agents = new AgentsScanner();
|
|
59144
59105
|
const history = new ClaudeHistoryReader();
|
|
59145
59106
|
let transport = null;
|
|
59146
|
-
const personaStore = new PersonaStore(
|
|
59147
|
-
const usersRoot =
|
|
59107
|
+
const personaStore = new PersonaStore(import_node_path62.default.join(config.dataDir, "personas"));
|
|
59108
|
+
const usersRoot = import_node_path62.default.join(config.dataDir, "users");
|
|
59148
59109
|
const defaultsRoot = findDefaultsRoot(logger);
|
|
59149
59110
|
if (defaultsRoot) {
|
|
59150
59111
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -59164,7 +59125,7 @@ async function startDaemon(config) {
|
|
|
59164
59125
|
migrateCodexSandbox({ store: personaStore, logger });
|
|
59165
59126
|
const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
|
|
59166
59127
|
const dispatchStore = createDispatchStore({
|
|
59167
|
-
filePath:
|
|
59128
|
+
filePath: import_node_path62.default.join(config.dataDir, "dispatch.json"),
|
|
59168
59129
|
now: () => Date.now(),
|
|
59169
59130
|
onFlushError: (err) => logger.error("dispatch store flush failed", {
|
|
59170
59131
|
reason: err instanceof Error ? err.message : String(err)
|
|
@@ -59179,20 +59140,23 @@ async function startDaemon(config) {
|
|
|
59179
59140
|
now: () => Date.now(),
|
|
59180
59141
|
onCompleted: (sid) => deliverPendingRef?.(sid)
|
|
59181
59142
|
});
|
|
59182
|
-
const here = typeof __dirname === "string" ? __dirname :
|
|
59143
|
+
const here = typeof __dirname === "string" ? __dirname : import_node_path62.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
|
|
59183
59144
|
const mcpConfigs = [];
|
|
59145
|
+
const mcpDaemonUrl = `http://127.0.0.1:${config.port}`;
|
|
59184
59146
|
const dispatchServerCandidates = [
|
|
59185
|
-
|
|
59147
|
+
import_node_path62.default.join(here, "dispatch", "mcp-server.cjs"),
|
|
59186
59148
|
// 生产 dist/index → dist/dispatch/mcp-server.cjs
|
|
59187
|
-
|
|
59149
|
+
import_node_path62.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
|
|
59188
59150
|
// dev tsx src/index → ../dist/dispatch/mcp-server.cjs
|
|
59189
59151
|
];
|
|
59190
|
-
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) =>
|
|
59152
|
+
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
|
|
59191
59153
|
if (dispatchServerScriptPath) {
|
|
59192
|
-
const dispatchLogPath =
|
|
59154
|
+
const dispatchLogPath = import_node_path62.default.join(logDir, "dispatch-mcp-server.log");
|
|
59193
59155
|
const dispatchCfgPath = writeDispatchMcpConfig({
|
|
59194
59156
|
dataDir: config.dataDir,
|
|
59195
59157
|
serverScriptPath: dispatchServerScriptPath,
|
|
59158
|
+
daemonToken: resolvedAuthToken,
|
|
59159
|
+
daemonUrl: mcpDaemonUrl,
|
|
59196
59160
|
logPath: dispatchLogPath
|
|
59197
59161
|
});
|
|
59198
59162
|
mcpConfigs.push({ id: DISPATCH_MCP_ID, path: dispatchCfgPath });
|
|
@@ -59207,17 +59171,19 @@ async function startDaemon(config) {
|
|
|
59207
59171
|
});
|
|
59208
59172
|
}
|
|
59209
59173
|
const ticketServerCandidates = [
|
|
59210
|
-
|
|
59211
|
-
|
|
59174
|
+
import_node_path62.default.join(here, "ticket", "mcp-server.cjs"),
|
|
59175
|
+
import_node_path62.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
|
|
59212
59176
|
];
|
|
59213
|
-
const ticketServerScriptPath = ticketServerCandidates.find((p2) =>
|
|
59177
|
+
const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
|
|
59214
59178
|
if (ticketServerScriptPath) {
|
|
59215
|
-
const ticketLogPath =
|
|
59179
|
+
const ticketLogPath = import_node_path62.default.join(logDir, "ticket-mcp-server.log");
|
|
59216
59180
|
const ticketCfgPath = writeTicketMcpConfig({
|
|
59217
59181
|
dataDir: config.dataDir,
|
|
59218
59182
|
serverScriptPath: ticketServerScriptPath,
|
|
59219
59183
|
apiBaseUrl: config.clawosApi ?? DEFAULT_CLAWOS_API,
|
|
59220
59184
|
apiKey: resolveClawdTicketsApiKey(),
|
|
59185
|
+
daemonToken: resolvedAuthToken,
|
|
59186
|
+
daemonUrl: mcpDaemonUrl,
|
|
59221
59187
|
logPath: ticketLogPath
|
|
59222
59188
|
});
|
|
59223
59189
|
mcpConfigs.push({ id: "ticket", path: ticketCfgPath });
|
|
@@ -59232,15 +59198,17 @@ async function startDaemon(config) {
|
|
|
59232
59198
|
});
|
|
59233
59199
|
}
|
|
59234
59200
|
const shiftServerCandidates = [
|
|
59235
|
-
|
|
59236
|
-
|
|
59201
|
+
import_node_path62.default.join(here, "shift", "mcp-server.cjs"),
|
|
59202
|
+
import_node_path62.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
|
|
59237
59203
|
];
|
|
59238
|
-
const shiftServerScriptPath = shiftServerCandidates.find((p2) =>
|
|
59204
|
+
const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
|
|
59239
59205
|
if (shiftServerScriptPath) {
|
|
59240
|
-
const shiftLogPath =
|
|
59206
|
+
const shiftLogPath = import_node_path62.default.join(logDir, "shift-mcp-server.log");
|
|
59241
59207
|
const shiftCfgPath = await writeShiftMcpConfig({
|
|
59242
59208
|
dataDir: config.dataDir,
|
|
59243
59209
|
serverScriptPath: shiftServerScriptPath,
|
|
59210
|
+
daemonToken: resolvedAuthToken,
|
|
59211
|
+
daemonUrl: mcpDaemonUrl,
|
|
59244
59212
|
logPath: shiftLogPath
|
|
59245
59213
|
});
|
|
59246
59214
|
mcpConfigs.push({ id: "shift", path: shiftCfgPath });
|
|
@@ -59256,15 +59224,17 @@ async function startDaemon(config) {
|
|
|
59256
59224
|
);
|
|
59257
59225
|
}
|
|
59258
59226
|
const inboxServerCandidates = [
|
|
59259
|
-
|
|
59260
|
-
|
|
59227
|
+
import_node_path62.default.join(here, "inbox", "mcp-server.cjs"),
|
|
59228
|
+
import_node_path62.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
|
|
59261
59229
|
];
|
|
59262
|
-
const inboxServerScriptPath = inboxServerCandidates.find((p2) =>
|
|
59230
|
+
const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
|
|
59263
59231
|
if (inboxServerScriptPath) {
|
|
59264
|
-
const inboxLogPath =
|
|
59232
|
+
const inboxLogPath = import_node_path62.default.join(logDir, "inbox-mcp-server.log");
|
|
59265
59233
|
const inboxCfgPath = await writeInboxMcpConfig({
|
|
59266
59234
|
dataDir: config.dataDir,
|
|
59267
59235
|
serverScriptPath: inboxServerScriptPath,
|
|
59236
|
+
daemonToken: resolvedAuthToken,
|
|
59237
|
+
daemonUrl: mcpDaemonUrl,
|
|
59268
59238
|
logPath: inboxLogPath
|
|
59269
59239
|
});
|
|
59270
59240
|
mcpConfigs.push({ id: "inbox", path: inboxCfgPath });
|
|
@@ -59280,14 +59250,16 @@ async function startDaemon(config) {
|
|
|
59280
59250
|
);
|
|
59281
59251
|
}
|
|
59282
59252
|
const peerOpsServerCandidates = [
|
|
59283
|
-
|
|
59284
|
-
|
|
59253
|
+
import_node_path62.default.join(here, "peer-ops", "mcp-server.cjs"),
|
|
59254
|
+
import_node_path62.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
|
|
59285
59255
|
];
|
|
59286
|
-
const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) =>
|
|
59256
|
+
const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
|
|
59287
59257
|
if (peerOpsServerScriptPath) {
|
|
59288
59258
|
const peerOpsCfgPath = writePeerOpsMcpConfig({
|
|
59289
59259
|
dataDir: config.dataDir,
|
|
59290
|
-
serverScriptPath: peerOpsServerScriptPath
|
|
59260
|
+
serverScriptPath: peerOpsServerScriptPath,
|
|
59261
|
+
daemonToken: resolvedAuthToken,
|
|
59262
|
+
daemonUrl: mcpDaemonUrl
|
|
59291
59263
|
});
|
|
59292
59264
|
mcpConfigs.push({ id: "peer-ops", path: peerOpsCfgPath });
|
|
59293
59265
|
logger.info("peer-ops.mcp.json written", {
|
|
@@ -59300,8 +59272,34 @@ async function startDaemon(config) {
|
|
|
59300
59272
|
{ tried: peerOpsServerCandidates }
|
|
59301
59273
|
);
|
|
59302
59274
|
}
|
|
59275
|
+
const rpcToolServerCandidates = [
|
|
59276
|
+
import_node_path62.default.join(here, "rpc-tool", "mcp-server.cjs"),
|
|
59277
|
+
import_node_path62.default.join(here, "..", "dist", "rpc-tool", "mcp-server.cjs")
|
|
59278
|
+
];
|
|
59279
|
+
const rpcToolServerScriptPath = rpcToolServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
|
|
59280
|
+
if (rpcToolServerScriptPath) {
|
|
59281
|
+
const rpcToolLogPath = import_node_path62.default.join(logDir, "rpc-tool-mcp-server.log");
|
|
59282
|
+
const rpcToolCfgPath = writeRpcToolMcpConfig({
|
|
59283
|
+
dataDir: config.dataDir,
|
|
59284
|
+
serverScriptPath: rpcToolServerScriptPath,
|
|
59285
|
+
daemonToken: resolvedAuthToken,
|
|
59286
|
+
daemonUrl: mcpDaemonUrl,
|
|
59287
|
+
logPath: rpcToolLogPath
|
|
59288
|
+
});
|
|
59289
|
+
mcpConfigs.push({ id: "rpc-tool", path: rpcToolCfgPath });
|
|
59290
|
+
logger.info("rpc-tool.mcp.json written", {
|
|
59291
|
+
path: rpcToolCfgPath,
|
|
59292
|
+
server: rpcToolServerScriptPath,
|
|
59293
|
+
serverLog: rpcToolLogPath
|
|
59294
|
+
});
|
|
59295
|
+
} else {
|
|
59296
|
+
logger.warn(
|
|
59297
|
+
"rpc-tool-mcp-server.cjs not found; clawd-rpc call tool disabled (dev tsx \u6A21\u5F0F\u4E0B\u9700\u5148\u8DD1\u8FC7 build)",
|
|
59298
|
+
{ tried: rpcToolServerCandidates }
|
|
59299
|
+
);
|
|
59300
|
+
}
|
|
59303
59301
|
const shiftStore = createShiftStore({
|
|
59304
|
-
filePath:
|
|
59302
|
+
filePath: import_node_path62.default.join(config.dataDir, "shift.json"),
|
|
59305
59303
|
ownerIdProvider: () => ownerPrincipalId,
|
|
59306
59304
|
now: () => Date.now()
|
|
59307
59305
|
});
|
|
@@ -59323,7 +59321,7 @@ async function startDaemon(config) {
|
|
|
59323
59321
|
getAdapter,
|
|
59324
59322
|
historyReader: history,
|
|
59325
59323
|
dataDir: config.dataDir,
|
|
59326
|
-
personaRoot:
|
|
59324
|
+
personaRoot: import_node_path62.default.join(config.dataDir, "personas"),
|
|
59327
59325
|
// dispatch / shift 自动化 spawn 按 persona 绑定 tool 起 B session(codex persona 用 codex)。
|
|
59328
59326
|
// personaRegistry 声明在 manager 之后(同作用域 const,调用时机在启动完成后,无 TDZ 风险)。
|
|
59329
59327
|
getPersonaTool: (personaId) => personaRegistry.get(personaId)?.tool,
|
|
@@ -59338,7 +59336,7 @@ async function startDaemon(config) {
|
|
|
59338
59336
|
contactStore,
|
|
59339
59337
|
mode: config.mode,
|
|
59340
59338
|
// 单栏 refactor (spec 2026-06-02 §5.1):cc 子进程 env CLAWD_DAEMON_URL 闭包来源。
|
|
59341
|
-
//
|
|
59339
|
+
// MCP server 用它拼 /rpc/<method>(Bearer CLAWD_DAEMON_TOKEN)调本机 daemon。
|
|
59342
59340
|
// 127.0.0.1(不是 config.host)—— cc 跑在本机,loopback 最稳;外部访问限制 + http-router
|
|
59343
59341
|
// 的 isLoopback 兜底已确保安全。
|
|
59344
59342
|
getDaemonUrl: () => `http://127.0.0.1:${config.port}`,
|
|
@@ -59374,10 +59372,10 @@ async function startDaemon(config) {
|
|
|
59374
59372
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
59375
59373
|
attachmentGroup: {
|
|
59376
59374
|
onFileEdit: (input) => {
|
|
59377
|
-
const absPath =
|
|
59375
|
+
const absPath = import_node_path62.default.isAbsolute(input.relPath) ? input.relPath : import_node_path62.default.join(input.cwd, input.relPath);
|
|
59378
59376
|
let size = 0;
|
|
59379
59377
|
try {
|
|
59380
|
-
size =
|
|
59378
|
+
size = import_node_fs47.default.statSync(absPath).size;
|
|
59381
59379
|
} catch (err) {
|
|
59382
59380
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
59383
59381
|
sessionId: input.sessionId,
|
|
@@ -59608,11 +59606,11 @@ async function startDaemon(config) {
|
|
|
59608
59606
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
59609
59607
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
59610
59608
|
// guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
|
|
59611
|
-
personaRoot:
|
|
59609
|
+
personaRoot: import_node_path62.default.join(config.dataDir, "personas"),
|
|
59612
59610
|
usersRoot
|
|
59613
59611
|
},
|
|
59614
59612
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
59615
|
-
personaRoot:
|
|
59613
|
+
personaRoot: import_node_path62.default.join(config.dataDir, "personas"),
|
|
59616
59614
|
// v2 多人 persona 隔离:handler 派生 guest user-dir 放行
|
|
59617
59615
|
usersRoot,
|
|
59618
59616
|
// v2 Phase 6: whoami handler 装在 owner principal.displayName + persona 解析
|
|
@@ -59741,11 +59739,11 @@ async function startDaemon(config) {
|
|
|
59741
59739
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2):
|
|
59742
59740
|
// appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
|
|
59743
59741
|
// dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
|
|
59744
|
-
appBuilderPersonaRoot:
|
|
59742
|
+
appBuilderPersonaRoot: import_node_path62.default.join(config.dataDir, "personas", "persona-app-builder"),
|
|
59745
59743
|
// 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
|
|
59746
|
-
deployKitRoot:
|
|
59744
|
+
deployKitRoot: import_node_path62.default.join(config.dataDir, "deploy-kit"),
|
|
59747
59745
|
// scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
|
|
59748
|
-
resolvePersonaRoot: (personaId) =>
|
|
59746
|
+
resolvePersonaRoot: (personaId) => import_node_path62.default.join(config.dataDir, "personas", personaId),
|
|
59749
59747
|
// 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
|
|
59750
59748
|
// 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
|
|
59751
59749
|
// 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
|
|
@@ -59791,7 +59789,7 @@ async function startDaemon(config) {
|
|
|
59791
59789
|
}
|
|
59792
59790
|
let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
|
|
59793
59791
|
if (sourceFile && sourceFile.toolSessionId) {
|
|
59794
|
-
sourceJsonlPath =
|
|
59792
|
+
sourceJsonlPath = import_node_path62.default.join(
|
|
59795
59793
|
import_node_os22.default.homedir(),
|
|
59796
59794
|
".claude",
|
|
59797
59795
|
"projects",
|
|
@@ -59861,17 +59859,6 @@ async function startDaemon(config) {
|
|
|
59861
59859
|
const authResolver = new AuthContextResolver({
|
|
59862
59860
|
ownerToken: resolvedAuthToken
|
|
59863
59861
|
});
|
|
59864
|
-
const rpcDispatcher = {
|
|
59865
|
-
async dispatch(method, args, _ctx) {
|
|
59866
|
-
const handler = resolveExposedHandler(handlers, method);
|
|
59867
|
-
if (!handler) {
|
|
59868
|
-
throw Object.assign(new Error(`unknown RPC method "${method}"`), { code: "METHOD_NOT_IMPLEMENTED" });
|
|
59869
|
-
}
|
|
59870
|
-
const emptyClient = {};
|
|
59871
|
-
const result = await handler(args, emptyClient);
|
|
59872
|
-
return result.response;
|
|
59873
|
-
}
|
|
59874
|
-
};
|
|
59875
59862
|
const authedRpc = {
|
|
59876
59863
|
authenticate: (token) => authenticate(token, buildConnectAuthDeps()),
|
|
59877
59864
|
dispatch: (method, body, ctx) => {
|
|
@@ -59896,7 +59883,6 @@ async function startDaemon(config) {
|
|
|
59896
59883
|
daemonVersion: version,
|
|
59897
59884
|
logger,
|
|
59898
59885
|
sessionStore: store,
|
|
59899
|
-
rpcDispatcher,
|
|
59900
59886
|
authedRpc,
|
|
59901
59887
|
...viewerAssetLoader ? { viewerAssetLoader } : {},
|
|
59902
59888
|
// persona web 分享:访客换 token(POST /share/exchange)+ guest SPA serve(GET /s)
|
|
@@ -60106,8 +60092,8 @@ async function startDaemon(config) {
|
|
|
60106
60092
|
const lines = [
|
|
60107
60093
|
`Tunnel: ${r.url}`,
|
|
60108
60094
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
60109
|
-
`Frpc config: ${
|
|
60110
|
-
`Frpc log: ${
|
|
60095
|
+
`Frpc config: ${import_node_path62.default.join(config.dataDir, "frpc.toml")}`,
|
|
60096
|
+
`Frpc log: ${import_node_path62.default.join(logDir, "frpc.log")}`
|
|
60111
60097
|
];
|
|
60112
60098
|
const width = Math.max(...lines.map((l) => l.length));
|
|
60113
60099
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -60120,8 +60106,8 @@ ${bar}
|
|
|
60120
60106
|
|
|
60121
60107
|
`);
|
|
60122
60108
|
try {
|
|
60123
|
-
const connectPath =
|
|
60124
|
-
|
|
60109
|
+
const connectPath = import_node_path62.default.join(config.dataDir, "connect.txt");
|
|
60110
|
+
import_node_fs47.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
|
|
60125
60111
|
} catch {
|
|
60126
60112
|
}
|
|
60127
60113
|
} catch (err) {
|
|
@@ -60193,9 +60179,9 @@ ${bar}
|
|
|
60193
60179
|
};
|
|
60194
60180
|
}
|
|
60195
60181
|
function migrateDropPersonsDir(dataDir) {
|
|
60196
|
-
const dir =
|
|
60182
|
+
const dir = import_node_path62.default.join(dataDir, "persons");
|
|
60197
60183
|
try {
|
|
60198
|
-
|
|
60184
|
+
import_node_fs47.default.rmSync(dir, { recursive: true, force: true });
|
|
60199
60185
|
} catch {
|
|
60200
60186
|
}
|
|
60201
60187
|
}
|