@clawos-dev/clawd 0.2.257 → 0.2.259

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 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
- // /api/rpc/session.currentTurnSender 拉当前 turn caller;WS handler 同名(loopback path 复用)
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:* (跨设备 dispatch) ----
115
- // personaDispatch:run 有两个入口:本地 cc 经 /api/rpc(EXPOSED_RPC_METHODS,127.0.0.1 token);
116
- // 跨设备时 A 的 peer daemon 经 /rpc(Bearer connect token)。后者要求它是 WS METHOD_NAME +
117
- // METHOD_GRANT_MAP capability-scoped(handler 自检 persona.public + assertGrant(send))。
118
- // personaDispatch:complete B 本地 cc 调(/api/rpc),不跨设备,故不列入 WS method。
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 侧本地 cc 经 /api/rpc(EXPOSED_RPC_METHODS)带 peerDeviceId → daemon forward;
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: path74, errorMaps, issueData } = params;
753
- const fullPath = [...path74, ...issueData.path || []];
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, path74, key) {
1081
+ constructor(parent, value, path75, key) {
1065
1082
  this._cachedPath = [];
1066
1083
  this.parent = parent;
1067
1084
  this.data = value;
1068
- this._path = path74;
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 path74 = req.path;
6637
- _req.url = typeof path74 === "string" ? path74 : req.url ? req.url.path || req.url : void 0;
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(path74) {
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 < path74.length; i++) {
6809
- const char = path74[i];
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 path74 of paths) {
6941
- const parts = parsePath(path74);
6970
+ for (const path75 of paths) {
6971
+ const parts = parsePath(path75);
6942
6972
  if (parts.includes("*")) {
6943
- redactWildcardPath(obj, parts, censor, path74, remove);
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, path74) => {
7029
- const fullPath = [...pathArray.slice(0, pathLength), ...path74];
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 path74 of pathsToClone) {
7065
- const parts = parsePath(path74);
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(path74) {
7118
- if (typeof path74 !== "string") {
7147
+ function validatePath(path75) {
7148
+ if (typeof path75 !== "string") {
7119
7149
  throw new Error("Paths must be (non-empty) strings");
7120
7150
  }
7121
- if (path74 === "") {
7151
+ if (path75 === "") {
7122
7152
  throw new Error("Invalid redaction path ()");
7123
7153
  }
7124
- if (path74.includes("..")) {
7125
- throw new Error(`Invalid redaction path (${path74})`);
7154
+ if (path75.includes("..")) {
7155
+ throw new Error(`Invalid redaction path (${path75})`);
7126
7156
  }
7127
- if (path74.includes(",")) {
7128
- throw new Error(`Invalid redaction path (${path74})`);
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 < path74.length; i++) {
7134
- const char = path74[i];
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 (${path74})`);
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 (${path74})`);
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 path74 of paths) {
7161
- validatePath(path74);
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, path74) => {
7330
- return censor(value, [k2, ...path74]);
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 fs65 = require("fs");
7578
+ var fs66 = require("fs");
7549
7579
  var EventEmitter3 = require("events");
7550
7580
  var inherits = require("util").inherits;
7551
- var path74 = require("path");
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) fs65.mkdirSync(path74.dirname(file), { recursive: true });
7606
- const fd = fs65.openSync(file, flags, mode);
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
- fs65.mkdir(path74.dirname(file), { recursive: true }, (err) => {
7643
+ fs66.mkdir(path75.dirname(file), { recursive: true }, (err) => {
7614
7644
  if (err) return fileOpened(err);
7615
- fs65.open(file, flags, mode, fileOpened);
7645
+ fs66.open(file, flags, mode, fileOpened);
7616
7646
  });
7617
7647
  } else {
7618
- fs65.open(file, flags, mode, fileOpened);
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 = () => fs65.writeSync(this.fd, this._writingBuf);
7660
- fsWrite = () => fs65.write(this.fd, this._writingBuf, this.release);
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 fs65.writeSync(this.fd, this._writingBuf);
7699
+ return fs66.writeSync(this.fd, this._writingBuf);
7670
7700
  }
7671
- return fs65.writeSync(this.fd, this._writingBuf, "utf8");
7701
+ return fs66.writeSync(this.fd, this._writingBuf, "utf8");
7672
7702
  };
7673
7703
  fsWrite = () => {
7674
7704
  if (Buffer.isBuffer(this._writingBuf)) {
7675
- return fs65.write(this.fd, this._writingBuf, this.release);
7705
+ return fs66.write(this.fd, this._writingBuf, this.release);
7676
7706
  }
7677
- return fs65.write(this.fd, this._writingBuf, "utf8", this.release);
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
- fs65.fsyncSync(this.fd);
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
- fs65.fsync(this.fd, (err) => {
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
- fs65.close(fd, (err) => {
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) ? fs65.writeSync(this.fd, buf) : fs65.writeSync(this.fd, buf, "utf8");
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
- fs65.fsyncSync(this.fd);
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 = fs65.writeSync(this.fd, buf);
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) ? fs65.writeSync(this.fd, this._writingBuf) : fs65.writeSync(this.fd, this._writingBuf, "utf8");
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
- fs65.write(this.fd, this._writingBuf, release);
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 = fs65.writeSync(this.fd, this._writingBuf);
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
- fs65.write(this.fd, this._writingBuf, release);
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
- fs65.fsync(sonic.fd, closeWrapped);
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
- fs65.close(sonic.fd, done);
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(path74, added, removed, oldPosInc, options) {
11245
- var last = path74.lastComponent;
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: path74.oldPos + oldPosInc,
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: path74.oldPos + oldPosInc,
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 path74 = typeof rec3.path === "string" ? rec3.path : null;
11797
+ const path75 = typeof rec3.path === "string" ? rec3.path : null;
11768
11798
  const content = typeof rec3.content === "string" ? rec3.content : null;
11769
- if (!path74 || content == null) return null;
11770
- const entry = { path: path74, content };
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 path74 = typeof rec3.path === "string" ? rec3.path : null;
12603
+ const path75 = typeof rec3.path === "string" ? rec3.path : null;
12574
12604
  const content = typeof rec3.content === "string" ? rec3.content : null;
12575
- if (!path74 || content == null) return null;
12576
- const out = { path: path74, content };
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(path74) {
33546
- var parts = path74.split("/");
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(path74) {
39400
- if (path74.slice(-1) === "/") {
39401
- path74 = path74.substring(0, path74.length - 1);
39429
+ var parentFolder = function(path75) {
39430
+ if (path75.slice(-1) === "/") {
39431
+ path75 = path75.substring(0, path75.length - 1);
39402
39432
  }
39403
- var lastSlash = path74.lastIndexOf("/");
39404
- return lastSlash > 0 ? path74.substring(0, lastSlash) : "";
39433
+ var lastSlash = path75.lastIndexOf("/");
39434
+ return lastSlash > 0 ? path75.substring(0, lastSlash) : "";
39405
39435
  };
39406
- var forceTrailingSlash = function(path74) {
39407
- if (path74.slice(-1) !== "/") {
39408
- path74 += "/";
39436
+ var forceTrailingSlash = function(path75) {
39437
+ if (path75.slice(-1) !== "/") {
39438
+ path75 += "/";
39409
39439
  }
39410
- return path74;
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 = import_node_path62.default.dirname(opts.recordPath);
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
- import_node_fs47.default.mkdirSync(dir, { recursive: true });
40427
- stream = import_node_fs47.default.createWriteStream(opts.recordPath, { flags: "a" });
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 import_node_fs47, import_node_path62;
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
- import_node_fs47 = __toESM(require("fs"), 1);
40457
- import_node_path62 = __toESM(require("path"), 1);
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, import_node_fs48.mkdtempSync)(import_node_path63.default.join(import_node_os23.default.tmpdir(), "clawd-runcase-"));
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, import_node_fs48.rmSync)(cwd, { recursive: true, force: true });
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 import_node_fs48, import_node_os23, import_node_path63;
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
- import_node_fs48 = require("fs");
40701
+ import_node_fs49 = require("fs");
40672
40702
  import_node_os23 = __toESM(require("os"), 1);
40673
- import_node_path63 = __toESM(require("path"), 1);
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 import_node_path61 = __toESM(require("path"), 1);
40949
- var import_node_fs46 = __toESM(require("fs"), 1);
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 daemon \u7684 HTTP RPC \u628A\u6587\u4EF6\u8F6C\u6210\u4E00\u4E2A\u5E26\u7B7E\u540D\u7684\u4E0B\u8F7D URL\uFF1A
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
- \`\`\`bash
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
- \`\`\`bash
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\u4F60\u81EA\u5DF1\u5DE5\u4F5C\u76EE\u5F55\u5185\u7684\u6587\u4EF6\uFF0C\u8D8A\u754C\u4F1A\u88AB\u62D2\u3002
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
- // curl daemon HTTP RPC adapter (/api/rpc/<method>) 触发管理操作。null = HTTP adapter 未启。
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/**` 线上文档。
@@ -46506,8 +46528,9 @@ var import_node_crypto4 = __toESM(require("crypto"), 1);
46506
46528
  init_runtime();
46507
46529
 
46508
46530
  // src/visitor/visitor-token.ts
46531
+ var DOMAIN_PREFIX = "clawd-visitor-v1|";
46509
46532
  function hmac(secret, body) {
46510
- return import_node_crypto4.default.createHmac("sha256", secret).update(body).digest("base64url");
46533
+ return import_node_crypto4.default.createHmac("sha256", secret).update(`${DOMAIN_PREFIX}${body}`).digest("base64url");
46511
46534
  }
46512
46535
  function signVisitorToken(secret, payload) {
46513
46536
  const body = Buffer.from(JSON.stringify(payload)).toString("base64url");
@@ -46880,8 +46903,8 @@ function turnStartInput(text) {
46880
46903
  const items = [];
46881
46904
  let leftover = text;
46882
46905
  for (const m2 of text.matchAll(SKILL_RE)) {
46883
- const [marker, name, path74] = m2;
46884
- items.push({ type: "skill", name, path: path74 });
46906
+ const [marker, name, path75] = m2;
46907
+ items.push({ type: "skill", name, path: path75 });
46885
46908
  leftover = leftover.replace(marker, "");
46886
46909
  }
46887
46910
  for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
@@ -48470,13 +48493,6 @@ function jsonlExistsForCtx(ctx) {
48470
48493
  init_claude_history();
48471
48494
 
48472
48495
  // 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
48496
  var PersonaDispatchManager = class {
48481
48497
  constructor(deps) {
48482
48498
  this.deps = deps;
@@ -48488,8 +48504,7 @@ var PersonaDispatchManager = class {
48488
48504
  this.map.set(dispatchId, {
48489
48505
  dispatchId,
48490
48506
  sourceSessionId: args.sourceSessionId,
48491
- targetPersona: args.targetPersona,
48492
- waiters: []
48507
+ targetPersona: args.targetPersona
48493
48508
  });
48494
48509
  this.deps.store.add({
48495
48510
  dispatchId,
@@ -48511,45 +48526,21 @@ var PersonaDispatchManager = class {
48511
48526
  outcome,
48512
48527
  completedAt: new Date(this.deps.now()).toISOString()
48513
48528
  });
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
- }
48529
+ if (state) this.map.delete(dispatchId);
48520
48530
  const sourceSessionId = updated?.sourceSessionId ?? state?.sourceSessionId;
48521
48531
  if (sourceSessionId) this.deps.onCompleted?.(sourceSessionId);
48522
48532
  }
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
48533
  /**
48541
48534
  * 续派(spec 2026-07-21):complete 已清掉 map 条目,续派前重建——bSessionId 映射
48542
48535
  * 保证 worker 进程死后 respawn 时 reducer lookupDispatchByBSessionId 仍注回同一
48543
48536
  * CLAWD_DISPATCH_ID env。台账行的复位由 handler 直接走 store(本方法只管内存态)。
48544
48537
  */
48545
48538
  reopen(dispatchId, args) {
48546
- const existing = this.map.get(dispatchId);
48547
48539
  this.map.set(dispatchId, {
48548
48540
  dispatchId,
48549
48541
  sourceSessionId: args.sourceSessionId,
48550
48542
  targetPersona: args.targetPersona,
48551
- bSessionId: args.workerSessionId,
48552
- waiters: existing?.waiters ?? []
48543
+ bSessionId: args.workerSessionId
48553
48544
  });
48554
48545
  }
48555
48546
  /** SessionManager 在 createDispatchedSession 时调,建立 dispatchId → B sessionId 映射 */
@@ -48678,13 +48669,17 @@ function dispatchMcpConfigPath(dataDir) {
48678
48669
  function writeDispatchMcpConfig(args) {
48679
48670
  const cfgPath = dispatchMcpConfigPath(args.dataDir);
48680
48671
  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",
48672
+ _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
48673
  mcpServers: {
48683
48674
  "clawd-dispatch": {
48684
48675
  type: "stdio",
48685
48676
  command: "node",
48686
48677
  args: [args.serverScriptPath],
48687
- ...args.logPath ? { env: { CLAWD_DISPATCH_LOG: args.logPath } } : {}
48678
+ env: {
48679
+ CLAWD_DAEMON_TOKEN: args.daemonToken,
48680
+ CLAWD_DAEMON_URL: args.daemonUrl,
48681
+ ...args.logPath ? { CLAWD_DISPATCH_LOG: args.logPath } : {}
48682
+ }
48688
48683
  }
48689
48684
  }
48690
48685
  };
@@ -48702,7 +48697,7 @@ function ticketMcpConfigPath(dataDir) {
48702
48697
  function writeTicketMcpConfig(args) {
48703
48698
  const cfgPath = ticketMcpConfigPath(args.dataDir);
48704
48699
  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 CLAWD_DAEMON_URL + CLAWD_SESSION_ID (cc \u5DF2\u6CE8) fetch /api/rpc/session.currentTurnSender \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",
48700
+ _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
48701
  mcpServers: {
48707
48702
  "clawd-ticket": {
48708
48703
  type: "stdio",
@@ -48712,6 +48707,8 @@ function writeTicketMcpConfig(args) {
48712
48707
  CLAWD_TICKET_API_URL: args.apiBaseUrl,
48713
48708
  CLAWD_TICKET_API_KEY: args.apiKey,
48714
48709
  CLAWD_TICKET_SCENE: "persona",
48710
+ CLAWD_DAEMON_TOKEN: args.daemonToken,
48711
+ CLAWD_DAEMON_URL: args.daemonUrl,
48715
48712
  ...args.logPath ? { CLAWD_TICKET_LOG: args.logPath } : {}
48716
48713
  }
48717
48714
  }
@@ -48737,7 +48734,11 @@ async function writeShiftMcpConfig(args) {
48737
48734
  type: "stdio",
48738
48735
  command: "node",
48739
48736
  args: [args.serverScriptPath],
48740
- ...args.logPath ? { env: { CLAWD_SHIFT_LOG: args.logPath } } : {}
48737
+ env: {
48738
+ CLAWD_DAEMON_TOKEN: args.daemonToken,
48739
+ CLAWD_DAEMON_URL: args.daemonUrl,
48740
+ ...args.logPath ? { CLAWD_SHIFT_LOG: args.logPath } : {}
48741
+ }
48741
48742
  }
48742
48743
  }
48743
48744
  };
@@ -48761,7 +48762,11 @@ async function writeInboxMcpConfig(args) {
48761
48762
  type: "stdio",
48762
48763
  command: "node",
48763
48764
  args: [args.serverScriptPath],
48764
- ...args.logPath ? { env: { CLAWD_INBOX_LOG: args.logPath } } : {}
48765
+ env: {
48766
+ CLAWD_DAEMON_TOKEN: args.daemonToken,
48767
+ CLAWD_DAEMON_URL: args.daemonUrl,
48768
+ ...args.logPath ? { CLAWD_INBOX_LOG: args.logPath } : {}
48769
+ }
48765
48770
  }
48766
48771
  }
48767
48772
  };
@@ -48779,11 +48784,15 @@ function peerOpsMcpConfigPath(dataDir) {
48779
48784
  function writePeerOpsMcpConfig(args) {
48780
48785
  const cfgPath = peerOpsMcpConfigPath(args.dataDir);
48781
48786
  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",
48787
+ _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
48788
  mcpServers: {
48784
48789
  "peer-ops": {
48785
48790
  command: "node",
48786
- args: [args.serverScriptPath]
48791
+ args: [args.serverScriptPath],
48792
+ env: {
48793
+ CLAWD_DAEMON_TOKEN: args.daemonToken,
48794
+ CLAWD_DAEMON_URL: args.daemonUrl
48795
+ }
48787
48796
  }
48788
48797
  }
48789
48798
  };
@@ -48792,9 +48801,37 @@ function writePeerOpsMcpConfig(args) {
48792
48801
  return cfgPath;
48793
48802
  }
48794
48803
 
48804
+ // src/rpc-tool/mcp-config.ts
48805
+ var import_node_fs25 = __toESM(require("fs"), 1);
48806
+ var import_node_path24 = __toESM(require("path"), 1);
48807
+ function rpcToolMcpConfigPath(dataDir) {
48808
+ return import_node_path24.default.join(dataDir, "rpc-tool.mcp.json");
48809
+ }
48810
+ function writeRpcToolMcpConfig(args) {
48811
+ const cfgPath = rpcToolMcpConfigPath(args.dataDir);
48812
+ const content = {
48813
+ _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",
48814
+ mcpServers: {
48815
+ "clawd-rpc": {
48816
+ type: "stdio",
48817
+ command: "node",
48818
+ args: [args.serverScriptPath],
48819
+ env: {
48820
+ CLAWD_DAEMON_TOKEN: args.daemonToken,
48821
+ CLAWD_DAEMON_URL: args.daemonUrl,
48822
+ ...args.logPath ? { CLAWD_RPC_LOG: args.logPath } : {}
48823
+ }
48824
+ }
48825
+ }
48826
+ };
48827
+ import_node_fs25.default.mkdirSync(args.dataDir, { recursive: true });
48828
+ import_node_fs25.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
48829
+ return cfgPath;
48830
+ }
48831
+
48795
48832
  // src/shift/store.ts
48796
48833
  var import_promises2 = __toESM(require("fs/promises"), 1);
48797
- var import_node_path24 = __toESM(require("path"), 1);
48834
+ var import_node_path25 = __toESM(require("path"), 1);
48798
48835
  var import_node_crypto6 = require("crypto");
48799
48836
 
48800
48837
  // src/shift/schedule.ts
@@ -48869,7 +48906,7 @@ function createShiftStore(deps) {
48869
48906
  flushTimer = null;
48870
48907
  }
48871
48908
  const content = { version: 1, shifts };
48872
- await import_promises2.default.mkdir(import_node_path24.default.dirname(deps.filePath), { recursive: true });
48909
+ await import_promises2.default.mkdir(import_node_path25.default.dirname(deps.filePath), { recursive: true });
48873
48910
  const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
48874
48911
  await import_promises2.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
48875
48912
  await import_promises2.default.rename(tmp, deps.filePath);
@@ -49322,7 +49359,7 @@ function buildPersonaDispatchHandlers(deps) {
49322
49359
  }
49323
49360
  if (!sourceSessionId) {
49324
49361
  throw new Error(
49325
- "personaDispatch:run requires sessionId (caller must pass x-clawd-session-id header)"
49362
+ "personaDispatch:run requires sessionId in the request body"
49326
49363
  );
49327
49364
  }
49328
49365
  const rec3 = deps.store.get(args.dispatchId);
@@ -49395,7 +49432,7 @@ function buildPersonaDispatchHandlers(deps) {
49395
49432
  }
49396
49433
  if (!sourceSessionId) {
49397
49434
  throw new Error(
49398
- "personaDispatch:run requires sessionId (caller must pass x-clawd-session-id header)"
49435
+ "personaDispatch:run requires sessionId in the request body"
49399
49436
  );
49400
49437
  }
49401
49438
  const { dispatchId: dispatchId2 } = mgr.start({
@@ -49462,14 +49499,13 @@ function buildPersonaDispatchHandlers(deps) {
49462
49499
  reason: `failed to spawn B: ${reason}`
49463
49500
  });
49464
49501
  });
49465
- const outcome = await mgr.waitForOutcome(dispatchId2);
49466
49502
  return {
49467
- response: { type: "personaDispatch:run:ok", outcome }
49503
+ response: { type: "personaDispatch:run:ok", dispatchId: dispatchId2 }
49468
49504
  };
49469
49505
  }
49470
49506
  if (!sourceSessionId) {
49471
49507
  throw new Error(
49472
- "personaDispatch:run requires sessionId (caller must pass x-clawd-session-id header)"
49508
+ "personaDispatch:run requires sessionId in the request body"
49473
49509
  );
49474
49510
  }
49475
49511
  const { dispatchId } = mgr.start({
@@ -49517,6 +49553,22 @@ function buildPersonaDispatchHandlers(deps) {
49517
49553
  response: { type: "personaDispatch:complete:ok" }
49518
49554
  };
49519
49555
  };
49556
+ const get = async (frame, _client, ctx) => {
49557
+ const { type: _t, requestId: _r, ...rest } = frame;
49558
+ const args = DispatchGetArgsSchema.parse(rest);
49559
+ const rec3 = deps.store.get(args.dispatchId);
49560
+ const denied = ctx?.principal.kind === "guest" && rec3?.sourceSessionId !== ctx.principal.id;
49561
+ if (!rec3 || denied) {
49562
+ throw new ClawdError(ERROR_CODES.DISPATCH_NOT_FOUND, `unknown dispatchId: ${args.dispatchId}`);
49563
+ }
49564
+ return {
49565
+ response: {
49566
+ type: "personaDispatch:get:ok",
49567
+ status: rec3.status,
49568
+ ...rec3.outcome ? { outcome: rec3.outcome } : {}
49569
+ }
49570
+ };
49571
+ };
49520
49572
  const list = async (frame) => {
49521
49573
  const { type: _t, requestId: _r, ...rest } = frame;
49522
49574
  const args = DispatchListArgsSchema.parse(rest);
@@ -49530,7 +49582,8 @@ function buildPersonaDispatchHandlers(deps) {
49530
49582
  return {
49531
49583
  "personaDispatch:run": run,
49532
49584
  "personaDispatch:complete": complete,
49533
- "personaDispatch:list": list
49585
+ "personaDispatch:list": list,
49586
+ "personaDispatch:get": get
49534
49587
  };
49535
49588
  }
49536
49589
 
@@ -49541,8 +49594,13 @@ function wsUrlToHttp(url) {
49541
49594
  if (url.startsWith("ws://")) return "http://" + url.slice("ws://".length);
49542
49595
  return url;
49543
49596
  }
49597
+ var DISPATCH_POLL_INTERVAL_MS = 5e3;
49598
+ var DISPATCH_POLL_UNREACHABLE_MS = 6e5;
49599
+ var DISPATCH_POLL_TOTAL_MS = 864e5;
49600
+ var defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
49544
49601
  async function forwardDispatchToPeer(args) {
49545
49602
  const f = args.fetchImpl ?? fetch;
49603
+ const sleep2 = args.sleepImpl ?? defaultSleep;
49546
49604
  const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
49547
49605
  const url = `${base}/rpc/personaDispatch:run`;
49548
49606
  let res;
@@ -49577,7 +49635,58 @@ async function forwardDispatchToPeer(args) {
49577
49635
  if (json.ok === false) {
49578
49636
  return { kind: "failure", reason: `peer rejected: ${json.error}: ${json.message}` };
49579
49637
  }
49580
- return json.result.outcome;
49638
+ if ("outcome" in json.result) return json.result.outcome;
49639
+ const dispatchId = json.result.dispatchId;
49640
+ let elapsed = 0;
49641
+ let unreachableFor = 0;
49642
+ while (elapsed < DISPATCH_POLL_TOTAL_MS) {
49643
+ await sleep2(DISPATCH_POLL_INTERVAL_MS);
49644
+ elapsed += DISPATCH_POLL_INTERVAL_MS;
49645
+ const markUnreachable = () => {
49646
+ unreachableFor += DISPATCH_POLL_INTERVAL_MS;
49647
+ return unreachableFor >= DISPATCH_POLL_UNREACHABLE_MS ? {
49648
+ kind: "failure",
49649
+ reason: "peer unreachable while polling dispatch outcome (TARGET_UNREACHABLE)"
49650
+ } : null;
49651
+ };
49652
+ let getRes;
49653
+ try {
49654
+ getRes = await f(`${base}/rpc/personaDispatch:get`, {
49655
+ method: "POST",
49656
+ headers: {
49657
+ "content-type": "application/json",
49658
+ authorization: `Bearer ${args.contact.connectToken}`
49659
+ },
49660
+ body: JSON.stringify({ dispatchId })
49661
+ });
49662
+ } catch {
49663
+ const dead = markUnreachable();
49664
+ if (dead) return dead;
49665
+ continue;
49666
+ }
49667
+ let getJson;
49668
+ try {
49669
+ getJson = await getRes.json();
49670
+ } catch {
49671
+ const dead = markUnreachable();
49672
+ if (dead) return dead;
49673
+ continue;
49674
+ }
49675
+ if (getJson.ok === false) {
49676
+ if (getJson.error === ERROR_CODES.DISPATCH_NOT_FOUND) {
49677
+ return { kind: "failure", reason: `peer lost dispatch ${dispatchId} (daemon restarted?)` };
49678
+ }
49679
+ const dead = markUnreachable();
49680
+ if (dead) return dead;
49681
+ continue;
49682
+ }
49683
+ unreachableFor = 0;
49684
+ const { status, outcome } = getJson.result;
49685
+ if (status === "completed" || status === "failed") {
49686
+ return outcome ?? { kind: "failure", reason: "peer returned terminal status without outcome" };
49687
+ }
49688
+ }
49689
+ return { kind: "failure", reason: "dispatch polling exceeded 24h (TIMEOUT)" };
49581
49690
  }
49582
49691
  async function forwardInboxPostToPeer(args) {
49583
49692
  const f = args.fetchImpl ?? fetch;
@@ -49821,7 +49930,7 @@ var CodexHistoryReader = class {
49821
49930
 
49822
49931
  // src/tools/codex-skills.ts
49823
49932
  var import_node_child_process7 = require("child_process");
49824
- var import_node_path25 = __toESM(require("path"), 1);
49933
+ var import_node_path26 = __toESM(require("path"), 1);
49825
49934
  var DEFAULT_TIMEOUT_MS3 = 8e3;
49826
49935
  function str3(v2) {
49827
49936
  return typeof v2 === "string" ? v2 : void 0;
@@ -49837,13 +49946,13 @@ function mapSkillsListResponse(res) {
49837
49946
  const r = s ?? {};
49838
49947
  const name = str3(r.name);
49839
49948
  if (!name) continue;
49840
- const path74 = str3(r.path);
49949
+ const path75 = str3(r.path);
49841
49950
  const description = str3(r.description);
49842
49951
  const isPlugin = name.includes(":");
49843
49952
  out.push({
49844
49953
  name,
49845
49954
  source: isPlugin ? "plugin" : "project",
49846
- ...path74 ? { path: path74 } : {},
49955
+ ...path75 ? { path: path75 } : {},
49847
49956
  ...description ? { description } : {},
49848
49957
  ...isPlugin ? { plugin: name.split(":")[0] } : {}
49849
49958
  });
@@ -49873,7 +49982,7 @@ async function listCodexSkills(cwd, deps = {}) {
49873
49982
  })(),
49874
49983
  timeout
49875
49984
  ]);
49876
- void client.request("skills/extraRoots/set", { extraRoots: [import_node_path25.default.join(cwd, ".claude", "skills")] }).catch(() => void 0);
49985
+ void client.request("skills/extraRoots/set", { extraRoots: [import_node_path26.default.join(cwd, ".claude", "skills")] }).catch(() => void 0);
49877
49986
  const res = await Promise.race([client.request("skills/list", { cwds: [cwd] }), timeout]);
49878
49987
  return mapSkillsListResponse(res);
49879
49988
  } finally {
@@ -49883,23 +49992,23 @@ async function listCodexSkills(cwd, deps = {}) {
49883
49992
  }
49884
49993
 
49885
49994
  // src/workspace/browser.ts
49886
- var import_node_fs25 = __toESM(require("fs"), 1);
49995
+ var import_node_fs26 = __toESM(require("fs"), 1);
49887
49996
  var import_node_os8 = __toESM(require("os"), 1);
49888
- var import_node_path26 = __toESM(require("path"), 1);
49997
+ var import_node_path27 = __toESM(require("path"), 1);
49889
49998
  init_protocol();
49890
49999
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
49891
50000
  function resolveInsideCwd(cwd, subpath) {
49892
- const absCwd = import_node_path26.default.resolve(cwd);
49893
- const joined = import_node_path26.default.resolve(absCwd, subpath ?? ".");
49894
- const rel = import_node_path26.default.relative(absCwd, joined);
49895
- if (rel.startsWith("..") || import_node_path26.default.isAbsolute(rel)) {
50001
+ const absCwd = import_node_path27.default.resolve(cwd);
50002
+ const joined = import_node_path27.default.resolve(absCwd, subpath ?? ".");
50003
+ const rel = import_node_path27.default.relative(absCwd, joined);
50004
+ if (rel.startsWith("..") || import_node_path27.default.isAbsolute(rel)) {
49896
50005
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `path escapes cwd: ${subpath}`);
49897
50006
  }
49898
50007
  return joined;
49899
50008
  }
49900
50009
  function ensureCwd(cwd) {
49901
50010
  try {
49902
- const stat = import_node_fs25.default.statSync(cwd);
50011
+ const stat = import_node_fs26.default.statSync(cwd);
49903
50012
  if (!stat.isDirectory()) {
49904
50013
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `not a directory: ${cwd}`);
49905
50014
  }
@@ -49913,7 +50022,7 @@ var WorkspaceBrowser = class {
49913
50022
  const cwd = args.cwd && args.cwd.length > 0 ? args.cwd : import_node_os8.default.homedir();
49914
50023
  ensureCwd(cwd);
49915
50024
  const full = resolveInsideCwd(cwd, args.path);
49916
- const dirents = import_node_fs25.default.readdirSync(full, { withFileTypes: true });
50025
+ const dirents = import_node_fs26.default.readdirSync(full, { withFileTypes: true });
49917
50026
  const entries = [];
49918
50027
  for (const d of dirents) {
49919
50028
  if (!args.showHidden && d.name.startsWith(".")) continue;
@@ -49923,7 +50032,7 @@ var WorkspaceBrowser = class {
49923
50032
  mtime: ""
49924
50033
  };
49925
50034
  try {
49926
- const st = import_node_fs25.default.statSync(import_node_path26.default.join(full, d.name));
50035
+ const st = import_node_fs26.default.statSync(import_node_path27.default.join(full, d.name));
49927
50036
  entry.mtime = new Date(st.mtimeMs).toISOString();
49928
50037
  if (d.isFile()) entry.size = st.size;
49929
50038
  } catch {
@@ -49939,14 +50048,14 @@ var WorkspaceBrowser = class {
49939
50048
  read(args) {
49940
50049
  ensureCwd(args.cwd);
49941
50050
  const full = resolveInsideCwd(args.cwd, args.path);
49942
- const st = import_node_fs25.default.statSync(full);
50051
+ const st = import_node_fs26.default.statSync(full);
49943
50052
  if (!st.isFile()) {
49944
50053
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `not a file: ${args.path}`);
49945
50054
  }
49946
50055
  if (st.size > MAX_FILE_BYTES) {
49947
50056
  throw new ClawdError(ERROR_CODES.FILE_TOO_LARGE, `file > ${MAX_FILE_BYTES} bytes`);
49948
50057
  }
49949
- const buf = import_node_fs25.default.readFileSync(full);
50058
+ const buf = import_node_fs26.default.readFileSync(full);
49950
50059
  const isBinary = buf.includes(0);
49951
50060
  if (isBinary) {
49952
50061
  return {
@@ -49968,20 +50077,20 @@ var WorkspaceBrowser = class {
49968
50077
  };
49969
50078
 
49970
50079
  // src/skills/agents-scanner.ts
49971
- var import_node_fs26 = __toESM(require("fs"), 1);
50080
+ var import_node_fs27 = __toESM(require("fs"), 1);
49972
50081
  var import_node_os9 = __toESM(require("os"), 1);
49973
- var import_node_path27 = __toESM(require("path"), 1);
50082
+ var import_node_path28 = __toESM(require("path"), 1);
49974
50083
  var DEFAULT_POLICY_DIR_DARWIN = "/Library/Application Support/ClaudeCode/.claude/agents";
49975
50084
  function isDirLikeSync2(p2) {
49976
50085
  try {
49977
- return import_node_fs26.default.statSync(p2).isDirectory();
50086
+ return import_node_fs27.default.statSync(p2).isDirectory();
49978
50087
  } catch {
49979
50088
  return false;
49980
50089
  }
49981
50090
  }
49982
50091
  function fileExistsSync(p2) {
49983
50092
  try {
49984
- return import_node_fs26.default.statSync(p2).isFile();
50093
+ return import_node_fs27.default.statSync(p2).isFile();
49985
50094
  } catch {
49986
50095
  return false;
49987
50096
  }
@@ -49989,7 +50098,7 @@ function fileExistsSync(p2) {
49989
50098
  function parseAgentFile(filePath) {
49990
50099
  let content;
49991
50100
  try {
49992
- content = import_node_fs26.default.readFileSync(filePath, "utf8");
50101
+ content = import_node_fs27.default.readFileSync(filePath, "utf8");
49993
50102
  } catch {
49994
50103
  return {};
49995
50104
  }
@@ -50002,16 +50111,16 @@ function parseAgentFile(filePath) {
50002
50111
  function scanAgentsDir(dir, source, seen, out) {
50003
50112
  let entries;
50004
50113
  try {
50005
- entries = import_node_fs26.default.readdirSync(dir, { withFileTypes: true });
50114
+ entries = import_node_fs27.default.readdirSync(dir, { withFileTypes: true });
50006
50115
  } catch {
50007
50116
  return;
50008
50117
  }
50009
50118
  for (const ent of entries) {
50010
50119
  if (!ent.name.endsWith(".md")) continue;
50011
- if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path27.default.join(dir, ent.name)))) {
50120
+ if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path28.default.join(dir, ent.name)))) {
50012
50121
  continue;
50013
50122
  }
50014
- const filePath = import_node_path27.default.join(dir, ent.name);
50123
+ const filePath = import_node_path28.default.join(dir, ent.name);
50015
50124
  const baseName = ent.name.replace(/\.md$/, "");
50016
50125
  if (seen.has(baseName)) continue;
50017
50126
  seen.add(baseName);
@@ -50029,12 +50138,12 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
50029
50138
  function walk2(dir, namespaces) {
50030
50139
  let entries;
50031
50140
  try {
50032
- entries = import_node_fs26.default.readdirSync(dir, { withFileTypes: true });
50141
+ entries = import_node_fs27.default.readdirSync(dir, { withFileTypes: true });
50033
50142
  } catch {
50034
50143
  return;
50035
50144
  }
50036
50145
  for (const ent of entries) {
50037
- const childPath = import_node_path27.default.join(dir, ent.name);
50146
+ const childPath = import_node_path28.default.join(dir, ent.name);
50038
50147
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync2(childPath)) {
50039
50148
  walk2(childPath, [...namespaces, ent.name]);
50040
50149
  continue;
@@ -50059,13 +50168,13 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
50059
50168
  walk2(root, []);
50060
50169
  }
50061
50170
  function readInstalledPlugins2(home) {
50062
- const pluginsDir = import_node_path27.default.join(home, ".claude", "plugins");
50063
- const v2 = import_node_path27.default.join(pluginsDir, "installed_plugins_v2.json");
50064
- const v1 = import_node_path27.default.join(pluginsDir, "installed_plugins.json");
50171
+ const pluginsDir = import_node_path28.default.join(home, ".claude", "plugins");
50172
+ const v2 = import_node_path28.default.join(pluginsDir, "installed_plugins_v2.json");
50173
+ const v1 = import_node_path28.default.join(pluginsDir, "installed_plugins.json");
50065
50174
  let raw = null;
50066
50175
  for (const candidate of [v2, v1]) {
50067
50176
  try {
50068
- raw = import_node_fs26.default.readFileSync(candidate, "utf8");
50177
+ raw = import_node_fs27.default.readFileSync(candidate, "utf8");
50069
50178
  break;
50070
50179
  } catch {
50071
50180
  }
@@ -50090,19 +50199,19 @@ function readInstalledPlugins2(home) {
50090
50199
  return out;
50091
50200
  }
50092
50201
  function walkUpProjectAgentsDirs(startCwd, home, seen, out) {
50093
- let cur = import_node_path27.default.resolve(startCwd);
50094
- const fsRoot = import_node_path27.default.parse(cur).root;
50202
+ let cur = import_node_path28.default.resolve(startCwd);
50203
+ const fsRoot = import_node_path28.default.parse(cur).root;
50095
50204
  while (true) {
50096
- scanAgentsDir(import_node_path27.default.join(cur, ".claude", "agents"), "project", seen, out);
50205
+ scanAgentsDir(import_node_path28.default.join(cur, ".claude", "agents"), "project", seen, out);
50097
50206
  let hasGit = false;
50098
50207
  try {
50099
- hasGit = import_node_fs26.default.existsSync(import_node_path27.default.join(cur, ".git"));
50208
+ hasGit = import_node_fs27.default.existsSync(import_node_path28.default.join(cur, ".git"));
50100
50209
  } catch {
50101
50210
  }
50102
50211
  if (hasGit) return;
50103
50212
  if (cur === home) return;
50104
50213
  if (cur === fsRoot) return;
50105
- const parent = import_node_path27.default.dirname(cur);
50214
+ const parent = import_node_path28.default.dirname(cur);
50106
50215
  if (parent === cur) return;
50107
50216
  cur = parent;
50108
50217
  }
@@ -50137,7 +50246,7 @@ var AgentsScanner = class {
50137
50246
  }
50138
50247
  const fsBlock = [];
50139
50248
  scanAgentsDir(
50140
- import_node_path27.default.join(this.home, ".claude", "agents"),
50249
+ import_node_path28.default.join(this.home, ".claude", "agents"),
50141
50250
  "global",
50142
50251
  seen,
50143
50252
  fsBlock
@@ -50151,7 +50260,7 @@ var AgentsScanner = class {
50151
50260
  ...this.extraPluginRoots
50152
50261
  ];
50153
50262
  for (const { name, root } of plugins) {
50154
- const agentsRoot = import_node_path27.default.join(root, "agents");
50263
+ const agentsRoot = import_node_path28.default.join(root, "agents");
50155
50264
  scanPluginAgentsTree(agentsRoot, name, seen, fsBlock);
50156
50265
  }
50157
50266
  return [...builtinBlock, ...fsBlock];
@@ -50159,15 +50268,15 @@ var AgentsScanner = class {
50159
50268
  };
50160
50269
 
50161
50270
  // src/observer/session-observer.ts
50162
- var import_node_fs28 = __toESM(require("fs"), 1);
50271
+ var import_node_fs29 = __toESM(require("fs"), 1);
50163
50272
  var import_node_os11 = __toESM(require("os"), 1);
50164
- var import_node_path29 = __toESM(require("path"), 1);
50273
+ var import_node_path30 = __toESM(require("path"), 1);
50165
50274
  init_claude_history();
50166
50275
 
50167
50276
  // src/observer/subagent-meta-observer.ts
50168
- var import_node_fs27 = __toESM(require("fs"), 1);
50277
+ var import_node_fs28 = __toESM(require("fs"), 1);
50169
50278
  var import_node_os10 = __toESM(require("os"), 1);
50170
- var import_node_path28 = __toESM(require("path"), 1);
50279
+ var import_node_path29 = __toESM(require("path"), 1);
50171
50280
  init_claude_history();
50172
50281
  var META_RE = /^agent-([A-Za-z0-9_-]+)\.meta\.json$/;
50173
50282
  var SubagentMetaObserver = class {
@@ -50180,7 +50289,7 @@ var SubagentMetaObserver = class {
50180
50289
  watches = /* @__PURE__ */ new Map();
50181
50290
  // public for spec only:测试直接拼路径写假 meta.json;生产 start() 内部自己解析
50182
50291
  resolveSubagentDir(cwd, toolSessionId) {
50183
- return import_node_path28.default.join(
50292
+ return import_node_path29.default.join(
50184
50293
  this.home,
50185
50294
  ".claude",
50186
50295
  "projects",
@@ -50210,7 +50319,7 @@ var SubagentMetaObserver = class {
50210
50319
  attachWatcher(w2) {
50211
50320
  if (w2.watcher) return;
50212
50321
  try {
50213
- w2.watcher = import_node_fs27.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
50322
+ w2.watcher = import_node_fs28.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
50214
50323
  if (!name) return;
50215
50324
  const m2 = META_RE.exec(String(name));
50216
50325
  if (!m2) return;
@@ -50222,7 +50331,7 @@ var SubagentMetaObserver = class {
50222
50331
  scan(w2) {
50223
50332
  let entries;
50224
50333
  try {
50225
- entries = import_node_fs27.default.readdirSync(w2.dirPath);
50334
+ entries = import_node_fs28.default.readdirSync(w2.dirPath);
50226
50335
  } catch {
50227
50336
  return;
50228
50337
  }
@@ -50236,10 +50345,10 @@ var SubagentMetaObserver = class {
50236
50345
  if (!m2) return;
50237
50346
  const agentId = m2[1];
50238
50347
  if (w2.emitted.has(agentId)) return;
50239
- const file = import_node_path28.default.join(w2.dirPath, name);
50348
+ const file = import_node_path29.default.join(w2.dirPath, name);
50240
50349
  let raw;
50241
50350
  try {
50242
- raw = import_node_fs27.default.readFileSync(file, "utf8");
50351
+ raw = import_node_fs28.default.readFileSync(file, "utf8");
50243
50352
  } catch {
50244
50353
  return;
50245
50354
  }
@@ -50296,14 +50405,14 @@ var SessionObserver = class {
50296
50405
  metaObserver;
50297
50406
  resolveJsonlPath(cwd, toolSessionId, override) {
50298
50407
  if (override) return override;
50299
- return import_node_path29.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
50408
+ return import_node_path30.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
50300
50409
  }
50301
50410
  start(args) {
50302
50411
  this.stop(args.sessionId);
50303
50412
  const filePath = this.resolveJsonlPath(args.cwd, args.toolSessionId, args.jsonlPath);
50304
50413
  let size = 0;
50305
50414
  try {
50306
- size = import_node_fs28.default.statSync(filePath).size;
50415
+ size = import_node_fs29.default.statSync(filePath).size;
50307
50416
  } catch {
50308
50417
  }
50309
50418
  const w2 = {
@@ -50316,16 +50425,16 @@ var SessionObserver = class {
50316
50425
  adapter: args.adapter,
50317
50426
  prevIsRejectSentinel: false,
50318
50427
  toolSessionId: args.toolSessionId,
50319
- sawFile: import_node_fs28.default.existsSync(filePath),
50428
+ sawFile: import_node_fs29.default.existsSync(filePath),
50320
50429
  lastRelocateScanMs: 0,
50321
50430
  pendingCwdDerive: false,
50322
50431
  lastDeriveMs: 0
50323
50432
  };
50324
50433
  try {
50325
- import_node_fs28.default.mkdirSync(import_node_path29.default.dirname(filePath), { recursive: true });
50434
+ import_node_fs29.default.mkdirSync(import_node_path30.default.dirname(filePath), { recursive: true });
50326
50435
  } catch {
50327
50436
  }
50328
- w2.watcher = import_node_fs28.default.watch(import_node_path29.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
50437
+ w2.watcher = import_node_fs29.default.watch(import_node_path30.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
50329
50438
  if (!changedName || !w2.filePath.endsWith(changedName)) return;
50330
50439
  this.poll(w2);
50331
50440
  });
@@ -50348,7 +50457,7 @@ var SessionObserver = class {
50348
50457
  // 异常静默吞,不阻塞 watcher 启动
50349
50458
  hydrateMetaTail(w2, maxLines = 200) {
50350
50459
  try {
50351
- const raw = import_node_fs28.default.readFileSync(w2.filePath, "utf8");
50460
+ const raw = import_node_fs29.default.readFileSync(w2.filePath, "utf8");
50352
50461
  if (!raw) return;
50353
50462
  const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
50354
50463
  if (allLines.length === 0) return;
@@ -50372,7 +50481,7 @@ var SessionObserver = class {
50372
50481
  poll(w2) {
50373
50482
  let size = 0;
50374
50483
  try {
50375
- size = import_node_fs28.default.statSync(w2.filePath).size;
50484
+ size = import_node_fs29.default.statSync(w2.filePath).size;
50376
50485
  } catch {
50377
50486
  if (w2.sawFile) this.maybeRelocate(w2);
50378
50487
  return;
@@ -50383,11 +50492,11 @@ var SessionObserver = class {
50383
50492
  w2.buf = "";
50384
50493
  }
50385
50494
  if (size === w2.lastSize) return;
50386
- const fd = import_node_fs28.default.openSync(w2.filePath, "r");
50495
+ const fd = import_node_fs29.default.openSync(w2.filePath, "r");
50387
50496
  try {
50388
50497
  const len = size - w2.lastSize;
50389
50498
  const buf = Buffer.alloc(len);
50390
- import_node_fs28.default.readSync(fd, buf, 0, len, w2.lastSize);
50499
+ import_node_fs29.default.readSync(fd, buf, 0, len, w2.lastSize);
50391
50500
  w2.lastSize = size;
50392
50501
  w2.buf += buf.toString("utf8");
50393
50502
  let newlineIndex;
@@ -50410,7 +50519,7 @@ var SessionObserver = class {
50410
50519
  }
50411
50520
  }
50412
50521
  } finally {
50413
- import_node_fs28.default.closeSync(fd);
50522
+ import_node_fs29.default.closeSync(fd);
50414
50523
  }
50415
50524
  if (w2.pendingCwdDerive) this.maybeDeriveCwd(w2);
50416
50525
  }
@@ -50422,7 +50531,7 @@ var SessionObserver = class {
50422
50531
  const now = Date.now();
50423
50532
  if (now - w2.lastRelocateScanMs < 1e3) return;
50424
50533
  w2.lastRelocateScanMs = now;
50425
- const projectsRoot = import_node_path29.default.join(this.home, ".claude", "projects");
50534
+ const projectsRoot = import_node_path30.default.join(this.home, ".claude", "projects");
50426
50535
  const found = findTranscriptByToolSessionId(projectsRoot, w2.toolSessionId);
50427
50536
  if (!found || found === w2.filePath) return;
50428
50537
  try {
@@ -50431,7 +50540,7 @@ var SessionObserver = class {
50431
50540
  }
50432
50541
  w2.filePath = found;
50433
50542
  try {
50434
- w2.watcher = import_node_fs28.default.watch(import_node_path29.default.dirname(found), { persistent: false }, (_event, changedName) => {
50543
+ w2.watcher = import_node_fs29.default.watch(import_node_path30.default.dirname(found), { persistent: false }, (_event, changedName) => {
50435
50544
  if (!changedName || !w2.filePath.endsWith(changedName)) return;
50436
50545
  this.poll(w2);
50437
50546
  });
@@ -50998,79 +51107,6 @@ function removeSubscription(client, sessionId) {
50998
51107
  else client.subscribedSessions.set(sessionId, next);
50999
51108
  }
51000
51109
 
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
51110
  // src/transport/auth.ts
51075
51111
  init_protocol();
51076
51112
  var AuthGate = class {
@@ -51287,8 +51323,8 @@ async function authenticate(token, deps) {
51287
51323
  }
51288
51324
 
51289
51325
  // src/legacy-cleanup.ts
51290
- var fs33 = __toESM(require("fs/promises"), 1);
51291
- var path34 = __toESM(require("path"), 1);
51326
+ var fs34 = __toESM(require("fs/promises"), 1);
51327
+ var path35 = __toESM(require("path"), 1);
51292
51328
  var LEGACY_FILES = [
51293
51329
  "personal-capability.json",
51294
51330
  "received-capabilities.json",
@@ -51297,9 +51333,9 @@ var LEGACY_FILES = [
51297
51333
  async function cleanupLegacyCapabilityFiles(dataDir, logger) {
51298
51334
  const removed = [];
51299
51335
  for (const name of LEGACY_FILES) {
51300
- const p2 = path34.join(dataDir, name);
51336
+ const p2 = path35.join(dataDir, name);
51301
51337
  try {
51302
- await fs33.unlink(p2);
51338
+ await fs34.unlink(p2);
51303
51339
  removed.push(name);
51304
51340
  logger.debug("legacy capability file removed", { file: name });
51305
51341
  } catch (err) {
@@ -51311,13 +51347,13 @@ async function cleanupLegacyCapabilityFiles(dataDir, logger) {
51311
51347
  }
51312
51348
 
51313
51349
  // src/inbox/inbox-store.ts
51314
- var fs34 = __toESM(require("fs"), 1);
51315
- var path35 = __toESM(require("path"), 1);
51350
+ var fs35 = __toESM(require("fs"), 1);
51351
+ var path36 = __toESM(require("path"), 1);
51316
51352
  var INBOX_SUBDIR = "inbox";
51317
51353
  var InboxStore = class {
51318
51354
  constructor(dataDir) {
51319
51355
  this.dataDir = dataDir;
51320
- fs34.mkdirSync(this.dirPath(), { recursive: true });
51356
+ fs35.mkdirSync(this.dirPath(), { recursive: true });
51321
51357
  }
51322
51358
  dataDir;
51323
51359
  /**
@@ -51329,7 +51365,7 @@ var InboxStore = class {
51329
51365
  const file = this.filePath(peerDeviceId);
51330
51366
  let raw;
51331
51367
  try {
51332
- raw = fs34.readFileSync(file, "utf8");
51368
+ raw = fs35.readFileSync(file, "utf8");
51333
51369
  } catch (err) {
51334
51370
  if (err?.code === "ENOENT") return [];
51335
51371
  return [];
@@ -51345,7 +51381,7 @@ var InboxStore = class {
51345
51381
  const dir = this.dirPath();
51346
51382
  let entries;
51347
51383
  try {
51348
- entries = fs34.readdirSync(dir);
51384
+ entries = fs35.readdirSync(dir);
51349
51385
  } catch (err) {
51350
51386
  if (err?.code === "ENOENT") return [];
51351
51387
  return [];
@@ -51361,9 +51397,9 @@ var InboxStore = class {
51361
51397
  if (existing.some((m2) => m2.id === message.id)) return;
51362
51398
  const file = this.filePath(message.peerDeviceId);
51363
51399
  const line = JSON.stringify(message) + "\n";
51364
- fs34.appendFileSync(file, line, { mode: 384 });
51400
+ fs35.appendFileSync(file, line, { mode: 384 });
51365
51401
  try {
51366
- fs34.chmodSync(file, 384);
51402
+ fs35.chmodSync(file, 384);
51367
51403
  } catch {
51368
51404
  }
51369
51405
  }
@@ -51393,7 +51429,7 @@ var InboxStore = class {
51393
51429
  removeByPeerDeviceId(peerDeviceId) {
51394
51430
  const file = this.filePath(peerDeviceId);
51395
51431
  try {
51396
- fs34.unlinkSync(file);
51432
+ fs35.unlinkSync(file);
51397
51433
  } catch (err) {
51398
51434
  if (err?.code === "ENOENT") return;
51399
51435
  }
@@ -51402,18 +51438,18 @@ var InboxStore = class {
51402
51438
  const file = this.filePath(peerDeviceId);
51403
51439
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
51404
51440
  const content = messages.map((m2) => JSON.stringify(m2)).join("\n") + (messages.length > 0 ? "\n" : "");
51405
- fs34.writeFileSync(tmp, content, { mode: 384 });
51406
- fs34.renameSync(tmp, file);
51441
+ fs35.writeFileSync(tmp, content, { mode: 384 });
51442
+ fs35.renameSync(tmp, file);
51407
51443
  try {
51408
- fs34.chmodSync(file, 384);
51444
+ fs35.chmodSync(file, 384);
51409
51445
  } catch {
51410
51446
  }
51411
51447
  }
51412
51448
  dirPath() {
51413
- return path35.join(this.dataDir, INBOX_SUBDIR);
51449
+ return path36.join(this.dataDir, INBOX_SUBDIR);
51414
51450
  }
51415
51451
  filePath(peerDeviceId) {
51416
- return path35.join(this.dirPath(), `${peerDeviceId}.jsonl`);
51452
+ return path36.join(this.dirPath(), `${peerDeviceId}.jsonl`);
51417
51453
  }
51418
51454
  };
51419
51455
  function parseAllLines(raw) {
@@ -51501,8 +51537,8 @@ var InboxManager = class {
51501
51537
  };
51502
51538
 
51503
51539
  // src/state/contact-store.ts
51504
- var fs35 = __toESM(require("fs"), 1);
51505
- var path36 = __toESM(require("path"), 1);
51540
+ var fs36 = __toESM(require("fs"), 1);
51541
+ var path37 = __toESM(require("path"), 1);
51506
51542
  var FILE_NAME = "contacts.json";
51507
51543
  var ContactStore = class {
51508
51544
  constructor(dataDir) {
@@ -51512,10 +51548,10 @@ var ContactStore = class {
51512
51548
  contacts = /* @__PURE__ */ new Map();
51513
51549
  load() {
51514
51550
  this.contacts.clear();
51515
- const file = path36.join(this.dataDir, FILE_NAME);
51551
+ const file = path37.join(this.dataDir, FILE_NAME);
51516
51552
  let raw;
51517
51553
  try {
51518
- raw = fs35.readFileSync(file, "utf8");
51554
+ raw = fs36.readFileSync(file, "utf8");
51519
51555
  } catch (err) {
51520
51556
  if (err?.code !== "ENOENT") this.renameBak(file);
51521
51557
  return;
@@ -51584,20 +51620,20 @@ var ContactStore = class {
51584
51620
  return true;
51585
51621
  }
51586
51622
  flush() {
51587
- const file = path36.join(this.dataDir, FILE_NAME);
51623
+ const file = path37.join(this.dataDir, FILE_NAME);
51588
51624
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
51589
51625
  const content = JSON.stringify(
51590
51626
  { contacts: Array.from(this.contacts.values()) },
51591
51627
  null,
51592
51628
  2
51593
51629
  );
51594
- fs35.mkdirSync(this.dataDir, { recursive: true });
51595
- fs35.writeFileSync(tmp, content, { mode: 384 });
51596
- fs35.renameSync(tmp, file);
51630
+ fs36.mkdirSync(this.dataDir, { recursive: true });
51631
+ fs36.writeFileSync(tmp, content, { mode: 384 });
51632
+ fs36.renameSync(tmp, file);
51597
51633
  }
51598
51634
  renameBak(file) {
51599
51635
  try {
51600
- fs35.renameSync(file, `${file}.bak`);
51636
+ fs36.renameSync(file, `${file}.bak`);
51601
51637
  } catch {
51602
51638
  }
51603
51639
  }
@@ -51705,61 +51741,61 @@ async function connectRemote(args) {
51705
51741
  }
51706
51742
 
51707
51743
  // src/migrations/2026-05-20-flatten-sessions.ts
51708
- var fs36 = __toESM(require("fs"), 1);
51709
- var path37 = __toESM(require("path"), 1);
51744
+ var fs37 = __toESM(require("fs"), 1);
51745
+ var path38 = __toESM(require("path"), 1);
51710
51746
  var MIGRATION_FLAG_NAME = ".migration.v1.done";
51711
51747
  function migrateFlattenSessions(opts) {
51712
51748
  const dataDir = opts.dataDir;
51713
51749
  const now = opts.now ?? Date.now;
51714
- const sessionsDir = path37.join(dataDir, "sessions");
51715
- const flagPath = path37.join(sessionsDir, MIGRATION_FLAG_NAME);
51750
+ const sessionsDir = path38.join(dataDir, "sessions");
51751
+ const flagPath = path38.join(sessionsDir, MIGRATION_FLAG_NAME);
51716
51752
  if (existsSync3(flagPath)) {
51717
51753
  return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
51718
51754
  }
51719
51755
  let movedBare = 0;
51720
51756
  let movedVmOwner = 0;
51721
51757
  let archivedListener = 0;
51722
- const defaultDir = path37.join(sessionsDir, "default");
51758
+ const defaultDir = path38.join(sessionsDir, "default");
51723
51759
  if (existsSync3(defaultDir)) {
51724
51760
  for (const entry of readdirSafe(defaultDir)) {
51725
51761
  if (!entry.endsWith(".json")) continue;
51726
- const src = path37.join(defaultDir, entry);
51727
- const dst = path37.join(sessionsDir, entry);
51728
- fs36.renameSync(src, dst);
51762
+ const src = path38.join(defaultDir, entry);
51763
+ const dst = path38.join(sessionsDir, entry);
51764
+ fs37.renameSync(src, dst);
51729
51765
  movedBare += 1;
51730
51766
  }
51731
51767
  rmdirIfEmpty(defaultDir);
51732
51768
  }
51733
51769
  for (const pid of readdirSafe(sessionsDir)) {
51734
- const personaDir = path37.join(sessionsDir, pid);
51770
+ const personaDir = path38.join(sessionsDir, pid);
51735
51771
  if (!isDir(personaDir)) continue;
51736
51772
  if (pid === "default") continue;
51737
- const ownerSrc = path37.join(personaDir, "owner");
51773
+ const ownerSrc = path38.join(personaDir, "owner");
51738
51774
  if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
51739
- const ownerDst = path37.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
51740
- fs36.mkdirSync(ownerDst, { recursive: true });
51775
+ const ownerDst = path38.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
51776
+ fs37.mkdirSync(ownerDst, { recursive: true });
51741
51777
  for (const file of readdirSafe(ownerSrc)) {
51742
51778
  if (!file.endsWith(".json")) continue;
51743
- fs36.renameSync(path37.join(ownerSrc, file), path37.join(ownerDst, file));
51779
+ fs37.renameSync(path38.join(ownerSrc, file), path38.join(ownerDst, file));
51744
51780
  movedVmOwner += 1;
51745
51781
  }
51746
51782
  rmdirIfEmpty(ownerSrc);
51747
51783
  }
51748
- const listenerSrc = path37.join(personaDir, "listener");
51784
+ const listenerSrc = path38.join(personaDir, "listener");
51749
51785
  if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
51750
- const archiveDst = path37.join(dataDir, ".legacy", `listener-${pid}`);
51751
- fs36.mkdirSync(archiveDst, { recursive: true });
51786
+ const archiveDst = path38.join(dataDir, ".legacy", `listener-${pid}`);
51787
+ fs37.mkdirSync(archiveDst, { recursive: true });
51752
51788
  for (const file of readdirSafe(listenerSrc)) {
51753
51789
  if (!file.endsWith(".json")) continue;
51754
- fs36.renameSync(path37.join(listenerSrc, file), path37.join(archiveDst, file));
51790
+ fs37.renameSync(path38.join(listenerSrc, file), path38.join(archiveDst, file));
51755
51791
  archivedListener += 1;
51756
51792
  }
51757
51793
  rmdirIfEmpty(listenerSrc);
51758
51794
  }
51759
51795
  rmdirIfEmpty(personaDir);
51760
51796
  }
51761
- fs36.mkdirSync(sessionsDir, { recursive: true });
51762
- fs36.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
51797
+ fs37.mkdirSync(sessionsDir, { recursive: true });
51798
+ fs37.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
51763
51799
  return {
51764
51800
  skipped: false,
51765
51801
  flagWritten: true,
@@ -51770,7 +51806,7 @@ function migrateFlattenSessions(opts) {
51770
51806
  }
51771
51807
  function existsSync3(p2) {
51772
51808
  try {
51773
- fs36.statSync(p2);
51809
+ fs37.statSync(p2);
51774
51810
  return true;
51775
51811
  } catch {
51776
51812
  return false;
@@ -51778,31 +51814,31 @@ function existsSync3(p2) {
51778
51814
  }
51779
51815
  function isDir(p2) {
51780
51816
  try {
51781
- return fs36.statSync(p2).isDirectory();
51817
+ return fs37.statSync(p2).isDirectory();
51782
51818
  } catch {
51783
51819
  return false;
51784
51820
  }
51785
51821
  }
51786
51822
  function readdirSafe(p2) {
51787
51823
  try {
51788
- return fs36.readdirSync(p2);
51824
+ return fs37.readdirSync(p2);
51789
51825
  } catch {
51790
51826
  return [];
51791
51827
  }
51792
51828
  }
51793
51829
  function rmdirIfEmpty(p2) {
51794
51830
  try {
51795
- fs36.rmdirSync(p2);
51831
+ fs37.rmdirSync(p2);
51796
51832
  } catch {
51797
51833
  }
51798
51834
  }
51799
51835
 
51800
51836
  // src/transport/http-router.ts
51801
- var import_node_fs30 = __toESM(require("fs"), 1);
51802
- var import_node_path33 = __toESM(require("path"), 1);
51837
+ var import_node_fs31 = __toESM(require("fs"), 1);
51838
+ var import_node_path34 = __toESM(require("path"), 1);
51803
51839
 
51804
51840
  // src/attachment/mime.ts
51805
- var import_node_path30 = __toESM(require("path"), 1);
51841
+ var import_node_path31 = __toESM(require("path"), 1);
51806
51842
  var TEXT_PLAIN = "text/plain; charset=utf-8";
51807
51843
  var EXT_TO_NATIVE_MIME = {
51808
51844
  // 图片
@@ -51909,7 +51945,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
51909
51945
  ".mk"
51910
51946
  ]);
51911
51947
  function lookupMime(filePathOrName) {
51912
- const ext = import_node_path30.default.extname(filePathOrName).toLowerCase();
51948
+ const ext = import_node_path31.default.extname(filePathOrName).toLowerCase();
51913
51949
  if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
51914
51950
  if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
51915
51951
  return "application/octet-stream";
@@ -51931,8 +51967,9 @@ function encodeAbsPathForUrl(absPath) {
51931
51967
  function decodeAbsPathFromUrl(encoded) {
51932
51968
  return encoded.split("/").map(decodeURIComponent).join("/");
51933
51969
  }
51970
+ var DOMAIN_PREFIX2 = "clawd-attach-v1|";
51934
51971
  function computeSig(secret, absPath, e) {
51935
- const msg = e === null ? absPath : `${absPath}|${e}`;
51972
+ const msg = e === null ? `${DOMAIN_PREFIX2}${absPath}` : `${DOMAIN_PREFIX2}${absPath}|${e}`;
51936
51973
  return import_node_crypto7.default.createHmac(HMAC_ALGO, secret).update(msg).digest();
51937
51974
  }
51938
51975
  function signUrlParts(secret, absPath, ttlSeconds, now = Date.now) {
@@ -51978,8 +52015,8 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
51978
52015
  }
51979
52016
 
51980
52017
  // src/attachment/upload.ts
51981
- var import_node_fs29 = __toESM(require("fs"), 1);
51982
- var import_node_path31 = __toESM(require("path"), 1);
52018
+ var import_node_fs30 = __toESM(require("fs"), 1);
52019
+ var import_node_path32 = __toESM(require("path"), 1);
51983
52020
  var import_node_crypto8 = __toESM(require("crypto"), 1);
51984
52021
  var import_promises3 = require("stream/promises");
51985
52022
  var UploadError = class extends Error {
@@ -51991,22 +52028,22 @@ var UploadError = class extends Error {
51991
52028
  code;
51992
52029
  };
51993
52030
  function assertValidFileName(fileName) {
51994
- if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path31.default.basename(fileName)) {
52031
+ if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path32.default.basename(fileName)) {
51995
52032
  throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
51996
52033
  }
51997
52034
  }
51998
52035
  var HASH_PREFIX_LEN = 16;
51999
52036
  async function writeUploadedAttachment(args) {
52000
52037
  assertValidFileName(args.fileName);
52001
- const attachmentsRoot = import_node_path31.default.join(args.sessionDir, ".attachments");
52038
+ const attachmentsRoot = import_node_path32.default.join(args.sessionDir, ".attachments");
52002
52039
  try {
52003
- import_node_fs29.default.mkdirSync(attachmentsRoot, { recursive: true });
52040
+ import_node_fs30.default.mkdirSync(attachmentsRoot, { recursive: true });
52004
52041
  } catch (err) {
52005
52042
  throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
52006
52043
  }
52007
52044
  const hasher = import_node_crypto8.default.createHash("sha256");
52008
52045
  let actualSize = 0;
52009
- const tmpPath = import_node_path31.default.join(
52046
+ const tmpPath = import_node_path32.default.join(
52010
52047
  attachmentsRoot,
52011
52048
  `.upload-${process.pid}-${Date.now()}-${import_node_crypto8.default.randomBytes(4).toString("hex")}`
52012
52049
  );
@@ -52021,18 +52058,18 @@ async function writeUploadedAttachment(args) {
52021
52058
  yield buf;
52022
52059
  }
52023
52060
  },
52024
- import_node_fs29.default.createWriteStream(tmpPath, { mode: 384 })
52061
+ import_node_fs30.default.createWriteStream(tmpPath, { mode: 384 })
52025
52062
  );
52026
52063
  } catch (err) {
52027
52064
  try {
52028
- import_node_fs29.default.unlinkSync(tmpPath);
52065
+ import_node_fs30.default.unlinkSync(tmpPath);
52029
52066
  } catch {
52030
52067
  }
52031
52068
  throw new UploadError("STORAGE_ERROR", `write failed: ${err.message}`);
52032
52069
  }
52033
52070
  if (actualSize !== args.contentLength) {
52034
52071
  try {
52035
- import_node_fs29.default.unlinkSync(tmpPath);
52072
+ import_node_fs30.default.unlinkSync(tmpPath);
52036
52073
  } catch {
52037
52074
  }
52038
52075
  throw new UploadError(
@@ -52041,35 +52078,35 @@ async function writeUploadedAttachment(args) {
52041
52078
  );
52042
52079
  }
52043
52080
  const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
52044
- const hashDir = import_node_path31.default.join(attachmentsRoot, attachmentId);
52081
+ const hashDir = import_node_path32.default.join(attachmentsRoot, attachmentId);
52045
52082
  let finalFileName;
52046
52083
  let hashDirExists = false;
52047
52084
  try {
52048
- hashDirExists = import_node_fs29.default.statSync(hashDir).isDirectory();
52085
+ hashDirExists = import_node_fs30.default.statSync(hashDir).isDirectory();
52049
52086
  } catch {
52050
52087
  }
52051
52088
  if (hashDirExists) {
52052
- const existing = import_node_fs29.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
52089
+ const existing = import_node_fs30.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
52053
52090
  finalFileName = existing[0] ?? args.fileName;
52054
52091
  try {
52055
- import_node_fs29.default.unlinkSync(tmpPath);
52092
+ import_node_fs30.default.unlinkSync(tmpPath);
52056
52093
  } catch {
52057
52094
  }
52058
52095
  } else {
52059
52096
  try {
52060
- import_node_fs29.default.mkdirSync(hashDir, { recursive: true });
52097
+ import_node_fs30.default.mkdirSync(hashDir, { recursive: true });
52061
52098
  finalFileName = args.fileName;
52062
- import_node_fs29.default.renameSync(tmpPath, import_node_path31.default.join(hashDir, finalFileName));
52099
+ import_node_fs30.default.renameSync(tmpPath, import_node_path32.default.join(hashDir, finalFileName));
52063
52100
  } catch (err) {
52064
52101
  try {
52065
- import_node_fs29.default.unlinkSync(tmpPath);
52102
+ import_node_fs30.default.unlinkSync(tmpPath);
52066
52103
  } catch {
52067
52104
  }
52068
52105
  throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
52069
52106
  }
52070
52107
  }
52071
- const absPath = import_node_path31.default.join(hashDir, finalFileName);
52072
- const relPath = import_node_path31.default.relative(args.sessionDir, absPath);
52108
+ const absPath = import_node_path32.default.join(hashDir, finalFileName);
52109
+ const relPath = import_node_path32.default.relative(args.sessionDir, absPath);
52073
52110
  args.groupFileStore.upsert(args.scope, args.sessionId, {
52074
52111
  relPath: absPath,
52075
52112
  // 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
@@ -52083,7 +52120,7 @@ async function writeUploadedAttachment(args) {
52083
52120
 
52084
52121
  // src/extension/import.ts
52085
52122
  var import_promises4 = __toESM(require("fs/promises"), 1);
52086
- var import_node_path32 = __toESM(require("path"), 1);
52123
+ var import_node_path33 = __toESM(require("path"), 1);
52087
52124
  var import_node_os12 = __toESM(require("os"), 1);
52088
52125
  var import_jszip = __toESM(require_lib3(), 1);
52089
52126
  var ImportError = class extends Error {
@@ -52101,7 +52138,7 @@ async function importZip(buf, root) {
52101
52138
  throw new ImportError("ZIP_INVALID", `failed to load zip: ${e.message}`);
52102
52139
  }
52103
52140
  for (const name of Object.keys(zip.files)) {
52104
- if (name.includes("..") || name.startsWith("/") || import_node_path32.default.isAbsolute(name)) {
52141
+ if (name.includes("..") || name.startsWith("/") || import_node_path33.default.isAbsolute(name)) {
52105
52142
  throw new ImportError("ZIP_INVALID", `unsafe zip entry path: ${name}`);
52106
52143
  }
52107
52144
  }
@@ -52134,7 +52171,7 @@ async function importZip(buf, root) {
52134
52171
  );
52135
52172
  }
52136
52173
  }
52137
- const destDir = import_node_path32.default.join(root, manifest.id);
52174
+ const destDir = import_node_path33.default.join(root, manifest.id);
52138
52175
  let destExists = false;
52139
52176
  try {
52140
52177
  await import_promises4.default.access(destDir);
@@ -52144,15 +52181,15 @@ async function importZip(buf, root) {
52144
52181
  if (destExists) {
52145
52182
  throw new ImportError("ALREADY_EXISTS", `extension ${manifest.id} already installed`);
52146
52183
  }
52147
- const stage = await import_promises4.default.mkdtemp(import_node_path32.default.join(import_node_os12.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
52184
+ const stage = await import_promises4.default.mkdtemp(import_node_path33.default.join(import_node_os12.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
52148
52185
  try {
52149
52186
  for (const [name, entry] of Object.entries(zip.files)) {
52150
- const dest = import_node_path32.default.join(stage, name);
52187
+ const dest = import_node_path33.default.join(stage, name);
52151
52188
  if (entry.dir) {
52152
52189
  await import_promises4.default.mkdir(dest, { recursive: true });
52153
52190
  continue;
52154
52191
  }
52155
- await import_promises4.default.mkdir(import_node_path32.default.dirname(dest), { recursive: true });
52192
+ await import_promises4.default.mkdir(import_node_path33.default.dirname(dest), { recursive: true });
52156
52193
  const content = await entry.async("nodebuffer");
52157
52194
  await import_promises4.default.writeFile(dest, content);
52158
52195
  }
@@ -52167,7 +52204,6 @@ async function importZip(buf, root) {
52167
52204
  }
52168
52205
 
52169
52206
  // src/transport/http-router.ts
52170
- var RPC_ROUTE_PREFIX = "/api/rpc/";
52171
52207
  var RPC_BODY_MAX_BYTES = 1 * 1024 * 1024;
52172
52208
  var SHARE_UI_ASSETS_PREFIX = "/share-ui/assets/";
52173
52209
  var SHARE_UI_ASSET_MIME = {
@@ -52183,11 +52219,9 @@ var SHARE_UI_ASSET_MIME = {
52183
52219
  ".wasm": "application/wasm"
52184
52220
  };
52185
52221
  function shareUiAssetMime(filePath) {
52186
- const ext = import_node_path33.default.extname(filePath).toLowerCase();
52222
+ const ext = import_node_path34.default.extname(filePath).toLowerCase();
52187
52223
  return SHARE_UI_ASSET_MIME[ext] ?? lookupMime(filePath);
52188
52224
  }
52189
- var DISPATCH_HEARTBEAT_MS = 25e3;
52190
- var DISPATCH_HOLD_METHOD = "personaDispatch:run";
52191
52225
  var AUTHED_RPC_ROUTE_PREFIX = "/rpc/";
52192
52226
  var AUTHED_RPC_CORS_HEADERS = {
52193
52227
  "Access-Control-Allow-Origin": "*",
@@ -52210,10 +52244,6 @@ function authedRpcErrorStatus(code) {
52210
52244
  return 400;
52211
52245
  }
52212
52246
  }
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
52247
  async function readJsonBody(req, max) {
52218
52248
  return await new Promise((resolve6, reject) => {
52219
52249
  let size = 0;
@@ -52270,7 +52300,7 @@ function isValidUploadFileName(fileName) {
52270
52300
  if (fileName === "." || fileName === "..") return false;
52271
52301
  if (fileName.startsWith(".")) return false;
52272
52302
  if (fileName.includes("/") || fileName.includes("\\")) return false;
52273
- return fileName === import_node_path33.default.basename(fileName);
52303
+ return fileName === import_node_path34.default.basename(fileName);
52274
52304
  }
52275
52305
  function createHttpRouter(deps) {
52276
52306
  return async (req, res) => {
@@ -52342,7 +52372,7 @@ function createHttpRouter(deps) {
52342
52372
  }
52343
52373
  let buf;
52344
52374
  try {
52345
- buf = await import_node_fs30.default.promises.readFile(abs);
52375
+ buf = await import_node_fs31.default.promises.readFile(abs);
52346
52376
  } catch {
52347
52377
  sendJson(res, 404, { code: "NOT_FOUND", message: "asset not found" });
52348
52378
  return true;
@@ -52390,57 +52420,6 @@ function createHttpRouter(deps) {
52390
52420
  return true;
52391
52421
  }
52392
52422
  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
52423
  try {
52445
52424
  const result = await deps.authedRpc.dispatch(method, dispatchArgs, auth.context);
52446
52425
  sendJson(res, 200, { ok: true, result: result.response }, AUTHED_RPC_CORS_HEADERS);
@@ -52451,34 +52430,6 @@ function createHttpRouter(deps) {
52451
52430
  }
52452
52431
  return true;
52453
52432
  }
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
52433
  if (url.pathname.startsWith("/preview/")) {
52483
52434
  const port = matchPreviewPort(url.pathname);
52484
52435
  if (port != null && isPreviewPortAllowed(port, { allowedPorts: deps.previewPorts ?? [] })) {
@@ -52566,7 +52517,7 @@ function createHttpRouter(deps) {
52566
52517
  sendHtml(res, statusByCode[r.code], loader.renderErrorHtml(r.code, msgByCode[r.code]));
52567
52518
  return true;
52568
52519
  }
52569
- sendHtml(res, 200, loader.renderViewerHtml(import_node_path33.default.basename(r.absPath)));
52520
+ sendHtml(res, 200, loader.renderViewerHtml(import_node_path34.default.basename(r.absPath)));
52570
52521
  return true;
52571
52522
  }
52572
52523
  const ctx = deps.authResolver.resolveFromHeader(
@@ -52687,7 +52638,7 @@ function createHttpRouter(deps) {
52687
52638
  return true;
52688
52639
  }
52689
52640
  let absPath;
52690
- if (import_node_path33.default.isAbsolute(pathParam)) {
52641
+ if (import_node_path34.default.isAbsolute(pathParam)) {
52691
52642
  absPath = pathParam;
52692
52643
  } else if (deps.sessionStore) {
52693
52644
  const file = deps.sessionStore.read(sid);
@@ -52695,7 +52646,7 @@ function createHttpRouter(deps) {
52695
52646
  sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sid} not found` });
52696
52647
  return true;
52697
52648
  }
52698
- absPath = import_node_path33.default.join(file.cwd, pathParam);
52649
+ absPath = import_node_path34.default.join(file.cwd, pathParam);
52699
52650
  } else {
52700
52651
  sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "sessionStore not wired" }));
52701
52652
  return true;
@@ -52782,7 +52733,7 @@ function withCtx(ctx, body) {
52782
52733
  function streamFile(res, absPath, logger) {
52783
52734
  let stat;
52784
52735
  try {
52785
- stat = import_node_fs30.default.statSync(absPath);
52736
+ stat = import_node_fs31.default.statSync(absPath);
52786
52737
  } catch (err) {
52787
52738
  const code = err?.code;
52788
52739
  if (code === "ENOENT") {
@@ -52797,7 +52748,7 @@ function streamFile(res, absPath, logger) {
52797
52748
  return;
52798
52749
  }
52799
52750
  const mime = lookupMime(absPath);
52800
- const basename = import_node_path33.default.basename(absPath);
52751
+ const basename = import_node_path34.default.basename(absPath);
52801
52752
  res.writeHead(200, {
52802
52753
  "Content-Type": mime,
52803
52754
  "Content-Length": String(stat.size),
@@ -52805,7 +52756,7 @@ function streamFile(res, absPath, logger) {
52805
52756
  // 防止浏览器把任意 mime 当 html 渲染
52806
52757
  "X-Content-Type-Options": "nosniff"
52807
52758
  });
52808
- const stream = import_node_fs30.default.createReadStream(absPath);
52759
+ const stream = import_node_fs31.default.createReadStream(absPath);
52809
52760
  stream.on("error", (err) => {
52810
52761
  logger?.warn("streamFile read error", { absPath, err: err.message });
52811
52762
  res.destroy();
@@ -52814,8 +52765,8 @@ function streamFile(res, absPath, logger) {
52814
52765
  }
52815
52766
 
52816
52767
  // src/attachment/gc.ts
52817
- var import_node_fs31 = __toESM(require("fs"), 1);
52818
- var import_node_path34 = __toESM(require("path"), 1);
52768
+ var import_node_fs32 = __toESM(require("fs"), 1);
52769
+ var import_node_path35 = __toESM(require("path"), 1);
52819
52770
  var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
52820
52771
  function runAttachmentGc(args) {
52821
52772
  const now = (args.now ?? Date.now)();
@@ -52824,38 +52775,38 @@ function runAttachmentGc(args) {
52824
52775
  for (const { scope, sessionId } of args.sessionScopes) {
52825
52776
  for (const entry of args.groupFileStore.list(scope, sessionId)) {
52826
52777
  if (entry.stale) continue;
52827
- if (import_node_path34.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
52778
+ if (import_node_path35.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
52828
52779
  }
52829
52780
  }
52830
52781
  for (const { scope, sessionId } of args.sessionScopes) {
52831
- const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path34.default.join(
52782
+ const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path35.default.join(
52832
52783
  args.dataDir,
52833
52784
  "sessions",
52834
52785
  ...scopeSubPath(scope).map(safeFileName),
52835
52786
  safeFileName(sessionId)
52836
52787
  );
52837
- const attRoot = import_node_path34.default.join(sessionDir, ".attachments");
52788
+ const attRoot = import_node_path35.default.join(sessionDir, ".attachments");
52838
52789
  let hashDirs;
52839
52790
  try {
52840
- hashDirs = import_node_fs31.default.readdirSync(attRoot);
52791
+ hashDirs = import_node_fs32.default.readdirSync(attRoot);
52841
52792
  } catch (err) {
52842
52793
  if (err.code === "ENOENT") continue;
52843
52794
  args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
52844
52795
  continue;
52845
52796
  }
52846
52797
  for (const hashDir of hashDirs) {
52847
- const hashDirAbs = import_node_path34.default.join(attRoot, hashDir);
52798
+ const hashDirAbs = import_node_path35.default.join(attRoot, hashDir);
52848
52799
  let files;
52849
52800
  try {
52850
- files = import_node_fs31.default.readdirSync(hashDirAbs);
52801
+ files = import_node_fs32.default.readdirSync(hashDirAbs);
52851
52802
  } catch {
52852
52803
  continue;
52853
52804
  }
52854
52805
  for (const name of files) {
52855
- const file = import_node_path34.default.join(hashDirAbs, name);
52806
+ const file = import_node_path35.default.join(hashDirAbs, name);
52856
52807
  let stat;
52857
52808
  try {
52858
- stat = import_node_fs31.default.statSync(file);
52809
+ stat = import_node_fs32.default.statSync(file);
52859
52810
  } catch {
52860
52811
  continue;
52861
52812
  }
@@ -52864,26 +52815,26 @@ function runAttachmentGc(args) {
52864
52815
  if (age < ttlMs) continue;
52865
52816
  if (liveAbs.has(file)) continue;
52866
52817
  try {
52867
- import_node_fs31.default.unlinkSync(file);
52818
+ import_node_fs32.default.unlinkSync(file);
52868
52819
  } catch (err) {
52869
52820
  args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
52870
52821
  }
52871
52822
  }
52872
52823
  try {
52873
- if (import_node_fs31.default.readdirSync(hashDirAbs).length === 0) import_node_fs31.default.rmdirSync(hashDirAbs);
52824
+ if (import_node_fs32.default.readdirSync(hashDirAbs).length === 0) import_node_fs32.default.rmdirSync(hashDirAbs);
52874
52825
  } catch {
52875
52826
  }
52876
52827
  }
52877
52828
  try {
52878
- if (import_node_fs31.default.readdirSync(attRoot).length === 0) import_node_fs31.default.rmdirSync(attRoot);
52829
+ if (import_node_fs32.default.readdirSync(attRoot).length === 0) import_node_fs32.default.rmdirSync(attRoot);
52879
52830
  } catch {
52880
52831
  }
52881
52832
  }
52882
52833
  }
52883
52834
 
52884
52835
  // src/attachment/group.ts
52885
- var import_node_fs32 = __toESM(require("fs"), 1);
52886
- var import_node_path35 = __toESM(require("path"), 1);
52836
+ var import_node_fs33 = __toESM(require("fs"), 1);
52837
+ var import_node_path36 = __toESM(require("path"), 1);
52887
52838
  var import_node_crypto9 = __toESM(require("crypto"), 1);
52888
52839
  init_protocol();
52889
52840
  var GroupFileStore = class {
@@ -52895,11 +52846,11 @@ var GroupFileStore = class {
52895
52846
  this.logger = opts.logger;
52896
52847
  }
52897
52848
  rootForScope(scope) {
52898
- return import_node_path35.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
52849
+ return import_node_path36.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
52899
52850
  }
52900
52851
  /** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
52901
52852
  filePath(scope, sessionId) {
52902
- return import_node_path35.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
52853
+ return import_node_path36.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
52903
52854
  }
52904
52855
  cacheKey(scope, sessionId) {
52905
52856
  return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
@@ -52908,7 +52859,7 @@ var GroupFileStore = class {
52908
52859
  readFile(scope, sessionId) {
52909
52860
  const file = this.filePath(scope, sessionId);
52910
52861
  try {
52911
- const raw = import_node_fs32.default.readFileSync(file, "utf8");
52862
+ const raw = import_node_fs33.default.readFileSync(file, "utf8");
52912
52863
  const parsed = JSON.parse(raw);
52913
52864
  if (!Array.isArray(parsed)) {
52914
52865
  this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
@@ -52934,10 +52885,10 @@ var GroupFileStore = class {
52934
52885
  }
52935
52886
  writeFile(scope, sessionId, entries) {
52936
52887
  const file = this.filePath(scope, sessionId);
52937
- import_node_fs32.default.mkdirSync(import_node_path35.default.dirname(file), { recursive: true });
52888
+ import_node_fs33.default.mkdirSync(import_node_path36.default.dirname(file), { recursive: true });
52938
52889
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
52939
- import_node_fs32.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
52940
- import_node_fs32.default.renameSync(tmp, file);
52890
+ import_node_fs33.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
52891
+ import_node_fs33.default.renameSync(tmp, file);
52941
52892
  }
52942
52893
  /** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
52943
52894
  list(scope, sessionId) {
@@ -53023,10 +52974,10 @@ var GroupFileStore = class {
53023
52974
  };
53024
52975
 
53025
52976
  // src/discovery/state-file.ts
53026
- var import_node_fs33 = __toESM(require("fs"), 1);
53027
- var import_node_path36 = __toESM(require("path"), 1);
52977
+ var import_node_fs34 = __toESM(require("fs"), 1);
52978
+ var import_node_path37 = __toESM(require("path"), 1);
53028
52979
  function defaultStateFilePath(dataDir) {
53029
- return import_node_path36.default.join(dataDir, "state.json");
52980
+ return import_node_path37.default.join(dataDir, "state.json");
53030
52981
  }
53031
52982
  function isPidAlive(pid) {
53032
52983
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -53048,7 +52999,7 @@ var StateFileManager = class {
53048
52999
  }
53049
53000
  read() {
53050
53001
  try {
53051
- const raw = import_node_fs33.default.readFileSync(this.file, "utf8");
53002
+ const raw = import_node_fs34.default.readFileSync(this.file, "utf8");
53052
53003
  const parsed = JSON.parse(raw);
53053
53004
  return parsed;
53054
53005
  } catch {
@@ -53062,20 +53013,20 @@ var StateFileManager = class {
53062
53013
  return { status: "stale", existing };
53063
53014
  }
53064
53015
  write(state) {
53065
- import_node_fs33.default.mkdirSync(import_node_path36.default.dirname(this.file), { recursive: true });
53016
+ import_node_fs34.default.mkdirSync(import_node_path37.default.dirname(this.file), { recursive: true });
53066
53017
  const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
53067
- import_node_fs33.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
53068
- import_node_fs33.default.renameSync(tmp, this.file);
53018
+ import_node_fs34.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
53019
+ import_node_fs34.default.renameSync(tmp, this.file);
53069
53020
  if (process.platform !== "win32") {
53070
53021
  try {
53071
- import_node_fs33.default.chmodSync(this.file, 384);
53022
+ import_node_fs34.default.chmodSync(this.file, 384);
53072
53023
  } catch {
53073
53024
  }
53074
53025
  }
53075
53026
  }
53076
53027
  delete() {
53077
53028
  try {
53078
- import_node_fs33.default.unlinkSync(this.file);
53029
+ import_node_fs34.default.unlinkSync(this.file);
53079
53030
  } catch {
53080
53031
  }
53081
53032
  }
@@ -53089,14 +53040,14 @@ function readSpawnedByDesktopFromEnv(env = process.env) {
53089
53040
  }
53090
53041
 
53091
53042
  // src/tunnel/tunnel-manager.ts
53092
- var import_node_fs37 = __toESM(require("fs"), 1);
53093
- var import_node_path40 = __toESM(require("path"), 1);
53043
+ var import_node_fs38 = __toESM(require("fs"), 1);
53044
+ var import_node_path41 = __toESM(require("path"), 1);
53094
53045
  var import_node_crypto10 = __toESM(require("crypto"), 1);
53095
53046
  var import_node_child_process10 = require("child_process");
53096
53047
 
53097
53048
  // src/tunnel/tunnel-store.ts
53098
- var import_node_fs34 = __toESM(require("fs"), 1);
53099
- var import_node_path37 = __toESM(require("path"), 1);
53049
+ var import_node_fs35 = __toESM(require("fs"), 1);
53050
+ var import_node_path38 = __toESM(require("path"), 1);
53100
53051
  var TunnelStore = class {
53101
53052
  constructor(filePath) {
53102
53053
  this.filePath = filePath;
@@ -53104,7 +53055,7 @@ var TunnelStore = class {
53104
53055
  filePath;
53105
53056
  async get() {
53106
53057
  try {
53107
- const raw = await import_node_fs34.default.promises.readFile(this.filePath, "utf8");
53058
+ const raw = await import_node_fs35.default.promises.readFile(this.filePath, "utf8");
53108
53059
  const obj = JSON.parse(raw);
53109
53060
  if (!isPersistedTunnel(obj)) return null;
53110
53061
  return obj;
@@ -53115,22 +53066,22 @@ var TunnelStore = class {
53115
53066
  }
53116
53067
  }
53117
53068
  async set(v2) {
53118
- const dir = import_node_path37.default.dirname(this.filePath);
53119
- await import_node_fs34.default.promises.mkdir(dir, { recursive: true });
53069
+ const dir = import_node_path38.default.dirname(this.filePath);
53070
+ await import_node_fs35.default.promises.mkdir(dir, { recursive: true });
53120
53071
  const data = JSON.stringify(v2, null, 2);
53121
53072
  const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
53122
- await import_node_fs34.default.promises.writeFile(tmp, data, { mode: 384 });
53073
+ await import_node_fs35.default.promises.writeFile(tmp, data, { mode: 384 });
53123
53074
  if (process.platform !== "win32") {
53124
53075
  try {
53125
- await import_node_fs34.default.promises.chmod(tmp, 384);
53076
+ await import_node_fs35.default.promises.chmod(tmp, 384);
53126
53077
  } catch {
53127
53078
  }
53128
53079
  }
53129
- await import_node_fs34.default.promises.rename(tmp, this.filePath);
53080
+ await import_node_fs35.default.promises.rename(tmp, this.filePath);
53130
53081
  }
53131
53082
  async clear() {
53132
53083
  try {
53133
- await import_node_fs34.default.promises.unlink(this.filePath);
53084
+ await import_node_fs35.default.promises.unlink(this.filePath);
53134
53085
  } catch (err) {
53135
53086
  const code = err?.code;
53136
53087
  if (code !== "ENOENT") throw err;
@@ -53225,9 +53176,9 @@ function escape(v2) {
53225
53176
  }
53226
53177
 
53227
53178
  // src/tunnel/frpc-binary.ts
53228
- var import_node_fs35 = __toESM(require("fs"), 1);
53179
+ var import_node_fs36 = __toESM(require("fs"), 1);
53229
53180
  var import_node_os13 = __toESM(require("os"), 1);
53230
- var import_node_path38 = __toESM(require("path"), 1);
53181
+ var import_node_path39 = __toESM(require("path"), 1);
53231
53182
  var import_node_child_process8 = require("child_process");
53232
53183
  var import_node_stream3 = require("stream");
53233
53184
  var import_promises5 = require("stream/promises");
@@ -53259,20 +53210,20 @@ function frpcDownloadUrl(version2, p2) {
53259
53210
  }
53260
53211
  async function ensureFrpcBinary(opts) {
53261
53212
  if (opts.override) {
53262
- if (!import_node_fs35.default.existsSync(opts.override)) {
53213
+ if (!import_node_fs36.default.existsSync(opts.override)) {
53263
53214
  throw new Error(`frpc binary not found at override path: ${opts.override}`);
53264
53215
  }
53265
53216
  return opts.override;
53266
53217
  }
53267
53218
  const version2 = opts.version ?? FRPC_VERSION;
53268
53219
  const platform = opts.platform ?? detectPlatform();
53269
- const binDir = import_node_path38.default.join(opts.dataDir, "bin");
53270
- import_node_fs35.default.mkdirSync(binDir, { recursive: true });
53220
+ const binDir = import_node_path39.default.join(opts.dataDir, "bin");
53221
+ import_node_fs36.default.mkdirSync(binDir, { recursive: true });
53271
53222
  cleanupStaleArtifacts(binDir);
53272
- const stableBin = import_node_path38.default.join(binDir, "frpc");
53273
- if (import_node_fs35.default.existsSync(stableBin)) return stableBin;
53223
+ const stableBin = import_node_path39.default.join(binDir, "frpc");
53224
+ if (import_node_fs36.default.existsSync(stableBin)) return stableBin;
53274
53225
  const partialBin = `${stableBin}.partial`;
53275
- const tarballPath = import_node_path38.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
53226
+ const tarballPath = import_node_path39.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
53276
53227
  try {
53277
53228
  const url = frpcDownloadUrl(version2, platform);
53278
53229
  await downloadToFile(url, tarballPath, opts.fetchImpl);
@@ -53281,8 +53232,8 @@ async function ensureFrpcBinary(opts) {
53281
53232
  } else {
53282
53233
  await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
53283
53234
  }
53284
- import_node_fs35.default.chmodSync(partialBin, 493);
53285
- import_node_fs35.default.renameSync(partialBin, stableBin);
53235
+ import_node_fs36.default.chmodSync(partialBin, 493);
53236
+ import_node_fs36.default.renameSync(partialBin, stableBin);
53286
53237
  } finally {
53287
53238
  safeUnlink(tarballPath);
53288
53239
  safeUnlink(partialBin);
@@ -53292,15 +53243,15 @@ async function ensureFrpcBinary(opts) {
53292
53243
  function cleanupStaleArtifacts(binDir) {
53293
53244
  let entries;
53294
53245
  try {
53295
- entries = import_node_fs35.default.readdirSync(binDir);
53246
+ entries = import_node_fs36.default.readdirSync(binDir);
53296
53247
  } catch {
53297
53248
  return;
53298
53249
  }
53299
53250
  for (const name of entries) {
53300
53251
  if (name.endsWith(".partial") || name.startsWith("extract-")) {
53301
- const full = import_node_path38.default.join(binDir, name);
53252
+ const full = import_node_path39.default.join(binDir, name);
53302
53253
  try {
53303
- import_node_fs35.default.rmSync(full, { recursive: true, force: true });
53254
+ import_node_fs36.default.rmSync(full, { recursive: true, force: true });
53304
53255
  } catch {
53305
53256
  }
53306
53257
  }
@@ -53308,7 +53259,7 @@ function cleanupStaleArtifacts(binDir) {
53308
53259
  }
53309
53260
  function safeUnlink(p2) {
53310
53261
  try {
53311
- import_node_fs35.default.unlinkSync(p2);
53262
+ import_node_fs36.default.unlinkSync(p2);
53312
53263
  } catch {
53313
53264
  }
53314
53265
  }
@@ -53319,13 +53270,13 @@ async function downloadToFile(url, dest, fetchImpl) {
53319
53270
  if (!res.ok || !res.body) {
53320
53271
  throw new Error(`download failed: ${res.status} ${res.statusText}`);
53321
53272
  }
53322
- const out = import_node_fs35.default.createWriteStream(dest);
53273
+ const out = import_node_fs36.default.createWriteStream(dest);
53323
53274
  const nodeStream = import_node_stream3.Readable.fromWeb(res.body);
53324
53275
  await (0, import_promises5.pipeline)(nodeStream, out);
53325
53276
  }
53326
53277
  async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
53327
- const work = import_node_path38.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
53328
- import_node_fs35.default.mkdirSync(work, { recursive: true });
53278
+ const work = import_node_path39.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
53279
+ import_node_fs36.default.mkdirSync(work, { recursive: true });
53329
53280
  try {
53330
53281
  await new Promise((resolve6, reject) => {
53331
53282
  const proc = (0, import_node_child_process8.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
@@ -53333,32 +53284,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
53333
53284
  proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
53334
53285
  });
53335
53286
  const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
53336
- const src = import_node_path38.default.join(work, dirName, "frpc");
53337
- if (!import_node_fs35.default.existsSync(src)) {
53287
+ const src = import_node_path39.default.join(work, dirName, "frpc");
53288
+ if (!import_node_fs36.default.existsSync(src)) {
53338
53289
  throw new Error(`frpc not found inside tarball at ${src}`);
53339
53290
  }
53340
- import_node_fs35.default.copyFileSync(src, destBin);
53291
+ import_node_fs36.default.copyFileSync(src, destBin);
53341
53292
  } finally {
53342
- import_node_fs35.default.rmSync(work, { recursive: true, force: true });
53293
+ import_node_fs36.default.rmSync(work, { recursive: true, force: true });
53343
53294
  }
53344
53295
  }
53345
53296
 
53346
53297
  // src/tunnel/frpc-process.ts
53347
- var import_node_fs36 = __toESM(require("fs"), 1);
53348
- var import_node_path39 = __toESM(require("path"), 1);
53298
+ var import_node_fs37 = __toESM(require("fs"), 1);
53299
+ var import_node_path40 = __toESM(require("path"), 1);
53349
53300
  var import_node_child_process9 = require("child_process");
53350
53301
  function frpcPidFilePath(dataDir) {
53351
- return import_node_path39.default.join(dataDir, "frpc.pid");
53302
+ return import_node_path40.default.join(dataDir, "frpc.pid");
53352
53303
  }
53353
53304
  function writeFrpcPid(dataDir, pid) {
53354
53305
  try {
53355
- import_node_fs36.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
53306
+ import_node_fs37.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
53356
53307
  } catch {
53357
53308
  }
53358
53309
  }
53359
53310
  function clearFrpcPid(dataDir) {
53360
53311
  try {
53361
- import_node_fs36.default.unlinkSync(frpcPidFilePath(dataDir));
53312
+ import_node_fs37.default.unlinkSync(frpcPidFilePath(dataDir));
53362
53313
  } catch {
53363
53314
  }
53364
53315
  }
@@ -53374,7 +53325,7 @@ function defaultIsPidAlive(pid) {
53374
53325
  }
53375
53326
  function defaultReadPidFile(file) {
53376
53327
  try {
53377
- return import_node_fs36.default.readFileSync(file, "utf8");
53328
+ return import_node_fs37.default.readFileSync(file, "utf8");
53378
53329
  } catch {
53379
53330
  return null;
53380
53331
  }
@@ -53385,17 +53336,17 @@ function defaultKillPid(pid, signal) {
53385
53336
  } catch {
53386
53337
  }
53387
53338
  }
53388
- function defaultSleep(ms) {
53339
+ function defaultSleep2(ms) {
53389
53340
  return new Promise((r) => setTimeout(r, ms));
53390
53341
  }
53391
53342
  async function killStaleFrpc(deps) {
53392
53343
  const pidFile = frpcPidFilePath(deps.dataDir);
53393
- const tomlPath = import_node_path39.default.join(deps.dataDir, "frpc.toml");
53344
+ const tomlPath = import_node_path40.default.join(deps.dataDir, "frpc.toml");
53394
53345
  const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
53395
53346
  const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
53396
53347
  const killPid = deps.killPidImpl ?? defaultKillPid;
53397
53348
  const scanPids = deps.scanFrpcPidsImpl ?? ((tp) => defaultScanFrpcPidsByCmdline(tp, deps.logger));
53398
- const sleep2 = deps.sleepImpl ?? defaultSleep;
53349
+ const sleep2 = deps.sleepImpl ?? defaultSleep2;
53399
53350
  const victims = /* @__PURE__ */ new Set();
53400
53351
  const raw = readPidFile(pidFile);
53401
53352
  if (raw) {
@@ -53414,7 +53365,7 @@ async function killStaleFrpc(deps) {
53414
53365
  }
53415
53366
  if (victims.size === 0) {
53416
53367
  try {
53417
- import_node_fs36.default.unlinkSync(pidFile);
53368
+ import_node_fs37.default.unlinkSync(pidFile);
53418
53369
  } catch {
53419
53370
  }
53420
53371
  return;
@@ -53425,7 +53376,7 @@ async function killStaleFrpc(deps) {
53425
53376
  }
53426
53377
  await sleep2(deps.reapWaitMs ?? 300);
53427
53378
  try {
53428
- import_node_fs36.default.unlinkSync(pidFile);
53379
+ import_node_fs37.default.unlinkSync(pidFile);
53429
53380
  } catch {
53430
53381
  }
53431
53382
  }
@@ -53462,7 +53413,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
53462
53413
  var TunnelManager = class {
53463
53414
  constructor(deps) {
53464
53415
  this.deps = deps;
53465
- this.store = deps.store ?? new TunnelStore(import_node_path40.default.join(deps.dataDir, "tunnel.json"));
53416
+ this.store = deps.store ?? new TunnelStore(import_node_path41.default.join(deps.dataDir, "tunnel.json"));
53466
53417
  this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
53467
53418
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
53468
53419
  }
@@ -53589,7 +53540,7 @@ var TunnelManager = class {
53589
53540
  dataDir: this.deps.dataDir,
53590
53541
  override: this.deps.frpcBinaryOverride ?? void 0
53591
53542
  });
53592
- const tomlPath = import_node_path40.default.join(this.deps.dataDir, "frpc.toml");
53543
+ const tomlPath = import_node_path41.default.join(this.deps.dataDir, "frpc.toml");
53593
53544
  const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto10.default.randomBytes(3).toString("hex")}`;
53594
53545
  const toml = buildFrpcToml({
53595
53546
  serverAddr: t.frpsHost,
@@ -53600,14 +53551,14 @@ var TunnelManager = class {
53600
53551
  localPort,
53601
53552
  logLevel: "info"
53602
53553
  });
53603
- await import_node_fs37.default.promises.writeFile(tomlPath, toml, { mode: 384 });
53554
+ await import_node_fs38.default.promises.writeFile(tomlPath, toml, { mode: 384 });
53604
53555
  const proc = (this.deps.spawnImpl ?? import_node_child_process10.spawn)(frpcBin, ["-c", tomlPath], {
53605
53556
  stdio: ["ignore", "pipe", "pipe"]
53606
53557
  });
53607
- const logDir = import_node_path40.default.join(this.deps.dataDir, "log");
53608
- import_node_fs37.default.mkdirSync(logDir, { recursive: true });
53609
- const logFilePath = import_node_path40.default.join(logDir, "frpc.log");
53610
- const logStream = import_node_fs37.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
53558
+ const logDir = import_node_path41.default.join(this.deps.dataDir, "log");
53559
+ import_node_fs38.default.mkdirSync(logDir, { recursive: true });
53560
+ const logFilePath = import_node_path41.default.join(logDir, "frpc.log");
53561
+ const logStream = import_node_fs38.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
53611
53562
  logStream.on("error", () => {
53612
53563
  });
53613
53564
  const tee = (chunk) => {
@@ -53690,27 +53641,27 @@ async function waitForFrpcReady(proc, timeoutMs) {
53690
53641
 
53691
53642
  // src/tunnel/device-key.ts
53692
53643
  var import_node_os14 = __toESM(require("os"), 1);
53693
- var import_node_path41 = __toESM(require("path"), 1);
53644
+ var import_node_path42 = __toESM(require("path"), 1);
53694
53645
  var import_node_crypto11 = __toESM(require("crypto"), 1);
53695
53646
  var DERIVE_SALT = "clawd-tunnel-device-v1";
53696
53647
  function deriveStableDeviceKey(opts = {}) {
53697
53648
  const hostname = opts.hostname ?? import_node_os14.default.hostname();
53698
53649
  const uid = opts.uid ?? (typeof import_node_os14.default.userInfo === "function" ? import_node_os14.default.userInfo().uid : 0);
53699
53650
  const home = opts.home ?? import_node_os14.default.homedir();
53700
- const defaultDataDir = import_node_path41.default.resolve(import_node_path41.default.join(home, ".clawd"));
53701
- const normalizedDataDir = opts.dataDir ? import_node_path41.default.resolve(opts.dataDir) : null;
53651
+ const defaultDataDir = import_node_path42.default.resolve(import_node_path42.default.join(home, ".clawd"));
53652
+ const normalizedDataDir = opts.dataDir ? import_node_path42.default.resolve(opts.dataDir) : null;
53702
53653
  const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
53703
53654
  const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
53704
53655
  return import_node_crypto11.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
53705
53656
  }
53706
53657
 
53707
53658
  // src/auth-store.ts
53708
- var import_node_fs38 = __toESM(require("fs"), 1);
53709
- var import_node_path42 = __toESM(require("path"), 1);
53659
+ var import_node_fs39 = __toESM(require("fs"), 1);
53660
+ var import_node_path43 = __toESM(require("path"), 1);
53710
53661
  var import_node_crypto12 = __toESM(require("crypto"), 1);
53711
53662
  var AUTH_FILE_NAME = "auth.json";
53712
53663
  function authFilePath(dataDir) {
53713
- return import_node_path42.default.join(dataDir, AUTH_FILE_NAME);
53664
+ return import_node_path43.default.join(dataDir, AUTH_FILE_NAME);
53714
53665
  }
53715
53666
  function loadOrCreateAuthFile(opts) {
53716
53667
  const file = authFilePath(opts.dataDir);
@@ -53718,23 +53669,19 @@ function loadOrCreateAuthFile(opts) {
53718
53669
  const genId = opts.genOwnerPrincipalId ?? defaultGenerateOwnerPrincipalId;
53719
53670
  const now = opts.now ?? (() => /* @__PURE__ */ new Date());
53720
53671
  const existing = readAuthFile(file);
53721
- if (existing && existing.token && existing.signSecret && existing.visitorTokenSecret && existing.ownerPrincipalId && existing.deviceId && existing.deviceId === existing.ownerPrincipalId) {
53672
+ if (existing && existing.token && existing.ownerPrincipalId && existing.deviceId && existing.deviceId === existing.ownerPrincipalId && !existing.hasLegacySecrets) {
53722
53673
  return {
53723
53674
  token: existing.token,
53724
- signSecret: existing.signSecret,
53725
- visitorTokenSecret: existing.visitorTokenSecret,
53726
53675
  ownerPrincipalId: existing.ownerPrincipalId,
53727
53676
  deviceId: existing.deviceId,
53728
53677
  createdAt: existing.createdAt ?? (/* @__PURE__ */ new Date(0)).toISOString()
53729
53678
  };
53730
53679
  }
53731
53680
  const token = existing?.token || generate();
53732
- const signSecret = existing?.signSecret || generate();
53733
- const visitorTokenSecret = existing?.visitorTokenSecret || generate();
53734
53681
  const ownerPrincipalId = existing?.ownerPrincipalId || genId();
53735
53682
  const deviceId = ownerPrincipalId;
53736
53683
  const createdAt = existing?.createdAt || now().toISOString();
53737
- const next = { token, signSecret, visitorTokenSecret, ownerPrincipalId, deviceId, createdAt };
53684
+ const next = { token, ownerPrincipalId, deviceId, createdAt };
53738
53685
  writeAuthFile(file, next);
53739
53686
  return next;
53740
53687
  }
@@ -53746,18 +53693,18 @@ function defaultGenerateOwnerPrincipalId() {
53746
53693
  }
53747
53694
  function readAuthFile(file) {
53748
53695
  try {
53749
- const raw = import_node_fs38.default.readFileSync(file, "utf8");
53696
+ const raw = import_node_fs39.default.readFileSync(file, "utf8");
53750
53697
  const parsed = JSON.parse(raw);
53751
53698
  if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
53752
53699
  return null;
53753
53700
  }
53754
53701
  return {
53755
53702
  token: parsed.token,
53756
- signSecret: typeof parsed.signSecret === "string" && parsed.signSecret.length > 0 ? parsed.signSecret : void 0,
53757
- visitorTokenSecret: typeof parsed.visitorTokenSecret === "string" && parsed.visitorTokenSecret.length > 0 ? parsed.visitorTokenSecret : void 0,
53758
53703
  ownerPrincipalId: typeof parsed.ownerPrincipalId === "string" && parsed.ownerPrincipalId.length > 0 ? parsed.ownerPrincipalId : void 0,
53759
53704
  deviceId: typeof parsed.deviceId === "string" && parsed.deviceId.length > 0 ? parsed.deviceId : void 0,
53760
- createdAt: typeof parsed.createdAt === "string" ? parsed.createdAt : void 0
53705
+ createdAt: typeof parsed.createdAt === "string" ? parsed.createdAt : void 0,
53706
+ // 单 secret 合并前的旧字段:出现即触发清理写回(值无所谓,硬切不再使用)
53707
+ hasLegacySecrets: "signSecret" in parsed || "visitorTokenSecret" in parsed
53761
53708
  };
53762
53709
  } catch (err) {
53763
53710
  const code = err?.code;
@@ -53766,25 +53713,25 @@ function readAuthFile(file) {
53766
53713
  }
53767
53714
  }
53768
53715
  function writeAuthFile(file, content) {
53769
- import_node_fs38.default.mkdirSync(import_node_path42.default.dirname(file), { recursive: true });
53770
- import_node_fs38.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
53716
+ import_node_fs39.default.mkdirSync(import_node_path43.default.dirname(file), { recursive: true });
53717
+ import_node_fs39.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
53771
53718
  try {
53772
- import_node_fs38.default.chmodSync(file, 384);
53719
+ import_node_fs39.default.chmodSync(file, 384);
53773
53720
  } catch {
53774
53721
  }
53775
53722
  }
53776
53723
 
53777
53724
  // src/owner-profile.ts
53778
- var import_node_fs39 = __toESM(require("fs"), 1);
53725
+ var import_node_fs40 = __toESM(require("fs"), 1);
53779
53726
  var import_node_os15 = __toESM(require("os"), 1);
53780
- var import_node_path43 = __toESM(require("path"), 1);
53727
+ var import_node_path44 = __toESM(require("path"), 1);
53781
53728
  var PROFILE_FILENAME = "profile.json";
53782
53729
  function loadOwnerDisplayName(dataDir) {
53783
53730
  const fallback = import_node_os15.default.userInfo().username;
53784
- const profilePath = import_node_path43.default.join(dataDir, PROFILE_FILENAME);
53731
+ const profilePath = import_node_path44.default.join(dataDir, PROFILE_FILENAME);
53785
53732
  let raw;
53786
53733
  try {
53787
- raw = import_node_fs39.default.readFileSync(profilePath, "utf8");
53734
+ raw = import_node_fs40.default.readFileSync(profilePath, "utf8");
53788
53735
  } catch {
53789
53736
  return fallback;
53790
53737
  }
@@ -53807,18 +53754,18 @@ function loadOwnerDisplayName(dataDir) {
53807
53754
  }
53808
53755
 
53809
53756
  // src/feishu-auth/owner-identity-store.ts
53810
- var import_node_fs40 = __toESM(require("fs"), 1);
53811
- var import_node_path44 = __toESM(require("path"), 1);
53757
+ var import_node_fs41 = __toESM(require("fs"), 1);
53758
+ var import_node_path45 = __toESM(require("path"), 1);
53812
53759
  var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
53813
53760
  var OwnerIdentityStore = class {
53814
53761
  file;
53815
53762
  constructor(dataDir) {
53816
- this.file = import_node_path44.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
53763
+ this.file = import_node_path45.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
53817
53764
  }
53818
53765
  read() {
53819
53766
  let raw;
53820
53767
  try {
53821
- raw = import_node_fs40.default.readFileSync(this.file, "utf8");
53768
+ raw = import_node_fs41.default.readFileSync(this.file, "utf8");
53822
53769
  } catch {
53823
53770
  return null;
53824
53771
  }
@@ -53846,16 +53793,16 @@ var OwnerIdentityStore = class {
53846
53793
  };
53847
53794
  }
53848
53795
  write(record) {
53849
- import_node_fs40.default.mkdirSync(import_node_path44.default.dirname(this.file), { recursive: true });
53850
- import_node_fs40.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
53796
+ import_node_fs41.default.mkdirSync(import_node_path45.default.dirname(this.file), { recursive: true });
53797
+ import_node_fs41.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
53851
53798
  try {
53852
- import_node_fs40.default.chmodSync(this.file, 384);
53799
+ import_node_fs41.default.chmodSync(this.file, 384);
53853
53800
  } catch {
53854
53801
  }
53855
53802
  }
53856
53803
  clear() {
53857
53804
  try {
53858
- import_node_fs40.default.unlinkSync(this.file);
53805
+ import_node_fs41.default.unlinkSync(this.file);
53859
53806
  } catch (err) {
53860
53807
  const code = err?.code;
53861
53808
  if (code !== "ENOENT") throw err;
@@ -53992,9 +53939,9 @@ var CentralClientError = class extends Error {
53992
53939
  code;
53993
53940
  cause;
53994
53941
  };
53995
- async function centralRequest(opts, path74, init) {
53942
+ async function centralRequest(opts, path75, init) {
53996
53943
  const f = opts.fetchImpl ?? globalThis.fetch;
53997
- const url = `${opts.api.replace(/\/+$/, "")}${path74}`;
53944
+ const url = `${opts.api.replace(/\/+$/, "")}${path75}`;
53998
53945
  const ctrl = new AbortController();
53999
53946
  const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
54000
53947
  let res;
@@ -54188,8 +54135,8 @@ function verifyConnectToken(args) {
54188
54135
  }
54189
54136
 
54190
54137
  // src/feishu-auth/server-key.ts
54191
- var fs50 = __toESM(require("fs"), 1);
54192
- var path53 = __toESM(require("path"), 1);
54138
+ var fs51 = __toESM(require("fs"), 1);
54139
+ var path54 = __toESM(require("path"), 1);
54193
54140
  var FILE_NAME2 = "server-signing-key.json";
54194
54141
  var ServerKeyStore = class {
54195
54142
  constructor(dataDir) {
@@ -54197,12 +54144,12 @@ var ServerKeyStore = class {
54197
54144
  }
54198
54145
  dataDir;
54199
54146
  filePath() {
54200
- return path53.join(this.dataDir, FILE_NAME2);
54147
+ return path54.join(this.dataDir, FILE_NAME2);
54201
54148
  }
54202
54149
  /** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
54203
54150
  read() {
54204
54151
  try {
54205
- const raw = fs50.readFileSync(this.filePath(), "utf8");
54152
+ const raw = fs51.readFileSync(this.filePath(), "utf8");
54206
54153
  const parsed = JSON.parse(raw);
54207
54154
  if (typeof parsed.publicKeyPem === "string" && parsed.publicKeyPem.includes("PUBLIC KEY")) {
54208
54155
  return parsed.publicKeyPem;
@@ -54217,12 +54164,12 @@ var ServerKeyStore = class {
54217
54164
  publicKeyPem,
54218
54165
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
54219
54166
  };
54220
- fs50.mkdirSync(this.dataDir, { recursive: true });
54221
- fs50.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
54167
+ fs51.mkdirSync(this.dataDir, { recursive: true });
54168
+ fs51.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
54222
54169
  }
54223
54170
  clear() {
54224
54171
  try {
54225
- fs50.unlinkSync(this.filePath());
54172
+ fs51.unlinkSync(this.filePath());
54226
54173
  } catch {
54227
54174
  }
54228
54175
  }
@@ -54235,12 +54182,12 @@ init_protocol();
54235
54182
  init_protocol();
54236
54183
 
54237
54184
  // src/session/fork.ts
54238
- var import_node_fs41 = __toESM(require("fs"), 1);
54185
+ var import_node_fs42 = __toESM(require("fs"), 1);
54239
54186
  var import_node_os16 = __toESM(require("os"), 1);
54240
- var import_node_path45 = __toESM(require("path"), 1);
54187
+ var import_node_path46 = __toESM(require("path"), 1);
54241
54188
  init_claude_history();
54242
54189
  function readJsonlEntries(file) {
54243
- const raw = import_node_fs41.default.readFileSync(file, "utf8");
54190
+ const raw = import_node_fs42.default.readFileSync(file, "utf8");
54244
54191
  const out = [];
54245
54192
  for (const line of raw.split("\n")) {
54246
54193
  const t = line.trim();
@@ -54253,19 +54200,19 @@ function readJsonlEntries(file) {
54253
54200
  return out;
54254
54201
  }
54255
54202
  function forkSession(input) {
54256
- const baseDir = input.baseDir ?? import_node_path45.default.join(import_node_os16.default.homedir(), ".claude");
54257
- let projectDir = import_node_path45.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
54258
- let sourceFile = import_node_path45.default.join(projectDir, `${input.toolSessionId}.jsonl`);
54259
- if (!import_node_fs41.default.existsSync(sourceFile)) {
54203
+ const baseDir = input.baseDir ?? import_node_path46.default.join(import_node_os16.default.homedir(), ".claude");
54204
+ let projectDir = import_node_path46.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
54205
+ let sourceFile = import_node_path46.default.join(projectDir, `${input.toolSessionId}.jsonl`);
54206
+ if (!import_node_fs42.default.existsSync(sourceFile)) {
54260
54207
  const found = findTranscriptByToolSessionId(
54261
- import_node_path45.default.join(baseDir, "projects"),
54208
+ import_node_path46.default.join(baseDir, "projects"),
54262
54209
  input.toolSessionId
54263
54210
  );
54264
54211
  if (!found) {
54265
54212
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
54266
54213
  }
54267
54214
  sourceFile = found;
54268
- projectDir = import_node_path45.default.dirname(found);
54215
+ projectDir = import_node_path46.default.dirname(found);
54269
54216
  }
54270
54217
  const entries = readJsonlEntries(sourceFile);
54271
54218
  const mainEntries = entries.filter((e) => e.isSidechain !== true);
@@ -54294,9 +54241,9 @@ function forkSession(input) {
54294
54241
  }
54295
54242
  forkedLines.push(JSON.stringify(forked));
54296
54243
  }
54297
- const forkedFilePath = import_node_path45.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
54298
- import_node_fs41.default.mkdirSync(projectDir, { recursive: true });
54299
- import_node_fs41.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
54244
+ const forkedFilePath = import_node_path46.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
54245
+ import_node_fs42.default.mkdirSync(projectDir, { recursive: true });
54246
+ import_node_fs42.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
54300
54247
  return { forkedToolSessionId, forkedFilePath };
54301
54248
  }
54302
54249
 
@@ -54708,7 +54655,7 @@ function buildPermissionHandlers(deps) {
54708
54655
  }
54709
54656
 
54710
54657
  // src/handlers/history.ts
54711
- var path56 = __toESM(require("path"), 1);
54658
+ var path57 = __toESM(require("path"), 1);
54712
54659
  init_protocol();
54713
54660
 
54714
54661
  // src/session/recent-dirs.ts
@@ -54726,7 +54673,7 @@ function listRecentDirs(store, limit = 50) {
54726
54673
  }
54727
54674
 
54728
54675
  // src/permission/persona-paths.ts
54729
- var path55 = __toESM(require("path"), 1);
54676
+ var path56 = __toESM(require("path"), 1);
54730
54677
  function getAllowedPersonaIds(grants, action) {
54731
54678
  const ids = /* @__PURE__ */ new Set();
54732
54679
  for (const g2 of grants) {
@@ -54739,42 +54686,42 @@ function getAllowedPersonaIds(grants, action) {
54739
54686
  return ids;
54740
54687
  }
54741
54688
  function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
54742
- const target = path55.resolve(absPath);
54689
+ const target = path56.resolve(absPath);
54743
54690
  if (userWorkDir) {
54744
- const u = path55.resolve(userWorkDir);
54745
- const usep = u.endsWith(path55.sep) ? "" : path55.sep;
54691
+ const u = path56.resolve(userWorkDir);
54692
+ const usep = u.endsWith(path56.sep) ? "" : path56.sep;
54746
54693
  if (target === u || target.startsWith(u + usep)) return true;
54747
54694
  }
54748
- const root = path55.resolve(personaRoot);
54749
- const sep3 = root.endsWith(path55.sep) ? "" : path55.sep;
54695
+ const root = path56.resolve(personaRoot);
54696
+ const sep3 = root.endsWith(path56.sep) ? "" : path56.sep;
54750
54697
  if (!target.startsWith(root + sep3)) return false;
54751
- const rel = path55.relative(root, target);
54698
+ const rel = path56.relative(root, target);
54752
54699
  if (!rel || rel.startsWith("..")) return false;
54753
- const personaId = rel.split(path55.sep)[0];
54700
+ const personaId = rel.split(path56.sep)[0];
54754
54701
  if (!personaId) return false;
54755
54702
  const allowed = getAllowedPersonaIds(grants, action);
54756
54703
  if (allowed === "*") return true;
54757
54704
  return allowed.has(personaId);
54758
54705
  }
54759
54706
  function personaIdFromPath(absPath, personaRoot) {
54760
- const root = path55.resolve(personaRoot);
54761
- const target = path55.resolve(absPath);
54762
- const sep3 = root.endsWith(path55.sep) ? "" : path55.sep;
54707
+ const root = path56.resolve(personaRoot);
54708
+ const target = path56.resolve(absPath);
54709
+ const sep3 = root.endsWith(path56.sep) ? "" : path56.sep;
54763
54710
  if (!target.startsWith(root + sep3)) return null;
54764
- const rel = path55.relative(root, target);
54711
+ const rel = path56.relative(root, target);
54765
54712
  if (!rel || rel.startsWith("..")) return null;
54766
- const id = rel.split(path55.sep)[0];
54713
+ const id = rel.split(path56.sep)[0];
54767
54714
  return id || null;
54768
54715
  }
54769
54716
  function isPathWithin(dir, absPath) {
54770
- const d = path55.resolve(dir);
54771
- const t = path55.resolve(absPath);
54772
- const sep3 = d.endsWith(path55.sep) ? "" : path55.sep;
54717
+ const d = path56.resolve(dir);
54718
+ const t = path56.resolve(absPath);
54719
+ const sep3 = d.endsWith(path56.sep) ? "" : path56.sep;
54773
54720
  return t === d || t.startsWith(d + sep3);
54774
54721
  }
54775
54722
  function isPathInGuestBoundary(personaRoot, personaId, userWorkDir, absPath) {
54776
54723
  if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
54777
- return personaIdFromPath(path55.resolve(absPath), personaRoot) === personaId;
54724
+ return personaIdFromPath(path56.resolve(absPath), personaRoot) === personaId;
54778
54725
  }
54779
54726
 
54780
54727
  // src/handlers/history.ts
@@ -54826,7 +54773,7 @@ function buildHistoryHandlers(deps) {
54826
54773
  if (!pid) return false;
54827
54774
  return isGuestPathAllowed(
54828
54775
  ctx.grants,
54829
- path56.join(personaRoot, pid),
54776
+ path57.join(personaRoot, pid),
54830
54777
  personaRoot,
54831
54778
  "read",
54832
54779
  userWorkDir
@@ -54838,7 +54785,7 @@ function buildHistoryHandlers(deps) {
54838
54785
  };
54839
54786
  const list = async (frame, _client, ctx) => {
54840
54787
  const args = HistoryListArgs.parse(frame);
54841
- assertGuestPath(ctx, path56.resolve(args.projectPath), personaRoot, "history:list");
54788
+ assertGuestPath(ctx, path57.resolve(args.projectPath), personaRoot, "history:list");
54842
54789
  const sessions = await history.listSessions(args);
54843
54790
  return { response: { type: "history:list", sessions } };
54844
54791
  };
@@ -54871,13 +54818,13 @@ function buildHistoryHandlers(deps) {
54871
54818
  };
54872
54819
  const subagents = async (frame, _client, ctx) => {
54873
54820
  const args = HistorySubagentsArgs.parse(frame);
54874
- assertGuestPath(ctx, path56.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
54821
+ assertGuestPath(ctx, path57.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
54875
54822
  const subs = await history.listSubagents(args);
54876
54823
  return { response: { type: "history:subagents", subagents: subs } };
54877
54824
  };
54878
54825
  const subagentRead = async (frame, _client, ctx) => {
54879
54826
  const args = HistorySubagentReadArgs.parse(frame);
54880
- assertGuestPath(ctx, path56.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
54827
+ assertGuestPath(ctx, path57.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
54881
54828
  const res = await history.readSubagent(args);
54882
54829
  return { response: { type: "history:subagent-read", ...res } };
54883
54830
  };
@@ -54886,7 +54833,7 @@ function buildHistoryHandlers(deps) {
54886
54833
  if (ctx?.principal.kind === "guest" && personaRoot) {
54887
54834
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
54888
54835
  const filtered = dirs.filter(
54889
- (d) => isGuestPathAllowed(ctx.grants, path56.resolve(d.cwd), personaRoot, "read", userWorkDir)
54836
+ (d) => isGuestPathAllowed(ctx.grants, path57.resolve(d.cwd), personaRoot, "read", userWorkDir)
54890
54837
  );
54891
54838
  return { response: { type: "history:recentDirs", dirs: filtered } };
54892
54839
  }
@@ -54903,7 +54850,7 @@ function buildHistoryHandlers(deps) {
54903
54850
  }
54904
54851
 
54905
54852
  // src/handlers/workspace.ts
54906
- var path57 = __toESM(require("path"), 1);
54853
+ var path58 = __toESM(require("path"), 1);
54907
54854
  var os16 = __toESM(require("os"), 1);
54908
54855
  init_protocol();
54909
54856
  init_protocol();
@@ -54945,22 +54892,22 @@ function buildWorkspaceHandlers(deps) {
54945
54892
  const args = WorkspaceListArgs.parse(frame);
54946
54893
  const isGuest = ctx?.principal.kind === "guest";
54947
54894
  const fallbackCwd = isGuest && personaRoot ? personaRoot : os16.homedir();
54948
- const resolvedCwd = path57.resolve(args.cwd ?? fallbackCwd);
54949
- const target = args.path ? path57.resolve(resolvedCwd, args.path) : resolvedCwd;
54895
+ const resolvedCwd = path58.resolve(args.cwd ?? fallbackCwd);
54896
+ const target = args.path ? path58.resolve(resolvedCwd, args.path) : resolvedCwd;
54950
54897
  assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
54951
54898
  const res = workspace.list({ ...args, cwd: resolvedCwd });
54952
54899
  return { response: { type: "workspace:list", ...res } };
54953
54900
  };
54954
54901
  const read = async (frame, _client, ctx) => {
54955
54902
  const args = WorkspaceReadArgs.parse(frame);
54956
- const target = path57.isAbsolute(args.path) ? path57.resolve(args.path) : path57.resolve(args.cwd, args.path);
54903
+ const target = path58.isAbsolute(args.path) ? path58.resolve(args.path) : path58.resolve(args.cwd, args.path);
54957
54904
  assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
54958
54905
  const res = workspace.read(args);
54959
54906
  return { response: { type: "workspace:read", ...res } };
54960
54907
  };
54961
54908
  const skillsList = async (frame, _client, ctx) => {
54962
54909
  const args = SkillsListArgs.parse(frame);
54963
- const cwdAbs = path57.resolve(args.cwd);
54910
+ const cwdAbs = path58.resolve(args.cwd);
54964
54911
  assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
54965
54912
  const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
54966
54913
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -54972,7 +54919,7 @@ function buildWorkspaceHandlers(deps) {
54972
54919
  };
54973
54920
  const agentsList = async (frame, _client, ctx) => {
54974
54921
  const args = AgentsListArgs.parse(frame);
54975
- const cwdAbs = path57.resolve(args.cwd);
54922
+ const cwdAbs = path58.resolve(args.cwd);
54976
54923
  assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
54977
54924
  if (args.tool === "codex") {
54978
54925
  return { response: { type: "agents:list", agents: [] } };
@@ -54994,20 +54941,20 @@ function buildWorkspaceHandlers(deps) {
54994
54941
  }
54995
54942
 
54996
54943
  // src/handlers/git.ts
54997
- var path59 = __toESM(require("path"), 1);
54944
+ var path60 = __toESM(require("path"), 1);
54998
54945
  init_protocol();
54999
54946
  init_protocol();
55000
54947
 
55001
54948
  // src/workspace/git.ts
55002
54949
  var import_node_child_process11 = require("child_process");
55003
- var import_node_fs42 = __toESM(require("fs"), 1);
55004
- var import_node_path46 = __toESM(require("path"), 1);
54950
+ var import_node_fs43 = __toESM(require("fs"), 1);
54951
+ var import_node_path47 = __toESM(require("path"), 1);
55005
54952
  var import_node_util = require("util");
55006
54953
  var pexec = (0, import_node_util.promisify)(import_node_child_process11.execFile);
55007
54954
  function normalizePath(p2) {
55008
- const resolved = import_node_path46.default.resolve(p2);
54955
+ const resolved = import_node_path47.default.resolve(p2);
55009
54956
  try {
55010
- return import_node_fs42.default.realpathSync(resolved);
54957
+ return import_node_fs43.default.realpathSync(resolved);
55011
54958
  } catch {
55012
54959
  return resolved;
55013
54960
  }
@@ -55081,7 +55028,7 @@ async function listGitBranches(cwd) {
55081
55028
  function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
55082
55029
  if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
55083
55030
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
55084
- if (!isGuestPathAllowed(ctx.grants, path59.resolve(cwd), personaRoot, "read", userWorkDir)) {
55031
+ if (!isGuestPathAllowed(ctx.grants, path60.resolve(cwd), personaRoot, "read", userWorkDir)) {
55085
55032
  throw new ClawdError(
55086
55033
  ERROR_CODES.UNAUTHORIZED,
55087
55034
  `guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
@@ -55826,7 +55773,7 @@ function buildPersonaHandlers(deps) {
55826
55773
  }
55827
55774
 
55828
55775
  // src/handlers/attachment.ts
55829
- var import_node_path47 = __toESM(require("path"), 1);
55776
+ var import_node_path48 = __toESM(require("path"), 1);
55830
55777
  init_protocol();
55831
55778
  init_protocol();
55832
55779
  var DEFAULT_TTL_SECONDS = 24 * 3600;
@@ -55842,10 +55789,8 @@ function assertGuestAttachmentPath(ctx, absPath, personaRoot, method, usersRoot)
55842
55789
  }
55843
55790
  function buildAttachmentHandlers(deps) {
55844
55791
  function guardAttachmentPath(ctx, sessionId, candidateAbs, method, nonGuestPolicy) {
55845
- if (ctx?.principal.kind === "owner") return;
55846
55792
  const scope = deps.getSessionScope?.(sessionId);
55847
- if (!scope) return;
55848
- if (scope.kind === "persona" && scope.mode === "guest") {
55793
+ if (scope?.kind === "persona" && scope.mode === "guest") {
55849
55794
  if (!deps.personaRoot) return;
55850
55795
  const userWorkDir = deps.usersRoot ? deriveUserWorkDir(scope.capId, deps.usersRoot) : void 0;
55851
55796
  if (!isPathInGuestBoundary(deps.personaRoot, scope.personaId, userWorkDir, candidateAbs)) {
@@ -55856,6 +55801,8 @@ function buildAttachmentHandlers(deps) {
55856
55801
  }
55857
55802
  return;
55858
55803
  }
55804
+ if (ctx?.principal.kind === "owner") return;
55805
+ if (!scope) return;
55859
55806
  if (nonGuestPolicy === "group-acl") return;
55860
55807
  const cwd = deps.sessionStore?.read(sessionId)?.cwd;
55861
55808
  if (!cwd) return;
@@ -55906,12 +55853,12 @@ function buildAttachmentHandlers(deps) {
55906
55853
  `session ${args.sessionId} scope unresolved`
55907
55854
  );
55908
55855
  }
55909
- const cwdAbs = import_node_path47.default.resolve(sessionFile.cwd);
55910
- const candidateAbs = import_node_path47.default.isAbsolute(args.relPath) ? import_node_path47.default.resolve(args.relPath) : import_node_path47.default.resolve(cwdAbs, args.relPath);
55856
+ const cwdAbs = import_node_path48.default.resolve(sessionFile.cwd);
55857
+ 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
55858
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
55912
55859
  const entries = deps.groupFileStore.list(scope, args.sessionId);
55913
55860
  const entry = entries.find((e) => {
55914
- const storedAbs = import_node_path47.default.isAbsolute(e.relPath) ? import_node_path47.default.resolve(e.relPath) : import_node_path47.default.resolve(cwdAbs, e.relPath);
55861
+ 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
55862
  return storedAbs === candidateAbs && !e.stale;
55916
55863
  });
55917
55864
  if (!entry) {
@@ -55936,7 +55883,7 @@ function buildAttachmentHandlers(deps) {
55936
55883
  if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
55937
55884
  const f = deps.sessionStore.read(sessionId);
55938
55885
  if (!f) return;
55939
- assertGuestAttachmentPath(ctx, import_node_path47.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
55886
+ assertGuestAttachmentPath(ctx, import_node_path48.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
55940
55887
  }
55941
55888
  const groupAdd = async (frame, _client, ctx) => {
55942
55889
  if (!deps.groupFileStore || !deps.getSessionScope) {
@@ -55951,10 +55898,10 @@ function buildAttachmentHandlers(deps) {
55951
55898
  if (!scope) {
55952
55899
  throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
55953
55900
  }
55954
- const cwdAbs = import_node_path47.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
55955
- const candidateAbs = import_node_path47.default.isAbsolute(args.relPath) ? import_node_path47.default.resolve(args.relPath) : import_node_path47.default.resolve(cwdAbs, args.relPath);
55901
+ const cwdAbs = import_node_path48.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
55902
+ 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
55903
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
55957
- const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
55904
+ const from = args.origin === "agent" ? "agent" : ctx?.principal.kind === "owner" ? "owner" : "agent";
55958
55905
  const size = 0;
55959
55906
  const entry = deps.groupFileStore.upsert(scope, args.sessionId, {
55960
55907
  relPath: args.relPath,
@@ -56011,19 +55958,19 @@ function buildAttachmentHandlers(deps) {
56011
55958
 
56012
55959
  // src/handlers/extension.ts
56013
55960
  var import_promises9 = __toESM(require("fs/promises"), 1);
56014
- var import_node_path52 = __toESM(require("path"), 1);
55961
+ var import_node_path53 = __toESM(require("path"), 1);
56015
55962
  init_protocol();
56016
55963
 
56017
55964
  // src/extension/bundle-zip.ts
56018
55965
  var import_promises6 = __toESM(require("fs/promises"), 1);
56019
- var import_node_path48 = __toESM(require("path"), 1);
55966
+ var import_node_path49 = __toESM(require("path"), 1);
56020
55967
  var import_node_crypto14 = __toESM(require("crypto"), 1);
56021
55968
  var import_jszip2 = __toESM(require_lib3(), 1);
56022
55969
  async function bundleExtensionDir(dir) {
56023
55970
  const entries = await listFilesSorted(dir);
56024
55971
  const zip = new import_jszip2.default();
56025
55972
  for (const rel of entries) {
56026
- const abs = import_node_path48.default.join(dir, rel);
55973
+ const abs = import_node_path49.default.join(dir, rel);
56027
55974
  const content = await import_promises6.default.readFile(abs);
56028
55975
  zip.file(rel, content, { date: FIXED_DATE });
56029
55976
  }
@@ -56044,7 +55991,7 @@ async function listFilesSorted(rootDir) {
56044
55991
  return out;
56045
55992
  }
56046
55993
  async function walk(absRoot, relPrefix, out) {
56047
- const dirAbs = import_node_path48.default.join(absRoot, relPrefix);
55994
+ const dirAbs = import_node_path49.default.join(absRoot, relPrefix);
56048
55995
  const entries = await import_promises6.default.readdir(dirAbs, { withFileTypes: true });
56049
55996
  for (const e of entries) {
56050
55997
  if (IGNORE_BASENAMES.has(e.name)) continue;
@@ -56098,25 +56045,25 @@ function computePublishCheck(args) {
56098
56045
 
56099
56046
  // src/extension/install-flow.ts
56100
56047
  var import_promises7 = __toESM(require("fs/promises"), 1);
56101
- var import_node_path50 = __toESM(require("path"), 1);
56048
+ var import_node_path51 = __toESM(require("path"), 1);
56102
56049
  var import_node_os19 = __toESM(require("os"), 1);
56103
56050
  var import_node_crypto15 = __toESM(require("crypto"), 1);
56104
56051
  var import_jszip3 = __toESM(require_lib3(), 1);
56105
56052
 
56106
56053
  // src/extension/paths.ts
56107
56054
  var import_node_os18 = __toESM(require("os"), 1);
56108
- var import_node_path49 = __toESM(require("path"), 1);
56055
+ var import_node_path50 = __toESM(require("path"), 1);
56109
56056
  function clawdHomeRoot(override) {
56110
- return override ?? process.env.CLAWD_HOME ?? import_node_path49.default.join(import_node_os18.default.homedir(), ".clawd");
56057
+ return override ?? process.env.CLAWD_HOME ?? import_node_path50.default.join(import_node_os18.default.homedir(), ".clawd");
56111
56058
  }
56112
56059
  function extensionsRoot(override) {
56113
- return import_node_path49.default.join(clawdHomeRoot(override), "extensions");
56060
+ return import_node_path50.default.join(clawdHomeRoot(override), "extensions");
56114
56061
  }
56115
56062
  function publishedChannelsFile(override) {
56116
- return import_node_path49.default.join(clawdHomeRoot(override), "extensions-published.json");
56063
+ return import_node_path50.default.join(clawdHomeRoot(override), "extensions-published.json");
56117
56064
  }
56118
56065
  function bundleCacheRoot(override) {
56119
- return import_node_path49.default.join(clawdHomeRoot(override), "extension-bundles");
56066
+ return import_node_path50.default.join(clawdHomeRoot(override), "extension-bundles");
56120
56067
  }
56121
56068
 
56122
56069
  // src/extension/install-flow.ts
@@ -56143,7 +56090,7 @@ async function installFromChannel(args, deps) {
56143
56090
  throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
56144
56091
  }
56145
56092
  for (const name of Object.keys(zip.files)) {
56146
- if (name.includes("..") || name.startsWith("/") || import_node_path50.default.isAbsolute(name)) {
56093
+ if (name.includes("..") || name.startsWith("/") || import_node_path51.default.isAbsolute(name)) {
56147
56094
  throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
56148
56095
  }
56149
56096
  }
@@ -56175,7 +56122,7 @@ async function installFromChannel(args, deps) {
56175
56122
  );
56176
56123
  }
56177
56124
  const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
56178
- const destDir = import_node_path50.default.join(deps.extensionsRoot, localExtId);
56125
+ const destDir = import_node_path51.default.join(deps.extensionsRoot, localExtId);
56179
56126
  let destExists = false;
56180
56127
  try {
56181
56128
  await import_promises7.default.access(destDir);
@@ -56189,16 +56136,16 @@ async function installFromChannel(args, deps) {
56189
56136
  );
56190
56137
  }
56191
56138
  const stage = await import_promises7.default.mkdtemp(
56192
- import_node_path50.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
56139
+ import_node_path51.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
56193
56140
  );
56194
56141
  try {
56195
56142
  for (const [name, entry] of Object.entries(zip.files)) {
56196
- const dest = import_node_path50.default.join(stage, name);
56143
+ const dest = import_node_path51.default.join(stage, name);
56197
56144
  if (entry.dir) {
56198
56145
  await import_promises7.default.mkdir(dest, { recursive: true });
56199
56146
  continue;
56200
56147
  }
56201
- await import_promises7.default.mkdir(import_node_path50.default.dirname(dest), { recursive: true });
56148
+ await import_promises7.default.mkdir(import_node_path51.default.dirname(dest), { recursive: true });
56202
56149
  if (name === "manifest.json") {
56203
56150
  const rewritten = { ...parsed.data, id: localExtId };
56204
56151
  await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -56219,7 +56166,7 @@ async function installFromChannel(args, deps) {
56219
56166
 
56220
56167
  // src/extension/update-flow.ts
56221
56168
  var import_promises8 = __toESM(require("fs/promises"), 1);
56222
- var import_node_path51 = __toESM(require("path"), 1);
56169
+ var import_node_path52 = __toESM(require("path"), 1);
56223
56170
  var import_node_os20 = __toESM(require("os"), 1);
56224
56171
  var import_node_crypto16 = __toESM(require("crypto"), 1);
56225
56172
  var import_jszip4 = __toESM(require_lib3(), 1);
@@ -56236,11 +56183,11 @@ async function updateFromChannel(args, deps) {
56236
56183
  channelRef.extId,
56237
56184
  channelRef.ownerPrincipalId
56238
56185
  );
56239
- const liveDir = import_node_path51.default.join(deps.extensionsRoot, localExtId);
56186
+ const liveDir = import_node_path52.default.join(deps.extensionsRoot, localExtId);
56240
56187
  const prevDir = `${liveDir}.prev`;
56241
56188
  let existingVersion;
56242
56189
  try {
56243
- const raw = await import_promises8.default.readFile(import_node_path51.default.join(liveDir, "manifest.json"), "utf8");
56190
+ const raw = await import_promises8.default.readFile(import_node_path52.default.join(liveDir, "manifest.json"), "utf8");
56244
56191
  const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
56245
56192
  if (!parsed2.success) {
56246
56193
  throw new UpdateError(
@@ -56273,7 +56220,7 @@ async function updateFromChannel(args, deps) {
56273
56220
  throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
56274
56221
  }
56275
56222
  for (const name of Object.keys(zip.files)) {
56276
- if (name.includes("..") || name.startsWith("/") || import_node_path51.default.isAbsolute(name)) {
56223
+ if (name.includes("..") || name.startsWith("/") || import_node_path52.default.isAbsolute(name)) {
56277
56224
  throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
56278
56225
  }
56279
56226
  }
@@ -56308,16 +56255,16 @@ async function updateFromChannel(args, deps) {
56308
56255
  await import_promises8.default.rm(prevDir, { recursive: true, force: true });
56309
56256
  await import_promises8.default.rename(liveDir, prevDir);
56310
56257
  const stage = await import_promises8.default.mkdtemp(
56311
- import_node_path51.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
56258
+ import_node_path52.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
56312
56259
  );
56313
56260
  try {
56314
56261
  for (const [name, entry] of Object.entries(zip.files)) {
56315
- const dest = import_node_path51.default.join(stage, name);
56262
+ const dest = import_node_path52.default.join(stage, name);
56316
56263
  if (entry.dir) {
56317
56264
  await import_promises8.default.mkdir(dest, { recursive: true });
56318
56265
  continue;
56319
56266
  }
56320
- await import_promises8.default.mkdir(import_node_path51.default.dirname(dest), { recursive: true });
56267
+ await import_promises8.default.mkdir(import_node_path52.default.dirname(dest), { recursive: true });
56321
56268
  if (name === "manifest.json") {
56322
56269
  const rewritten = { ...parsed.data, id: localExtId };
56323
56270
  await import_promises8.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -56410,7 +56357,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
56410
56357
  );
56411
56358
  }
56412
56359
  }
56413
- const manifestPath = import_node_path52.default.join(root, extId, "manifest.json");
56360
+ const manifestPath = import_node_path53.default.join(root, extId, "manifest.json");
56414
56361
  const manifest = await readManifest(root, extId);
56415
56362
  const next = { ...manifest, version: newVersion };
56416
56363
  const tmp = `${manifestPath}.tmp`;
@@ -56418,7 +56365,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
56418
56365
  await import_promises9.default.rename(tmp, manifestPath);
56419
56366
  }
56420
56367
  async function readManifest(root, extId) {
56421
- const file = import_node_path52.default.join(root, extId, "manifest.json");
56368
+ const file = import_node_path53.default.join(root, extId, "manifest.json");
56422
56369
  let raw;
56423
56370
  try {
56424
56371
  raw = await import_promises9.default.readFile(file, "utf8");
@@ -56509,7 +56456,7 @@ function buildExtensionHandlers(deps) {
56509
56456
  };
56510
56457
  async function buildSnapshotMeta(extId) {
56511
56458
  const manifest = await readManifest(deps.root, extId);
56512
- const { sha256, buffer } = await bundleExtensionDir(import_node_path52.default.join(deps.root, extId));
56459
+ const { sha256, buffer } = await bundleExtensionDir(import_node_path53.default.join(deps.root, extId));
56513
56460
  return { manifest, contentHash: sha256, buffer };
56514
56461
  }
56515
56462
  const publish = async (frame, _client, ctx) => {
@@ -56690,9 +56637,9 @@ function buildExtensionHandlers(deps) {
56690
56637
  }
56691
56638
 
56692
56639
  // src/app-builder/project-store.ts
56693
- var import_node_fs43 = require("fs");
56640
+ var import_node_fs44 = require("fs");
56694
56641
  var import_node_child_process12 = require("child_process");
56695
- var import_node_path53 = require("path");
56642
+ var import_node_path54 = require("path");
56696
56643
  init_protocol();
56697
56644
  var PROJECTS_DIR = "projects";
56698
56645
  var META_FILE = ".clawd-project.json";
@@ -56706,19 +56653,19 @@ var ProjectStore = class {
56706
56653
  root;
56707
56654
  /** projects/<name>/.clawd-project.json 路径 */
56708
56655
  metaPath(name) {
56709
- return (0, import_node_path53.join)(this.projectsRoot(), name, META_FILE);
56656
+ return (0, import_node_path54.join)(this.projectsRoot(), name, META_FILE);
56710
56657
  }
56711
56658
  /** projects/<name>/ 目录路径(cwd 用) */
56712
56659
  projectDir(name) {
56713
- return (0, import_node_path53.join)(this.projectsRoot(), name);
56660
+ return (0, import_node_path54.join)(this.projectsRoot(), name);
56714
56661
  }
56715
56662
  projectsRoot() {
56716
- return (0, import_node_path53.join)(this.root, PROJECTS_DIR);
56663
+ return (0, import_node_path54.join)(this.root, PROJECTS_DIR);
56717
56664
  }
56718
56665
  async list() {
56719
56666
  let entries;
56720
56667
  try {
56721
- entries = await import_node_fs43.promises.readdir(this.projectsRoot());
56668
+ entries = await import_node_fs44.promises.readdir(this.projectsRoot());
56722
56669
  } catch (err) {
56723
56670
  if (err.code === "ENOENT") return [];
56724
56671
  throw err;
@@ -56726,7 +56673,7 @@ var ProjectStore = class {
56726
56673
  const out = [];
56727
56674
  for (const name of entries) {
56728
56675
  try {
56729
- const raw = await import_node_fs43.promises.readFile(this.metaPath(name), "utf8");
56676
+ const raw = await import_node_fs44.promises.readFile(this.metaPath(name), "utf8");
56730
56677
  const json = JSON.parse(raw);
56731
56678
  let migrated = false;
56732
56679
  if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
@@ -56737,7 +56684,7 @@ var ProjectStore = class {
56737
56684
  if (parsed.success) {
56738
56685
  out.push(parsed.data);
56739
56686
  if (migrated) {
56740
- void import_node_fs43.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
56687
+ void import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
56741
56688
  });
56742
56689
  }
56743
56690
  }
@@ -56781,8 +56728,8 @@ var ProjectStore = class {
56781
56728
  throw new Error(`invalid name "${name}": ${validated.error.message}`);
56782
56729
  }
56783
56730
  const dir = this.projectDir(name);
56784
- await import_node_fs43.promises.mkdir(dir, { recursive: true });
56785
- await import_node_fs43.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
56731
+ await import_node_fs44.promises.mkdir(dir, { recursive: true });
56732
+ await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
56786
56733
  return meta;
56787
56734
  }
56788
56735
  /**
@@ -56825,7 +56772,7 @@ var ProjectStore = class {
56825
56772
  }
56826
56773
  async delete(name) {
56827
56774
  const dir = this.projectDir(name);
56828
- await import_node_fs43.promises.rm(dir, { recursive: true, force: true });
56775
+ await import_node_fs44.promises.rm(dir, { recursive: true, force: true });
56829
56776
  }
56830
56777
  async updatePort(name, newPort) {
56831
56778
  if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
@@ -56841,7 +56788,7 @@ var ProjectStore = class {
56841
56788
  throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
56842
56789
  }
56843
56790
  const updated = { ...target, port: newPort };
56844
- await import_node_fs43.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
56791
+ await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
56845
56792
  return updated;
56846
56793
  }
56847
56794
  /**
@@ -56858,7 +56805,7 @@ var ProjectStore = class {
56858
56805
  if (!validated.success) {
56859
56806
  throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
56860
56807
  }
56861
- await import_node_fs43.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
56808
+ await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
56862
56809
  return validated.data;
56863
56810
  }
56864
56811
  /**
@@ -56879,7 +56826,7 @@ var ProjectStore = class {
56879
56826
  if (!validated.success) {
56880
56827
  throw new Error(`invalid publishJob: ${validated.error.message}`);
56881
56828
  }
56882
- await import_node_fs43.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
56829
+ await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
56883
56830
  return validated.data;
56884
56831
  }
56885
56832
  /** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
@@ -56894,7 +56841,7 @@ var ProjectStore = class {
56894
56841
  if (!validated.success) {
56895
56842
  throw new Error(`failed to clear publishJob: ${validated.error.message}`);
56896
56843
  }
56897
- await import_node_fs43.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
56844
+ await import_node_fs44.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
56898
56845
  return validated.data;
56899
56846
  }
56900
56847
  };
@@ -57015,8 +56962,8 @@ var PublishJobRegistry = class {
57015
56962
 
57016
56963
  // src/app-builder/publish-job-runner.ts
57017
56964
  var import_node_child_process14 = require("child_process");
57018
- var import_node_fs44 = require("fs");
57019
- var import_node_path54 = require("path");
56965
+ var import_node_fs45 = require("fs");
56966
+ var import_node_path55 = require("path");
57020
56967
 
57021
56968
  // src/app-builder/publish-stage-parser.ts
57022
56969
  var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
@@ -57048,10 +56995,10 @@ async function startPublishJob(deps, args) {
57048
56995
  return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
57049
56996
  }
57050
56997
  const projDir = projectDir(args.name);
57051
- const logPath = (0, import_node_path54.join)(projDir, ".publish.log");
56998
+ const logPath = (0, import_node_path55.join)(projDir, ".publish.log");
57052
56999
  let logStream = null;
57053
57000
  try {
57054
- logStream = (0, import_node_fs44.createWriteStream)(logPath, { flags: "w" });
57001
+ logStream = (0, import_node_fs45.createWriteStream)(logPath, { flags: "w" });
57055
57002
  } catch {
57056
57003
  logStream = null;
57057
57004
  }
@@ -57308,8 +57255,8 @@ async function recoverInterruptedJobs(deps) {
57308
57255
 
57309
57256
  // src/handlers/app-builder.ts
57310
57257
  init_protocol();
57311
- var import_node_path55 = require("path");
57312
- var import_node_fs45 = require("fs");
57258
+ var import_node_path56 = require("path");
57259
+ var import_node_fs46 = require("fs");
57313
57260
  var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
57314
57261
  var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
57315
57262
  async function recoverInterruptedPublishJobs(store, logger) {
@@ -57390,7 +57337,7 @@ function buildAppBuilderHandlers(deps) {
57390
57337
  async function listAllUsersProjects() {
57391
57338
  if (!deps.usersRoot || !deps.getStore) return [];
57392
57339
  const getStore = deps.getStore;
57393
- const userIds = await import_node_fs45.promises.readdir(deps.usersRoot).catch(() => []);
57340
+ const userIds = await import_node_fs46.promises.readdir(deps.usersRoot).catch(() => []);
57394
57341
  const perUser = await Promise.all(
57395
57342
  userIds.map((uid) => getStore(uid).list().catch(() => []))
57396
57343
  );
@@ -57409,12 +57356,14 @@ function buildAppBuilderHandlers(deps) {
57409
57356
  }
57410
57357
  const listProjects = async (frame, _client, ctx) => {
57411
57358
  const f = frame;
57359
+ const sessionId = typeof f.sessionId === "string" ? f.sessionId : void 0;
57412
57360
  let metas;
57413
- if (ctx?.principal.kind === "owner" && deps.usersRoot && deps.getStore) {
57361
+ if (sessionId) {
57362
+ metas = await resolveStore({ ctx, sessionId }).list();
57363
+ } else if (ctx?.principal.kind === "owner" && deps.usersRoot && deps.getStore) {
57414
57364
  metas = await listAllUsersProjects();
57415
57365
  } else {
57416
- const sessionId = typeof f.sessionId === "string" ? f.sessionId : void 0;
57417
- metas = await resolveStore({ ctx, sessionId }).list();
57366
+ metas = await resolveStore({ ctx }).list();
57418
57367
  }
57419
57368
  return { response: { projects: metas.map(toProjectWithStatus) } };
57420
57369
  };
@@ -57466,8 +57415,8 @@ function buildAppBuilderHandlers(deps) {
57466
57415
  const project = await userStore.create(f.name, reservedPorts);
57467
57416
  try {
57468
57417
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
57469
- const templateSrcDir = (0, import_node_path55.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
57470
- const scaffoldScript = (0, import_node_path55.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
57418
+ const templateSrcDir = (0, import_node_path56.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
57419
+ const scaffoldScript = (0, import_node_path56.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
57471
57420
  const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
57472
57421
  deps.logger?.info("app-builder.scaffold.done", {
57473
57422
  name: project.name,
@@ -57688,7 +57637,7 @@ function buildAppBuilderHandlers(deps) {
57688
57637
  await userStore.clearPublishJob(args.name);
57689
57638
  }
57690
57639
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
57691
- const scriptPath = (0, import_node_path55.join)(deps.deployKitRoot, "scripts", "publish.sh");
57640
+ const scriptPath = (0, import_node_path56.join)(deps.deployKitRoot, "scripts", "publish.sh");
57692
57641
  deps.logger?.info("app-builder.publish.start", {
57693
57642
  name: args.name,
57694
57643
  sessionId: boundSession.sessionId,
@@ -57864,7 +57813,7 @@ function buildVisitorHandlers(deps) {
57864
57813
 
57865
57814
  // src/extension/registry.ts
57866
57815
  var import_promises10 = __toESM(require("fs/promises"), 1);
57867
- var import_node_path56 = __toESM(require("path"), 1);
57816
+ var import_node_path57 = __toESM(require("path"), 1);
57868
57817
  async function loadAll(root) {
57869
57818
  let entries;
57870
57819
  try {
@@ -57877,13 +57826,13 @@ async function loadAll(root) {
57877
57826
  for (const ent of entries) {
57878
57827
  if (!ent.isDirectory()) continue;
57879
57828
  if (ent.name.startsWith(".")) continue;
57880
- records.push(await loadOne(import_node_path56.default.join(root, ent.name), ent.name));
57829
+ records.push(await loadOne(import_node_path57.default.join(root, ent.name), ent.name));
57881
57830
  }
57882
57831
  records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
57883
57832
  return records;
57884
57833
  }
57885
57834
  async function loadOne(dir, dirName) {
57886
- const manifestPath = import_node_path56.default.join(dir, "manifest.json");
57835
+ const manifestPath = import_node_path57.default.join(dir, "manifest.json");
57887
57836
  let raw;
57888
57837
  try {
57889
57838
  raw = await import_promises10.default.readFile(manifestPath, "utf8");
@@ -57928,7 +57877,7 @@ async function loadOne(dir, dirName) {
57928
57877
 
57929
57878
  // src/extension/uninstall.ts
57930
57879
  var import_promises11 = __toESM(require("fs/promises"), 1);
57931
- var import_node_path57 = __toESM(require("path"), 1);
57880
+ var import_node_path58 = __toESM(require("path"), 1);
57932
57881
  var UninstallError = class extends Error {
57933
57882
  constructor(code, message) {
57934
57883
  super(message);
@@ -57937,7 +57886,7 @@ var UninstallError = class extends Error {
57937
57886
  code;
57938
57887
  };
57939
57888
  async function uninstall(deps) {
57940
- const dir = import_node_path57.default.join(deps.root, deps.extId);
57889
+ const dir = import_node_path58.default.join(deps.root, deps.extId);
57941
57890
  try {
57942
57891
  await import_promises11.default.access(dir);
57943
57892
  } catch {
@@ -58524,6 +58473,21 @@ var METHOD_GRANT_MAP = {
58524
58473
  // ---- shift:* (排班 mock,owner-only 读) ----
58525
58474
  // mock 阶段只 owner 用;接 guest 后再考虑分级
58526
58475
  "shift:list": ADMIN_ANY,
58476
+ // ---- shift-internal:* + inbox:sendDm + personaDispatch complete/list ----
58477
+ // 统一 RPC 鉴权(spec 2026-07-28):原 /api/rpc 白名单 method 收编。调用方 = 本机
58478
+ // MCP server 持 owner token → ADMIN_ANY 即「owner token 即可」;业务归因(sendDm 的
58479
+ // sender、shift 的 personaId)在 handler 内按 body 字段处理,与鉴权无关。
58480
+ "shift-internal:add": ADMIN_ANY,
58481
+ "shift-internal:list": ADMIN_ANY,
58482
+ "shift-internal:cancel": ADMIN_ANY,
58483
+ "shift-internal:update": ADMIN_ANY,
58484
+ "shift-internal:history": ADMIN_ANY,
58485
+ "inbox:sendDm": ADMIN_ANY,
58486
+ "personaDispatch:complete": ADMIN_ANY,
58487
+ "personaDispatch:list": ADMIN_ANY,
58488
+ // 跨设备轮询(spec §5):A 端 daemon 凭 connect token 查 outcome;handler 内校验
58489
+ // 台账行 sourceSessionId === ctx.principal.id,防 guest 查别人的 dispatch。
58490
+ "personaDispatch:get": CAPABILITY_SCOPED,
58527
58491
  "skills:list": CAPABILITY_SCOPED,
58528
58492
  "agents:list": CAPABILITY_SCOPED,
58529
58493
  "git:root": CAPABILITY_SCOPED,
@@ -58543,7 +58507,7 @@ var METHOD_GRANT_MAP = {
58543
58507
  "persona:delete": ADMIN_ANY,
58544
58508
  // 跨设备 dispatch:A 的 peer daemon 凭 connect token 经 /rpc 调进来;dispatcher 放行,
58545
58509
  // run handler 内做 persona.public + assertGrant(send) 双重校验(guest grants 是 wildcard,
58546
- // 单 assertGrant 不够)。本地 cc 仍走 /api/rpc(EXPOSED_RPC_METHODS),不经此表。
58510
+ // 单 assertGrant 不够)。本地 MCP server /rpc + Bearer 进来,同过此表(owner 恒过)。
58547
58511
  "personaDispatch:run": CAPABILITY_SCOPED,
58548
58512
  // 跨设备 exec(spec 2026-07-16):public——"能连上=有 auth",业务在 handler 校
58549
58513
  // (ensureGuest + remoteAccessAllowed),跟 inbox:postMessage 同模式。
@@ -58655,7 +58619,7 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
58655
58619
 
58656
58620
  // src/extension/runtime.ts
58657
58621
  var import_node_child_process17 = require("child_process");
58658
- var import_node_path58 = __toESM(require("path"), 1);
58622
+ var import_node_path59 = __toESM(require("path"), 1);
58659
58623
  var import_promises12 = require("timers/promises");
58660
58624
 
58661
58625
  // src/extension/port-allocator.ts
@@ -58756,7 +58720,7 @@ var Runtime = class {
58756
58720
  /\$CLAWOS_EXT_PORT/g,
58757
58721
  String(port)
58758
58722
  );
58759
- const dir = import_node_path58.default.join(this.root, extId);
58723
+ const dir = import_node_path59.default.join(this.root, extId);
58760
58724
  const env = {
58761
58725
  ...process.env,
58762
58726
  CLAWOS_EXT_PORT: String(port),
@@ -58868,7 +58832,7 @@ ${handle.stderrTail}`
58868
58832
 
58869
58833
  // src/extension/published-channels.ts
58870
58834
  var import_promises13 = __toESM(require("fs/promises"), 1);
58871
- var import_node_path59 = __toESM(require("path"), 1);
58835
+ var import_node_path60 = __toESM(require("path"), 1);
58872
58836
  init_zod();
58873
58837
  var PublishedChannelsError = class extends Error {
58874
58838
  constructor(code, message) {
@@ -58967,7 +58931,7 @@ var PublishedChannelStore = class {
58967
58931
  )
58968
58932
  };
58969
58933
  const tmp = `${this.filePath}.tmp`;
58970
- await import_promises13.default.mkdir(import_node_path59.default.dirname(this.filePath), { recursive: true });
58934
+ await import_promises13.default.mkdir(import_node_path60.default.dirname(this.filePath), { recursive: true });
58971
58935
  await import_promises13.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
58972
58936
  await import_promises13.default.rename(tmp, this.filePath);
58973
58937
  }
@@ -58975,7 +58939,7 @@ var PublishedChannelStore = class {
58975
58939
 
58976
58940
  // src/extension/bundle-cache.ts
58977
58941
  var import_promises14 = __toESM(require("fs/promises"), 1);
58978
- var import_node_path60 = __toESM(require("path"), 1);
58942
+ var import_node_path61 = __toESM(require("path"), 1);
58979
58943
  var BundleCache = class {
58980
58944
  constructor(rootDir) {
58981
58945
  this.rootDir = rootDir;
@@ -58984,14 +58948,14 @@ var BundleCache = class {
58984
58948
  /** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
58985
58949
  async write(snapshotHash, buffer) {
58986
58950
  await import_promises14.default.mkdir(this.rootDir, { recursive: true });
58987
- const file = import_node_path60.default.join(this.rootDir, `${snapshotHash}.zip`);
58951
+ const file = import_node_path61.default.join(this.rootDir, `${snapshotHash}.zip`);
58988
58952
  const tmp = `${file}.tmp`;
58989
58953
  await import_promises14.default.writeFile(tmp, buffer, { mode: 384 });
58990
58954
  await import_promises14.default.rename(tmp, file);
58991
58955
  }
58992
58956
  /** Returns the bundle bytes, or null when the file doesn't exist. */
58993
58957
  async read(snapshotHash) {
58994
- const file = import_node_path60.default.join(this.rootDir, `${snapshotHash}.zip`);
58958
+ const file = import_node_path61.default.join(this.rootDir, `${snapshotHash}.zip`);
58995
58959
  try {
58996
58960
  return await import_promises14.default.readFile(file);
58997
58961
  } catch (e) {
@@ -59001,7 +58965,7 @@ var BundleCache = class {
59001
58965
  }
59002
58966
  /** Idempotent — missing file is not an error. */
59003
58967
  async delete(snapshotHash) {
59004
- const file = import_node_path60.default.join(this.rootDir, `${snapshotHash}.zip`);
58968
+ const file = import_node_path61.default.join(this.rootDir, `${snapshotHash}.zip`);
59005
58969
  await import_promises14.default.rm(file, { force: true });
59006
58970
  }
59007
58971
  };
@@ -59024,24 +58988,24 @@ async function startDaemon(config) {
59024
58988
  sampling: logShippingCfg.sampling,
59025
58989
  homeDir: import_node_os22.default.homedir()
59026
58990
  });
59027
- const logDir = import_node_path61.default.join(config.dataDir, "log");
59028
- import_node_fs46.default.mkdirSync(logDir, { recursive: true });
58991
+ const logDir = import_node_path62.default.join(config.dataDir, "log");
58992
+ import_node_fs47.default.mkdirSync(logDir, { recursive: true });
59029
58993
  const logger = createLogger({
59030
58994
  level: config.logLevel,
59031
- file: import_node_path61.default.join(logDir, "clawd.log"),
58995
+ file: import_node_path62.default.join(logDir, "clawd.log"),
59032
58996
  logClient
59033
58997
  });
59034
58998
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
59035
58999
  const screenIdleProbeLogger = createFileOnlyLogger({
59036
- file: import_node_path61.default.join(logDir, "screen-idle-probe.log"),
59000
+ file: import_node_path62.default.join(logDir, "screen-idle-probe.log"),
59037
59001
  level: "debug"
59038
59002
  });
59039
59003
  logger.info("screen-idle probe logger enabled", {
59040
- file: import_node_path61.default.join(logDir, "screen-idle-probe.log")
59004
+ file: import_node_path62.default.join(logDir, "screen-idle-probe.log")
59041
59005
  });
59042
59006
  const CAP_TARGETS = [
59043
- import_node_path61.default.join(logDir, "clawd.log"),
59044
- import_node_path61.default.join(logDir, "screen-idle-probe.log")
59007
+ import_node_path62.default.join(logDir, "clawd.log"),
59008
+ import_node_path62.default.join(logDir, "screen-idle-probe.log")
59045
59009
  ];
59046
59010
  const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
59047
59011
  const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
@@ -59065,12 +59029,7 @@ async function startDaemon(config) {
59065
59029
  if (pre.status === "stale") {
59066
59030
  logger.warn("stale state file detected, overwriting", { pid: pre.existing.pid });
59067
59031
  }
59068
- let resolvedAuthToken = null;
59069
- if (config.authToken && config.authToken.trim()) {
59070
- resolvedAuthToken = config.authToken.trim();
59071
- } else if (config.tunnel) {
59072
- resolvedAuthToken = authFile.token;
59073
- }
59032
+ const resolvedAuthToken = config.authToken && config.authToken.trim() ? config.authToken.trim() : authFile.token;
59074
59033
  const ownerIdentityStore = new OwnerIdentityStore(config.dataDir);
59075
59034
  const feishuIdentity = ownerIdentityStore.read();
59076
59035
  let feishuActive = feishuIdentity !== null;
@@ -59079,7 +59038,7 @@ async function startDaemon(config) {
59079
59038
  let ownerProvider = feishuIdentity?.identity.provider ?? "";
59080
59039
  let ownerDisplayName = feishuIdentity?.identity.displayName ?? loadOwnerDisplayName(config.dataDir);
59081
59040
  let ownerFeishuUnionId = feishuIdentity?.identity.unionId;
59082
- const authMode = resolvedAuthToken == null ? "none" : "first-message";
59041
+ const authMode = "first-message";
59083
59042
  const inboxStore = new InboxStore(config.dataDir);
59084
59043
  const inboxManager = new InboxManager(inboxStore, (_peerDeviceId, frame) => {
59085
59044
  wsServer?.broadcastToOwners(frame);
@@ -59095,7 +59054,7 @@ async function startDaemon(config) {
59095
59054
  // persona web 分享:访客 visitor token = daemon 自签 HMAC(无 aud),独立于飞书 connect token
59096
59055
  // 基建——secret 恒存在(auth-store backfill),故无条件注入。验签 ok → guest ctx(persona wildcard)。
59097
59056
  verifyVisitorToken: (t) => {
59098
- const r = verifyVisitorToken(authFile.visitorTokenSecret, t, Math.floor(Date.now() / 1e3));
59057
+ const r = verifyVisitorToken(authFile.token, t, Math.floor(Date.now() / 1e3));
59099
59058
  return r.ok ? { ok: true, payload: r.payload } : { ok: false };
59100
59059
  },
59101
59060
  ...feishuActive ? {
@@ -59143,8 +59102,8 @@ async function startDaemon(config) {
59143
59102
  const agents = new AgentsScanner();
59144
59103
  const history = new ClaudeHistoryReader();
59145
59104
  let transport = null;
59146
- const personaStore = new PersonaStore(import_node_path61.default.join(config.dataDir, "personas"));
59147
- const usersRoot = import_node_path61.default.join(config.dataDir, "users");
59105
+ const personaStore = new PersonaStore(import_node_path62.default.join(config.dataDir, "personas"));
59106
+ const usersRoot = import_node_path62.default.join(config.dataDir, "users");
59148
59107
  const defaultsRoot = findDefaultsRoot(logger);
59149
59108
  if (defaultsRoot) {
59150
59109
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -59164,7 +59123,7 @@ async function startDaemon(config) {
59164
59123
  migrateCodexSandbox({ store: personaStore, logger });
59165
59124
  const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
59166
59125
  const dispatchStore = createDispatchStore({
59167
- filePath: import_node_path61.default.join(config.dataDir, "dispatch.json"),
59126
+ filePath: import_node_path62.default.join(config.dataDir, "dispatch.json"),
59168
59127
  now: () => Date.now(),
59169
59128
  onFlushError: (err) => logger.error("dispatch store flush failed", {
59170
59129
  reason: err instanceof Error ? err.message : String(err)
@@ -59179,20 +59138,23 @@ async function startDaemon(config) {
59179
59138
  now: () => Date.now(),
59180
59139
  onCompleted: (sid) => deliverPendingRef?.(sid)
59181
59140
  });
59182
- const here = typeof __dirname === "string" ? __dirname : import_node_path61.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
59141
+ const here = typeof __dirname === "string" ? __dirname : import_node_path62.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
59183
59142
  const mcpConfigs = [];
59143
+ const mcpDaemonUrl = `http://127.0.0.1:${config.port}`;
59184
59144
  const dispatchServerCandidates = [
59185
- import_node_path61.default.join(here, "dispatch", "mcp-server.cjs"),
59145
+ import_node_path62.default.join(here, "dispatch", "mcp-server.cjs"),
59186
59146
  // 生产 dist/index → dist/dispatch/mcp-server.cjs
59187
- import_node_path61.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
59147
+ import_node_path62.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
59188
59148
  // dev tsx src/index → ../dist/dispatch/mcp-server.cjs
59189
59149
  ];
59190
- const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs46.default.existsSync(p2));
59150
+ const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
59191
59151
  if (dispatchServerScriptPath) {
59192
- const dispatchLogPath = import_node_path61.default.join(logDir, "dispatch-mcp-server.log");
59152
+ const dispatchLogPath = import_node_path62.default.join(logDir, "dispatch-mcp-server.log");
59193
59153
  const dispatchCfgPath = writeDispatchMcpConfig({
59194
59154
  dataDir: config.dataDir,
59195
59155
  serverScriptPath: dispatchServerScriptPath,
59156
+ daemonToken: resolvedAuthToken,
59157
+ daemonUrl: mcpDaemonUrl,
59196
59158
  logPath: dispatchLogPath
59197
59159
  });
59198
59160
  mcpConfigs.push({ id: DISPATCH_MCP_ID, path: dispatchCfgPath });
@@ -59207,17 +59169,19 @@ async function startDaemon(config) {
59207
59169
  });
59208
59170
  }
59209
59171
  const ticketServerCandidates = [
59210
- import_node_path61.default.join(here, "ticket", "mcp-server.cjs"),
59211
- import_node_path61.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
59172
+ import_node_path62.default.join(here, "ticket", "mcp-server.cjs"),
59173
+ import_node_path62.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
59212
59174
  ];
59213
- const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs46.default.existsSync(p2));
59175
+ const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
59214
59176
  if (ticketServerScriptPath) {
59215
- const ticketLogPath = import_node_path61.default.join(logDir, "ticket-mcp-server.log");
59177
+ const ticketLogPath = import_node_path62.default.join(logDir, "ticket-mcp-server.log");
59216
59178
  const ticketCfgPath = writeTicketMcpConfig({
59217
59179
  dataDir: config.dataDir,
59218
59180
  serverScriptPath: ticketServerScriptPath,
59219
59181
  apiBaseUrl: config.clawosApi ?? DEFAULT_CLAWOS_API,
59220
59182
  apiKey: resolveClawdTicketsApiKey(),
59183
+ daemonToken: resolvedAuthToken,
59184
+ daemonUrl: mcpDaemonUrl,
59221
59185
  logPath: ticketLogPath
59222
59186
  });
59223
59187
  mcpConfigs.push({ id: "ticket", path: ticketCfgPath });
@@ -59232,15 +59196,17 @@ async function startDaemon(config) {
59232
59196
  });
59233
59197
  }
59234
59198
  const shiftServerCandidates = [
59235
- import_node_path61.default.join(here, "shift", "mcp-server.cjs"),
59236
- import_node_path61.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
59199
+ import_node_path62.default.join(here, "shift", "mcp-server.cjs"),
59200
+ import_node_path62.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
59237
59201
  ];
59238
- const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs46.default.existsSync(p2));
59202
+ const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
59239
59203
  if (shiftServerScriptPath) {
59240
- const shiftLogPath = import_node_path61.default.join(logDir, "shift-mcp-server.log");
59204
+ const shiftLogPath = import_node_path62.default.join(logDir, "shift-mcp-server.log");
59241
59205
  const shiftCfgPath = await writeShiftMcpConfig({
59242
59206
  dataDir: config.dataDir,
59243
59207
  serverScriptPath: shiftServerScriptPath,
59208
+ daemonToken: resolvedAuthToken,
59209
+ daemonUrl: mcpDaemonUrl,
59244
59210
  logPath: shiftLogPath
59245
59211
  });
59246
59212
  mcpConfigs.push({ id: "shift", path: shiftCfgPath });
@@ -59256,15 +59222,17 @@ async function startDaemon(config) {
59256
59222
  );
59257
59223
  }
59258
59224
  const inboxServerCandidates = [
59259
- import_node_path61.default.join(here, "inbox", "mcp-server.cjs"),
59260
- import_node_path61.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
59225
+ import_node_path62.default.join(here, "inbox", "mcp-server.cjs"),
59226
+ import_node_path62.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
59261
59227
  ];
59262
- const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs46.default.existsSync(p2));
59228
+ const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
59263
59229
  if (inboxServerScriptPath) {
59264
- const inboxLogPath = import_node_path61.default.join(logDir, "inbox-mcp-server.log");
59230
+ const inboxLogPath = import_node_path62.default.join(logDir, "inbox-mcp-server.log");
59265
59231
  const inboxCfgPath = await writeInboxMcpConfig({
59266
59232
  dataDir: config.dataDir,
59267
59233
  serverScriptPath: inboxServerScriptPath,
59234
+ daemonToken: resolvedAuthToken,
59235
+ daemonUrl: mcpDaemonUrl,
59268
59236
  logPath: inboxLogPath
59269
59237
  });
59270
59238
  mcpConfigs.push({ id: "inbox", path: inboxCfgPath });
@@ -59280,14 +59248,16 @@ async function startDaemon(config) {
59280
59248
  );
59281
59249
  }
59282
59250
  const peerOpsServerCandidates = [
59283
- import_node_path61.default.join(here, "peer-ops", "mcp-server.cjs"),
59284
- import_node_path61.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
59251
+ import_node_path62.default.join(here, "peer-ops", "mcp-server.cjs"),
59252
+ import_node_path62.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
59285
59253
  ];
59286
- const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs46.default.existsSync(p2));
59254
+ const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
59287
59255
  if (peerOpsServerScriptPath) {
59288
59256
  const peerOpsCfgPath = writePeerOpsMcpConfig({
59289
59257
  dataDir: config.dataDir,
59290
- serverScriptPath: peerOpsServerScriptPath
59258
+ serverScriptPath: peerOpsServerScriptPath,
59259
+ daemonToken: resolvedAuthToken,
59260
+ daemonUrl: mcpDaemonUrl
59291
59261
  });
59292
59262
  mcpConfigs.push({ id: "peer-ops", path: peerOpsCfgPath });
59293
59263
  logger.info("peer-ops.mcp.json written", {
@@ -59300,8 +59270,34 @@ async function startDaemon(config) {
59300
59270
  { tried: peerOpsServerCandidates }
59301
59271
  );
59302
59272
  }
59273
+ const rpcToolServerCandidates = [
59274
+ import_node_path62.default.join(here, "rpc-tool", "mcp-server.cjs"),
59275
+ import_node_path62.default.join(here, "..", "dist", "rpc-tool", "mcp-server.cjs")
59276
+ ];
59277
+ const rpcToolServerScriptPath = rpcToolServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
59278
+ if (rpcToolServerScriptPath) {
59279
+ const rpcToolLogPath = import_node_path62.default.join(logDir, "rpc-tool-mcp-server.log");
59280
+ const rpcToolCfgPath = writeRpcToolMcpConfig({
59281
+ dataDir: config.dataDir,
59282
+ serverScriptPath: rpcToolServerScriptPath,
59283
+ daemonToken: resolvedAuthToken,
59284
+ daemonUrl: mcpDaemonUrl,
59285
+ logPath: rpcToolLogPath
59286
+ });
59287
+ mcpConfigs.push({ id: "rpc-tool", path: rpcToolCfgPath });
59288
+ logger.info("rpc-tool.mcp.json written", {
59289
+ path: rpcToolCfgPath,
59290
+ server: rpcToolServerScriptPath,
59291
+ serverLog: rpcToolLogPath
59292
+ });
59293
+ } else {
59294
+ logger.warn(
59295
+ "rpc-tool-mcp-server.cjs not found; clawd-rpc call tool disabled (dev tsx \u6A21\u5F0F\u4E0B\u9700\u5148\u8DD1\u8FC7 build)",
59296
+ { tried: rpcToolServerCandidates }
59297
+ );
59298
+ }
59303
59299
  const shiftStore = createShiftStore({
59304
- filePath: import_node_path61.default.join(config.dataDir, "shift.json"),
59300
+ filePath: import_node_path62.default.join(config.dataDir, "shift.json"),
59305
59301
  ownerIdProvider: () => ownerPrincipalId,
59306
59302
  now: () => Date.now()
59307
59303
  });
@@ -59323,7 +59319,7 @@ async function startDaemon(config) {
59323
59319
  getAdapter,
59324
59320
  historyReader: history,
59325
59321
  dataDir: config.dataDir,
59326
- personaRoot: import_node_path61.default.join(config.dataDir, "personas"),
59322
+ personaRoot: import_node_path62.default.join(config.dataDir, "personas"),
59327
59323
  // dispatch / shift 自动化 spawn 按 persona 绑定 tool 起 B session(codex persona 用 codex)。
59328
59324
  // personaRegistry 声明在 manager 之后(同作用域 const,调用时机在启动完成后,无 TDZ 风险)。
59329
59325
  getPersonaTool: (personaId) => personaRegistry.get(personaId)?.tool,
@@ -59338,7 +59334,7 @@ async function startDaemon(config) {
59338
59334
  contactStore,
59339
59335
  mode: config.mode,
59340
59336
  // 单栏 refactor (spec 2026-06-02 §5.1):cc 子进程 env CLAWD_DAEMON_URL 闭包来源。
59341
- // assistant curl 调本机 daemon HTTP RPC adapter(/api/rpc/<method>)触发 appBuilder.* 管理操作。
59337
+ // MCP server 用它拼 /rpc/<method>(Bearer CLAWD_DAEMON_TOKEN)调本机 daemon。
59342
59338
  // 127.0.0.1(不是 config.host)—— cc 跑在本机,loopback 最稳;外部访问限制 + http-router
59343
59339
  // 的 isLoopback 兜底已确保安全。
59344
59340
  getDaemonUrl: () => `http://127.0.0.1:${config.port}`,
@@ -59374,10 +59370,10 @@ async function startDaemon(config) {
59374
59370
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
59375
59371
  attachmentGroup: {
59376
59372
  onFileEdit: (input) => {
59377
- const absPath = import_node_path61.default.isAbsolute(input.relPath) ? input.relPath : import_node_path61.default.join(input.cwd, input.relPath);
59373
+ const absPath = import_node_path62.default.isAbsolute(input.relPath) ? input.relPath : import_node_path62.default.join(input.cwd, input.relPath);
59378
59374
  let size = 0;
59379
59375
  try {
59380
- size = import_node_fs46.default.statSync(absPath).size;
59376
+ size = import_node_fs47.default.statSync(absPath).size;
59381
59377
  } catch (err) {
59382
59378
  logger.warn("attachment.onFileEdit stat failed", {
59383
59379
  sessionId: input.sessionId,
@@ -59545,7 +59541,7 @@ async function startDaemon(config) {
59545
59541
  });
59546
59542
  const visitorStore = createVisitorStore({ dir: config.dataDir });
59547
59543
  const exchangeVisitorToken = buildVisitorLogin({
59548
- getSecret: () => authFile.visitorTokenSecret,
59544
+ getSecret: () => authFile.token,
59549
59545
  fetchUserInfo: (ttcToken) => fetchTtcUserInfo({ apiBase: TTC_HOSTS.api, token: ttcToken }),
59550
59546
  visitorStore,
59551
59547
  now: () => Date.now(),
@@ -59601,18 +59597,17 @@ async function startDaemon(config) {
59601
59597
  groupFileStore,
59602
59598
  sessionStore: { read: (sid) => manager.findOwnedSession(sid) },
59603
59599
  getHttpBaseUrl,
59604
- // HMAC sign secret:~/.clawd/auth.json signSecret 字段(与 WS Bearer token 独立)。
59605
- // --auth-token CLI 模式 / noAuth 模式 authFile 为 null → handler 自己返 NOT_IMPLEMENTED。
59606
- getSignSecret: () => authFile?.signSecret ?? "",
59600
+ // HMAC sign secret = auth.json 唯一 token(authFile 恒非空,startDaemon 开头加载)。
59601
+ getSignSecret: () => authFile.token,
59607
59602
  // group RPC + sign 都用:根据 sessionId 反查 scope。owner-mode persona session 走
59608
59603
  // 'persona/<pid>/owner',default 走 'default'。
59609
59604
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
59610
59605
  // guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
59611
- personaRoot: import_node_path61.default.join(config.dataDir, "personas"),
59606
+ personaRoot: import_node_path62.default.join(config.dataDir, "personas"),
59612
59607
  usersRoot
59613
59608
  },
59614
59609
  // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
59615
- personaRoot: import_node_path61.default.join(config.dataDir, "personas"),
59610
+ personaRoot: import_node_path62.default.join(config.dataDir, "personas"),
59616
59611
  // v2 多人 persona 隔离:handler 派生 guest user-dir 放行
59617
59612
  usersRoot,
59618
59613
  // v2 Phase 6: whoami handler 装在 owner principal.displayName + persona 解析
@@ -59741,11 +59736,11 @@ async function startDaemon(config) {
59741
59736
  // 发布上线脚手架化 (spec 2026-06-03 §5.2):
59742
59737
  // appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
59743
59738
  // dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
59744
- appBuilderPersonaRoot: import_node_path61.default.join(config.dataDir, "personas", "persona-app-builder"),
59739
+ appBuilderPersonaRoot: import_node_path62.default.join(config.dataDir, "personas", "persona-app-builder"),
59745
59740
  // 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
59746
- deployKitRoot: import_node_path61.default.join(config.dataDir, "deploy-kit"),
59741
+ deployKitRoot: import_node_path62.default.join(config.dataDir, "deploy-kit"),
59747
59742
  // scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
59748
- resolvePersonaRoot: (personaId) => import_node_path61.default.join(config.dataDir, "personas", personaId),
59743
+ resolvePersonaRoot: (personaId) => import_node_path62.default.join(config.dataDir, "personas", personaId),
59749
59744
  // 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
59750
59745
  // 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
59751
59746
  // 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
@@ -59791,7 +59786,7 @@ async function startDaemon(config) {
59791
59786
  }
59792
59787
  let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
59793
59788
  if (sourceFile && sourceFile.toolSessionId) {
59794
- sourceJsonlPath = import_node_path61.default.join(
59789
+ sourceJsonlPath = import_node_path62.default.join(
59795
59790
  import_node_os22.default.homedir(),
59796
59791
  ".claude",
59797
59792
  "projects",
@@ -59861,17 +59856,6 @@ async function startDaemon(config) {
59861
59856
  const authResolver = new AuthContextResolver({
59862
59857
  ownerToken: resolvedAuthToken
59863
59858
  });
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
59859
  const authedRpc = {
59876
59860
  authenticate: (token) => authenticate(token, buildConnectAuthDeps()),
59877
59861
  dispatch: (method, body, ctx) => {
@@ -59896,17 +59880,15 @@ async function startDaemon(config) {
59896
59880
  daemonVersion: version,
59897
59881
  logger,
59898
59882
  sessionStore: store,
59899
- rpcDispatcher,
59900
59883
  authedRpc,
59901
59884
  ...viewerAssetLoader ? { viewerAssetLoader } : {},
59902
59885
  // persona web 分享:访客换 token(POST /share/exchange)+ guest SPA serve(GET /s)
59903
59886
  visitorExchange: exchangeVisitorToken,
59904
59887
  ...shareUiAssetLoader ? { shareUiAssetLoader } : {},
59905
- // /files HMAC verify 用 auth.json signSecret 字段(与 attachment.signUrl 同源)。
59906
- // --auth-token CLI 模式没 signSecret → 路由返 501,sign URL 功能整体禁用。
59907
- getSignSecret: () => authFile?.signSecret ?? null,
59888
+ // /files HMAC verify 用 auth.json token(与 attachment.signUrl 同源)。
59889
+ getSignSecret: () => authFile.token,
59908
59890
  // chatinput 附件上传(POST /attachments/upload,spec §6.1)。upload pipeline
59909
- // 在 session.cwd 下的 .attachments/ 落盘;signSecret 与 /files HMAC 同源;
59891
+ // 在 session.cwd 下的 .attachments/ 落盘;签名 secret 与 /files HMAC 同源;
59910
59892
  // tunnel 未启时返 503 TUNNEL_NOT_READY 让前端显式提示。
59911
59893
  attachmentUpload: {
59912
59894
  groupFileStore,
@@ -59987,7 +59969,7 @@ async function startDaemon(config) {
59987
59969
  // (非 persona-web 场景静默 return null;tunnel 模式走 AuthGate 不经此路)。参数名
59988
59970
  // tryVerifyCapabilityToken 不再准确,跨 local-ws-server 改名属 followup 清理。
59989
59971
  tryVerifyCapabilityToken: (token) => {
59990
- const vis = verifyVisitorToken(authFile.visitorTokenSecret, token, Math.floor(Date.now() / 1e3));
59972
+ const vis = verifyVisitorToken(authFile.token, token, Math.floor(Date.now() / 1e3));
59991
59973
  if (vis.ok) return visitorGuestContext(vis.payload);
59992
59974
  return null;
59993
59975
  },
@@ -60106,8 +60088,8 @@ async function startDaemon(config) {
60106
60088
  const lines = [
60107
60089
  `Tunnel: ${r.url}`,
60108
60090
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
60109
- `Frpc config: ${import_node_path61.default.join(config.dataDir, "frpc.toml")}`,
60110
- `Frpc log: ${import_node_path61.default.join(logDir, "frpc.log")}`
60091
+ `Frpc config: ${import_node_path62.default.join(config.dataDir, "frpc.toml")}`,
60092
+ `Frpc log: ${import_node_path62.default.join(logDir, "frpc.log")}`
60111
60093
  ];
60112
60094
  const width = Math.max(...lines.map((l) => l.length));
60113
60095
  const bar = "\u2550".repeat(width + 4);
@@ -60120,8 +60102,8 @@ ${bar}
60120
60102
 
60121
60103
  `);
60122
60104
  try {
60123
- const connectPath = import_node_path61.default.join(config.dataDir, "connect.txt");
60124
- import_node_fs46.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
60105
+ const connectPath = import_node_path62.default.join(config.dataDir, "connect.txt");
60106
+ import_node_fs47.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
60125
60107
  } catch {
60126
60108
  }
60127
60109
  } catch (err) {
@@ -60193,9 +60175,9 @@ ${bar}
60193
60175
  };
60194
60176
  }
60195
60177
  function migrateDropPersonsDir(dataDir) {
60196
- const dir = import_node_path61.default.join(dataDir, "persons");
60178
+ const dir = import_node_path62.default.join(dataDir, "persons");
60197
60179
  try {
60198
- import_node_fs46.default.rmSync(dir, { recursive: true, force: true });
60180
+ import_node_fs47.default.rmSync(dir, { recursive: true, force: true });
60199
60181
  } catch {
60200
60182
  }
60201
60183
  }