@clawos-dev/clawd 0.2.257 → 0.2.258

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