@clawos-dev/clawd 0.2.256 → 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();
@@ -40774,6 +40804,7 @@ init_protocol();
40774
40804
  var DEFAULT_PORT = 18790;
40775
40805
  var DEFAULT_HOST = "127.0.0.1";
40776
40806
  var DEFAULT_CLAWOS_API = "https://api.clawos.chat";
40807
+ var DEFAULT_OWNER_IDLE_KILL_DELAY_MS = 6 * 60 * 60 * 1e3;
40777
40808
  var DEFAULT_LOG_ENDPOINT = "https://clawd-prod.cn-hangzhou.log.aliyuncs.com/logstores/app-logs/track";
40778
40809
  function resolveLogShipping(raw, cliNoShipping) {
40779
40810
  if (cliNoShipping) {
@@ -40788,6 +40819,12 @@ function resolveLogShipping(raw, cliNoShipping) {
40788
40819
  const sampling = mode === "errors-only" ? { debug: 0, info: 0, warn: 1, error: 1 } : isPlainSamplingMap(raw?.sampling) ? raw.sampling : {};
40789
40820
  return { mode, endpoint, sampling };
40790
40821
  }
40822
+ function resolveIdleKillMs(raw) {
40823
+ if (raw === void 0 || raw === null || raw === "") return null;
40824
+ const n = typeof raw === "number" ? raw : Number(raw);
40825
+ if (!Number.isFinite(n) || n < 0) return null;
40826
+ return n;
40827
+ }
40791
40828
  function isPlainSamplingMap(v2) {
40792
40829
  if (!v2 || typeof v2 !== "object") return false;
40793
40830
  return Object.values(v2).every((n) => typeof n === "number");
@@ -40885,6 +40922,7 @@ function resolveConfig(opts) {
40885
40922
  const mode = DAEMON_MODE_VALUES.includes(rawMode ?? "") ? rawMode : "sdk";
40886
40923
  const filePreviewPorts = Array.isArray(fileCfg.previewPorts) ? fileCfg.previewPorts.map((n) => Number(n)).filter((n) => Number.isInteger(n) && n > 0) : null;
40887
40924
  const previewPorts = env.CLAWD_PREVIEW_PORTS ? env.CLAWD_PREVIEW_PORTS.split(",").map((s) => Number(s.trim())).filter((n) => Number.isInteger(n) && n > 0) : filePreviewPorts && filePreviewPorts.length > 0 ? filePreviewPorts : null;
40925
+ const ownerIdleKillDelayMs = resolveIdleKillMs(env.CLAWD_OWNER_IDLE_KILL_DELAY_MS) ?? resolveIdleKillMs(fileCfg.ownerIdleKillDelayMs) ?? DEFAULT_OWNER_IDLE_KILL_DELAY_MS;
40888
40926
  const logShipping = resolveLogShipping(
40889
40927
  fileCfg.logShipping,
40890
40928
  args.noLogShipping ?? false
@@ -40902,7 +40940,8 @@ function resolveConfig(opts) {
40902
40940
  frpcBinary,
40903
40941
  mode,
40904
40942
  previewPorts,
40905
- logShipping
40943
+ logShipping,
40944
+ ownerIdleKillDelayMs
40906
40945
  };
40907
40946
  }
40908
40947
  var HELP_TEXT = `clawd [options]
@@ -40929,11 +40968,15 @@ Env (advanced):
40929
40968
  CLAWD_CC_MODE CC \u5B50\u8FDB\u7A0B\u4EA4\u4E92\u6A21\u5F0F\uFF1A'sdk'\uFF08\u9ED8\u8BA4\uFF0C--print stream-json\uFF09/ 'tui'\uFF08pty \u4EA4\u4E92\u5F0F\uFF09\uFF1B
40930
40969
  \u503C\u975E\u5408\u6CD5\u5B57\u9762\u91CF\u65F6\u9759\u9ED8\u56DE\u9000 'sdk'\u3002ready \u5E27 mode \u5B57\u6BB5\u900F\u4F20\u7ED9 UI\uFF1B
40931
40970
  'tui' \u6A21\u5F0F\u989D\u5916\u63A8 session:control + session:pty \u4E24\u4E2A\u5E27\uFF08\u4E1A\u52A1\u5E27\u540D\u4E0D\u53D8\uFF09
40971
+ CLAWD_OWNER_IDLE_KILL_DELAY_MS
40972
+ owner persona session \u95F2\u7F6E\u56DE\u6536\u515C\u5E95\u4E0A\u9650\uFF08ms\uFF0C\u9ED8\u8BA4 21600000 = 6h\uFF09\uFF1B
40973
+ \u8BBE 0 \u5173\u95ED\u56DE\u6536\u3002\u4E5F\u53EF\u5199 config.json \u7684 ownerIdleKillDelayMs\u3002
40974
+ guest session \u7684 10min \u662F\u72EC\u7ACB\u5EF6\u8FDF\uFF0C\u4E0D\u53D7\u6B64\u9879\u5F71\u54CD
40932
40975
  `;
40933
40976
 
40934
40977
  // src/index.ts
40935
- var import_node_path61 = __toESM(require("path"), 1);
40936
- 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);
40937
40980
  var import_node_os22 = __toESM(require("os"), 1);
40938
40981
 
40939
40982
  // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
@@ -41412,12 +41455,14 @@ function scopeSubPath(scope) {
41412
41455
  if (scope.mode === "guest") return [scope.personaId, "guests", scope.capId];
41413
41456
  return [scope.personaId, scope.mode];
41414
41457
  }
41415
- function metaFromScope(scope, personaRoot, usersRoot, ownerPrincipalId, callerDisplayName, callerFeishuUnionId) {
41458
+ var GUEST_IDLE_KILL_DELAY_MS = 6e5;
41459
+ function metaFromScope(scope, personaRoot, usersRoot, ownerPrincipalId, callerDisplayName, callerFeishuUnionId, opts) {
41416
41460
  if (scope.kind === "default") return void 0;
41461
+ const ownerDelay = opts?.ownerIdleKillDelayMs !== void 0 && opts.ownerIdleKillDelayMs > 0 ? opts.ownerIdleKillDelayMs : void 0;
41417
41462
  if (scope.mode === "owner") {
41418
41463
  if (!ownerPrincipalId || !usersRoot) {
41419
41464
  return {
41420
- idleKillEnabled: false,
41465
+ idleKillDelayMs: ownerDelay,
41421
41466
  personaMode: "owner",
41422
41467
  callerDisplayName,
41423
41468
  callerDeviceId: ownerPrincipalId,
@@ -41426,7 +41471,7 @@ function metaFromScope(scope, personaRoot, usersRoot, ownerPrincipalId, callerDi
41426
41471
  }
41427
41472
  const userWorkDir2 = deriveUserWorkDir(ownerPrincipalId, usersRoot);
41428
41473
  return {
41429
- idleKillEnabled: false,
41474
+ idleKillDelayMs: ownerDelay,
41430
41475
  personaMode: "owner",
41431
41476
  userWorkDir: userWorkDir2,
41432
41477
  addDirs: [userWorkDir2],
@@ -41437,7 +41482,7 @@ function metaFromScope(scope, personaRoot, usersRoot, ownerPrincipalId, callerDi
41437
41482
  }
41438
41483
  const userWorkDir = deriveUserWorkDir(scope.capId, usersRoot);
41439
41484
  return {
41440
- idleKillEnabled: true,
41485
+ idleKillDelayMs: GUEST_IDLE_KILL_DELAY_MS,
41441
41486
  personaMode: "guest",
41442
41487
  userWorkDir,
41443
41488
  addDirs: [userWorkDir],
@@ -41918,24 +41963,16 @@ function buildConnectionPrompt(args) {
41918
41963
  // src/persona/sharing-prompt.ts
41919
41964
  var ATTACHMENT_SHARING_HINT = `## \u628A\u4EA7\u51FA\u6587\u4EF6\u5206\u4EAB\u7ED9\u5BF9\u65B9\uFF08attachment\uFF09
41920
41965
 
41921
- \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
41922
41967
 
41923
41968
  1. \u5165\u7FA4\uFF08\u767B\u8BB0\u5230\u672C session \u7684\u5171\u4EAB\u6E05\u5355\uFF09\uFF1A
41924
- \`\`\`bash
41925
- curl -sS -X POST "$CLAWD_DAEMON_URL/api/rpc/attachment.groupAdd" \\
41926
- -H "X-Clawd-Session-Id: $CLAWD_SESSION_ID" -H 'Content-Type: application/json' \\
41927
- -d '{"relPath":"<\u6587\u4EF6\u7684\u7EDD\u5BF9\u8DEF\u5F84>"}'
41928
- \`\`\`
41969
+ call({ method: "attachment.groupAdd", args: { relPath: "<\u6587\u4EF6\u7684\u7EDD\u5BF9\u8DEF\u5F84>", origin: "agent" } })
41929
41970
  2. \u7B7E\u540D\u62FF URL\uFF1A
41930
- \`\`\`bash
41931
- curl -sS -X POST "$CLAWD_DAEMON_URL/api/rpc/attachment.signUrl" \\
41932
- -H "X-Clawd-Session-Id: $CLAWD_SESSION_ID" -H 'Content-Type: application/json' \\
41933
- -d '{"relPath":"<\u540C\u4E00\u4E2A\u7EDD\u5BF9\u8DEF\u5F84>"}'
41934
- \`\`\`
41971
+ call({ method: "attachment.signUrl", args: { relPath: "<\u540C\u4E00\u4E2A\u7EDD\u5BF9\u8DEF\u5F84>" } })
41935
41972
  \u8FD4\u56DE\u7684 \`url\` \u5C31\u662F\u5BF9\u65B9\u53EF\u76F4\u63A5\u6253\u5F00/\u4E0B\u8F7D\u7684\u94FE\u63A5\u3002
41936
41973
 
41937
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
41938
- - \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
41939
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`;
41940
41977
 
41941
41978
  // src/persona/no-ask-user-question-prompt.ts
@@ -42021,7 +42058,10 @@ function cloneState(s) {
42021
42058
  currentTurnSender: s.currentTurnSender
42022
42059
  };
42023
42060
  }
42024
- var IDLE_KILL_DELAY_MS = 6e5;
42061
+ function idleKillDelayOf(state) {
42062
+ const ms = state.subSessionMeta?.idleKillDelayMs;
42063
+ return ms !== void 0 && ms > 0 ? ms : void 0;
42064
+ }
42025
42065
  function tryFlushPending(state, deps) {
42026
42066
  if (!state.readyForSend || state.pendingSend.length === 0) return [];
42027
42067
  const text = state.pendingSend.shift();
@@ -42296,11 +42336,12 @@ function pushEventToBuffer(state, event, deps) {
42296
42336
  status: "running-idle"
42297
42337
  }
42298
42338
  });
42299
- if (next.subSessionMeta?.idleKillEnabled) {
42339
+ const idleKillMs = idleKillDelayOf(next);
42340
+ if (idleKillMs !== void 0) {
42300
42341
  effects.push({
42301
42342
  kind: "schedule-idle-kill",
42302
42343
  sessionId: next.file.sessionId,
42303
- ms: IDLE_KILL_DELAY_MS
42344
+ ms: idleKillMs
42304
42345
  });
42305
42346
  }
42306
42347
  }
@@ -42737,14 +42778,15 @@ function reduceSessionInner(state, input, deps) {
42737
42778
  if (state.status !== "running-idle") {
42738
42779
  return { state, effects: [] };
42739
42780
  }
42740
- if (input.subagentActive) {
42781
+ const rescheduleMs = idleKillDelayOf(state);
42782
+ if (input.subagentActive && rescheduleMs !== void 0) {
42741
42783
  return {
42742
42784
  state,
42743
42785
  effects: [
42744
42786
  {
42745
42787
  kind: "schedule-idle-kill",
42746
42788
  sessionId: state.file.sessionId,
42747
- ms: IDLE_KILL_DELAY_MS
42789
+ ms: rescheduleMs
42748
42790
  }
42749
42791
  ]
42750
42792
  };
@@ -42930,7 +42972,7 @@ var SessionRunner = class {
42930
42972
  ownerFeishuUnionId: this.hooks.ownerFeishuUnionId,
42931
42973
  personaRoot: this.hooks.personaRoot,
42932
42974
  // 单栏 refactor (spec 2026-06-02 §5.1): cc 子进程 env 注入 CLAWD_DAEMON_URL,让 assistant
42933
- // curl daemon HTTP RPC adapter (/api/rpc/<method>) 触发管理操作。null = HTTP adapter 未启。
42975
+ // MCP server /rpc/<method>(Bearer)调 daemon。null = HTTP RPC 未启。
42934
42976
  getDaemonUrl: this.hooks.getDaemonUrl,
42935
42977
  // clawd 管家 (spec 2026-07-22-clawd-butler-persona §3.1): cc 子进程 env 注入 CLAWOS_API,
42936
42978
  // 让 clawd 管家 skill fetch `${CLAWOS_API}/docs/**` 线上文档。
@@ -43664,8 +43706,8 @@ var SessionManager = class {
43664
43706
  // 经过 compressFrameForWire 后决定是 push collector 还是走 deps.broadcastFrame。
43665
43707
  // scope 决定两件事:SessionStore 路由 + SubSessionMeta 派生(metaFromScope)。
43666
43708
  // - default → 复用 deps.store,无 subSessionMeta(普通 session)
43667
- // - persona/owner → personas/<pid>/.clawd/sessions/owner/,meta={idleKillEnabled:false, personaMode:'owner'}
43668
- // - persona/guest → personas/<pid>/.clawd/sessions/guests/<capId>/,meta={idleKillEnabled:true, personaMode:'guest', extraSettings:<sandbox>}
43709
+ // - persona/owner → personas/<pid>/.clawd/sessions/owner/,meta={idleKillDelayMs:<config 默认 6h>, personaMode:'owner'}
43710
+ // - persona/guest → personas/<pid>/.clawd/sessions/guests/<capId>/,meta={idleKillDelayMs:10min, personaMode:'guest', extraSettings:<sandbox>}
43669
43711
  newRunner(file, scope) {
43670
43712
  const adapter = this.deps.getAdapter(file.tool ?? "claude");
43671
43713
  const store = this.storeFor(scope);
@@ -43677,7 +43719,8 @@ var SessionManager = class {
43677
43719
  this.deps.usersRoot ?? "",
43678
43720
  this.deps.ownerPrincipalId,
43679
43721
  callerDisplayName,
43680
- callerFeishuUnionId
43722
+ callerFeishuUnionId,
43723
+ { ownerIdleKillDelayMs: this.deps.ownerIdleKillDelayMs }
43681
43724
  );
43682
43725
  if (subSessionMeta?.userWorkDir) {
43683
43726
  import_node_fs9.default.mkdirSync(subSessionMeta.userWorkDir, { recursive: true });
@@ -44515,8 +44558,8 @@ var SessionManager = class {
44515
44558
  //
44516
44559
  // 区别于 owner / default 单 sessionId 入口,这些 API 接 SessionScope 参数 ——
44517
44560
  // 调用方(PersonaManager / observer 等)明确传 scope,由 storeFor 路由到正确目录:
44518
- // - {kind:'persona', personaId, mode:'guest', capId} → guests/<capId>/,meta={idleKill:true, sandbox}
44519
- // - {kind:'persona', personaId, mode:'owner'} → owner/,meta={idleKill:false}
44561
+ // - {kind:'persona', personaId, mode:'guest', capId} → guests/<capId>/,meta={idleKill:10min, sandbox}
44562
+ // - {kind:'persona', personaId, mode:'owner'} → owner/,meta={idleKill:<config 默认 6h>}
44520
44563
  //
44521
44564
  // SubSessionMeta 由 scope → metaFromScope 派生,不进 SessionFile schema。
44522
44565
  /** 按 scope 读取 SessionFile;不存在返回 null */
@@ -46859,8 +46902,8 @@ function turnStartInput(text) {
46859
46902
  const items = [];
46860
46903
  let leftover = text;
46861
46904
  for (const m2 of text.matchAll(SKILL_RE)) {
46862
- const [marker, name, path74] = m2;
46863
- items.push({ type: "skill", name, path: path74 });
46905
+ const [marker, name, path75] = m2;
46906
+ items.push({ type: "skill", name, path: path75 });
46864
46907
  leftover = leftover.replace(marker, "");
46865
46908
  }
46866
46909
  for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
@@ -48449,13 +48492,6 @@ function jsonlExistsForCtx(ctx) {
48449
48492
  init_claude_history();
48450
48493
 
48451
48494
  // src/dispatch/manager.ts
48452
- function defer() {
48453
- let resolve6;
48454
- const promise = new Promise((r) => {
48455
- resolve6 = r;
48456
- });
48457
- return { promise, resolve: resolve6 };
48458
- }
48459
48495
  var PersonaDispatchManager = class {
48460
48496
  constructor(deps) {
48461
48497
  this.deps = deps;
@@ -48467,8 +48503,7 @@ var PersonaDispatchManager = class {
48467
48503
  this.map.set(dispatchId, {
48468
48504
  dispatchId,
48469
48505
  sourceSessionId: args.sourceSessionId,
48470
- targetPersona: args.targetPersona,
48471
- waiters: []
48506
+ targetPersona: args.targetPersona
48472
48507
  });
48473
48508
  this.deps.store.add({
48474
48509
  dispatchId,
@@ -48490,45 +48525,21 @@ var PersonaDispatchManager = class {
48490
48525
  outcome,
48491
48526
  completedAt: new Date(this.deps.now()).toISOString()
48492
48527
  });
48493
- if (state) {
48494
- const waiters = state.waiters;
48495
- state.waiters = [];
48496
- for (const w2 of waiters) w2.resolve(outcome);
48497
- this.map.delete(dispatchId);
48498
- }
48528
+ if (state) this.map.delete(dispatchId);
48499
48529
  const sourceSessionId = updated?.sourceSessionId ?? state?.sourceSessionId;
48500
48530
  if (sourceSessionId) this.deps.onCompleted?.(sourceSessionId);
48501
48531
  }
48502
- /**
48503
- * mesh B 角色 hold 专用(跨设备 dispatch 的 peer 侧):guest B-role handler 用它
48504
- * 挂住 /rpc HTTP 响应直到 complete,把 outcome 回传给 A 侧 daemon 的 forwardToPeer
48505
- * (A 侧后台 await,A 的 tool call 不受影响照样秒回)。
48506
- * 本地 dispatch 链路**不用**这个——本地 outcome 只走台账 + deliverPending。
48507
- */
48508
- async waitForOutcome(dispatchId) {
48509
- const state = this.map.get(dispatchId);
48510
- if (!state) {
48511
- const rec3 = this.deps.store.get(dispatchId);
48512
- if (rec3?.outcome) return rec3.outcome;
48513
- throw new Error(`unknown dispatchId: ${dispatchId}`);
48514
- }
48515
- const d = defer();
48516
- state.waiters.push(d);
48517
- return d.promise;
48518
- }
48519
48532
  /**
48520
48533
  * 续派(spec 2026-07-21):complete 已清掉 map 条目,续派前重建——bSessionId 映射
48521
48534
  * 保证 worker 进程死后 respawn 时 reducer lookupDispatchByBSessionId 仍注回同一
48522
48535
  * CLAWD_DISPATCH_ID env。台账行的复位由 handler 直接走 store(本方法只管内存态)。
48523
48536
  */
48524
48537
  reopen(dispatchId, args) {
48525
- const existing = this.map.get(dispatchId);
48526
48538
  this.map.set(dispatchId, {
48527
48539
  dispatchId,
48528
48540
  sourceSessionId: args.sourceSessionId,
48529
48541
  targetPersona: args.targetPersona,
48530
- bSessionId: args.workerSessionId,
48531
- waiters: existing?.waiters ?? []
48542
+ bSessionId: args.workerSessionId
48532
48543
  });
48533
48544
  }
48534
48545
  /** SessionManager 在 createDispatchedSession 时调,建立 dispatchId → B sessionId 映射 */
@@ -48657,13 +48668,17 @@ function dispatchMcpConfigPath(dataDir) {
48657
48668
  function writeDispatchMcpConfig(args) {
48658
48669
  const cfgPath = dispatchMcpConfigPath(args.dataDir);
48659
48670
  const content = {
48660
- _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",
48661
48672
  mcpServers: {
48662
48673
  "clawd-dispatch": {
48663
48674
  type: "stdio",
48664
48675
  command: "node",
48665
48676
  args: [args.serverScriptPath],
48666
- ...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
+ }
48667
48682
  }
48668
48683
  }
48669
48684
  };
@@ -48681,7 +48696,7 @@ function ticketMcpConfigPath(dataDir) {
48681
48696
  function writeTicketMcpConfig(args) {
48682
48697
  const cfgPath = ticketMcpConfigPath(args.dataDir);
48683
48698
  const content = {
48684
- _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",
48685
48700
  mcpServers: {
48686
48701
  "clawd-ticket": {
48687
48702
  type: "stdio",
@@ -48691,6 +48706,8 @@ function writeTicketMcpConfig(args) {
48691
48706
  CLAWD_TICKET_API_URL: args.apiBaseUrl,
48692
48707
  CLAWD_TICKET_API_KEY: args.apiKey,
48693
48708
  CLAWD_TICKET_SCENE: "persona",
48709
+ CLAWD_DAEMON_TOKEN: args.daemonToken,
48710
+ CLAWD_DAEMON_URL: args.daemonUrl,
48694
48711
  ...args.logPath ? { CLAWD_TICKET_LOG: args.logPath } : {}
48695
48712
  }
48696
48713
  }
@@ -48716,7 +48733,11 @@ async function writeShiftMcpConfig(args) {
48716
48733
  type: "stdio",
48717
48734
  command: "node",
48718
48735
  args: [args.serverScriptPath],
48719
- ...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
+ }
48720
48741
  }
48721
48742
  }
48722
48743
  };
@@ -48740,7 +48761,11 @@ async function writeInboxMcpConfig(args) {
48740
48761
  type: "stdio",
48741
48762
  command: "node",
48742
48763
  args: [args.serverScriptPath],
48743
- ...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
+ }
48744
48769
  }
48745
48770
  }
48746
48771
  };
@@ -48758,11 +48783,15 @@ function peerOpsMcpConfigPath(dataDir) {
48758
48783
  function writePeerOpsMcpConfig(args) {
48759
48784
  const cfgPath = peerOpsMcpConfigPath(args.dataDir);
48760
48785
  const cfg = {
48761
- _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",
48762
48787
  mcpServers: {
48763
48788
  "peer-ops": {
48764
48789
  command: "node",
48765
- args: [args.serverScriptPath]
48790
+ args: [args.serverScriptPath],
48791
+ env: {
48792
+ CLAWD_DAEMON_TOKEN: args.daemonToken,
48793
+ CLAWD_DAEMON_URL: args.daemonUrl
48794
+ }
48766
48795
  }
48767
48796
  }
48768
48797
  };
@@ -48771,9 +48800,37 @@ function writePeerOpsMcpConfig(args) {
48771
48800
  return cfgPath;
48772
48801
  }
48773
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
+
48774
48831
  // src/shift/store.ts
48775
48832
  var import_promises2 = __toESM(require("fs/promises"), 1);
48776
- var import_node_path24 = __toESM(require("path"), 1);
48833
+ var import_node_path25 = __toESM(require("path"), 1);
48777
48834
  var import_node_crypto6 = require("crypto");
48778
48835
 
48779
48836
  // src/shift/schedule.ts
@@ -48848,7 +48905,7 @@ function createShiftStore(deps) {
48848
48905
  flushTimer = null;
48849
48906
  }
48850
48907
  const content = { version: 1, shifts };
48851
- 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 });
48852
48909
  const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
48853
48910
  await import_promises2.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
48854
48911
  await import_promises2.default.rename(tmp, deps.filePath);
@@ -49301,7 +49358,7 @@ function buildPersonaDispatchHandlers(deps) {
49301
49358
  }
49302
49359
  if (!sourceSessionId) {
49303
49360
  throw new Error(
49304
- "personaDispatch:run requires sessionId (caller must pass x-clawd-session-id header)"
49361
+ "personaDispatch:run requires sessionId in the request body"
49305
49362
  );
49306
49363
  }
49307
49364
  const rec3 = deps.store.get(args.dispatchId);
@@ -49374,7 +49431,7 @@ function buildPersonaDispatchHandlers(deps) {
49374
49431
  }
49375
49432
  if (!sourceSessionId) {
49376
49433
  throw new Error(
49377
- "personaDispatch:run requires sessionId (caller must pass x-clawd-session-id header)"
49434
+ "personaDispatch:run requires sessionId in the request body"
49378
49435
  );
49379
49436
  }
49380
49437
  const { dispatchId: dispatchId2 } = mgr.start({
@@ -49441,14 +49498,13 @@ function buildPersonaDispatchHandlers(deps) {
49441
49498
  reason: `failed to spawn B: ${reason}`
49442
49499
  });
49443
49500
  });
49444
- const outcome = await mgr.waitForOutcome(dispatchId2);
49445
49501
  return {
49446
- response: { type: "personaDispatch:run:ok", outcome }
49502
+ response: { type: "personaDispatch:run:ok", dispatchId: dispatchId2 }
49447
49503
  };
49448
49504
  }
49449
49505
  if (!sourceSessionId) {
49450
49506
  throw new Error(
49451
- "personaDispatch:run requires sessionId (caller must pass x-clawd-session-id header)"
49507
+ "personaDispatch:run requires sessionId in the request body"
49452
49508
  );
49453
49509
  }
49454
49510
  const { dispatchId } = mgr.start({
@@ -49496,6 +49552,22 @@ function buildPersonaDispatchHandlers(deps) {
49496
49552
  response: { type: "personaDispatch:complete:ok" }
49497
49553
  };
49498
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
+ };
49499
49571
  const list = async (frame) => {
49500
49572
  const { type: _t, requestId: _r, ...rest } = frame;
49501
49573
  const args = DispatchListArgsSchema.parse(rest);
@@ -49509,7 +49581,8 @@ function buildPersonaDispatchHandlers(deps) {
49509
49581
  return {
49510
49582
  "personaDispatch:run": run,
49511
49583
  "personaDispatch:complete": complete,
49512
- "personaDispatch:list": list
49584
+ "personaDispatch:list": list,
49585
+ "personaDispatch:get": get
49513
49586
  };
49514
49587
  }
49515
49588
 
@@ -49520,8 +49593,13 @@ function wsUrlToHttp(url) {
49520
49593
  if (url.startsWith("ws://")) return "http://" + url.slice("ws://".length);
49521
49594
  return url;
49522
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));
49523
49600
  async function forwardDispatchToPeer(args) {
49524
49601
  const f = args.fetchImpl ?? fetch;
49602
+ const sleep2 = args.sleepImpl ?? defaultSleep;
49525
49603
  const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
49526
49604
  const url = `${base}/rpc/personaDispatch:run`;
49527
49605
  let res;
@@ -49556,7 +49634,58 @@ async function forwardDispatchToPeer(args) {
49556
49634
  if (json.ok === false) {
49557
49635
  return { kind: "failure", reason: `peer rejected: ${json.error}: ${json.message}` };
49558
49636
  }
49559
- 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)" };
49560
49689
  }
49561
49690
  async function forwardInboxPostToPeer(args) {
49562
49691
  const f = args.fetchImpl ?? fetch;
@@ -49800,7 +49929,7 @@ var CodexHistoryReader = class {
49800
49929
 
49801
49930
  // src/tools/codex-skills.ts
49802
49931
  var import_node_child_process7 = require("child_process");
49803
- var import_node_path25 = __toESM(require("path"), 1);
49932
+ var import_node_path26 = __toESM(require("path"), 1);
49804
49933
  var DEFAULT_TIMEOUT_MS3 = 8e3;
49805
49934
  function str3(v2) {
49806
49935
  return typeof v2 === "string" ? v2 : void 0;
@@ -49816,13 +49945,13 @@ function mapSkillsListResponse(res) {
49816
49945
  const r = s ?? {};
49817
49946
  const name = str3(r.name);
49818
49947
  if (!name) continue;
49819
- const path74 = str3(r.path);
49948
+ const path75 = str3(r.path);
49820
49949
  const description = str3(r.description);
49821
49950
  const isPlugin = name.includes(":");
49822
49951
  out.push({
49823
49952
  name,
49824
49953
  source: isPlugin ? "plugin" : "project",
49825
- ...path74 ? { path: path74 } : {},
49954
+ ...path75 ? { path: path75 } : {},
49826
49955
  ...description ? { description } : {},
49827
49956
  ...isPlugin ? { plugin: name.split(":")[0] } : {}
49828
49957
  });
@@ -49852,7 +49981,7 @@ async function listCodexSkills(cwd, deps = {}) {
49852
49981
  })(),
49853
49982
  timeout
49854
49983
  ]);
49855
- 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);
49856
49985
  const res = await Promise.race([client.request("skills/list", { cwds: [cwd] }), timeout]);
49857
49986
  return mapSkillsListResponse(res);
49858
49987
  } finally {
@@ -49862,23 +49991,23 @@ async function listCodexSkills(cwd, deps = {}) {
49862
49991
  }
49863
49992
 
49864
49993
  // src/workspace/browser.ts
49865
- var import_node_fs25 = __toESM(require("fs"), 1);
49994
+ var import_node_fs26 = __toESM(require("fs"), 1);
49866
49995
  var import_node_os8 = __toESM(require("os"), 1);
49867
- var import_node_path26 = __toESM(require("path"), 1);
49996
+ var import_node_path27 = __toESM(require("path"), 1);
49868
49997
  init_protocol();
49869
49998
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
49870
49999
  function resolveInsideCwd(cwd, subpath) {
49871
- const absCwd = import_node_path26.default.resolve(cwd);
49872
- const joined = import_node_path26.default.resolve(absCwd, subpath ?? ".");
49873
- const rel = import_node_path26.default.relative(absCwd, joined);
49874
- 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)) {
49875
50004
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `path escapes cwd: ${subpath}`);
49876
50005
  }
49877
50006
  return joined;
49878
50007
  }
49879
50008
  function ensureCwd(cwd) {
49880
50009
  try {
49881
- const stat = import_node_fs25.default.statSync(cwd);
50010
+ const stat = import_node_fs26.default.statSync(cwd);
49882
50011
  if (!stat.isDirectory()) {
49883
50012
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `not a directory: ${cwd}`);
49884
50013
  }
@@ -49892,7 +50021,7 @@ var WorkspaceBrowser = class {
49892
50021
  const cwd = args.cwd && args.cwd.length > 0 ? args.cwd : import_node_os8.default.homedir();
49893
50022
  ensureCwd(cwd);
49894
50023
  const full = resolveInsideCwd(cwd, args.path);
49895
- const dirents = import_node_fs25.default.readdirSync(full, { withFileTypes: true });
50024
+ const dirents = import_node_fs26.default.readdirSync(full, { withFileTypes: true });
49896
50025
  const entries = [];
49897
50026
  for (const d of dirents) {
49898
50027
  if (!args.showHidden && d.name.startsWith(".")) continue;
@@ -49902,7 +50031,7 @@ var WorkspaceBrowser = class {
49902
50031
  mtime: ""
49903
50032
  };
49904
50033
  try {
49905
- 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));
49906
50035
  entry.mtime = new Date(st.mtimeMs).toISOString();
49907
50036
  if (d.isFile()) entry.size = st.size;
49908
50037
  } catch {
@@ -49918,14 +50047,14 @@ var WorkspaceBrowser = class {
49918
50047
  read(args) {
49919
50048
  ensureCwd(args.cwd);
49920
50049
  const full = resolveInsideCwd(args.cwd, args.path);
49921
- const st = import_node_fs25.default.statSync(full);
50050
+ const st = import_node_fs26.default.statSync(full);
49922
50051
  if (!st.isFile()) {
49923
50052
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `not a file: ${args.path}`);
49924
50053
  }
49925
50054
  if (st.size > MAX_FILE_BYTES) {
49926
50055
  throw new ClawdError(ERROR_CODES.FILE_TOO_LARGE, `file > ${MAX_FILE_BYTES} bytes`);
49927
50056
  }
49928
- const buf = import_node_fs25.default.readFileSync(full);
50057
+ const buf = import_node_fs26.default.readFileSync(full);
49929
50058
  const isBinary = buf.includes(0);
49930
50059
  if (isBinary) {
49931
50060
  return {
@@ -49947,20 +50076,20 @@ var WorkspaceBrowser = class {
49947
50076
  };
49948
50077
 
49949
50078
  // src/skills/agents-scanner.ts
49950
- var import_node_fs26 = __toESM(require("fs"), 1);
50079
+ var import_node_fs27 = __toESM(require("fs"), 1);
49951
50080
  var import_node_os9 = __toESM(require("os"), 1);
49952
- var import_node_path27 = __toESM(require("path"), 1);
50081
+ var import_node_path28 = __toESM(require("path"), 1);
49953
50082
  var DEFAULT_POLICY_DIR_DARWIN = "/Library/Application Support/ClaudeCode/.claude/agents";
49954
50083
  function isDirLikeSync2(p2) {
49955
50084
  try {
49956
- return import_node_fs26.default.statSync(p2).isDirectory();
50085
+ return import_node_fs27.default.statSync(p2).isDirectory();
49957
50086
  } catch {
49958
50087
  return false;
49959
50088
  }
49960
50089
  }
49961
50090
  function fileExistsSync(p2) {
49962
50091
  try {
49963
- return import_node_fs26.default.statSync(p2).isFile();
50092
+ return import_node_fs27.default.statSync(p2).isFile();
49964
50093
  } catch {
49965
50094
  return false;
49966
50095
  }
@@ -49968,7 +50097,7 @@ function fileExistsSync(p2) {
49968
50097
  function parseAgentFile(filePath) {
49969
50098
  let content;
49970
50099
  try {
49971
- content = import_node_fs26.default.readFileSync(filePath, "utf8");
50100
+ content = import_node_fs27.default.readFileSync(filePath, "utf8");
49972
50101
  } catch {
49973
50102
  return {};
49974
50103
  }
@@ -49981,16 +50110,16 @@ function parseAgentFile(filePath) {
49981
50110
  function scanAgentsDir(dir, source, seen, out) {
49982
50111
  let entries;
49983
50112
  try {
49984
- entries = import_node_fs26.default.readdirSync(dir, { withFileTypes: true });
50113
+ entries = import_node_fs27.default.readdirSync(dir, { withFileTypes: true });
49985
50114
  } catch {
49986
50115
  return;
49987
50116
  }
49988
50117
  for (const ent of entries) {
49989
50118
  if (!ent.name.endsWith(".md")) continue;
49990
- 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)))) {
49991
50120
  continue;
49992
50121
  }
49993
- const filePath = import_node_path27.default.join(dir, ent.name);
50122
+ const filePath = import_node_path28.default.join(dir, ent.name);
49994
50123
  const baseName = ent.name.replace(/\.md$/, "");
49995
50124
  if (seen.has(baseName)) continue;
49996
50125
  seen.add(baseName);
@@ -50008,12 +50137,12 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
50008
50137
  function walk2(dir, namespaces) {
50009
50138
  let entries;
50010
50139
  try {
50011
- entries = import_node_fs26.default.readdirSync(dir, { withFileTypes: true });
50140
+ entries = import_node_fs27.default.readdirSync(dir, { withFileTypes: true });
50012
50141
  } catch {
50013
50142
  return;
50014
50143
  }
50015
50144
  for (const ent of entries) {
50016
- const childPath = import_node_path27.default.join(dir, ent.name);
50145
+ const childPath = import_node_path28.default.join(dir, ent.name);
50017
50146
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync2(childPath)) {
50018
50147
  walk2(childPath, [...namespaces, ent.name]);
50019
50148
  continue;
@@ -50038,13 +50167,13 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
50038
50167
  walk2(root, []);
50039
50168
  }
50040
50169
  function readInstalledPlugins2(home) {
50041
- const pluginsDir = import_node_path27.default.join(home, ".claude", "plugins");
50042
- const v2 = import_node_path27.default.join(pluginsDir, "installed_plugins_v2.json");
50043
- 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");
50044
50173
  let raw = null;
50045
50174
  for (const candidate of [v2, v1]) {
50046
50175
  try {
50047
- raw = import_node_fs26.default.readFileSync(candidate, "utf8");
50176
+ raw = import_node_fs27.default.readFileSync(candidate, "utf8");
50048
50177
  break;
50049
50178
  } catch {
50050
50179
  }
@@ -50069,19 +50198,19 @@ function readInstalledPlugins2(home) {
50069
50198
  return out;
50070
50199
  }
50071
50200
  function walkUpProjectAgentsDirs(startCwd, home, seen, out) {
50072
- let cur = import_node_path27.default.resolve(startCwd);
50073
- 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;
50074
50203
  while (true) {
50075
- 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);
50076
50205
  let hasGit = false;
50077
50206
  try {
50078
- 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"));
50079
50208
  } catch {
50080
50209
  }
50081
50210
  if (hasGit) return;
50082
50211
  if (cur === home) return;
50083
50212
  if (cur === fsRoot) return;
50084
- const parent = import_node_path27.default.dirname(cur);
50213
+ const parent = import_node_path28.default.dirname(cur);
50085
50214
  if (parent === cur) return;
50086
50215
  cur = parent;
50087
50216
  }
@@ -50116,7 +50245,7 @@ var AgentsScanner = class {
50116
50245
  }
50117
50246
  const fsBlock = [];
50118
50247
  scanAgentsDir(
50119
- import_node_path27.default.join(this.home, ".claude", "agents"),
50248
+ import_node_path28.default.join(this.home, ".claude", "agents"),
50120
50249
  "global",
50121
50250
  seen,
50122
50251
  fsBlock
@@ -50130,7 +50259,7 @@ var AgentsScanner = class {
50130
50259
  ...this.extraPluginRoots
50131
50260
  ];
50132
50261
  for (const { name, root } of plugins) {
50133
- const agentsRoot = import_node_path27.default.join(root, "agents");
50262
+ const agentsRoot = import_node_path28.default.join(root, "agents");
50134
50263
  scanPluginAgentsTree(agentsRoot, name, seen, fsBlock);
50135
50264
  }
50136
50265
  return [...builtinBlock, ...fsBlock];
@@ -50138,15 +50267,15 @@ var AgentsScanner = class {
50138
50267
  };
50139
50268
 
50140
50269
  // src/observer/session-observer.ts
50141
- var import_node_fs28 = __toESM(require("fs"), 1);
50270
+ var import_node_fs29 = __toESM(require("fs"), 1);
50142
50271
  var import_node_os11 = __toESM(require("os"), 1);
50143
- var import_node_path29 = __toESM(require("path"), 1);
50272
+ var import_node_path30 = __toESM(require("path"), 1);
50144
50273
  init_claude_history();
50145
50274
 
50146
50275
  // src/observer/subagent-meta-observer.ts
50147
- var import_node_fs27 = __toESM(require("fs"), 1);
50276
+ var import_node_fs28 = __toESM(require("fs"), 1);
50148
50277
  var import_node_os10 = __toESM(require("os"), 1);
50149
- var import_node_path28 = __toESM(require("path"), 1);
50278
+ var import_node_path29 = __toESM(require("path"), 1);
50150
50279
  init_claude_history();
50151
50280
  var META_RE = /^agent-([A-Za-z0-9_-]+)\.meta\.json$/;
50152
50281
  var SubagentMetaObserver = class {
@@ -50159,7 +50288,7 @@ var SubagentMetaObserver = class {
50159
50288
  watches = /* @__PURE__ */ new Map();
50160
50289
  // public for spec only:测试直接拼路径写假 meta.json;生产 start() 内部自己解析
50161
50290
  resolveSubagentDir(cwd, toolSessionId) {
50162
- return import_node_path28.default.join(
50291
+ return import_node_path29.default.join(
50163
50292
  this.home,
50164
50293
  ".claude",
50165
50294
  "projects",
@@ -50189,7 +50318,7 @@ var SubagentMetaObserver = class {
50189
50318
  attachWatcher(w2) {
50190
50319
  if (w2.watcher) return;
50191
50320
  try {
50192
- 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) => {
50193
50322
  if (!name) return;
50194
50323
  const m2 = META_RE.exec(String(name));
50195
50324
  if (!m2) return;
@@ -50201,7 +50330,7 @@ var SubagentMetaObserver = class {
50201
50330
  scan(w2) {
50202
50331
  let entries;
50203
50332
  try {
50204
- entries = import_node_fs27.default.readdirSync(w2.dirPath);
50333
+ entries = import_node_fs28.default.readdirSync(w2.dirPath);
50205
50334
  } catch {
50206
50335
  return;
50207
50336
  }
@@ -50215,10 +50344,10 @@ var SubagentMetaObserver = class {
50215
50344
  if (!m2) return;
50216
50345
  const agentId = m2[1];
50217
50346
  if (w2.emitted.has(agentId)) return;
50218
- const file = import_node_path28.default.join(w2.dirPath, name);
50347
+ const file = import_node_path29.default.join(w2.dirPath, name);
50219
50348
  let raw;
50220
50349
  try {
50221
- raw = import_node_fs27.default.readFileSync(file, "utf8");
50350
+ raw = import_node_fs28.default.readFileSync(file, "utf8");
50222
50351
  } catch {
50223
50352
  return;
50224
50353
  }
@@ -50275,14 +50404,14 @@ var SessionObserver = class {
50275
50404
  metaObserver;
50276
50405
  resolveJsonlPath(cwd, toolSessionId, override) {
50277
50406
  if (override) return override;
50278
- 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`);
50279
50408
  }
50280
50409
  start(args) {
50281
50410
  this.stop(args.sessionId);
50282
50411
  const filePath = this.resolveJsonlPath(args.cwd, args.toolSessionId, args.jsonlPath);
50283
50412
  let size = 0;
50284
50413
  try {
50285
- size = import_node_fs28.default.statSync(filePath).size;
50414
+ size = import_node_fs29.default.statSync(filePath).size;
50286
50415
  } catch {
50287
50416
  }
50288
50417
  const w2 = {
@@ -50295,16 +50424,16 @@ var SessionObserver = class {
50295
50424
  adapter: args.adapter,
50296
50425
  prevIsRejectSentinel: false,
50297
50426
  toolSessionId: args.toolSessionId,
50298
- sawFile: import_node_fs28.default.existsSync(filePath),
50427
+ sawFile: import_node_fs29.default.existsSync(filePath),
50299
50428
  lastRelocateScanMs: 0,
50300
50429
  pendingCwdDerive: false,
50301
50430
  lastDeriveMs: 0
50302
50431
  };
50303
50432
  try {
50304
- 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 });
50305
50434
  } catch {
50306
50435
  }
50307
- 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) => {
50308
50437
  if (!changedName || !w2.filePath.endsWith(changedName)) return;
50309
50438
  this.poll(w2);
50310
50439
  });
@@ -50327,7 +50456,7 @@ var SessionObserver = class {
50327
50456
  // 异常静默吞,不阻塞 watcher 启动
50328
50457
  hydrateMetaTail(w2, maxLines = 200) {
50329
50458
  try {
50330
- const raw = import_node_fs28.default.readFileSync(w2.filePath, "utf8");
50459
+ const raw = import_node_fs29.default.readFileSync(w2.filePath, "utf8");
50331
50460
  if (!raw) return;
50332
50461
  const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
50333
50462
  if (allLines.length === 0) return;
@@ -50351,7 +50480,7 @@ var SessionObserver = class {
50351
50480
  poll(w2) {
50352
50481
  let size = 0;
50353
50482
  try {
50354
- size = import_node_fs28.default.statSync(w2.filePath).size;
50483
+ size = import_node_fs29.default.statSync(w2.filePath).size;
50355
50484
  } catch {
50356
50485
  if (w2.sawFile) this.maybeRelocate(w2);
50357
50486
  return;
@@ -50362,11 +50491,11 @@ var SessionObserver = class {
50362
50491
  w2.buf = "";
50363
50492
  }
50364
50493
  if (size === w2.lastSize) return;
50365
- const fd = import_node_fs28.default.openSync(w2.filePath, "r");
50494
+ const fd = import_node_fs29.default.openSync(w2.filePath, "r");
50366
50495
  try {
50367
50496
  const len = size - w2.lastSize;
50368
50497
  const buf = Buffer.alloc(len);
50369
- import_node_fs28.default.readSync(fd, buf, 0, len, w2.lastSize);
50498
+ import_node_fs29.default.readSync(fd, buf, 0, len, w2.lastSize);
50370
50499
  w2.lastSize = size;
50371
50500
  w2.buf += buf.toString("utf8");
50372
50501
  let newlineIndex;
@@ -50389,7 +50518,7 @@ var SessionObserver = class {
50389
50518
  }
50390
50519
  }
50391
50520
  } finally {
50392
- import_node_fs28.default.closeSync(fd);
50521
+ import_node_fs29.default.closeSync(fd);
50393
50522
  }
50394
50523
  if (w2.pendingCwdDerive) this.maybeDeriveCwd(w2);
50395
50524
  }
@@ -50401,7 +50530,7 @@ var SessionObserver = class {
50401
50530
  const now = Date.now();
50402
50531
  if (now - w2.lastRelocateScanMs < 1e3) return;
50403
50532
  w2.lastRelocateScanMs = now;
50404
- const projectsRoot = import_node_path29.default.join(this.home, ".claude", "projects");
50533
+ const projectsRoot = import_node_path30.default.join(this.home, ".claude", "projects");
50405
50534
  const found = findTranscriptByToolSessionId(projectsRoot, w2.toolSessionId);
50406
50535
  if (!found || found === w2.filePath) return;
50407
50536
  try {
@@ -50410,7 +50539,7 @@ var SessionObserver = class {
50410
50539
  }
50411
50540
  w2.filePath = found;
50412
50541
  try {
50413
- 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) => {
50414
50543
  if (!changedName || !w2.filePath.endsWith(changedName)) return;
50415
50544
  this.poll(w2);
50416
50545
  });
@@ -50977,79 +51106,6 @@ function removeSubscription(client, sessionId) {
50977
51106
  else client.subscribedSessions.set(sessionId, next);
50978
51107
  }
50979
51108
 
50980
- // src/transport/rpc-adapter.ts
50981
- var EXPOSED_RPC_METHODS = [
50982
- "appBuilder.createProject",
50983
- "appBuilder.listProjects",
50984
- "appBuilder.updateProjectPort",
50985
- "appBuilder.setProdUrl",
50986
- // 单栏 refactor §5.6.2 stage fix:scaffold + install 完成调 startDevServer 起 dev server;
50987
- // 中间用 reportStage 上报阶段(scaffolding / installing / failed),UI 据 stage 推帧切 PreviewPane。
50988
- "appBuilder.startDevServer",
50989
- "appBuilder.reportStage",
50990
- // 单栏 §5.6.5 fix: UI 切 session 时调(assistant 不需要主动调,但暴露白名单避免误猜成 forbidden)
50991
- "appBuilder.setActiveSession",
50992
- // dataclaw-builder:persona 建页时取建页人身份(含 union_id)写进生成 app 的配置。
50993
- // 路由到 handlers['auth:getIdentity'],只返 identity、绝不带 ttcToken(见 handlers/feishu-auth.ts)。
50994
- "auth.getIdentity",
50995
- // Persona dispatch(异步,spec 2026-07-20):A 端 dispatch MCP server 调 run(秒回 dispatchId);
50996
- // B 端调 complete(带 outcome → 台账 + 投递);发起方调 list 查台账。详见 protocol/src/dispatch.ts。
50997
- "personaDispatch.run",
50998
- // 跨设备 exec(spec 2026-07-16):A 侧本地 cc 经 /api/rpc 调,带 peerDeviceId → daemon forward。
50999
- "peerExec.run",
51000
- "personaDispatch.complete",
51001
- "personaDispatch.list",
51002
- // persona 清单(spec 2026-07-21):两级路由第一级——发起方 cc(loopback owner 语义)
51003
- // 用 curl 拿全量 personaId+label 紧凑清单,再按需读候选 CLAUDE.md 首段做终选。
51004
- "persona.list",
51005
- // clawd-shift v1 (spec 2026-06-24-clawd-shift):cc 子进程内的 shift MCP server 调
51006
- // 这 5 个 method 管理 owner 的 shift(fire-and-forget 排班)。鉴权靠 body.personaId
51007
- // (mcp-server 从 process.env.CLAWD_PERSONA_ID 读,源 = SessionFile.ownerPersonaId)。
51008
- // handler 内 cancel/update/history 校验 createdByPersona === personaId 防越权。
51009
- "shift-internal.add",
51010
- "shift-internal.list",
51011
- "shift-internal.cancel",
51012
- "shift-internal.update",
51013
- "shift-internal.history",
51014
- // file-sharing:persona guest agent 把自己工作目录内的产物入群+签名分享。
51015
- // 路径越界由 handlers/attachment.ts guardAttachmentPath(scope-based)拦截。
51016
- "attachment.groupAdd",
51017
- "attachment.signUrl",
51018
- // persona 受控发 P2P IM DM(sender 由 session scope 推,driver 分级授权见 handlers/inbox.ts)
51019
- "inbox.sendDm",
51020
- // per-turn sender 2026-07-08 spec §4.1:MCP servers (ticket / inbox / ...) 每 tool call
51021
- // 查当前 turn caller —— cc 长驻 + owner/guest 交替驱动同一 session 时不能靠启动时 env
51022
- // 写死身份,必须运行时 fetch。header X-Clawd-Session-Id 作 session 定位,handler 内查
51023
- // manager.tryGetState(sid).currentTurnSender 返 SenderInfo | null。
51024
- "session.currentTurnSender"
51025
- ];
51026
- async function handleRpcRequest(input) {
51027
- const { method, body, sessionId, dispatcher } = input;
51028
- if (!sessionId) {
51029
- return { status: 400, body: { ok: false, error: "MISSING_SESSION_ID", message: "X-Clawd-Session-Id header required" } };
51030
- }
51031
- if (!EXPOSED_RPC_METHODS.includes(method)) {
51032
- return { status: 403, body: { ok: false, error: "METHOD_NOT_EXPOSED", message: `method "${method}" not exposed via HTTP RPC` } };
51033
- }
51034
- const internalMethod = method.replace(".", ":");
51035
- const augmentedBody = {
51036
- ...typeof body === "object" && body != null ? body : {},
51037
- sessionId
51038
- };
51039
- try {
51040
- const result = await dispatcher.dispatch(internalMethod, augmentedBody, { sessionId });
51041
- return { status: 200, body: { ok: true, result } };
51042
- } catch (err) {
51043
- const e = err;
51044
- const code = e?.code ?? "INTERNAL";
51045
- const message = e?.message ?? String(err);
51046
- return { status: 400, body: { ok: false, error: code, message } };
51047
- }
51048
- }
51049
- function resolveExposedHandler(handlers, internalMethod) {
51050
- return handlers[internalMethod] ?? handlers[internalMethod.replace(":", ".")];
51051
- }
51052
-
51053
51109
  // src/transport/auth.ts
51054
51110
  init_protocol();
51055
51111
  var AuthGate = class {
@@ -51266,8 +51322,8 @@ async function authenticate(token, deps) {
51266
51322
  }
51267
51323
 
51268
51324
  // src/legacy-cleanup.ts
51269
- var fs33 = __toESM(require("fs/promises"), 1);
51270
- var path34 = __toESM(require("path"), 1);
51325
+ var fs34 = __toESM(require("fs/promises"), 1);
51326
+ var path35 = __toESM(require("path"), 1);
51271
51327
  var LEGACY_FILES = [
51272
51328
  "personal-capability.json",
51273
51329
  "received-capabilities.json",
@@ -51276,9 +51332,9 @@ var LEGACY_FILES = [
51276
51332
  async function cleanupLegacyCapabilityFiles(dataDir, logger) {
51277
51333
  const removed = [];
51278
51334
  for (const name of LEGACY_FILES) {
51279
- const p2 = path34.join(dataDir, name);
51335
+ const p2 = path35.join(dataDir, name);
51280
51336
  try {
51281
- await fs33.unlink(p2);
51337
+ await fs34.unlink(p2);
51282
51338
  removed.push(name);
51283
51339
  logger.debug("legacy capability file removed", { file: name });
51284
51340
  } catch (err) {
@@ -51290,13 +51346,13 @@ async function cleanupLegacyCapabilityFiles(dataDir, logger) {
51290
51346
  }
51291
51347
 
51292
51348
  // src/inbox/inbox-store.ts
51293
- var fs34 = __toESM(require("fs"), 1);
51294
- var path35 = __toESM(require("path"), 1);
51349
+ var fs35 = __toESM(require("fs"), 1);
51350
+ var path36 = __toESM(require("path"), 1);
51295
51351
  var INBOX_SUBDIR = "inbox";
51296
51352
  var InboxStore = class {
51297
51353
  constructor(dataDir) {
51298
51354
  this.dataDir = dataDir;
51299
- fs34.mkdirSync(this.dirPath(), { recursive: true });
51355
+ fs35.mkdirSync(this.dirPath(), { recursive: true });
51300
51356
  }
51301
51357
  dataDir;
51302
51358
  /**
@@ -51308,7 +51364,7 @@ var InboxStore = class {
51308
51364
  const file = this.filePath(peerDeviceId);
51309
51365
  let raw;
51310
51366
  try {
51311
- raw = fs34.readFileSync(file, "utf8");
51367
+ raw = fs35.readFileSync(file, "utf8");
51312
51368
  } catch (err) {
51313
51369
  if (err?.code === "ENOENT") return [];
51314
51370
  return [];
@@ -51324,7 +51380,7 @@ var InboxStore = class {
51324
51380
  const dir = this.dirPath();
51325
51381
  let entries;
51326
51382
  try {
51327
- entries = fs34.readdirSync(dir);
51383
+ entries = fs35.readdirSync(dir);
51328
51384
  } catch (err) {
51329
51385
  if (err?.code === "ENOENT") return [];
51330
51386
  return [];
@@ -51340,9 +51396,9 @@ var InboxStore = class {
51340
51396
  if (existing.some((m2) => m2.id === message.id)) return;
51341
51397
  const file = this.filePath(message.peerDeviceId);
51342
51398
  const line = JSON.stringify(message) + "\n";
51343
- fs34.appendFileSync(file, line, { mode: 384 });
51399
+ fs35.appendFileSync(file, line, { mode: 384 });
51344
51400
  try {
51345
- fs34.chmodSync(file, 384);
51401
+ fs35.chmodSync(file, 384);
51346
51402
  } catch {
51347
51403
  }
51348
51404
  }
@@ -51372,7 +51428,7 @@ var InboxStore = class {
51372
51428
  removeByPeerDeviceId(peerDeviceId) {
51373
51429
  const file = this.filePath(peerDeviceId);
51374
51430
  try {
51375
- fs34.unlinkSync(file);
51431
+ fs35.unlinkSync(file);
51376
51432
  } catch (err) {
51377
51433
  if (err?.code === "ENOENT") return;
51378
51434
  }
@@ -51381,18 +51437,18 @@ var InboxStore = class {
51381
51437
  const file = this.filePath(peerDeviceId);
51382
51438
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
51383
51439
  const content = messages.map((m2) => JSON.stringify(m2)).join("\n") + (messages.length > 0 ? "\n" : "");
51384
- fs34.writeFileSync(tmp, content, { mode: 384 });
51385
- fs34.renameSync(tmp, file);
51440
+ fs35.writeFileSync(tmp, content, { mode: 384 });
51441
+ fs35.renameSync(tmp, file);
51386
51442
  try {
51387
- fs34.chmodSync(file, 384);
51443
+ fs35.chmodSync(file, 384);
51388
51444
  } catch {
51389
51445
  }
51390
51446
  }
51391
51447
  dirPath() {
51392
- return path35.join(this.dataDir, INBOX_SUBDIR);
51448
+ return path36.join(this.dataDir, INBOX_SUBDIR);
51393
51449
  }
51394
51450
  filePath(peerDeviceId) {
51395
- return path35.join(this.dirPath(), `${peerDeviceId}.jsonl`);
51451
+ return path36.join(this.dirPath(), `${peerDeviceId}.jsonl`);
51396
51452
  }
51397
51453
  };
51398
51454
  function parseAllLines(raw) {
@@ -51480,8 +51536,8 @@ var InboxManager = class {
51480
51536
  };
51481
51537
 
51482
51538
  // src/state/contact-store.ts
51483
- var fs35 = __toESM(require("fs"), 1);
51484
- var path36 = __toESM(require("path"), 1);
51539
+ var fs36 = __toESM(require("fs"), 1);
51540
+ var path37 = __toESM(require("path"), 1);
51485
51541
  var FILE_NAME = "contacts.json";
51486
51542
  var ContactStore = class {
51487
51543
  constructor(dataDir) {
@@ -51491,10 +51547,10 @@ var ContactStore = class {
51491
51547
  contacts = /* @__PURE__ */ new Map();
51492
51548
  load() {
51493
51549
  this.contacts.clear();
51494
- const file = path36.join(this.dataDir, FILE_NAME);
51550
+ const file = path37.join(this.dataDir, FILE_NAME);
51495
51551
  let raw;
51496
51552
  try {
51497
- raw = fs35.readFileSync(file, "utf8");
51553
+ raw = fs36.readFileSync(file, "utf8");
51498
51554
  } catch (err) {
51499
51555
  if (err?.code !== "ENOENT") this.renameBak(file);
51500
51556
  return;
@@ -51563,20 +51619,20 @@ var ContactStore = class {
51563
51619
  return true;
51564
51620
  }
51565
51621
  flush() {
51566
- const file = path36.join(this.dataDir, FILE_NAME);
51622
+ const file = path37.join(this.dataDir, FILE_NAME);
51567
51623
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
51568
51624
  const content = JSON.stringify(
51569
51625
  { contacts: Array.from(this.contacts.values()) },
51570
51626
  null,
51571
51627
  2
51572
51628
  );
51573
- fs35.mkdirSync(this.dataDir, { recursive: true });
51574
- fs35.writeFileSync(tmp, content, { mode: 384 });
51575
- fs35.renameSync(tmp, file);
51629
+ fs36.mkdirSync(this.dataDir, { recursive: true });
51630
+ fs36.writeFileSync(tmp, content, { mode: 384 });
51631
+ fs36.renameSync(tmp, file);
51576
51632
  }
51577
51633
  renameBak(file) {
51578
51634
  try {
51579
- fs35.renameSync(file, `${file}.bak`);
51635
+ fs36.renameSync(file, `${file}.bak`);
51580
51636
  } catch {
51581
51637
  }
51582
51638
  }
@@ -51684,61 +51740,61 @@ async function connectRemote(args) {
51684
51740
  }
51685
51741
 
51686
51742
  // src/migrations/2026-05-20-flatten-sessions.ts
51687
- var fs36 = __toESM(require("fs"), 1);
51688
- var path37 = __toESM(require("path"), 1);
51743
+ var fs37 = __toESM(require("fs"), 1);
51744
+ var path38 = __toESM(require("path"), 1);
51689
51745
  var MIGRATION_FLAG_NAME = ".migration.v1.done";
51690
51746
  function migrateFlattenSessions(opts) {
51691
51747
  const dataDir = opts.dataDir;
51692
51748
  const now = opts.now ?? Date.now;
51693
- const sessionsDir = path37.join(dataDir, "sessions");
51694
- const flagPath = path37.join(sessionsDir, MIGRATION_FLAG_NAME);
51749
+ const sessionsDir = path38.join(dataDir, "sessions");
51750
+ const flagPath = path38.join(sessionsDir, MIGRATION_FLAG_NAME);
51695
51751
  if (existsSync3(flagPath)) {
51696
51752
  return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
51697
51753
  }
51698
51754
  let movedBare = 0;
51699
51755
  let movedVmOwner = 0;
51700
51756
  let archivedListener = 0;
51701
- const defaultDir = path37.join(sessionsDir, "default");
51757
+ const defaultDir = path38.join(sessionsDir, "default");
51702
51758
  if (existsSync3(defaultDir)) {
51703
51759
  for (const entry of readdirSafe(defaultDir)) {
51704
51760
  if (!entry.endsWith(".json")) continue;
51705
- const src = path37.join(defaultDir, entry);
51706
- const dst = path37.join(sessionsDir, entry);
51707
- fs36.renameSync(src, dst);
51761
+ const src = path38.join(defaultDir, entry);
51762
+ const dst = path38.join(sessionsDir, entry);
51763
+ fs37.renameSync(src, dst);
51708
51764
  movedBare += 1;
51709
51765
  }
51710
51766
  rmdirIfEmpty(defaultDir);
51711
51767
  }
51712
51768
  for (const pid of readdirSafe(sessionsDir)) {
51713
- const personaDir = path37.join(sessionsDir, pid);
51769
+ const personaDir = path38.join(sessionsDir, pid);
51714
51770
  if (!isDir(personaDir)) continue;
51715
51771
  if (pid === "default") continue;
51716
- const ownerSrc = path37.join(personaDir, "owner");
51772
+ const ownerSrc = path38.join(personaDir, "owner");
51717
51773
  if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
51718
- const ownerDst = path37.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
51719
- fs36.mkdirSync(ownerDst, { recursive: true });
51774
+ const ownerDst = path38.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
51775
+ fs37.mkdirSync(ownerDst, { recursive: true });
51720
51776
  for (const file of readdirSafe(ownerSrc)) {
51721
51777
  if (!file.endsWith(".json")) continue;
51722
- fs36.renameSync(path37.join(ownerSrc, file), path37.join(ownerDst, file));
51778
+ fs37.renameSync(path38.join(ownerSrc, file), path38.join(ownerDst, file));
51723
51779
  movedVmOwner += 1;
51724
51780
  }
51725
51781
  rmdirIfEmpty(ownerSrc);
51726
51782
  }
51727
- const listenerSrc = path37.join(personaDir, "listener");
51783
+ const listenerSrc = path38.join(personaDir, "listener");
51728
51784
  if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
51729
- const archiveDst = path37.join(dataDir, ".legacy", `listener-${pid}`);
51730
- fs36.mkdirSync(archiveDst, { recursive: true });
51785
+ const archiveDst = path38.join(dataDir, ".legacy", `listener-${pid}`);
51786
+ fs37.mkdirSync(archiveDst, { recursive: true });
51731
51787
  for (const file of readdirSafe(listenerSrc)) {
51732
51788
  if (!file.endsWith(".json")) continue;
51733
- fs36.renameSync(path37.join(listenerSrc, file), path37.join(archiveDst, file));
51789
+ fs37.renameSync(path38.join(listenerSrc, file), path38.join(archiveDst, file));
51734
51790
  archivedListener += 1;
51735
51791
  }
51736
51792
  rmdirIfEmpty(listenerSrc);
51737
51793
  }
51738
51794
  rmdirIfEmpty(personaDir);
51739
51795
  }
51740
- fs36.mkdirSync(sessionsDir, { recursive: true });
51741
- 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));
51742
51798
  return {
51743
51799
  skipped: false,
51744
51800
  flagWritten: true,
@@ -51749,7 +51805,7 @@ function migrateFlattenSessions(opts) {
51749
51805
  }
51750
51806
  function existsSync3(p2) {
51751
51807
  try {
51752
- fs36.statSync(p2);
51808
+ fs37.statSync(p2);
51753
51809
  return true;
51754
51810
  } catch {
51755
51811
  return false;
@@ -51757,31 +51813,31 @@ function existsSync3(p2) {
51757
51813
  }
51758
51814
  function isDir(p2) {
51759
51815
  try {
51760
- return fs36.statSync(p2).isDirectory();
51816
+ return fs37.statSync(p2).isDirectory();
51761
51817
  } catch {
51762
51818
  return false;
51763
51819
  }
51764
51820
  }
51765
51821
  function readdirSafe(p2) {
51766
51822
  try {
51767
- return fs36.readdirSync(p2);
51823
+ return fs37.readdirSync(p2);
51768
51824
  } catch {
51769
51825
  return [];
51770
51826
  }
51771
51827
  }
51772
51828
  function rmdirIfEmpty(p2) {
51773
51829
  try {
51774
- fs36.rmdirSync(p2);
51830
+ fs37.rmdirSync(p2);
51775
51831
  } catch {
51776
51832
  }
51777
51833
  }
51778
51834
 
51779
51835
  // src/transport/http-router.ts
51780
- var import_node_fs30 = __toESM(require("fs"), 1);
51781
- 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);
51782
51838
 
51783
51839
  // src/attachment/mime.ts
51784
- var import_node_path30 = __toESM(require("path"), 1);
51840
+ var import_node_path31 = __toESM(require("path"), 1);
51785
51841
  var TEXT_PLAIN = "text/plain; charset=utf-8";
51786
51842
  var EXT_TO_NATIVE_MIME = {
51787
51843
  // 图片
@@ -51888,7 +51944,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
51888
51944
  ".mk"
51889
51945
  ]);
51890
51946
  function lookupMime(filePathOrName) {
51891
- const ext = import_node_path30.default.extname(filePathOrName).toLowerCase();
51947
+ const ext = import_node_path31.default.extname(filePathOrName).toLowerCase();
51892
51948
  if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
51893
51949
  if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
51894
51950
  return "application/octet-stream";
@@ -51957,8 +52013,8 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
51957
52013
  }
51958
52014
 
51959
52015
  // src/attachment/upload.ts
51960
- var import_node_fs29 = __toESM(require("fs"), 1);
51961
- 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);
51962
52018
  var import_node_crypto8 = __toESM(require("crypto"), 1);
51963
52019
  var import_promises3 = require("stream/promises");
51964
52020
  var UploadError = class extends Error {
@@ -51970,22 +52026,22 @@ var UploadError = class extends Error {
51970
52026
  code;
51971
52027
  };
51972
52028
  function assertValidFileName(fileName) {
51973
- 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)) {
51974
52030
  throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
51975
52031
  }
51976
52032
  }
51977
52033
  var HASH_PREFIX_LEN = 16;
51978
52034
  async function writeUploadedAttachment(args) {
51979
52035
  assertValidFileName(args.fileName);
51980
- const attachmentsRoot = import_node_path31.default.join(args.sessionDir, ".attachments");
52036
+ const attachmentsRoot = import_node_path32.default.join(args.sessionDir, ".attachments");
51981
52037
  try {
51982
- import_node_fs29.default.mkdirSync(attachmentsRoot, { recursive: true });
52038
+ import_node_fs30.default.mkdirSync(attachmentsRoot, { recursive: true });
51983
52039
  } catch (err) {
51984
52040
  throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
51985
52041
  }
51986
52042
  const hasher = import_node_crypto8.default.createHash("sha256");
51987
52043
  let actualSize = 0;
51988
- const tmpPath = import_node_path31.default.join(
52044
+ const tmpPath = import_node_path32.default.join(
51989
52045
  attachmentsRoot,
51990
52046
  `.upload-${process.pid}-${Date.now()}-${import_node_crypto8.default.randomBytes(4).toString("hex")}`
51991
52047
  );
@@ -52000,18 +52056,18 @@ async function writeUploadedAttachment(args) {
52000
52056
  yield buf;
52001
52057
  }
52002
52058
  },
52003
- import_node_fs29.default.createWriteStream(tmpPath, { mode: 384 })
52059
+ import_node_fs30.default.createWriteStream(tmpPath, { mode: 384 })
52004
52060
  );
52005
52061
  } catch (err) {
52006
52062
  try {
52007
- import_node_fs29.default.unlinkSync(tmpPath);
52063
+ import_node_fs30.default.unlinkSync(tmpPath);
52008
52064
  } catch {
52009
52065
  }
52010
52066
  throw new UploadError("STORAGE_ERROR", `write failed: ${err.message}`);
52011
52067
  }
52012
52068
  if (actualSize !== args.contentLength) {
52013
52069
  try {
52014
- import_node_fs29.default.unlinkSync(tmpPath);
52070
+ import_node_fs30.default.unlinkSync(tmpPath);
52015
52071
  } catch {
52016
52072
  }
52017
52073
  throw new UploadError(
@@ -52020,35 +52076,35 @@ async function writeUploadedAttachment(args) {
52020
52076
  );
52021
52077
  }
52022
52078
  const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
52023
- const hashDir = import_node_path31.default.join(attachmentsRoot, attachmentId);
52079
+ const hashDir = import_node_path32.default.join(attachmentsRoot, attachmentId);
52024
52080
  let finalFileName;
52025
52081
  let hashDirExists = false;
52026
52082
  try {
52027
- hashDirExists = import_node_fs29.default.statSync(hashDir).isDirectory();
52083
+ hashDirExists = import_node_fs30.default.statSync(hashDir).isDirectory();
52028
52084
  } catch {
52029
52085
  }
52030
52086
  if (hashDirExists) {
52031
- 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("."));
52032
52088
  finalFileName = existing[0] ?? args.fileName;
52033
52089
  try {
52034
- import_node_fs29.default.unlinkSync(tmpPath);
52090
+ import_node_fs30.default.unlinkSync(tmpPath);
52035
52091
  } catch {
52036
52092
  }
52037
52093
  } else {
52038
52094
  try {
52039
- import_node_fs29.default.mkdirSync(hashDir, { recursive: true });
52095
+ import_node_fs30.default.mkdirSync(hashDir, { recursive: true });
52040
52096
  finalFileName = args.fileName;
52041
- 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));
52042
52098
  } catch (err) {
52043
52099
  try {
52044
- import_node_fs29.default.unlinkSync(tmpPath);
52100
+ import_node_fs30.default.unlinkSync(tmpPath);
52045
52101
  } catch {
52046
52102
  }
52047
52103
  throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
52048
52104
  }
52049
52105
  }
52050
- const absPath = import_node_path31.default.join(hashDir, finalFileName);
52051
- 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);
52052
52108
  args.groupFileStore.upsert(args.scope, args.sessionId, {
52053
52109
  relPath: absPath,
52054
52110
  // 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
@@ -52062,7 +52118,7 @@ async function writeUploadedAttachment(args) {
52062
52118
 
52063
52119
  // src/extension/import.ts
52064
52120
  var import_promises4 = __toESM(require("fs/promises"), 1);
52065
- var import_node_path32 = __toESM(require("path"), 1);
52121
+ var import_node_path33 = __toESM(require("path"), 1);
52066
52122
  var import_node_os12 = __toESM(require("os"), 1);
52067
52123
  var import_jszip = __toESM(require_lib3(), 1);
52068
52124
  var ImportError = class extends Error {
@@ -52080,7 +52136,7 @@ async function importZip(buf, root) {
52080
52136
  throw new ImportError("ZIP_INVALID", `failed to load zip: ${e.message}`);
52081
52137
  }
52082
52138
  for (const name of Object.keys(zip.files)) {
52083
- if (name.includes("..") || name.startsWith("/") || import_node_path32.default.isAbsolute(name)) {
52139
+ if (name.includes("..") || name.startsWith("/") || import_node_path33.default.isAbsolute(name)) {
52084
52140
  throw new ImportError("ZIP_INVALID", `unsafe zip entry path: ${name}`);
52085
52141
  }
52086
52142
  }
@@ -52113,7 +52169,7 @@ async function importZip(buf, root) {
52113
52169
  );
52114
52170
  }
52115
52171
  }
52116
- const destDir = import_node_path32.default.join(root, manifest.id);
52172
+ const destDir = import_node_path33.default.join(root, manifest.id);
52117
52173
  let destExists = false;
52118
52174
  try {
52119
52175
  await import_promises4.default.access(destDir);
@@ -52123,15 +52179,15 @@ async function importZip(buf, root) {
52123
52179
  if (destExists) {
52124
52180
  throw new ImportError("ALREADY_EXISTS", `extension ${manifest.id} already installed`);
52125
52181
  }
52126
- 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}-`));
52127
52183
  try {
52128
52184
  for (const [name, entry] of Object.entries(zip.files)) {
52129
- const dest = import_node_path32.default.join(stage, name);
52185
+ const dest = import_node_path33.default.join(stage, name);
52130
52186
  if (entry.dir) {
52131
52187
  await import_promises4.default.mkdir(dest, { recursive: true });
52132
52188
  continue;
52133
52189
  }
52134
- 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 });
52135
52191
  const content = await entry.async("nodebuffer");
52136
52192
  await import_promises4.default.writeFile(dest, content);
52137
52193
  }
@@ -52146,7 +52202,6 @@ async function importZip(buf, root) {
52146
52202
  }
52147
52203
 
52148
52204
  // src/transport/http-router.ts
52149
- var RPC_ROUTE_PREFIX = "/api/rpc/";
52150
52205
  var RPC_BODY_MAX_BYTES = 1 * 1024 * 1024;
52151
52206
  var SHARE_UI_ASSETS_PREFIX = "/share-ui/assets/";
52152
52207
  var SHARE_UI_ASSET_MIME = {
@@ -52162,11 +52217,9 @@ var SHARE_UI_ASSET_MIME = {
52162
52217
  ".wasm": "application/wasm"
52163
52218
  };
52164
52219
  function shareUiAssetMime(filePath) {
52165
- const ext = import_node_path33.default.extname(filePath).toLowerCase();
52220
+ const ext = import_node_path34.default.extname(filePath).toLowerCase();
52166
52221
  return SHARE_UI_ASSET_MIME[ext] ?? lookupMime(filePath);
52167
52222
  }
52168
- var DISPATCH_HEARTBEAT_MS = 25e3;
52169
- var DISPATCH_HOLD_METHOD = "personaDispatch:run";
52170
52223
  var AUTHED_RPC_ROUTE_PREFIX = "/rpc/";
52171
52224
  var AUTHED_RPC_CORS_HEADERS = {
52172
52225
  "Access-Control-Allow-Origin": "*",
@@ -52189,10 +52242,6 @@ function authedRpcErrorStatus(code) {
52189
52242
  return 400;
52190
52243
  }
52191
52244
  }
52192
- function isLoopbackAddress(addr) {
52193
- if (!addr) return false;
52194
- return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1" || addr.startsWith("127.");
52195
- }
52196
52245
  async function readJsonBody(req, max) {
52197
52246
  return await new Promise((resolve6, reject) => {
52198
52247
  let size = 0;
@@ -52249,7 +52298,7 @@ function isValidUploadFileName(fileName) {
52249
52298
  if (fileName === "." || fileName === "..") return false;
52250
52299
  if (fileName.startsWith(".")) return false;
52251
52300
  if (fileName.includes("/") || fileName.includes("\\")) return false;
52252
- return fileName === import_node_path33.default.basename(fileName);
52301
+ return fileName === import_node_path34.default.basename(fileName);
52253
52302
  }
52254
52303
  function createHttpRouter(deps) {
52255
52304
  return async (req, res) => {
@@ -52321,7 +52370,7 @@ function createHttpRouter(deps) {
52321
52370
  }
52322
52371
  let buf;
52323
52372
  try {
52324
- buf = await import_node_fs30.default.promises.readFile(abs);
52373
+ buf = await import_node_fs31.default.promises.readFile(abs);
52325
52374
  } catch {
52326
52375
  sendJson(res, 404, { code: "NOT_FOUND", message: "asset not found" });
52327
52376
  return true;
@@ -52369,57 +52418,6 @@ function createHttpRouter(deps) {
52369
52418
  return true;
52370
52419
  }
52371
52420
  const dispatchArgs = typeof body === "object" && body != null ? body : {};
52372
- if (method === DISPATCH_HOLD_METHOD) {
52373
- const heartbeatMs = deps.dispatchHeartbeatMs ?? DISPATCH_HEARTBEAT_MS;
52374
- let headSent = false;
52375
- let hbInterval;
52376
- const startStreaming = () => {
52377
- headSent = true;
52378
- req.socket?.setTimeout?.(0);
52379
- res.socket?.setTimeout?.(0);
52380
- res.writeHead(200, {
52381
- "Content-Type": "application/json; charset=utf-8",
52382
- "Transfer-Encoding": "chunked",
52383
- // nginx 专用:禁用该响应缓冲,让心跳字节即时 flush 到上游 read(否则被攒着不重置计时)
52384
- "X-Accel-Buffering": "no",
52385
- ...AUTHED_RPC_CORS_HEADERS
52386
- });
52387
- res.write(" ");
52388
- hbInterval = setInterval(() => {
52389
- try {
52390
- res.write(" ");
52391
- } catch {
52392
- }
52393
- }, heartbeatMs);
52394
- };
52395
- const graceTimer = setTimeout(startStreaming, heartbeatMs);
52396
- const cleanup = () => {
52397
- clearTimeout(graceTimer);
52398
- if (hbInterval) clearInterval(hbInterval);
52399
- };
52400
- const safeEnd = (payload) => {
52401
- if (res.writableEnded || res.destroyed) return;
52402
- try {
52403
- res.end(payload);
52404
- } catch {
52405
- }
52406
- };
52407
- res.on("close", cleanup);
52408
- try {
52409
- const result = await deps.authedRpc.dispatch(method, dispatchArgs, auth.context);
52410
- cleanup();
52411
- if (headSent) safeEnd(JSON.stringify({ ok: true, result: result.response }));
52412
- else sendJson(res, 200, { ok: true, result: result.response }, AUTHED_RPC_CORS_HEADERS);
52413
- } catch (err) {
52414
- cleanup();
52415
- const e = err;
52416
- const code = e?.code ?? "INTERNAL";
52417
- const payload = { ok: false, error: code, message: e?.message ?? String(err) };
52418
- if (headSent) safeEnd(JSON.stringify(payload));
52419
- else sendJson(res, authedRpcErrorStatus(code), payload, AUTHED_RPC_CORS_HEADERS);
52420
- }
52421
- return true;
52422
- }
52423
52421
  try {
52424
52422
  const result = await deps.authedRpc.dispatch(method, dispatchArgs, auth.context);
52425
52423
  sendJson(res, 200, { ok: true, result: result.response }, AUTHED_RPC_CORS_HEADERS);
@@ -52430,34 +52428,6 @@ function createHttpRouter(deps) {
52430
52428
  }
52431
52429
  return true;
52432
52430
  }
52433
- if (url.pathname.startsWith(RPC_ROUTE_PREFIX)) {
52434
- if (!deps.rpcDispatcher) {
52435
- sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "HTTP RPC adapter not wired" });
52436
- return true;
52437
- }
52438
- if (req.method !== "POST") {
52439
- sendJson(res, 405, { code: "METHOD_NOT_ALLOWED", message: "POST only" });
52440
- return true;
52441
- }
52442
- if (!isLoopbackAddress(req.socket.remoteAddress)) {
52443
- sendJson(res, 403, { code: "NOT_LOOPBACK", message: "RPC adapter only accepts loopback connections" });
52444
- return true;
52445
- }
52446
- req.socket.setTimeout(0);
52447
- res.socket?.setTimeout(0);
52448
- const method = url.pathname.slice(RPC_ROUTE_PREFIX.length).split("?")[0] ?? "";
52449
- const sessionId = req.headers["x-clawd-session-id"] ?? void 0;
52450
- let body;
52451
- try {
52452
- body = await readJsonBody(req, RPC_BODY_MAX_BYTES);
52453
- } catch (err) {
52454
- sendJson(res, 400, { code: "INVALID_BODY", message: err instanceof Error ? err.message : String(err) });
52455
- return true;
52456
- }
52457
- const result = await handleRpcRequest({ method, body, sessionId, dispatcher: deps.rpcDispatcher });
52458
- sendJson(res, result.status, result.body);
52459
- return true;
52460
- }
52461
52431
  if (url.pathname.startsWith("/preview/")) {
52462
52432
  const port = matchPreviewPort(url.pathname);
52463
52433
  if (port != null && isPreviewPortAllowed(port, { allowedPorts: deps.previewPorts ?? [] })) {
@@ -52545,7 +52515,7 @@ function createHttpRouter(deps) {
52545
52515
  sendHtml(res, statusByCode[r.code], loader.renderErrorHtml(r.code, msgByCode[r.code]));
52546
52516
  return true;
52547
52517
  }
52548
- 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)));
52549
52519
  return true;
52550
52520
  }
52551
52521
  const ctx = deps.authResolver.resolveFromHeader(
@@ -52666,7 +52636,7 @@ function createHttpRouter(deps) {
52666
52636
  return true;
52667
52637
  }
52668
52638
  let absPath;
52669
- if (import_node_path33.default.isAbsolute(pathParam)) {
52639
+ if (import_node_path34.default.isAbsolute(pathParam)) {
52670
52640
  absPath = pathParam;
52671
52641
  } else if (deps.sessionStore) {
52672
52642
  const file = deps.sessionStore.read(sid);
@@ -52674,7 +52644,7 @@ function createHttpRouter(deps) {
52674
52644
  sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sid} not found` });
52675
52645
  return true;
52676
52646
  }
52677
- absPath = import_node_path33.default.join(file.cwd, pathParam);
52647
+ absPath = import_node_path34.default.join(file.cwd, pathParam);
52678
52648
  } else {
52679
52649
  sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "sessionStore not wired" }));
52680
52650
  return true;
@@ -52761,7 +52731,7 @@ function withCtx(ctx, body) {
52761
52731
  function streamFile(res, absPath, logger) {
52762
52732
  let stat;
52763
52733
  try {
52764
- stat = import_node_fs30.default.statSync(absPath);
52734
+ stat = import_node_fs31.default.statSync(absPath);
52765
52735
  } catch (err) {
52766
52736
  const code = err?.code;
52767
52737
  if (code === "ENOENT") {
@@ -52776,7 +52746,7 @@ function streamFile(res, absPath, logger) {
52776
52746
  return;
52777
52747
  }
52778
52748
  const mime = lookupMime(absPath);
52779
- const basename = import_node_path33.default.basename(absPath);
52749
+ const basename = import_node_path34.default.basename(absPath);
52780
52750
  res.writeHead(200, {
52781
52751
  "Content-Type": mime,
52782
52752
  "Content-Length": String(stat.size),
@@ -52784,7 +52754,7 @@ function streamFile(res, absPath, logger) {
52784
52754
  // 防止浏览器把任意 mime 当 html 渲染
52785
52755
  "X-Content-Type-Options": "nosniff"
52786
52756
  });
52787
- const stream = import_node_fs30.default.createReadStream(absPath);
52757
+ const stream = import_node_fs31.default.createReadStream(absPath);
52788
52758
  stream.on("error", (err) => {
52789
52759
  logger?.warn("streamFile read error", { absPath, err: err.message });
52790
52760
  res.destroy();
@@ -52793,8 +52763,8 @@ function streamFile(res, absPath, logger) {
52793
52763
  }
52794
52764
 
52795
52765
  // src/attachment/gc.ts
52796
- var import_node_fs31 = __toESM(require("fs"), 1);
52797
- 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);
52798
52768
  var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
52799
52769
  function runAttachmentGc(args) {
52800
52770
  const now = (args.now ?? Date.now)();
@@ -52803,38 +52773,38 @@ function runAttachmentGc(args) {
52803
52773
  for (const { scope, sessionId } of args.sessionScopes) {
52804
52774
  for (const entry of args.groupFileStore.list(scope, sessionId)) {
52805
52775
  if (entry.stale) continue;
52806
- 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);
52807
52777
  }
52808
52778
  }
52809
52779
  for (const { scope, sessionId } of args.sessionScopes) {
52810
- const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path34.default.join(
52780
+ const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path35.default.join(
52811
52781
  args.dataDir,
52812
52782
  "sessions",
52813
52783
  ...scopeSubPath(scope).map(safeFileName),
52814
52784
  safeFileName(sessionId)
52815
52785
  );
52816
- const attRoot = import_node_path34.default.join(sessionDir, ".attachments");
52786
+ const attRoot = import_node_path35.default.join(sessionDir, ".attachments");
52817
52787
  let hashDirs;
52818
52788
  try {
52819
- hashDirs = import_node_fs31.default.readdirSync(attRoot);
52789
+ hashDirs = import_node_fs32.default.readdirSync(attRoot);
52820
52790
  } catch (err) {
52821
52791
  if (err.code === "ENOENT") continue;
52822
52792
  args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
52823
52793
  continue;
52824
52794
  }
52825
52795
  for (const hashDir of hashDirs) {
52826
- const hashDirAbs = import_node_path34.default.join(attRoot, hashDir);
52796
+ const hashDirAbs = import_node_path35.default.join(attRoot, hashDir);
52827
52797
  let files;
52828
52798
  try {
52829
- files = import_node_fs31.default.readdirSync(hashDirAbs);
52799
+ files = import_node_fs32.default.readdirSync(hashDirAbs);
52830
52800
  } catch {
52831
52801
  continue;
52832
52802
  }
52833
52803
  for (const name of files) {
52834
- const file = import_node_path34.default.join(hashDirAbs, name);
52804
+ const file = import_node_path35.default.join(hashDirAbs, name);
52835
52805
  let stat;
52836
52806
  try {
52837
- stat = import_node_fs31.default.statSync(file);
52807
+ stat = import_node_fs32.default.statSync(file);
52838
52808
  } catch {
52839
52809
  continue;
52840
52810
  }
@@ -52843,26 +52813,26 @@ function runAttachmentGc(args) {
52843
52813
  if (age < ttlMs) continue;
52844
52814
  if (liveAbs.has(file)) continue;
52845
52815
  try {
52846
- import_node_fs31.default.unlinkSync(file);
52816
+ import_node_fs32.default.unlinkSync(file);
52847
52817
  } catch (err) {
52848
52818
  args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
52849
52819
  }
52850
52820
  }
52851
52821
  try {
52852
- 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);
52853
52823
  } catch {
52854
52824
  }
52855
52825
  }
52856
52826
  try {
52857
- 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);
52858
52828
  } catch {
52859
52829
  }
52860
52830
  }
52861
52831
  }
52862
52832
 
52863
52833
  // src/attachment/group.ts
52864
- var import_node_fs32 = __toESM(require("fs"), 1);
52865
- 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);
52866
52836
  var import_node_crypto9 = __toESM(require("crypto"), 1);
52867
52837
  init_protocol();
52868
52838
  var GroupFileStore = class {
@@ -52874,11 +52844,11 @@ var GroupFileStore = class {
52874
52844
  this.logger = opts.logger;
52875
52845
  }
52876
52846
  rootForScope(scope) {
52877
- 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));
52878
52848
  }
52879
52849
  /** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
52880
52850
  filePath(scope, sessionId) {
52881
- 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`);
52882
52852
  }
52883
52853
  cacheKey(scope, sessionId) {
52884
52854
  return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
@@ -52887,7 +52857,7 @@ var GroupFileStore = class {
52887
52857
  readFile(scope, sessionId) {
52888
52858
  const file = this.filePath(scope, sessionId);
52889
52859
  try {
52890
- const raw = import_node_fs32.default.readFileSync(file, "utf8");
52860
+ const raw = import_node_fs33.default.readFileSync(file, "utf8");
52891
52861
  const parsed = JSON.parse(raw);
52892
52862
  if (!Array.isArray(parsed)) {
52893
52863
  this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
@@ -52913,10 +52883,10 @@ var GroupFileStore = class {
52913
52883
  }
52914
52884
  writeFile(scope, sessionId, entries) {
52915
52885
  const file = this.filePath(scope, sessionId);
52916
- 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 });
52917
52887
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
52918
- import_node_fs32.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
52919
- 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);
52920
52890
  }
52921
52891
  /** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
52922
52892
  list(scope, sessionId) {
@@ -53002,10 +52972,10 @@ var GroupFileStore = class {
53002
52972
  };
53003
52973
 
53004
52974
  // src/discovery/state-file.ts
53005
- var import_node_fs33 = __toESM(require("fs"), 1);
53006
- 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);
53007
52977
  function defaultStateFilePath(dataDir) {
53008
- return import_node_path36.default.join(dataDir, "state.json");
52978
+ return import_node_path37.default.join(dataDir, "state.json");
53009
52979
  }
53010
52980
  function isPidAlive(pid) {
53011
52981
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -53027,7 +52997,7 @@ var StateFileManager = class {
53027
52997
  }
53028
52998
  read() {
53029
52999
  try {
53030
- const raw = import_node_fs33.default.readFileSync(this.file, "utf8");
53000
+ const raw = import_node_fs34.default.readFileSync(this.file, "utf8");
53031
53001
  const parsed = JSON.parse(raw);
53032
53002
  return parsed;
53033
53003
  } catch {
@@ -53041,20 +53011,20 @@ var StateFileManager = class {
53041
53011
  return { status: "stale", existing };
53042
53012
  }
53043
53013
  write(state) {
53044
- 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 });
53045
53015
  const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
53046
- import_node_fs33.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
53047
- 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);
53048
53018
  if (process.platform !== "win32") {
53049
53019
  try {
53050
- import_node_fs33.default.chmodSync(this.file, 384);
53020
+ import_node_fs34.default.chmodSync(this.file, 384);
53051
53021
  } catch {
53052
53022
  }
53053
53023
  }
53054
53024
  }
53055
53025
  delete() {
53056
53026
  try {
53057
- import_node_fs33.default.unlinkSync(this.file);
53027
+ import_node_fs34.default.unlinkSync(this.file);
53058
53028
  } catch {
53059
53029
  }
53060
53030
  }
@@ -53068,14 +53038,14 @@ function readSpawnedByDesktopFromEnv(env = process.env) {
53068
53038
  }
53069
53039
 
53070
53040
  // src/tunnel/tunnel-manager.ts
53071
- var import_node_fs37 = __toESM(require("fs"), 1);
53072
- 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);
53073
53043
  var import_node_crypto10 = __toESM(require("crypto"), 1);
53074
53044
  var import_node_child_process10 = require("child_process");
53075
53045
 
53076
53046
  // src/tunnel/tunnel-store.ts
53077
- var import_node_fs34 = __toESM(require("fs"), 1);
53078
- 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);
53079
53049
  var TunnelStore = class {
53080
53050
  constructor(filePath) {
53081
53051
  this.filePath = filePath;
@@ -53083,7 +53053,7 @@ var TunnelStore = class {
53083
53053
  filePath;
53084
53054
  async get() {
53085
53055
  try {
53086
- 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");
53087
53057
  const obj = JSON.parse(raw);
53088
53058
  if (!isPersistedTunnel(obj)) return null;
53089
53059
  return obj;
@@ -53094,22 +53064,22 @@ var TunnelStore = class {
53094
53064
  }
53095
53065
  }
53096
53066
  async set(v2) {
53097
- const dir = import_node_path37.default.dirname(this.filePath);
53098
- 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 });
53099
53069
  const data = JSON.stringify(v2, null, 2);
53100
53070
  const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
53101
- await import_node_fs34.default.promises.writeFile(tmp, data, { mode: 384 });
53071
+ await import_node_fs35.default.promises.writeFile(tmp, data, { mode: 384 });
53102
53072
  if (process.platform !== "win32") {
53103
53073
  try {
53104
- await import_node_fs34.default.promises.chmod(tmp, 384);
53074
+ await import_node_fs35.default.promises.chmod(tmp, 384);
53105
53075
  } catch {
53106
53076
  }
53107
53077
  }
53108
- await import_node_fs34.default.promises.rename(tmp, this.filePath);
53078
+ await import_node_fs35.default.promises.rename(tmp, this.filePath);
53109
53079
  }
53110
53080
  async clear() {
53111
53081
  try {
53112
- await import_node_fs34.default.promises.unlink(this.filePath);
53082
+ await import_node_fs35.default.promises.unlink(this.filePath);
53113
53083
  } catch (err) {
53114
53084
  const code = err?.code;
53115
53085
  if (code !== "ENOENT") throw err;
@@ -53204,9 +53174,9 @@ function escape(v2) {
53204
53174
  }
53205
53175
 
53206
53176
  // src/tunnel/frpc-binary.ts
53207
- var import_node_fs35 = __toESM(require("fs"), 1);
53177
+ var import_node_fs36 = __toESM(require("fs"), 1);
53208
53178
  var import_node_os13 = __toESM(require("os"), 1);
53209
- var import_node_path38 = __toESM(require("path"), 1);
53179
+ var import_node_path39 = __toESM(require("path"), 1);
53210
53180
  var import_node_child_process8 = require("child_process");
53211
53181
  var import_node_stream3 = require("stream");
53212
53182
  var import_promises5 = require("stream/promises");
@@ -53238,20 +53208,20 @@ function frpcDownloadUrl(version2, p2) {
53238
53208
  }
53239
53209
  async function ensureFrpcBinary(opts) {
53240
53210
  if (opts.override) {
53241
- if (!import_node_fs35.default.existsSync(opts.override)) {
53211
+ if (!import_node_fs36.default.existsSync(opts.override)) {
53242
53212
  throw new Error(`frpc binary not found at override path: ${opts.override}`);
53243
53213
  }
53244
53214
  return opts.override;
53245
53215
  }
53246
53216
  const version2 = opts.version ?? FRPC_VERSION;
53247
53217
  const platform = opts.platform ?? detectPlatform();
53248
- const binDir = import_node_path38.default.join(opts.dataDir, "bin");
53249
- 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 });
53250
53220
  cleanupStaleArtifacts(binDir);
53251
- const stableBin = import_node_path38.default.join(binDir, "frpc");
53252
- 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;
53253
53223
  const partialBin = `${stableBin}.partial`;
53254
- 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`);
53255
53225
  try {
53256
53226
  const url = frpcDownloadUrl(version2, platform);
53257
53227
  await downloadToFile(url, tarballPath, opts.fetchImpl);
@@ -53260,8 +53230,8 @@ async function ensureFrpcBinary(opts) {
53260
53230
  } else {
53261
53231
  await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
53262
53232
  }
53263
- import_node_fs35.default.chmodSync(partialBin, 493);
53264
- import_node_fs35.default.renameSync(partialBin, stableBin);
53233
+ import_node_fs36.default.chmodSync(partialBin, 493);
53234
+ import_node_fs36.default.renameSync(partialBin, stableBin);
53265
53235
  } finally {
53266
53236
  safeUnlink(tarballPath);
53267
53237
  safeUnlink(partialBin);
@@ -53271,15 +53241,15 @@ async function ensureFrpcBinary(opts) {
53271
53241
  function cleanupStaleArtifacts(binDir) {
53272
53242
  let entries;
53273
53243
  try {
53274
- entries = import_node_fs35.default.readdirSync(binDir);
53244
+ entries = import_node_fs36.default.readdirSync(binDir);
53275
53245
  } catch {
53276
53246
  return;
53277
53247
  }
53278
53248
  for (const name of entries) {
53279
53249
  if (name.endsWith(".partial") || name.startsWith("extract-")) {
53280
- const full = import_node_path38.default.join(binDir, name);
53250
+ const full = import_node_path39.default.join(binDir, name);
53281
53251
  try {
53282
- import_node_fs35.default.rmSync(full, { recursive: true, force: true });
53252
+ import_node_fs36.default.rmSync(full, { recursive: true, force: true });
53283
53253
  } catch {
53284
53254
  }
53285
53255
  }
@@ -53287,7 +53257,7 @@ function cleanupStaleArtifacts(binDir) {
53287
53257
  }
53288
53258
  function safeUnlink(p2) {
53289
53259
  try {
53290
- import_node_fs35.default.unlinkSync(p2);
53260
+ import_node_fs36.default.unlinkSync(p2);
53291
53261
  } catch {
53292
53262
  }
53293
53263
  }
@@ -53298,13 +53268,13 @@ async function downloadToFile(url, dest, fetchImpl) {
53298
53268
  if (!res.ok || !res.body) {
53299
53269
  throw new Error(`download failed: ${res.status} ${res.statusText}`);
53300
53270
  }
53301
- const out = import_node_fs35.default.createWriteStream(dest);
53271
+ const out = import_node_fs36.default.createWriteStream(dest);
53302
53272
  const nodeStream = import_node_stream3.Readable.fromWeb(res.body);
53303
53273
  await (0, import_promises5.pipeline)(nodeStream, out);
53304
53274
  }
53305
53275
  async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
53306
- const work = import_node_path38.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
53307
- 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 });
53308
53278
  try {
53309
53279
  await new Promise((resolve6, reject) => {
53310
53280
  const proc = (0, import_node_child_process8.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
@@ -53312,32 +53282,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
53312
53282
  proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
53313
53283
  });
53314
53284
  const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
53315
- const src = import_node_path38.default.join(work, dirName, "frpc");
53316
- 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)) {
53317
53287
  throw new Error(`frpc not found inside tarball at ${src}`);
53318
53288
  }
53319
- import_node_fs35.default.copyFileSync(src, destBin);
53289
+ import_node_fs36.default.copyFileSync(src, destBin);
53320
53290
  } finally {
53321
- import_node_fs35.default.rmSync(work, { recursive: true, force: true });
53291
+ import_node_fs36.default.rmSync(work, { recursive: true, force: true });
53322
53292
  }
53323
53293
  }
53324
53294
 
53325
53295
  // src/tunnel/frpc-process.ts
53326
- var import_node_fs36 = __toESM(require("fs"), 1);
53327
- 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);
53328
53298
  var import_node_child_process9 = require("child_process");
53329
53299
  function frpcPidFilePath(dataDir) {
53330
- return import_node_path39.default.join(dataDir, "frpc.pid");
53300
+ return import_node_path40.default.join(dataDir, "frpc.pid");
53331
53301
  }
53332
53302
  function writeFrpcPid(dataDir, pid) {
53333
53303
  try {
53334
- import_node_fs36.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
53304
+ import_node_fs37.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
53335
53305
  } catch {
53336
53306
  }
53337
53307
  }
53338
53308
  function clearFrpcPid(dataDir) {
53339
53309
  try {
53340
- import_node_fs36.default.unlinkSync(frpcPidFilePath(dataDir));
53310
+ import_node_fs37.default.unlinkSync(frpcPidFilePath(dataDir));
53341
53311
  } catch {
53342
53312
  }
53343
53313
  }
@@ -53353,7 +53323,7 @@ function defaultIsPidAlive(pid) {
53353
53323
  }
53354
53324
  function defaultReadPidFile(file) {
53355
53325
  try {
53356
- return import_node_fs36.default.readFileSync(file, "utf8");
53326
+ return import_node_fs37.default.readFileSync(file, "utf8");
53357
53327
  } catch {
53358
53328
  return null;
53359
53329
  }
@@ -53364,17 +53334,17 @@ function defaultKillPid(pid, signal) {
53364
53334
  } catch {
53365
53335
  }
53366
53336
  }
53367
- function defaultSleep(ms) {
53337
+ function defaultSleep2(ms) {
53368
53338
  return new Promise((r) => setTimeout(r, ms));
53369
53339
  }
53370
53340
  async function killStaleFrpc(deps) {
53371
53341
  const pidFile = frpcPidFilePath(deps.dataDir);
53372
- const tomlPath = import_node_path39.default.join(deps.dataDir, "frpc.toml");
53342
+ const tomlPath = import_node_path40.default.join(deps.dataDir, "frpc.toml");
53373
53343
  const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
53374
53344
  const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
53375
53345
  const killPid = deps.killPidImpl ?? defaultKillPid;
53376
53346
  const scanPids = deps.scanFrpcPidsImpl ?? ((tp) => defaultScanFrpcPidsByCmdline(tp, deps.logger));
53377
- const sleep2 = deps.sleepImpl ?? defaultSleep;
53347
+ const sleep2 = deps.sleepImpl ?? defaultSleep2;
53378
53348
  const victims = /* @__PURE__ */ new Set();
53379
53349
  const raw = readPidFile(pidFile);
53380
53350
  if (raw) {
@@ -53393,7 +53363,7 @@ async function killStaleFrpc(deps) {
53393
53363
  }
53394
53364
  if (victims.size === 0) {
53395
53365
  try {
53396
- import_node_fs36.default.unlinkSync(pidFile);
53366
+ import_node_fs37.default.unlinkSync(pidFile);
53397
53367
  } catch {
53398
53368
  }
53399
53369
  return;
@@ -53404,7 +53374,7 @@ async function killStaleFrpc(deps) {
53404
53374
  }
53405
53375
  await sleep2(deps.reapWaitMs ?? 300);
53406
53376
  try {
53407
- import_node_fs36.default.unlinkSync(pidFile);
53377
+ import_node_fs37.default.unlinkSync(pidFile);
53408
53378
  } catch {
53409
53379
  }
53410
53380
  }
@@ -53441,7 +53411,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
53441
53411
  var TunnelManager = class {
53442
53412
  constructor(deps) {
53443
53413
  this.deps = deps;
53444
- 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"));
53445
53415
  this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
53446
53416
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
53447
53417
  }
@@ -53568,7 +53538,7 @@ var TunnelManager = class {
53568
53538
  dataDir: this.deps.dataDir,
53569
53539
  override: this.deps.frpcBinaryOverride ?? void 0
53570
53540
  });
53571
- 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");
53572
53542
  const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto10.default.randomBytes(3).toString("hex")}`;
53573
53543
  const toml = buildFrpcToml({
53574
53544
  serverAddr: t.frpsHost,
@@ -53579,14 +53549,14 @@ var TunnelManager = class {
53579
53549
  localPort,
53580
53550
  logLevel: "info"
53581
53551
  });
53582
- await import_node_fs37.default.promises.writeFile(tomlPath, toml, { mode: 384 });
53552
+ await import_node_fs38.default.promises.writeFile(tomlPath, toml, { mode: 384 });
53583
53553
  const proc = (this.deps.spawnImpl ?? import_node_child_process10.spawn)(frpcBin, ["-c", tomlPath], {
53584
53554
  stdio: ["ignore", "pipe", "pipe"]
53585
53555
  });
53586
- const logDir = import_node_path40.default.join(this.deps.dataDir, "log");
53587
- import_node_fs37.default.mkdirSync(logDir, { recursive: true });
53588
- const logFilePath = import_node_path40.default.join(logDir, "frpc.log");
53589
- 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 });
53590
53560
  logStream.on("error", () => {
53591
53561
  });
53592
53562
  const tee = (chunk) => {
@@ -53669,27 +53639,27 @@ async function waitForFrpcReady(proc, timeoutMs) {
53669
53639
 
53670
53640
  // src/tunnel/device-key.ts
53671
53641
  var import_node_os14 = __toESM(require("os"), 1);
53672
- var import_node_path41 = __toESM(require("path"), 1);
53642
+ var import_node_path42 = __toESM(require("path"), 1);
53673
53643
  var import_node_crypto11 = __toESM(require("crypto"), 1);
53674
53644
  var DERIVE_SALT = "clawd-tunnel-device-v1";
53675
53645
  function deriveStableDeviceKey(opts = {}) {
53676
53646
  const hostname = opts.hostname ?? import_node_os14.default.hostname();
53677
53647
  const uid = opts.uid ?? (typeof import_node_os14.default.userInfo === "function" ? import_node_os14.default.userInfo().uid : 0);
53678
53648
  const home = opts.home ?? import_node_os14.default.homedir();
53679
- const defaultDataDir = import_node_path41.default.resolve(import_node_path41.default.join(home, ".clawd"));
53680
- 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;
53681
53651
  const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
53682
53652
  const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
53683
53653
  return import_node_crypto11.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
53684
53654
  }
53685
53655
 
53686
53656
  // src/auth-store.ts
53687
- var import_node_fs38 = __toESM(require("fs"), 1);
53688
- 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);
53689
53659
  var import_node_crypto12 = __toESM(require("crypto"), 1);
53690
53660
  var AUTH_FILE_NAME = "auth.json";
53691
53661
  function authFilePath(dataDir) {
53692
- return import_node_path42.default.join(dataDir, AUTH_FILE_NAME);
53662
+ return import_node_path43.default.join(dataDir, AUTH_FILE_NAME);
53693
53663
  }
53694
53664
  function loadOrCreateAuthFile(opts) {
53695
53665
  const file = authFilePath(opts.dataDir);
@@ -53725,7 +53695,7 @@ function defaultGenerateOwnerPrincipalId() {
53725
53695
  }
53726
53696
  function readAuthFile(file) {
53727
53697
  try {
53728
- const raw = import_node_fs38.default.readFileSync(file, "utf8");
53698
+ const raw = import_node_fs39.default.readFileSync(file, "utf8");
53729
53699
  const parsed = JSON.parse(raw);
53730
53700
  if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
53731
53701
  return null;
@@ -53745,25 +53715,25 @@ function readAuthFile(file) {
53745
53715
  }
53746
53716
  }
53747
53717
  function writeAuthFile(file, content) {
53748
- import_node_fs38.default.mkdirSync(import_node_path42.default.dirname(file), { recursive: true });
53749
- 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 });
53750
53720
  try {
53751
- import_node_fs38.default.chmodSync(file, 384);
53721
+ import_node_fs39.default.chmodSync(file, 384);
53752
53722
  } catch {
53753
53723
  }
53754
53724
  }
53755
53725
 
53756
53726
  // src/owner-profile.ts
53757
- var import_node_fs39 = __toESM(require("fs"), 1);
53727
+ var import_node_fs40 = __toESM(require("fs"), 1);
53758
53728
  var import_node_os15 = __toESM(require("os"), 1);
53759
- var import_node_path43 = __toESM(require("path"), 1);
53729
+ var import_node_path44 = __toESM(require("path"), 1);
53760
53730
  var PROFILE_FILENAME = "profile.json";
53761
53731
  function loadOwnerDisplayName(dataDir) {
53762
53732
  const fallback = import_node_os15.default.userInfo().username;
53763
- const profilePath = import_node_path43.default.join(dataDir, PROFILE_FILENAME);
53733
+ const profilePath = import_node_path44.default.join(dataDir, PROFILE_FILENAME);
53764
53734
  let raw;
53765
53735
  try {
53766
- raw = import_node_fs39.default.readFileSync(profilePath, "utf8");
53736
+ raw = import_node_fs40.default.readFileSync(profilePath, "utf8");
53767
53737
  } catch {
53768
53738
  return fallback;
53769
53739
  }
@@ -53786,18 +53756,18 @@ function loadOwnerDisplayName(dataDir) {
53786
53756
  }
53787
53757
 
53788
53758
  // src/feishu-auth/owner-identity-store.ts
53789
- var import_node_fs40 = __toESM(require("fs"), 1);
53790
- 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);
53791
53761
  var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
53792
53762
  var OwnerIdentityStore = class {
53793
53763
  file;
53794
53764
  constructor(dataDir) {
53795
- 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);
53796
53766
  }
53797
53767
  read() {
53798
53768
  let raw;
53799
53769
  try {
53800
- raw = import_node_fs40.default.readFileSync(this.file, "utf8");
53770
+ raw = import_node_fs41.default.readFileSync(this.file, "utf8");
53801
53771
  } catch {
53802
53772
  return null;
53803
53773
  }
@@ -53825,16 +53795,16 @@ var OwnerIdentityStore = class {
53825
53795
  };
53826
53796
  }
53827
53797
  write(record) {
53828
- import_node_fs40.default.mkdirSync(import_node_path44.default.dirname(this.file), { recursive: true });
53829
- 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 });
53830
53800
  try {
53831
- import_node_fs40.default.chmodSync(this.file, 384);
53801
+ import_node_fs41.default.chmodSync(this.file, 384);
53832
53802
  } catch {
53833
53803
  }
53834
53804
  }
53835
53805
  clear() {
53836
53806
  try {
53837
- import_node_fs40.default.unlinkSync(this.file);
53807
+ import_node_fs41.default.unlinkSync(this.file);
53838
53808
  } catch (err) {
53839
53809
  const code = err?.code;
53840
53810
  if (code !== "ENOENT") throw err;
@@ -53971,9 +53941,9 @@ var CentralClientError = class extends Error {
53971
53941
  code;
53972
53942
  cause;
53973
53943
  };
53974
- async function centralRequest(opts, path74, init) {
53944
+ async function centralRequest(opts, path75, init) {
53975
53945
  const f = opts.fetchImpl ?? globalThis.fetch;
53976
- const url = `${opts.api.replace(/\/+$/, "")}${path74}`;
53946
+ const url = `${opts.api.replace(/\/+$/, "")}${path75}`;
53977
53947
  const ctrl = new AbortController();
53978
53948
  const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
53979
53949
  let res;
@@ -54167,8 +54137,8 @@ function verifyConnectToken(args) {
54167
54137
  }
54168
54138
 
54169
54139
  // src/feishu-auth/server-key.ts
54170
- var fs50 = __toESM(require("fs"), 1);
54171
- var path53 = __toESM(require("path"), 1);
54140
+ var fs51 = __toESM(require("fs"), 1);
54141
+ var path54 = __toESM(require("path"), 1);
54172
54142
  var FILE_NAME2 = "server-signing-key.json";
54173
54143
  var ServerKeyStore = class {
54174
54144
  constructor(dataDir) {
@@ -54176,12 +54146,12 @@ var ServerKeyStore = class {
54176
54146
  }
54177
54147
  dataDir;
54178
54148
  filePath() {
54179
- return path53.join(this.dataDir, FILE_NAME2);
54149
+ return path54.join(this.dataDir, FILE_NAME2);
54180
54150
  }
54181
54151
  /** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
54182
54152
  read() {
54183
54153
  try {
54184
- const raw = fs50.readFileSync(this.filePath(), "utf8");
54154
+ const raw = fs51.readFileSync(this.filePath(), "utf8");
54185
54155
  const parsed = JSON.parse(raw);
54186
54156
  if (typeof parsed.publicKeyPem === "string" && parsed.publicKeyPem.includes("PUBLIC KEY")) {
54187
54157
  return parsed.publicKeyPem;
@@ -54196,12 +54166,12 @@ var ServerKeyStore = class {
54196
54166
  publicKeyPem,
54197
54167
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
54198
54168
  };
54199
- fs50.mkdirSync(this.dataDir, { recursive: true });
54200
- 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 });
54201
54171
  }
54202
54172
  clear() {
54203
54173
  try {
54204
- fs50.unlinkSync(this.filePath());
54174
+ fs51.unlinkSync(this.filePath());
54205
54175
  } catch {
54206
54176
  }
54207
54177
  }
@@ -54214,12 +54184,12 @@ init_protocol();
54214
54184
  init_protocol();
54215
54185
 
54216
54186
  // src/session/fork.ts
54217
- var import_node_fs41 = __toESM(require("fs"), 1);
54187
+ var import_node_fs42 = __toESM(require("fs"), 1);
54218
54188
  var import_node_os16 = __toESM(require("os"), 1);
54219
- var import_node_path45 = __toESM(require("path"), 1);
54189
+ var import_node_path46 = __toESM(require("path"), 1);
54220
54190
  init_claude_history();
54221
54191
  function readJsonlEntries(file) {
54222
- const raw = import_node_fs41.default.readFileSync(file, "utf8");
54192
+ const raw = import_node_fs42.default.readFileSync(file, "utf8");
54223
54193
  const out = [];
54224
54194
  for (const line of raw.split("\n")) {
54225
54195
  const t = line.trim();
@@ -54232,19 +54202,19 @@ function readJsonlEntries(file) {
54232
54202
  return out;
54233
54203
  }
54234
54204
  function forkSession(input) {
54235
- const baseDir = input.baseDir ?? import_node_path45.default.join(import_node_os16.default.homedir(), ".claude");
54236
- let projectDir = import_node_path45.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
54237
- let sourceFile = import_node_path45.default.join(projectDir, `${input.toolSessionId}.jsonl`);
54238
- 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)) {
54239
54209
  const found = findTranscriptByToolSessionId(
54240
- import_node_path45.default.join(baseDir, "projects"),
54210
+ import_node_path46.default.join(baseDir, "projects"),
54241
54211
  input.toolSessionId
54242
54212
  );
54243
54213
  if (!found) {
54244
54214
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
54245
54215
  }
54246
54216
  sourceFile = found;
54247
- projectDir = import_node_path45.default.dirname(found);
54217
+ projectDir = import_node_path46.default.dirname(found);
54248
54218
  }
54249
54219
  const entries = readJsonlEntries(sourceFile);
54250
54220
  const mainEntries = entries.filter((e) => e.isSidechain !== true);
@@ -54273,9 +54243,9 @@ function forkSession(input) {
54273
54243
  }
54274
54244
  forkedLines.push(JSON.stringify(forked));
54275
54245
  }
54276
- const forkedFilePath = import_node_path45.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
54277
- import_node_fs41.default.mkdirSync(projectDir, { recursive: true });
54278
- 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 });
54279
54249
  return { forkedToolSessionId, forkedFilePath };
54280
54250
  }
54281
54251
 
@@ -54687,7 +54657,7 @@ function buildPermissionHandlers(deps) {
54687
54657
  }
54688
54658
 
54689
54659
  // src/handlers/history.ts
54690
- var path56 = __toESM(require("path"), 1);
54660
+ var path57 = __toESM(require("path"), 1);
54691
54661
  init_protocol();
54692
54662
 
54693
54663
  // src/session/recent-dirs.ts
@@ -54705,7 +54675,7 @@ function listRecentDirs(store, limit = 50) {
54705
54675
  }
54706
54676
 
54707
54677
  // src/permission/persona-paths.ts
54708
- var path55 = __toESM(require("path"), 1);
54678
+ var path56 = __toESM(require("path"), 1);
54709
54679
  function getAllowedPersonaIds(grants, action) {
54710
54680
  const ids = /* @__PURE__ */ new Set();
54711
54681
  for (const g2 of grants) {
@@ -54718,42 +54688,42 @@ function getAllowedPersonaIds(grants, action) {
54718
54688
  return ids;
54719
54689
  }
54720
54690
  function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
54721
- const target = path55.resolve(absPath);
54691
+ const target = path56.resolve(absPath);
54722
54692
  if (userWorkDir) {
54723
- const u = path55.resolve(userWorkDir);
54724
- const usep = u.endsWith(path55.sep) ? "" : path55.sep;
54693
+ const u = path56.resolve(userWorkDir);
54694
+ const usep = u.endsWith(path56.sep) ? "" : path56.sep;
54725
54695
  if (target === u || target.startsWith(u + usep)) return true;
54726
54696
  }
54727
- const root = path55.resolve(personaRoot);
54728
- const sep3 = root.endsWith(path55.sep) ? "" : path55.sep;
54697
+ const root = path56.resolve(personaRoot);
54698
+ const sep3 = root.endsWith(path56.sep) ? "" : path56.sep;
54729
54699
  if (!target.startsWith(root + sep3)) return false;
54730
- const rel = path55.relative(root, target);
54700
+ const rel = path56.relative(root, target);
54731
54701
  if (!rel || rel.startsWith("..")) return false;
54732
- const personaId = rel.split(path55.sep)[0];
54702
+ const personaId = rel.split(path56.sep)[0];
54733
54703
  if (!personaId) return false;
54734
54704
  const allowed = getAllowedPersonaIds(grants, action);
54735
54705
  if (allowed === "*") return true;
54736
54706
  return allowed.has(personaId);
54737
54707
  }
54738
54708
  function personaIdFromPath(absPath, personaRoot) {
54739
- const root = path55.resolve(personaRoot);
54740
- const target = path55.resolve(absPath);
54741
- 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;
54742
54712
  if (!target.startsWith(root + sep3)) return null;
54743
- const rel = path55.relative(root, target);
54713
+ const rel = path56.relative(root, target);
54744
54714
  if (!rel || rel.startsWith("..")) return null;
54745
- const id = rel.split(path55.sep)[0];
54715
+ const id = rel.split(path56.sep)[0];
54746
54716
  return id || null;
54747
54717
  }
54748
54718
  function isPathWithin(dir, absPath) {
54749
- const d = path55.resolve(dir);
54750
- const t = path55.resolve(absPath);
54751
- 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;
54752
54722
  return t === d || t.startsWith(d + sep3);
54753
54723
  }
54754
54724
  function isPathInGuestBoundary(personaRoot, personaId, userWorkDir, absPath) {
54755
54725
  if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
54756
- return personaIdFromPath(path55.resolve(absPath), personaRoot) === personaId;
54726
+ return personaIdFromPath(path56.resolve(absPath), personaRoot) === personaId;
54757
54727
  }
54758
54728
 
54759
54729
  // src/handlers/history.ts
@@ -54805,7 +54775,7 @@ function buildHistoryHandlers(deps) {
54805
54775
  if (!pid) return false;
54806
54776
  return isGuestPathAllowed(
54807
54777
  ctx.grants,
54808
- path56.join(personaRoot, pid),
54778
+ path57.join(personaRoot, pid),
54809
54779
  personaRoot,
54810
54780
  "read",
54811
54781
  userWorkDir
@@ -54817,7 +54787,7 @@ function buildHistoryHandlers(deps) {
54817
54787
  };
54818
54788
  const list = async (frame, _client, ctx) => {
54819
54789
  const args = HistoryListArgs.parse(frame);
54820
- assertGuestPath(ctx, path56.resolve(args.projectPath), personaRoot, "history:list");
54790
+ assertGuestPath(ctx, path57.resolve(args.projectPath), personaRoot, "history:list");
54821
54791
  const sessions = await history.listSessions(args);
54822
54792
  return { response: { type: "history:list", sessions } };
54823
54793
  };
@@ -54850,13 +54820,13 @@ function buildHistoryHandlers(deps) {
54850
54820
  };
54851
54821
  const subagents = async (frame, _client, ctx) => {
54852
54822
  const args = HistorySubagentsArgs.parse(frame);
54853
- assertGuestPath(ctx, path56.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
54823
+ assertGuestPath(ctx, path57.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
54854
54824
  const subs = await history.listSubagents(args);
54855
54825
  return { response: { type: "history:subagents", subagents: subs } };
54856
54826
  };
54857
54827
  const subagentRead = async (frame, _client, ctx) => {
54858
54828
  const args = HistorySubagentReadArgs.parse(frame);
54859
- assertGuestPath(ctx, path56.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
54829
+ assertGuestPath(ctx, path57.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
54860
54830
  const res = await history.readSubagent(args);
54861
54831
  return { response: { type: "history:subagent-read", ...res } };
54862
54832
  };
@@ -54865,7 +54835,7 @@ function buildHistoryHandlers(deps) {
54865
54835
  if (ctx?.principal.kind === "guest" && personaRoot) {
54866
54836
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
54867
54837
  const filtered = dirs.filter(
54868
- (d) => isGuestPathAllowed(ctx.grants, path56.resolve(d.cwd), personaRoot, "read", userWorkDir)
54838
+ (d) => isGuestPathAllowed(ctx.grants, path57.resolve(d.cwd), personaRoot, "read", userWorkDir)
54869
54839
  );
54870
54840
  return { response: { type: "history:recentDirs", dirs: filtered } };
54871
54841
  }
@@ -54882,7 +54852,7 @@ function buildHistoryHandlers(deps) {
54882
54852
  }
54883
54853
 
54884
54854
  // src/handlers/workspace.ts
54885
- var path57 = __toESM(require("path"), 1);
54855
+ var path58 = __toESM(require("path"), 1);
54886
54856
  var os16 = __toESM(require("os"), 1);
54887
54857
  init_protocol();
54888
54858
  init_protocol();
@@ -54924,22 +54894,22 @@ function buildWorkspaceHandlers(deps) {
54924
54894
  const args = WorkspaceListArgs.parse(frame);
54925
54895
  const isGuest = ctx?.principal.kind === "guest";
54926
54896
  const fallbackCwd = isGuest && personaRoot ? personaRoot : os16.homedir();
54927
- const resolvedCwd = path57.resolve(args.cwd ?? fallbackCwd);
54928
- 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;
54929
54899
  assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
54930
54900
  const res = workspace.list({ ...args, cwd: resolvedCwd });
54931
54901
  return { response: { type: "workspace:list", ...res } };
54932
54902
  };
54933
54903
  const read = async (frame, _client, ctx) => {
54934
54904
  const args = WorkspaceReadArgs.parse(frame);
54935
- 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);
54936
54906
  assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
54937
54907
  const res = workspace.read(args);
54938
54908
  return { response: { type: "workspace:read", ...res } };
54939
54909
  };
54940
54910
  const skillsList = async (frame, _client, ctx) => {
54941
54911
  const args = SkillsListArgs.parse(frame);
54942
- const cwdAbs = path57.resolve(args.cwd);
54912
+ const cwdAbs = path58.resolve(args.cwd);
54943
54913
  assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
54944
54914
  const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
54945
54915
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -54951,7 +54921,7 @@ function buildWorkspaceHandlers(deps) {
54951
54921
  };
54952
54922
  const agentsList = async (frame, _client, ctx) => {
54953
54923
  const args = AgentsListArgs.parse(frame);
54954
- const cwdAbs = path57.resolve(args.cwd);
54924
+ const cwdAbs = path58.resolve(args.cwd);
54955
54925
  assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
54956
54926
  if (args.tool === "codex") {
54957
54927
  return { response: { type: "agents:list", agents: [] } };
@@ -54973,20 +54943,20 @@ function buildWorkspaceHandlers(deps) {
54973
54943
  }
54974
54944
 
54975
54945
  // src/handlers/git.ts
54976
- var path59 = __toESM(require("path"), 1);
54946
+ var path60 = __toESM(require("path"), 1);
54977
54947
  init_protocol();
54978
54948
  init_protocol();
54979
54949
 
54980
54950
  // src/workspace/git.ts
54981
54951
  var import_node_child_process11 = require("child_process");
54982
- var import_node_fs42 = __toESM(require("fs"), 1);
54983
- 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);
54984
54954
  var import_node_util = require("util");
54985
54955
  var pexec = (0, import_node_util.promisify)(import_node_child_process11.execFile);
54986
54956
  function normalizePath(p2) {
54987
- const resolved = import_node_path46.default.resolve(p2);
54957
+ const resolved = import_node_path47.default.resolve(p2);
54988
54958
  try {
54989
- return import_node_fs42.default.realpathSync(resolved);
54959
+ return import_node_fs43.default.realpathSync(resolved);
54990
54960
  } catch {
54991
54961
  return resolved;
54992
54962
  }
@@ -55060,7 +55030,7 @@ async function listGitBranches(cwd) {
55060
55030
  function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
55061
55031
  if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
55062
55032
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
55063
- if (!isGuestPathAllowed(ctx.grants, path59.resolve(cwd), personaRoot, "read", userWorkDir)) {
55033
+ if (!isGuestPathAllowed(ctx.grants, path60.resolve(cwd), personaRoot, "read", userWorkDir)) {
55064
55034
  throw new ClawdError(
55065
55035
  ERROR_CODES.UNAUTHORIZED,
55066
55036
  `guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
@@ -55805,7 +55775,7 @@ function buildPersonaHandlers(deps) {
55805
55775
  }
55806
55776
 
55807
55777
  // src/handlers/attachment.ts
55808
- var import_node_path47 = __toESM(require("path"), 1);
55778
+ var import_node_path48 = __toESM(require("path"), 1);
55809
55779
  init_protocol();
55810
55780
  init_protocol();
55811
55781
  var DEFAULT_TTL_SECONDS = 24 * 3600;
@@ -55821,10 +55791,8 @@ function assertGuestAttachmentPath(ctx, absPath, personaRoot, method, usersRoot)
55821
55791
  }
55822
55792
  function buildAttachmentHandlers(deps) {
55823
55793
  function guardAttachmentPath(ctx, sessionId, candidateAbs, method, nonGuestPolicy) {
55824
- if (ctx?.principal.kind === "owner") return;
55825
55794
  const scope = deps.getSessionScope?.(sessionId);
55826
- if (!scope) return;
55827
- if (scope.kind === "persona" && scope.mode === "guest") {
55795
+ if (scope?.kind === "persona" && scope.mode === "guest") {
55828
55796
  if (!deps.personaRoot) return;
55829
55797
  const userWorkDir = deps.usersRoot ? deriveUserWorkDir(scope.capId, deps.usersRoot) : void 0;
55830
55798
  if (!isPathInGuestBoundary(deps.personaRoot, scope.personaId, userWorkDir, candidateAbs)) {
@@ -55835,6 +55803,8 @@ function buildAttachmentHandlers(deps) {
55835
55803
  }
55836
55804
  return;
55837
55805
  }
55806
+ if (ctx?.principal.kind === "owner") return;
55807
+ if (!scope) return;
55838
55808
  if (nonGuestPolicy === "group-acl") return;
55839
55809
  const cwd = deps.sessionStore?.read(sessionId)?.cwd;
55840
55810
  if (!cwd) return;
@@ -55885,12 +55855,12 @@ function buildAttachmentHandlers(deps) {
55885
55855
  `session ${args.sessionId} scope unresolved`
55886
55856
  );
55887
55857
  }
55888
- const cwdAbs = import_node_path47.default.resolve(sessionFile.cwd);
55889
- 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);
55890
55860
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
55891
55861
  const entries = deps.groupFileStore.list(scope, args.sessionId);
55892
55862
  const entry = entries.find((e) => {
55893
- 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);
55894
55864
  return storedAbs === candidateAbs && !e.stale;
55895
55865
  });
55896
55866
  if (!entry) {
@@ -55915,7 +55885,7 @@ function buildAttachmentHandlers(deps) {
55915
55885
  if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
55916
55886
  const f = deps.sessionStore.read(sessionId);
55917
55887
  if (!f) return;
55918
- 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);
55919
55889
  }
55920
55890
  const groupAdd = async (frame, _client, ctx) => {
55921
55891
  if (!deps.groupFileStore || !deps.getSessionScope) {
@@ -55930,10 +55900,10 @@ function buildAttachmentHandlers(deps) {
55930
55900
  if (!scope) {
55931
55901
  throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
55932
55902
  }
55933
- const cwdAbs = import_node_path47.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
55934
- 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);
55935
55905
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
55936
- const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
55906
+ const from = args.origin === "agent" ? "agent" : ctx?.principal.kind === "owner" ? "owner" : "agent";
55937
55907
  const size = 0;
55938
55908
  const entry = deps.groupFileStore.upsert(scope, args.sessionId, {
55939
55909
  relPath: args.relPath,
@@ -55990,19 +55960,19 @@ function buildAttachmentHandlers(deps) {
55990
55960
 
55991
55961
  // src/handlers/extension.ts
55992
55962
  var import_promises9 = __toESM(require("fs/promises"), 1);
55993
- var import_node_path52 = __toESM(require("path"), 1);
55963
+ var import_node_path53 = __toESM(require("path"), 1);
55994
55964
  init_protocol();
55995
55965
 
55996
55966
  // src/extension/bundle-zip.ts
55997
55967
  var import_promises6 = __toESM(require("fs/promises"), 1);
55998
- var import_node_path48 = __toESM(require("path"), 1);
55968
+ var import_node_path49 = __toESM(require("path"), 1);
55999
55969
  var import_node_crypto14 = __toESM(require("crypto"), 1);
56000
55970
  var import_jszip2 = __toESM(require_lib3(), 1);
56001
55971
  async function bundleExtensionDir(dir) {
56002
55972
  const entries = await listFilesSorted(dir);
56003
55973
  const zip = new import_jszip2.default();
56004
55974
  for (const rel of entries) {
56005
- const abs = import_node_path48.default.join(dir, rel);
55975
+ const abs = import_node_path49.default.join(dir, rel);
56006
55976
  const content = await import_promises6.default.readFile(abs);
56007
55977
  zip.file(rel, content, { date: FIXED_DATE });
56008
55978
  }
@@ -56023,7 +55993,7 @@ async function listFilesSorted(rootDir) {
56023
55993
  return out;
56024
55994
  }
56025
55995
  async function walk(absRoot, relPrefix, out) {
56026
- const dirAbs = import_node_path48.default.join(absRoot, relPrefix);
55996
+ const dirAbs = import_node_path49.default.join(absRoot, relPrefix);
56027
55997
  const entries = await import_promises6.default.readdir(dirAbs, { withFileTypes: true });
56028
55998
  for (const e of entries) {
56029
55999
  if (IGNORE_BASENAMES.has(e.name)) continue;
@@ -56077,25 +56047,25 @@ function computePublishCheck(args) {
56077
56047
 
56078
56048
  // src/extension/install-flow.ts
56079
56049
  var import_promises7 = __toESM(require("fs/promises"), 1);
56080
- var import_node_path50 = __toESM(require("path"), 1);
56050
+ var import_node_path51 = __toESM(require("path"), 1);
56081
56051
  var import_node_os19 = __toESM(require("os"), 1);
56082
56052
  var import_node_crypto15 = __toESM(require("crypto"), 1);
56083
56053
  var import_jszip3 = __toESM(require_lib3(), 1);
56084
56054
 
56085
56055
  // src/extension/paths.ts
56086
56056
  var import_node_os18 = __toESM(require("os"), 1);
56087
- var import_node_path49 = __toESM(require("path"), 1);
56057
+ var import_node_path50 = __toESM(require("path"), 1);
56088
56058
  function clawdHomeRoot(override) {
56089
- 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");
56090
56060
  }
56091
56061
  function extensionsRoot(override) {
56092
- return import_node_path49.default.join(clawdHomeRoot(override), "extensions");
56062
+ return import_node_path50.default.join(clawdHomeRoot(override), "extensions");
56093
56063
  }
56094
56064
  function publishedChannelsFile(override) {
56095
- return import_node_path49.default.join(clawdHomeRoot(override), "extensions-published.json");
56065
+ return import_node_path50.default.join(clawdHomeRoot(override), "extensions-published.json");
56096
56066
  }
56097
56067
  function bundleCacheRoot(override) {
56098
- return import_node_path49.default.join(clawdHomeRoot(override), "extension-bundles");
56068
+ return import_node_path50.default.join(clawdHomeRoot(override), "extension-bundles");
56099
56069
  }
56100
56070
 
56101
56071
  // src/extension/install-flow.ts
@@ -56122,7 +56092,7 @@ async function installFromChannel(args, deps) {
56122
56092
  throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
56123
56093
  }
56124
56094
  for (const name of Object.keys(zip.files)) {
56125
- if (name.includes("..") || name.startsWith("/") || import_node_path50.default.isAbsolute(name)) {
56095
+ if (name.includes("..") || name.startsWith("/") || import_node_path51.default.isAbsolute(name)) {
56126
56096
  throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
56127
56097
  }
56128
56098
  }
@@ -56154,7 +56124,7 @@ async function installFromChannel(args, deps) {
56154
56124
  );
56155
56125
  }
56156
56126
  const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
56157
- const destDir = import_node_path50.default.join(deps.extensionsRoot, localExtId);
56127
+ const destDir = import_node_path51.default.join(deps.extensionsRoot, localExtId);
56158
56128
  let destExists = false;
56159
56129
  try {
56160
56130
  await import_promises7.default.access(destDir);
@@ -56168,16 +56138,16 @@ async function installFromChannel(args, deps) {
56168
56138
  );
56169
56139
  }
56170
56140
  const stage = await import_promises7.default.mkdtemp(
56171
- 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}-`)
56172
56142
  );
56173
56143
  try {
56174
56144
  for (const [name, entry] of Object.entries(zip.files)) {
56175
- const dest = import_node_path50.default.join(stage, name);
56145
+ const dest = import_node_path51.default.join(stage, name);
56176
56146
  if (entry.dir) {
56177
56147
  await import_promises7.default.mkdir(dest, { recursive: true });
56178
56148
  continue;
56179
56149
  }
56180
- 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 });
56181
56151
  if (name === "manifest.json") {
56182
56152
  const rewritten = { ...parsed.data, id: localExtId };
56183
56153
  await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -56198,7 +56168,7 @@ async function installFromChannel(args, deps) {
56198
56168
 
56199
56169
  // src/extension/update-flow.ts
56200
56170
  var import_promises8 = __toESM(require("fs/promises"), 1);
56201
- var import_node_path51 = __toESM(require("path"), 1);
56171
+ var import_node_path52 = __toESM(require("path"), 1);
56202
56172
  var import_node_os20 = __toESM(require("os"), 1);
56203
56173
  var import_node_crypto16 = __toESM(require("crypto"), 1);
56204
56174
  var import_jszip4 = __toESM(require_lib3(), 1);
@@ -56215,11 +56185,11 @@ async function updateFromChannel(args, deps) {
56215
56185
  channelRef.extId,
56216
56186
  channelRef.ownerPrincipalId
56217
56187
  );
56218
- const liveDir = import_node_path51.default.join(deps.extensionsRoot, localExtId);
56188
+ const liveDir = import_node_path52.default.join(deps.extensionsRoot, localExtId);
56219
56189
  const prevDir = `${liveDir}.prev`;
56220
56190
  let existingVersion;
56221
56191
  try {
56222
- 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");
56223
56193
  const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
56224
56194
  if (!parsed2.success) {
56225
56195
  throw new UpdateError(
@@ -56252,7 +56222,7 @@ async function updateFromChannel(args, deps) {
56252
56222
  throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
56253
56223
  }
56254
56224
  for (const name of Object.keys(zip.files)) {
56255
- if (name.includes("..") || name.startsWith("/") || import_node_path51.default.isAbsolute(name)) {
56225
+ if (name.includes("..") || name.startsWith("/") || import_node_path52.default.isAbsolute(name)) {
56256
56226
  throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
56257
56227
  }
56258
56228
  }
@@ -56287,16 +56257,16 @@ async function updateFromChannel(args, deps) {
56287
56257
  await import_promises8.default.rm(prevDir, { recursive: true, force: true });
56288
56258
  await import_promises8.default.rename(liveDir, prevDir);
56289
56259
  const stage = await import_promises8.default.mkdtemp(
56290
- 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}-`)
56291
56261
  );
56292
56262
  try {
56293
56263
  for (const [name, entry] of Object.entries(zip.files)) {
56294
- const dest = import_node_path51.default.join(stage, name);
56264
+ const dest = import_node_path52.default.join(stage, name);
56295
56265
  if (entry.dir) {
56296
56266
  await import_promises8.default.mkdir(dest, { recursive: true });
56297
56267
  continue;
56298
56268
  }
56299
- 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 });
56300
56270
  if (name === "manifest.json") {
56301
56271
  const rewritten = { ...parsed.data, id: localExtId };
56302
56272
  await import_promises8.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -56389,7 +56359,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
56389
56359
  );
56390
56360
  }
56391
56361
  }
56392
- const manifestPath = import_node_path52.default.join(root, extId, "manifest.json");
56362
+ const manifestPath = import_node_path53.default.join(root, extId, "manifest.json");
56393
56363
  const manifest = await readManifest(root, extId);
56394
56364
  const next = { ...manifest, version: newVersion };
56395
56365
  const tmp = `${manifestPath}.tmp`;
@@ -56397,7 +56367,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
56397
56367
  await import_promises9.default.rename(tmp, manifestPath);
56398
56368
  }
56399
56369
  async function readManifest(root, extId) {
56400
- const file = import_node_path52.default.join(root, extId, "manifest.json");
56370
+ const file = import_node_path53.default.join(root, extId, "manifest.json");
56401
56371
  let raw;
56402
56372
  try {
56403
56373
  raw = await import_promises9.default.readFile(file, "utf8");
@@ -56488,7 +56458,7 @@ function buildExtensionHandlers(deps) {
56488
56458
  };
56489
56459
  async function buildSnapshotMeta(extId) {
56490
56460
  const manifest = await readManifest(deps.root, extId);
56491
- 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));
56492
56462
  return { manifest, contentHash: sha256, buffer };
56493
56463
  }
56494
56464
  const publish = async (frame, _client, ctx) => {
@@ -56669,9 +56639,9 @@ function buildExtensionHandlers(deps) {
56669
56639
  }
56670
56640
 
56671
56641
  // src/app-builder/project-store.ts
56672
- var import_node_fs43 = require("fs");
56642
+ var import_node_fs44 = require("fs");
56673
56643
  var import_node_child_process12 = require("child_process");
56674
- var import_node_path53 = require("path");
56644
+ var import_node_path54 = require("path");
56675
56645
  init_protocol();
56676
56646
  var PROJECTS_DIR = "projects";
56677
56647
  var META_FILE = ".clawd-project.json";
@@ -56685,19 +56655,19 @@ var ProjectStore = class {
56685
56655
  root;
56686
56656
  /** projects/<name>/.clawd-project.json 路径 */
56687
56657
  metaPath(name) {
56688
- return (0, import_node_path53.join)(this.projectsRoot(), name, META_FILE);
56658
+ return (0, import_node_path54.join)(this.projectsRoot(), name, META_FILE);
56689
56659
  }
56690
56660
  /** projects/<name>/ 目录路径(cwd 用) */
56691
56661
  projectDir(name) {
56692
- return (0, import_node_path53.join)(this.projectsRoot(), name);
56662
+ return (0, import_node_path54.join)(this.projectsRoot(), name);
56693
56663
  }
56694
56664
  projectsRoot() {
56695
- return (0, import_node_path53.join)(this.root, PROJECTS_DIR);
56665
+ return (0, import_node_path54.join)(this.root, PROJECTS_DIR);
56696
56666
  }
56697
56667
  async list() {
56698
56668
  let entries;
56699
56669
  try {
56700
- entries = await import_node_fs43.promises.readdir(this.projectsRoot());
56670
+ entries = await import_node_fs44.promises.readdir(this.projectsRoot());
56701
56671
  } catch (err) {
56702
56672
  if (err.code === "ENOENT") return [];
56703
56673
  throw err;
@@ -56705,7 +56675,7 @@ var ProjectStore = class {
56705
56675
  const out = [];
56706
56676
  for (const name of entries) {
56707
56677
  try {
56708
- 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");
56709
56679
  const json = JSON.parse(raw);
56710
56680
  let migrated = false;
56711
56681
  if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
@@ -56716,7 +56686,7 @@ var ProjectStore = class {
56716
56686
  if (parsed.success) {
56717
56687
  out.push(parsed.data);
56718
56688
  if (migrated) {
56719
- 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(() => {
56720
56690
  });
56721
56691
  }
56722
56692
  }
@@ -56760,8 +56730,8 @@ var ProjectStore = class {
56760
56730
  throw new Error(`invalid name "${name}": ${validated.error.message}`);
56761
56731
  }
56762
56732
  const dir = this.projectDir(name);
56763
- await import_node_fs43.promises.mkdir(dir, { recursive: true });
56764
- 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");
56765
56735
  return meta;
56766
56736
  }
56767
56737
  /**
@@ -56804,7 +56774,7 @@ var ProjectStore = class {
56804
56774
  }
56805
56775
  async delete(name) {
56806
56776
  const dir = this.projectDir(name);
56807
- await import_node_fs43.promises.rm(dir, { recursive: true, force: true });
56777
+ await import_node_fs44.promises.rm(dir, { recursive: true, force: true });
56808
56778
  }
56809
56779
  async updatePort(name, newPort) {
56810
56780
  if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
@@ -56820,7 +56790,7 @@ var ProjectStore = class {
56820
56790
  throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
56821
56791
  }
56822
56792
  const updated = { ...target, port: newPort };
56823
- 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");
56824
56794
  return updated;
56825
56795
  }
56826
56796
  /**
@@ -56837,7 +56807,7 @@ var ProjectStore = class {
56837
56807
  if (!validated.success) {
56838
56808
  throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
56839
56809
  }
56840
- 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");
56841
56811
  return validated.data;
56842
56812
  }
56843
56813
  /**
@@ -56858,7 +56828,7 @@ var ProjectStore = class {
56858
56828
  if (!validated.success) {
56859
56829
  throw new Error(`invalid publishJob: ${validated.error.message}`);
56860
56830
  }
56861
- 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");
56862
56832
  return validated.data;
56863
56833
  }
56864
56834
  /** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
@@ -56873,7 +56843,7 @@ var ProjectStore = class {
56873
56843
  if (!validated.success) {
56874
56844
  throw new Error(`failed to clear publishJob: ${validated.error.message}`);
56875
56845
  }
56876
- 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");
56877
56847
  return validated.data;
56878
56848
  }
56879
56849
  };
@@ -56994,8 +56964,8 @@ var PublishJobRegistry = class {
56994
56964
 
56995
56965
  // src/app-builder/publish-job-runner.ts
56996
56966
  var import_node_child_process14 = require("child_process");
56997
- var import_node_fs44 = require("fs");
56998
- var import_node_path54 = require("path");
56967
+ var import_node_fs45 = require("fs");
56968
+ var import_node_path55 = require("path");
56999
56969
 
57000
56970
  // src/app-builder/publish-stage-parser.ts
57001
56971
  var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
@@ -57027,10 +56997,10 @@ async function startPublishJob(deps, args) {
57027
56997
  return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
57028
56998
  }
57029
56999
  const projDir = projectDir(args.name);
57030
- const logPath = (0, import_node_path54.join)(projDir, ".publish.log");
57000
+ const logPath = (0, import_node_path55.join)(projDir, ".publish.log");
57031
57001
  let logStream = null;
57032
57002
  try {
57033
- logStream = (0, import_node_fs44.createWriteStream)(logPath, { flags: "w" });
57003
+ logStream = (0, import_node_fs45.createWriteStream)(logPath, { flags: "w" });
57034
57004
  } catch {
57035
57005
  logStream = null;
57036
57006
  }
@@ -57287,8 +57257,8 @@ async function recoverInterruptedJobs(deps) {
57287
57257
 
57288
57258
  // src/handlers/app-builder.ts
57289
57259
  init_protocol();
57290
- var import_node_path55 = require("path");
57291
- var import_node_fs45 = require("fs");
57260
+ var import_node_path56 = require("path");
57261
+ var import_node_fs46 = require("fs");
57292
57262
  var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
57293
57263
  var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
57294
57264
  async function recoverInterruptedPublishJobs(store, logger) {
@@ -57369,7 +57339,7 @@ function buildAppBuilderHandlers(deps) {
57369
57339
  async function listAllUsersProjects() {
57370
57340
  if (!deps.usersRoot || !deps.getStore) return [];
57371
57341
  const getStore = deps.getStore;
57372
- const userIds = await import_node_fs45.promises.readdir(deps.usersRoot).catch(() => []);
57342
+ const userIds = await import_node_fs46.promises.readdir(deps.usersRoot).catch(() => []);
57373
57343
  const perUser = await Promise.all(
57374
57344
  userIds.map((uid) => getStore(uid).list().catch(() => []))
57375
57345
  );
@@ -57388,12 +57358,14 @@ function buildAppBuilderHandlers(deps) {
57388
57358
  }
57389
57359
  const listProjects = async (frame, _client, ctx) => {
57390
57360
  const f = frame;
57361
+ const sessionId = typeof f.sessionId === "string" ? f.sessionId : void 0;
57391
57362
  let metas;
57392
- 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) {
57393
57366
  metas = await listAllUsersProjects();
57394
57367
  } else {
57395
- const sessionId = typeof f.sessionId === "string" ? f.sessionId : void 0;
57396
- metas = await resolveStore({ ctx, sessionId }).list();
57368
+ metas = await resolveStore({ ctx }).list();
57397
57369
  }
57398
57370
  return { response: { projects: metas.map(toProjectWithStatus) } };
57399
57371
  };
@@ -57445,8 +57417,8 @@ function buildAppBuilderHandlers(deps) {
57445
57417
  const project = await userStore.create(f.name, reservedPorts);
57446
57418
  try {
57447
57419
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
57448
- const templateSrcDir = (0, import_node_path55.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
57449
- 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");
57450
57422
  const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
57451
57423
  deps.logger?.info("app-builder.scaffold.done", {
57452
57424
  name: project.name,
@@ -57667,7 +57639,7 @@ function buildAppBuilderHandlers(deps) {
57667
57639
  await userStore.clearPublishJob(args.name);
57668
57640
  }
57669
57641
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
57670
- 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");
57671
57643
  deps.logger?.info("app-builder.publish.start", {
57672
57644
  name: args.name,
57673
57645
  sessionId: boundSession.sessionId,
@@ -57843,7 +57815,7 @@ function buildVisitorHandlers(deps) {
57843
57815
 
57844
57816
  // src/extension/registry.ts
57845
57817
  var import_promises10 = __toESM(require("fs/promises"), 1);
57846
- var import_node_path56 = __toESM(require("path"), 1);
57818
+ var import_node_path57 = __toESM(require("path"), 1);
57847
57819
  async function loadAll(root) {
57848
57820
  let entries;
57849
57821
  try {
@@ -57856,13 +57828,13 @@ async function loadAll(root) {
57856
57828
  for (const ent of entries) {
57857
57829
  if (!ent.isDirectory()) continue;
57858
57830
  if (ent.name.startsWith(".")) continue;
57859
- 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));
57860
57832
  }
57861
57833
  records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
57862
57834
  return records;
57863
57835
  }
57864
57836
  async function loadOne(dir, dirName) {
57865
- const manifestPath = import_node_path56.default.join(dir, "manifest.json");
57837
+ const manifestPath = import_node_path57.default.join(dir, "manifest.json");
57866
57838
  let raw;
57867
57839
  try {
57868
57840
  raw = await import_promises10.default.readFile(manifestPath, "utf8");
@@ -57907,7 +57879,7 @@ async function loadOne(dir, dirName) {
57907
57879
 
57908
57880
  // src/extension/uninstall.ts
57909
57881
  var import_promises11 = __toESM(require("fs/promises"), 1);
57910
- var import_node_path57 = __toESM(require("path"), 1);
57882
+ var import_node_path58 = __toESM(require("path"), 1);
57911
57883
  var UninstallError = class extends Error {
57912
57884
  constructor(code, message) {
57913
57885
  super(message);
@@ -57916,7 +57888,7 @@ var UninstallError = class extends Error {
57916
57888
  code;
57917
57889
  };
57918
57890
  async function uninstall(deps) {
57919
- const dir = import_node_path57.default.join(deps.root, deps.extId);
57891
+ const dir = import_node_path58.default.join(deps.root, deps.extId);
57920
57892
  try {
57921
57893
  await import_promises11.default.access(dir);
57922
57894
  } catch {
@@ -58503,6 +58475,21 @@ var METHOD_GRANT_MAP = {
58503
58475
  // ---- shift:* (排班 mock,owner-only 读) ----
58504
58476
  // mock 阶段只 owner 用;接 guest 后再考虑分级
58505
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,
58506
58493
  "skills:list": CAPABILITY_SCOPED,
58507
58494
  "agents:list": CAPABILITY_SCOPED,
58508
58495
  "git:root": CAPABILITY_SCOPED,
@@ -58522,7 +58509,7 @@ var METHOD_GRANT_MAP = {
58522
58509
  "persona:delete": ADMIN_ANY,
58523
58510
  // 跨设备 dispatch:A 的 peer daemon 凭 connect token 经 /rpc 调进来;dispatcher 放行,
58524
58511
  // run handler 内做 persona.public + assertGrant(send) 双重校验(guest grants 是 wildcard,
58525
- // 单 assertGrant 不够)。本地 cc 仍走 /api/rpc(EXPOSED_RPC_METHODS),不经此表。
58512
+ // 单 assertGrant 不够)。本地 MCP server /rpc + Bearer 进来,同过此表(owner 恒过)。
58526
58513
  "personaDispatch:run": CAPABILITY_SCOPED,
58527
58514
  // 跨设备 exec(spec 2026-07-16):public——"能连上=有 auth",业务在 handler 校
58528
58515
  // (ensureGuest + remoteAccessAllowed),跟 inbox:postMessage 同模式。
@@ -58634,7 +58621,7 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
58634
58621
 
58635
58622
  // src/extension/runtime.ts
58636
58623
  var import_node_child_process17 = require("child_process");
58637
- var import_node_path58 = __toESM(require("path"), 1);
58624
+ var import_node_path59 = __toESM(require("path"), 1);
58638
58625
  var import_promises12 = require("timers/promises");
58639
58626
 
58640
58627
  // src/extension/port-allocator.ts
@@ -58735,7 +58722,7 @@ var Runtime = class {
58735
58722
  /\$CLAWOS_EXT_PORT/g,
58736
58723
  String(port)
58737
58724
  );
58738
- const dir = import_node_path58.default.join(this.root, extId);
58725
+ const dir = import_node_path59.default.join(this.root, extId);
58739
58726
  const env = {
58740
58727
  ...process.env,
58741
58728
  CLAWOS_EXT_PORT: String(port),
@@ -58847,7 +58834,7 @@ ${handle.stderrTail}`
58847
58834
 
58848
58835
  // src/extension/published-channels.ts
58849
58836
  var import_promises13 = __toESM(require("fs/promises"), 1);
58850
- var import_node_path59 = __toESM(require("path"), 1);
58837
+ var import_node_path60 = __toESM(require("path"), 1);
58851
58838
  init_zod();
58852
58839
  var PublishedChannelsError = class extends Error {
58853
58840
  constructor(code, message) {
@@ -58946,7 +58933,7 @@ var PublishedChannelStore = class {
58946
58933
  )
58947
58934
  };
58948
58935
  const tmp = `${this.filePath}.tmp`;
58949
- 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 });
58950
58937
  await import_promises13.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
58951
58938
  await import_promises13.default.rename(tmp, this.filePath);
58952
58939
  }
@@ -58954,7 +58941,7 @@ var PublishedChannelStore = class {
58954
58941
 
58955
58942
  // src/extension/bundle-cache.ts
58956
58943
  var import_promises14 = __toESM(require("fs/promises"), 1);
58957
- var import_node_path60 = __toESM(require("path"), 1);
58944
+ var import_node_path61 = __toESM(require("path"), 1);
58958
58945
  var BundleCache = class {
58959
58946
  constructor(rootDir) {
58960
58947
  this.rootDir = rootDir;
@@ -58963,14 +58950,14 @@ var BundleCache = class {
58963
58950
  /** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
58964
58951
  async write(snapshotHash, buffer) {
58965
58952
  await import_promises14.default.mkdir(this.rootDir, { recursive: true });
58966
- const file = import_node_path60.default.join(this.rootDir, `${snapshotHash}.zip`);
58953
+ const file = import_node_path61.default.join(this.rootDir, `${snapshotHash}.zip`);
58967
58954
  const tmp = `${file}.tmp`;
58968
58955
  await import_promises14.default.writeFile(tmp, buffer, { mode: 384 });
58969
58956
  await import_promises14.default.rename(tmp, file);
58970
58957
  }
58971
58958
  /** Returns the bundle bytes, or null when the file doesn't exist. */
58972
58959
  async read(snapshotHash) {
58973
- const file = import_node_path60.default.join(this.rootDir, `${snapshotHash}.zip`);
58960
+ const file = import_node_path61.default.join(this.rootDir, `${snapshotHash}.zip`);
58974
58961
  try {
58975
58962
  return await import_promises14.default.readFile(file);
58976
58963
  } catch (e) {
@@ -58980,7 +58967,7 @@ var BundleCache = class {
58980
58967
  }
58981
58968
  /** Idempotent — missing file is not an error. */
58982
58969
  async delete(snapshotHash) {
58983
- const file = import_node_path60.default.join(this.rootDir, `${snapshotHash}.zip`);
58970
+ const file = import_node_path61.default.join(this.rootDir, `${snapshotHash}.zip`);
58984
58971
  await import_promises14.default.rm(file, { force: true });
58985
58972
  }
58986
58973
  };
@@ -59003,24 +58990,24 @@ async function startDaemon(config) {
59003
58990
  sampling: logShippingCfg.sampling,
59004
58991
  homeDir: import_node_os22.default.homedir()
59005
58992
  });
59006
- const logDir = import_node_path61.default.join(config.dataDir, "log");
59007
- 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 });
59008
58995
  const logger = createLogger({
59009
58996
  level: config.logLevel,
59010
- file: import_node_path61.default.join(logDir, "clawd.log"),
58997
+ file: import_node_path62.default.join(logDir, "clawd.log"),
59011
58998
  logClient
59012
58999
  });
59013
59000
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
59014
59001
  const screenIdleProbeLogger = createFileOnlyLogger({
59015
- file: import_node_path61.default.join(logDir, "screen-idle-probe.log"),
59002
+ file: import_node_path62.default.join(logDir, "screen-idle-probe.log"),
59016
59003
  level: "debug"
59017
59004
  });
59018
59005
  logger.info("screen-idle probe logger enabled", {
59019
- file: import_node_path61.default.join(logDir, "screen-idle-probe.log")
59006
+ file: import_node_path62.default.join(logDir, "screen-idle-probe.log")
59020
59007
  });
59021
59008
  const CAP_TARGETS = [
59022
- import_node_path61.default.join(logDir, "clawd.log"),
59023
- 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")
59024
59011
  ];
59025
59012
  const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
59026
59013
  const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
@@ -59044,12 +59031,7 @@ async function startDaemon(config) {
59044
59031
  if (pre.status === "stale") {
59045
59032
  logger.warn("stale state file detected, overwriting", { pid: pre.existing.pid });
59046
59033
  }
59047
- let resolvedAuthToken = null;
59048
- if (config.authToken && config.authToken.trim()) {
59049
- resolvedAuthToken = config.authToken.trim();
59050
- } else if (config.tunnel) {
59051
- resolvedAuthToken = authFile.token;
59052
- }
59034
+ const resolvedAuthToken = config.authToken && config.authToken.trim() ? config.authToken.trim() : authFile.token;
59053
59035
  const ownerIdentityStore = new OwnerIdentityStore(config.dataDir);
59054
59036
  const feishuIdentity = ownerIdentityStore.read();
59055
59037
  let feishuActive = feishuIdentity !== null;
@@ -59058,7 +59040,7 @@ async function startDaemon(config) {
59058
59040
  let ownerProvider = feishuIdentity?.identity.provider ?? "";
59059
59041
  let ownerDisplayName = feishuIdentity?.identity.displayName ?? loadOwnerDisplayName(config.dataDir);
59060
59042
  let ownerFeishuUnionId = feishuIdentity?.identity.unionId;
59061
- const authMode = resolvedAuthToken == null ? "none" : "first-message";
59043
+ const authMode = "first-message";
59062
59044
  const inboxStore = new InboxStore(config.dataDir);
59063
59045
  const inboxManager = new InboxManager(inboxStore, (_peerDeviceId, frame) => {
59064
59046
  wsServer?.broadcastToOwners(frame);
@@ -59122,8 +59104,8 @@ async function startDaemon(config) {
59122
59104
  const agents = new AgentsScanner();
59123
59105
  const history = new ClaudeHistoryReader();
59124
59106
  let transport = null;
59125
- const personaStore = new PersonaStore(import_node_path61.default.join(config.dataDir, "personas"));
59126
- 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");
59127
59109
  const defaultsRoot = findDefaultsRoot(logger);
59128
59110
  if (defaultsRoot) {
59129
59111
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -59143,7 +59125,7 @@ async function startDaemon(config) {
59143
59125
  migrateCodexSandbox({ store: personaStore, logger });
59144
59126
  const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
59145
59127
  const dispatchStore = createDispatchStore({
59146
- filePath: import_node_path61.default.join(config.dataDir, "dispatch.json"),
59128
+ filePath: import_node_path62.default.join(config.dataDir, "dispatch.json"),
59147
59129
  now: () => Date.now(),
59148
59130
  onFlushError: (err) => logger.error("dispatch store flush failed", {
59149
59131
  reason: err instanceof Error ? err.message : String(err)
@@ -59158,20 +59140,23 @@ async function startDaemon(config) {
59158
59140
  now: () => Date.now(),
59159
59141
  onCompleted: (sid) => deliverPendingRef?.(sid)
59160
59142
  });
59161
- 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));
59162
59144
  const mcpConfigs = [];
59145
+ const mcpDaemonUrl = `http://127.0.0.1:${config.port}`;
59163
59146
  const dispatchServerCandidates = [
59164
- import_node_path61.default.join(here, "dispatch", "mcp-server.cjs"),
59147
+ import_node_path62.default.join(here, "dispatch", "mcp-server.cjs"),
59165
59148
  // 生产 dist/index → dist/dispatch/mcp-server.cjs
59166
- import_node_path61.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
59149
+ import_node_path62.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
59167
59150
  // dev tsx src/index → ../dist/dispatch/mcp-server.cjs
59168
59151
  ];
59169
- const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs46.default.existsSync(p2));
59152
+ const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
59170
59153
  if (dispatchServerScriptPath) {
59171
- 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");
59172
59155
  const dispatchCfgPath = writeDispatchMcpConfig({
59173
59156
  dataDir: config.dataDir,
59174
59157
  serverScriptPath: dispatchServerScriptPath,
59158
+ daemonToken: resolvedAuthToken,
59159
+ daemonUrl: mcpDaemonUrl,
59175
59160
  logPath: dispatchLogPath
59176
59161
  });
59177
59162
  mcpConfigs.push({ id: DISPATCH_MCP_ID, path: dispatchCfgPath });
@@ -59186,17 +59171,19 @@ async function startDaemon(config) {
59186
59171
  });
59187
59172
  }
59188
59173
  const ticketServerCandidates = [
59189
- import_node_path61.default.join(here, "ticket", "mcp-server.cjs"),
59190
- 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")
59191
59176
  ];
59192
- const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs46.default.existsSync(p2));
59177
+ const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
59193
59178
  if (ticketServerScriptPath) {
59194
- 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");
59195
59180
  const ticketCfgPath = writeTicketMcpConfig({
59196
59181
  dataDir: config.dataDir,
59197
59182
  serverScriptPath: ticketServerScriptPath,
59198
59183
  apiBaseUrl: config.clawosApi ?? DEFAULT_CLAWOS_API,
59199
59184
  apiKey: resolveClawdTicketsApiKey(),
59185
+ daemonToken: resolvedAuthToken,
59186
+ daemonUrl: mcpDaemonUrl,
59200
59187
  logPath: ticketLogPath
59201
59188
  });
59202
59189
  mcpConfigs.push({ id: "ticket", path: ticketCfgPath });
@@ -59211,15 +59198,17 @@ async function startDaemon(config) {
59211
59198
  });
59212
59199
  }
59213
59200
  const shiftServerCandidates = [
59214
- import_node_path61.default.join(here, "shift", "mcp-server.cjs"),
59215
- 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")
59216
59203
  ];
59217
- const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs46.default.existsSync(p2));
59204
+ const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
59218
59205
  if (shiftServerScriptPath) {
59219
- 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");
59220
59207
  const shiftCfgPath = await writeShiftMcpConfig({
59221
59208
  dataDir: config.dataDir,
59222
59209
  serverScriptPath: shiftServerScriptPath,
59210
+ daemonToken: resolvedAuthToken,
59211
+ daemonUrl: mcpDaemonUrl,
59223
59212
  logPath: shiftLogPath
59224
59213
  });
59225
59214
  mcpConfigs.push({ id: "shift", path: shiftCfgPath });
@@ -59235,15 +59224,17 @@ async function startDaemon(config) {
59235
59224
  );
59236
59225
  }
59237
59226
  const inboxServerCandidates = [
59238
- import_node_path61.default.join(here, "inbox", "mcp-server.cjs"),
59239
- 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")
59240
59229
  ];
59241
- const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs46.default.existsSync(p2));
59230
+ const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
59242
59231
  if (inboxServerScriptPath) {
59243
- 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");
59244
59233
  const inboxCfgPath = await writeInboxMcpConfig({
59245
59234
  dataDir: config.dataDir,
59246
59235
  serverScriptPath: inboxServerScriptPath,
59236
+ daemonToken: resolvedAuthToken,
59237
+ daemonUrl: mcpDaemonUrl,
59247
59238
  logPath: inboxLogPath
59248
59239
  });
59249
59240
  mcpConfigs.push({ id: "inbox", path: inboxCfgPath });
@@ -59259,14 +59250,16 @@ async function startDaemon(config) {
59259
59250
  );
59260
59251
  }
59261
59252
  const peerOpsServerCandidates = [
59262
- import_node_path61.default.join(here, "peer-ops", "mcp-server.cjs"),
59263
- 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")
59264
59255
  ];
59265
- const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs46.default.existsSync(p2));
59256
+ const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs47.default.existsSync(p2));
59266
59257
  if (peerOpsServerScriptPath) {
59267
59258
  const peerOpsCfgPath = writePeerOpsMcpConfig({
59268
59259
  dataDir: config.dataDir,
59269
- serverScriptPath: peerOpsServerScriptPath
59260
+ serverScriptPath: peerOpsServerScriptPath,
59261
+ daemonToken: resolvedAuthToken,
59262
+ daemonUrl: mcpDaemonUrl
59270
59263
  });
59271
59264
  mcpConfigs.push({ id: "peer-ops", path: peerOpsCfgPath });
59272
59265
  logger.info("peer-ops.mcp.json written", {
@@ -59279,8 +59272,34 @@ async function startDaemon(config) {
59279
59272
  { tried: peerOpsServerCandidates }
59280
59273
  );
59281
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
+ }
59282
59301
  const shiftStore = createShiftStore({
59283
- filePath: import_node_path61.default.join(config.dataDir, "shift.json"),
59302
+ filePath: import_node_path62.default.join(config.dataDir, "shift.json"),
59284
59303
  ownerIdProvider: () => ownerPrincipalId,
59285
59304
  now: () => Date.now()
59286
59305
  });
@@ -59302,7 +59321,7 @@ async function startDaemon(config) {
59302
59321
  getAdapter,
59303
59322
  historyReader: history,
59304
59323
  dataDir: config.dataDir,
59305
- personaRoot: import_node_path61.default.join(config.dataDir, "personas"),
59324
+ personaRoot: import_node_path62.default.join(config.dataDir, "personas"),
59306
59325
  // dispatch / shift 自动化 spawn 按 persona 绑定 tool 起 B session(codex persona 用 codex)。
59307
59326
  // personaRegistry 声明在 manager 之后(同作用域 const,调用时机在启动完成后,无 TDZ 风险)。
59308
59327
  getPersonaTool: (personaId) => personaRegistry.get(personaId)?.tool,
@@ -59311,10 +59330,13 @@ async function startDaemon(config) {
59311
59330
  ownerDisplayName,
59312
59331
  ownerPrincipalId,
59313
59332
  ownerFeishuUnionId,
59333
+ // T-37 owner session 闲置回收兜底上限(默认 6h,config.json / env 可调,设 0 关闭)。
59334
+ // guest 的 10min 不走这条——它是 session/scope.ts 的固定常量。
59335
+ ownerIdleKillDelayMs: config.ownerIdleKillDelayMs,
59314
59336
  contactStore,
59315
59337
  mode: config.mode,
59316
59338
  // 单栏 refactor (spec 2026-06-02 §5.1):cc 子进程 env CLAWD_DAEMON_URL 闭包来源。
59317
- // assistant curl 调本机 daemon HTTP RPC adapter(/api/rpc/<method>)触发 appBuilder.* 管理操作。
59339
+ // MCP server 用它拼 /rpc/<method>(Bearer CLAWD_DAEMON_TOKEN)调本机 daemon。
59318
59340
  // 127.0.0.1(不是 config.host)—— cc 跑在本机,loopback 最稳;外部访问限制 + http-router
59319
59341
  // 的 isLoopback 兜底已确保安全。
59320
59342
  getDaemonUrl: () => `http://127.0.0.1:${config.port}`,
@@ -59350,10 +59372,10 @@ async function startDaemon(config) {
59350
59372
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
59351
59373
  attachmentGroup: {
59352
59374
  onFileEdit: (input) => {
59353
- 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);
59354
59376
  let size = 0;
59355
59377
  try {
59356
- size = import_node_fs46.default.statSync(absPath).size;
59378
+ size = import_node_fs47.default.statSync(absPath).size;
59357
59379
  } catch (err) {
59358
59380
  logger.warn("attachment.onFileEdit stat failed", {
59359
59381
  sessionId: input.sessionId,
@@ -59584,11 +59606,11 @@ async function startDaemon(config) {
59584
59606
  // 'persona/<pid>/owner',default 走 'default'。
59585
59607
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
59586
59608
  // guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
59587
- personaRoot: import_node_path61.default.join(config.dataDir, "personas"),
59609
+ personaRoot: import_node_path62.default.join(config.dataDir, "personas"),
59588
59610
  usersRoot
59589
59611
  },
59590
59612
  // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
59591
- personaRoot: import_node_path61.default.join(config.dataDir, "personas"),
59613
+ personaRoot: import_node_path62.default.join(config.dataDir, "personas"),
59592
59614
  // v2 多人 persona 隔离:handler 派生 guest user-dir 放行
59593
59615
  usersRoot,
59594
59616
  // v2 Phase 6: whoami handler 装在 owner principal.displayName + persona 解析
@@ -59717,11 +59739,11 @@ async function startDaemon(config) {
59717
59739
  // 发布上线脚手架化 (spec 2026-06-03 §5.2):
59718
59740
  // appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
59719
59741
  // dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
59720
- 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"),
59721
59743
  // 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
59722
- deployKitRoot: import_node_path61.default.join(config.dataDir, "deploy-kit"),
59744
+ deployKitRoot: import_node_path62.default.join(config.dataDir, "deploy-kit"),
59723
59745
  // scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
59724
- resolvePersonaRoot: (personaId) => import_node_path61.default.join(config.dataDir, "personas", personaId),
59746
+ resolvePersonaRoot: (personaId) => import_node_path62.default.join(config.dataDir, "personas", personaId),
59725
59747
  // 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
59726
59748
  // 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
59727
59749
  // 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
@@ -59767,7 +59789,7 @@ async function startDaemon(config) {
59767
59789
  }
59768
59790
  let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
59769
59791
  if (sourceFile && sourceFile.toolSessionId) {
59770
- sourceJsonlPath = import_node_path61.default.join(
59792
+ sourceJsonlPath = import_node_path62.default.join(
59771
59793
  import_node_os22.default.homedir(),
59772
59794
  ".claude",
59773
59795
  "projects",
@@ -59837,17 +59859,6 @@ async function startDaemon(config) {
59837
59859
  const authResolver = new AuthContextResolver({
59838
59860
  ownerToken: resolvedAuthToken
59839
59861
  });
59840
- const rpcDispatcher = {
59841
- async dispatch(method, args, _ctx) {
59842
- const handler = resolveExposedHandler(handlers, method);
59843
- if (!handler) {
59844
- throw Object.assign(new Error(`unknown RPC method "${method}"`), { code: "METHOD_NOT_IMPLEMENTED" });
59845
- }
59846
- const emptyClient = {};
59847
- const result = await handler(args, emptyClient);
59848
- return result.response;
59849
- }
59850
- };
59851
59862
  const authedRpc = {
59852
59863
  authenticate: (token) => authenticate(token, buildConnectAuthDeps()),
59853
59864
  dispatch: (method, body, ctx) => {
@@ -59872,7 +59883,6 @@ async function startDaemon(config) {
59872
59883
  daemonVersion: version,
59873
59884
  logger,
59874
59885
  sessionStore: store,
59875
- rpcDispatcher,
59876
59886
  authedRpc,
59877
59887
  ...viewerAssetLoader ? { viewerAssetLoader } : {},
59878
59888
  // persona web 分享:访客换 token(POST /share/exchange)+ guest SPA serve(GET /s)
@@ -60082,8 +60092,8 @@ async function startDaemon(config) {
60082
60092
  const lines = [
60083
60093
  `Tunnel: ${r.url}`,
60084
60094
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
60085
- `Frpc config: ${import_node_path61.default.join(config.dataDir, "frpc.toml")}`,
60086
- `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")}`
60087
60097
  ];
60088
60098
  const width = Math.max(...lines.map((l) => l.length));
60089
60099
  const bar = "\u2550".repeat(width + 4);
@@ -60096,8 +60106,8 @@ ${bar}
60096
60106
 
60097
60107
  `);
60098
60108
  try {
60099
- const connectPath = import_node_path61.default.join(config.dataDir, "connect.txt");
60100
- 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 });
60101
60111
  } catch {
60102
60112
  }
60103
60113
  } catch (err) {
@@ -60169,9 +60179,9 @@ ${bar}
60169
60179
  };
60170
60180
  }
60171
60181
  function migrateDropPersonsDir(dataDir) {
60172
- const dir = import_node_path61.default.join(dataDir, "persons");
60182
+ const dir = import_node_path62.default.join(dataDir, "persons");
60173
60183
  try {
60174
- import_node_fs46.default.rmSync(dir, { recursive: true, force: true });
60184
+ import_node_fs47.default.rmSync(dir, { recursive: true, force: true });
60175
60185
  } catch {
60176
60186
  }
60177
60187
  }