@clawos-dev/clawd 0.2.239 → 0.2.240

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
@@ -115,6 +115,11 @@ var init_methods = __esm({
115
115
  // METHOD_GRANT_MAP capability-scoped(handler 自检 persona.public + assertGrant(send))。
116
116
  // personaDispatch:complete 仅 B 本地 cc 调(/api/rpc),不跨设备,故不列入 WS method。
117
117
  "personaDispatch:run",
118
+ // ---- peerExec:run (跨设备 exec,spec 2026-07-16-cross-device-exec-mcp) ----
119
+ // A 侧本地 cc 经 /api/rpc(EXPOSED_RPC_METHODS)带 peerDeviceId → daemon forward;
120
+ // B 侧对端经 /rpc(Bearer connect token)无 peerDeviceId → 本地 spawn。METHOD_GRANT_MAP
121
+ // 设 public,handler 内判 remoteAccessAllowed(跟 inbox:postMessage 同款)。
122
+ "peerExec:run",
118
123
  // ---- session:pty 双向透传(仅 CLAWD_CC_MODE=tui 才生效,对应 session:pty push 帧的上行) ----
119
124
  // session:pty:input — UI 把用户键盘字节(base64 UTF-8)发给 daemon,daemon 直写 pty stdin。
120
125
  // 高频帧(每次按键 1 帧),不入 reducer / 不广播。response 是 fire-and-forget 风格 ack。
@@ -161,10 +166,10 @@ var init_methods = __esm({
161
166
  "contact:list",
162
167
  "contact:pin",
163
168
  "contact:remove",
164
- // ---- contact:setSshAccess (SSH 反向访问,PR: contact-ssh-sandbox) ----
165
- // owner UI 授权 contact 通过 daemon 专用 sshd 反向 SSH 进本机;key 分发走建联握手
166
- // pubkey 互换(spec 2026-07-15,无签发 RPC)。详见 protocol/src/contact-ssh.ts.
167
- "contact:setSshAccess",
169
+ // ---- contact:setRemoteAccess (远程访问授权,spec 2026-07-16) ----
170
+ // owner UI 授权 contact 反向操作我的设备(exec + SSH 共用开关 remoteAccessAllowed)。
171
+ // 详见 protocol/src/contact-ssh.ts.
172
+ "contact:setRemoteAccess",
168
173
  // ---- visitor:* (web 访客 · persona web 分享,spec 2026-06-24-persona-web-share-design) ----
169
174
  // owner-only:列出登录访问过本机 public persona 的 web 访客(零安装、飞书登录、daemon 自签
170
175
  // visitor token)。存储 ~/.clawd/visitors.json(VisitorStore),登录(exchange)即 upsert;
@@ -744,8 +749,8 @@ var init_parseUtil = __esm({
744
749
  init_errors2();
745
750
  init_en();
746
751
  makeIssue = (params) => {
747
- const { data, path: path76, errorMaps, issueData } = params;
748
- const fullPath = [...path76, ...issueData.path || []];
752
+ const { data, path: path77, errorMaps, issueData } = params;
753
+ const fullPath = [...path77, ...issueData.path || []];
749
754
  const fullIssue = {
750
755
  ...issueData,
751
756
  path: fullPath
@@ -1056,11 +1061,11 @@ var init_types = __esm({
1056
1061
  init_parseUtil();
1057
1062
  init_util();
1058
1063
  ParseInputLazyPath = class {
1059
- constructor(parent, value, path76, key) {
1064
+ constructor(parent, value, path77, key) {
1060
1065
  this._cachedPath = [];
1061
1066
  this.parent = parent;
1062
1067
  this.data = value;
1063
- this._path = path76;
1068
+ this._path = path77;
1064
1069
  this._key = key;
1065
1070
  }
1066
1071
  get path() {
@@ -5745,21 +5750,21 @@ var init_contact = __esm({
5745
5750
  */
5746
5751
  pinnedAt: external_exports.number().int().nullable().default(null),
5747
5752
  /**
5748
- * SSH 反向访问授权(PR: contact-ssh-sandbox)。owner 视角:允许该 contact 通过 daemon
5749
- * 专用 sshd 反向 SSH 进本机。default=false 保证老 contacts.json 兼容(zod default 补齐)。
5750
- * 授权粒度 per-people (per-contact):借用别人 persona 本来就带信任对方本人的前提;
5751
- * 极端场景(区别对方多个 persona)留 per-persona override 演进空间。
5753
+ * 远程访问授权(spec 2026-07-16)。owner 视角:允许该 contact 反向操作我的设备——
5754
+ * 一个开关两种实现:跨设备 exec(peerExec:run handler 判它)+ SSH 反向访问
5755
+ * (rebuildAuthorizedKeys 读它)。default=false,owner 显式开。
5756
+ * 授权粒度 per-people (per-contact):借用别人 persona 本来就带信任对方本人的前提。
5752
5757
  */
5753
- sshAllowed: external_exports.boolean().default(false),
5758
+ remoteAccessAllowed: external_exports.boolean().default(false),
5754
5759
  /** 对端设备 SSH identity pubkey(一行 `ssh-ed25519 AAAA... comment`);见 doc/glossary/peer-mesh.md#建联交换清单 */
5755
5760
  sshPublicKey: external_exports.string().optional(),
5756
5761
  /** 对端 OS 登录账号名,SSH 拨号登录名(`<osUser>@127.0.0.1`);见 doc/glossary/peer-mesh.md#建联交换清单 */
5757
5762
  osUser: external_exports.string().optional(),
5758
5763
  /**
5759
5764
  * @deprecated T-29 去沙箱后废弃。原为授权访问的目录白名单(clawd-ssh-jail 生成 sbpl/bwrap
5760
- * policy 用);去掉 OS 沙箱后不再有目录级限制,SSH 授权即 on/off(见 sshAllowed)。
5765
+ * policy 用);去掉 OS 沙箱后不再有目录级限制,远程访问授权即 on/off(见 remoteAccessAllowed)。
5761
5766
  * 字段仅为兼容老 contacts.json 保留(default [] 保证 strict parse 不炸),不再有代码读写它,
5762
- * 也不再进 setSshAccess RPC / ContactSettingsDrawer / jail。勿新增消费方。
5767
+ * 也不再进 setRemoteAccess RPC / ContactSettingsDrawer / jail。勿新增消费方。
5763
5768
  */
5764
5769
  exposedDirs: external_exports.array(external_exports.string()).default([])
5765
5770
  }).strict();
@@ -5801,24 +5806,51 @@ var init_contact = __esm({
5801
5806
  });
5802
5807
 
5803
5808
  // ../protocol/src/contact-ssh.ts
5804
- var ContactSetSshAccessArgsSchema, ContactSetSshAccessOkSchema, ContactSshAccessUpdatedFrameSchema;
5809
+ var ContactSetRemoteAccessArgsSchema, ContactSetRemoteAccessOkSchema, ContactRemoteAccessUpdatedFrameSchema;
5805
5810
  var init_contact_ssh = __esm({
5806
5811
  "../protocol/src/contact-ssh.ts"() {
5807
5812
  "use strict";
5808
5813
  init_zod();
5809
- ContactSetSshAccessArgsSchema = external_exports.object({
5814
+ ContactSetRemoteAccessArgsSchema = external_exports.object({
5810
5815
  deviceId: external_exports.string().min(1),
5811
- sshAllowed: external_exports.boolean()
5816
+ remoteAccessAllowed: external_exports.boolean()
5812
5817
  }).strict();
5813
- ContactSetSshAccessOkSchema = external_exports.object({
5814
- type: external_exports.literal("contact:setSshAccess:ok"),
5818
+ ContactSetRemoteAccessOkSchema = external_exports.object({
5819
+ type: external_exports.literal("contact:setRemoteAccess:ok"),
5815
5820
  deviceId: external_exports.string().min(1),
5816
- sshAllowed: external_exports.boolean()
5821
+ remoteAccessAllowed: external_exports.boolean()
5817
5822
  }).strict();
5818
- ContactSshAccessUpdatedFrameSchema = external_exports.object({
5819
- type: external_exports.literal("contact:ssh-access-updated"),
5823
+ ContactRemoteAccessUpdatedFrameSchema = external_exports.object({
5824
+ type: external_exports.literal("contact:remote-access-updated"),
5820
5825
  deviceId: external_exports.string().min(1),
5821
- sshAllowed: external_exports.boolean()
5826
+ remoteAccessAllowed: external_exports.boolean()
5827
+ }).strict();
5828
+ }
5829
+ });
5830
+
5831
+ // ../protocol/src/peer-exec.ts
5832
+ var PeerExecRunArgsSchema, PeerExecRunOkSchema;
5833
+ var init_peer_exec = __esm({
5834
+ "../protocol/src/peer-exec.ts"() {
5835
+ "use strict";
5836
+ init_zod();
5837
+ PeerExecRunArgsSchema = external_exports.object({
5838
+ /** A 侧发起时带:目标设备 deviceId。peer-forward 转发前 strip 掉,B 侧本地执行时无此字段。 */
5839
+ peerDeviceId: external_exports.string().min(1).optional(),
5840
+ /** 交给对端 `bash -c` 的命令;cc 自拼完整路径(一发一收,无跨命令会话状态)。 */
5841
+ command: external_exports.string().min(1),
5842
+ /** 超时上限(ms);缺省用 handler 默认 30_000,clamp 300_000。 */
5843
+ timeoutMs: external_exports.number().int().positive().optional()
5844
+ }).strict();
5845
+ PeerExecRunOkSchema = external_exports.object({
5846
+ type: external_exports.literal("peerExec:run:ok"),
5847
+ stdout: external_exports.string(),
5848
+ stderr: external_exports.string(),
5849
+ /** 进程退出码;被超时 kill 时 null。 */
5850
+ exitCode: external_exports.number().int().nullable(),
5851
+ timedOut: external_exports.boolean(),
5852
+ stdoutTruncated: external_exports.boolean(),
5853
+ stderrTruncated: external_exports.boolean()
5822
5854
  }).strict();
5823
5855
  }
5824
5856
  });
@@ -6230,6 +6262,7 @@ var init_runtime = __esm({
6230
6262
  init_inbox();
6231
6263
  init_contact();
6232
6264
  init_contact_ssh();
6265
+ init_peer_exec();
6233
6266
  init_extension();
6234
6267
  init_feishu_auth();
6235
6268
  init_dispatch();
@@ -6509,8 +6542,8 @@ var require_req = __commonJS({
6509
6542
  if (req.originalUrl) {
6510
6543
  _req.url = req.originalUrl;
6511
6544
  } else {
6512
- const path76 = req.path;
6513
- _req.url = typeof path76 === "string" ? path76 : req.url ? req.url.path || req.url : void 0;
6545
+ const path77 = req.path;
6546
+ _req.url = typeof path77 === "string" ? path77 : req.url ? req.url.path || req.url : void 0;
6514
6547
  }
6515
6548
  if (req.query) {
6516
6549
  _req.query = req.query;
@@ -6675,14 +6708,14 @@ var require_redact = __commonJS({
6675
6708
  }
6676
6709
  return obj;
6677
6710
  }
6678
- function parsePath(path76) {
6711
+ function parsePath(path77) {
6679
6712
  const parts = [];
6680
6713
  let current = "";
6681
6714
  let inBrackets = false;
6682
6715
  let inQuotes = false;
6683
6716
  let quoteChar = "";
6684
- for (let i = 0; i < path76.length; i++) {
6685
- const char = path76[i];
6717
+ for (let i = 0; i < path77.length; i++) {
6718
+ const char = path77[i];
6686
6719
  if (!inBrackets && char === ".") {
6687
6720
  if (current) {
6688
6721
  parts.push(current);
@@ -6813,10 +6846,10 @@ var require_redact = __commonJS({
6813
6846
  return current;
6814
6847
  }
6815
6848
  function redactPaths(obj, paths, censor, remove = false) {
6816
- for (const path76 of paths) {
6817
- const parts = parsePath(path76);
6849
+ for (const path77 of paths) {
6850
+ const parts = parsePath(path77);
6818
6851
  if (parts.includes("*")) {
6819
- redactWildcardPath(obj, parts, censor, path76, remove);
6852
+ redactWildcardPath(obj, parts, censor, path77, remove);
6820
6853
  } else {
6821
6854
  if (remove) {
6822
6855
  removeKey(obj, parts);
@@ -6901,8 +6934,8 @@ var require_redact = __commonJS({
6901
6934
  }
6902
6935
  } else {
6903
6936
  if (afterWildcard.includes("*")) {
6904
- const wrappedCensor = typeof censor === "function" ? (value, path76) => {
6905
- const fullPath = [...pathArray.slice(0, pathLength), ...path76];
6937
+ const wrappedCensor = typeof censor === "function" ? (value, path77) => {
6938
+ const fullPath = [...pathArray.slice(0, pathLength), ...path77];
6906
6939
  return censor(value, fullPath);
6907
6940
  } : censor;
6908
6941
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -6937,8 +6970,8 @@ var require_redact = __commonJS({
6937
6970
  return null;
6938
6971
  }
6939
6972
  const pathStructure = /* @__PURE__ */ new Map();
6940
- for (const path76 of pathsToClone) {
6941
- const parts = parsePath(path76);
6973
+ for (const path77 of pathsToClone) {
6974
+ const parts = parsePath(path77);
6942
6975
  let current = pathStructure;
6943
6976
  for (let i = 0; i < parts.length; i++) {
6944
6977
  const part = parts[i];
@@ -6990,24 +7023,24 @@ var require_redact = __commonJS({
6990
7023
  }
6991
7024
  return cloneSelectively(obj, pathStructure);
6992
7025
  }
6993
- function validatePath(path76) {
6994
- if (typeof path76 !== "string") {
7026
+ function validatePath(path77) {
7027
+ if (typeof path77 !== "string") {
6995
7028
  throw new Error("Paths must be (non-empty) strings");
6996
7029
  }
6997
- if (path76 === "") {
7030
+ if (path77 === "") {
6998
7031
  throw new Error("Invalid redaction path ()");
6999
7032
  }
7000
- if (path76.includes("..")) {
7001
- throw new Error(`Invalid redaction path (${path76})`);
7033
+ if (path77.includes("..")) {
7034
+ throw new Error(`Invalid redaction path (${path77})`);
7002
7035
  }
7003
- if (path76.includes(",")) {
7004
- throw new Error(`Invalid redaction path (${path76})`);
7036
+ if (path77.includes(",")) {
7037
+ throw new Error(`Invalid redaction path (${path77})`);
7005
7038
  }
7006
7039
  let bracketCount = 0;
7007
7040
  let inQuotes = false;
7008
7041
  let quoteChar = "";
7009
- for (let i = 0; i < path76.length; i++) {
7010
- const char = path76[i];
7042
+ for (let i = 0; i < path77.length; i++) {
7043
+ const char = path77[i];
7011
7044
  if ((char === '"' || char === "'") && bracketCount > 0) {
7012
7045
  if (!inQuotes) {
7013
7046
  inQuotes = true;
@@ -7021,20 +7054,20 @@ var require_redact = __commonJS({
7021
7054
  } else if (char === "]" && !inQuotes) {
7022
7055
  bracketCount--;
7023
7056
  if (bracketCount < 0) {
7024
- throw new Error(`Invalid redaction path (${path76})`);
7057
+ throw new Error(`Invalid redaction path (${path77})`);
7025
7058
  }
7026
7059
  }
7027
7060
  }
7028
7061
  if (bracketCount !== 0) {
7029
- throw new Error(`Invalid redaction path (${path76})`);
7062
+ throw new Error(`Invalid redaction path (${path77})`);
7030
7063
  }
7031
7064
  }
7032
7065
  function validatePaths(paths) {
7033
7066
  if (!Array.isArray(paths)) {
7034
7067
  throw new TypeError("paths must be an array");
7035
7068
  }
7036
- for (const path76 of paths) {
7037
- validatePath(path76);
7069
+ for (const path77 of paths) {
7070
+ validatePath(path77);
7038
7071
  }
7039
7072
  }
7040
7073
  function slowRedact(options = {}) {
@@ -7202,8 +7235,8 @@ var require_redaction = __commonJS({
7202
7235
  if (shape[k2] === null) {
7203
7236
  o[k2] = (value) => topCensor(value, [k2]);
7204
7237
  } else {
7205
- const wrappedCensor = typeof censor === "function" ? (value, path76) => {
7206
- return censor(value, [k2, ...path76]);
7238
+ const wrappedCensor = typeof censor === "function" ? (value, path77) => {
7239
+ return censor(value, [k2, ...path77]);
7207
7240
  } : censor;
7208
7241
  o[k2] = Redact({
7209
7242
  paths: shape[k2],
@@ -7421,10 +7454,10 @@ var require_atomic_sleep = __commonJS({
7421
7454
  var require_sonic_boom = __commonJS({
7422
7455
  "../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
7423
7456
  "use strict";
7424
- var fs70 = require("fs");
7457
+ var fs71 = require("fs");
7425
7458
  var EventEmitter3 = require("events");
7426
7459
  var inherits = require("util").inherits;
7427
- var path76 = require("path");
7460
+ var path77 = require("path");
7428
7461
  var sleep2 = require_atomic_sleep();
7429
7462
  var assert = require("assert");
7430
7463
  var BUSY_WRITE_TIMEOUT = 100;
@@ -7478,20 +7511,20 @@ var require_sonic_boom = __commonJS({
7478
7511
  const mode = sonic.mode;
7479
7512
  if (sonic.sync) {
7480
7513
  try {
7481
- if (sonic.mkdir) fs70.mkdirSync(path76.dirname(file), { recursive: true });
7482
- const fd = fs70.openSync(file, flags, mode);
7514
+ if (sonic.mkdir) fs71.mkdirSync(path77.dirname(file), { recursive: true });
7515
+ const fd = fs71.openSync(file, flags, mode);
7483
7516
  fileOpened(null, fd);
7484
7517
  } catch (err) {
7485
7518
  fileOpened(err);
7486
7519
  throw err;
7487
7520
  }
7488
7521
  } else if (sonic.mkdir) {
7489
- fs70.mkdir(path76.dirname(file), { recursive: true }, (err) => {
7522
+ fs71.mkdir(path77.dirname(file), { recursive: true }, (err) => {
7490
7523
  if (err) return fileOpened(err);
7491
- fs70.open(file, flags, mode, fileOpened);
7524
+ fs71.open(file, flags, mode, fileOpened);
7492
7525
  });
7493
7526
  } else {
7494
- fs70.open(file, flags, mode, fileOpened);
7527
+ fs71.open(file, flags, mode, fileOpened);
7495
7528
  }
7496
7529
  }
7497
7530
  function SonicBoom(opts) {
@@ -7532,8 +7565,8 @@ var require_sonic_boom = __commonJS({
7532
7565
  this.flush = flushBuffer;
7533
7566
  this.flushSync = flushBufferSync;
7534
7567
  this._actualWrite = actualWriteBuffer;
7535
- fsWriteSync = () => fs70.writeSync(this.fd, this._writingBuf);
7536
- fsWrite = () => fs70.write(this.fd, this._writingBuf, this.release);
7568
+ fsWriteSync = () => fs71.writeSync(this.fd, this._writingBuf);
7569
+ fsWrite = () => fs71.write(this.fd, this._writingBuf, this.release);
7537
7570
  } else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
7538
7571
  this._writingBuf = "";
7539
7572
  this.write = write;
@@ -7542,15 +7575,15 @@ var require_sonic_boom = __commonJS({
7542
7575
  this._actualWrite = actualWrite;
7543
7576
  fsWriteSync = () => {
7544
7577
  if (Buffer.isBuffer(this._writingBuf)) {
7545
- return fs70.writeSync(this.fd, this._writingBuf);
7578
+ return fs71.writeSync(this.fd, this._writingBuf);
7546
7579
  }
7547
- return fs70.writeSync(this.fd, this._writingBuf, "utf8");
7580
+ return fs71.writeSync(this.fd, this._writingBuf, "utf8");
7548
7581
  };
7549
7582
  fsWrite = () => {
7550
7583
  if (Buffer.isBuffer(this._writingBuf)) {
7551
- return fs70.write(this.fd, this._writingBuf, this.release);
7584
+ return fs71.write(this.fd, this._writingBuf, this.release);
7552
7585
  }
7553
- return fs70.write(this.fd, this._writingBuf, "utf8", this.release);
7586
+ return fs71.write(this.fd, this._writingBuf, "utf8", this.release);
7554
7587
  };
7555
7588
  } else {
7556
7589
  throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
@@ -7607,7 +7640,7 @@ var require_sonic_boom = __commonJS({
7607
7640
  }
7608
7641
  }
7609
7642
  if (this._fsync) {
7610
- fs70.fsyncSync(this.fd);
7643
+ fs71.fsyncSync(this.fd);
7611
7644
  }
7612
7645
  const len = this._len;
7613
7646
  if (this._reopening) {
@@ -7721,7 +7754,7 @@ var require_sonic_boom = __commonJS({
7721
7754
  const onDrain = () => {
7722
7755
  if (!this._fsync) {
7723
7756
  try {
7724
- fs70.fsync(this.fd, (err) => {
7757
+ fs71.fsync(this.fd, (err) => {
7725
7758
  this._flushPending = false;
7726
7759
  cb(err);
7727
7760
  });
@@ -7823,7 +7856,7 @@ var require_sonic_boom = __commonJS({
7823
7856
  const fd = this.fd;
7824
7857
  this.once("ready", () => {
7825
7858
  if (fd !== this.fd) {
7826
- fs70.close(fd, (err) => {
7859
+ fs71.close(fd, (err) => {
7827
7860
  if (err) {
7828
7861
  return this.emit("error", err);
7829
7862
  }
@@ -7872,7 +7905,7 @@ var require_sonic_boom = __commonJS({
7872
7905
  buf = this._bufs[0];
7873
7906
  }
7874
7907
  try {
7875
- const n = Buffer.isBuffer(buf) ? fs70.writeSync(this.fd, buf) : fs70.writeSync(this.fd, buf, "utf8");
7908
+ const n = Buffer.isBuffer(buf) ? fs71.writeSync(this.fd, buf) : fs71.writeSync(this.fd, buf, "utf8");
7876
7909
  const releasedBufObj = releaseWritingBuf(buf, this._len, n);
7877
7910
  buf = releasedBufObj.writingBuf;
7878
7911
  this._len = releasedBufObj.len;
@@ -7888,7 +7921,7 @@ var require_sonic_boom = __commonJS({
7888
7921
  }
7889
7922
  }
7890
7923
  try {
7891
- fs70.fsyncSync(this.fd);
7924
+ fs71.fsyncSync(this.fd);
7892
7925
  } catch {
7893
7926
  }
7894
7927
  }
@@ -7909,7 +7942,7 @@ var require_sonic_boom = __commonJS({
7909
7942
  buf = mergeBuf(this._bufs[0], this._lens[0]);
7910
7943
  }
7911
7944
  try {
7912
- const n = fs70.writeSync(this.fd, buf);
7945
+ const n = fs71.writeSync(this.fd, buf);
7913
7946
  buf = buf.subarray(n);
7914
7947
  this._len = Math.max(this._len - n, 0);
7915
7948
  if (buf.length <= 0) {
@@ -7937,13 +7970,13 @@ var require_sonic_boom = __commonJS({
7937
7970
  this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
7938
7971
  if (this.sync) {
7939
7972
  try {
7940
- const written = Buffer.isBuffer(this._writingBuf) ? fs70.writeSync(this.fd, this._writingBuf) : fs70.writeSync(this.fd, this._writingBuf, "utf8");
7973
+ const written = Buffer.isBuffer(this._writingBuf) ? fs71.writeSync(this.fd, this._writingBuf) : fs71.writeSync(this.fd, this._writingBuf, "utf8");
7941
7974
  release(null, written);
7942
7975
  } catch (err) {
7943
7976
  release(err);
7944
7977
  }
7945
7978
  } else {
7946
- fs70.write(this.fd, this._writingBuf, release);
7979
+ fs71.write(this.fd, this._writingBuf, release);
7947
7980
  }
7948
7981
  }
7949
7982
  function actualWriteBuffer() {
@@ -7952,7 +7985,7 @@ var require_sonic_boom = __commonJS({
7952
7985
  this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
7953
7986
  if (this.sync) {
7954
7987
  try {
7955
- const written = fs70.writeSync(this.fd, this._writingBuf);
7988
+ const written = fs71.writeSync(this.fd, this._writingBuf);
7956
7989
  release(null, written);
7957
7990
  } catch (err) {
7958
7991
  release(err);
@@ -7961,7 +7994,7 @@ var require_sonic_boom = __commonJS({
7961
7994
  if (kCopyBuffer) {
7962
7995
  this._writingBuf = Buffer.from(this._writingBuf);
7963
7996
  }
7964
- fs70.write(this.fd, this._writingBuf, release);
7997
+ fs71.write(this.fd, this._writingBuf, release);
7965
7998
  }
7966
7999
  }
7967
8000
  function actualClose(sonic) {
@@ -7977,12 +8010,12 @@ var require_sonic_boom = __commonJS({
7977
8010
  sonic._lens = [];
7978
8011
  assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
7979
8012
  try {
7980
- fs70.fsync(sonic.fd, closeWrapped);
8013
+ fs71.fsync(sonic.fd, closeWrapped);
7981
8014
  } catch {
7982
8015
  }
7983
8016
  function closeWrapped() {
7984
8017
  if (sonic.fd !== 1 && sonic.fd !== 2) {
7985
- fs70.close(sonic.fd, done);
8018
+ fs71.close(sonic.fd, done);
7986
8019
  } else {
7987
8020
  done();
7988
8021
  }
@@ -10346,7 +10379,7 @@ var require_multistream = __commonJS({
10346
10379
  var require_pino = __commonJS({
10347
10380
  "../node_modules/.pnpm/pino@9.14.0/node_modules/pino/pino.js"(exports2, module2) {
10348
10381
  "use strict";
10349
- var os23 = require("os");
10382
+ var os24 = require("os");
10350
10383
  var stdSerializers = require_pino_std_serializers();
10351
10384
  var caller = require_caller();
10352
10385
  var redaction = require_redaction();
@@ -10393,7 +10426,7 @@ var require_pino = __commonJS({
10393
10426
  } = symbols;
10394
10427
  var { epochTime, nullTime } = time;
10395
10428
  var { pid } = process;
10396
- var hostname = os23.hostname();
10429
+ var hostname = os24.hostname();
10397
10430
  var defaultErrorSerializer = stdSerializers.err;
10398
10431
  var defaultOptions = {
10399
10432
  level: "info",
@@ -11117,11 +11150,11 @@ var init_lib = __esm({
11117
11150
  }
11118
11151
  }
11119
11152
  },
11120
- addToPath: function addToPath(path76, added, removed, oldPosInc, options) {
11121
- var last = path76.lastComponent;
11153
+ addToPath: function addToPath(path77, added, removed, oldPosInc, options) {
11154
+ var last = path77.lastComponent;
11122
11155
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
11123
11156
  return {
11124
- oldPos: path76.oldPos + oldPosInc,
11157
+ oldPos: path77.oldPos + oldPosInc,
11125
11158
  lastComponent: {
11126
11159
  count: last.count + 1,
11127
11160
  added,
@@ -11131,7 +11164,7 @@ var init_lib = __esm({
11131
11164
  };
11132
11165
  } else {
11133
11166
  return {
11134
- oldPos: path76.oldPos + oldPosInc,
11167
+ oldPos: path77.oldPos + oldPosInc,
11135
11168
  lastComponent: {
11136
11169
  count: 1,
11137
11170
  added,
@@ -11636,10 +11669,10 @@ function attachmentToHistoryMessage(o, ts) {
11636
11669
  const memories = raw.map((m2) => {
11637
11670
  if (!m2 || typeof m2 !== "object") return null;
11638
11671
  const rec3 = m2;
11639
- const path76 = typeof rec3.path === "string" ? rec3.path : null;
11672
+ const path77 = typeof rec3.path === "string" ? rec3.path : null;
11640
11673
  const content = typeof rec3.content === "string" ? rec3.content : null;
11641
- if (!path76 || content == null) return null;
11642
- const entry = { path: path76, content };
11674
+ if (!path77 || content == null) return null;
11675
+ const entry = { path: path77, content };
11643
11676
  if (typeof rec3.mtimeMs === "number") entry.mtimeMs = rec3.mtimeMs;
11644
11677
  return entry;
11645
11678
  }).filter((m2) => m2 !== null);
@@ -12442,10 +12475,10 @@ function parseAttachment(obj) {
12442
12475
  const memories = raw.map((m2) => {
12443
12476
  if (!m2 || typeof m2 !== "object") return null;
12444
12477
  const rec3 = m2;
12445
- const path76 = typeof rec3.path === "string" ? rec3.path : null;
12478
+ const path77 = typeof rec3.path === "string" ? rec3.path : null;
12446
12479
  const content = typeof rec3.content === "string" ? rec3.content : null;
12447
- if (!path76 || content == null) return null;
12448
- const out = { path: path76, content };
12480
+ if (!path77 || content == null) return null;
12481
+ const out = { path: path77, content };
12449
12482
  if (typeof rec3.mtimeMs === "number") out.mtimeMs = rec3.mtimeMs;
12450
12483
  return out;
12451
12484
  }).filter((m2) => m2 !== null);
@@ -33431,8 +33464,8 @@ var require_utils = __commonJS({
33431
33464
  var result = transform[inputType][outputType](input);
33432
33465
  return result;
33433
33466
  };
33434
- exports2.resolve = function(path76) {
33435
- var parts = path76.split("/");
33467
+ exports2.resolve = function(path77) {
33468
+ var parts = path77.split("/");
33436
33469
  var result = [];
33437
33470
  for (var index = 0; index < parts.length; index++) {
33438
33471
  var part = parts[index];
@@ -39285,18 +39318,18 @@ var require_object = __commonJS({
39285
39318
  var object = new ZipObject(name, zipObjectContent, o);
39286
39319
  this.files[name] = object;
39287
39320
  };
39288
- var parentFolder = function(path76) {
39289
- if (path76.slice(-1) === "/") {
39290
- path76 = path76.substring(0, path76.length - 1);
39321
+ var parentFolder = function(path77) {
39322
+ if (path77.slice(-1) === "/") {
39323
+ path77 = path77.substring(0, path77.length - 1);
39291
39324
  }
39292
- var lastSlash = path76.lastIndexOf("/");
39293
- return lastSlash > 0 ? path76.substring(0, lastSlash) : "";
39325
+ var lastSlash = path77.lastIndexOf("/");
39326
+ return lastSlash > 0 ? path77.substring(0, lastSlash) : "";
39294
39327
  };
39295
- var forceTrailingSlash = function(path76) {
39296
- if (path76.slice(-1) !== "/") {
39297
- path76 += "/";
39328
+ var forceTrailingSlash = function(path77) {
39329
+ if (path77.slice(-1) !== "/") {
39330
+ path77 += "/";
39298
39331
  }
39299
- return path76;
39332
+ return path77;
39300
39333
  };
39301
39334
  var folderAdd = function(name, createFolders) {
39302
39335
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
@@ -40298,7 +40331,7 @@ var require_lib3 = __commonJS({
40298
40331
  // src/run-case/recorder.ts
40299
40332
  function startRunCaseRecorder(opts) {
40300
40333
  const now = opts.now ?? Date.now;
40301
- const dir = import_node_path64.default.dirname(opts.recordPath);
40334
+ const dir = import_node_path65.default.dirname(opts.recordPath);
40302
40335
  let stream = null;
40303
40336
  let closing = false;
40304
40337
  let closedSettled = false;
@@ -40312,8 +40345,8 @@ function startRunCaseRecorder(opts) {
40312
40345
  });
40313
40346
  const ensureStream = () => {
40314
40347
  if (stream) return stream;
40315
- import_node_fs52.default.mkdirSync(dir, { recursive: true });
40316
- stream = import_node_fs52.default.createWriteStream(opts.recordPath, { flags: "a" });
40348
+ import_node_fs53.default.mkdirSync(dir, { recursive: true });
40349
+ stream = import_node_fs53.default.createWriteStream(opts.recordPath, { flags: "a" });
40317
40350
  stream.on("close", () => closedResolve());
40318
40351
  return stream;
40319
40352
  };
@@ -40338,12 +40371,12 @@ function startRunCaseRecorder(opts) {
40338
40371
  };
40339
40372
  return { tap, close, closed };
40340
40373
  }
40341
- var import_node_fs52, import_node_path64;
40374
+ var import_node_fs53, import_node_path65;
40342
40375
  var init_recorder = __esm({
40343
40376
  "src/run-case/recorder.ts"() {
40344
40377
  "use strict";
40345
- import_node_fs52 = __toESM(require("fs"), 1);
40346
- import_node_path64 = __toESM(require("path"), 1);
40378
+ import_node_fs53 = __toESM(require("fs"), 1);
40379
+ import_node_path65 = __toESM(require("path"), 1);
40347
40380
  }
40348
40381
  });
40349
40382
 
@@ -40386,7 +40419,7 @@ var init_wire = __esm({
40386
40419
  // src/run-case/controller.ts
40387
40420
  async function runController(opts) {
40388
40421
  const now = opts.now ?? Date.now;
40389
- const cwd = opts.cwd ?? (0, import_node_fs53.mkdtempSync)(import_node_path65.default.join(import_node_os22.default.tmpdir(), "clawd-runcase-"));
40422
+ const cwd = opts.cwd ?? (0, import_node_fs54.mkdtempSync)(import_node_path66.default.join(import_node_os23.default.tmpdir(), "clawd-runcase-"));
40390
40423
  const ownsCwd = opts.cwd === void 0;
40391
40424
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
40392
40425
  const spawnCtx = { cwd };
@@ -40547,19 +40580,19 @@ async function runController(opts) {
40547
40580
  if (sigintHandler) process.off("SIGINT", sigintHandler);
40548
40581
  if (ownsCwd) {
40549
40582
  try {
40550
- (0, import_node_fs53.rmSync)(cwd, { recursive: true, force: true });
40583
+ (0, import_node_fs54.rmSync)(cwd, { recursive: true, force: true });
40551
40584
  } catch {
40552
40585
  }
40553
40586
  }
40554
40587
  return exitCode ?? 0;
40555
40588
  }
40556
- var import_node_fs53, import_node_os22, import_node_path65;
40589
+ var import_node_fs54, import_node_os23, import_node_path66;
40557
40590
  var init_controller = __esm({
40558
40591
  "src/run-case/controller.ts"() {
40559
40592
  "use strict";
40560
- import_node_fs53 = require("fs");
40561
- import_node_os22 = __toESM(require("os"), 1);
40562
- import_node_path65 = __toESM(require("path"), 1);
40593
+ import_node_fs54 = require("fs");
40594
+ import_node_os23 = __toESM(require("os"), 1);
40595
+ import_node_path66 = __toESM(require("path"), 1);
40563
40596
  init_claude();
40564
40597
  init_stdout_splitter();
40565
40598
  init_permission_stdio();
@@ -40957,9 +40990,9 @@ Env (advanced):
40957
40990
  `;
40958
40991
 
40959
40992
  // src/index.ts
40960
- var import_node_path63 = __toESM(require("path"), 1);
40961
- var import_node_fs51 = __toESM(require("fs"), 1);
40962
- var import_node_os21 = __toESM(require("os"), 1);
40993
+ var import_node_path64 = __toESM(require("path"), 1);
40994
+ var import_node_fs52 = __toESM(require("fs"), 1);
40995
+ var import_node_os22 = __toESM(require("os"), 1);
40963
40996
 
40964
40997
  // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
40965
40998
  var byteToHex = [];
@@ -42003,7 +42036,7 @@ ssh -o ProxyCommand='~/.clawd/bin/clawd ssh-relay <A>' \\
42003
42036
  **\u4F60\u80FD\u8BBF\u95EE\u4EC0\u4E48**\uFF1A**\u6CA1\u6709\u76EE\u5F55\u7EA7\u9650\u5236**\u2014\u2014\u4F60\u4EE5 A \u673A\u5668\u4E0A\u8BE5 SSH \u7528\u6237\u7684\u6743\u9650\u8BBF\u95EE\u6587\u4EF6\uFF08\u7B49\u540C\u4E8E A \u7ED9\u4F60\u5F00\u4E86\u4E00\u4E2A shell\uFF09\u3002\u8BF7\u53EA\u5728 A \u6388\u6743/\u9700\u8981\u7684\u8303\u56F4\u5185\u64CD\u4F5C\uFF0C\u4E0D\u8981\u8D8A\u754C\u7FFB\u770B\u65E0\u5173\u76EE\u5F55\u3002
42004
42037
 
42005
42038
  **\u6CE8\u610F**\uFF1A
42006
- - \u6388\u6743\u662F\u5B9E\u65F6\u7684\uFF1AA \u64A4\u9500 sshAllowed \u540E\u4F60\u4E0B\u4E00\u6B21\u62E8\u53F7\u7ACB\u5373\u5931\u6548\uFF08authorized_keys \u7ACB\u5373\u5237\uFF09
42039
+ - \u6388\u6743\u662F\u5B9E\u65F6\u7684\uFF1AA \u64A4\u9500 remoteAccessAllowed \u540E\u4F60\u4E0B\u4E00\u6B21\u62E8\u53F7\u7ACB\u5373\u5931\u6548\uFF08authorized_keys \u7ACB\u5373\u5237\uFF09
42007
42040
  - \u62E8\u53F7\u88AB\u62D2\uFF08Permission denied\uFF09\u901A\u5E38\u662F A \u5C1A\u672A\u6388\u6743\u672C\u8BBE\u5907\uFF0C\u6216\u53CC\u65B9\u8001\u7248\u672C\u5EFA\u8054\u3001A \u7684 contact \u91CC\u6CA1\u6709\u672C\u673A pubkey\uFF08\u4EFB\u4E00\u65B9\u91CD\u65B0 device:connect \u4E00\u6B21\u5373\u8865\uFF09\uFF0C\u5982\u5B9E\u544A\u8BC9\u7528\u6237
42008
42041
  - \u6392\u969C\uFF1A\u672C\u673A debug \u65E5\u5FD7\u5728 \`~/.clawd/log/contact-ssh.log\`\uFF08\u7528\u6237\u53EF \`tail -f\` \u770B\u6BCF\u4E00\u6B65\uFF09\uFF0C\u4F60\u51FA\u9519\u65F6\u544A\u8BC9\u7528\u6237\u67E5\u8FD9\u4E2A\u6587\u4EF6
42009
42042
  `;
@@ -46760,8 +46793,8 @@ function turnStartInput(text) {
46760
46793
  const items = [];
46761
46794
  let leftover = text;
46762
46795
  for (const m2 of text.matchAll(SKILL_RE)) {
46763
- const [marker, name, path76] = m2;
46764
- items.push({ type: "skill", name, path: path76 });
46796
+ const [marker, name, path77] = m2;
46797
+ items.push({ type: "skill", name, path: path77 });
46765
46798
  leftover = leftover.replace(marker, "");
46766
46799
  }
46767
46800
  for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
@@ -48400,9 +48433,31 @@ async function writeInboxMcpConfig(args) {
48400
48433
  return cfgPath;
48401
48434
  }
48402
48435
 
48436
+ // src/peer-ops/mcp-config.ts
48437
+ var import_node_fs23 = __toESM(require("fs"), 1);
48438
+ var import_node_path20 = __toESM(require("path"), 1);
48439
+ function peerOpsMcpConfigPath(dataDir) {
48440
+ return import_node_path20.default.join(dataDir, "peer-ops.mcp.json");
48441
+ }
48442
+ function writePeerOpsMcpConfig(args) {
48443
+ const cfgPath = peerOpsMcpConfigPath(args.dataDir);
48444
+ const cfg = {
48445
+ _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",
48446
+ mcpServers: {
48447
+ "peer-ops": {
48448
+ command: "node",
48449
+ args: [args.serverScriptPath]
48450
+ }
48451
+ }
48452
+ };
48453
+ import_node_fs23.default.mkdirSync(args.dataDir, { recursive: true });
48454
+ import_node_fs23.default.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
48455
+ return cfgPath;
48456
+ }
48457
+
48403
48458
  // src/shift/store.ts
48404
48459
  var import_promises = __toESM(require("fs/promises"), 1);
48405
- var import_node_path20 = __toESM(require("path"), 1);
48460
+ var import_node_path21 = __toESM(require("path"), 1);
48406
48461
  var import_node_crypto6 = require("crypto");
48407
48462
 
48408
48463
  // src/shift/constants.ts
@@ -48485,7 +48540,7 @@ function createShiftStore(deps) {
48485
48540
  flushTimer = null;
48486
48541
  }
48487
48542
  const content = { version: 1, shifts };
48488
- await import_promises.default.mkdir(import_node_path20.default.dirname(deps.filePath), { recursive: true });
48543
+ await import_promises.default.mkdir(import_node_path21.default.dirname(deps.filePath), { recursive: true });
48489
48544
  const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
48490
48545
  await import_promises.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
48491
48546
  await import_promises.default.rename(tmp, deps.filePath);
@@ -49038,6 +49093,7 @@ function buildPersonaDispatchHandlers(deps) {
49038
49093
  }
49039
49094
 
49040
49095
  // src/dispatch/peer-forward.ts
49096
+ init_protocol();
49041
49097
  function wsUrlToHttp(url) {
49042
49098
  if (url.startsWith("wss://")) return "https://" + url.slice("wss://".length);
49043
49099
  if (url.startsWith("ws://")) return "http://" + url.slice("ws://".length);
@@ -49114,6 +49170,43 @@ async function forwardInboxPostToPeer(args) {
49114
49170
  }
49115
49171
  return { ok: true };
49116
49172
  }
49173
+ async function forwardPeerExecToPeer(args) {
49174
+ const f = args.fetchImpl ?? fetch;
49175
+ const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
49176
+ const url = `${base}/rpc/peerExec:run`;
49177
+ const tunnelFail = (msg) => ({
49178
+ type: "peerExec:run:ok",
49179
+ stdout: "",
49180
+ stderr: msg,
49181
+ exitCode: null,
49182
+ timedOut: false,
49183
+ stdoutTruncated: false,
49184
+ stderrTruncated: false
49185
+ });
49186
+ let res;
49187
+ try {
49188
+ res = await f(url, {
49189
+ method: "POST",
49190
+ headers: {
49191
+ "content-type": "application/json",
49192
+ authorization: `Bearer ${args.contact.connectToken}`
49193
+ },
49194
+ body: JSON.stringify({ command: args.command, ...args.timeoutMs ? { timeoutMs: args.timeoutMs } : {} })
49195
+ });
49196
+ } catch (err) {
49197
+ return tunnelFail(`forward to peer failed: ${err instanceof Error ? err.message : String(err)}`);
49198
+ }
49199
+ let json;
49200
+ try {
49201
+ json = await res.json();
49202
+ } catch {
49203
+ return tunnelFail(`peer returned non-JSON response (HTTP ${res.status})`);
49204
+ }
49205
+ if (json.ok === false) {
49206
+ throw new ClawdError(json.error || ERROR_CODES.INTERNAL, json.message || json.error);
49207
+ }
49208
+ return json.result;
49209
+ }
49117
49210
 
49118
49211
  // src/tools/codex-history.ts
49119
49212
  var import_node_child_process5 = require("child_process");
@@ -49301,13 +49394,13 @@ function mapSkillsListResponse(res) {
49301
49394
  const r = s ?? {};
49302
49395
  const name = str3(r.name);
49303
49396
  if (!name) continue;
49304
- const path76 = str3(r.path);
49397
+ const path77 = str3(r.path);
49305
49398
  const description = str3(r.description);
49306
49399
  const isPlugin = name.includes(":");
49307
49400
  out.push({
49308
49401
  name,
49309
49402
  source: isPlugin ? "plugin" : "project",
49310
- ...path76 ? { path: path76 } : {},
49403
+ ...path77 ? { path: path77 } : {},
49311
49404
  ...description ? { description } : {},
49312
49405
  ...isPlugin ? { plugin: name.split(":")[0] } : {}
49313
49406
  });
@@ -49346,23 +49439,23 @@ async function listCodexSkills(cwd, deps = {}) {
49346
49439
  }
49347
49440
 
49348
49441
  // src/workspace/browser.ts
49349
- var import_node_fs23 = __toESM(require("fs"), 1);
49442
+ var import_node_fs24 = __toESM(require("fs"), 1);
49350
49443
  var import_node_os8 = __toESM(require("os"), 1);
49351
- var import_node_path21 = __toESM(require("path"), 1);
49444
+ var import_node_path22 = __toESM(require("path"), 1);
49352
49445
  init_protocol();
49353
49446
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
49354
49447
  function resolveInsideCwd(cwd, subpath) {
49355
- const absCwd = import_node_path21.default.resolve(cwd);
49356
- const joined = import_node_path21.default.resolve(absCwd, subpath ?? ".");
49357
- const rel = import_node_path21.default.relative(absCwd, joined);
49358
- if (rel.startsWith("..") || import_node_path21.default.isAbsolute(rel)) {
49448
+ const absCwd = import_node_path22.default.resolve(cwd);
49449
+ const joined = import_node_path22.default.resolve(absCwd, subpath ?? ".");
49450
+ const rel = import_node_path22.default.relative(absCwd, joined);
49451
+ if (rel.startsWith("..") || import_node_path22.default.isAbsolute(rel)) {
49359
49452
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `path escapes cwd: ${subpath}`);
49360
49453
  }
49361
49454
  return joined;
49362
49455
  }
49363
49456
  function ensureCwd(cwd) {
49364
49457
  try {
49365
- const stat = import_node_fs23.default.statSync(cwd);
49458
+ const stat = import_node_fs24.default.statSync(cwd);
49366
49459
  if (!stat.isDirectory()) {
49367
49460
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `not a directory: ${cwd}`);
49368
49461
  }
@@ -49376,7 +49469,7 @@ var WorkspaceBrowser = class {
49376
49469
  const cwd = args.cwd && args.cwd.length > 0 ? args.cwd : import_node_os8.default.homedir();
49377
49470
  ensureCwd(cwd);
49378
49471
  const full = resolveInsideCwd(cwd, args.path);
49379
- const dirents = import_node_fs23.default.readdirSync(full, { withFileTypes: true });
49472
+ const dirents = import_node_fs24.default.readdirSync(full, { withFileTypes: true });
49380
49473
  const entries = [];
49381
49474
  for (const d of dirents) {
49382
49475
  if (!args.showHidden && d.name.startsWith(".")) continue;
@@ -49386,7 +49479,7 @@ var WorkspaceBrowser = class {
49386
49479
  mtime: ""
49387
49480
  };
49388
49481
  try {
49389
- const st = import_node_fs23.default.statSync(import_node_path21.default.join(full, d.name));
49482
+ const st = import_node_fs24.default.statSync(import_node_path22.default.join(full, d.name));
49390
49483
  entry.mtime = new Date(st.mtimeMs).toISOString();
49391
49484
  if (d.isFile()) entry.size = st.size;
49392
49485
  } catch {
@@ -49402,14 +49495,14 @@ var WorkspaceBrowser = class {
49402
49495
  read(args) {
49403
49496
  ensureCwd(args.cwd);
49404
49497
  const full = resolveInsideCwd(args.cwd, args.path);
49405
- const st = import_node_fs23.default.statSync(full);
49498
+ const st = import_node_fs24.default.statSync(full);
49406
49499
  if (!st.isFile()) {
49407
49500
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `not a file: ${args.path}`);
49408
49501
  }
49409
49502
  if (st.size > MAX_FILE_BYTES) {
49410
49503
  throw new ClawdError(ERROR_CODES.FILE_TOO_LARGE, `file > ${MAX_FILE_BYTES} bytes`);
49411
49504
  }
49412
- const buf = import_node_fs23.default.readFileSync(full);
49505
+ const buf = import_node_fs24.default.readFileSync(full);
49413
49506
  const isBinary = buf.includes(0);
49414
49507
  if (isBinary) {
49415
49508
  return {
@@ -49431,20 +49524,20 @@ var WorkspaceBrowser = class {
49431
49524
  };
49432
49525
 
49433
49526
  // src/skills/agents-scanner.ts
49434
- var import_node_fs24 = __toESM(require("fs"), 1);
49527
+ var import_node_fs25 = __toESM(require("fs"), 1);
49435
49528
  var import_node_os9 = __toESM(require("os"), 1);
49436
- var import_node_path22 = __toESM(require("path"), 1);
49529
+ var import_node_path23 = __toESM(require("path"), 1);
49437
49530
  var DEFAULT_POLICY_DIR_DARWIN = "/Library/Application Support/ClaudeCode/.claude/agents";
49438
49531
  function isDirLikeSync2(p2) {
49439
49532
  try {
49440
- return import_node_fs24.default.statSync(p2).isDirectory();
49533
+ return import_node_fs25.default.statSync(p2).isDirectory();
49441
49534
  } catch {
49442
49535
  return false;
49443
49536
  }
49444
49537
  }
49445
49538
  function fileExistsSync(p2) {
49446
49539
  try {
49447
- return import_node_fs24.default.statSync(p2).isFile();
49540
+ return import_node_fs25.default.statSync(p2).isFile();
49448
49541
  } catch {
49449
49542
  return false;
49450
49543
  }
@@ -49452,7 +49545,7 @@ function fileExistsSync(p2) {
49452
49545
  function parseAgentFile(filePath) {
49453
49546
  let content;
49454
49547
  try {
49455
- content = import_node_fs24.default.readFileSync(filePath, "utf8");
49548
+ content = import_node_fs25.default.readFileSync(filePath, "utf8");
49456
49549
  } catch {
49457
49550
  return {};
49458
49551
  }
@@ -49465,16 +49558,16 @@ function parseAgentFile(filePath) {
49465
49558
  function scanAgentsDir(dir, source, seen, out) {
49466
49559
  let entries;
49467
49560
  try {
49468
- entries = import_node_fs24.default.readdirSync(dir, { withFileTypes: true });
49561
+ entries = import_node_fs25.default.readdirSync(dir, { withFileTypes: true });
49469
49562
  } catch {
49470
49563
  return;
49471
49564
  }
49472
49565
  for (const ent of entries) {
49473
49566
  if (!ent.name.endsWith(".md")) continue;
49474
- if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path22.default.join(dir, ent.name)))) {
49567
+ if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path23.default.join(dir, ent.name)))) {
49475
49568
  continue;
49476
49569
  }
49477
- const filePath = import_node_path22.default.join(dir, ent.name);
49570
+ const filePath = import_node_path23.default.join(dir, ent.name);
49478
49571
  const baseName = ent.name.replace(/\.md$/, "");
49479
49572
  if (seen.has(baseName)) continue;
49480
49573
  seen.add(baseName);
@@ -49492,12 +49585,12 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
49492
49585
  function walk2(dir, namespaces) {
49493
49586
  let entries;
49494
49587
  try {
49495
- entries = import_node_fs24.default.readdirSync(dir, { withFileTypes: true });
49588
+ entries = import_node_fs25.default.readdirSync(dir, { withFileTypes: true });
49496
49589
  } catch {
49497
49590
  return;
49498
49591
  }
49499
49592
  for (const ent of entries) {
49500
- const childPath = import_node_path22.default.join(dir, ent.name);
49593
+ const childPath = import_node_path23.default.join(dir, ent.name);
49501
49594
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync2(childPath)) {
49502
49595
  walk2(childPath, [...namespaces, ent.name]);
49503
49596
  continue;
@@ -49522,13 +49615,13 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
49522
49615
  walk2(root, []);
49523
49616
  }
49524
49617
  function readInstalledPlugins2(home) {
49525
- const pluginsDir = import_node_path22.default.join(home, ".claude", "plugins");
49526
- const v2 = import_node_path22.default.join(pluginsDir, "installed_plugins_v2.json");
49527
- const v1 = import_node_path22.default.join(pluginsDir, "installed_plugins.json");
49618
+ const pluginsDir = import_node_path23.default.join(home, ".claude", "plugins");
49619
+ const v2 = import_node_path23.default.join(pluginsDir, "installed_plugins_v2.json");
49620
+ const v1 = import_node_path23.default.join(pluginsDir, "installed_plugins.json");
49528
49621
  let raw = null;
49529
49622
  for (const candidate of [v2, v1]) {
49530
49623
  try {
49531
- raw = import_node_fs24.default.readFileSync(candidate, "utf8");
49624
+ raw = import_node_fs25.default.readFileSync(candidate, "utf8");
49532
49625
  break;
49533
49626
  } catch {
49534
49627
  }
@@ -49553,19 +49646,19 @@ function readInstalledPlugins2(home) {
49553
49646
  return out;
49554
49647
  }
49555
49648
  function walkUpProjectAgentsDirs(startCwd, home, seen, out) {
49556
- let cur = import_node_path22.default.resolve(startCwd);
49557
- const fsRoot = import_node_path22.default.parse(cur).root;
49649
+ let cur = import_node_path23.default.resolve(startCwd);
49650
+ const fsRoot = import_node_path23.default.parse(cur).root;
49558
49651
  while (true) {
49559
- scanAgentsDir(import_node_path22.default.join(cur, ".claude", "agents"), "project", seen, out);
49652
+ scanAgentsDir(import_node_path23.default.join(cur, ".claude", "agents"), "project", seen, out);
49560
49653
  let hasGit = false;
49561
49654
  try {
49562
- hasGit = import_node_fs24.default.existsSync(import_node_path22.default.join(cur, ".git"));
49655
+ hasGit = import_node_fs25.default.existsSync(import_node_path23.default.join(cur, ".git"));
49563
49656
  } catch {
49564
49657
  }
49565
49658
  if (hasGit) return;
49566
49659
  if (cur === home) return;
49567
49660
  if (cur === fsRoot) return;
49568
- const parent = import_node_path22.default.dirname(cur);
49661
+ const parent = import_node_path23.default.dirname(cur);
49569
49662
  if (parent === cur) return;
49570
49663
  cur = parent;
49571
49664
  }
@@ -49600,7 +49693,7 @@ var AgentsScanner = class {
49600
49693
  }
49601
49694
  const fsBlock = [];
49602
49695
  scanAgentsDir(
49603
- import_node_path22.default.join(this.home, ".claude", "agents"),
49696
+ import_node_path23.default.join(this.home, ".claude", "agents"),
49604
49697
  "global",
49605
49698
  seen,
49606
49699
  fsBlock
@@ -49614,7 +49707,7 @@ var AgentsScanner = class {
49614
49707
  ...this.extraPluginRoots
49615
49708
  ];
49616
49709
  for (const { name, root } of plugins) {
49617
- const agentsRoot = import_node_path22.default.join(root, "agents");
49710
+ const agentsRoot = import_node_path23.default.join(root, "agents");
49618
49711
  scanPluginAgentsTree(agentsRoot, name, seen, fsBlock);
49619
49712
  }
49620
49713
  return [...builtinBlock, ...fsBlock];
@@ -49622,15 +49715,15 @@ var AgentsScanner = class {
49622
49715
  };
49623
49716
 
49624
49717
  // src/observer/session-observer.ts
49625
- var import_node_fs26 = __toESM(require("fs"), 1);
49718
+ var import_node_fs27 = __toESM(require("fs"), 1);
49626
49719
  var import_node_os11 = __toESM(require("os"), 1);
49627
- var import_node_path24 = __toESM(require("path"), 1);
49720
+ var import_node_path25 = __toESM(require("path"), 1);
49628
49721
  init_claude_history();
49629
49722
 
49630
49723
  // src/observer/subagent-meta-observer.ts
49631
- var import_node_fs25 = __toESM(require("fs"), 1);
49724
+ var import_node_fs26 = __toESM(require("fs"), 1);
49632
49725
  var import_node_os10 = __toESM(require("os"), 1);
49633
- var import_node_path23 = __toESM(require("path"), 1);
49726
+ var import_node_path24 = __toESM(require("path"), 1);
49634
49727
  init_claude_history();
49635
49728
  var META_RE = /^agent-([A-Za-z0-9_-]+)\.meta\.json$/;
49636
49729
  var SubagentMetaObserver = class {
@@ -49643,7 +49736,7 @@ var SubagentMetaObserver = class {
49643
49736
  watches = /* @__PURE__ */ new Map();
49644
49737
  // public for spec only:测试直接拼路径写假 meta.json;生产 start() 内部自己解析
49645
49738
  resolveSubagentDir(cwd, toolSessionId) {
49646
- return import_node_path23.default.join(
49739
+ return import_node_path24.default.join(
49647
49740
  this.home,
49648
49741
  ".claude",
49649
49742
  "projects",
@@ -49673,7 +49766,7 @@ var SubagentMetaObserver = class {
49673
49766
  attachWatcher(w2) {
49674
49767
  if (w2.watcher) return;
49675
49768
  try {
49676
- w2.watcher = import_node_fs25.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
49769
+ w2.watcher = import_node_fs26.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
49677
49770
  if (!name) return;
49678
49771
  const m2 = META_RE.exec(String(name));
49679
49772
  if (!m2) return;
@@ -49685,7 +49778,7 @@ var SubagentMetaObserver = class {
49685
49778
  scan(w2) {
49686
49779
  let entries;
49687
49780
  try {
49688
- entries = import_node_fs25.default.readdirSync(w2.dirPath);
49781
+ entries = import_node_fs26.default.readdirSync(w2.dirPath);
49689
49782
  } catch {
49690
49783
  return;
49691
49784
  }
@@ -49699,10 +49792,10 @@ var SubagentMetaObserver = class {
49699
49792
  if (!m2) return;
49700
49793
  const agentId = m2[1];
49701
49794
  if (w2.emitted.has(agentId)) return;
49702
- const file = import_node_path23.default.join(w2.dirPath, name);
49795
+ const file = import_node_path24.default.join(w2.dirPath, name);
49703
49796
  let raw;
49704
49797
  try {
49705
- raw = import_node_fs25.default.readFileSync(file, "utf8");
49798
+ raw = import_node_fs26.default.readFileSync(file, "utf8");
49706
49799
  } catch {
49707
49800
  return;
49708
49801
  }
@@ -49759,14 +49852,14 @@ var SessionObserver = class {
49759
49852
  metaObserver;
49760
49853
  resolveJsonlPath(cwd, toolSessionId, override) {
49761
49854
  if (override) return override;
49762
- return import_node_path24.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
49855
+ return import_node_path25.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
49763
49856
  }
49764
49857
  start(args) {
49765
49858
  this.stop(args.sessionId);
49766
49859
  const filePath = this.resolveJsonlPath(args.cwd, args.toolSessionId, args.jsonlPath);
49767
49860
  let size = 0;
49768
49861
  try {
49769
- size = import_node_fs26.default.statSync(filePath).size;
49862
+ size = import_node_fs27.default.statSync(filePath).size;
49770
49863
  } catch {
49771
49864
  }
49772
49865
  const w2 = {
@@ -49779,16 +49872,16 @@ var SessionObserver = class {
49779
49872
  adapter: args.adapter,
49780
49873
  prevIsRejectSentinel: false,
49781
49874
  toolSessionId: args.toolSessionId,
49782
- sawFile: import_node_fs26.default.existsSync(filePath),
49875
+ sawFile: import_node_fs27.default.existsSync(filePath),
49783
49876
  lastRelocateScanMs: 0,
49784
49877
  pendingCwdDerive: false,
49785
49878
  lastDeriveMs: 0
49786
49879
  };
49787
49880
  try {
49788
- import_node_fs26.default.mkdirSync(import_node_path24.default.dirname(filePath), { recursive: true });
49881
+ import_node_fs27.default.mkdirSync(import_node_path25.default.dirname(filePath), { recursive: true });
49789
49882
  } catch {
49790
49883
  }
49791
- w2.watcher = import_node_fs26.default.watch(import_node_path24.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
49884
+ w2.watcher = import_node_fs27.default.watch(import_node_path25.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
49792
49885
  if (!changedName || !w2.filePath.endsWith(changedName)) return;
49793
49886
  this.poll(w2);
49794
49887
  });
@@ -49811,7 +49904,7 @@ var SessionObserver = class {
49811
49904
  // 异常静默吞,不阻塞 watcher 启动
49812
49905
  hydrateMetaTail(w2, maxLines = 200) {
49813
49906
  try {
49814
- const raw = import_node_fs26.default.readFileSync(w2.filePath, "utf8");
49907
+ const raw = import_node_fs27.default.readFileSync(w2.filePath, "utf8");
49815
49908
  if (!raw) return;
49816
49909
  const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
49817
49910
  if (allLines.length === 0) return;
@@ -49835,7 +49928,7 @@ var SessionObserver = class {
49835
49928
  poll(w2) {
49836
49929
  let size = 0;
49837
49930
  try {
49838
- size = import_node_fs26.default.statSync(w2.filePath).size;
49931
+ size = import_node_fs27.default.statSync(w2.filePath).size;
49839
49932
  } catch {
49840
49933
  if (w2.sawFile) this.maybeRelocate(w2);
49841
49934
  return;
@@ -49846,11 +49939,11 @@ var SessionObserver = class {
49846
49939
  w2.buf = "";
49847
49940
  }
49848
49941
  if (size === w2.lastSize) return;
49849
- const fd = import_node_fs26.default.openSync(w2.filePath, "r");
49942
+ const fd = import_node_fs27.default.openSync(w2.filePath, "r");
49850
49943
  try {
49851
49944
  const len = size - w2.lastSize;
49852
49945
  const buf = Buffer.alloc(len);
49853
- import_node_fs26.default.readSync(fd, buf, 0, len, w2.lastSize);
49946
+ import_node_fs27.default.readSync(fd, buf, 0, len, w2.lastSize);
49854
49947
  w2.lastSize = size;
49855
49948
  w2.buf += buf.toString("utf8");
49856
49949
  let newlineIndex;
@@ -49873,7 +49966,7 @@ var SessionObserver = class {
49873
49966
  }
49874
49967
  }
49875
49968
  } finally {
49876
- import_node_fs26.default.closeSync(fd);
49969
+ import_node_fs27.default.closeSync(fd);
49877
49970
  }
49878
49971
  if (w2.pendingCwdDerive) this.maybeDeriveCwd(w2);
49879
49972
  }
@@ -49885,7 +49978,7 @@ var SessionObserver = class {
49885
49978
  const now = Date.now();
49886
49979
  if (now - w2.lastRelocateScanMs < 1e3) return;
49887
49980
  w2.lastRelocateScanMs = now;
49888
- const projectsRoot = import_node_path24.default.join(this.home, ".claude", "projects");
49981
+ const projectsRoot = import_node_path25.default.join(this.home, ".claude", "projects");
49889
49982
  const found = findTranscriptByToolSessionId(projectsRoot, w2.toolSessionId);
49890
49983
  if (!found || found === w2.filePath) return;
49891
49984
  try {
@@ -49894,7 +49987,7 @@ var SessionObserver = class {
49894
49987
  }
49895
49988
  w2.filePath = found;
49896
49989
  try {
49897
- w2.watcher = import_node_fs26.default.watch(import_node_path24.default.dirname(found), { persistent: false }, (_event, changedName) => {
49990
+ w2.watcher = import_node_fs27.default.watch(import_node_path25.default.dirname(found), { persistent: false }, (_event, changedName) => {
49898
49991
  if (!changedName || !w2.filePath.endsWith(changedName)) return;
49899
49992
  this.poll(w2);
49900
49993
  });
@@ -50486,6 +50579,8 @@ var EXPOSED_RPC_METHODS = [
50486
50579
  // B 端 dispatch MCP server 调 complete(带 outcome 触发 run 端 resolve)。详见
50487
50580
  // protocol/src/dispatch.ts。
50488
50581
  "personaDispatch.run",
50582
+ // 跨设备 exec(spec 2026-07-16):A 侧本地 cc 经 /api/rpc 调,带 peerDeviceId → daemon forward。
50583
+ "peerExec.run",
50489
50584
  "personaDispatch.complete",
50490
50585
  // clawd-shift v1 (spec 2026-06-24-clawd-shift):cc 子进程内的 shift MCP server 调
50491
50586
  // 这 5 个 method 管理 owner 的 shift(fire-and-forget 排班)。鉴权靠 body.personaId
@@ -50753,14 +50848,14 @@ async function authenticate(token, deps) {
50753
50848
  }
50754
50849
 
50755
50850
  // src/permission/capability-store.ts
50756
- var fs30 = __toESM(require("fs"), 1);
50757
- var path29 = __toESM(require("path"), 1);
50851
+ var fs31 = __toESM(require("fs"), 1);
50852
+ var path30 = __toESM(require("path"), 1);
50758
50853
  var CAPABILITIES_FILE_NAME = "capabilities.json";
50759
50854
  var FILE_VERSION = 1;
50760
50855
  var CapabilityStore = class {
50761
50856
  constructor(dataDir) {
50762
50857
  this.dataDir = dataDir;
50763
- fs30.mkdirSync(dataDir, { recursive: true });
50858
+ fs31.mkdirSync(dataDir, { recursive: true });
50764
50859
  this.cache = this.readFromDisk();
50765
50860
  }
50766
50861
  dataDir;
@@ -50784,13 +50879,13 @@ var CapabilityStore = class {
50784
50879
  this.flush();
50785
50880
  }
50786
50881
  filePath() {
50787
- return path29.join(this.dataDir, CAPABILITIES_FILE_NAME);
50882
+ return path30.join(this.dataDir, CAPABILITIES_FILE_NAME);
50788
50883
  }
50789
50884
  readFromDisk() {
50790
50885
  const file = this.filePath();
50791
50886
  let raw;
50792
50887
  try {
50793
- raw = fs30.readFileSync(file, "utf8");
50888
+ raw = fs31.readFileSync(file, "utf8");
50794
50889
  } catch (err) {
50795
50890
  if (err?.code === "ENOENT") return [];
50796
50891
  return [];
@@ -50818,10 +50913,10 @@ var CapabilityStore = class {
50818
50913
  }
50819
50914
  atomicWrite(file, content) {
50820
50915
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
50821
- fs30.writeFileSync(tmp, content, { mode: 384 });
50822
- fs30.renameSync(tmp, file);
50916
+ fs31.writeFileSync(tmp, content, { mode: 384 });
50917
+ fs31.renameSync(tmp, file);
50823
50918
  try {
50824
- fs30.chmodSync(file, 384);
50919
+ fs31.chmodSync(file, 384);
50825
50920
  } catch {
50826
50921
  }
50827
50922
  }
@@ -50914,14 +51009,14 @@ var CapabilityManager = class {
50914
51009
  };
50915
51010
 
50916
51011
  // src/permission/cleanup.ts
50917
- var fs31 = __toESM(require("fs"), 1);
51012
+ var fs32 = __toESM(require("fs"), 1);
50918
51013
  function cleanupGuestSessionsForCapability(cap, factory) {
50919
51014
  const removed = [];
50920
51015
  for (const g2 of cap.grants) {
50921
51016
  if (g2.resource.type !== "persona") continue;
50922
51017
  const dir = factory.vmGuestRoot(g2.resource.id, cap.id);
50923
51018
  try {
50924
- fs31.rmSync(dir, { recursive: true, force: true });
51019
+ fs32.rmSync(dir, { recursive: true, force: true });
50925
51020
  removed.push(dir);
50926
51021
  } catch {
50927
51022
  }
@@ -50930,13 +51025,13 @@ function cleanupGuestSessionsForCapability(cap, factory) {
50930
51025
  }
50931
51026
 
50932
51027
  // src/inbox/inbox-store.ts
50933
- var fs32 = __toESM(require("fs"), 1);
50934
- var path30 = __toESM(require("path"), 1);
51028
+ var fs33 = __toESM(require("fs"), 1);
51029
+ var path31 = __toESM(require("path"), 1);
50935
51030
  var INBOX_SUBDIR = "inbox";
50936
51031
  var InboxStore = class {
50937
51032
  constructor(dataDir) {
50938
51033
  this.dataDir = dataDir;
50939
- fs32.mkdirSync(this.dirPath(), { recursive: true });
51034
+ fs33.mkdirSync(this.dirPath(), { recursive: true });
50940
51035
  }
50941
51036
  dataDir;
50942
51037
  /**
@@ -50948,7 +51043,7 @@ var InboxStore = class {
50948
51043
  const file = this.filePath(peerDeviceId);
50949
51044
  let raw;
50950
51045
  try {
50951
- raw = fs32.readFileSync(file, "utf8");
51046
+ raw = fs33.readFileSync(file, "utf8");
50952
51047
  } catch (err) {
50953
51048
  if (err?.code === "ENOENT") return [];
50954
51049
  return [];
@@ -50964,7 +51059,7 @@ var InboxStore = class {
50964
51059
  const dir = this.dirPath();
50965
51060
  let entries;
50966
51061
  try {
50967
- entries = fs32.readdirSync(dir);
51062
+ entries = fs33.readdirSync(dir);
50968
51063
  } catch (err) {
50969
51064
  if (err?.code === "ENOENT") return [];
50970
51065
  return [];
@@ -50980,9 +51075,9 @@ var InboxStore = class {
50980
51075
  if (existing.some((m2) => m2.id === message.id)) return;
50981
51076
  const file = this.filePath(message.peerDeviceId);
50982
51077
  const line = JSON.stringify(message) + "\n";
50983
- fs32.appendFileSync(file, line, { mode: 384 });
51078
+ fs33.appendFileSync(file, line, { mode: 384 });
50984
51079
  try {
50985
- fs32.chmodSync(file, 384);
51080
+ fs33.chmodSync(file, 384);
50986
51081
  } catch {
50987
51082
  }
50988
51083
  }
@@ -51012,7 +51107,7 @@ var InboxStore = class {
51012
51107
  removeByPeerDeviceId(peerDeviceId) {
51013
51108
  const file = this.filePath(peerDeviceId);
51014
51109
  try {
51015
- fs32.unlinkSync(file);
51110
+ fs33.unlinkSync(file);
51016
51111
  } catch (err) {
51017
51112
  if (err?.code === "ENOENT") return;
51018
51113
  }
@@ -51021,18 +51116,18 @@ var InboxStore = class {
51021
51116
  const file = this.filePath(peerDeviceId);
51022
51117
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
51023
51118
  const content = messages.map((m2) => JSON.stringify(m2)).join("\n") + (messages.length > 0 ? "\n" : "");
51024
- fs32.writeFileSync(tmp, content, { mode: 384 });
51025
- fs32.renameSync(tmp, file);
51119
+ fs33.writeFileSync(tmp, content, { mode: 384 });
51120
+ fs33.renameSync(tmp, file);
51026
51121
  try {
51027
- fs32.chmodSync(file, 384);
51122
+ fs33.chmodSync(file, 384);
51028
51123
  } catch {
51029
51124
  }
51030
51125
  }
51031
51126
  dirPath() {
51032
- return path30.join(this.dataDir, INBOX_SUBDIR);
51127
+ return path31.join(this.dataDir, INBOX_SUBDIR);
51033
51128
  }
51034
51129
  filePath(peerDeviceId) {
51035
- return path30.join(this.dirPath(), `${peerDeviceId}.jsonl`);
51130
+ return path31.join(this.dirPath(), `${peerDeviceId}.jsonl`);
51036
51131
  }
51037
51132
  };
51038
51133
  function parseAllLines(raw) {
@@ -51120,8 +51215,8 @@ var InboxManager = class {
51120
51215
  };
51121
51216
 
51122
51217
  // src/state/contact-store.ts
51123
- var fs33 = __toESM(require("fs"), 1);
51124
- var path31 = __toESM(require("path"), 1);
51218
+ var fs34 = __toESM(require("fs"), 1);
51219
+ var path32 = __toESM(require("path"), 1);
51125
51220
  var FILE_NAME = "contacts.json";
51126
51221
  var ContactStore = class {
51127
51222
  constructor(dataDir) {
@@ -51131,10 +51226,10 @@ var ContactStore = class {
51131
51226
  contacts = /* @__PURE__ */ new Map();
51132
51227
  load() {
51133
51228
  this.contacts.clear();
51134
- const file = path31.join(this.dataDir, FILE_NAME);
51229
+ const file = path32.join(this.dataDir, FILE_NAME);
51135
51230
  let raw;
51136
51231
  try {
51137
- raw = fs33.readFileSync(file, "utf8");
51232
+ raw = fs34.readFileSync(file, "utf8");
51138
51233
  } catch (err) {
51139
51234
  if (err?.code !== "ENOENT") this.renameBak(file);
51140
51235
  return;
@@ -51189,35 +51284,35 @@ var ContactStore = class {
51189
51284
  return true;
51190
51285
  }
51191
51286
  /**
51192
- * 更新单条 contact 的 SSH 授权开关(对齐 setPin pattern)。T-29 去沙箱后只改 sshAllowed
51287
+ * 更新单条 contact 的 SSH 授权开关(对齐 setPin pattern)。T-29 去沙箱后只改 remoteAccessAllowed
51193
51288
  * exposedDirs 已废弃,本方法不再触碰它(保留 contact 原值不动)。
51194
51289
  * @returns 是否命中:deviceId 不存在返 false;命中即 flush.
51195
51290
  */
51196
- setSshAccess(deviceId, opts) {
51291
+ setRemoteAccess(deviceId, opts) {
51197
51292
  const existing = this.contacts.get(deviceId);
51198
51293
  if (!existing) return false;
51199
51294
  this.contacts.set(deviceId, {
51200
51295
  ...existing,
51201
- sshAllowed: opts.sshAllowed
51296
+ remoteAccessAllowed: opts.remoteAccessAllowed
51202
51297
  });
51203
51298
  this.flush();
51204
51299
  return true;
51205
51300
  }
51206
51301
  flush() {
51207
- const file = path31.join(this.dataDir, FILE_NAME);
51302
+ const file = path32.join(this.dataDir, FILE_NAME);
51208
51303
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
51209
51304
  const content = JSON.stringify(
51210
51305
  { contacts: Array.from(this.contacts.values()) },
51211
51306
  null,
51212
51307
  2
51213
51308
  );
51214
- fs33.mkdirSync(this.dataDir, { recursive: true });
51215
- fs33.writeFileSync(tmp, content, { mode: 384 });
51216
- fs33.renameSync(tmp, file);
51309
+ fs34.mkdirSync(this.dataDir, { recursive: true });
51310
+ fs34.writeFileSync(tmp, content, { mode: 384 });
51311
+ fs34.renameSync(tmp, file);
51217
51312
  }
51218
51313
  renameBak(file) {
51219
51314
  try {
51220
- fs33.renameSync(file, `${file}.bak`);
51315
+ fs34.renameSync(file, `${file}.bak`);
51221
51316
  } catch {
51222
51317
  }
51223
51318
  }
@@ -51362,7 +51457,7 @@ async function autoReverseContact(args) {
51362
51457
  grants,
51363
51458
  addedAt: now(),
51364
51459
  pinnedAt: existing?.pinnedAt ?? null,
51365
- sshAllowed: existing?.sshAllowed ?? false,
51460
+ remoteAccessAllowed: existing?.remoteAccessAllowed ?? false,
51366
51461
  // 建联交换:缺省不落 undefined 占位(JSON 持久化干净)。见 …#建联交换清单
51367
51462
  ...sshPublicKey ? { sshPublicKey } : {},
51368
51463
  ...osUser ? { osUser } : {},
@@ -51370,7 +51465,7 @@ async function autoReverseContact(args) {
51370
51465
  };
51371
51466
  args.store.upsert(base);
51372
51467
  args.broadcast({ type: "contact:added", contact: base });
51373
- if (base.sshAllowed && sshPublicKey && sshPublicKey !== existing?.sshPublicKey) {
51468
+ if (base.remoteAccessAllowed && sshPublicKey && sshPublicKey !== existing?.sshPublicKey) {
51374
51469
  args.rebuildAuthorizedKeys?.();
51375
51470
  }
51376
51471
  if (!args.exchangeToken) return;
@@ -51387,61 +51482,61 @@ async function autoReverseContact(args) {
51387
51482
  }
51388
51483
 
51389
51484
  // src/migrations/2026-05-20-flatten-sessions.ts
51390
- var fs34 = __toESM(require("fs"), 1);
51391
- var path32 = __toESM(require("path"), 1);
51485
+ var fs35 = __toESM(require("fs"), 1);
51486
+ var path33 = __toESM(require("path"), 1);
51392
51487
  var MIGRATION_FLAG_NAME = ".migration.v1.done";
51393
51488
  function migrateFlattenSessions(opts) {
51394
51489
  const dataDir = opts.dataDir;
51395
51490
  const now = opts.now ?? Date.now;
51396
- const sessionsDir = path32.join(dataDir, "sessions");
51397
- const flagPath = path32.join(sessionsDir, MIGRATION_FLAG_NAME);
51491
+ const sessionsDir = path33.join(dataDir, "sessions");
51492
+ const flagPath = path33.join(sessionsDir, MIGRATION_FLAG_NAME);
51398
51493
  if (existsSync3(flagPath)) {
51399
51494
  return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
51400
51495
  }
51401
51496
  let movedBare = 0;
51402
51497
  let movedVmOwner = 0;
51403
51498
  let archivedListener = 0;
51404
- const defaultDir = path32.join(sessionsDir, "default");
51499
+ const defaultDir = path33.join(sessionsDir, "default");
51405
51500
  if (existsSync3(defaultDir)) {
51406
51501
  for (const entry of readdirSafe(defaultDir)) {
51407
51502
  if (!entry.endsWith(".json")) continue;
51408
- const src = path32.join(defaultDir, entry);
51409
- const dst = path32.join(sessionsDir, entry);
51410
- fs34.renameSync(src, dst);
51503
+ const src = path33.join(defaultDir, entry);
51504
+ const dst = path33.join(sessionsDir, entry);
51505
+ fs35.renameSync(src, dst);
51411
51506
  movedBare += 1;
51412
51507
  }
51413
51508
  rmdirIfEmpty(defaultDir);
51414
51509
  }
51415
51510
  for (const pid of readdirSafe(sessionsDir)) {
51416
- const personaDir = path32.join(sessionsDir, pid);
51511
+ const personaDir = path33.join(sessionsDir, pid);
51417
51512
  if (!isDir(personaDir)) continue;
51418
51513
  if (pid === "default") continue;
51419
- const ownerSrc = path32.join(personaDir, "owner");
51514
+ const ownerSrc = path33.join(personaDir, "owner");
51420
51515
  if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
51421
- const ownerDst = path32.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
51422
- fs34.mkdirSync(ownerDst, { recursive: true });
51516
+ const ownerDst = path33.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
51517
+ fs35.mkdirSync(ownerDst, { recursive: true });
51423
51518
  for (const file of readdirSafe(ownerSrc)) {
51424
51519
  if (!file.endsWith(".json")) continue;
51425
- fs34.renameSync(path32.join(ownerSrc, file), path32.join(ownerDst, file));
51520
+ fs35.renameSync(path33.join(ownerSrc, file), path33.join(ownerDst, file));
51426
51521
  movedVmOwner += 1;
51427
51522
  }
51428
51523
  rmdirIfEmpty(ownerSrc);
51429
51524
  }
51430
- const listenerSrc = path32.join(personaDir, "listener");
51525
+ const listenerSrc = path33.join(personaDir, "listener");
51431
51526
  if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
51432
- const archiveDst = path32.join(dataDir, ".legacy", `listener-${pid}`);
51433
- fs34.mkdirSync(archiveDst, { recursive: true });
51527
+ const archiveDst = path33.join(dataDir, ".legacy", `listener-${pid}`);
51528
+ fs35.mkdirSync(archiveDst, { recursive: true });
51434
51529
  for (const file of readdirSafe(listenerSrc)) {
51435
51530
  if (!file.endsWith(".json")) continue;
51436
- fs34.renameSync(path32.join(listenerSrc, file), path32.join(archiveDst, file));
51531
+ fs35.renameSync(path33.join(listenerSrc, file), path33.join(archiveDst, file));
51437
51532
  archivedListener += 1;
51438
51533
  }
51439
51534
  rmdirIfEmpty(listenerSrc);
51440
51535
  }
51441
51536
  rmdirIfEmpty(personaDir);
51442
51537
  }
51443
- fs34.mkdirSync(sessionsDir, { recursive: true });
51444
- fs34.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
51538
+ fs35.mkdirSync(sessionsDir, { recursive: true });
51539
+ fs35.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
51445
51540
  return {
51446
51541
  skipped: false,
51447
51542
  flagWritten: true,
@@ -51452,7 +51547,7 @@ function migrateFlattenSessions(opts) {
51452
51547
  }
51453
51548
  function existsSync3(p2) {
51454
51549
  try {
51455
- fs34.statSync(p2);
51550
+ fs35.statSync(p2);
51456
51551
  return true;
51457
51552
  } catch {
51458
51553
  return false;
@@ -51460,31 +51555,31 @@ function existsSync3(p2) {
51460
51555
  }
51461
51556
  function isDir(p2) {
51462
51557
  try {
51463
- return fs34.statSync(p2).isDirectory();
51558
+ return fs35.statSync(p2).isDirectory();
51464
51559
  } catch {
51465
51560
  return false;
51466
51561
  }
51467
51562
  }
51468
51563
  function readdirSafe(p2) {
51469
51564
  try {
51470
- return fs34.readdirSync(p2);
51565
+ return fs35.readdirSync(p2);
51471
51566
  } catch {
51472
51567
  return [];
51473
51568
  }
51474
51569
  }
51475
51570
  function rmdirIfEmpty(p2) {
51476
51571
  try {
51477
- fs34.rmdirSync(p2);
51572
+ fs35.rmdirSync(p2);
51478
51573
  } catch {
51479
51574
  }
51480
51575
  }
51481
51576
 
51482
51577
  // src/transport/http-router.ts
51483
- var import_node_fs28 = __toESM(require("fs"), 1);
51484
- var import_node_path28 = __toESM(require("path"), 1);
51578
+ var import_node_fs29 = __toESM(require("fs"), 1);
51579
+ var import_node_path29 = __toESM(require("path"), 1);
51485
51580
 
51486
51581
  // src/attachment/mime.ts
51487
- var import_node_path25 = __toESM(require("path"), 1);
51582
+ var import_node_path26 = __toESM(require("path"), 1);
51488
51583
  var TEXT_PLAIN = "text/plain; charset=utf-8";
51489
51584
  var EXT_TO_NATIVE_MIME = {
51490
51585
  // 图片
@@ -51591,7 +51686,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
51591
51686
  ".mk"
51592
51687
  ]);
51593
51688
  function lookupMime(filePathOrName) {
51594
- const ext = import_node_path25.default.extname(filePathOrName).toLowerCase();
51689
+ const ext = import_node_path26.default.extname(filePathOrName).toLowerCase();
51595
51690
  if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
51596
51691
  if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
51597
51692
  return "application/octet-stream";
@@ -51660,8 +51755,8 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
51660
51755
  }
51661
51756
 
51662
51757
  // src/attachment/upload.ts
51663
- var import_node_fs27 = __toESM(require("fs"), 1);
51664
- var import_node_path26 = __toESM(require("path"), 1);
51758
+ var import_node_fs28 = __toESM(require("fs"), 1);
51759
+ var import_node_path27 = __toESM(require("path"), 1);
51665
51760
  var import_node_crypto8 = __toESM(require("crypto"), 1);
51666
51761
  var import_promises2 = require("stream/promises");
51667
51762
  var UploadError = class extends Error {
@@ -51673,22 +51768,22 @@ var UploadError = class extends Error {
51673
51768
  code;
51674
51769
  };
51675
51770
  function assertValidFileName(fileName) {
51676
- if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path26.default.basename(fileName)) {
51771
+ if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path27.default.basename(fileName)) {
51677
51772
  throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
51678
51773
  }
51679
51774
  }
51680
51775
  var HASH_PREFIX_LEN = 16;
51681
51776
  async function writeUploadedAttachment(args) {
51682
51777
  assertValidFileName(args.fileName);
51683
- const attachmentsRoot = import_node_path26.default.join(args.sessionDir, ".attachments");
51778
+ const attachmentsRoot = import_node_path27.default.join(args.sessionDir, ".attachments");
51684
51779
  try {
51685
- import_node_fs27.default.mkdirSync(attachmentsRoot, { recursive: true });
51780
+ import_node_fs28.default.mkdirSync(attachmentsRoot, { recursive: true });
51686
51781
  } catch (err) {
51687
51782
  throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
51688
51783
  }
51689
51784
  const hasher = import_node_crypto8.default.createHash("sha256");
51690
51785
  let actualSize = 0;
51691
- const tmpPath = import_node_path26.default.join(
51786
+ const tmpPath = import_node_path27.default.join(
51692
51787
  attachmentsRoot,
51693
51788
  `.upload-${process.pid}-${Date.now()}-${import_node_crypto8.default.randomBytes(4).toString("hex")}`
51694
51789
  );
@@ -51703,18 +51798,18 @@ async function writeUploadedAttachment(args) {
51703
51798
  yield buf;
51704
51799
  }
51705
51800
  },
51706
- import_node_fs27.default.createWriteStream(tmpPath, { mode: 384 })
51801
+ import_node_fs28.default.createWriteStream(tmpPath, { mode: 384 })
51707
51802
  );
51708
51803
  } catch (err) {
51709
51804
  try {
51710
- import_node_fs27.default.unlinkSync(tmpPath);
51805
+ import_node_fs28.default.unlinkSync(tmpPath);
51711
51806
  } catch {
51712
51807
  }
51713
51808
  throw new UploadError("STORAGE_ERROR", `write failed: ${err.message}`);
51714
51809
  }
51715
51810
  if (actualSize !== args.contentLength) {
51716
51811
  try {
51717
- import_node_fs27.default.unlinkSync(tmpPath);
51812
+ import_node_fs28.default.unlinkSync(tmpPath);
51718
51813
  } catch {
51719
51814
  }
51720
51815
  throw new UploadError(
@@ -51723,35 +51818,35 @@ async function writeUploadedAttachment(args) {
51723
51818
  );
51724
51819
  }
51725
51820
  const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
51726
- const hashDir = import_node_path26.default.join(attachmentsRoot, attachmentId);
51821
+ const hashDir = import_node_path27.default.join(attachmentsRoot, attachmentId);
51727
51822
  let finalFileName;
51728
51823
  let hashDirExists = false;
51729
51824
  try {
51730
- hashDirExists = import_node_fs27.default.statSync(hashDir).isDirectory();
51825
+ hashDirExists = import_node_fs28.default.statSync(hashDir).isDirectory();
51731
51826
  } catch {
51732
51827
  }
51733
51828
  if (hashDirExists) {
51734
- const existing = import_node_fs27.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
51829
+ const existing = import_node_fs28.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
51735
51830
  finalFileName = existing[0] ?? args.fileName;
51736
51831
  try {
51737
- import_node_fs27.default.unlinkSync(tmpPath);
51832
+ import_node_fs28.default.unlinkSync(tmpPath);
51738
51833
  } catch {
51739
51834
  }
51740
51835
  } else {
51741
51836
  try {
51742
- import_node_fs27.default.mkdirSync(hashDir, { recursive: true });
51837
+ import_node_fs28.default.mkdirSync(hashDir, { recursive: true });
51743
51838
  finalFileName = args.fileName;
51744
- import_node_fs27.default.renameSync(tmpPath, import_node_path26.default.join(hashDir, finalFileName));
51839
+ import_node_fs28.default.renameSync(tmpPath, import_node_path27.default.join(hashDir, finalFileName));
51745
51840
  } catch (err) {
51746
51841
  try {
51747
- import_node_fs27.default.unlinkSync(tmpPath);
51842
+ import_node_fs28.default.unlinkSync(tmpPath);
51748
51843
  } catch {
51749
51844
  }
51750
51845
  throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
51751
51846
  }
51752
51847
  }
51753
- const absPath = import_node_path26.default.join(hashDir, finalFileName);
51754
- const relPath = import_node_path26.default.relative(args.sessionDir, absPath);
51848
+ const absPath = import_node_path27.default.join(hashDir, finalFileName);
51849
+ const relPath = import_node_path27.default.relative(args.sessionDir, absPath);
51755
51850
  args.groupFileStore.upsert(args.scope, args.sessionId, {
51756
51851
  relPath: absPath,
51757
51852
  // 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
@@ -51765,7 +51860,7 @@ async function writeUploadedAttachment(args) {
51765
51860
 
51766
51861
  // src/extension/import.ts
51767
51862
  var import_promises3 = __toESM(require("fs/promises"), 1);
51768
- var import_node_path27 = __toESM(require("path"), 1);
51863
+ var import_node_path28 = __toESM(require("path"), 1);
51769
51864
  var import_node_os12 = __toESM(require("os"), 1);
51770
51865
  var import_jszip = __toESM(require_lib3(), 1);
51771
51866
  var ImportError = class extends Error {
@@ -51783,7 +51878,7 @@ async function importZip(buf, root) {
51783
51878
  throw new ImportError("ZIP_INVALID", `failed to load zip: ${e.message}`);
51784
51879
  }
51785
51880
  for (const name of Object.keys(zip.files)) {
51786
- if (name.includes("..") || name.startsWith("/") || import_node_path27.default.isAbsolute(name)) {
51881
+ if (name.includes("..") || name.startsWith("/") || import_node_path28.default.isAbsolute(name)) {
51787
51882
  throw new ImportError("ZIP_INVALID", `unsafe zip entry path: ${name}`);
51788
51883
  }
51789
51884
  }
@@ -51816,7 +51911,7 @@ async function importZip(buf, root) {
51816
51911
  );
51817
51912
  }
51818
51913
  }
51819
- const destDir = import_node_path27.default.join(root, manifest.id);
51914
+ const destDir = import_node_path28.default.join(root, manifest.id);
51820
51915
  let destExists = false;
51821
51916
  try {
51822
51917
  await import_promises3.default.access(destDir);
@@ -51826,15 +51921,15 @@ async function importZip(buf, root) {
51826
51921
  if (destExists) {
51827
51922
  throw new ImportError("ALREADY_EXISTS", `extension ${manifest.id} already installed`);
51828
51923
  }
51829
- const stage = await import_promises3.default.mkdtemp(import_node_path27.default.join(import_node_os12.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
51924
+ const stage = await import_promises3.default.mkdtemp(import_node_path28.default.join(import_node_os12.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
51830
51925
  try {
51831
51926
  for (const [name, entry] of Object.entries(zip.files)) {
51832
- const dest = import_node_path27.default.join(stage, name);
51927
+ const dest = import_node_path28.default.join(stage, name);
51833
51928
  if (entry.dir) {
51834
51929
  await import_promises3.default.mkdir(dest, { recursive: true });
51835
51930
  continue;
51836
51931
  }
51837
- await import_promises3.default.mkdir(import_node_path27.default.dirname(dest), { recursive: true });
51932
+ await import_promises3.default.mkdir(import_node_path28.default.dirname(dest), { recursive: true });
51838
51933
  const content = await entry.async("nodebuffer");
51839
51934
  await import_promises3.default.writeFile(dest, content);
51840
51935
  }
@@ -51865,7 +51960,7 @@ var SHARE_UI_ASSET_MIME = {
51865
51960
  ".wasm": "application/wasm"
51866
51961
  };
51867
51962
  function shareUiAssetMime(filePath) {
51868
- const ext = import_node_path28.default.extname(filePath).toLowerCase();
51963
+ const ext = import_node_path29.default.extname(filePath).toLowerCase();
51869
51964
  return SHARE_UI_ASSET_MIME[ext] ?? lookupMime(filePath);
51870
51965
  }
51871
51966
  var DISPATCH_HEARTBEAT_MS = 25e3;
@@ -51952,7 +52047,7 @@ function isValidUploadFileName(fileName) {
51952
52047
  if (fileName === "." || fileName === "..") return false;
51953
52048
  if (fileName.startsWith(".")) return false;
51954
52049
  if (fileName.includes("/") || fileName.includes("\\")) return false;
51955
- return fileName === import_node_path28.default.basename(fileName);
52050
+ return fileName === import_node_path29.default.basename(fileName);
51956
52051
  }
51957
52052
  function createHttpRouter(deps) {
51958
52053
  return async (req, res) => {
@@ -52024,7 +52119,7 @@ function createHttpRouter(deps) {
52024
52119
  }
52025
52120
  let buf;
52026
52121
  try {
52027
- buf = await import_node_fs28.default.promises.readFile(abs);
52122
+ buf = await import_node_fs29.default.promises.readFile(abs);
52028
52123
  } catch {
52029
52124
  sendJson(res, 404, { code: "NOT_FOUND", message: "asset not found" });
52030
52125
  return true;
@@ -52248,7 +52343,7 @@ function createHttpRouter(deps) {
52248
52343
  sendHtml(res, statusByCode[r.code], loader.renderErrorHtml(r.code, msgByCode[r.code]));
52249
52344
  return true;
52250
52345
  }
52251
- sendHtml(res, 200, loader.renderViewerHtml(import_node_path28.default.basename(r.absPath)));
52346
+ sendHtml(res, 200, loader.renderViewerHtml(import_node_path29.default.basename(r.absPath)));
52252
52347
  return true;
52253
52348
  }
52254
52349
  const ctx = deps.authResolver.resolveFromHeader(
@@ -52369,7 +52464,7 @@ function createHttpRouter(deps) {
52369
52464
  return true;
52370
52465
  }
52371
52466
  let absPath;
52372
- if (import_node_path28.default.isAbsolute(pathParam)) {
52467
+ if (import_node_path29.default.isAbsolute(pathParam)) {
52373
52468
  absPath = pathParam;
52374
52469
  } else if (deps.sessionStore) {
52375
52470
  const file = deps.sessionStore.read(sid);
@@ -52377,7 +52472,7 @@ function createHttpRouter(deps) {
52377
52472
  sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sid} not found` });
52378
52473
  return true;
52379
52474
  }
52380
- absPath = import_node_path28.default.join(file.cwd, pathParam);
52475
+ absPath = import_node_path29.default.join(file.cwd, pathParam);
52381
52476
  } else {
52382
52477
  sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "sessionStore not wired" }));
52383
52478
  return true;
@@ -52464,7 +52559,7 @@ function withCtx(ctx, body) {
52464
52559
  function streamFile(res, absPath, logger) {
52465
52560
  let stat;
52466
52561
  try {
52467
- stat = import_node_fs28.default.statSync(absPath);
52562
+ stat = import_node_fs29.default.statSync(absPath);
52468
52563
  } catch (err) {
52469
52564
  const code = err?.code;
52470
52565
  if (code === "ENOENT") {
@@ -52479,7 +52574,7 @@ function streamFile(res, absPath, logger) {
52479
52574
  return;
52480
52575
  }
52481
52576
  const mime = lookupMime(absPath);
52482
- const basename = import_node_path28.default.basename(absPath);
52577
+ const basename = import_node_path29.default.basename(absPath);
52483
52578
  res.writeHead(200, {
52484
52579
  "Content-Type": mime,
52485
52580
  "Content-Length": String(stat.size),
@@ -52487,7 +52582,7 @@ function streamFile(res, absPath, logger) {
52487
52582
  // 防止浏览器把任意 mime 当 html 渲染
52488
52583
  "X-Content-Type-Options": "nosniff"
52489
52584
  });
52490
- const stream = import_node_fs28.default.createReadStream(absPath);
52585
+ const stream = import_node_fs29.default.createReadStream(absPath);
52491
52586
  stream.on("error", (err) => {
52492
52587
  logger?.warn("streamFile read error", { absPath, err: err.message });
52493
52588
  res.destroy();
@@ -52496,8 +52591,8 @@ function streamFile(res, absPath, logger) {
52496
52591
  }
52497
52592
 
52498
52593
  // src/attachment/gc.ts
52499
- var import_node_fs29 = __toESM(require("fs"), 1);
52500
- var import_node_path29 = __toESM(require("path"), 1);
52594
+ var import_node_fs30 = __toESM(require("fs"), 1);
52595
+ var import_node_path30 = __toESM(require("path"), 1);
52501
52596
  var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
52502
52597
  function runAttachmentGc(args) {
52503
52598
  const now = (args.now ?? Date.now)();
@@ -52506,38 +52601,38 @@ function runAttachmentGc(args) {
52506
52601
  for (const { scope, sessionId } of args.sessionScopes) {
52507
52602
  for (const entry of args.groupFileStore.list(scope, sessionId)) {
52508
52603
  if (entry.stale) continue;
52509
- if (import_node_path29.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
52604
+ if (import_node_path30.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
52510
52605
  }
52511
52606
  }
52512
52607
  for (const { scope, sessionId } of args.sessionScopes) {
52513
- const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path29.default.join(
52608
+ const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path30.default.join(
52514
52609
  args.dataDir,
52515
52610
  "sessions",
52516
52611
  ...scopeSubPath(scope).map(safeFileName),
52517
52612
  safeFileName(sessionId)
52518
52613
  );
52519
- const attRoot = import_node_path29.default.join(sessionDir, ".attachments");
52614
+ const attRoot = import_node_path30.default.join(sessionDir, ".attachments");
52520
52615
  let hashDirs;
52521
52616
  try {
52522
- hashDirs = import_node_fs29.default.readdirSync(attRoot);
52617
+ hashDirs = import_node_fs30.default.readdirSync(attRoot);
52523
52618
  } catch (err) {
52524
52619
  if (err.code === "ENOENT") continue;
52525
52620
  args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
52526
52621
  continue;
52527
52622
  }
52528
52623
  for (const hashDir of hashDirs) {
52529
- const hashDirAbs = import_node_path29.default.join(attRoot, hashDir);
52624
+ const hashDirAbs = import_node_path30.default.join(attRoot, hashDir);
52530
52625
  let files;
52531
52626
  try {
52532
- files = import_node_fs29.default.readdirSync(hashDirAbs);
52627
+ files = import_node_fs30.default.readdirSync(hashDirAbs);
52533
52628
  } catch {
52534
52629
  continue;
52535
52630
  }
52536
52631
  for (const name of files) {
52537
- const file = import_node_path29.default.join(hashDirAbs, name);
52632
+ const file = import_node_path30.default.join(hashDirAbs, name);
52538
52633
  let stat;
52539
52634
  try {
52540
- stat = import_node_fs29.default.statSync(file);
52635
+ stat = import_node_fs30.default.statSync(file);
52541
52636
  } catch {
52542
52637
  continue;
52543
52638
  }
@@ -52546,26 +52641,26 @@ function runAttachmentGc(args) {
52546
52641
  if (age < ttlMs) continue;
52547
52642
  if (liveAbs.has(file)) continue;
52548
52643
  try {
52549
- import_node_fs29.default.unlinkSync(file);
52644
+ import_node_fs30.default.unlinkSync(file);
52550
52645
  } catch (err) {
52551
52646
  args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
52552
52647
  }
52553
52648
  }
52554
52649
  try {
52555
- if (import_node_fs29.default.readdirSync(hashDirAbs).length === 0) import_node_fs29.default.rmdirSync(hashDirAbs);
52650
+ if (import_node_fs30.default.readdirSync(hashDirAbs).length === 0) import_node_fs30.default.rmdirSync(hashDirAbs);
52556
52651
  } catch {
52557
52652
  }
52558
52653
  }
52559
52654
  try {
52560
- if (import_node_fs29.default.readdirSync(attRoot).length === 0) import_node_fs29.default.rmdirSync(attRoot);
52655
+ if (import_node_fs30.default.readdirSync(attRoot).length === 0) import_node_fs30.default.rmdirSync(attRoot);
52561
52656
  } catch {
52562
52657
  }
52563
52658
  }
52564
52659
  }
52565
52660
 
52566
52661
  // src/attachment/group.ts
52567
- var import_node_fs30 = __toESM(require("fs"), 1);
52568
- var import_node_path30 = __toESM(require("path"), 1);
52662
+ var import_node_fs31 = __toESM(require("fs"), 1);
52663
+ var import_node_path31 = __toESM(require("path"), 1);
52569
52664
  var import_node_crypto9 = __toESM(require("crypto"), 1);
52570
52665
  init_protocol();
52571
52666
  var GroupFileStore = class {
@@ -52577,11 +52672,11 @@ var GroupFileStore = class {
52577
52672
  this.logger = opts.logger;
52578
52673
  }
52579
52674
  rootForScope(scope) {
52580
- return import_node_path30.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
52675
+ return import_node_path31.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
52581
52676
  }
52582
52677
  /** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
52583
52678
  filePath(scope, sessionId) {
52584
- return import_node_path30.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
52679
+ return import_node_path31.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
52585
52680
  }
52586
52681
  cacheKey(scope, sessionId) {
52587
52682
  return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
@@ -52590,7 +52685,7 @@ var GroupFileStore = class {
52590
52685
  readFile(scope, sessionId) {
52591
52686
  const file = this.filePath(scope, sessionId);
52592
52687
  try {
52593
- const raw = import_node_fs30.default.readFileSync(file, "utf8");
52688
+ const raw = import_node_fs31.default.readFileSync(file, "utf8");
52594
52689
  const parsed = JSON.parse(raw);
52595
52690
  if (!Array.isArray(parsed)) {
52596
52691
  this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
@@ -52616,10 +52711,10 @@ var GroupFileStore = class {
52616
52711
  }
52617
52712
  writeFile(scope, sessionId, entries) {
52618
52713
  const file = this.filePath(scope, sessionId);
52619
- import_node_fs30.default.mkdirSync(import_node_path30.default.dirname(file), { recursive: true });
52714
+ import_node_fs31.default.mkdirSync(import_node_path31.default.dirname(file), { recursive: true });
52620
52715
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
52621
- import_node_fs30.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
52622
- import_node_fs30.default.renameSync(tmp, file);
52716
+ import_node_fs31.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
52717
+ import_node_fs31.default.renameSync(tmp, file);
52623
52718
  }
52624
52719
  /** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
52625
52720
  list(scope, sessionId) {
@@ -52705,10 +52800,10 @@ var GroupFileStore = class {
52705
52800
  };
52706
52801
 
52707
52802
  // src/discovery/state-file.ts
52708
- var import_node_fs31 = __toESM(require("fs"), 1);
52709
- var import_node_path31 = __toESM(require("path"), 1);
52803
+ var import_node_fs32 = __toESM(require("fs"), 1);
52804
+ var import_node_path32 = __toESM(require("path"), 1);
52710
52805
  function defaultStateFilePath(dataDir) {
52711
- return import_node_path31.default.join(dataDir, "state.json");
52806
+ return import_node_path32.default.join(dataDir, "state.json");
52712
52807
  }
52713
52808
  function isPidAlive(pid) {
52714
52809
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -52730,7 +52825,7 @@ var StateFileManager = class {
52730
52825
  }
52731
52826
  read() {
52732
52827
  try {
52733
- const raw = import_node_fs31.default.readFileSync(this.file, "utf8");
52828
+ const raw = import_node_fs32.default.readFileSync(this.file, "utf8");
52734
52829
  const parsed = JSON.parse(raw);
52735
52830
  return parsed;
52736
52831
  } catch {
@@ -52744,20 +52839,20 @@ var StateFileManager = class {
52744
52839
  return { status: "stale", existing };
52745
52840
  }
52746
52841
  write(state) {
52747
- import_node_fs31.default.mkdirSync(import_node_path31.default.dirname(this.file), { recursive: true });
52842
+ import_node_fs32.default.mkdirSync(import_node_path32.default.dirname(this.file), { recursive: true });
52748
52843
  const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
52749
- import_node_fs31.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
52750
- import_node_fs31.default.renameSync(tmp, this.file);
52844
+ import_node_fs32.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
52845
+ import_node_fs32.default.renameSync(tmp, this.file);
52751
52846
  if (process.platform !== "win32") {
52752
52847
  try {
52753
- import_node_fs31.default.chmodSync(this.file, 384);
52848
+ import_node_fs32.default.chmodSync(this.file, 384);
52754
52849
  } catch {
52755
52850
  }
52756
52851
  }
52757
52852
  }
52758
52853
  delete() {
52759
52854
  try {
52760
- import_node_fs31.default.unlinkSync(this.file);
52855
+ import_node_fs32.default.unlinkSync(this.file);
52761
52856
  } catch {
52762
52857
  }
52763
52858
  }
@@ -52771,14 +52866,14 @@ function readSpawnedByDesktopFromEnv(env = process.env) {
52771
52866
  }
52772
52867
 
52773
52868
  // src/tunnel/tunnel-manager.ts
52774
- var import_node_fs35 = __toESM(require("fs"), 1);
52775
- var import_node_path35 = __toESM(require("path"), 1);
52869
+ var import_node_fs36 = __toESM(require("fs"), 1);
52870
+ var import_node_path36 = __toESM(require("path"), 1);
52776
52871
  var import_node_crypto10 = __toESM(require("crypto"), 1);
52777
52872
  var import_node_child_process9 = require("child_process");
52778
52873
 
52779
52874
  // src/tunnel/tunnel-store.ts
52780
- var import_node_fs32 = __toESM(require("fs"), 1);
52781
- var import_node_path32 = __toESM(require("path"), 1);
52875
+ var import_node_fs33 = __toESM(require("fs"), 1);
52876
+ var import_node_path33 = __toESM(require("path"), 1);
52782
52877
  var TunnelStore = class {
52783
52878
  constructor(filePath) {
52784
52879
  this.filePath = filePath;
@@ -52786,7 +52881,7 @@ var TunnelStore = class {
52786
52881
  filePath;
52787
52882
  async get() {
52788
52883
  try {
52789
- const raw = await import_node_fs32.default.promises.readFile(this.filePath, "utf8");
52884
+ const raw = await import_node_fs33.default.promises.readFile(this.filePath, "utf8");
52790
52885
  const obj = JSON.parse(raw);
52791
52886
  if (!isPersistedTunnel(obj)) return null;
52792
52887
  return obj;
@@ -52797,22 +52892,22 @@ var TunnelStore = class {
52797
52892
  }
52798
52893
  }
52799
52894
  async set(v2) {
52800
- const dir = import_node_path32.default.dirname(this.filePath);
52801
- await import_node_fs32.default.promises.mkdir(dir, { recursive: true });
52895
+ const dir = import_node_path33.default.dirname(this.filePath);
52896
+ await import_node_fs33.default.promises.mkdir(dir, { recursive: true });
52802
52897
  const data = JSON.stringify(v2, null, 2);
52803
52898
  const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
52804
- await import_node_fs32.default.promises.writeFile(tmp, data, { mode: 384 });
52899
+ await import_node_fs33.default.promises.writeFile(tmp, data, { mode: 384 });
52805
52900
  if (process.platform !== "win32") {
52806
52901
  try {
52807
- await import_node_fs32.default.promises.chmod(tmp, 384);
52902
+ await import_node_fs33.default.promises.chmod(tmp, 384);
52808
52903
  } catch {
52809
52904
  }
52810
52905
  }
52811
- await import_node_fs32.default.promises.rename(tmp, this.filePath);
52906
+ await import_node_fs33.default.promises.rename(tmp, this.filePath);
52812
52907
  }
52813
52908
  async clear() {
52814
52909
  try {
52815
- await import_node_fs32.default.promises.unlink(this.filePath);
52910
+ await import_node_fs33.default.promises.unlink(this.filePath);
52816
52911
  } catch (err) {
52817
52912
  const code = err?.code;
52818
52913
  if (code !== "ENOENT") throw err;
@@ -52907,9 +53002,9 @@ function escape(v2) {
52907
53002
  }
52908
53003
 
52909
53004
  // src/tunnel/frpc-binary.ts
52910
- var import_node_fs33 = __toESM(require("fs"), 1);
53005
+ var import_node_fs34 = __toESM(require("fs"), 1);
52911
53006
  var import_node_os13 = __toESM(require("os"), 1);
52912
- var import_node_path33 = __toESM(require("path"), 1);
53007
+ var import_node_path34 = __toESM(require("path"), 1);
52913
53008
  var import_node_child_process7 = require("child_process");
52914
53009
  var import_node_stream3 = require("stream");
52915
53010
  var import_promises4 = require("stream/promises");
@@ -52941,20 +53036,20 @@ function frpcDownloadUrl(version2, p2) {
52941
53036
  }
52942
53037
  async function ensureFrpcBinary(opts) {
52943
53038
  if (opts.override) {
52944
- if (!import_node_fs33.default.existsSync(opts.override)) {
53039
+ if (!import_node_fs34.default.existsSync(opts.override)) {
52945
53040
  throw new Error(`frpc binary not found at override path: ${opts.override}`);
52946
53041
  }
52947
53042
  return opts.override;
52948
53043
  }
52949
53044
  const version2 = opts.version ?? FRPC_VERSION;
52950
53045
  const platform = opts.platform ?? detectPlatform();
52951
- const binDir = import_node_path33.default.join(opts.dataDir, "bin");
52952
- import_node_fs33.default.mkdirSync(binDir, { recursive: true });
53046
+ const binDir = import_node_path34.default.join(opts.dataDir, "bin");
53047
+ import_node_fs34.default.mkdirSync(binDir, { recursive: true });
52953
53048
  cleanupStaleArtifacts(binDir);
52954
- const stableBin = import_node_path33.default.join(binDir, "frpc");
52955
- if (import_node_fs33.default.existsSync(stableBin)) return stableBin;
53049
+ const stableBin = import_node_path34.default.join(binDir, "frpc");
53050
+ if (import_node_fs34.default.existsSync(stableBin)) return stableBin;
52956
53051
  const partialBin = `${stableBin}.partial`;
52957
- const tarballPath = import_node_path33.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
53052
+ const tarballPath = import_node_path34.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
52958
53053
  try {
52959
53054
  const url = frpcDownloadUrl(version2, platform);
52960
53055
  await downloadToFile(url, tarballPath, opts.fetchImpl);
@@ -52963,8 +53058,8 @@ async function ensureFrpcBinary(opts) {
52963
53058
  } else {
52964
53059
  await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
52965
53060
  }
52966
- import_node_fs33.default.chmodSync(partialBin, 493);
52967
- import_node_fs33.default.renameSync(partialBin, stableBin);
53061
+ import_node_fs34.default.chmodSync(partialBin, 493);
53062
+ import_node_fs34.default.renameSync(partialBin, stableBin);
52968
53063
  } finally {
52969
53064
  safeUnlink(tarballPath);
52970
53065
  safeUnlink(partialBin);
@@ -52974,15 +53069,15 @@ async function ensureFrpcBinary(opts) {
52974
53069
  function cleanupStaleArtifacts(binDir) {
52975
53070
  let entries;
52976
53071
  try {
52977
- entries = import_node_fs33.default.readdirSync(binDir);
53072
+ entries = import_node_fs34.default.readdirSync(binDir);
52978
53073
  } catch {
52979
53074
  return;
52980
53075
  }
52981
53076
  for (const name of entries) {
52982
53077
  if (name.endsWith(".partial") || name.startsWith("extract-")) {
52983
- const full = import_node_path33.default.join(binDir, name);
53078
+ const full = import_node_path34.default.join(binDir, name);
52984
53079
  try {
52985
- import_node_fs33.default.rmSync(full, { recursive: true, force: true });
53080
+ import_node_fs34.default.rmSync(full, { recursive: true, force: true });
52986
53081
  } catch {
52987
53082
  }
52988
53083
  }
@@ -52990,7 +53085,7 @@ function cleanupStaleArtifacts(binDir) {
52990
53085
  }
52991
53086
  function safeUnlink(p2) {
52992
53087
  try {
52993
- import_node_fs33.default.unlinkSync(p2);
53088
+ import_node_fs34.default.unlinkSync(p2);
52994
53089
  } catch {
52995
53090
  }
52996
53091
  }
@@ -53001,13 +53096,13 @@ async function downloadToFile(url, dest, fetchImpl) {
53001
53096
  if (!res.ok || !res.body) {
53002
53097
  throw new Error(`download failed: ${res.status} ${res.statusText}`);
53003
53098
  }
53004
- const out = import_node_fs33.default.createWriteStream(dest);
53099
+ const out = import_node_fs34.default.createWriteStream(dest);
53005
53100
  const nodeStream = import_node_stream3.Readable.fromWeb(res.body);
53006
53101
  await (0, import_promises4.pipeline)(nodeStream, out);
53007
53102
  }
53008
53103
  async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
53009
- const work = import_node_path33.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
53010
- import_node_fs33.default.mkdirSync(work, { recursive: true });
53104
+ const work = import_node_path34.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
53105
+ import_node_fs34.default.mkdirSync(work, { recursive: true });
53011
53106
  try {
53012
53107
  await new Promise((resolve6, reject) => {
53013
53108
  const proc = (0, import_node_child_process7.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
@@ -53015,32 +53110,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
53015
53110
  proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
53016
53111
  });
53017
53112
  const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
53018
- const src = import_node_path33.default.join(work, dirName, "frpc");
53019
- if (!import_node_fs33.default.existsSync(src)) {
53113
+ const src = import_node_path34.default.join(work, dirName, "frpc");
53114
+ if (!import_node_fs34.default.existsSync(src)) {
53020
53115
  throw new Error(`frpc not found inside tarball at ${src}`);
53021
53116
  }
53022
- import_node_fs33.default.copyFileSync(src, destBin);
53117
+ import_node_fs34.default.copyFileSync(src, destBin);
53023
53118
  } finally {
53024
- import_node_fs33.default.rmSync(work, { recursive: true, force: true });
53119
+ import_node_fs34.default.rmSync(work, { recursive: true, force: true });
53025
53120
  }
53026
53121
  }
53027
53122
 
53028
53123
  // src/tunnel/frpc-process.ts
53029
- var import_node_fs34 = __toESM(require("fs"), 1);
53030
- var import_node_path34 = __toESM(require("path"), 1);
53124
+ var import_node_fs35 = __toESM(require("fs"), 1);
53125
+ var import_node_path35 = __toESM(require("path"), 1);
53031
53126
  var import_node_child_process8 = require("child_process");
53032
53127
  function frpcPidFilePath(dataDir) {
53033
- return import_node_path34.default.join(dataDir, "frpc.pid");
53128
+ return import_node_path35.default.join(dataDir, "frpc.pid");
53034
53129
  }
53035
53130
  function writeFrpcPid(dataDir, pid) {
53036
53131
  try {
53037
- import_node_fs34.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
53132
+ import_node_fs35.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
53038
53133
  } catch {
53039
53134
  }
53040
53135
  }
53041
53136
  function clearFrpcPid(dataDir) {
53042
53137
  try {
53043
- import_node_fs34.default.unlinkSync(frpcPidFilePath(dataDir));
53138
+ import_node_fs35.default.unlinkSync(frpcPidFilePath(dataDir));
53044
53139
  } catch {
53045
53140
  }
53046
53141
  }
@@ -53056,7 +53151,7 @@ function defaultIsPidAlive(pid) {
53056
53151
  }
53057
53152
  function defaultReadPidFile(file) {
53058
53153
  try {
53059
- return import_node_fs34.default.readFileSync(file, "utf8");
53154
+ return import_node_fs35.default.readFileSync(file, "utf8");
53060
53155
  } catch {
53061
53156
  return null;
53062
53157
  }
@@ -53072,7 +53167,7 @@ function defaultSleep(ms) {
53072
53167
  }
53073
53168
  async function killStaleFrpc(deps) {
53074
53169
  const pidFile = frpcPidFilePath(deps.dataDir);
53075
- const tomlPath = import_node_path34.default.join(deps.dataDir, "frpc.toml");
53170
+ const tomlPath = import_node_path35.default.join(deps.dataDir, "frpc.toml");
53076
53171
  const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
53077
53172
  const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
53078
53173
  const killPid = deps.killPidImpl ?? defaultKillPid;
@@ -53096,7 +53191,7 @@ async function killStaleFrpc(deps) {
53096
53191
  }
53097
53192
  if (victims.size === 0) {
53098
53193
  try {
53099
- import_node_fs34.default.unlinkSync(pidFile);
53194
+ import_node_fs35.default.unlinkSync(pidFile);
53100
53195
  } catch {
53101
53196
  }
53102
53197
  return;
@@ -53107,7 +53202,7 @@ async function killStaleFrpc(deps) {
53107
53202
  }
53108
53203
  await sleep2(deps.reapWaitMs ?? 300);
53109
53204
  try {
53110
- import_node_fs34.default.unlinkSync(pidFile);
53205
+ import_node_fs35.default.unlinkSync(pidFile);
53111
53206
  } catch {
53112
53207
  }
53113
53208
  }
@@ -53144,7 +53239,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
53144
53239
  var TunnelManager = class {
53145
53240
  constructor(deps) {
53146
53241
  this.deps = deps;
53147
- this.store = deps.store ?? new TunnelStore(import_node_path35.default.join(deps.dataDir, "tunnel.json"));
53242
+ this.store = deps.store ?? new TunnelStore(import_node_path36.default.join(deps.dataDir, "tunnel.json"));
53148
53243
  this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
53149
53244
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
53150
53245
  }
@@ -53271,7 +53366,7 @@ var TunnelManager = class {
53271
53366
  dataDir: this.deps.dataDir,
53272
53367
  override: this.deps.frpcBinaryOverride ?? void 0
53273
53368
  });
53274
- const tomlPath = import_node_path35.default.join(this.deps.dataDir, "frpc.toml");
53369
+ const tomlPath = import_node_path36.default.join(this.deps.dataDir, "frpc.toml");
53275
53370
  const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto10.default.randomBytes(3).toString("hex")}`;
53276
53371
  const toml = buildFrpcToml({
53277
53372
  serverAddr: t.frpsHost,
@@ -53282,14 +53377,14 @@ var TunnelManager = class {
53282
53377
  localPort,
53283
53378
  logLevel: "info"
53284
53379
  });
53285
- await import_node_fs35.default.promises.writeFile(tomlPath, toml, { mode: 384 });
53380
+ await import_node_fs36.default.promises.writeFile(tomlPath, toml, { mode: 384 });
53286
53381
  const proc = (this.deps.spawnImpl ?? import_node_child_process9.spawn)(frpcBin, ["-c", tomlPath], {
53287
53382
  stdio: ["ignore", "pipe", "pipe"]
53288
53383
  });
53289
- const logDir = import_node_path35.default.join(this.deps.dataDir, "log");
53290
- import_node_fs35.default.mkdirSync(logDir, { recursive: true });
53291
- const logFilePath = import_node_path35.default.join(logDir, "frpc.log");
53292
- const logStream = import_node_fs35.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
53384
+ const logDir = import_node_path36.default.join(this.deps.dataDir, "log");
53385
+ import_node_fs36.default.mkdirSync(logDir, { recursive: true });
53386
+ const logFilePath = import_node_path36.default.join(logDir, "frpc.log");
53387
+ const logStream = import_node_fs36.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
53293
53388
  logStream.on("error", () => {
53294
53389
  });
53295
53390
  const tee = (chunk) => {
@@ -53371,8 +53466,8 @@ async function waitForFrpcReady(proc, timeoutMs) {
53371
53466
  }
53372
53467
 
53373
53468
  // src/sshd/sshd-manager.ts
53374
- var import_node_fs39 = __toESM(require("fs"), 1);
53375
- var import_node_path39 = __toESM(require("path"), 1);
53469
+ var import_node_fs40 = __toESM(require("fs"), 1);
53470
+ var import_node_path40 = __toESM(require("path"), 1);
53376
53471
  var import_node_child_process11 = require("child_process");
53377
53472
 
53378
53473
  // src/sshd/sshd-config.ts
@@ -53397,23 +53492,23 @@ function buildSshdConfig(input) {
53397
53492
  }
53398
53493
 
53399
53494
  // src/sshd/sshd-process.ts
53400
- var import_node_fs36 = __toESM(require("fs"), 1);
53401
- var import_node_path36 = __toESM(require("path"), 1);
53495
+ var import_node_fs37 = __toESM(require("fs"), 1);
53496
+ var import_node_path37 = __toESM(require("path"), 1);
53402
53497
  var import_node_child_process10 = require("child_process");
53403
53498
  function sshdPidFilePath(dataDir) {
53404
- return import_node_path36.default.join(dataDir, "sshd", "sshd.pid");
53499
+ return import_node_path37.default.join(dataDir, "sshd", "sshd.pid");
53405
53500
  }
53406
53501
  function writeSshdPid(dataDir, pid) {
53407
53502
  try {
53408
53503
  const p2 = sshdPidFilePath(dataDir);
53409
- import_node_fs36.default.mkdirSync(import_node_path36.default.dirname(p2), { recursive: true, mode: 448 });
53410
- import_node_fs36.default.writeFileSync(p2, String(pid), { mode: 384 });
53504
+ import_node_fs37.default.mkdirSync(import_node_path37.default.dirname(p2), { recursive: true, mode: 448 });
53505
+ import_node_fs37.default.writeFileSync(p2, String(pid), { mode: 384 });
53411
53506
  } catch {
53412
53507
  }
53413
53508
  }
53414
53509
  function clearSshdPid(dataDir) {
53415
53510
  try {
53416
- import_node_fs36.default.unlinkSync(sshdPidFilePath(dataDir));
53511
+ import_node_fs37.default.unlinkSync(sshdPidFilePath(dataDir));
53417
53512
  } catch {
53418
53513
  }
53419
53514
  }
@@ -53429,7 +53524,7 @@ function defaultIsPidAlive2(pid) {
53429
53524
  }
53430
53525
  function defaultReadPidFile2(file) {
53431
53526
  try {
53432
- return import_node_fs36.default.readFileSync(file, "utf8");
53527
+ return import_node_fs37.default.readFileSync(file, "utf8");
53433
53528
  } catch {
53434
53529
  return null;
53435
53530
  }
@@ -53445,7 +53540,7 @@ function defaultSleep2(ms) {
53445
53540
  }
53446
53541
  async function killStaleSshd(deps) {
53447
53542
  const pidFile = sshdPidFilePath(deps.dataDir);
53448
- const configPath = import_node_path36.default.join(deps.dataDir, "sshd", "sshd_config");
53543
+ const configPath = import_node_path37.default.join(deps.dataDir, "sshd", "sshd_config");
53449
53544
  const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile2;
53450
53545
  const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive2;
53451
53546
  const killPid = deps.killPidImpl ?? defaultKillPid2;
@@ -53469,7 +53564,7 @@ async function killStaleSshd(deps) {
53469
53564
  }
53470
53565
  if (victims.size === 0) {
53471
53566
  try {
53472
- import_node_fs36.default.unlinkSync(pidFile);
53567
+ import_node_fs37.default.unlinkSync(pidFile);
53473
53568
  } catch {
53474
53569
  }
53475
53570
  return;
@@ -53480,7 +53575,7 @@ async function killStaleSshd(deps) {
53480
53575
  }
53481
53576
  await sleep2(deps.reapWaitMs ?? 300);
53482
53577
  try {
53483
- import_node_fs36.default.unlinkSync(pidFile);
53578
+ import_node_fs37.default.unlinkSync(pidFile);
53484
53579
  } catch {
53485
53580
  }
53486
53581
  }
@@ -53513,8 +53608,8 @@ async function defaultScanSshdPidsByCmdline(configPath, logger) {
53513
53608
  }
53514
53609
 
53515
53610
  // src/sshd/jail-script.ts
53516
- var import_node_fs37 = __toESM(require("fs"), 1);
53517
- var import_node_path37 = __toESM(require("path"), 1);
53611
+ var import_node_fs38 = __toESM(require("fs"), 1);
53612
+ var import_node_path38 = __toESM(require("path"), 1);
53518
53613
  var CLAWD_SSH_JAIL_SCRIPT = String.raw`#!/usr/bin/env bash
53519
53614
  # clawd-ssh-jail — SSH reverse access sandbox wrapper (managed by clawd; do not edit)
53520
53615
  #
@@ -53523,7 +53618,7 @@ var CLAWD_SSH_JAIL_SCRIPT = String.raw`#!/usr/bin/env bash
53523
53618
  # 真实请求(interactive shell 时为空)。
53524
53619
  #
53525
53620
  # 职责:
53526
- # 1. 读 ~/.clawd/contacts.json 校验 contact.sshAllowed(不通过即拒)
53621
+ # 1. 读 ~/.clawd/contacts.json 校验 contact.remoteAccessAllowed(不通过即拒)
53527
53622
  # 2. 直接 exec shell(去沙箱后无目录级限制)
53528
53623
 
53529
53624
  set -euo pipefail
@@ -53551,13 +53646,13 @@ log_line INFO jail.entered "clawd-ssh-jail 起来 device=${"${DEVICE_ID}"} cmd=$
53551
53646
 
53552
53647
  CONTACTS="${"${HOME}"}/.clawd/contacts.json"
53553
53648
  if [ ! -f "$CONTACTS" ]; then
53554
- log_line ERROR jail.entered "contacts.json 不存在,无法校验 sshAllowed"
53649
+ log_line ERROR jail.entered "contacts.json 不存在,无法校验 remoteAccessAllowed"
53555
53650
  echo "clawd-ssh-jail: contacts.json missing" >&2
53556
53651
  exit 1
53557
53652
  fi
53558
53653
 
53559
- # 校验该 contact 的 sshAllowed(mac/linux 都自带 python3)。去沙箱后不再读目录白名单:
53560
- # sshAllowed=false → DENIED exit 2;deviceId 未命中 → NOT_FOUND exit 3;命中且允许 → exit 0。
53654
+ # 校验该 contact 的 remoteAccessAllowed(mac/linux 都自带 python3)。去沙箱后不再读目录白名单:
53655
+ # remoteAccessAllowed=false → DENIED exit 2;deviceId 未命中 → NOT_FOUND exit 3;命中且允许 → exit 0。
53561
53656
  # set -e 下 python 非 0 退出即终止脚本(不进 exec),只有 exit 0 才继续 exec shell。
53562
53657
  python3 -c "
53563
53658
  import json, sys
@@ -53565,13 +53660,13 @@ with open('$CONTACTS') as f:
53565
53660
  data = json.load(f)
53566
53661
  for c in data.get('contacts', []):
53567
53662
  if c.get('deviceId') == '$DEVICE_ID':
53568
- if not c.get('sshAllowed'):
53663
+ if not c.get('remoteAccessAllowed'):
53569
53664
  print('DENIED', file=sys.stderr); sys.exit(2)
53570
53665
  sys.exit(0)
53571
53666
  print('NOT_FOUND', file=sys.stderr); sys.exit(3)
53572
53667
  "
53573
53668
 
53574
- log_line INFO jail.entered "sshAllowed 校验通过,放行 exec shell"
53669
+ log_line INFO jail.entered "remoteAccessAllowed 校验通过,放行 exec shell"
53575
53670
 
53576
53671
  CMD="${"${SSH_ORIGINAL_COMMAND:-}"}"
53577
53672
  if [ -z "$CMD" ]; then
@@ -53582,21 +53677,21 @@ fi
53582
53677
 
53583
53678
  # 去掉 OS 沙箱层(原本 macOS Seatbelt 策略 / Linux 命名空间隔离):SSH 进来即该用户
53584
53679
  # 权限内的 shell,直接 exec。沙箱只限制文件、拦不住网络/进程,隔离价值有限,且相关 API
53585
- # 在 macOS 已 deprecated。授权即完全信任——sshAllowed 授权开关 + authorized_keys 即时
53680
+ # 在 macOS 已 deprecated。授权即完全信任——remoteAccessAllowed 授权开关 + authorized_keys 即时
53586
53681
  # 踢行是唯一访问闸。要加回沙箱:从 git 历史(T-29 之前)恢复本段的 OS 分支即可。
53587
53682
  exec "${"${SHELL_CMD[@]}"}"
53588
53683
  `;
53589
53684
  function ensureJailScript(dataDir) {
53590
- const binDir = import_node_path37.default.join(dataDir, "bin");
53591
- import_node_fs37.default.mkdirSync(binDir, { recursive: true, mode: 493 });
53592
- const target = import_node_path37.default.join(binDir, "clawd-ssh-jail");
53593
- import_node_fs37.default.writeFileSync(target, CLAWD_SSH_JAIL_SCRIPT, { mode: 493 });
53685
+ const binDir = import_node_path38.default.join(dataDir, "bin");
53686
+ import_node_fs38.default.mkdirSync(binDir, { recursive: true, mode: 493 });
53687
+ const target = import_node_path38.default.join(binDir, "clawd-ssh-jail");
53688
+ import_node_fs38.default.writeFileSync(target, CLAWD_SSH_JAIL_SCRIPT, { mode: 493 });
53594
53689
  return target;
53595
53690
  }
53596
53691
 
53597
53692
  // src/sshd/clawd-shim.ts
53598
- var import_node_fs38 = __toESM(require("fs"), 1);
53599
- var import_node_path38 = __toESM(require("path"), 1);
53693
+ var import_node_fs39 = __toESM(require("fs"), 1);
53694
+ var import_node_path39 = __toESM(require("path"), 1);
53600
53695
  var DEFAULT_FALLBACK_APPS = ["/Applications/Clawd.app", "$HOME/Applications/Clawd.app"];
53601
53696
  function shellQuote(s) {
53602
53697
  return `'${s.replace(/'/g, "'\\''")}'`;
@@ -53640,10 +53735,10 @@ exec "$EXEC" "$CLI" "$@"
53640
53735
  `;
53641
53736
  }
53642
53737
  function ensureClawdShim(dataDir, execPath, cliPath) {
53643
- const binDir = import_node_path38.default.join(dataDir, "bin");
53644
- import_node_fs38.default.mkdirSync(binDir, { recursive: true, mode: 493 });
53645
- const target = import_node_path38.default.join(binDir, "clawd");
53646
- import_node_fs38.default.writeFileSync(target, buildClawdShim(execPath, cliPath), { mode: 493 });
53738
+ const binDir = import_node_path39.default.join(dataDir, "bin");
53739
+ import_node_fs39.default.mkdirSync(binDir, { recursive: true, mode: 493 });
53740
+ const target = import_node_path39.default.join(binDir, "clawd");
53741
+ import_node_fs39.default.writeFileSync(target, buildClawdShim(execPath, cliPath), { mode: 493 });
53647
53742
  return target;
53648
53743
  }
53649
53744
 
@@ -53651,7 +53746,7 @@ function ensureClawdShim(dataDir, execPath, cliPath) {
53651
53746
  var SshdManager = class {
53652
53747
  constructor(deps) {
53653
53748
  this.deps = deps;
53654
- this.sshdDir = import_node_path39.default.join(deps.dataDir, "sshd");
53749
+ this.sshdDir = import_node_path40.default.join(deps.dataDir, "sshd");
53655
53750
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
53656
53751
  }
53657
53752
  deps;
@@ -53672,34 +53767,34 @@ var SshdManager = class {
53672
53767
  ownPid: process.pid,
53673
53768
  logger
53674
53769
  });
53675
- import_node_fs39.default.mkdirSync(this.sshdDir, { recursive: true, mode: 448 });
53676
- import_node_fs39.default.mkdirSync(import_node_path39.default.join(this.sshdDir, "authorized_keys.d"), { recursive: true, mode: 448 });
53770
+ import_node_fs40.default.mkdirSync(this.sshdDir, { recursive: true, mode: 448 });
53771
+ import_node_fs40.default.mkdirSync(import_node_path40.default.join(this.sshdDir, "authorized_keys.d"), { recursive: true, mode: 448 });
53677
53772
  ensureJailScript(this.deps.dataDir);
53678
53773
  ensureClawdShim(this.deps.dataDir, process.execPath, process.argv[1] ?? "");
53679
- const hostKeyPath = import_node_path39.default.join(this.sshdDir, "host_key");
53680
- if (!import_node_fs39.default.existsSync(hostKeyPath)) {
53774
+ const hostKeyPath = import_node_path40.default.join(this.sshdDir, "host_key");
53775
+ if (!import_node_fs40.default.existsSync(hostKeyPath)) {
53681
53776
  await this.generateHostKey(hostKeyPath);
53682
53777
  }
53683
- const akFile = import_node_path39.default.join(this.sshdDir, "authorized_keys.d", "clawd-contacts");
53684
- if (!import_node_fs39.default.existsSync(akFile)) {
53685
- import_node_fs39.default.writeFileSync(akFile, "", { mode: 384 });
53778
+ const akFile = import_node_path40.default.join(this.sshdDir, "authorized_keys.d", "clawd-contacts");
53779
+ if (!import_node_fs40.default.existsSync(akFile)) {
53780
+ import_node_fs40.default.writeFileSync(akFile, "", { mode: 384 });
53686
53781
  }
53687
- const configPath = import_node_path39.default.join(this.sshdDir, "sshd_config");
53782
+ const configPath = import_node_path40.default.join(this.sshdDir, "sshd_config");
53688
53783
  const config = buildSshdConfig({
53689
53784
  listenAddress: "127.0.0.1",
53690
53785
  port: this.deps.port,
53691
53786
  hostKeyPath,
53692
53787
  authorizedKeysFile: akFile,
53693
- pidFilePath: import_node_path39.default.join(this.sshdDir, "sshd.pid")
53788
+ pidFilePath: import_node_path40.default.join(this.sshdDir, "sshd.pid")
53694
53789
  });
53695
- import_node_fs39.default.writeFileSync(configPath, config, { mode: 384 });
53790
+ import_node_fs40.default.writeFileSync(configPath, config, { mode: 384 });
53696
53791
  const sshdBin = this.deps.sshdBin ?? "/usr/sbin/sshd";
53697
53792
  const proc = (this.deps.spawnImpl ?? import_node_child_process11.spawn)(sshdBin, ["-D", "-e", "-f", configPath], {
53698
53793
  stdio: ["ignore", "pipe", "pipe"]
53699
53794
  });
53700
- const sshdLogDir = import_node_path39.default.join(this.deps.dataDir, "log");
53701
- import_node_fs39.default.mkdirSync(sshdLogDir, { recursive: true });
53702
- const logStream = import_node_fs39.default.createWriteStream(import_node_path39.default.join(sshdLogDir, "sshd.log"), {
53795
+ const sshdLogDir = import_node_path40.default.join(this.deps.dataDir, "log");
53796
+ import_node_fs40.default.mkdirSync(sshdLogDir, { recursive: true });
53797
+ const logStream = import_node_fs40.default.createWriteStream(import_node_path40.default.join(sshdLogDir, "sshd.log"), {
53703
53798
  flags: "a",
53704
53799
  mode: 384
53705
53800
  });
@@ -53796,7 +53891,7 @@ ${tail}` : ready.error;
53796
53891
  p2.on("error", reject);
53797
53892
  });
53798
53893
  try {
53799
- import_node_fs39.default.chmodSync(hostKeyPath, 384);
53894
+ import_node_fs40.default.chmodSync(hostKeyPath, 384);
53800
53895
  } catch {
53801
53896
  }
53802
53897
  }
@@ -53868,20 +53963,20 @@ async function waitForSshdReady(proc, timeoutMs) {
53868
53963
  }
53869
53964
 
53870
53965
  // src/sshd/authorized-keys.ts
53871
- var import_node_fs40 = __toESM(require("fs"), 1);
53872
- var import_node_path40 = __toESM(require("path"), 1);
53966
+ var import_node_fs41 = __toESM(require("fs"), 1);
53967
+ var import_node_path41 = __toESM(require("path"), 1);
53873
53968
  var JAIL_BIN_PATH_ENV = "CLAWD_JAIL_BIN_PATH";
53874
53969
  var AUTHORIZED_KEYS_FILE = "clawd-contacts";
53875
53970
  function jailBinPath() {
53876
- return process.env[JAIL_BIN_PATH_ENV] ?? import_node_path40.default.join(process.env.HOME ?? "", ".clawd", "bin", "clawd-ssh-jail");
53971
+ return process.env[JAIL_BIN_PATH_ENV] ?? import_node_path41.default.join(process.env.HOME ?? "", ".clawd", "bin", "clawd-ssh-jail");
53877
53972
  }
53878
53973
  function rebuildAuthorizedKeys(store, sshdDir) {
53879
- const akDir = import_node_path40.default.join(sshdDir, "authorized_keys.d");
53880
- const target = import_node_path40.default.join(akDir, AUTHORIZED_KEYS_FILE);
53881
- import_node_fs40.default.mkdirSync(akDir, { recursive: true, mode: 448 });
53974
+ const akDir = import_node_path41.default.join(sshdDir, "authorized_keys.d");
53975
+ const target = import_node_path41.default.join(akDir, AUTHORIZED_KEYS_FILE);
53976
+ import_node_fs41.default.mkdirSync(akDir, { recursive: true, mode: 448 });
53882
53977
  const lines = ["# managed by clawd; do not edit", ""];
53883
53978
  for (const c of store.list()) {
53884
- if (!c.sshAllowed) continue;
53979
+ if (!c.remoteAccessAllowed) continue;
53885
53980
  const safe = /^[A-Za-z0-9_.-]+$/.test(c.deviceId);
53886
53981
  if (!safe) continue;
53887
53982
  const pubkey = c.sshPublicKey?.trim();
@@ -53892,25 +53987,25 @@ function rebuildAuthorizedKeys(store, sshdDir) {
53892
53987
  }
53893
53988
  const body = lines.join("\n") + "\n";
53894
53989
  const tmp = `${target}.tmp-${process.pid}-${Date.now()}`;
53895
- import_node_fs40.default.writeFileSync(tmp, body, { mode: 384 });
53896
- import_node_fs40.default.renameSync(tmp, target);
53990
+ import_node_fs41.default.writeFileSync(tmp, body, { mode: 384 });
53991
+ import_node_fs41.default.renameSync(tmp, target);
53897
53992
  }
53898
53993
 
53899
53994
  // src/sshd/ssh-identity.ts
53900
- var import_node_fs41 = __toESM(require("fs"), 1);
53901
- var import_node_path41 = __toESM(require("path"), 1);
53995
+ var import_node_fs42 = __toESM(require("fs"), 1);
53996
+ var import_node_path42 = __toESM(require("path"), 1);
53902
53997
  var import_node_child_process12 = require("child_process");
53903
53998
  var IDENTITY_DIR = "ssh-identity";
53904
53999
  var KEY_FILE = "id_ed25519";
53905
54000
  function identityPaths(dataDir) {
53906
- const privPath = import_node_path41.default.join(dataDir, IDENTITY_DIR, KEY_FILE);
54001
+ const privPath = import_node_path42.default.join(dataDir, IDENTITY_DIR, KEY_FILE);
53907
54002
  return { privPath, pubPath: `${privPath}.pub` };
53908
54003
  }
53909
54004
  async function ensureSshIdentity(dataDir, opts = {}) {
53910
54005
  const { privPath, pubPath } = identityPaths(dataDir);
53911
- import_node_fs41.default.mkdirSync(import_node_path41.default.dirname(privPath), { recursive: true, mode: 448 });
53912
- if (import_node_fs41.default.existsSync(privPath) && import_node_fs41.default.existsSync(pubPath)) {
53913
- return { privateKeyPath: privPath, publicKeyLine: import_node_fs41.default.readFileSync(pubPath, "utf8").trim() };
54006
+ import_node_fs42.default.mkdirSync(import_node_path42.default.dirname(privPath), { recursive: true, mode: 448 });
54007
+ if (import_node_fs42.default.existsSync(privPath) && import_node_fs42.default.existsSync(pubPath)) {
54008
+ return { privateKeyPath: privPath, publicKeyLine: import_node_fs42.default.readFileSync(pubPath, "utf8").trim() };
53914
54009
  }
53915
54010
  const bin = opts.keygenBin ?? "/usr/bin/ssh-keygen";
53916
54011
  await new Promise((resolve6, reject) => {
@@ -53923,18 +54018,18 @@ async function ensureSshIdentity(dataDir, opts = {}) {
53923
54018
  p2.on("error", reject);
53924
54019
  });
53925
54020
  try {
53926
- import_node_fs41.default.chmodSync(privPath, 384);
54021
+ import_node_fs42.default.chmodSync(privPath, 384);
53927
54022
  } catch {
53928
54023
  }
53929
54024
  try {
53930
- import_node_fs41.default.chmodSync(pubPath, 420);
54025
+ import_node_fs42.default.chmodSync(pubPath, 420);
53931
54026
  } catch {
53932
54027
  }
53933
- return { privateKeyPath: privPath, publicKeyLine: import_node_fs41.default.readFileSync(pubPath, "utf8").trim() };
54028
+ return { privateKeyPath: privPath, publicKeyLine: import_node_fs42.default.readFileSync(pubPath, "utf8").trim() };
53934
54029
  }
53935
54030
  function readSshIdentityPublicKey(dataDir) {
53936
54031
  try {
53937
- return import_node_fs41.default.readFileSync(identityPaths(dataDir).pubPath, "utf8").trim();
54032
+ return import_node_fs42.default.readFileSync(identityPaths(dataDir).pubPath, "utf8").trim();
53938
54033
  } catch {
53939
54034
  return null;
53940
54035
  }
@@ -53944,10 +54039,10 @@ function readSshIdentityPublicKey(dataDir) {
53944
54039
  var import_node_net2 = __toESM(require("net"), 1);
53945
54040
 
53946
54041
  // src/sshd/contact-ssh-log.ts
53947
- var import_node_fs42 = __toESM(require("fs"), 1);
53948
- var import_node_path42 = __toESM(require("path"), 1);
54042
+ var import_node_fs43 = __toESM(require("fs"), 1);
54043
+ var import_node_path43 = __toESM(require("path"), 1);
53949
54044
  function createContactSshLog(dataDir) {
53950
- const file = import_node_path42.default.join(dataDir, "log", "contact-ssh.log");
54045
+ const file = import_node_path43.default.join(dataDir, "log", "contact-ssh.log");
53951
54046
  function append(level, tag, message, meta) {
53952
54047
  const time = (/* @__PURE__ */ new Date()).toISOString();
53953
54048
  let line = `[${time}] [${level}] [${tag}] ${message}`;
@@ -53960,8 +54055,8 @@ function createContactSshLog(dataDir) {
53960
54055
  }
53961
54056
  line += "\n";
53962
54057
  try {
53963
- import_node_fs42.default.mkdirSync(import_node_path42.default.dirname(file), { recursive: true });
53964
- import_node_fs42.default.appendFileSync(file, line, { mode: 384 });
54058
+ import_node_fs43.default.mkdirSync(import_node_path43.default.dirname(file), { recursive: true });
54059
+ import_node_fs43.default.appendFileSync(file, line, { mode: 384 });
53965
54060
  } catch {
53966
54061
  }
53967
54062
  }
@@ -54269,27 +54364,27 @@ function respond404(socket) {
54269
54364
 
54270
54365
  // src/tunnel/device-key.ts
54271
54366
  var import_node_os14 = __toESM(require("os"), 1);
54272
- var import_node_path43 = __toESM(require("path"), 1);
54367
+ var import_node_path44 = __toESM(require("path"), 1);
54273
54368
  var import_node_crypto11 = __toESM(require("crypto"), 1);
54274
54369
  var DERIVE_SALT = "clawd-tunnel-device-v1";
54275
54370
  function deriveStableDeviceKey(opts = {}) {
54276
54371
  const hostname = opts.hostname ?? import_node_os14.default.hostname();
54277
54372
  const uid = opts.uid ?? (typeof import_node_os14.default.userInfo === "function" ? import_node_os14.default.userInfo().uid : 0);
54278
54373
  const home = opts.home ?? import_node_os14.default.homedir();
54279
- const defaultDataDir = import_node_path43.default.resolve(import_node_path43.default.join(home, ".clawd"));
54280
- const normalizedDataDir = opts.dataDir ? import_node_path43.default.resolve(opts.dataDir) : null;
54374
+ const defaultDataDir = import_node_path44.default.resolve(import_node_path44.default.join(home, ".clawd"));
54375
+ const normalizedDataDir = opts.dataDir ? import_node_path44.default.resolve(opts.dataDir) : null;
54281
54376
  const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
54282
54377
  const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
54283
54378
  return import_node_crypto11.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
54284
54379
  }
54285
54380
 
54286
54381
  // src/auth-store.ts
54287
- var import_node_fs43 = __toESM(require("fs"), 1);
54288
- var import_node_path44 = __toESM(require("path"), 1);
54382
+ var import_node_fs44 = __toESM(require("fs"), 1);
54383
+ var import_node_path45 = __toESM(require("path"), 1);
54289
54384
  var import_node_crypto12 = __toESM(require("crypto"), 1);
54290
54385
  var AUTH_FILE_NAME = "auth.json";
54291
54386
  function authFilePath(dataDir) {
54292
- return import_node_path44.default.join(dataDir, AUTH_FILE_NAME);
54387
+ return import_node_path45.default.join(dataDir, AUTH_FILE_NAME);
54293
54388
  }
54294
54389
  function loadOrCreateAuthFile(opts) {
54295
54390
  const file = authFilePath(opts.dataDir);
@@ -54325,7 +54420,7 @@ function defaultGenerateOwnerPrincipalId() {
54325
54420
  }
54326
54421
  function readAuthFile(file) {
54327
54422
  try {
54328
- const raw = import_node_fs43.default.readFileSync(file, "utf8");
54423
+ const raw = import_node_fs44.default.readFileSync(file, "utf8");
54329
54424
  const parsed = JSON.parse(raw);
54330
54425
  if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
54331
54426
  return null;
@@ -54345,25 +54440,25 @@ function readAuthFile(file) {
54345
54440
  }
54346
54441
  }
54347
54442
  function writeAuthFile(file, content) {
54348
- import_node_fs43.default.mkdirSync(import_node_path44.default.dirname(file), { recursive: true });
54349
- import_node_fs43.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
54443
+ import_node_fs44.default.mkdirSync(import_node_path45.default.dirname(file), { recursive: true });
54444
+ import_node_fs44.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
54350
54445
  try {
54351
- import_node_fs43.default.chmodSync(file, 384);
54446
+ import_node_fs44.default.chmodSync(file, 384);
54352
54447
  } catch {
54353
54448
  }
54354
54449
  }
54355
54450
 
54356
54451
  // src/owner-profile.ts
54357
- var import_node_fs44 = __toESM(require("fs"), 1);
54452
+ var import_node_fs45 = __toESM(require("fs"), 1);
54358
54453
  var import_node_os15 = __toESM(require("os"), 1);
54359
- var import_node_path45 = __toESM(require("path"), 1);
54454
+ var import_node_path46 = __toESM(require("path"), 1);
54360
54455
  var PROFILE_FILENAME = "profile.json";
54361
54456
  function loadOwnerDisplayName(dataDir) {
54362
54457
  const fallback = import_node_os15.default.userInfo().username;
54363
- const profilePath = import_node_path45.default.join(dataDir, PROFILE_FILENAME);
54458
+ const profilePath = import_node_path46.default.join(dataDir, PROFILE_FILENAME);
54364
54459
  let raw;
54365
54460
  try {
54366
- raw = import_node_fs44.default.readFileSync(profilePath, "utf8");
54461
+ raw = import_node_fs45.default.readFileSync(profilePath, "utf8");
54367
54462
  } catch {
54368
54463
  return fallback;
54369
54464
  }
@@ -54386,18 +54481,18 @@ function loadOwnerDisplayName(dataDir) {
54386
54481
  }
54387
54482
 
54388
54483
  // src/feishu-auth/owner-identity-store.ts
54389
- var import_node_fs45 = __toESM(require("fs"), 1);
54390
- var import_node_path46 = __toESM(require("path"), 1);
54484
+ var import_node_fs46 = __toESM(require("fs"), 1);
54485
+ var import_node_path47 = __toESM(require("path"), 1);
54391
54486
  var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
54392
54487
  var OwnerIdentityStore = class {
54393
54488
  file;
54394
54489
  constructor(dataDir) {
54395
- this.file = import_node_path46.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
54490
+ this.file = import_node_path47.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
54396
54491
  }
54397
54492
  read() {
54398
54493
  let raw;
54399
54494
  try {
54400
- raw = import_node_fs45.default.readFileSync(this.file, "utf8");
54495
+ raw = import_node_fs46.default.readFileSync(this.file, "utf8");
54401
54496
  } catch {
54402
54497
  return null;
54403
54498
  }
@@ -54425,16 +54520,16 @@ var OwnerIdentityStore = class {
54425
54520
  };
54426
54521
  }
54427
54522
  write(record) {
54428
- import_node_fs45.default.mkdirSync(import_node_path46.default.dirname(this.file), { recursive: true });
54429
- import_node_fs45.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
54523
+ import_node_fs46.default.mkdirSync(import_node_path47.default.dirname(this.file), { recursive: true });
54524
+ import_node_fs46.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
54430
54525
  try {
54431
- import_node_fs45.default.chmodSync(this.file, 384);
54526
+ import_node_fs46.default.chmodSync(this.file, 384);
54432
54527
  } catch {
54433
54528
  }
54434
54529
  }
54435
54530
  clear() {
54436
54531
  try {
54437
- import_node_fs45.default.unlinkSync(this.file);
54532
+ import_node_fs46.default.unlinkSync(this.file);
54438
54533
  } catch (err) {
54439
54534
  const code = err?.code;
54440
54535
  if (code !== "ENOENT") throw err;
@@ -54571,9 +54666,9 @@ var CentralClientError = class extends Error {
54571
54666
  code;
54572
54667
  cause;
54573
54668
  };
54574
- async function centralRequest(opts, path76, init) {
54669
+ async function centralRequest(opts, path77, init) {
54575
54670
  const f = opts.fetchImpl ?? globalThis.fetch;
54576
- const url = `${opts.api.replace(/\/+$/, "")}${path76}`;
54671
+ const url = `${opts.api.replace(/\/+$/, "")}${path77}`;
54577
54672
  const ctrl = new AbortController();
54578
54673
  const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
54579
54674
  let res;
@@ -54767,8 +54862,8 @@ function verifyConnectToken(args) {
54767
54862
  }
54768
54863
 
54769
54864
  // src/feishu-auth/server-key.ts
54770
- var fs55 = __toESM(require("fs"), 1);
54771
- var path55 = __toESM(require("path"), 1);
54865
+ var fs56 = __toESM(require("fs"), 1);
54866
+ var path56 = __toESM(require("path"), 1);
54772
54867
  var FILE_NAME2 = "server-signing-key.json";
54773
54868
  var ServerKeyStore = class {
54774
54869
  constructor(dataDir) {
@@ -54776,12 +54871,12 @@ var ServerKeyStore = class {
54776
54871
  }
54777
54872
  dataDir;
54778
54873
  filePath() {
54779
- return path55.join(this.dataDir, FILE_NAME2);
54874
+ return path56.join(this.dataDir, FILE_NAME2);
54780
54875
  }
54781
54876
  /** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
54782
54877
  read() {
54783
54878
  try {
54784
- const raw = fs55.readFileSync(this.filePath(), "utf8");
54879
+ const raw = fs56.readFileSync(this.filePath(), "utf8");
54785
54880
  const parsed = JSON.parse(raw);
54786
54881
  if (typeof parsed.publicKeyPem === "string" && parsed.publicKeyPem.includes("PUBLIC KEY")) {
54787
54882
  return parsed.publicKeyPem;
@@ -54796,12 +54891,12 @@ var ServerKeyStore = class {
54796
54891
  publicKeyPem,
54797
54892
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
54798
54893
  };
54799
- fs55.mkdirSync(this.dataDir, { recursive: true });
54800
- fs55.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
54894
+ fs56.mkdirSync(this.dataDir, { recursive: true });
54895
+ fs56.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
54801
54896
  }
54802
54897
  clear() {
54803
54898
  try {
54804
- fs55.unlinkSync(this.filePath());
54899
+ fs56.unlinkSync(this.filePath());
54805
54900
  } catch {
54806
54901
  }
54807
54902
  }
@@ -54814,12 +54909,12 @@ init_protocol();
54814
54909
  init_protocol();
54815
54910
 
54816
54911
  // src/session/fork.ts
54817
- var import_node_fs46 = __toESM(require("fs"), 1);
54912
+ var import_node_fs47 = __toESM(require("fs"), 1);
54818
54913
  var import_node_os16 = __toESM(require("os"), 1);
54819
- var import_node_path47 = __toESM(require("path"), 1);
54914
+ var import_node_path48 = __toESM(require("path"), 1);
54820
54915
  init_claude_history();
54821
54916
  function readJsonlEntries(file) {
54822
- const raw = import_node_fs46.default.readFileSync(file, "utf8");
54917
+ const raw = import_node_fs47.default.readFileSync(file, "utf8");
54823
54918
  const out = [];
54824
54919
  for (const line of raw.split("\n")) {
54825
54920
  const t = line.trim();
@@ -54832,19 +54927,19 @@ function readJsonlEntries(file) {
54832
54927
  return out;
54833
54928
  }
54834
54929
  function forkSession(input) {
54835
- const baseDir = input.baseDir ?? import_node_path47.default.join(import_node_os16.default.homedir(), ".claude");
54836
- let projectDir = import_node_path47.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
54837
- let sourceFile = import_node_path47.default.join(projectDir, `${input.toolSessionId}.jsonl`);
54838
- if (!import_node_fs46.default.existsSync(sourceFile)) {
54930
+ const baseDir = input.baseDir ?? import_node_path48.default.join(import_node_os16.default.homedir(), ".claude");
54931
+ let projectDir = import_node_path48.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
54932
+ let sourceFile = import_node_path48.default.join(projectDir, `${input.toolSessionId}.jsonl`);
54933
+ if (!import_node_fs47.default.existsSync(sourceFile)) {
54839
54934
  const found = findTranscriptByToolSessionId(
54840
- import_node_path47.default.join(baseDir, "projects"),
54935
+ import_node_path48.default.join(baseDir, "projects"),
54841
54936
  input.toolSessionId
54842
54937
  );
54843
54938
  if (!found) {
54844
54939
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
54845
54940
  }
54846
54941
  sourceFile = found;
54847
- projectDir = import_node_path47.default.dirname(found);
54942
+ projectDir = import_node_path48.default.dirname(found);
54848
54943
  }
54849
54944
  const entries = readJsonlEntries(sourceFile);
54850
54945
  const mainEntries = entries.filter((e) => e.isSidechain !== true);
@@ -54873,9 +54968,9 @@ function forkSession(input) {
54873
54968
  }
54874
54969
  forkedLines.push(JSON.stringify(forked));
54875
54970
  }
54876
- const forkedFilePath = import_node_path47.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
54877
- import_node_fs46.default.mkdirSync(projectDir, { recursive: true });
54878
- import_node_fs46.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
54971
+ const forkedFilePath = import_node_path48.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
54972
+ import_node_fs47.default.mkdirSync(projectDir, { recursive: true });
54973
+ import_node_fs47.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
54879
54974
  return { forkedToolSessionId, forkedFilePath };
54880
54975
  }
54881
54976
 
@@ -55246,7 +55341,7 @@ function buildPermissionHandlers(deps) {
55246
55341
  }
55247
55342
 
55248
55343
  // src/handlers/history.ts
55249
- var path58 = __toESM(require("path"), 1);
55344
+ var path59 = __toESM(require("path"), 1);
55250
55345
  init_protocol();
55251
55346
 
55252
55347
  // src/session/recent-dirs.ts
@@ -55264,7 +55359,7 @@ function listRecentDirs(store, limit = 50) {
55264
55359
  }
55265
55360
 
55266
55361
  // src/permission/persona-paths.ts
55267
- var path57 = __toESM(require("path"), 1);
55362
+ var path58 = __toESM(require("path"), 1);
55268
55363
  function getAllowedPersonaIds(grants, action) {
55269
55364
  const ids = /* @__PURE__ */ new Set();
55270
55365
  for (const g2 of grants) {
@@ -55277,42 +55372,42 @@ function getAllowedPersonaIds(grants, action) {
55277
55372
  return ids;
55278
55373
  }
55279
55374
  function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
55280
- const target = path57.resolve(absPath);
55375
+ const target = path58.resolve(absPath);
55281
55376
  if (userWorkDir) {
55282
- const u = path57.resolve(userWorkDir);
55283
- const usep = u.endsWith(path57.sep) ? "" : path57.sep;
55377
+ const u = path58.resolve(userWorkDir);
55378
+ const usep = u.endsWith(path58.sep) ? "" : path58.sep;
55284
55379
  if (target === u || target.startsWith(u + usep)) return true;
55285
55380
  }
55286
- const root = path57.resolve(personaRoot);
55287
- const sep3 = root.endsWith(path57.sep) ? "" : path57.sep;
55381
+ const root = path58.resolve(personaRoot);
55382
+ const sep3 = root.endsWith(path58.sep) ? "" : path58.sep;
55288
55383
  if (!target.startsWith(root + sep3)) return false;
55289
- const rel = path57.relative(root, target);
55384
+ const rel = path58.relative(root, target);
55290
55385
  if (!rel || rel.startsWith("..")) return false;
55291
- const personaId = rel.split(path57.sep)[0];
55386
+ const personaId = rel.split(path58.sep)[0];
55292
55387
  if (!personaId) return false;
55293
55388
  const allowed = getAllowedPersonaIds(grants, action);
55294
55389
  if (allowed === "*") return true;
55295
55390
  return allowed.has(personaId);
55296
55391
  }
55297
55392
  function personaIdFromPath(absPath, personaRoot) {
55298
- const root = path57.resolve(personaRoot);
55299
- const target = path57.resolve(absPath);
55300
- const sep3 = root.endsWith(path57.sep) ? "" : path57.sep;
55393
+ const root = path58.resolve(personaRoot);
55394
+ const target = path58.resolve(absPath);
55395
+ const sep3 = root.endsWith(path58.sep) ? "" : path58.sep;
55301
55396
  if (!target.startsWith(root + sep3)) return null;
55302
- const rel = path57.relative(root, target);
55397
+ const rel = path58.relative(root, target);
55303
55398
  if (!rel || rel.startsWith("..")) return null;
55304
- const id = rel.split(path57.sep)[0];
55399
+ const id = rel.split(path58.sep)[0];
55305
55400
  return id || null;
55306
55401
  }
55307
55402
  function isPathWithin(dir, absPath) {
55308
- const d = path57.resolve(dir);
55309
- const t = path57.resolve(absPath);
55310
- const sep3 = d.endsWith(path57.sep) ? "" : path57.sep;
55403
+ const d = path58.resolve(dir);
55404
+ const t = path58.resolve(absPath);
55405
+ const sep3 = d.endsWith(path58.sep) ? "" : path58.sep;
55311
55406
  return t === d || t.startsWith(d + sep3);
55312
55407
  }
55313
55408
  function isPathInGuestBoundary(personaRoot, personaId, userWorkDir, absPath) {
55314
55409
  if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
55315
- return personaIdFromPath(path57.resolve(absPath), personaRoot) === personaId;
55410
+ return personaIdFromPath(path58.resolve(absPath), personaRoot) === personaId;
55316
55411
  }
55317
55412
 
55318
55413
  // src/handlers/history.ts
@@ -55338,7 +55433,7 @@ function buildHistoryHandlers(deps) {
55338
55433
  if (!pid) return false;
55339
55434
  return isGuestPathAllowed(
55340
55435
  ctx.grants,
55341
- path58.join(personaRoot, pid),
55436
+ path59.join(personaRoot, pid),
55342
55437
  personaRoot,
55343
55438
  "read",
55344
55439
  userWorkDir
@@ -55350,7 +55445,7 @@ function buildHistoryHandlers(deps) {
55350
55445
  };
55351
55446
  const list = async (frame, _client, ctx) => {
55352
55447
  const args = HistoryListArgs.parse(frame);
55353
- assertGuestPath(ctx, path58.resolve(args.projectPath), personaRoot, "history:list");
55448
+ assertGuestPath(ctx, path59.resolve(args.projectPath), personaRoot, "history:list");
55354
55449
  const sessions = await history.listSessions(args);
55355
55450
  return { response: { type: "history:list", sessions } };
55356
55451
  };
@@ -55382,13 +55477,13 @@ function buildHistoryHandlers(deps) {
55382
55477
  };
55383
55478
  const subagents = async (frame, _client, ctx) => {
55384
55479
  const args = HistorySubagentsArgs.parse(frame);
55385
- assertGuestPath(ctx, path58.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
55480
+ assertGuestPath(ctx, path59.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
55386
55481
  const subs = await history.listSubagents(args);
55387
55482
  return { response: { type: "history:subagents", subagents: subs } };
55388
55483
  };
55389
55484
  const subagentRead = async (frame, _client, ctx) => {
55390
55485
  const args = HistorySubagentReadArgs.parse(frame);
55391
- assertGuestPath(ctx, path58.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
55486
+ assertGuestPath(ctx, path59.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
55392
55487
  const res = await history.readSubagent(args);
55393
55488
  return { response: { type: "history:subagent-read", ...res } };
55394
55489
  };
@@ -55397,7 +55492,7 @@ function buildHistoryHandlers(deps) {
55397
55492
  if (ctx?.principal.kind === "guest" && personaRoot) {
55398
55493
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
55399
55494
  const filtered = dirs.filter(
55400
- (d) => isGuestPathAllowed(ctx.grants, path58.resolve(d.cwd), personaRoot, "read", userWorkDir)
55495
+ (d) => isGuestPathAllowed(ctx.grants, path59.resolve(d.cwd), personaRoot, "read", userWorkDir)
55401
55496
  );
55402
55497
  return { response: { type: "history:recentDirs", dirs: filtered } };
55403
55498
  }
@@ -55414,7 +55509,7 @@ function buildHistoryHandlers(deps) {
55414
55509
  }
55415
55510
 
55416
55511
  // src/handlers/workspace.ts
55417
- var path59 = __toESM(require("path"), 1);
55512
+ var path60 = __toESM(require("path"), 1);
55418
55513
  var os16 = __toESM(require("os"), 1);
55419
55514
  init_protocol();
55420
55515
  init_protocol();
@@ -55456,22 +55551,22 @@ function buildWorkspaceHandlers(deps) {
55456
55551
  const args = WorkspaceListArgs.parse(frame);
55457
55552
  const isGuest = ctx?.principal.kind === "guest";
55458
55553
  const fallbackCwd = isGuest && personaRoot ? personaRoot : os16.homedir();
55459
- const resolvedCwd = path59.resolve(args.cwd ?? fallbackCwd);
55460
- const target = args.path ? path59.resolve(resolvedCwd, args.path) : resolvedCwd;
55554
+ const resolvedCwd = path60.resolve(args.cwd ?? fallbackCwd);
55555
+ const target = args.path ? path60.resolve(resolvedCwd, args.path) : resolvedCwd;
55461
55556
  assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
55462
55557
  const res = workspace.list({ ...args, cwd: resolvedCwd });
55463
55558
  return { response: { type: "workspace:list", ...res } };
55464
55559
  };
55465
55560
  const read = async (frame, _client, ctx) => {
55466
55561
  const args = WorkspaceReadArgs.parse(frame);
55467
- const target = path59.isAbsolute(args.path) ? path59.resolve(args.path) : path59.resolve(args.cwd, args.path);
55562
+ const target = path60.isAbsolute(args.path) ? path60.resolve(args.path) : path60.resolve(args.cwd, args.path);
55468
55563
  assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
55469
55564
  const res = workspace.read(args);
55470
55565
  return { response: { type: "workspace:read", ...res } };
55471
55566
  };
55472
55567
  const skillsList = async (frame, _client, ctx) => {
55473
55568
  const args = SkillsListArgs.parse(frame);
55474
- const cwdAbs = path59.resolve(args.cwd);
55569
+ const cwdAbs = path60.resolve(args.cwd);
55475
55570
  assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
55476
55571
  const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
55477
55572
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -55483,7 +55578,7 @@ function buildWorkspaceHandlers(deps) {
55483
55578
  };
55484
55579
  const agentsList = async (frame, _client, ctx) => {
55485
55580
  const args = AgentsListArgs.parse(frame);
55486
- const cwdAbs = path59.resolve(args.cwd);
55581
+ const cwdAbs = path60.resolve(args.cwd);
55487
55582
  assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
55488
55583
  if (args.tool === "codex") {
55489
55584
  return { response: { type: "agents:list", agents: [] } };
@@ -55505,20 +55600,20 @@ function buildWorkspaceHandlers(deps) {
55505
55600
  }
55506
55601
 
55507
55602
  // src/handlers/git.ts
55508
- var path61 = __toESM(require("path"), 1);
55603
+ var path62 = __toESM(require("path"), 1);
55509
55604
  init_protocol();
55510
55605
  init_protocol();
55511
55606
 
55512
55607
  // src/workspace/git.ts
55513
55608
  var import_node_child_process13 = require("child_process");
55514
- var import_node_fs47 = __toESM(require("fs"), 1);
55515
- var import_node_path48 = __toESM(require("path"), 1);
55609
+ var import_node_fs48 = __toESM(require("fs"), 1);
55610
+ var import_node_path49 = __toESM(require("path"), 1);
55516
55611
  var import_node_util = require("util");
55517
55612
  var pexec = (0, import_node_util.promisify)(import_node_child_process13.execFile);
55518
55613
  function normalizePath(p2) {
55519
- const resolved = import_node_path48.default.resolve(p2);
55614
+ const resolved = import_node_path49.default.resolve(p2);
55520
55615
  try {
55521
- return import_node_fs47.default.realpathSync(resolved);
55616
+ return import_node_fs48.default.realpathSync(resolved);
55522
55617
  } catch {
55523
55618
  return resolved;
55524
55619
  }
@@ -55592,7 +55687,7 @@ async function listGitBranches(cwd) {
55592
55687
  function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
55593
55688
  if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
55594
55689
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
55595
- if (!isGuestPathAllowed(ctx.grants, path61.resolve(cwd), personaRoot, "read", userWorkDir)) {
55690
+ if (!isGuestPathAllowed(ctx.grants, path62.resolve(cwd), personaRoot, "read", userWorkDir)) {
55596
55691
  throw new ClawdError(
55597
55692
  ERROR_CODES.UNAUTHORIZED,
55598
55693
  `guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
@@ -55944,21 +56039,21 @@ function ensureOwner2(ctx) {
55944
56039
  if (!ctx || ctx.principal.kind !== "owner") {
55945
56040
  throw new ClawdError(
55946
56041
  ERROR_CODES.UNAUTHORIZED,
55947
- "UNAUTHORIZED: contact:setSshAccess requires owner ctx"
56042
+ "UNAUTHORIZED: contact:setRemoteAccess requires owner ctx"
55948
56043
  );
55949
56044
  }
55950
56045
  }
55951
56046
  function buildContactSshHandlers(deps) {
55952
56047
  const sshLog = deps.sshLog ?? nullContactSshLog;
55953
- const setSshAccess = async (frame, _client, ctx) => {
56048
+ const setRemoteAccess = async (frame, _client, ctx) => {
55954
56049
  ensureOwner2(ctx);
55955
56050
  const { type: _t, requestId: _r, ...rest } = frame;
55956
- const args = ContactSetSshAccessArgsSchema.parse(rest);
55957
- const hit = deps.store.setSshAccess(args.deviceId, {
55958
- sshAllowed: args.sshAllowed
56051
+ const args = ContactSetRemoteAccessArgsSchema.parse(rest);
56052
+ const hit = deps.store.setRemoteAccess(args.deviceId, {
56053
+ remoteAccessAllowed: args.remoteAccessAllowed
55959
56054
  });
55960
56055
  if (!hit) {
55961
- sshLog.warn("authz.setSshAccess", "owner \u5C1D\u8BD5\u6539 SSH \u6388\u6743\u4F46 contact \u4E0D\u5728 store", {
56056
+ sshLog.warn("authz.setRemoteAccess", "owner \u5C1D\u8BD5\u6539 SSH \u6388\u6743\u4F46 contact \u4E0D\u5728 store", {
55962
56057
  peerDeviceId: args.deviceId
55963
56058
  });
55964
56059
  throw new ClawdError(
@@ -55967,25 +56062,25 @@ function buildContactSshHandlers(deps) {
55967
56062
  );
55968
56063
  }
55969
56064
  rebuildAuthorizedKeys(deps.store, deps.sshdDir);
55970
- sshLog.info("authz.setSshAccess", "owner \u6539\u4E86 SSH \u6388\u6743 \u2192 authorized_keys \u5DF2\u91CD\u5EFA", {
56065
+ sshLog.info("authz.setRemoteAccess", "owner \u6539\u4E86 SSH \u6388\u6743 \u2192 authorized_keys \u5DF2\u91CD\u5EFA", {
55971
56066
  peerDeviceId: args.deviceId,
55972
- sshAllowed: args.sshAllowed
56067
+ remoteAccessAllowed: args.remoteAccessAllowed
55973
56068
  });
55974
56069
  deps.broadcast({
55975
- type: "contact:ssh-access-updated",
56070
+ type: "contact:remote-access-updated",
55976
56071
  deviceId: args.deviceId,
55977
- sshAllowed: args.sshAllowed
56072
+ remoteAccessAllowed: args.remoteAccessAllowed
55978
56073
  });
55979
56074
  return {
55980
56075
  response: {
55981
- type: "contact:setSshAccess:ok",
56076
+ type: "contact:setRemoteAccess:ok",
55982
56077
  deviceId: args.deviceId,
55983
- sshAllowed: args.sshAllowed
56078
+ remoteAccessAllowed: args.remoteAccessAllowed
55984
56079
  }
55985
56080
  };
55986
56081
  };
55987
56082
  return {
55988
- "contact:setSshAccess": setSshAccess
56083
+ "contact:setRemoteAccess": setRemoteAccess
55989
56084
  };
55990
56085
  }
55991
56086
 
@@ -56153,7 +56248,7 @@ function buildDeviceHandlers(deps) {
56153
56248
  grants: wh.grants,
56154
56249
  addedAt: now(),
56155
56250
  pinnedAt: existing?.pinnedAt ?? null,
56156
- sshAllowed: existing?.sshAllowed ?? false,
56251
+ remoteAccessAllowed: existing?.remoteAccessAllowed ?? false,
56157
56252
  // 建联交换:对方 whoami:ok 下发的 pubkey / osUser 落库;对方老版本不带 →
56158
56253
  // 保留既有值 / 不落 undefined 占位。见 doc/glossary/peer-mesh.md#建联交换清单
56159
56254
  ...sshPublicKey ? { sshPublicKey } : {},
@@ -56162,7 +56257,7 @@ function buildDeviceHandlers(deps) {
56162
56257
  };
56163
56258
  deps.store.upsert(contact);
56164
56259
  deps.broadcast({ type: "contact:added", contact });
56165
- if (contact.sshAllowed && sshPublicKey && sshPublicKey !== existing?.sshPublicKey) {
56260
+ if (contact.remoteAccessAllowed && sshPublicKey && sshPublicKey !== existing?.sshPublicKey) {
56166
56261
  deps.rebuildAuthorizedKeys?.();
56167
56262
  }
56168
56263
  return {
@@ -56317,7 +56412,7 @@ function buildPersonaHandlers(deps) {
56317
56412
  }
56318
56413
 
56319
56414
  // src/handlers/attachment.ts
56320
- var import_node_path49 = __toESM(require("path"), 1);
56415
+ var import_node_path50 = __toESM(require("path"), 1);
56321
56416
  init_protocol();
56322
56417
  init_protocol();
56323
56418
  var DEFAULT_TTL_SECONDS = 24 * 3600;
@@ -56397,12 +56492,12 @@ function buildAttachmentHandlers(deps) {
56397
56492
  `session ${args.sessionId} scope unresolved`
56398
56493
  );
56399
56494
  }
56400
- const cwdAbs = import_node_path49.default.resolve(sessionFile.cwd);
56401
- const candidateAbs = import_node_path49.default.isAbsolute(args.relPath) ? import_node_path49.default.resolve(args.relPath) : import_node_path49.default.resolve(cwdAbs, args.relPath);
56495
+ const cwdAbs = import_node_path50.default.resolve(sessionFile.cwd);
56496
+ const candidateAbs = import_node_path50.default.isAbsolute(args.relPath) ? import_node_path50.default.resolve(args.relPath) : import_node_path50.default.resolve(cwdAbs, args.relPath);
56402
56497
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
56403
56498
  const entries = deps.groupFileStore.list(scope, args.sessionId);
56404
56499
  const entry = entries.find((e) => {
56405
- const storedAbs = import_node_path49.default.isAbsolute(e.relPath) ? import_node_path49.default.resolve(e.relPath) : import_node_path49.default.resolve(cwdAbs, e.relPath);
56500
+ const storedAbs = import_node_path50.default.isAbsolute(e.relPath) ? import_node_path50.default.resolve(e.relPath) : import_node_path50.default.resolve(cwdAbs, e.relPath);
56406
56501
  return storedAbs === candidateAbs && !e.stale;
56407
56502
  });
56408
56503
  if (!entry) {
@@ -56427,7 +56522,7 @@ function buildAttachmentHandlers(deps) {
56427
56522
  if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
56428
56523
  const f = deps.sessionStore.read(sessionId);
56429
56524
  if (!f) return;
56430
- assertGuestAttachmentPath(ctx, import_node_path49.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
56525
+ assertGuestAttachmentPath(ctx, import_node_path50.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
56431
56526
  }
56432
56527
  const groupAdd = async (frame, _client, ctx) => {
56433
56528
  if (!deps.groupFileStore || !deps.getSessionScope) {
@@ -56442,8 +56537,8 @@ function buildAttachmentHandlers(deps) {
56442
56537
  if (!scope) {
56443
56538
  throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
56444
56539
  }
56445
- const cwdAbs = import_node_path49.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
56446
- const candidateAbs = import_node_path49.default.isAbsolute(args.relPath) ? import_node_path49.default.resolve(args.relPath) : import_node_path49.default.resolve(cwdAbs, args.relPath);
56540
+ const cwdAbs = import_node_path50.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
56541
+ const candidateAbs = import_node_path50.default.isAbsolute(args.relPath) ? import_node_path50.default.resolve(args.relPath) : import_node_path50.default.resolve(cwdAbs, args.relPath);
56447
56542
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
56448
56543
  const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
56449
56544
  const size = 0;
@@ -56502,19 +56597,19 @@ function buildAttachmentHandlers(deps) {
56502
56597
 
56503
56598
  // src/handlers/extension.ts
56504
56599
  var import_promises8 = __toESM(require("fs/promises"), 1);
56505
- var import_node_path54 = __toESM(require("path"), 1);
56600
+ var import_node_path55 = __toESM(require("path"), 1);
56506
56601
  init_protocol();
56507
56602
 
56508
56603
  // src/extension/bundle-zip.ts
56509
56604
  var import_promises5 = __toESM(require("fs/promises"), 1);
56510
- var import_node_path50 = __toESM(require("path"), 1);
56605
+ var import_node_path51 = __toESM(require("path"), 1);
56511
56606
  var import_node_crypto14 = __toESM(require("crypto"), 1);
56512
56607
  var import_jszip2 = __toESM(require_lib3(), 1);
56513
56608
  async function bundleExtensionDir(dir) {
56514
56609
  const entries = await listFilesSorted(dir);
56515
56610
  const zip = new import_jszip2.default();
56516
56611
  for (const rel of entries) {
56517
- const abs = import_node_path50.default.join(dir, rel);
56612
+ const abs = import_node_path51.default.join(dir, rel);
56518
56613
  const content = await import_promises5.default.readFile(abs);
56519
56614
  zip.file(rel, content, { date: FIXED_DATE });
56520
56615
  }
@@ -56535,7 +56630,7 @@ async function listFilesSorted(rootDir) {
56535
56630
  return out;
56536
56631
  }
56537
56632
  async function walk(absRoot, relPrefix, out) {
56538
- const dirAbs = import_node_path50.default.join(absRoot, relPrefix);
56633
+ const dirAbs = import_node_path51.default.join(absRoot, relPrefix);
56539
56634
  const entries = await import_promises5.default.readdir(dirAbs, { withFileTypes: true });
56540
56635
  for (const e of entries) {
56541
56636
  if (IGNORE_BASENAMES.has(e.name)) continue;
@@ -56589,25 +56684,25 @@ function computePublishCheck(args) {
56589
56684
 
56590
56685
  // src/extension/install-flow.ts
56591
56686
  var import_promises6 = __toESM(require("fs/promises"), 1);
56592
- var import_node_path52 = __toESM(require("path"), 1);
56687
+ var import_node_path53 = __toESM(require("path"), 1);
56593
56688
  var import_node_os19 = __toESM(require("os"), 1);
56594
56689
  var import_node_crypto15 = __toESM(require("crypto"), 1);
56595
56690
  var import_jszip3 = __toESM(require_lib3(), 1);
56596
56691
 
56597
56692
  // src/extension/paths.ts
56598
56693
  var import_node_os18 = __toESM(require("os"), 1);
56599
- var import_node_path51 = __toESM(require("path"), 1);
56694
+ var import_node_path52 = __toESM(require("path"), 1);
56600
56695
  function clawdHomeRoot(override) {
56601
- return override ?? process.env.CLAWD_HOME ?? import_node_path51.default.join(import_node_os18.default.homedir(), ".clawd");
56696
+ return override ?? process.env.CLAWD_HOME ?? import_node_path52.default.join(import_node_os18.default.homedir(), ".clawd");
56602
56697
  }
56603
56698
  function extensionsRoot(override) {
56604
- return import_node_path51.default.join(clawdHomeRoot(override), "extensions");
56699
+ return import_node_path52.default.join(clawdHomeRoot(override), "extensions");
56605
56700
  }
56606
56701
  function publishedChannelsFile(override) {
56607
- return import_node_path51.default.join(clawdHomeRoot(override), "extensions-published.json");
56702
+ return import_node_path52.default.join(clawdHomeRoot(override), "extensions-published.json");
56608
56703
  }
56609
56704
  function bundleCacheRoot(override) {
56610
- return import_node_path51.default.join(clawdHomeRoot(override), "extension-bundles");
56705
+ return import_node_path52.default.join(clawdHomeRoot(override), "extension-bundles");
56611
56706
  }
56612
56707
 
56613
56708
  // src/extension/install-flow.ts
@@ -56634,7 +56729,7 @@ async function installFromChannel(args, deps) {
56634
56729
  throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
56635
56730
  }
56636
56731
  for (const name of Object.keys(zip.files)) {
56637
- if (name.includes("..") || name.startsWith("/") || import_node_path52.default.isAbsolute(name)) {
56732
+ if (name.includes("..") || name.startsWith("/") || import_node_path53.default.isAbsolute(name)) {
56638
56733
  throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
56639
56734
  }
56640
56735
  }
@@ -56666,7 +56761,7 @@ async function installFromChannel(args, deps) {
56666
56761
  );
56667
56762
  }
56668
56763
  const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
56669
- const destDir = import_node_path52.default.join(deps.extensionsRoot, localExtId);
56764
+ const destDir = import_node_path53.default.join(deps.extensionsRoot, localExtId);
56670
56765
  let destExists = false;
56671
56766
  try {
56672
56767
  await import_promises6.default.access(destDir);
@@ -56680,16 +56775,16 @@ async function installFromChannel(args, deps) {
56680
56775
  );
56681
56776
  }
56682
56777
  const stage = await import_promises6.default.mkdtemp(
56683
- import_node_path52.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
56778
+ import_node_path53.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
56684
56779
  );
56685
56780
  try {
56686
56781
  for (const [name, entry] of Object.entries(zip.files)) {
56687
- const dest = import_node_path52.default.join(stage, name);
56782
+ const dest = import_node_path53.default.join(stage, name);
56688
56783
  if (entry.dir) {
56689
56784
  await import_promises6.default.mkdir(dest, { recursive: true });
56690
56785
  continue;
56691
56786
  }
56692
- await import_promises6.default.mkdir(import_node_path52.default.dirname(dest), { recursive: true });
56787
+ await import_promises6.default.mkdir(import_node_path53.default.dirname(dest), { recursive: true });
56693
56788
  if (name === "manifest.json") {
56694
56789
  const rewritten = { ...parsed.data, id: localExtId };
56695
56790
  await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -56710,7 +56805,7 @@ async function installFromChannel(args, deps) {
56710
56805
 
56711
56806
  // src/extension/update-flow.ts
56712
56807
  var import_promises7 = __toESM(require("fs/promises"), 1);
56713
- var import_node_path53 = __toESM(require("path"), 1);
56808
+ var import_node_path54 = __toESM(require("path"), 1);
56714
56809
  var import_node_os20 = __toESM(require("os"), 1);
56715
56810
  var import_node_crypto16 = __toESM(require("crypto"), 1);
56716
56811
  var import_jszip4 = __toESM(require_lib3(), 1);
@@ -56727,11 +56822,11 @@ async function updateFromChannel(args, deps) {
56727
56822
  channelRef.extId,
56728
56823
  channelRef.ownerPrincipalId
56729
56824
  );
56730
- const liveDir = import_node_path53.default.join(deps.extensionsRoot, localExtId);
56825
+ const liveDir = import_node_path54.default.join(deps.extensionsRoot, localExtId);
56731
56826
  const prevDir = `${liveDir}.prev`;
56732
56827
  let existingVersion;
56733
56828
  try {
56734
- const raw = await import_promises7.default.readFile(import_node_path53.default.join(liveDir, "manifest.json"), "utf8");
56829
+ const raw = await import_promises7.default.readFile(import_node_path54.default.join(liveDir, "manifest.json"), "utf8");
56735
56830
  const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
56736
56831
  if (!parsed2.success) {
56737
56832
  throw new UpdateError(
@@ -56764,7 +56859,7 @@ async function updateFromChannel(args, deps) {
56764
56859
  throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
56765
56860
  }
56766
56861
  for (const name of Object.keys(zip.files)) {
56767
- if (name.includes("..") || name.startsWith("/") || import_node_path53.default.isAbsolute(name)) {
56862
+ if (name.includes("..") || name.startsWith("/") || import_node_path54.default.isAbsolute(name)) {
56768
56863
  throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
56769
56864
  }
56770
56865
  }
@@ -56799,16 +56894,16 @@ async function updateFromChannel(args, deps) {
56799
56894
  await import_promises7.default.rm(prevDir, { recursive: true, force: true });
56800
56895
  await import_promises7.default.rename(liveDir, prevDir);
56801
56896
  const stage = await import_promises7.default.mkdtemp(
56802
- import_node_path53.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
56897
+ import_node_path54.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
56803
56898
  );
56804
56899
  try {
56805
56900
  for (const [name, entry] of Object.entries(zip.files)) {
56806
- const dest = import_node_path53.default.join(stage, name);
56901
+ const dest = import_node_path54.default.join(stage, name);
56807
56902
  if (entry.dir) {
56808
56903
  await import_promises7.default.mkdir(dest, { recursive: true });
56809
56904
  continue;
56810
56905
  }
56811
- await import_promises7.default.mkdir(import_node_path53.default.dirname(dest), { recursive: true });
56906
+ await import_promises7.default.mkdir(import_node_path54.default.dirname(dest), { recursive: true });
56812
56907
  if (name === "manifest.json") {
56813
56908
  const rewritten = { ...parsed.data, id: localExtId };
56814
56909
  await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -56901,7 +56996,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
56901
56996
  );
56902
56997
  }
56903
56998
  }
56904
- const manifestPath = import_node_path54.default.join(root, extId, "manifest.json");
56999
+ const manifestPath = import_node_path55.default.join(root, extId, "manifest.json");
56905
57000
  const manifest = await readManifest(root, extId);
56906
57001
  const next = { ...manifest, version: newVersion };
56907
57002
  const tmp = `${manifestPath}.tmp`;
@@ -56909,7 +57004,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
56909
57004
  await import_promises8.default.rename(tmp, manifestPath);
56910
57005
  }
56911
57006
  async function readManifest(root, extId) {
56912
- const file = import_node_path54.default.join(root, extId, "manifest.json");
57007
+ const file = import_node_path55.default.join(root, extId, "manifest.json");
56913
57008
  let raw;
56914
57009
  try {
56915
57010
  raw = await import_promises8.default.readFile(file, "utf8");
@@ -57000,7 +57095,7 @@ function buildExtensionHandlers(deps) {
57000
57095
  };
57001
57096
  async function buildSnapshotMeta(extId) {
57002
57097
  const manifest = await readManifest(deps.root, extId);
57003
- const { sha256, buffer } = await bundleExtensionDir(import_node_path54.default.join(deps.root, extId));
57098
+ const { sha256, buffer } = await bundleExtensionDir(import_node_path55.default.join(deps.root, extId));
57004
57099
  return { manifest, contentHash: sha256, buffer };
57005
57100
  }
57006
57101
  const publish = async (frame, _client, ctx) => {
@@ -57181,9 +57276,9 @@ function buildExtensionHandlers(deps) {
57181
57276
  }
57182
57277
 
57183
57278
  // src/app-builder/project-store.ts
57184
- var import_node_fs48 = require("fs");
57279
+ var import_node_fs49 = require("fs");
57185
57280
  var import_node_child_process14 = require("child_process");
57186
- var import_node_path55 = require("path");
57281
+ var import_node_path56 = require("path");
57187
57282
  init_protocol();
57188
57283
  var PROJECTS_DIR = "projects";
57189
57284
  var META_FILE = ".clawd-project.json";
@@ -57197,19 +57292,19 @@ var ProjectStore = class {
57197
57292
  root;
57198
57293
  /** projects/<name>/.clawd-project.json 路径 */
57199
57294
  metaPath(name) {
57200
- return (0, import_node_path55.join)(this.projectsRoot(), name, META_FILE);
57295
+ return (0, import_node_path56.join)(this.projectsRoot(), name, META_FILE);
57201
57296
  }
57202
57297
  /** projects/<name>/ 目录路径(cwd 用) */
57203
57298
  projectDir(name) {
57204
- return (0, import_node_path55.join)(this.projectsRoot(), name);
57299
+ return (0, import_node_path56.join)(this.projectsRoot(), name);
57205
57300
  }
57206
57301
  projectsRoot() {
57207
- return (0, import_node_path55.join)(this.root, PROJECTS_DIR);
57302
+ return (0, import_node_path56.join)(this.root, PROJECTS_DIR);
57208
57303
  }
57209
57304
  async list() {
57210
57305
  let entries;
57211
57306
  try {
57212
- entries = await import_node_fs48.promises.readdir(this.projectsRoot());
57307
+ entries = await import_node_fs49.promises.readdir(this.projectsRoot());
57213
57308
  } catch (err) {
57214
57309
  if (err.code === "ENOENT") return [];
57215
57310
  throw err;
@@ -57217,7 +57312,7 @@ var ProjectStore = class {
57217
57312
  const out = [];
57218
57313
  for (const name of entries) {
57219
57314
  try {
57220
- const raw = await import_node_fs48.promises.readFile(this.metaPath(name), "utf8");
57315
+ const raw = await import_node_fs49.promises.readFile(this.metaPath(name), "utf8");
57221
57316
  const json = JSON.parse(raw);
57222
57317
  let migrated = false;
57223
57318
  if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
@@ -57228,7 +57323,7 @@ var ProjectStore = class {
57228
57323
  if (parsed.success) {
57229
57324
  out.push(parsed.data);
57230
57325
  if (migrated) {
57231
- void import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
57326
+ void import_node_fs49.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
57232
57327
  });
57233
57328
  }
57234
57329
  }
@@ -57272,8 +57367,8 @@ var ProjectStore = class {
57272
57367
  throw new Error(`invalid name "${name}": ${validated.error.message}`);
57273
57368
  }
57274
57369
  const dir = this.projectDir(name);
57275
- await import_node_fs48.promises.mkdir(dir, { recursive: true });
57276
- await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
57370
+ await import_node_fs49.promises.mkdir(dir, { recursive: true });
57371
+ await import_node_fs49.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
57277
57372
  return meta;
57278
57373
  }
57279
57374
  /**
@@ -57316,7 +57411,7 @@ var ProjectStore = class {
57316
57411
  }
57317
57412
  async delete(name) {
57318
57413
  const dir = this.projectDir(name);
57319
- await import_node_fs48.promises.rm(dir, { recursive: true, force: true });
57414
+ await import_node_fs49.promises.rm(dir, { recursive: true, force: true });
57320
57415
  }
57321
57416
  async updatePort(name, newPort) {
57322
57417
  if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
@@ -57332,7 +57427,7 @@ var ProjectStore = class {
57332
57427
  throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
57333
57428
  }
57334
57429
  const updated = { ...target, port: newPort };
57335
- await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
57430
+ await import_node_fs49.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
57336
57431
  return updated;
57337
57432
  }
57338
57433
  /**
@@ -57349,7 +57444,7 @@ var ProjectStore = class {
57349
57444
  if (!validated.success) {
57350
57445
  throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
57351
57446
  }
57352
- await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
57447
+ await import_node_fs49.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
57353
57448
  return validated.data;
57354
57449
  }
57355
57450
  /**
@@ -57370,7 +57465,7 @@ var ProjectStore = class {
57370
57465
  if (!validated.success) {
57371
57466
  throw new Error(`invalid publishJob: ${validated.error.message}`);
57372
57467
  }
57373
- await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
57468
+ await import_node_fs49.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
57374
57469
  return validated.data;
57375
57470
  }
57376
57471
  /** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
@@ -57385,7 +57480,7 @@ var ProjectStore = class {
57385
57480
  if (!validated.success) {
57386
57481
  throw new Error(`failed to clear publishJob: ${validated.error.message}`);
57387
57482
  }
57388
- await import_node_fs48.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
57483
+ await import_node_fs49.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
57389
57484
  return validated.data;
57390
57485
  }
57391
57486
  };
@@ -57506,8 +57601,8 @@ var PublishJobRegistry = class {
57506
57601
 
57507
57602
  // src/app-builder/publish-job-runner.ts
57508
57603
  var import_node_child_process16 = require("child_process");
57509
- var import_node_fs49 = require("fs");
57510
- var import_node_path56 = require("path");
57604
+ var import_node_fs50 = require("fs");
57605
+ var import_node_path57 = require("path");
57511
57606
 
57512
57607
  // src/app-builder/publish-stage-parser.ts
57513
57608
  var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
@@ -57534,19 +57629,19 @@ function tailStderrLines(buf, n) {
57534
57629
  // src/app-builder/publish-job-runner.ts
57535
57630
  async function startPublishJob(deps, args) {
57536
57631
  const { registry: registry2, projectDir } = deps;
57537
- const spawn15 = deps.spawnImpl ?? import_node_child_process16.spawn;
57632
+ const spawn16 = deps.spawnImpl ?? import_node_child_process16.spawn;
57538
57633
  if (registry2.has(args.name)) {
57539
57634
  return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
57540
57635
  }
57541
57636
  const projDir = projectDir(args.name);
57542
- const logPath = (0, import_node_path56.join)(projDir, ".publish.log");
57637
+ const logPath = (0, import_node_path57.join)(projDir, ".publish.log");
57543
57638
  let logStream = null;
57544
57639
  try {
57545
- logStream = (0, import_node_fs49.createWriteStream)(logPath, { flags: "w" });
57640
+ logStream = (0, import_node_fs50.createWriteStream)(logPath, { flags: "w" });
57546
57641
  } catch {
57547
57642
  logStream = null;
57548
57643
  }
57549
- const child = spawn15("bash", [args.scriptPath, projDir, args.personaRoot ?? ""], {
57644
+ const child = spawn16("bash", [args.scriptPath, projDir, args.personaRoot ?? ""], {
57550
57645
  cwd: projDir,
57551
57646
  env: process.env,
57552
57647
  stdio: ["ignore", "pipe", "pipe"]
@@ -57799,8 +57894,8 @@ async function recoverInterruptedJobs(deps) {
57799
57894
 
57800
57895
  // src/handlers/app-builder.ts
57801
57896
  init_protocol();
57802
- var import_node_path57 = require("path");
57803
- var import_node_fs50 = require("fs");
57897
+ var import_node_path58 = require("path");
57898
+ var import_node_fs51 = require("fs");
57804
57899
  var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
57805
57900
  var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
57806
57901
  async function recoverInterruptedPublishJobs(store, logger) {
@@ -57881,7 +57976,7 @@ function buildAppBuilderHandlers(deps) {
57881
57976
  async function listAllUsersProjects() {
57882
57977
  if (!deps.usersRoot || !deps.getStore) return [];
57883
57978
  const getStore = deps.getStore;
57884
- const userIds = await import_node_fs50.promises.readdir(deps.usersRoot).catch(() => []);
57979
+ const userIds = await import_node_fs51.promises.readdir(deps.usersRoot).catch(() => []);
57885
57980
  const perUser = await Promise.all(
57886
57981
  userIds.map((uid) => getStore(uid).list().catch(() => []))
57887
57982
  );
@@ -57957,8 +58052,8 @@ function buildAppBuilderHandlers(deps) {
57957
58052
  const project = await userStore.create(f.name, reservedPorts);
57958
58053
  try {
57959
58054
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
57960
- const templateSrcDir = (0, import_node_path57.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
57961
- const scaffoldScript = (0, import_node_path57.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
58055
+ const templateSrcDir = (0, import_node_path58.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
58056
+ const scaffoldScript = (0, import_node_path58.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
57962
58057
  const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
57963
58058
  deps.logger?.info("app-builder.scaffold.done", {
57964
58059
  name: project.name,
@@ -58179,7 +58274,7 @@ function buildAppBuilderHandlers(deps) {
58179
58274
  await userStore.clearPublishJob(args.name);
58180
58275
  }
58181
58276
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
58182
- const scriptPath = (0, import_node_path57.join)(deps.deployKitRoot, "scripts", "publish.sh");
58277
+ const scriptPath = (0, import_node_path58.join)(deps.deployKitRoot, "scripts", "publish.sh");
58183
58278
  deps.logger?.info("app-builder.publish.start", {
58184
58279
  name: args.name,
58185
58280
  sessionId: boundSession.sessionId,
@@ -58355,7 +58450,7 @@ function buildVisitorHandlers(deps) {
58355
58450
 
58356
58451
  // src/extension/registry.ts
58357
58452
  var import_promises9 = __toESM(require("fs/promises"), 1);
58358
- var import_node_path58 = __toESM(require("path"), 1);
58453
+ var import_node_path59 = __toESM(require("path"), 1);
58359
58454
  async function loadAll(root) {
58360
58455
  let entries;
58361
58456
  try {
@@ -58368,13 +58463,13 @@ async function loadAll(root) {
58368
58463
  for (const ent of entries) {
58369
58464
  if (!ent.isDirectory()) continue;
58370
58465
  if (ent.name.startsWith(".")) continue;
58371
- records.push(await loadOne(import_node_path58.default.join(root, ent.name), ent.name));
58466
+ records.push(await loadOne(import_node_path59.default.join(root, ent.name), ent.name));
58372
58467
  }
58373
58468
  records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
58374
58469
  return records;
58375
58470
  }
58376
58471
  async function loadOne(dir, dirName) {
58377
- const manifestPath = import_node_path58.default.join(dir, "manifest.json");
58472
+ const manifestPath = import_node_path59.default.join(dir, "manifest.json");
58378
58473
  let raw;
58379
58474
  try {
58380
58475
  raw = await import_promises9.default.readFile(manifestPath, "utf8");
@@ -58419,7 +58514,7 @@ async function loadOne(dir, dirName) {
58419
58514
 
58420
58515
  // src/extension/uninstall.ts
58421
58516
  var import_promises10 = __toESM(require("fs/promises"), 1);
58422
- var import_node_path59 = __toESM(require("path"), 1);
58517
+ var import_node_path60 = __toESM(require("path"), 1);
58423
58518
  var UninstallError = class extends Error {
58424
58519
  constructor(code, message) {
58425
58520
  super(message);
@@ -58428,7 +58523,7 @@ var UninstallError = class extends Error {
58428
58523
  code;
58429
58524
  };
58430
58525
  async function uninstall(deps) {
58431
- const dir = import_node_path59.default.join(deps.root, deps.extId);
58526
+ const dir = import_node_path60.default.join(deps.root, deps.extId);
58432
58527
  try {
58433
58528
  await import_promises10.default.access(dir);
58434
58529
  } catch {
@@ -58440,6 +58535,131 @@ async function uninstall(deps) {
58440
58535
 
58441
58536
  // src/handlers/index.ts
58442
58537
  var import_node_crypto18 = require("crypto");
58538
+
58539
+ // src/handlers/peer-exec.ts
58540
+ init_protocol();
58541
+
58542
+ // src/peer-ops/run-command.ts
58543
+ var import_node_child_process17 = require("child_process");
58544
+ var import_node_os21 = __toESM(require("os"), 1);
58545
+ var DEFAULT_TIMEOUT_MS5 = 3e4;
58546
+ var MAX_TIMEOUT_MS = 3e5;
58547
+ var MAX_OUTPUT_BYTES = 1048576;
58548
+ function runCommand(command, opts = {}) {
58549
+ const timeoutMs = Math.min(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS5, MAX_TIMEOUT_MS);
58550
+ return new Promise((resolve6) => {
58551
+ const child = (0, import_node_child_process17.spawn)("bash", ["-c", command], { cwd: import_node_os21.default.homedir() });
58552
+ const out = [];
58553
+ const err = [];
58554
+ let outBytes = 0;
58555
+ let errBytes = 0;
58556
+ let outTrunc = false;
58557
+ let errTrunc = false;
58558
+ let timedOut = false;
58559
+ const capture = (chunk, buf, which) => {
58560
+ const used = which === "out" ? outBytes : errBytes;
58561
+ const room = MAX_OUTPUT_BYTES - used;
58562
+ if (room <= 0) {
58563
+ if (which === "out") outTrunc = true;
58564
+ else errTrunc = true;
58565
+ return;
58566
+ }
58567
+ if (chunk.length > room) {
58568
+ buf.push(chunk.subarray(0, room));
58569
+ if (which === "out") {
58570
+ outBytes += room;
58571
+ outTrunc = true;
58572
+ } else {
58573
+ errBytes += room;
58574
+ errTrunc = true;
58575
+ }
58576
+ } else {
58577
+ buf.push(chunk);
58578
+ if (which === "out") outBytes += chunk.length;
58579
+ else errBytes += chunk.length;
58580
+ }
58581
+ };
58582
+ child.stdout.on("data", (c) => capture(c, out, "out"));
58583
+ child.stderr.on("data", (c) => capture(c, err, "err"));
58584
+ const timer = setTimeout(() => {
58585
+ timedOut = true;
58586
+ child.kill("SIGTERM");
58587
+ setTimeout(() => child.kill("SIGKILL"), 2e3).unref();
58588
+ }, timeoutMs);
58589
+ child.on("close", (code) => {
58590
+ clearTimeout(timer);
58591
+ resolve6({
58592
+ stdout: Buffer.concat(out).toString("utf8"),
58593
+ stderr: Buffer.concat(err).toString("utf8"),
58594
+ exitCode: timedOut ? null : code,
58595
+ timedOut,
58596
+ stdoutTruncated: outTrunc,
58597
+ stderrTruncated: errTrunc
58598
+ });
58599
+ });
58600
+ child.on("error", () => {
58601
+ clearTimeout(timer);
58602
+ resolve6({
58603
+ stdout: Buffer.concat(out).toString("utf8"),
58604
+ stderr: Buffer.concat(err).toString("utf8"),
58605
+ exitCode: null,
58606
+ timedOut,
58607
+ stdoutTruncated: outTrunc,
58608
+ stderrTruncated: errTrunc
58609
+ });
58610
+ });
58611
+ });
58612
+ }
58613
+
58614
+ // src/handlers/peer-exec.ts
58615
+ function buildPeerExecHandlers(deps) {
58616
+ const run = async (frame, _client, ctx) => {
58617
+ const { type: _t, requestId: _r, sessionId: _s, ...rest } = frame;
58618
+ const args = PeerExecRunArgsSchema.parse(rest);
58619
+ if (args.peerDeviceId) {
58620
+ if (ctx?.principal.kind === "guest") {
58621
+ throw new ClawdError(
58622
+ ERROR_CODES.UNAUTHORIZED,
58623
+ "UNAUTHORIZED: cross-device exec cannot be initiated by a guest"
58624
+ );
58625
+ }
58626
+ const contact2 = deps.store.get(args.peerDeviceId);
58627
+ if (!contact2) {
58628
+ throw new ClawdError(
58629
+ ERROR_CODES.CONTACT_NOT_FOUND,
58630
+ `CONTACT_NOT_FOUND: ${args.peerDeviceId} not in store`
58631
+ );
58632
+ }
58633
+ const outcome = await deps.forwardToPeer({
58634
+ contact: { remoteUrl: contact2.remoteUrl, connectToken: contact2.connectToken },
58635
+ command: args.command,
58636
+ ...args.timeoutMs ? { timeoutMs: args.timeoutMs } : {}
58637
+ });
58638
+ return { response: outcome };
58639
+ }
58640
+ const callerId = ensureGuest(ctx);
58641
+ const contact = deps.store.get(callerId);
58642
+ if (!contact?.remoteAccessAllowed) {
58643
+ throw new ClawdError(
58644
+ ERROR_CODES.UNAUTHORIZED,
58645
+ `UNAUTHORIZED: ${callerId} not authorized for remote access`
58646
+ );
58647
+ }
58648
+ const result = await runCommand(args.command, args.timeoutMs ? { timeoutMs: args.timeoutMs } : {});
58649
+ return {
58650
+ response: { type: "peerExec:run:ok", ...result }
58651
+ };
58652
+ };
58653
+ return { "peerExec:run": run };
58654
+ }
58655
+ function ensureGuest(ctx) {
58656
+ if (!ctx || ctx.principal.kind !== "guest") {
58657
+ throw new ClawdError(ERROR_CODES.UNAUTHORIZED, "UNAUTHORIZED: peerExec:run requires guest ctx");
58658
+ }
58659
+ return ctx.principal.id;
58660
+ }
58661
+
58662
+ // src/handlers/index.ts
58443
58663
  function buildMethodHandlers(deps) {
58444
58664
  return {
58445
58665
  ...buildSessionHandlers({
@@ -58489,6 +58709,12 @@ function buildMethodHandlers(deps) {
58489
58709
  sshdDir: deps.sshdDir,
58490
58710
  sshLog: deps.contactSshLog
58491
58711
  }),
58712
+ // 跨设备 exec(spec 2026-07-16):A 侧带 peerDeviceId → forwardPeerExecToPeer;
58713
+ // B 侧无 peerDeviceId → 本地 runCommand(handler 判 remoteAccessAllowed)。
58714
+ ...buildPeerExecHandlers({
58715
+ store: deps.contactStore,
58716
+ forwardToPeer: (a) => forwardPeerExecToPeer(a)
58717
+ }),
58492
58718
  whoami: buildWhoamiHandler({
58493
58719
  ownerDisplayName: deps.ownerDisplayName,
58494
58720
  ownerPrincipalId: deps.ownerPrincipalId,
@@ -58540,7 +58766,7 @@ function buildMethodHandlers(deps) {
58540
58766
  }
58541
58767
 
58542
58768
  // src/app-builder/dev-server-supervisor.ts
58543
- var import_node_child_process17 = require("child_process");
58769
+ var import_node_child_process18 = require("child_process");
58544
58770
  var import_node_events2 = require("events");
58545
58771
  var DEFAULT_READY_PATTERN = /Local:\s+https?:\/\/|Nest application successfully started|server listening on/i;
58546
58772
  var DevServerSupervisor = class extends import_node_events2.EventEmitter {
@@ -58577,7 +58803,7 @@ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
58577
58803
  tunnelHost: args.tunnelHost,
58578
58804
  devCommand: cmd
58579
58805
  });
58580
- const child = (0, import_node_child_process17.spawn)("sh", ["-c", cmd], {
58806
+ const child = (0, import_node_child_process18.spawn)("sh", ["-c", cmd], {
58581
58807
  cwd: args.cwd,
58582
58808
  env,
58583
58809
  stdio: "pipe",
@@ -58829,9 +59055,9 @@ var METHOD_GRANT_MAP = {
58829
59055
  "contact:list": ADMIN_ANY,
58830
59056
  "contact:pin": ADMIN_ANY,
58831
59057
  "contact:remove": ADMIN_ANY,
58832
- // contact:setSshAccess (owner UI 配 SSH 授权):ADMIN_ANY
59058
+ // contact:setRemoteAccess (owner UI 配 SSH 授权):ADMIN_ANY
58833
59059
  // (对齐 inbox:postMessage 的"能连上=有 auth,业务在 handler 校"模式)
58834
- "contact:setSshAccess": ADMIN_ANY,
59060
+ "contact:setRemoteAccess": ADMIN_ANY,
58835
59061
  // ---- visitor:* (访客名单,owner-only) ----
58836
59062
  // owner 看完整访客名单(含没开会话的);guest 不可调(handler 内再 assertOwner 兜底)。
58837
59063
  "visitor:list": ADMIN_ANY,
@@ -58910,6 +59136,9 @@ var METHOD_GRANT_MAP = {
58910
59136
  // run handler 内做 persona.public + assertGrant(send) 双重校验(guest grants 是 wildcard,
58911
59137
  // 单 assertGrant 不够)。本地 cc 仍走 /api/rpc(EXPOSED_RPC_METHODS),不经此表。
58912
59138
  "personaDispatch:run": CAPABILITY_SCOPED,
59139
+ // 跨设备 exec(spec 2026-07-16):public——"能连上=有 auth",业务在 handler 校
59140
+ // (ensureGuest + remoteAccessAllowed),跟 inbox:postMessage 同模式。
59141
+ "peerExec:run": { kind: "public" },
58913
59142
  // 2026-05-26 修 clawd people TUI 渲染 mix 串扰回归(参考 f1133ff0 原 owner 路径
58914
59143
  // snapshot/buffer/flush 修复):之前三件套 ADMIN_ANY 把 guest 拦在 dispatcher 401,
58915
59144
  // 导致 XtermPanel 重挂载拉 snapshot 在 guest 路径不生效,老的字节流断裂串扰回来。
@@ -59016,8 +59245,8 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
59016
59245
  }
59017
59246
 
59018
59247
  // src/extension/runtime.ts
59019
- var import_node_child_process18 = require("child_process");
59020
- var import_node_path60 = __toESM(require("path"), 1);
59248
+ var import_node_child_process19 = require("child_process");
59249
+ var import_node_path61 = __toESM(require("path"), 1);
59021
59250
  var import_promises11 = require("timers/promises");
59022
59251
 
59023
59252
  // src/extension/port-allocator.ts
@@ -59118,13 +59347,13 @@ var Runtime = class {
59118
59347
  /\$CLAWOS_EXT_PORT/g,
59119
59348
  String(port)
59120
59349
  );
59121
- const dir = import_node_path60.default.join(this.root, extId);
59350
+ const dir = import_node_path61.default.join(this.root, extId);
59122
59351
  const env = {
59123
59352
  ...process.env,
59124
59353
  CLAWOS_EXT_PORT: String(port),
59125
59354
  CLAWOS_EXT_ID: extId
59126
59355
  };
59127
- const child = (0, import_node_child_process18.spawn)("sh", ["-c", cmd], {
59356
+ const child = (0, import_node_child_process19.spawn)("sh", ["-c", cmd], {
59128
59357
  cwd: dir,
59129
59358
  env,
59130
59359
  stdio: ["ignore", "pipe", "pipe"],
@@ -59230,7 +59459,7 @@ ${handle.stderrTail}`
59230
59459
 
59231
59460
  // src/extension/published-channels.ts
59232
59461
  var import_promises12 = __toESM(require("fs/promises"), 1);
59233
- var import_node_path61 = __toESM(require("path"), 1);
59462
+ var import_node_path62 = __toESM(require("path"), 1);
59234
59463
  init_zod();
59235
59464
  var PublishedChannelsError = class extends Error {
59236
59465
  constructor(code, message) {
@@ -59329,7 +59558,7 @@ var PublishedChannelStore = class {
59329
59558
  )
59330
59559
  };
59331
59560
  const tmp = `${this.filePath}.tmp`;
59332
- await import_promises12.default.mkdir(import_node_path61.default.dirname(this.filePath), { recursive: true });
59561
+ await import_promises12.default.mkdir(import_node_path62.default.dirname(this.filePath), { recursive: true });
59333
59562
  await import_promises12.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
59334
59563
  await import_promises12.default.rename(tmp, this.filePath);
59335
59564
  }
@@ -59337,7 +59566,7 @@ var PublishedChannelStore = class {
59337
59566
 
59338
59567
  // src/extension/bundle-cache.ts
59339
59568
  var import_promises13 = __toESM(require("fs/promises"), 1);
59340
- var import_node_path62 = __toESM(require("path"), 1);
59569
+ var import_node_path63 = __toESM(require("path"), 1);
59341
59570
  var BundleCache = class {
59342
59571
  constructor(rootDir) {
59343
59572
  this.rootDir = rootDir;
@@ -59346,14 +59575,14 @@ var BundleCache = class {
59346
59575
  /** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
59347
59576
  async write(snapshotHash, buffer) {
59348
59577
  await import_promises13.default.mkdir(this.rootDir, { recursive: true });
59349
- const file = import_node_path62.default.join(this.rootDir, `${snapshotHash}.zip`);
59578
+ const file = import_node_path63.default.join(this.rootDir, `${snapshotHash}.zip`);
59350
59579
  const tmp = `${file}.tmp`;
59351
59580
  await import_promises13.default.writeFile(tmp, buffer, { mode: 384 });
59352
59581
  await import_promises13.default.rename(tmp, file);
59353
59582
  }
59354
59583
  /** Returns the bundle bytes, or null when the file doesn't exist. */
59355
59584
  async read(snapshotHash) {
59356
- const file = import_node_path62.default.join(this.rootDir, `${snapshotHash}.zip`);
59585
+ const file = import_node_path63.default.join(this.rootDir, `${snapshotHash}.zip`);
59357
59586
  try {
59358
59587
  return await import_promises13.default.readFile(file);
59359
59588
  } catch (e) {
@@ -59363,7 +59592,7 @@ var BundleCache = class {
59363
59592
  }
59364
59593
  /** Idempotent — missing file is not an error. */
59365
59594
  async delete(snapshotHash) {
59366
- const file = import_node_path62.default.join(this.rootDir, `${snapshotHash}.zip`);
59595
+ const file = import_node_path63.default.join(this.rootDir, `${snapshotHash}.zip`);
59367
59596
  await import_promises13.default.rm(file, { force: true });
59368
59597
  }
59369
59598
  };
@@ -59372,7 +59601,7 @@ var BundleCache = class {
59372
59601
  var import_meta6 = {};
59373
59602
  function currentOsUser() {
59374
59603
  try {
59375
- return import_node_os21.default.userInfo().username;
59604
+ return import_node_os22.default.userInfo().username;
59376
59605
  } catch {
59377
59606
  return "";
59378
59607
  }
@@ -59391,26 +59620,26 @@ async function startDaemon(config) {
59391
59620
  },
59392
59621
  source: "daemon",
59393
59622
  sampling: logShippingCfg.sampling,
59394
- homeDir: import_node_os21.default.homedir()
59623
+ homeDir: import_node_os22.default.homedir()
59395
59624
  });
59396
- const logDir = import_node_path63.default.join(config.dataDir, "log");
59397
- import_node_fs51.default.mkdirSync(logDir, { recursive: true });
59625
+ const logDir = import_node_path64.default.join(config.dataDir, "log");
59626
+ import_node_fs52.default.mkdirSync(logDir, { recursive: true });
59398
59627
  const logger = createLogger({
59399
59628
  level: config.logLevel,
59400
- file: import_node_path63.default.join(logDir, "clawd.log"),
59629
+ file: import_node_path64.default.join(logDir, "clawd.log"),
59401
59630
  logClient
59402
59631
  });
59403
59632
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
59404
59633
  const screenIdleProbeLogger = createFileOnlyLogger({
59405
- file: import_node_path63.default.join(logDir, "screen-idle-probe.log"),
59634
+ file: import_node_path64.default.join(logDir, "screen-idle-probe.log"),
59406
59635
  level: "debug"
59407
59636
  });
59408
59637
  logger.info("screen-idle probe logger enabled", {
59409
- file: import_node_path63.default.join(logDir, "screen-idle-probe.log")
59638
+ file: import_node_path64.default.join(logDir, "screen-idle-probe.log")
59410
59639
  });
59411
59640
  const CAP_TARGETS = [
59412
- import_node_path63.default.join(logDir, "clawd.log"),
59413
- import_node_path63.default.join(logDir, "screen-idle-probe.log")
59641
+ import_node_path64.default.join(logDir, "clawd.log"),
59642
+ import_node_path64.default.join(logDir, "screen-idle-probe.log")
59414
59643
  ];
59415
59644
  const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
59416
59645
  const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
@@ -59521,7 +59750,7 @@ async function startDaemon(config) {
59521
59750
  // 立即重建 authorized_keys(重新握手 = 迁移路径)。见 …#建联交换清单
59522
59751
  sshPublicKey: selfSshPublicKey,
59523
59752
  osUser: selfOsUser,
59524
- rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore, import_node_path63.default.join(config.dataDir, "sshd")),
59753
+ rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore, import_node_path64.default.join(config.dataDir, "sshd")),
59525
59754
  // 决策 #16 换票补全:用 owner 当前 jwt 换 aud=对方设备 的票(本机主动连对方用)。
59526
59755
  // 现读 ownerIdentityStore(热切换后用新身份);失败 → null → 保持空票。
59527
59756
  exchangeToken: async (targetDeviceId) => {
@@ -59569,8 +59798,8 @@ async function startDaemon(config) {
59569
59798
  const agents = new AgentsScanner();
59570
59799
  const history = new ClaudeHistoryReader();
59571
59800
  let transport = null;
59572
- const personaStore = new PersonaStore(import_node_path63.default.join(config.dataDir, "personas"));
59573
- const usersRoot = import_node_path63.default.join(config.dataDir, "users");
59801
+ const personaStore = new PersonaStore(import_node_path64.default.join(config.dataDir, "personas"));
59802
+ const usersRoot = import_node_path64.default.join(config.dataDir, "users");
59574
59803
  const defaultsRoot = findDefaultsRoot(logger);
59575
59804
  if (defaultsRoot) {
59576
59805
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -59590,17 +59819,17 @@ async function startDaemon(config) {
59590
59819
  migrateCodexSandbox({ store: personaStore, logger });
59591
59820
  const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
59592
59821
  const personaDispatchManager = new PersonaDispatchManager({ genId: () => v4_default() });
59593
- const here = typeof __dirname === "string" ? __dirname : import_node_path63.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
59822
+ const here = typeof __dirname === "string" ? __dirname : import_node_path64.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
59594
59823
  const mcpConfigs = [];
59595
59824
  const dispatchServerCandidates = [
59596
- import_node_path63.default.join(here, "dispatch", "mcp-server.cjs"),
59825
+ import_node_path64.default.join(here, "dispatch", "mcp-server.cjs"),
59597
59826
  // 生产 dist/index → dist/dispatch/mcp-server.cjs
59598
- import_node_path63.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
59827
+ import_node_path64.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
59599
59828
  // dev tsx src/index → ../dist/dispatch/mcp-server.cjs
59600
59829
  ];
59601
- const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs51.default.existsSync(p2));
59830
+ const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
59602
59831
  if (dispatchServerScriptPath) {
59603
- const dispatchLogPath = import_node_path63.default.join(logDir, "dispatch-mcp-server.log");
59832
+ const dispatchLogPath = import_node_path64.default.join(logDir, "dispatch-mcp-server.log");
59604
59833
  const dispatchCfgPath = writeDispatchMcpConfig({
59605
59834
  dataDir: config.dataDir,
59606
59835
  serverScriptPath: dispatchServerScriptPath,
@@ -59618,12 +59847,12 @@ async function startDaemon(config) {
59618
59847
  });
59619
59848
  }
59620
59849
  const ticketServerCandidates = [
59621
- import_node_path63.default.join(here, "ticket", "mcp-server.cjs"),
59622
- import_node_path63.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
59850
+ import_node_path64.default.join(here, "ticket", "mcp-server.cjs"),
59851
+ import_node_path64.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
59623
59852
  ];
59624
- const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs51.default.existsSync(p2));
59853
+ const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
59625
59854
  if (ticketServerScriptPath) {
59626
- const ticketLogPath = import_node_path63.default.join(logDir, "ticket-mcp-server.log");
59855
+ const ticketLogPath = import_node_path64.default.join(logDir, "ticket-mcp-server.log");
59627
59856
  const ticketCfgPath = writeTicketMcpConfig({
59628
59857
  dataDir: config.dataDir,
59629
59858
  serverScriptPath: ticketServerScriptPath,
@@ -59643,12 +59872,12 @@ async function startDaemon(config) {
59643
59872
  });
59644
59873
  }
59645
59874
  const shiftServerCandidates = [
59646
- import_node_path63.default.join(here, "shift", "mcp-server.cjs"),
59647
- import_node_path63.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
59875
+ import_node_path64.default.join(here, "shift", "mcp-server.cjs"),
59876
+ import_node_path64.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
59648
59877
  ];
59649
- const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs51.default.existsSync(p2));
59878
+ const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
59650
59879
  if (shiftServerScriptPath) {
59651
- const shiftLogPath = import_node_path63.default.join(logDir, "shift-mcp-server.log");
59880
+ const shiftLogPath = import_node_path64.default.join(logDir, "shift-mcp-server.log");
59652
59881
  const shiftCfgPath = await writeShiftMcpConfig({
59653
59882
  dataDir: config.dataDir,
59654
59883
  serverScriptPath: shiftServerScriptPath,
@@ -59667,12 +59896,12 @@ async function startDaemon(config) {
59667
59896
  );
59668
59897
  }
59669
59898
  const inboxServerCandidates = [
59670
- import_node_path63.default.join(here, "inbox", "mcp-server.cjs"),
59671
- import_node_path63.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
59899
+ import_node_path64.default.join(here, "inbox", "mcp-server.cjs"),
59900
+ import_node_path64.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
59672
59901
  ];
59673
- const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs51.default.existsSync(p2));
59902
+ const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
59674
59903
  if (inboxServerScriptPath) {
59675
- const inboxLogPath = import_node_path63.default.join(logDir, "inbox-mcp-server.log");
59904
+ const inboxLogPath = import_node_path64.default.join(logDir, "inbox-mcp-server.log");
59676
59905
  const inboxCfgPath = await writeInboxMcpConfig({
59677
59906
  dataDir: config.dataDir,
59678
59907
  serverScriptPath: inboxServerScriptPath,
@@ -59690,8 +59919,29 @@ async function startDaemon(config) {
59690
59919
  { tried: inboxServerCandidates }
59691
59920
  );
59692
59921
  }
59922
+ const peerOpsServerCandidates = [
59923
+ import_node_path64.default.join(here, "peer-ops", "mcp-server.cjs"),
59924
+ import_node_path64.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
59925
+ ];
59926
+ const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
59927
+ if (peerOpsServerScriptPath) {
59928
+ const peerOpsCfgPath = writePeerOpsMcpConfig({
59929
+ dataDir: config.dataDir,
59930
+ serverScriptPath: peerOpsServerScriptPath
59931
+ });
59932
+ mcpConfigs.push({ id: "peer-ops", path: peerOpsCfgPath });
59933
+ logger.info("peer-ops.mcp.json written", {
59934
+ path: peerOpsCfgPath,
59935
+ server: peerOpsServerScriptPath
59936
+ });
59937
+ } else {
59938
+ logger.warn(
59939
+ "peer-ops-mcp-server.cjs not found; cross-device exec MCP tool disabled (dev tsx \u6A21\u5F0F\u4E0B\u9700\u5148\u8DD1\u8FC7 build)",
59940
+ { tried: peerOpsServerCandidates }
59941
+ );
59942
+ }
59693
59943
  const shiftStore = createShiftStore({
59694
- filePath: import_node_path63.default.join(config.dataDir, "shift.json"),
59944
+ filePath: import_node_path64.default.join(config.dataDir, "shift.json"),
59695
59945
  ownerIdProvider: () => ownerPrincipalId,
59696
59946
  now: () => Date.now()
59697
59947
  });
@@ -59713,7 +59963,7 @@ async function startDaemon(config) {
59713
59963
  getAdapter,
59714
59964
  historyReader: history,
59715
59965
  dataDir: config.dataDir,
59716
- personaRoot: import_node_path63.default.join(config.dataDir, "personas"),
59966
+ personaRoot: import_node_path64.default.join(config.dataDir, "personas"),
59717
59967
  usersRoot,
59718
59968
  personaStore,
59719
59969
  ownerDisplayName,
@@ -59751,10 +60001,10 @@ async function startDaemon(config) {
59751
60001
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
59752
60002
  attachmentGroup: {
59753
60003
  onFileEdit: (input) => {
59754
- const absPath = import_node_path63.default.isAbsolute(input.relPath) ? input.relPath : import_node_path63.default.join(input.cwd, input.relPath);
60004
+ const absPath = import_node_path64.default.isAbsolute(input.relPath) ? input.relPath : import_node_path64.default.join(input.cwd, input.relPath);
59755
60005
  let size = 0;
59756
60006
  try {
59757
- size = import_node_fs51.default.statSync(absPath).size;
60007
+ size = import_node_fs52.default.statSync(absPath).size;
59758
60008
  } catch (err) {
59759
60009
  logger.warn("attachment.onFileEdit stat failed", {
59760
60010
  sessionId: input.sessionId,
@@ -59991,11 +60241,11 @@ async function startDaemon(config) {
59991
60241
  // 'persona/<pid>/owner',default 走 'default'。
59992
60242
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
59993
60243
  // guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
59994
- personaRoot: import_node_path63.default.join(config.dataDir, "personas"),
60244
+ personaRoot: import_node_path64.default.join(config.dataDir, "personas"),
59995
60245
  usersRoot
59996
60246
  },
59997
60247
  // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
59998
- personaRoot: import_node_path63.default.join(config.dataDir, "personas"),
60248
+ personaRoot: import_node_path64.default.join(config.dataDir, "personas"),
59999
60249
  // v2 多人 persona 隔离:handler 派生 guest user-dir 放行
60000
60250
  usersRoot,
60001
60251
  // capability:list / delete handler 依赖
@@ -60024,7 +60274,7 @@ async function startDaemon(config) {
60024
60274
  contactStore,
60025
60275
  // <dataDir>/sshd 绝对路径 —— contact-ssh handlers 用它拼 authorized_keys / keys/ 子路径
60026
60276
  // Task 10 会加 SshdManager 起 sshd;handlers wire 提前挂 sshdDir 让 typecheck 过
60027
- sshdDir: import_node_path63.default.join(config.dataDir, "sshd"),
60277
+ sshdDir: import_node_path64.default.join(config.dataDir, "sshd"),
60028
60278
  contactSshLog: sshLog,
60029
60279
  // inbox:sendDm 用:sessionId → session 出身(复用 attachment 同款 findOwnedSessionScope)
60030
60280
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
@@ -60098,7 +60348,7 @@ async function startDaemon(config) {
60098
60348
  selfSshPublicKey: () => readSshIdentityPublicKey(config.dataDir),
60099
60349
  // 建联交换:出站自报本机 OS 账号名。见 doc/glossary/peer-mesh.md#建联交换清单
60100
60350
  selfOsUser: currentOsUser,
60101
- rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore, import_node_path63.default.join(config.dataDir, "sshd"))
60351
+ rebuildAuthorizedKeys: () => rebuildAuthorizedKeys(contactStore, import_node_path64.default.join(config.dataDir, "sshd"))
60102
60352
  },
60103
60353
  // app-builder
60104
60354
  appBuilderStore,
@@ -60121,11 +60371,11 @@ async function startDaemon(config) {
60121
60371
  // 发布上线脚手架化 (spec 2026-06-03 §5.2):
60122
60372
  // appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
60123
60373
  // dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
60124
- appBuilderPersonaRoot: import_node_path63.default.join(config.dataDir, "personas", "persona-app-builder"),
60374
+ appBuilderPersonaRoot: import_node_path64.default.join(config.dataDir, "personas", "persona-app-builder"),
60125
60375
  // 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
60126
- deployKitRoot: import_node_path63.default.join(config.dataDir, "deploy-kit"),
60376
+ deployKitRoot: import_node_path64.default.join(config.dataDir, "deploy-kit"),
60127
60377
  // scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
60128
- resolvePersonaRoot: (personaId) => import_node_path63.default.join(config.dataDir, "personas", personaId),
60378
+ resolvePersonaRoot: (personaId) => import_node_path64.default.join(config.dataDir, "personas", personaId),
60129
60379
  // 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
60130
60380
  // 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
60131
60381
  // 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
@@ -60168,8 +60418,8 @@ async function startDaemon(config) {
60168
60418
  }
60169
60419
  let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
60170
60420
  if (sourceFile && sourceFile.toolSessionId) {
60171
- sourceJsonlPath = import_node_path63.default.join(
60172
- import_node_os21.default.homedir(),
60421
+ sourceJsonlPath = import_node_path64.default.join(
60422
+ import_node_os22.default.homedir(),
60173
60423
  ".claude",
60174
60424
  "projects",
60175
60425
  cwdToHashDir(sourceFile.cwd),
@@ -60528,8 +60778,8 @@ async function startDaemon(config) {
60528
60778
  const lines = [
60529
60779
  `Tunnel: ${r.url}`,
60530
60780
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
60531
- `Frpc config: ${import_node_path63.default.join(config.dataDir, "frpc.toml")}`,
60532
- `Frpc log: ${import_node_path63.default.join(logDir, "frpc.log")}`
60781
+ `Frpc config: ${import_node_path64.default.join(config.dataDir, "frpc.toml")}`,
60782
+ `Frpc log: ${import_node_path64.default.join(logDir, "frpc.log")}`
60533
60783
  ];
60534
60784
  const width = Math.max(...lines.map((l) => l.length));
60535
60785
  const bar = "\u2550".repeat(width + 4);
@@ -60542,8 +60792,8 @@ ${bar}
60542
60792
 
60543
60793
  `);
60544
60794
  try {
60545
- const connectPath = import_node_path63.default.join(config.dataDir, "connect.txt");
60546
- import_node_fs51.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
60795
+ const connectPath = import_node_path64.default.join(config.dataDir, "connect.txt");
60796
+ import_node_fs52.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
60547
60797
  } catch {
60548
60798
  }
60549
60799
  } catch (err) {
@@ -60579,7 +60829,7 @@ ${bar}
60579
60829
  });
60580
60830
  try {
60581
60831
  await sshdMgr.start();
60582
- rebuildAuthorizedKeys(contactStore, import_node_path63.default.join(config.dataDir, "sshd"));
60832
+ rebuildAuthorizedKeys(contactStore, import_node_path64.default.join(config.dataDir, "sshd"));
60583
60833
  logger.info("sshd: contact-ssh sandbox ready", { port: config.sshdPort });
60584
60834
  } catch (err) {
60585
60835
  logger.warn("sshd start failed; contact SSH grant will not work until fixed", {
@@ -60638,9 +60888,9 @@ ${bar}
60638
60888
  };
60639
60889
  }
60640
60890
  function migrateDropPersonsDir(dataDir) {
60641
- const dir = import_node_path63.default.join(dataDir, "persons");
60891
+ const dir = import_node_path64.default.join(dataDir, "persons");
60642
60892
  try {
60643
- import_node_fs51.default.rmSync(dir, { recursive: true, force: true });
60893
+ import_node_fs52.default.rmSync(dir, { recursive: true, force: true });
60644
60894
  } catch {
60645
60895
  }
60646
60896
  }