@clawos-dev/clawd 0.2.250 → 0.2.252

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
@@ -148,12 +148,6 @@ var init_methods = __esm({
148
148
  "attachment.groupAdd",
149
149
  "attachment.groupRemove",
150
150
  "attachment.groupList",
151
- // ---- capability:* (capability platform 鉴权底座) ----
152
- // 飞书统一身份 Phase 2 (2026-06-01, spec 决策 #12): personal token / 邀请码全链路删除——
153
- // device:connect 已完整替代(中心 server 签发自包含 connect token)。capability:list / capability:delete
154
- // 保留:调试 + whoami 反查 + 撤销级联 + 清旧 per-peer cap。personal-cap:get 已下线。
155
- "capability:list",
156
- "capability:delete",
157
151
  // ---- inbox:* (capability platform Phase 3 跨用户通知 + Phase 4 DM) ----
158
152
  // owner 接 guest 的 cross-principal 消息事件 + DM 双向私聊.
159
153
  // inbox:list / markRead: admin-only (Phase 3); inbox:postMessage: DM 自带能力,
@@ -751,8 +745,8 @@ var init_parseUtil = __esm({
751
745
  init_errors2();
752
746
  init_en();
753
747
  makeIssue = (params) => {
754
- const { data, path: path71, errorMaps, issueData } = params;
755
- const fullPath = [...path71, ...issueData.path || []];
748
+ const { data, path: path74, errorMaps, issueData } = params;
749
+ const fullPath = [...path74, ...issueData.path || []];
756
750
  const fullIssue = {
757
751
  ...issueData,
758
752
  path: fullPath
@@ -1063,11 +1057,11 @@ var init_types = __esm({
1063
1057
  init_parseUtil();
1064
1058
  init_util();
1065
1059
  ParseInputLazyPath = class {
1066
- constructor(parent, value, path71, key) {
1060
+ constructor(parent, value, path74, key) {
1067
1061
  this._cachedPath = [];
1068
1062
  this.parent = parent;
1069
1063
  this.data = value;
1070
- this._path = path71;
1064
+ this._path = path74;
1071
1065
  this._key = key;
1072
1066
  }
1073
1067
  get path() {
@@ -5643,11 +5637,7 @@ var init_persona_mode = __esm({
5643
5637
  });
5644
5638
 
5645
5639
  // ../protocol/src/capability.ts
5646
- function stripSecretHash(cap) {
5647
- const { secretHash: _hash, ...wire } = cap;
5648
- return wire;
5649
- }
5650
- var ResourceSchema, ActionSchema, GrantSchema, CapabilitySchema, CapabilityWireSchema, CapabilityErrorCodeSchema, WhoamiResponseSchema, PERSONAL_CAP_GRANTS;
5640
+ var ResourceSchema, ActionSchema, GrantSchema, CapabilitySchema, CapabilityWireSchema, WhoamiResponseSchema, PERSONAL_CAP_GRANTS;
5651
5641
  var init_capability = __esm({
5652
5642
  "../protocol/src/capability.ts"() {
5653
5643
  "use strict";
@@ -5688,12 +5678,6 @@ var init_capability = __esm({
5688
5678
  peerDisplayName: external_exports.string().optional()
5689
5679
  }).strict();
5690
5680
  CapabilityWireSchema = CapabilitySchema.omit({ secretHash: true });
5691
- CapabilityErrorCodeSchema = external_exports.enum([
5692
- "TOKEN_INVALID",
5693
- "TOKEN_REVOKED",
5694
- "TOKEN_EXPIRED",
5695
- "TOKEN_EXHAUSTED"
5696
- ]);
5697
5681
  WhoamiResponseSchema = external_exports.object({
5698
5682
  type: external_exports.literal("whoami:ok"),
5699
5683
  owner: external_exports.object({
@@ -6099,9 +6083,6 @@ var init_feishu_auth = __esm({
6099
6083
  "use strict";
6100
6084
  init_zod();
6101
6085
  FEISHU_GATED_METHODS = [
6102
- // capability(调试 / 撤销,仍属远程身份体系)
6103
- "capability:list",
6104
- "capability:delete",
6105
6086
  // 联系人列表(device:connect 落同一 store;list 读 / remove 删)
6106
6087
  "contact:list",
6107
6088
  "contact:remove",
@@ -6615,8 +6596,8 @@ var require_req = __commonJS({
6615
6596
  if (req.originalUrl) {
6616
6597
  _req.url = req.originalUrl;
6617
6598
  } else {
6618
- const path71 = req.path;
6619
- _req.url = typeof path71 === "string" ? path71 : req.url ? req.url.path || req.url : void 0;
6599
+ const path74 = req.path;
6600
+ _req.url = typeof path74 === "string" ? path74 : req.url ? req.url.path || req.url : void 0;
6620
6601
  }
6621
6602
  if (req.query) {
6622
6603
  _req.query = req.query;
@@ -6781,14 +6762,14 @@ var require_redact = __commonJS({
6781
6762
  }
6782
6763
  return obj;
6783
6764
  }
6784
- function parsePath(path71) {
6765
+ function parsePath(path74) {
6785
6766
  const parts = [];
6786
6767
  let current = "";
6787
6768
  let inBrackets = false;
6788
6769
  let inQuotes = false;
6789
6770
  let quoteChar = "";
6790
- for (let i = 0; i < path71.length; i++) {
6791
- const char = path71[i];
6771
+ for (let i = 0; i < path74.length; i++) {
6772
+ const char = path74[i];
6792
6773
  if (!inBrackets && char === ".") {
6793
6774
  if (current) {
6794
6775
  parts.push(current);
@@ -6919,10 +6900,10 @@ var require_redact = __commonJS({
6919
6900
  return current;
6920
6901
  }
6921
6902
  function redactPaths(obj, paths, censor, remove = false) {
6922
- for (const path71 of paths) {
6923
- const parts = parsePath(path71);
6903
+ for (const path74 of paths) {
6904
+ const parts = parsePath(path74);
6924
6905
  if (parts.includes("*")) {
6925
- redactWildcardPath(obj, parts, censor, path71, remove);
6906
+ redactWildcardPath(obj, parts, censor, path74, remove);
6926
6907
  } else {
6927
6908
  if (remove) {
6928
6909
  removeKey(obj, parts);
@@ -7007,8 +6988,8 @@ var require_redact = __commonJS({
7007
6988
  }
7008
6989
  } else {
7009
6990
  if (afterWildcard.includes("*")) {
7010
- const wrappedCensor = typeof censor === "function" ? (value, path71) => {
7011
- const fullPath = [...pathArray.slice(0, pathLength), ...path71];
6991
+ const wrappedCensor = typeof censor === "function" ? (value, path74) => {
6992
+ const fullPath = [...pathArray.slice(0, pathLength), ...path74];
7012
6993
  return censor(value, fullPath);
7013
6994
  } : censor;
7014
6995
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -7043,8 +7024,8 @@ var require_redact = __commonJS({
7043
7024
  return null;
7044
7025
  }
7045
7026
  const pathStructure = /* @__PURE__ */ new Map();
7046
- for (const path71 of pathsToClone) {
7047
- const parts = parsePath(path71);
7027
+ for (const path74 of pathsToClone) {
7028
+ const parts = parsePath(path74);
7048
7029
  let current = pathStructure;
7049
7030
  for (let i = 0; i < parts.length; i++) {
7050
7031
  const part = parts[i];
@@ -7096,24 +7077,24 @@ var require_redact = __commonJS({
7096
7077
  }
7097
7078
  return cloneSelectively(obj, pathStructure);
7098
7079
  }
7099
- function validatePath(path71) {
7100
- if (typeof path71 !== "string") {
7080
+ function validatePath(path74) {
7081
+ if (typeof path74 !== "string") {
7101
7082
  throw new Error("Paths must be (non-empty) strings");
7102
7083
  }
7103
- if (path71 === "") {
7084
+ if (path74 === "") {
7104
7085
  throw new Error("Invalid redaction path ()");
7105
7086
  }
7106
- if (path71.includes("..")) {
7107
- throw new Error(`Invalid redaction path (${path71})`);
7087
+ if (path74.includes("..")) {
7088
+ throw new Error(`Invalid redaction path (${path74})`);
7108
7089
  }
7109
- if (path71.includes(",")) {
7110
- throw new Error(`Invalid redaction path (${path71})`);
7090
+ if (path74.includes(",")) {
7091
+ throw new Error(`Invalid redaction path (${path74})`);
7111
7092
  }
7112
7093
  let bracketCount = 0;
7113
7094
  let inQuotes = false;
7114
7095
  let quoteChar = "";
7115
- for (let i = 0; i < path71.length; i++) {
7116
- const char = path71[i];
7096
+ for (let i = 0; i < path74.length; i++) {
7097
+ const char = path74[i];
7117
7098
  if ((char === '"' || char === "'") && bracketCount > 0) {
7118
7099
  if (!inQuotes) {
7119
7100
  inQuotes = true;
@@ -7127,20 +7108,20 @@ var require_redact = __commonJS({
7127
7108
  } else if (char === "]" && !inQuotes) {
7128
7109
  bracketCount--;
7129
7110
  if (bracketCount < 0) {
7130
- throw new Error(`Invalid redaction path (${path71})`);
7111
+ throw new Error(`Invalid redaction path (${path74})`);
7131
7112
  }
7132
7113
  }
7133
7114
  }
7134
7115
  if (bracketCount !== 0) {
7135
- throw new Error(`Invalid redaction path (${path71})`);
7116
+ throw new Error(`Invalid redaction path (${path74})`);
7136
7117
  }
7137
7118
  }
7138
7119
  function validatePaths(paths) {
7139
7120
  if (!Array.isArray(paths)) {
7140
7121
  throw new TypeError("paths must be an array");
7141
7122
  }
7142
- for (const path71 of paths) {
7143
- validatePath(path71);
7123
+ for (const path74 of paths) {
7124
+ validatePath(path74);
7144
7125
  }
7145
7126
  }
7146
7127
  function slowRedact(options = {}) {
@@ -7308,8 +7289,8 @@ var require_redaction = __commonJS({
7308
7289
  if (shape[k2] === null) {
7309
7290
  o[k2] = (value) => topCensor(value, [k2]);
7310
7291
  } else {
7311
- const wrappedCensor = typeof censor === "function" ? (value, path71) => {
7312
- return censor(value, [k2, ...path71]);
7292
+ const wrappedCensor = typeof censor === "function" ? (value, path74) => {
7293
+ return censor(value, [k2, ...path74]);
7313
7294
  } : censor;
7314
7295
  o[k2] = Redact({
7315
7296
  paths: shape[k2],
@@ -7530,7 +7511,7 @@ var require_sonic_boom = __commonJS({
7530
7511
  var fs65 = require("fs");
7531
7512
  var EventEmitter3 = require("events");
7532
7513
  var inherits = require("util").inherits;
7533
- var path71 = require("path");
7514
+ var path74 = require("path");
7534
7515
  var sleep2 = require_atomic_sleep();
7535
7516
  var assert = require("assert");
7536
7517
  var BUSY_WRITE_TIMEOUT = 100;
@@ -7584,7 +7565,7 @@ var require_sonic_boom = __commonJS({
7584
7565
  const mode = sonic.mode;
7585
7566
  if (sonic.sync) {
7586
7567
  try {
7587
- if (sonic.mkdir) fs65.mkdirSync(path71.dirname(file), { recursive: true });
7568
+ if (sonic.mkdir) fs65.mkdirSync(path74.dirname(file), { recursive: true });
7588
7569
  const fd = fs65.openSync(file, flags, mode);
7589
7570
  fileOpened(null, fd);
7590
7571
  } catch (err) {
@@ -7592,7 +7573,7 @@ var require_sonic_boom = __commonJS({
7592
7573
  throw err;
7593
7574
  }
7594
7575
  } else if (sonic.mkdir) {
7595
- fs65.mkdir(path71.dirname(file), { recursive: true }, (err) => {
7576
+ fs65.mkdir(path74.dirname(file), { recursive: true }, (err) => {
7596
7577
  if (err) return fileOpened(err);
7597
7578
  fs65.open(file, flags, mode, fileOpened);
7598
7579
  });
@@ -11223,11 +11204,11 @@ var init_lib = __esm({
11223
11204
  }
11224
11205
  }
11225
11206
  },
11226
- addToPath: function addToPath(path71, added, removed, oldPosInc, options) {
11227
- var last = path71.lastComponent;
11207
+ addToPath: function addToPath(path74, added, removed, oldPosInc, options) {
11208
+ var last = path74.lastComponent;
11228
11209
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
11229
11210
  return {
11230
- oldPos: path71.oldPos + oldPosInc,
11211
+ oldPos: path74.oldPos + oldPosInc,
11231
11212
  lastComponent: {
11232
11213
  count: last.count + 1,
11233
11214
  added,
@@ -11237,7 +11218,7 @@ var init_lib = __esm({
11237
11218
  };
11238
11219
  } else {
11239
11220
  return {
11240
- oldPos: path71.oldPos + oldPosInc,
11221
+ oldPos: path74.oldPos + oldPosInc,
11241
11222
  lastComponent: {
11242
11223
  count: 1,
11243
11224
  added,
@@ -11746,10 +11727,10 @@ function attachmentToHistoryMessage(o, ts) {
11746
11727
  const memories = raw.map((m2) => {
11747
11728
  if (!m2 || typeof m2 !== "object") return null;
11748
11729
  const rec3 = m2;
11749
- const path71 = typeof rec3.path === "string" ? rec3.path : null;
11730
+ const path74 = typeof rec3.path === "string" ? rec3.path : null;
11750
11731
  const content = typeof rec3.content === "string" ? rec3.content : null;
11751
- if (!path71 || content == null) return null;
11752
- const entry = { path: path71, content };
11732
+ if (!path74 || content == null) return null;
11733
+ const entry = { path: path74, content };
11753
11734
  if (typeof rec3.mtimeMs === "number") entry.mtimeMs = rec3.mtimeMs;
11754
11735
  return entry;
11755
11736
  }).filter((m2) => m2 !== null);
@@ -12552,10 +12533,10 @@ function parseAttachment(obj) {
12552
12533
  const memories = raw.map((m2) => {
12553
12534
  if (!m2 || typeof m2 !== "object") return null;
12554
12535
  const rec3 = m2;
12555
- const path71 = typeof rec3.path === "string" ? rec3.path : null;
12536
+ const path74 = typeof rec3.path === "string" ? rec3.path : null;
12556
12537
  const content = typeof rec3.content === "string" ? rec3.content : null;
12557
- if (!path71 || content == null) return null;
12558
- const out = { path: path71, content };
12538
+ if (!path74 || content == null) return null;
12539
+ const out = { path: path74, content };
12559
12540
  if (typeof rec3.mtimeMs === "number") out.mtimeMs = rec3.mtimeMs;
12560
12541
  return out;
12561
12542
  }).filter((m2) => m2 !== null);
@@ -26932,8 +26913,8 @@ var require_CronFileParser = __commonJS({
26932
26913
  * @throws If file cannot be read
26933
26914
  */
26934
26915
  static parseFileSync(filePath) {
26935
- const { readFileSync: readFileSync7 } = require("fs");
26936
- const data = readFileSync7(filePath, "utf8");
26916
+ const { readFileSync: readFileSync6 } = require("fs");
26917
+ const data = readFileSync6(filePath, "utf8");
26937
26918
  return _CronFileParser.#parseContent(data);
26938
26919
  }
26939
26920
  /**
@@ -29234,7 +29215,7 @@ var require_websocket = __commonJS({
29234
29215
  var http3 = require("http");
29235
29216
  var net3 = require("net");
29236
29217
  var tls = require("tls");
29237
- var { randomBytes, createHash: createHash3 } = require("crypto");
29218
+ var { randomBytes, createHash: createHash2 } = require("crypto");
29238
29219
  var { Duplex, Readable: Readable3 } = require("stream");
29239
29220
  var { URL: URL2 } = require("url");
29240
29221
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -29894,7 +29875,7 @@ var require_websocket = __commonJS({
29894
29875
  abortHandshake(websocket, socket, "Invalid Upgrade header");
29895
29876
  return;
29896
29877
  }
29897
- const digest = createHash3("sha1").update(key + GUID).digest("base64");
29878
+ const digest = createHash2("sha1").update(key + GUID).digest("base64");
29898
29879
  if (res.headers["sec-websocket-accept"] !== digest) {
29899
29880
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
29900
29881
  return;
@@ -30261,7 +30242,7 @@ var require_websocket_server = __commonJS({
30261
30242
  var EventEmitter3 = require("events");
30262
30243
  var http3 = require("http");
30263
30244
  var { Duplex } = require("stream");
30264
- var { createHash: createHash3 } = require("crypto");
30245
+ var { createHash: createHash2 } = require("crypto");
30265
30246
  var extension2 = require_extension();
30266
30247
  var PerMessageDeflate2 = require_permessage_deflate();
30267
30248
  var subprotocol2 = require_subprotocol();
@@ -30562,7 +30543,7 @@ var require_websocket_server = __commonJS({
30562
30543
  );
30563
30544
  }
30564
30545
  if (this._state > RUNNING) return abortHandshake(socket, 503);
30565
- const digest = createHash3("sha1").update(key + GUID).digest("base64");
30546
+ const digest = createHash2("sha1").update(key + GUID).digest("base64");
30566
30547
  const headers = [
30567
30548
  "HTTP/1.1 101 Switching Protocols",
30568
30549
  "Upgrade: websocket",
@@ -33524,8 +33505,8 @@ var require_utils = __commonJS({
33524
33505
  var result = transform[inputType][outputType](input);
33525
33506
  return result;
33526
33507
  };
33527
- exports2.resolve = function(path71) {
33528
- var parts = path71.split("/");
33508
+ exports2.resolve = function(path74) {
33509
+ var parts = path74.split("/");
33529
33510
  var result = [];
33530
33511
  for (var index = 0; index < parts.length; index++) {
33531
33512
  var part = parts[index];
@@ -39378,18 +39359,18 @@ var require_object = __commonJS({
39378
39359
  var object = new ZipObject(name, zipObjectContent, o);
39379
39360
  this.files[name] = object;
39380
39361
  };
39381
- var parentFolder = function(path71) {
39382
- if (path71.slice(-1) === "/") {
39383
- path71 = path71.substring(0, path71.length - 1);
39362
+ var parentFolder = function(path74) {
39363
+ if (path74.slice(-1) === "/") {
39364
+ path74 = path74.substring(0, path74.length - 1);
39384
39365
  }
39385
- var lastSlash = path71.lastIndexOf("/");
39386
- return lastSlash > 0 ? path71.substring(0, lastSlash) : "";
39366
+ var lastSlash = path74.lastIndexOf("/");
39367
+ return lastSlash > 0 ? path74.substring(0, lastSlash) : "";
39387
39368
  };
39388
- var forceTrailingSlash = function(path71) {
39389
- if (path71.slice(-1) !== "/") {
39390
- path71 += "/";
39369
+ var forceTrailingSlash = function(path74) {
39370
+ if (path74.slice(-1) !== "/") {
39371
+ path74 += "/";
39391
39372
  }
39392
- return path71;
39373
+ return path74;
39393
39374
  };
39394
39375
  var folderAdd = function(name, createFolders) {
39395
39376
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
@@ -40391,7 +40372,7 @@ var require_lib3 = __commonJS({
40391
40372
  // src/run-case/recorder.ts
40392
40373
  function startRunCaseRecorder(opts) {
40393
40374
  const now = opts.now ?? Date.now;
40394
- const dir = import_node_path59.default.dirname(opts.recordPath);
40375
+ const dir = import_node_path62.default.dirname(opts.recordPath);
40395
40376
  let stream = null;
40396
40377
  let closing = false;
40397
40378
  let closedSettled = false;
@@ -40405,8 +40386,8 @@ function startRunCaseRecorder(opts) {
40405
40386
  });
40406
40387
  const ensureStream = () => {
40407
40388
  if (stream) return stream;
40408
- import_node_fs46.default.mkdirSync(dir, { recursive: true });
40409
- stream = import_node_fs46.default.createWriteStream(opts.recordPath, { flags: "a" });
40389
+ import_node_fs47.default.mkdirSync(dir, { recursive: true });
40390
+ stream = import_node_fs47.default.createWriteStream(opts.recordPath, { flags: "a" });
40410
40391
  stream.on("close", () => closedResolve());
40411
40392
  return stream;
40412
40393
  };
@@ -40431,12 +40412,12 @@ function startRunCaseRecorder(opts) {
40431
40412
  };
40432
40413
  return { tap, close, closed };
40433
40414
  }
40434
- var import_node_fs46, import_node_path59;
40415
+ var import_node_fs47, import_node_path62;
40435
40416
  var init_recorder = __esm({
40436
40417
  "src/run-case/recorder.ts"() {
40437
40418
  "use strict";
40438
- import_node_fs46 = __toESM(require("fs"), 1);
40439
- import_node_path59 = __toESM(require("path"), 1);
40419
+ import_node_fs47 = __toESM(require("fs"), 1);
40420
+ import_node_path62 = __toESM(require("path"), 1);
40440
40421
  }
40441
40422
  });
40442
40423
 
@@ -40479,7 +40460,7 @@ var init_wire = __esm({
40479
40460
  // src/run-case/controller.ts
40480
40461
  async function runController(opts) {
40481
40462
  const now = opts.now ?? Date.now;
40482
- const cwd = opts.cwd ?? (0, import_node_fs47.mkdtempSync)(import_node_path60.default.join(import_node_os23.default.tmpdir(), "clawd-runcase-"));
40463
+ const cwd = opts.cwd ?? (0, import_node_fs48.mkdtempSync)(import_node_path63.default.join(import_node_os23.default.tmpdir(), "clawd-runcase-"));
40483
40464
  const ownsCwd = opts.cwd === void 0;
40484
40465
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
40485
40466
  const spawnCtx = { cwd };
@@ -40640,19 +40621,19 @@ async function runController(opts) {
40640
40621
  if (sigintHandler) process.off("SIGINT", sigintHandler);
40641
40622
  if (ownsCwd) {
40642
40623
  try {
40643
- (0, import_node_fs47.rmSync)(cwd, { recursive: true, force: true });
40624
+ (0, import_node_fs48.rmSync)(cwd, { recursive: true, force: true });
40644
40625
  } catch {
40645
40626
  }
40646
40627
  }
40647
40628
  return exitCode ?? 0;
40648
40629
  }
40649
- var import_node_fs47, import_node_os23, import_node_path60;
40630
+ var import_node_fs48, import_node_os23, import_node_path63;
40650
40631
  var init_controller = __esm({
40651
40632
  "src/run-case/controller.ts"() {
40652
40633
  "use strict";
40653
- import_node_fs47 = require("fs");
40634
+ import_node_fs48 = require("fs");
40654
40635
  import_node_os23 = __toESM(require("os"), 1);
40655
- import_node_path60 = __toESM(require("path"), 1);
40636
+ import_node_path63 = __toESM(require("path"), 1);
40656
40637
  init_claude();
40657
40638
  init_stdout_splitter();
40658
40639
  init_permission_stdio();
@@ -40914,8 +40895,8 @@ Env (advanced):
40914
40895
  `;
40915
40896
 
40916
40897
  // src/index.ts
40917
- var import_node_path58 = __toESM(require("path"), 1);
40918
- var import_node_fs45 = __toESM(require("fs"), 1);
40898
+ var import_node_path61 = __toESM(require("path"), 1);
40899
+ var import_node_fs46 = __toESM(require("fs"), 1);
40919
40900
  var import_node_os22 = __toESM(require("os"), 1);
40920
40901
 
40921
40902
  // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
@@ -41918,17 +41899,17 @@ var ATTACHMENT_SHARING_HINT = `## \u628A\u4EA7\u51FA\u6587\u4EF6\u5206\u4EAB\u7E
41918
41899
 
41919
41900
  - \`relPath\` \u5FC5\u987B\u4F20**\u7EDD\u5BF9\u8DEF\u5F84**\uFF08\u4F60\u4EA7\u51FA\u6587\u4EF6\u7684\u771F\u5B9E\u8DEF\u5F84\uFF09\uFF0C\u4E0D\u8981\u4F20\u76F8\u5BF9\u8DEF\u5F84\u3002
41920
41901
  - \u53EA\u80FD\u5206\u4EAB\u4F60\u81EA\u5DF1\u5DE5\u4F5C\u76EE\u5F55\u5185\u7684\u6587\u4EF6\uFF0C\u8D8A\u754C\u4F1A\u88AB\u62D2\u3002
41921
- - \u88AB dispatch \u63A5\u5230\u4EFB\u52A1\u3001\u8981\u56DE\u4F20\u4EA7\u7269\u65F6\uFF1A\u628A\u7B7E\u540D URL \u5199\u8FDB \`personaDispatchComplete\` \u7684 \`text\` \u91CC\uFF08\`filePaths\` \u53EA\u662F\u4F60\u672C\u673A\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u8DE8\u8BBE\u5907\u5BF9\u65B9\u6253\u4E0D\u5F00\uFF0CURL \u624D\u80FD\u6253\u5F00\uFF09\u3002`;
41902
+ - \u88AB dispatch \u63A5\u5230\u4EFB\u52A1\u3001\u8981\u56DE\u4F20\u4EA7\u7269\u65F6\uFF1A\u628A\u7B7E\u540D URL \u5199\u8FDB \`personaDispatchComplete\` tool\uFF08\`clawd-dispatch\` MCP\uFF09\u7684 \`text\` \u91CC\uFF08\`filePaths\` \u53EA\u662F\u4F60\u672C\u673A\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u8DE8\u8BBE\u5907\u5BF9\u65B9\u6253\u4E0D\u5F00\uFF0CURL \u624D\u80FD\u6253\u5F00\uFF09\u3002`;
41922
41903
 
41923
41904
  // src/dispatch/system-prompt.ts
41924
41905
  var DISPATCH_SYSTEM_PROMPT_HINT = `## \u59D4\u6D3E\u7ED9\u53E6\u4E00\u4E2A persona\uFF08dispatch\uFF09
41925
41906
 
41926
- \u5F53\u7528\u6237\u6D88\u606F\u91CC\u542B \`@persona/<id>\`\uFF08\u5982 \`@persona/persona-app-builder\`\uFF09\u65F6\uFF0C**\u4F60\u5FC5\u987B**\u7ACB\u5373\u8C03 MCP tool \`mcp__clawd-dispatch__personaDispatch\` \u59D4\u6D3E\uFF0C\u4E0D\u8981\u5C1D\u8BD5\u81EA\u5DF1\u6267\u884C\uFF1A
41907
+ \u5F53\u7528\u6237\u6D88\u606F\u91CC\u542B \`@persona/<id>\`\uFF08\u5982 \`@persona/persona-app-builder\`\uFF09\u65F6\uFF0C**\u4F60\u5FC5\u987B**\u7ACB\u5373\u8C03 \`clawd-dispatch\` MCP server \u7684 \`personaDispatch\` tool \u59D4\u6D3E\uFF08\u5404 agent \u5BF9 MCP tool \u7684\u5C55\u793A\u540D\u4E0D\u540C\uFF0C\u5982 claude \u663E\u793A\u4E3A \`mcp__clawd-dispatch__personaDispatch\`\uFF1B\u6309\u4F60\u73AF\u5883\u91CC\u5B9E\u9645\u5217\u51FA\u7684\u540D\u5B57\u8C03\u7528\uFF09\uFF0C\u4E0D\u8981\u5C1D\u8BD5\u81EA\u5DF1\u6267\u884C\uFF1A
41927
41908
 
41928
41909
  - \`targetPersona = <id>\`\uFF08@ token \u91CC\u7684 persona id\uFF09
41929
41910
  - \`prompt = \u53BB\u6389 @persona/<id> token \u7684\u7528\u6237\u539F\u6587\`
41930
41911
 
41931
- tool \u4F1A**\u7ACB\u5373\u8FD4\u56DE** dispatchId\uFF08\u5F02\u6B65\u59D4\u6D3E\uFF09\uFF0C\u5BF9\u65B9\u4EBA\u683C\u5728\u540E\u53F0\u5E72\u6D3B\u3002\u4F60\u544A\u8BC9\u7528\u6237"\u4EFB\u52A1\u5DF2\u4EA4\u529E"\u5373\u53EF\u7ED3\u675F\u672C\u8F6E\u2014\u2014**\u4E0D\u8981\u7B49\u5F85\u3001\u4E0D\u8981\u8F6E\u8BE2**\u3002\u5BF9\u65B9\u5E72\u5B8C\u540E\uFF0C\u7ED3\u679C\u4F1A\u4F5C\u4E3A\u4E00\u6761\u65B0\u6D88\u606F\u81EA\u52A8\u6CE8\u5165\u56DE\u4F60\u7684 session\uFF0C\u4F60\u5C4A\u65F6\u518D\u7EFC\u5408\u8F6C\u8FBE\u7ED9\u7528\u6237\u3002\u60F3\u67E5\u8FDB\u5C55\u65F6\uFF08\u7528\u6237\u95EE\u8D77\uFF09\u8C03 \`mcp__clawd-dispatch__personaDispatchList\`\u3002
41912
+ tool \u4F1A**\u7ACB\u5373\u8FD4\u56DE** dispatchId\uFF08\u5F02\u6B65\u59D4\u6D3E\uFF09\uFF0C\u5BF9\u65B9\u4EBA\u683C\u5728\u540E\u53F0\u5E72\u6D3B\u3002\u4F60\u544A\u8BC9\u7528\u6237"\u4EFB\u52A1\u5DF2\u4EA4\u529E"\u5373\u53EF\u7ED3\u675F\u672C\u8F6E\u2014\u2014**\u4E0D\u8981\u7B49\u5F85\u3001\u4E0D\u8981\u8F6E\u8BE2**\u3002\u5BF9\u65B9\u5E72\u5B8C\u540E\uFF0C\u7ED3\u679C\u4F1A\u4F5C\u4E3A\u4E00\u6761\u65B0\u6D88\u606F\u81EA\u52A8\u6CE8\u5165\u56DE\u4F60\u7684 session\uFF0C\u4F60\u5C4A\u65F6\u518D\u7EFC\u5408\u8F6C\u8FBE\u7ED9\u7528\u6237\u3002\u60F3\u67E5\u8FDB\u5C55\u65F6\uFF08\u7528\u6237\u95EE\u8D77\uFF09\u8C03\u540C\u4E00 MCP \u7684 \`personaDispatchList\`\u3002
41932
41913
 
41933
41914
  - **\u4E0D\u8981**\u5C1D\u8BD5\u81EA\u5DF1\u6267\u884C\uFF08\u4F60\u4E0D\u662F\u90A3\u4E2A persona\uFF09
41934
41915
  - **\u4E0D\u8981** in-place \u6A21\u4EFF\u5BF9\u65B9\u4EBA\u683C \u2014\u2014 dispatch \u662F\u771F\u5207\u5230\u53E6\u4E00\u4E2A\u72EC\u7ACB session
@@ -41953,7 +41934,7 @@ deviceId \u76F4\u63A5\u53D6\u81EA token\uFF0C\u4E0D\u8981\u6539\u5199\u6216\u731
41953
41934
 
41954
41935
  - \u4EFB\u52A1\u5728 \`Task:\` \u6BB5
41955
41936
  - \u539F\u4F1A\u8BDD\u7684\u5BF9\u8BDD\u5386\u53F2\u5728 \`Source conversation\` \u6BB5\u7ED9\u7684 jsonl \u8DEF\u5F84\u4E0B\uFF1B\u6309\u9700 Read \u5B83\u4E86\u89E3\u4E0A\u4E0B\u6587\uFF08\u4E0D\u5FC5\u8BFB\u5B8C\uFF09
41956
- - \u5E72\u5B8C\u540E**\u5FC5\u987B**\u8C03 \`mcp__clawd-dispatch__personaDispatchComplete\` \u62A5\u544A\u7ED3\u679C\uFF1A
41937
+ - \u5E72\u5B8C\u540E**\u5FC5\u987B**\u8C03 \`clawd-dispatch\` MCP \u7684 \`personaDispatchComplete\` tool \u62A5\u544A\u7ED3\u679C\uFF1A
41957
41938
  - \u6210\u529F\uFF1A\`{ text: "\u603B\u7ED3/\u7ED3\u8BBA", filePaths?: ["\u4F60\u4EA7\u51FA/\u6539\u7684\u6587\u4EF6\u7EDD\u5BF9\u8DEF\u5F84"] }\`
41958
41939
  - \u5931\u8D25\uFF1A\`{ isFailure: true, reason: "..." }\`
41959
41940
  - \u62A5\u544A\u5B8C\u540E\u4F60\u7684 session \u5C31\u7ED3\u675F\u4E86\uFF0C\u4E0D\u8981\u7EE7\u7EED\u8F93\u51FA`;
@@ -43353,7 +43334,8 @@ Source conversation (jsonl, CC stream-json format, one message per line):
43353
43334
  ${args.sourceJsonlPath}
43354
43335
  You may read this file with your Read tool to understand context. You do not have to read it all.
43355
43336
 
43356
- When done, call the MCP tool \`mcp__clawd-dispatch__personaDispatchComplete\` with your result:
43337
+ When done, call the \`personaDispatchComplete\` tool from the \`clawd-dispatch\` MCP server with your result
43338
+ (tool display name varies by agent, e.g. \`mcp__clawd-dispatch__personaDispatchComplete\` in claude):
43357
43339
  - Success: { text: "...", filePaths?: ["abs/path", ...] }
43358
43340
  - Failure: { isFailure: true, reason: "..." }
43359
43341
 
@@ -43658,18 +43640,20 @@ var SessionManager = class {
43658
43640
  `newRunner: guest session ${file.sessionId} \u7F3A personaRoot/userWorkDir\uFF0C\u65E0\u6CD5\u7269\u5316 OS \u6C99\u7BB1\uFF08\u62D2\u7EDD\u521B\u5EFA\u65E0\u6C99\u7BB1 guest\uFF09`
43659
43641
  );
43660
43642
  }
43661
- const base = this.deps.personaStore?.readSandboxSettings(scope.personaId) ?? null;
43662
- const sandboxSettings = composeGuestSandbox(base, subSessionMeta.userWorkDir, file.cwd);
43663
- const ownerCreds = readOwnerCredentials(import_node_os5.default.homedir(), this.deps.logger);
43664
- const merged = mergeOwnerCredentials(sandboxSettings, ownerCreds);
43665
- if (ownerCreds) {
43666
- this.deps.logger?.info("owner credentials injected into guest spawn", {
43667
- sessionId: file.sessionId,
43668
- envKeys: ownerCreds.env ? Object.keys(ownerCreds.env) : [],
43669
- apiKeyHelper: ownerCreds.apiKeyHelper ? "present" : "absent"
43670
- });
43643
+ if ((file.tool ?? "claude") === "claude") {
43644
+ const base = this.deps.personaStore?.readSandboxSettings(scope.personaId) ?? null;
43645
+ const sandboxSettings = composeGuestSandbox(base, subSessionMeta.userWorkDir, file.cwd);
43646
+ const ownerCreds = readOwnerCredentials(import_node_os5.default.homedir(), this.deps.logger);
43647
+ const merged = mergeOwnerCredentials(sandboxSettings, ownerCreds);
43648
+ if (ownerCreds) {
43649
+ this.deps.logger?.info("owner credentials injected into guest spawn", {
43650
+ sessionId: file.sessionId,
43651
+ envKeys: ownerCreds.env ? Object.keys(ownerCreds.env) : [],
43652
+ apiKeyHelper: ownerCreds.apiKeyHelper ? "present" : "absent"
43653
+ });
43654
+ }
43655
+ subSessionMeta.extraSettings = JSON.stringify(merged);
43671
43656
  }
43672
- subSessionMeta.extraSettings = JSON.stringify(merged);
43673
43657
  const home = import_node_os5.default.homedir();
43674
43658
  const expand = (p2) => p2 === "~" ? home : p2.startsWith("~/") ? import_node_path9.default.join(home, p2.slice(2)) : p2;
43675
43659
  const codexCfg = this.deps.personaStore?.readCodexSandboxSettings(scope.personaId) ?? null;
@@ -44719,7 +44703,9 @@ var SessionManager = class {
44719
44703
  const file = {
44720
44704
  sessionId,
44721
44705
  cwd,
44722
- tool: "claude",
44706
+ // dispatch B session 跟随 persona 绑定的 tool(codex persona 用 codex spawn),
44707
+ // 与 UI 手建路径(handlers/session.ts create 的 persona.tool 优先)同语义
44708
+ tool: this.deps.getPersonaTool?.(args.targetPersona) ?? "claude",
44723
44709
  ownerPersonaId: args.targetPersona,
44724
44710
  dispatchedFromSessionId: args.sourceSessionId,
44725
44711
  // 跨设备显示关键(spec §8):creatorPrincipalId = A 的 deviceId,让 A 的
@@ -44809,7 +44795,8 @@ var SessionManager = class {
44809
44795
  const file = {
44810
44796
  sessionId,
44811
44797
  cwd,
44812
- tool: "claude",
44798
+ // shift 定时 spawn 同样跟随 persona 绑定 tool(对齐 dispatch B session)
44799
+ tool: this.deps.getPersonaTool?.(args.targetPersona) ?? "claude",
44813
44800
  ownerPersonaId: args.targetPersona,
44814
44801
  ...args.sourceSessionId ? { shiftFiredFromSessionId: args.sourceSessionId } : {},
44815
44802
  createdAt: now,
@@ -45874,17 +45861,9 @@ var PersonaManager = class {
45874
45861
  }
45875
45862
  /**
45876
45863
  * 删除 persona。
45877
- * PersonaStore.remove(personaId) 已级联删除整个 <personaRoot>/<personaId>/ 目录,
45878
- * Phase 2 capability platform 新布局下含 .clawd/sessions/owner/ +
45879
- * .clawd/sessions/guests/<capId>/, rmSync recursive force 一锅清.
45880
- *
45881
- * ⚠️ TODO (Phase 3+ design gap, reviewer P1 flagged):
45882
- * ~/.clawd/capabilities.json 里若有 cap.grants 含本 personaId, grant 会变成
45883
- * 悬空引用 (guest 仍能持 token 接入, 但调依赖该 persona 的 RPC 会 fail).
45884
- * 选项: (A) 这里扫所有 cap 过滤掉该 personaId 的 grant; (B) revoke 所有 grant
45885
- * 含该 personaId 的 cap. 当前因 cleanupGuestSessionsForCapability 用 rmSync
45886
- * force=true 吞 ENOENT 不会 crash, 但 UI CapabilityManagerDrawer 会展示
45887
- * "无效的 persona" grant. Phase 3 加 cross-reference 矩阵后正式实现.
45864
+ * PersonaStore.remove(personaId) 级联删整个 <personaRoot>/<personaId>/ 目录
45865
+ * (含 .clawd/sessions/owner/ 和 .clawd/sessions/guests/<capId>/),rmSync
45866
+ * recursive force 一锅清。
45888
45867
  */
45889
45868
  delete(personaId) {
45890
45869
  this.deps.store.remove(personaId);
@@ -46185,6 +46164,9 @@ function refreshDaemonManagedDirs(args) {
46185
46164
  const dstPath = path14.join(personaDir, relPath);
46186
46165
  try {
46187
46166
  fs12.cpSync(srcPath, dstPath, { recursive: true, force: true, dereference: true, filter: skipNodeModulesUnder(srcPath) });
46167
+ if (relPath === "CLAUDE.md") {
46168
+ fs12.cpSync(srcPath, path14.join(personaDir, "AGENTS.md"), { force: true, dereference: true });
46169
+ }
46188
46170
  args.logger.info("persona.refresh.synced", {
46189
46171
  personaId: entry.personaId,
46190
46172
  path: relPath
@@ -46552,11 +46534,12 @@ function createVisitorStore(opts) {
46552
46534
  init_claude();
46553
46535
 
46554
46536
  // src/tools/codex.ts
46555
- var import_node_child_process4 = require("child_process");
46556
- var import_node_fs17 = __toESM(require("fs"), 1);
46537
+ var import_node_child_process5 = require("child_process");
46538
+ var import_node_fs18 = __toESM(require("fs"), 1);
46557
46539
 
46558
46540
  // src/tools/codex-session.ts
46559
- var import_node_child_process3 = require("child_process");
46541
+ var import_node_child_process4 = require("child_process");
46542
+ var import_node_path16 = __toESM(require("path"), 1);
46560
46543
 
46561
46544
  // src/tools/codex-app-server-client.ts
46562
46545
  var import_node_readline = require("readline");
@@ -46739,7 +46722,7 @@ function resolveApprovalPolicy(ctx) {
46739
46722
  if (m2 === "untrusted" || m2 === "on-failure" || m2 === "on-request" || m2 === "never") return m2;
46740
46723
  return "on-request";
46741
46724
  }
46742
- function buildCodexGuestConfig(cs) {
46725
+ function buildCodexGuestConfig(cs, opts = {}) {
46743
46726
  const filesystem = {};
46744
46727
  for (const w2 of cs.writableRoots ?? []) filesystem[w2] = "write";
46745
46728
  const deny = cs.denyRead ?? [];
@@ -46754,7 +46737,7 @@ function buildCodexGuestConfig(cs) {
46754
46737
  clawos_guest: {
46755
46738
  extends: ":workspace",
46756
46739
  filesystem,
46757
- network: cs.network ? { enabled: true, domains: { "*": "allow" } } : { enabled: false }
46740
+ network: cs.network ? { enabled: true, domains: { "*": "allow" } } : opts.loopbackDaemonAccess ? { enabled: true, allowed_domains: [], allow_local_binding: true } : { enabled: false }
46758
46741
  }
46759
46742
  }
46760
46743
  };
@@ -46768,15 +46751,32 @@ function tomlValue(v2) {
46768
46751
  }
46769
46752
  return JSON.stringify(v2);
46770
46753
  }
46771
- function codexGuestCliArgs(cs) {
46772
- const cfg = buildCodexGuestConfig(cs);
46754
+ function codexGuestCliArgs(cs, opts = {}) {
46755
+ const cfg = buildCodexGuestConfig(cs, opts);
46773
46756
  const profile = cfg.permissions.clawos_guest;
46774
- return [
46757
+ const args = [
46775
46758
  "-c",
46776
46759
  `default_permissions=${tomlValue("clawos_guest")}`,
46777
46760
  "-c",
46778
46761
  `permissions.clawos_guest=${tomlValue(profile)}`
46779
46762
  ];
46763
+ if (!cs.network && opts.loopbackDaemonAccess) {
46764
+ args.push("-c", "features.network_proxy=true");
46765
+ }
46766
+ return args;
46767
+ }
46768
+ function codexMcpCliArgs(defs) {
46769
+ const out = [];
46770
+ for (const d of defs) {
46771
+ const cfg = {
46772
+ command: d.command,
46773
+ ...d.args && d.args.length > 0 ? { args: d.args } : {},
46774
+ ...d.env && Object.keys(d.env).length > 0 ? { env: d.env } : {}
46775
+ };
46776
+ const key = /^[A-Za-z0-9_-]+$/.test(d.name) ? d.name : JSON.stringify(d.name);
46777
+ out.push("-c", `mcp_servers.${key}=${tomlValue(cfg)}`);
46778
+ }
46779
+ return out;
46780
46780
  }
46781
46781
  function threadStartParams(ctx) {
46782
46782
  const params = {
@@ -46801,8 +46801,8 @@ function turnStartInput(text) {
46801
46801
  const items = [];
46802
46802
  let leftover = text;
46803
46803
  for (const m2 of text.matchAll(SKILL_RE)) {
46804
- const [marker, name, path71] = m2;
46805
- items.push({ type: "skill", name, path: path71 });
46804
+ const [marker, name, path74] = m2;
46805
+ items.push({ type: "skill", name, path: path74 });
46806
46806
  leftover = leftover.replace(marker, "");
46807
46807
  }
46808
46808
  for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
@@ -46817,15 +46817,122 @@ function turnStartInput(text) {
46817
46817
  return items;
46818
46818
  }
46819
46819
 
46820
+ // src/tools/codex-mcp-config.ts
46821
+ var import_node_fs17 = __toESM(require("fs"), 1);
46822
+ var import_node_path15 = __toESM(require("path"), 1);
46823
+ function loadCodexMcpServerDefs(args) {
46824
+ const out = [];
46825
+ const seen = /* @__PURE__ */ new Set();
46826
+ const sources = [...args.mcpConfigPaths, import_node_path15.default.join(args.cwd, ".mcp.json")];
46827
+ for (const file of sources) {
46828
+ let parsed;
46829
+ try {
46830
+ parsed = JSON.parse(import_node_fs17.default.readFileSync(file, "utf8"));
46831
+ } catch (e) {
46832
+ const code = e.code;
46833
+ if (code !== "ENOENT") {
46834
+ args.logger?.warn("codex mcp config unreadable, skipped", { file, code: code ?? "parse-error" });
46835
+ }
46836
+ continue;
46837
+ }
46838
+ const servers = parsed?.mcpServers;
46839
+ if (!servers || typeof servers !== "object") continue;
46840
+ for (const [name, raw] of Object.entries(servers)) {
46841
+ const def = raw ?? {};
46842
+ const type = typeof def.type === "string" ? def.type : "stdio";
46843
+ if (type !== "stdio") {
46844
+ args.logger?.warn("codex mcp server type unsupported, skipped", { file, name, type });
46845
+ continue;
46846
+ }
46847
+ if (typeof def.command !== "string" || !def.command) {
46848
+ args.logger?.warn("codex mcp server missing command, skipped", { file, name });
46849
+ continue;
46850
+ }
46851
+ if (seen.has(name)) {
46852
+ args.logger?.warn("codex mcp server name collision, first-wins", { file, name });
46853
+ continue;
46854
+ }
46855
+ seen.add(name);
46856
+ const fileEnv = def.env && typeof def.env === "object" ? Object.fromEntries(
46857
+ Object.entries(def.env).filter(
46858
+ (kv) => typeof kv[1] === "string"
46859
+ )
46860
+ ) : {};
46861
+ const env = { ...args.baseEnv ?? {}, ...fileEnv };
46862
+ out.push({
46863
+ name,
46864
+ command: def.command,
46865
+ ...Array.isArray(def.args) && def.args.every((a) => typeof a === "string") ? { args: def.args } : {},
46866
+ ...Object.keys(env).length > 0 ? { env } : {}
46867
+ });
46868
+ }
46869
+ }
46870
+ return out;
46871
+ }
46872
+
46873
+ // src/tools/codex-features.ts
46874
+ var import_node_child_process3 = require("child_process");
46875
+ var cachedNetworkProxy;
46876
+ function hasNetworkProxyFeature(deps = {}) {
46877
+ if (cachedNetworkProxy !== void 0) return cachedNetworkProxy;
46878
+ try {
46879
+ const out = deps.execOverride ? deps.execOverride() : (0, import_node_child_process3.execFileSync)(process.env.CODEX_BIN ?? "codex", ["features", "list"], {
46880
+ encoding: "utf8",
46881
+ timeout: 3e3
46882
+ });
46883
+ const m2 = /^network_proxy\s+(.+?)\s+(?:true|false)\s*$/m.exec(out);
46884
+ cachedNetworkProxy = m2 !== null && m2[1].trim() !== "removed";
46885
+ } catch {
46886
+ cachedNetworkProxy = false;
46887
+ }
46888
+ return cachedNetworkProxy;
46889
+ }
46890
+ var warming = false;
46891
+ function warmCodexFeatureCache() {
46892
+ if (cachedNetworkProxy !== void 0 || warming) return;
46893
+ warming = true;
46894
+ (0, import_node_child_process3.execFile)(
46895
+ process.env.CODEX_BIN ?? "codex",
46896
+ ["features", "list"],
46897
+ { encoding: "utf8", timeout: 3e3 },
46898
+ (err, stdout) => {
46899
+ warming = false;
46900
+ if (cachedNetworkProxy !== void 0) return;
46901
+ if (err) {
46902
+ cachedNetworkProxy = false;
46903
+ return;
46904
+ }
46905
+ const m2 = /^network_proxy\s+(.+?)\s+(?:true|false)\s*$/m.exec(stdout);
46906
+ cachedNetworkProxy = m2 !== null && m2[1].trim() !== "removed";
46907
+ }
46908
+ );
46909
+ }
46910
+
46820
46911
  // src/tools/codex-session.ts
46821
46912
  function createCodexSession(ctx, sink, deps = {}) {
46822
46913
  const cmd = process.env.CODEX_BIN ?? "codex";
46823
46914
  const args = ["app-server", "--listen", "stdio://"];
46824
46915
  if (ctx.personaMode === "guest" && ctx.codexSandbox) {
46825
- args.push(...codexGuestCliArgs(ctx.codexSandbox));
46916
+ const loopbackDaemonAccess = deps.hasNetworkProxyOverride ? deps.hasNetworkProxyOverride() : hasNetworkProxyFeature();
46917
+ args.push(...codexGuestCliArgs(ctx.codexSandbox, { loopbackDaemonAccess }));
46826
46918
  }
46919
+ const clawdEnv = Object.fromEntries(
46920
+ Object.entries(ctx.env ?? {}).filter(
46921
+ (kv) => kv[0].startsWith("CLAWD_") && typeof kv[1] === "string"
46922
+ )
46923
+ );
46924
+ args.push(
46925
+ ...codexMcpCliArgs(
46926
+ loadCodexMcpServerDefs({
46927
+ mcpConfigPaths: (ctx.mcpConfigs ?? []).map((m2) => m2.path),
46928
+ cwd: ctx.cwd,
46929
+ baseEnv: clawdEnv,
46930
+ logger: deps.logger
46931
+ })
46932
+ )
46933
+ );
46827
46934
  const env = { ...process.env, ...ctx.env };
46828
- const proc = deps.spawnOverride ? deps.spawnOverride(cmd, args, ctx.cwd, env) : (0, import_node_child_process3.spawn)(cmd, args, { cwd: ctx.cwd, env, stdio: ["pipe", "pipe", "pipe"] });
46935
+ const proc = deps.spawnOverride ? deps.spawnOverride(cmd, args, ctx.cwd, env) : (0, import_node_child_process4.spawn)(cmd, args, { cwd: ctx.cwd, env, stdio: ["pipe", "pipe", "pipe"] });
46829
46936
  let threadId = ctx.toolSessionId;
46830
46937
  let turnId;
46831
46938
  const approvalIds = /* @__PURE__ */ new Map();
@@ -46882,6 +46989,7 @@ function createCodexSession(ctx, sink, deps = {}) {
46882
46989
  capabilities: null
46883
46990
  });
46884
46991
  client.notify("initialized", {});
46992
+ void client.request("skills/extraRoots/set", { extraRoots: [import_node_path16.default.join(ctx.cwd, ".claude", "skills")] }).catch((e) => deps.logger?.warn("codex skills/extraRoots/set failed", { message: e.message }));
46885
46993
  if (threadId) {
46886
46994
  await client.request("thread/resume", { threadId, ...threadStartParams(ctx) });
46887
46995
  } else {
@@ -46928,6 +47036,7 @@ function createCodexSession(ctx, sink, deps = {}) {
46928
47036
  }
46929
47037
 
46930
47038
  // src/tools/codex.ts
47039
+ var CODEX_MODEL_IDS = ["gpt-5.5", "gpt-5.4", "gpt-5.4-mini"];
46931
47040
  var CODEX_MODELS = [
46932
47041
  { id: "", label: "Default", description: "codex config.toml \u9ED8\u8BA4", contextWindowSize: 258400, default: true },
46933
47042
  { id: "gpt-5.5", label: "GPT-5.5", description: "Frontier model for complex coding", contextWindowSize: 258400 },
@@ -46986,10 +47095,10 @@ function sanitizeCodexModel(model) {
46986
47095
  return CODEX_MODELS.some((m2) => m2.id === model) ? model : void 0;
46987
47096
  }
46988
47097
  async function probeCodex(env = process.env) {
46989
- if (env.CODEX_BIN && import_node_fs17.default.existsSync(env.CODEX_BIN)) return { available: true, path: env.CODEX_BIN };
47098
+ if (env.CODEX_BIN && import_node_fs18.default.existsSync(env.CODEX_BIN)) return { available: true, path: env.CODEX_BIN };
46990
47099
  try {
46991
- const out = (0, import_node_child_process4.execFileSync)("which", ["codex"], { encoding: "utf8" }).trim();
46992
- if (out && import_node_fs17.default.existsSync(out)) return { available: true, path: out };
47100
+ const out = (0, import_node_child_process5.execFileSync)("which", ["codex"], { encoding: "utf8" }).trim();
47101
+ if (out && import_node_fs18.default.existsSync(out)) return { available: true, path: out };
46993
47102
  } catch {
46994
47103
  }
46995
47104
  return { available: false };
@@ -47035,9 +47144,9 @@ var CodexAdapter = class {
47035
47144
 
47036
47145
  // src/tools/claude-tui.ts
47037
47146
  var import_node_crypto5 = require("crypto");
47038
- var import_node_fs18 = __toESM(require("fs"), 1);
47147
+ var import_node_fs19 = __toESM(require("fs"), 1);
47039
47148
  var import_node_os7 = __toESM(require("os"), 1);
47040
- var import_node_path15 = __toESM(require("path"), 1);
47149
+ var import_node_path17 = __toESM(require("path"), 1);
47041
47150
  var import_headless = __toESM(require_xterm_headless(), 1);
47042
47151
 
47043
47152
  // ../node_modules/.pnpm/@xterm+addon-serialize@0.14.0/node_modules/@xterm/addon-serialize/lib/addon-serialize.mjs
@@ -48270,9 +48379,9 @@ function buildTuiSpawnArgs(ctx, isResume = false) {
48270
48379
  function jsonlExistsForCtx(ctx) {
48271
48380
  if (!ctx.toolSessionId) return false;
48272
48381
  const home = import_node_os7.default.homedir();
48273
- const file = import_node_path15.default.join(home, ".claude", "projects", cwdToHashDir(ctx.cwd), `${ctx.toolSessionId}.jsonl`);
48382
+ const file = import_node_path17.default.join(home, ".claude", "projects", cwdToHashDir(ctx.cwd), `${ctx.toolSessionId}.jsonl`);
48274
48383
  try {
48275
- return import_node_fs18.default.statSync(file).isFile();
48384
+ return import_node_fs19.default.statSync(file).isFile();
48276
48385
  } catch {
48277
48386
  return false;
48278
48387
  }
@@ -48382,7 +48491,7 @@ var PersonaDispatchManager = class {
48382
48491
 
48383
48492
  // src/dispatch/store.ts
48384
48493
  var import_promises = __toESM(require("fs/promises"), 1);
48385
- var import_node_path16 = __toESM(require("path"), 1);
48494
+ var import_node_path18 = __toESM(require("path"), 1);
48386
48495
 
48387
48496
  // src/shift/constants.ts
48388
48497
  var MAX_RUNS_PER_SHIFT = 30;
@@ -48420,7 +48529,7 @@ function createDispatchStore(deps) {
48420
48529
  flushTimer = null;
48421
48530
  }
48422
48531
  const content = { version: 1, records };
48423
- await import_promises.default.mkdir(import_node_path16.default.dirname(deps.filePath), { recursive: true });
48532
+ await import_promises.default.mkdir(import_node_path18.default.dirname(deps.filePath), { recursive: true });
48424
48533
  const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
48425
48534
  await import_promises.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
48426
48535
  await import_promises.default.rename(tmp, deps.filePath);
@@ -48482,10 +48591,10 @@ function createDispatchStore(deps) {
48482
48591
  }
48483
48592
 
48484
48593
  // src/dispatch/mcp-config.ts
48485
- var import_node_fs19 = __toESM(require("fs"), 1);
48486
- var import_node_path17 = __toESM(require("path"), 1);
48594
+ var import_node_fs20 = __toESM(require("fs"), 1);
48595
+ var import_node_path19 = __toESM(require("path"), 1);
48487
48596
  function dispatchMcpConfigPath(dataDir) {
48488
- return import_node_path17.default.join(dataDir, "dispatch.mcp.json");
48597
+ return import_node_path19.default.join(dataDir, "dispatch.mcp.json");
48489
48598
  }
48490
48599
  function writeDispatchMcpConfig(args) {
48491
48600
  const cfgPath = dispatchMcpConfigPath(args.dataDir);
@@ -48500,16 +48609,16 @@ function writeDispatchMcpConfig(args) {
48500
48609
  }
48501
48610
  }
48502
48611
  };
48503
- import_node_fs19.default.mkdirSync(args.dataDir, { recursive: true });
48504
- import_node_fs19.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
48612
+ import_node_fs20.default.mkdirSync(args.dataDir, { recursive: true });
48613
+ import_node_fs20.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
48505
48614
  return cfgPath;
48506
48615
  }
48507
48616
 
48508
48617
  // src/ticket/mcp-config.ts
48509
- var import_node_fs20 = __toESM(require("fs"), 1);
48510
- var import_node_path18 = __toESM(require("path"), 1);
48618
+ var import_node_fs21 = __toESM(require("fs"), 1);
48619
+ var import_node_path20 = __toESM(require("path"), 1);
48511
48620
  function ticketMcpConfigPath(dataDir) {
48512
- return import_node_path18.default.join(dataDir, "ticket.mcp.json");
48621
+ return import_node_path20.default.join(dataDir, "ticket.mcp.json");
48513
48622
  }
48514
48623
  function writeTicketMcpConfig(args) {
48515
48624
  const cfgPath = ticketMcpConfigPath(args.dataDir);
@@ -48529,16 +48638,16 @@ function writeTicketMcpConfig(args) {
48529
48638
  }
48530
48639
  }
48531
48640
  };
48532
- import_node_fs20.default.mkdirSync(args.dataDir, { recursive: true });
48533
- import_node_fs20.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
48641
+ import_node_fs21.default.mkdirSync(args.dataDir, { recursive: true });
48642
+ import_node_fs21.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
48534
48643
  return cfgPath;
48535
48644
  }
48536
48645
 
48537
48646
  // src/shift/mcp-config.ts
48538
- var import_node_fs21 = __toESM(require("fs"), 1);
48539
- var import_node_path19 = __toESM(require("path"), 1);
48647
+ var import_node_fs22 = __toESM(require("fs"), 1);
48648
+ var import_node_path21 = __toESM(require("path"), 1);
48540
48649
  function shiftMcpConfigPath(dataDir) {
48541
- return import_node_path19.default.join(dataDir, "shift.mcp.json");
48650
+ return import_node_path21.default.join(dataDir, "shift.mcp.json");
48542
48651
  }
48543
48652
  async function writeShiftMcpConfig(args) {
48544
48653
  const cfgPath = shiftMcpConfigPath(args.dataDir);
@@ -48553,16 +48662,16 @@ async function writeShiftMcpConfig(args) {
48553
48662
  }
48554
48663
  }
48555
48664
  };
48556
- await import_node_fs21.default.promises.mkdir(args.dataDir, { recursive: true });
48557
- await import_node_fs21.default.promises.writeFile(cfgPath, JSON.stringify(content, null, 2), "utf8");
48665
+ await import_node_fs22.default.promises.mkdir(args.dataDir, { recursive: true });
48666
+ await import_node_fs22.default.promises.writeFile(cfgPath, JSON.stringify(content, null, 2), "utf8");
48558
48667
  return cfgPath;
48559
48668
  }
48560
48669
 
48561
48670
  // src/inbox/mcp-config.ts
48562
- var import_node_fs22 = __toESM(require("fs"), 1);
48563
- var import_node_path20 = __toESM(require("path"), 1);
48671
+ var import_node_fs23 = __toESM(require("fs"), 1);
48672
+ var import_node_path22 = __toESM(require("path"), 1);
48564
48673
  function inboxMcpConfigPath(dataDir) {
48565
- return import_node_path20.default.join(dataDir, "inbox.mcp.json");
48674
+ return import_node_path22.default.join(dataDir, "inbox.mcp.json");
48566
48675
  }
48567
48676
  async function writeInboxMcpConfig(args) {
48568
48677
  const cfgPath = inboxMcpConfigPath(args.dataDir);
@@ -48577,16 +48686,16 @@ async function writeInboxMcpConfig(args) {
48577
48686
  }
48578
48687
  }
48579
48688
  };
48580
- await import_node_fs22.default.promises.mkdir(args.dataDir, { recursive: true });
48581
- await import_node_fs22.default.promises.writeFile(cfgPath, JSON.stringify(content, null, 2), "utf8");
48689
+ await import_node_fs23.default.promises.mkdir(args.dataDir, { recursive: true });
48690
+ await import_node_fs23.default.promises.writeFile(cfgPath, JSON.stringify(content, null, 2), "utf8");
48582
48691
  return cfgPath;
48583
48692
  }
48584
48693
 
48585
48694
  // src/peer-ops/mcp-config.ts
48586
- var import_node_fs23 = __toESM(require("fs"), 1);
48587
- var import_node_path21 = __toESM(require("path"), 1);
48695
+ var import_node_fs24 = __toESM(require("fs"), 1);
48696
+ var import_node_path23 = __toESM(require("path"), 1);
48588
48697
  function peerOpsMcpConfigPath(dataDir) {
48589
- return import_node_path21.default.join(dataDir, "peer-ops.mcp.json");
48698
+ return import_node_path23.default.join(dataDir, "peer-ops.mcp.json");
48590
48699
  }
48591
48700
  function writePeerOpsMcpConfig(args) {
48592
48701
  const cfgPath = peerOpsMcpConfigPath(args.dataDir);
@@ -48599,14 +48708,14 @@ function writePeerOpsMcpConfig(args) {
48599
48708
  }
48600
48709
  }
48601
48710
  };
48602
- import_node_fs23.default.mkdirSync(args.dataDir, { recursive: true });
48603
- import_node_fs23.default.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
48711
+ import_node_fs24.default.mkdirSync(args.dataDir, { recursive: true });
48712
+ import_node_fs24.default.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
48604
48713
  return cfgPath;
48605
48714
  }
48606
48715
 
48607
48716
  // src/shift/store.ts
48608
48717
  var import_promises2 = __toESM(require("fs/promises"), 1);
48609
- var import_node_path22 = __toESM(require("path"), 1);
48718
+ var import_node_path24 = __toESM(require("path"), 1);
48610
48719
  var import_node_crypto6 = require("crypto");
48611
48720
 
48612
48721
  // src/shift/schedule.ts
@@ -48681,7 +48790,7 @@ function createShiftStore(deps) {
48681
48790
  flushTimer = null;
48682
48791
  }
48683
48792
  const content = { version: 1, shifts };
48684
- await import_promises2.default.mkdir(import_node_path22.default.dirname(deps.filePath), { recursive: true });
48793
+ await import_promises2.default.mkdir(import_node_path24.default.dirname(deps.filePath), { recursive: true });
48685
48794
  const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
48686
48795
  await import_promises2.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
48687
48796
  await import_promises2.default.rename(tmp, deps.filePath);
@@ -49114,11 +49223,16 @@ function buildPersonaDispatchHandlers(deps) {
49114
49223
  const { type: _t, requestId: _r, ...rest } = frame;
49115
49224
  const sourceSessionId = typeof rest.sessionId === "string" ? rest.sessionId : void 0;
49116
49225
  const args = DispatchRunArgsSchema.parse(rest);
49117
- if (args.model !== void 0 && !CLAUDE_MODEL_IDS.includes(args.model)) {
49118
- throw new ClawdError(
49119
- ERROR_CODES.INVALID_PARAM,
49120
- `model '${args.model}' not supported; allowed: ${CLAUDE_MODEL_IDS.join(", ")}`
49121
- );
49226
+ if (args.model !== void 0) {
49227
+ const targetPersona2 = args.targetPersona ?? (args.dispatchId ? deps.store.get(args.dispatchId)?.targetPersonaId : void 0);
49228
+ const targetTool = (targetPersona2 ? deps.getPersonaTool?.(targetPersona2) : void 0) ?? "claude";
49229
+ const allowed = targetTool === "codex" ? CODEX_MODEL_IDS : CLAUDE_MODEL_IDS;
49230
+ if (!allowed.includes(args.model)) {
49231
+ throw new ClawdError(
49232
+ ERROR_CODES.INVALID_PARAM,
49233
+ `model '${args.model}' not supported for tool '${targetTool}'; allowed: ${allowed.join(", ")}`
49234
+ );
49235
+ }
49122
49236
  }
49123
49237
  if (args.dispatchId) {
49124
49238
  if (ctx?.principal.kind === "guest") {
@@ -49453,7 +49567,7 @@ async function forwardPeerExecToPeer(args) {
49453
49567
  }
49454
49568
 
49455
49569
  // src/tools/codex-history.ts
49456
- var import_node_child_process5 = require("child_process");
49570
+ var import_node_child_process6 = require("child_process");
49457
49571
  var DEFAULT_TIMEOUT_MS2 = 8e3;
49458
49572
  function rec2(v2) {
49459
49573
  return v2 && typeof v2 === "object" && !Array.isArray(v2) ? v2 : void 0;
@@ -49516,7 +49630,7 @@ function threadItemToHistoryMessages(item) {
49516
49630
  async function queryCodexAppServer(cwd, method, params, deps) {
49517
49631
  const cmd = process.env.CODEX_BIN ?? "codex";
49518
49632
  const args = ["app-server", "--listen", "stdio://"];
49519
- const proc = deps.spawnOverride ? deps.spawnOverride(cmd, args, cwd) : (0, import_node_child_process5.spawn)(cmd, args, { cwd, env: process.env, stdio: ["pipe", "pipe", "pipe"] });
49633
+ const proc = deps.spawnOverride ? deps.spawnOverride(cmd, args, cwd) : (0, import_node_child_process6.spawn)(cmd, args, { cwd, env: process.env, stdio: ["pipe", "pipe", "pipe"] });
49520
49634
  const client = new CodexAppServerClient(proc, {});
49521
49635
  const timeoutMs = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
49522
49636
  let timer;
@@ -49622,7 +49736,8 @@ var CodexHistoryReader = class {
49622
49736
  };
49623
49737
 
49624
49738
  // src/tools/codex-skills.ts
49625
- var import_node_child_process6 = require("child_process");
49739
+ var import_node_child_process7 = require("child_process");
49740
+ var import_node_path25 = __toESM(require("path"), 1);
49626
49741
  var DEFAULT_TIMEOUT_MS3 = 8e3;
49627
49742
  function str3(v2) {
49628
49743
  return typeof v2 === "string" ? v2 : void 0;
@@ -49638,13 +49753,13 @@ function mapSkillsListResponse(res) {
49638
49753
  const r = s ?? {};
49639
49754
  const name = str3(r.name);
49640
49755
  if (!name) continue;
49641
- const path71 = str3(r.path);
49756
+ const path74 = str3(r.path);
49642
49757
  const description = str3(r.description);
49643
49758
  const isPlugin = name.includes(":");
49644
49759
  out.push({
49645
49760
  name,
49646
49761
  source: isPlugin ? "plugin" : "project",
49647
- ...path71 ? { path: path71 } : {},
49762
+ ...path74 ? { path: path74 } : {},
49648
49763
  ...description ? { description } : {},
49649
49764
  ...isPlugin ? { plugin: name.split(":")[0] } : {}
49650
49765
  });
@@ -49655,7 +49770,7 @@ function mapSkillsListResponse(res) {
49655
49770
  async function listCodexSkills(cwd, deps = {}) {
49656
49771
  const cmd = process.env.CODEX_BIN ?? "codex";
49657
49772
  const args = ["app-server", "--listen", "stdio://"];
49658
- const proc = deps.spawnOverride ? deps.spawnOverride(cmd, args, cwd) : (0, import_node_child_process6.spawn)(cmd, args, { cwd, env: process.env, stdio: ["pipe", "pipe", "pipe"] });
49773
+ const proc = deps.spawnOverride ? deps.spawnOverride(cmd, args, cwd) : (0, import_node_child_process7.spawn)(cmd, args, { cwd, env: process.env, stdio: ["pipe", "pipe", "pipe"] });
49659
49774
  const client = new CodexAppServerClient(proc, {});
49660
49775
  const timeoutMs = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS3;
49661
49776
  let timer;
@@ -49674,6 +49789,7 @@ async function listCodexSkills(cwd, deps = {}) {
49674
49789
  })(),
49675
49790
  timeout
49676
49791
  ]);
49792
+ void client.request("skills/extraRoots/set", { extraRoots: [import_node_path25.default.join(cwd, ".claude", "skills")] }).catch(() => void 0);
49677
49793
  const res = await Promise.race([client.request("skills/list", { cwds: [cwd] }), timeout]);
49678
49794
  return mapSkillsListResponse(res);
49679
49795
  } finally {
@@ -49683,23 +49799,23 @@ async function listCodexSkills(cwd, deps = {}) {
49683
49799
  }
49684
49800
 
49685
49801
  // src/workspace/browser.ts
49686
- var import_node_fs24 = __toESM(require("fs"), 1);
49802
+ var import_node_fs25 = __toESM(require("fs"), 1);
49687
49803
  var import_node_os8 = __toESM(require("os"), 1);
49688
- var import_node_path23 = __toESM(require("path"), 1);
49804
+ var import_node_path26 = __toESM(require("path"), 1);
49689
49805
  init_protocol();
49690
49806
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
49691
49807
  function resolveInsideCwd(cwd, subpath) {
49692
- const absCwd = import_node_path23.default.resolve(cwd);
49693
- const joined = import_node_path23.default.resolve(absCwd, subpath ?? ".");
49694
- const rel = import_node_path23.default.relative(absCwd, joined);
49695
- if (rel.startsWith("..") || import_node_path23.default.isAbsolute(rel)) {
49808
+ const absCwd = import_node_path26.default.resolve(cwd);
49809
+ const joined = import_node_path26.default.resolve(absCwd, subpath ?? ".");
49810
+ const rel = import_node_path26.default.relative(absCwd, joined);
49811
+ if (rel.startsWith("..") || import_node_path26.default.isAbsolute(rel)) {
49696
49812
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `path escapes cwd: ${subpath}`);
49697
49813
  }
49698
49814
  return joined;
49699
49815
  }
49700
49816
  function ensureCwd(cwd) {
49701
49817
  try {
49702
- const stat = import_node_fs24.default.statSync(cwd);
49818
+ const stat = import_node_fs25.default.statSync(cwd);
49703
49819
  if (!stat.isDirectory()) {
49704
49820
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `not a directory: ${cwd}`);
49705
49821
  }
@@ -49713,7 +49829,7 @@ var WorkspaceBrowser = class {
49713
49829
  const cwd = args.cwd && args.cwd.length > 0 ? args.cwd : import_node_os8.default.homedir();
49714
49830
  ensureCwd(cwd);
49715
49831
  const full = resolveInsideCwd(cwd, args.path);
49716
- const dirents = import_node_fs24.default.readdirSync(full, { withFileTypes: true });
49832
+ const dirents = import_node_fs25.default.readdirSync(full, { withFileTypes: true });
49717
49833
  const entries = [];
49718
49834
  for (const d of dirents) {
49719
49835
  if (!args.showHidden && d.name.startsWith(".")) continue;
@@ -49723,7 +49839,7 @@ var WorkspaceBrowser = class {
49723
49839
  mtime: ""
49724
49840
  };
49725
49841
  try {
49726
- const st = import_node_fs24.default.statSync(import_node_path23.default.join(full, d.name));
49842
+ const st = import_node_fs25.default.statSync(import_node_path26.default.join(full, d.name));
49727
49843
  entry.mtime = new Date(st.mtimeMs).toISOString();
49728
49844
  if (d.isFile()) entry.size = st.size;
49729
49845
  } catch {
@@ -49739,14 +49855,14 @@ var WorkspaceBrowser = class {
49739
49855
  read(args) {
49740
49856
  ensureCwd(args.cwd);
49741
49857
  const full = resolveInsideCwd(args.cwd, args.path);
49742
- const st = import_node_fs24.default.statSync(full);
49858
+ const st = import_node_fs25.default.statSync(full);
49743
49859
  if (!st.isFile()) {
49744
49860
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `not a file: ${args.path}`);
49745
49861
  }
49746
49862
  if (st.size > MAX_FILE_BYTES) {
49747
49863
  throw new ClawdError(ERROR_CODES.FILE_TOO_LARGE, `file > ${MAX_FILE_BYTES} bytes`);
49748
49864
  }
49749
- const buf = import_node_fs24.default.readFileSync(full);
49865
+ const buf = import_node_fs25.default.readFileSync(full);
49750
49866
  const isBinary = buf.includes(0);
49751
49867
  if (isBinary) {
49752
49868
  return {
@@ -49768,20 +49884,20 @@ var WorkspaceBrowser = class {
49768
49884
  };
49769
49885
 
49770
49886
  // src/skills/agents-scanner.ts
49771
- var import_node_fs25 = __toESM(require("fs"), 1);
49887
+ var import_node_fs26 = __toESM(require("fs"), 1);
49772
49888
  var import_node_os9 = __toESM(require("os"), 1);
49773
- var import_node_path24 = __toESM(require("path"), 1);
49889
+ var import_node_path27 = __toESM(require("path"), 1);
49774
49890
  var DEFAULT_POLICY_DIR_DARWIN = "/Library/Application Support/ClaudeCode/.claude/agents";
49775
49891
  function isDirLikeSync2(p2) {
49776
49892
  try {
49777
- return import_node_fs25.default.statSync(p2).isDirectory();
49893
+ return import_node_fs26.default.statSync(p2).isDirectory();
49778
49894
  } catch {
49779
49895
  return false;
49780
49896
  }
49781
49897
  }
49782
49898
  function fileExistsSync(p2) {
49783
49899
  try {
49784
- return import_node_fs25.default.statSync(p2).isFile();
49900
+ return import_node_fs26.default.statSync(p2).isFile();
49785
49901
  } catch {
49786
49902
  return false;
49787
49903
  }
@@ -49789,7 +49905,7 @@ function fileExistsSync(p2) {
49789
49905
  function parseAgentFile(filePath) {
49790
49906
  let content;
49791
49907
  try {
49792
- content = import_node_fs25.default.readFileSync(filePath, "utf8");
49908
+ content = import_node_fs26.default.readFileSync(filePath, "utf8");
49793
49909
  } catch {
49794
49910
  return {};
49795
49911
  }
@@ -49802,16 +49918,16 @@ function parseAgentFile(filePath) {
49802
49918
  function scanAgentsDir(dir, source, seen, out) {
49803
49919
  let entries;
49804
49920
  try {
49805
- entries = import_node_fs25.default.readdirSync(dir, { withFileTypes: true });
49921
+ entries = import_node_fs26.default.readdirSync(dir, { withFileTypes: true });
49806
49922
  } catch {
49807
49923
  return;
49808
49924
  }
49809
49925
  for (const ent of entries) {
49810
49926
  if (!ent.name.endsWith(".md")) continue;
49811
- if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path24.default.join(dir, ent.name)))) {
49927
+ if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path27.default.join(dir, ent.name)))) {
49812
49928
  continue;
49813
49929
  }
49814
- const filePath = import_node_path24.default.join(dir, ent.name);
49930
+ const filePath = import_node_path27.default.join(dir, ent.name);
49815
49931
  const baseName = ent.name.replace(/\.md$/, "");
49816
49932
  if (seen.has(baseName)) continue;
49817
49933
  seen.add(baseName);
@@ -49829,12 +49945,12 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
49829
49945
  function walk2(dir, namespaces) {
49830
49946
  let entries;
49831
49947
  try {
49832
- entries = import_node_fs25.default.readdirSync(dir, { withFileTypes: true });
49948
+ entries = import_node_fs26.default.readdirSync(dir, { withFileTypes: true });
49833
49949
  } catch {
49834
49950
  return;
49835
49951
  }
49836
49952
  for (const ent of entries) {
49837
- const childPath = import_node_path24.default.join(dir, ent.name);
49953
+ const childPath = import_node_path27.default.join(dir, ent.name);
49838
49954
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync2(childPath)) {
49839
49955
  walk2(childPath, [...namespaces, ent.name]);
49840
49956
  continue;
@@ -49859,13 +49975,13 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
49859
49975
  walk2(root, []);
49860
49976
  }
49861
49977
  function readInstalledPlugins2(home) {
49862
- const pluginsDir = import_node_path24.default.join(home, ".claude", "plugins");
49863
- const v2 = import_node_path24.default.join(pluginsDir, "installed_plugins_v2.json");
49864
- const v1 = import_node_path24.default.join(pluginsDir, "installed_plugins.json");
49978
+ const pluginsDir = import_node_path27.default.join(home, ".claude", "plugins");
49979
+ const v2 = import_node_path27.default.join(pluginsDir, "installed_plugins_v2.json");
49980
+ const v1 = import_node_path27.default.join(pluginsDir, "installed_plugins.json");
49865
49981
  let raw = null;
49866
49982
  for (const candidate of [v2, v1]) {
49867
49983
  try {
49868
- raw = import_node_fs25.default.readFileSync(candidate, "utf8");
49984
+ raw = import_node_fs26.default.readFileSync(candidate, "utf8");
49869
49985
  break;
49870
49986
  } catch {
49871
49987
  }
@@ -49890,19 +50006,19 @@ function readInstalledPlugins2(home) {
49890
50006
  return out;
49891
50007
  }
49892
50008
  function walkUpProjectAgentsDirs(startCwd, home, seen, out) {
49893
- let cur = import_node_path24.default.resolve(startCwd);
49894
- const fsRoot = import_node_path24.default.parse(cur).root;
50009
+ let cur = import_node_path27.default.resolve(startCwd);
50010
+ const fsRoot = import_node_path27.default.parse(cur).root;
49895
50011
  while (true) {
49896
- scanAgentsDir(import_node_path24.default.join(cur, ".claude", "agents"), "project", seen, out);
50012
+ scanAgentsDir(import_node_path27.default.join(cur, ".claude", "agents"), "project", seen, out);
49897
50013
  let hasGit = false;
49898
50014
  try {
49899
- hasGit = import_node_fs25.default.existsSync(import_node_path24.default.join(cur, ".git"));
50015
+ hasGit = import_node_fs26.default.existsSync(import_node_path27.default.join(cur, ".git"));
49900
50016
  } catch {
49901
50017
  }
49902
50018
  if (hasGit) return;
49903
50019
  if (cur === home) return;
49904
50020
  if (cur === fsRoot) return;
49905
- const parent = import_node_path24.default.dirname(cur);
50021
+ const parent = import_node_path27.default.dirname(cur);
49906
50022
  if (parent === cur) return;
49907
50023
  cur = parent;
49908
50024
  }
@@ -49937,7 +50053,7 @@ var AgentsScanner = class {
49937
50053
  }
49938
50054
  const fsBlock = [];
49939
50055
  scanAgentsDir(
49940
- import_node_path24.default.join(this.home, ".claude", "agents"),
50056
+ import_node_path27.default.join(this.home, ".claude", "agents"),
49941
50057
  "global",
49942
50058
  seen,
49943
50059
  fsBlock
@@ -49951,7 +50067,7 @@ var AgentsScanner = class {
49951
50067
  ...this.extraPluginRoots
49952
50068
  ];
49953
50069
  for (const { name, root } of plugins) {
49954
- const agentsRoot = import_node_path24.default.join(root, "agents");
50070
+ const agentsRoot = import_node_path27.default.join(root, "agents");
49955
50071
  scanPluginAgentsTree(agentsRoot, name, seen, fsBlock);
49956
50072
  }
49957
50073
  return [...builtinBlock, ...fsBlock];
@@ -49959,15 +50075,15 @@ var AgentsScanner = class {
49959
50075
  };
49960
50076
 
49961
50077
  // src/observer/session-observer.ts
49962
- var import_node_fs27 = __toESM(require("fs"), 1);
50078
+ var import_node_fs28 = __toESM(require("fs"), 1);
49963
50079
  var import_node_os11 = __toESM(require("os"), 1);
49964
- var import_node_path26 = __toESM(require("path"), 1);
50080
+ var import_node_path29 = __toESM(require("path"), 1);
49965
50081
  init_claude_history();
49966
50082
 
49967
50083
  // src/observer/subagent-meta-observer.ts
49968
- var import_node_fs26 = __toESM(require("fs"), 1);
50084
+ var import_node_fs27 = __toESM(require("fs"), 1);
49969
50085
  var import_node_os10 = __toESM(require("os"), 1);
49970
- var import_node_path25 = __toESM(require("path"), 1);
50086
+ var import_node_path28 = __toESM(require("path"), 1);
49971
50087
  init_claude_history();
49972
50088
  var META_RE = /^agent-([A-Za-z0-9_-]+)\.meta\.json$/;
49973
50089
  var SubagentMetaObserver = class {
@@ -49980,7 +50096,7 @@ var SubagentMetaObserver = class {
49980
50096
  watches = /* @__PURE__ */ new Map();
49981
50097
  // public for spec only:测试直接拼路径写假 meta.json;生产 start() 内部自己解析
49982
50098
  resolveSubagentDir(cwd, toolSessionId) {
49983
- return import_node_path25.default.join(
50099
+ return import_node_path28.default.join(
49984
50100
  this.home,
49985
50101
  ".claude",
49986
50102
  "projects",
@@ -50010,7 +50126,7 @@ var SubagentMetaObserver = class {
50010
50126
  attachWatcher(w2) {
50011
50127
  if (w2.watcher) return;
50012
50128
  try {
50013
- w2.watcher = import_node_fs26.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
50129
+ w2.watcher = import_node_fs27.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
50014
50130
  if (!name) return;
50015
50131
  const m2 = META_RE.exec(String(name));
50016
50132
  if (!m2) return;
@@ -50022,7 +50138,7 @@ var SubagentMetaObserver = class {
50022
50138
  scan(w2) {
50023
50139
  let entries;
50024
50140
  try {
50025
- entries = import_node_fs26.default.readdirSync(w2.dirPath);
50141
+ entries = import_node_fs27.default.readdirSync(w2.dirPath);
50026
50142
  } catch {
50027
50143
  return;
50028
50144
  }
@@ -50036,10 +50152,10 @@ var SubagentMetaObserver = class {
50036
50152
  if (!m2) return;
50037
50153
  const agentId = m2[1];
50038
50154
  if (w2.emitted.has(agentId)) return;
50039
- const file = import_node_path25.default.join(w2.dirPath, name);
50155
+ const file = import_node_path28.default.join(w2.dirPath, name);
50040
50156
  let raw;
50041
50157
  try {
50042
- raw = import_node_fs26.default.readFileSync(file, "utf8");
50158
+ raw = import_node_fs27.default.readFileSync(file, "utf8");
50043
50159
  } catch {
50044
50160
  return;
50045
50161
  }
@@ -50096,14 +50212,14 @@ var SessionObserver = class {
50096
50212
  metaObserver;
50097
50213
  resolveJsonlPath(cwd, toolSessionId, override) {
50098
50214
  if (override) return override;
50099
- return import_node_path26.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
50215
+ return import_node_path29.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
50100
50216
  }
50101
50217
  start(args) {
50102
50218
  this.stop(args.sessionId);
50103
50219
  const filePath = this.resolveJsonlPath(args.cwd, args.toolSessionId, args.jsonlPath);
50104
50220
  let size = 0;
50105
50221
  try {
50106
- size = import_node_fs27.default.statSync(filePath).size;
50222
+ size = import_node_fs28.default.statSync(filePath).size;
50107
50223
  } catch {
50108
50224
  }
50109
50225
  const w2 = {
@@ -50116,16 +50232,16 @@ var SessionObserver = class {
50116
50232
  adapter: args.adapter,
50117
50233
  prevIsRejectSentinel: false,
50118
50234
  toolSessionId: args.toolSessionId,
50119
- sawFile: import_node_fs27.default.existsSync(filePath),
50235
+ sawFile: import_node_fs28.default.existsSync(filePath),
50120
50236
  lastRelocateScanMs: 0,
50121
50237
  pendingCwdDerive: false,
50122
50238
  lastDeriveMs: 0
50123
50239
  };
50124
50240
  try {
50125
- import_node_fs27.default.mkdirSync(import_node_path26.default.dirname(filePath), { recursive: true });
50241
+ import_node_fs28.default.mkdirSync(import_node_path29.default.dirname(filePath), { recursive: true });
50126
50242
  } catch {
50127
50243
  }
50128
- w2.watcher = import_node_fs27.default.watch(import_node_path26.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
50244
+ w2.watcher = import_node_fs28.default.watch(import_node_path29.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
50129
50245
  if (!changedName || !w2.filePath.endsWith(changedName)) return;
50130
50246
  this.poll(w2);
50131
50247
  });
@@ -50148,7 +50264,7 @@ var SessionObserver = class {
50148
50264
  // 异常静默吞,不阻塞 watcher 启动
50149
50265
  hydrateMetaTail(w2, maxLines = 200) {
50150
50266
  try {
50151
- const raw = import_node_fs27.default.readFileSync(w2.filePath, "utf8");
50267
+ const raw = import_node_fs28.default.readFileSync(w2.filePath, "utf8");
50152
50268
  if (!raw) return;
50153
50269
  const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
50154
50270
  if (allLines.length === 0) return;
@@ -50172,7 +50288,7 @@ var SessionObserver = class {
50172
50288
  poll(w2) {
50173
50289
  let size = 0;
50174
50290
  try {
50175
- size = import_node_fs27.default.statSync(w2.filePath).size;
50291
+ size = import_node_fs28.default.statSync(w2.filePath).size;
50176
50292
  } catch {
50177
50293
  if (w2.sawFile) this.maybeRelocate(w2);
50178
50294
  return;
@@ -50183,11 +50299,11 @@ var SessionObserver = class {
50183
50299
  w2.buf = "";
50184
50300
  }
50185
50301
  if (size === w2.lastSize) return;
50186
- const fd = import_node_fs27.default.openSync(w2.filePath, "r");
50302
+ const fd = import_node_fs28.default.openSync(w2.filePath, "r");
50187
50303
  try {
50188
50304
  const len = size - w2.lastSize;
50189
50305
  const buf = Buffer.alloc(len);
50190
- import_node_fs27.default.readSync(fd, buf, 0, len, w2.lastSize);
50306
+ import_node_fs28.default.readSync(fd, buf, 0, len, w2.lastSize);
50191
50307
  w2.lastSize = size;
50192
50308
  w2.buf += buf.toString("utf8");
50193
50309
  let newlineIndex;
@@ -50210,7 +50326,7 @@ var SessionObserver = class {
50210
50326
  }
50211
50327
  }
50212
50328
  } finally {
50213
- import_node_fs27.default.closeSync(fd);
50329
+ import_node_fs28.default.closeSync(fd);
50214
50330
  }
50215
50331
  if (w2.pendingCwdDerive) this.maybeDeriveCwd(w2);
50216
50332
  }
@@ -50222,7 +50338,7 @@ var SessionObserver = class {
50222
50338
  const now = Date.now();
50223
50339
  if (now - w2.lastRelocateScanMs < 1e3) return;
50224
50340
  w2.lastRelocateScanMs = now;
50225
- const projectsRoot = import_node_path26.default.join(this.home, ".claude", "projects");
50341
+ const projectsRoot = import_node_path29.default.join(this.home, ".claude", "projects");
50226
50342
  const found = findTranscriptByToolSessionId(projectsRoot, w2.toolSessionId);
50227
50343
  if (!found || found === w2.filePath) return;
50228
50344
  try {
@@ -50231,7 +50347,7 @@ var SessionObserver = class {
50231
50347
  }
50232
50348
  w2.filePath = found;
50233
50349
  try {
50234
- w2.watcher = import_node_fs27.default.watch(import_node_path26.default.dirname(found), { persistent: false }, (_event, changedName) => {
50350
+ w2.watcher = import_node_fs28.default.watch(import_node_path29.default.dirname(found), { persistent: false }, (_event, changedName) => {
50235
50351
  if (!changedName || !w2.filePath.endsWith(changedName)) return;
50236
50352
  this.poll(w2);
50237
50353
  });
@@ -50535,9 +50651,9 @@ var LocalWsServer = class {
50535
50651
  this.safeSend(c.ws, frame);
50536
50652
  }
50537
50653
  }
50538
- // Task 1.9 capability platform:仅广播给 owner 连接(跳过 guest ws)。
50539
- // 用于 capability:tokenIssued / capability:tokenDeleted owner-only push frame——
50540
- // guest 没必要也无法消费这类管理帧。noAuth localhost ws 没有 ctx, 视作 owner.
50654
+ // 仅广播给 owner 连接(跳过 guest ws)。用于 contact:added / contact:removed /
50655
+ // contact:pinned / friend:reverseTokenOffered / appBuilder:project-updated
50656
+ // owner-only push frame——guest 无需消费。noAuth localhost ws ctx,视作 owner
50541
50657
  broadcastToOwners(frame) {
50542
50658
  const gate = this.opts.authGate;
50543
50659
  for (const c of this.clients.values()) {
@@ -50611,10 +50727,8 @@ var LocalWsServer = class {
50611
50727
  }
50612
50728
  }
50613
50729
  }
50614
- // Task 1.10 撤销级联:关闭某 guest principal 的所有活跃 ws 连接。close code 4401
50615
- // 让客户端识别 'capability revoked' 而非普通断连。
50616
- // capability:delete 级联用 cap.id 调本方法——noAuth 旧 per-peer cap guest 的
50617
- // principal.id ≡ cap.id,级联语义不变(Phase 3 决策 #14 正名)。
50730
+ // 关闭某 guest principal 的所有活跃 ws 连接。close code 4401 让客户端识别
50731
+ // 'TOKEN_REVOKED' 而非普通断连;调用方按需触发(如 contact:remove 后要断对方现有连接)。
50618
50732
  closeConnectionsByGuestId(guestPrincipalId, code = 4401, reason = "TOKEN_REVOKED") {
50619
50733
  const set = this.guestIdToClients.get(guestPrincipalId);
50620
50734
  if (!set) return;
@@ -51088,178 +51202,25 @@ async function authenticate(token, deps) {
51088
51202
  };
51089
51203
  }
51090
51204
 
51091
- // src/permission/capability-store.ts
51092
- var fs32 = __toESM(require("fs"), 1);
51093
- var path31 = __toESM(require("path"), 1);
51094
- var CAPABILITIES_FILE_NAME = "capabilities.json";
51095
- var FILE_VERSION = 1;
51096
- var CapabilityStore = class {
51097
- constructor(dataDir) {
51098
- this.dataDir = dataDir;
51099
- fs32.mkdirSync(dataDir, { recursive: true });
51100
- this.cache = this.readFromDisk();
51101
- }
51102
- dataDir;
51103
- cache;
51104
- list() {
51105
- return [...this.cache];
51106
- }
51107
- upsert(cap) {
51108
- const idx = this.cache.findIndex((c) => c.id === cap.id);
51109
- if (idx >= 0) {
51110
- this.cache[idx] = cap;
51111
- } else {
51112
- this.cache.push(cap);
51113
- }
51114
- this.flush();
51115
- }
51116
- remove(id) {
51117
- const next = this.cache.filter((c) => c.id !== id);
51118
- if (next.length === this.cache.length) return;
51119
- this.cache = next;
51120
- this.flush();
51121
- }
51122
- filePath() {
51123
- return path31.join(this.dataDir, CAPABILITIES_FILE_NAME);
51124
- }
51125
- readFromDisk() {
51126
- const file = this.filePath();
51127
- let raw;
51128
- try {
51129
- raw = fs32.readFileSync(file, "utf8");
51130
- } catch (err) {
51131
- if (err?.code === "ENOENT") return [];
51132
- return [];
51133
- }
51134
- if (!raw.trim()) return [];
51135
- let parsed;
51136
- try {
51137
- parsed = JSON.parse(raw);
51138
- } catch {
51139
- return [];
51140
- }
51141
- if (!parsed || typeof parsed !== "object") return [];
51142
- const arr = parsed.capabilities;
51143
- if (!Array.isArray(arr)) return [];
51144
- const out = [];
51145
- for (const item of arr) {
51146
- const r = CapabilitySchema.safeParse(item);
51147
- if (r.success) out.push(r.data);
51148
- }
51149
- return out;
51150
- }
51151
- flush() {
51152
- const content = { version: FILE_VERSION, capabilities: this.cache };
51153
- this.atomicWrite(this.filePath(), JSON.stringify(content, null, 2));
51154
- }
51155
- atomicWrite(file, content) {
51156
- const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
51157
- fs32.writeFileSync(tmp, content, { mode: 384 });
51158
- fs32.renameSync(tmp, file);
51159
- try {
51160
- fs32.chmodSync(file, 384);
51161
- } catch {
51162
- }
51163
- }
51164
- };
51165
-
51166
- // src/permission/capability-registry.ts
51167
- var crypto5 = __toESM(require("crypto"), 1);
51168
- var CapabilityRegistry = class {
51169
- constructor(store, now = () => Date.now()) {
51170
- this.store = store;
51171
- this.now = now;
51172
- for (const cap of store.list()) {
51173
- this.bySecretHash.set(cap.secretHash, cap);
51174
- }
51175
- }
51176
- store;
51177
- now;
51178
- // sha256(token) → Capability
51179
- bySecretHash = /* @__PURE__ */ new Map();
51180
- list() {
51181
- return this.store.list();
51182
- }
51183
- verifyToken(token) {
51184
- if (!token) return { ok: false, code: "TOKEN_INVALID" };
51185
- const hash = sha256Hex(token);
51186
- const cap = this.bySecretHash.get(hash);
51187
- if (!cap) return { ok: false, code: "TOKEN_INVALID" };
51188
- if (cap.expiresAt && this.now() >= cap.expiresAt) {
51189
- return { ok: false, code: "TOKEN_EXPIRED" };
51190
- }
51191
- if (cap.maxUses && cap.usedCount >= cap.maxUses) {
51192
- return { ok: false, code: "TOKEN_EXHAUSTED" };
51193
- }
51194
- const updated = { ...cap, usedCount: cap.usedCount + 1 };
51195
- this.bySecretHash.set(hash, updated);
51196
- this.store.upsert(updated);
51197
- return { ok: true, capability: updated };
51198
- }
51199
- upsertCapability(cap) {
51200
- this.bySecretHash.set(cap.secretHash, cap);
51201
- this.store.upsert(cap);
51202
- }
51203
- /**
51204
- * Hard delete:从 store 物理移除 + 从 Map 清掉 secretHash 索引。
51205
- * 后续 verifyToken 该 token 走 'TOKEN_INVALID' 分支(secretHash 已不在 Map)。
51206
- * idempotent:不存在 → null。
51207
- */
51208
- delete(id) {
51209
- const current = this.store.list().find((c) => c.id === id);
51210
- if (!current) return null;
51211
- this.bySecretHash.delete(current.secretHash);
51212
- this.store.remove(id);
51213
- return current;
51214
- }
51215
- findById(id) {
51216
- return this.store.list().find((c) => c.id === id) ?? null;
51217
- }
51218
- };
51219
- function sha256Hex(s) {
51220
- return crypto5.createHash("sha256").update(s).digest("hex");
51221
- }
51222
-
51223
- // src/permission/capability-manager.ts
51224
- var CapabilityManager = class {
51225
- constructor(registry2, hooks = {}) {
51226
- this.registry = registry2;
51227
- this.hooks = hooks;
51228
- }
51229
- registry;
51230
- hooks;
51231
- list() {
51232
- return this.registry.list();
51233
- }
51234
- // 调试 / 旧 per-peer cap 反查用(Phase 3 决策 #14 后无生产调用方,保留属三件套范围)。
51235
- // 返回 Capability 持久化形态;调用方需 stripSecretHash 后再上 wire。
51236
- findById(id) {
51237
- return this.registry.findById(id);
51238
- }
51239
- /**
51240
- * Hard delete:从 registry / store 物理移除 capability。
51241
- * idempotent:不存在 → null(handler 翻译成 VALIDATION_ERROR)
51242
- * onDeleted hook 在 daemon/index.ts wire 负责 close 连接 + cleanup 文件 + 广播
51243
- */
51244
- delete(id) {
51245
- const removed = this.registry.delete(id);
51246
- if (!removed) return null;
51247
- this.hooks.onDeleted?.(removed);
51248
- return { capability: removed };
51249
- }
51250
- };
51251
-
51252
- // src/permission/cleanup.ts
51253
- var fs33 = __toESM(require("fs"), 1);
51254
- function cleanupGuestSessionsForCapability(cap, factory) {
51205
+ // src/legacy-cleanup.ts
51206
+ var fs33 = __toESM(require("fs/promises"), 1);
51207
+ var path34 = __toESM(require("path"), 1);
51208
+ var LEGACY_FILES = [
51209
+ "personal-capability.json",
51210
+ "received-capabilities.json",
51211
+ "capabilities.json"
51212
+ ];
51213
+ async function cleanupLegacyCapabilityFiles(dataDir, logger) {
51255
51214
  const removed = [];
51256
- for (const g2 of cap.grants) {
51257
- if (g2.resource.type !== "persona") continue;
51258
- const dir = factory.vmGuestRoot(g2.resource.id, cap.id);
51215
+ for (const name of LEGACY_FILES) {
51216
+ const p2 = path34.join(dataDir, name);
51259
51217
  try {
51260
- fs33.rmSync(dir, { recursive: true, force: true });
51261
- removed.push(dir);
51262
- } catch {
51218
+ await fs33.unlink(p2);
51219
+ removed.push(name);
51220
+ logger.debug("legacy capability file removed", { file: name });
51221
+ } catch (err) {
51222
+ if (err?.code === "ENOENT") continue;
51223
+ throw err;
51263
51224
  }
51264
51225
  }
51265
51226
  return { removed };
@@ -51267,7 +51228,7 @@ function cleanupGuestSessionsForCapability(cap, factory) {
51267
51228
 
51268
51229
  // src/inbox/inbox-store.ts
51269
51230
  var fs34 = __toESM(require("fs"), 1);
51270
- var path32 = __toESM(require("path"), 1);
51231
+ var path35 = __toESM(require("path"), 1);
51271
51232
  var INBOX_SUBDIR = "inbox";
51272
51233
  var InboxStore = class {
51273
51234
  constructor(dataDir) {
@@ -51365,10 +51326,10 @@ var InboxStore = class {
51365
51326
  }
51366
51327
  }
51367
51328
  dirPath() {
51368
- return path32.join(this.dataDir, INBOX_SUBDIR);
51329
+ return path35.join(this.dataDir, INBOX_SUBDIR);
51369
51330
  }
51370
51331
  filePath(peerDeviceId) {
51371
- return path32.join(this.dirPath(), `${peerDeviceId}.jsonl`);
51332
+ return path35.join(this.dirPath(), `${peerDeviceId}.jsonl`);
51372
51333
  }
51373
51334
  };
51374
51335
  function parseAllLines(raw) {
@@ -51457,7 +51418,7 @@ var InboxManager = class {
51457
51418
 
51458
51419
  // src/state/contact-store.ts
51459
51420
  var fs35 = __toESM(require("fs"), 1);
51460
- var path33 = __toESM(require("path"), 1);
51421
+ var path36 = __toESM(require("path"), 1);
51461
51422
  var FILE_NAME = "contacts.json";
51462
51423
  var ContactStore = class {
51463
51424
  constructor(dataDir) {
@@ -51467,7 +51428,7 @@ var ContactStore = class {
51467
51428
  contacts = /* @__PURE__ */ new Map();
51468
51429
  load() {
51469
51430
  this.contacts.clear();
51470
- const file = path33.join(this.dataDir, FILE_NAME);
51431
+ const file = path36.join(this.dataDir, FILE_NAME);
51471
51432
  let raw;
51472
51433
  try {
51473
51434
  raw = fs35.readFileSync(file, "utf8");
@@ -51539,7 +51500,7 @@ var ContactStore = class {
51539
51500
  return true;
51540
51501
  }
51541
51502
  flush() {
51542
- const file = path33.join(this.dataDir, FILE_NAME);
51503
+ const file = path36.join(this.dataDir, FILE_NAME);
51543
51504
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
51544
51505
  const content = JSON.stringify(
51545
51506
  { contacts: Array.from(this.contacts.values()) },
@@ -51559,7 +51520,7 @@ var ContactStore = class {
51559
51520
  };
51560
51521
 
51561
51522
  // src/contact/connect-remote.ts
51562
- var crypto6 = __toESM(require("crypto"), 1);
51523
+ var crypto5 = __toESM(require("crypto"), 1);
51563
51524
  var HANDSHAKE_TIMEOUT_MS = 5e3;
51564
51525
  var RPC_TIMEOUT_MS = 5e3;
51565
51526
  async function connectRemote(args) {
@@ -51623,7 +51584,7 @@ async function connectRemote(args) {
51623
51584
  ws.once("close", onClose);
51624
51585
  });
51625
51586
  function call(method, payload) {
51626
- const requestId = crypto6.randomUUID();
51587
+ const requestId = crypto5.randomUUID();
51627
51588
  return new Promise((resolve6, reject) => {
51628
51589
  const onMessage = (raw) => {
51629
51590
  let f;
@@ -51706,52 +51667,52 @@ async function autoReverseContact(args) {
51706
51667
 
51707
51668
  // src/migrations/2026-05-20-flatten-sessions.ts
51708
51669
  var fs36 = __toESM(require("fs"), 1);
51709
- var path34 = __toESM(require("path"), 1);
51670
+ var path37 = __toESM(require("path"), 1);
51710
51671
  var MIGRATION_FLAG_NAME = ".migration.v1.done";
51711
51672
  function migrateFlattenSessions(opts) {
51712
51673
  const dataDir = opts.dataDir;
51713
51674
  const now = opts.now ?? Date.now;
51714
- const sessionsDir = path34.join(dataDir, "sessions");
51715
- const flagPath = path34.join(sessionsDir, MIGRATION_FLAG_NAME);
51675
+ const sessionsDir = path37.join(dataDir, "sessions");
51676
+ const flagPath = path37.join(sessionsDir, MIGRATION_FLAG_NAME);
51716
51677
  if (existsSync3(flagPath)) {
51717
51678
  return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
51718
51679
  }
51719
51680
  let movedBare = 0;
51720
51681
  let movedVmOwner = 0;
51721
51682
  let archivedListener = 0;
51722
- const defaultDir = path34.join(sessionsDir, "default");
51683
+ const defaultDir = path37.join(sessionsDir, "default");
51723
51684
  if (existsSync3(defaultDir)) {
51724
51685
  for (const entry of readdirSafe(defaultDir)) {
51725
51686
  if (!entry.endsWith(".json")) continue;
51726
- const src = path34.join(defaultDir, entry);
51727
- const dst = path34.join(sessionsDir, entry);
51687
+ const src = path37.join(defaultDir, entry);
51688
+ const dst = path37.join(sessionsDir, entry);
51728
51689
  fs36.renameSync(src, dst);
51729
51690
  movedBare += 1;
51730
51691
  }
51731
51692
  rmdirIfEmpty(defaultDir);
51732
51693
  }
51733
51694
  for (const pid of readdirSafe(sessionsDir)) {
51734
- const personaDir = path34.join(sessionsDir, pid);
51695
+ const personaDir = path37.join(sessionsDir, pid);
51735
51696
  if (!isDir(personaDir)) continue;
51736
51697
  if (pid === "default") continue;
51737
- const ownerSrc = path34.join(personaDir, "owner");
51698
+ const ownerSrc = path37.join(personaDir, "owner");
51738
51699
  if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
51739
- const ownerDst = path34.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
51700
+ const ownerDst = path37.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
51740
51701
  fs36.mkdirSync(ownerDst, { recursive: true });
51741
51702
  for (const file of readdirSafe(ownerSrc)) {
51742
51703
  if (!file.endsWith(".json")) continue;
51743
- fs36.renameSync(path34.join(ownerSrc, file), path34.join(ownerDst, file));
51704
+ fs36.renameSync(path37.join(ownerSrc, file), path37.join(ownerDst, file));
51744
51705
  movedVmOwner += 1;
51745
51706
  }
51746
51707
  rmdirIfEmpty(ownerSrc);
51747
51708
  }
51748
- const listenerSrc = path34.join(personaDir, "listener");
51709
+ const listenerSrc = path37.join(personaDir, "listener");
51749
51710
  if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
51750
- const archiveDst = path34.join(dataDir, ".legacy", `listener-${pid}`);
51711
+ const archiveDst = path37.join(dataDir, ".legacy", `listener-${pid}`);
51751
51712
  fs36.mkdirSync(archiveDst, { recursive: true });
51752
51713
  for (const file of readdirSafe(listenerSrc)) {
51753
51714
  if (!file.endsWith(".json")) continue;
51754
- fs36.renameSync(path34.join(listenerSrc, file), path34.join(archiveDst, file));
51715
+ fs36.renameSync(path37.join(listenerSrc, file), path37.join(archiveDst, file));
51755
51716
  archivedListener += 1;
51756
51717
  }
51757
51718
  rmdirIfEmpty(listenerSrc);
@@ -51798,11 +51759,11 @@ function rmdirIfEmpty(p2) {
51798
51759
  }
51799
51760
 
51800
51761
  // src/transport/http-router.ts
51801
- var import_node_fs29 = __toESM(require("fs"), 1);
51802
- var import_node_path30 = __toESM(require("path"), 1);
51762
+ var import_node_fs30 = __toESM(require("fs"), 1);
51763
+ var import_node_path33 = __toESM(require("path"), 1);
51803
51764
 
51804
51765
  // src/attachment/mime.ts
51805
- var import_node_path27 = __toESM(require("path"), 1);
51766
+ var import_node_path30 = __toESM(require("path"), 1);
51806
51767
  var TEXT_PLAIN = "text/plain; charset=utf-8";
51807
51768
  var EXT_TO_NATIVE_MIME = {
51808
51769
  // 图片
@@ -51909,7 +51870,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
51909
51870
  ".mk"
51910
51871
  ]);
51911
51872
  function lookupMime(filePathOrName) {
51912
- const ext = import_node_path27.default.extname(filePathOrName).toLowerCase();
51873
+ const ext = import_node_path30.default.extname(filePathOrName).toLowerCase();
51913
51874
  if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
51914
51875
  if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
51915
51876
  return "application/octet-stream";
@@ -51978,8 +51939,8 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
51978
51939
  }
51979
51940
 
51980
51941
  // src/attachment/upload.ts
51981
- var import_node_fs28 = __toESM(require("fs"), 1);
51982
- var import_node_path28 = __toESM(require("path"), 1);
51942
+ var import_node_fs29 = __toESM(require("fs"), 1);
51943
+ var import_node_path31 = __toESM(require("path"), 1);
51983
51944
  var import_node_crypto8 = __toESM(require("crypto"), 1);
51984
51945
  var import_promises3 = require("stream/promises");
51985
51946
  var UploadError = class extends Error {
@@ -51991,22 +51952,22 @@ var UploadError = class extends Error {
51991
51952
  code;
51992
51953
  };
51993
51954
  function assertValidFileName(fileName) {
51994
- if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path28.default.basename(fileName)) {
51955
+ if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path31.default.basename(fileName)) {
51995
51956
  throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
51996
51957
  }
51997
51958
  }
51998
51959
  var HASH_PREFIX_LEN = 16;
51999
51960
  async function writeUploadedAttachment(args) {
52000
51961
  assertValidFileName(args.fileName);
52001
- const attachmentsRoot = import_node_path28.default.join(args.sessionDir, ".attachments");
51962
+ const attachmentsRoot = import_node_path31.default.join(args.sessionDir, ".attachments");
52002
51963
  try {
52003
- import_node_fs28.default.mkdirSync(attachmentsRoot, { recursive: true });
51964
+ import_node_fs29.default.mkdirSync(attachmentsRoot, { recursive: true });
52004
51965
  } catch (err) {
52005
51966
  throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
52006
51967
  }
52007
51968
  const hasher = import_node_crypto8.default.createHash("sha256");
52008
51969
  let actualSize = 0;
52009
- const tmpPath = import_node_path28.default.join(
51970
+ const tmpPath = import_node_path31.default.join(
52010
51971
  attachmentsRoot,
52011
51972
  `.upload-${process.pid}-${Date.now()}-${import_node_crypto8.default.randomBytes(4).toString("hex")}`
52012
51973
  );
@@ -52021,18 +51982,18 @@ async function writeUploadedAttachment(args) {
52021
51982
  yield buf;
52022
51983
  }
52023
51984
  },
52024
- import_node_fs28.default.createWriteStream(tmpPath, { mode: 384 })
51985
+ import_node_fs29.default.createWriteStream(tmpPath, { mode: 384 })
52025
51986
  );
52026
51987
  } catch (err) {
52027
51988
  try {
52028
- import_node_fs28.default.unlinkSync(tmpPath);
51989
+ import_node_fs29.default.unlinkSync(tmpPath);
52029
51990
  } catch {
52030
51991
  }
52031
51992
  throw new UploadError("STORAGE_ERROR", `write failed: ${err.message}`);
52032
51993
  }
52033
51994
  if (actualSize !== args.contentLength) {
52034
51995
  try {
52035
- import_node_fs28.default.unlinkSync(tmpPath);
51996
+ import_node_fs29.default.unlinkSync(tmpPath);
52036
51997
  } catch {
52037
51998
  }
52038
51999
  throw new UploadError(
@@ -52041,35 +52002,35 @@ async function writeUploadedAttachment(args) {
52041
52002
  );
52042
52003
  }
52043
52004
  const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
52044
- const hashDir = import_node_path28.default.join(attachmentsRoot, attachmentId);
52005
+ const hashDir = import_node_path31.default.join(attachmentsRoot, attachmentId);
52045
52006
  let finalFileName;
52046
52007
  let hashDirExists = false;
52047
52008
  try {
52048
- hashDirExists = import_node_fs28.default.statSync(hashDir).isDirectory();
52009
+ hashDirExists = import_node_fs29.default.statSync(hashDir).isDirectory();
52049
52010
  } catch {
52050
52011
  }
52051
52012
  if (hashDirExists) {
52052
- const existing = import_node_fs28.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
52013
+ const existing = import_node_fs29.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
52053
52014
  finalFileName = existing[0] ?? args.fileName;
52054
52015
  try {
52055
- import_node_fs28.default.unlinkSync(tmpPath);
52016
+ import_node_fs29.default.unlinkSync(tmpPath);
52056
52017
  } catch {
52057
52018
  }
52058
52019
  } else {
52059
52020
  try {
52060
- import_node_fs28.default.mkdirSync(hashDir, { recursive: true });
52021
+ import_node_fs29.default.mkdirSync(hashDir, { recursive: true });
52061
52022
  finalFileName = args.fileName;
52062
- import_node_fs28.default.renameSync(tmpPath, import_node_path28.default.join(hashDir, finalFileName));
52023
+ import_node_fs29.default.renameSync(tmpPath, import_node_path31.default.join(hashDir, finalFileName));
52063
52024
  } catch (err) {
52064
52025
  try {
52065
- import_node_fs28.default.unlinkSync(tmpPath);
52026
+ import_node_fs29.default.unlinkSync(tmpPath);
52066
52027
  } catch {
52067
52028
  }
52068
52029
  throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
52069
52030
  }
52070
52031
  }
52071
- const absPath = import_node_path28.default.join(hashDir, finalFileName);
52072
- const relPath = import_node_path28.default.relative(args.sessionDir, absPath);
52032
+ const absPath = import_node_path31.default.join(hashDir, finalFileName);
52033
+ const relPath = import_node_path31.default.relative(args.sessionDir, absPath);
52073
52034
  args.groupFileStore.upsert(args.scope, args.sessionId, {
52074
52035
  relPath: absPath,
52075
52036
  // 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
@@ -52083,7 +52044,7 @@ async function writeUploadedAttachment(args) {
52083
52044
 
52084
52045
  // src/extension/import.ts
52085
52046
  var import_promises4 = __toESM(require("fs/promises"), 1);
52086
- var import_node_path29 = __toESM(require("path"), 1);
52047
+ var import_node_path32 = __toESM(require("path"), 1);
52087
52048
  var import_node_os12 = __toESM(require("os"), 1);
52088
52049
  var import_jszip = __toESM(require_lib3(), 1);
52089
52050
  var ImportError = class extends Error {
@@ -52101,7 +52062,7 @@ async function importZip(buf, root) {
52101
52062
  throw new ImportError("ZIP_INVALID", `failed to load zip: ${e.message}`);
52102
52063
  }
52103
52064
  for (const name of Object.keys(zip.files)) {
52104
- if (name.includes("..") || name.startsWith("/") || import_node_path29.default.isAbsolute(name)) {
52065
+ if (name.includes("..") || name.startsWith("/") || import_node_path32.default.isAbsolute(name)) {
52105
52066
  throw new ImportError("ZIP_INVALID", `unsafe zip entry path: ${name}`);
52106
52067
  }
52107
52068
  }
@@ -52134,7 +52095,7 @@ async function importZip(buf, root) {
52134
52095
  );
52135
52096
  }
52136
52097
  }
52137
- const destDir = import_node_path29.default.join(root, manifest.id);
52098
+ const destDir = import_node_path32.default.join(root, manifest.id);
52138
52099
  let destExists = false;
52139
52100
  try {
52140
52101
  await import_promises4.default.access(destDir);
@@ -52144,15 +52105,15 @@ async function importZip(buf, root) {
52144
52105
  if (destExists) {
52145
52106
  throw new ImportError("ALREADY_EXISTS", `extension ${manifest.id} already installed`);
52146
52107
  }
52147
- const stage = await import_promises4.default.mkdtemp(import_node_path29.default.join(import_node_os12.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
52108
+ const stage = await import_promises4.default.mkdtemp(import_node_path32.default.join(import_node_os12.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
52148
52109
  try {
52149
52110
  for (const [name, entry] of Object.entries(zip.files)) {
52150
- const dest = import_node_path29.default.join(stage, name);
52111
+ const dest = import_node_path32.default.join(stage, name);
52151
52112
  if (entry.dir) {
52152
52113
  await import_promises4.default.mkdir(dest, { recursive: true });
52153
52114
  continue;
52154
52115
  }
52155
- await import_promises4.default.mkdir(import_node_path29.default.dirname(dest), { recursive: true });
52116
+ await import_promises4.default.mkdir(import_node_path32.default.dirname(dest), { recursive: true });
52156
52117
  const content = await entry.async("nodebuffer");
52157
52118
  await import_promises4.default.writeFile(dest, content);
52158
52119
  }
@@ -52183,7 +52144,7 @@ var SHARE_UI_ASSET_MIME = {
52183
52144
  ".wasm": "application/wasm"
52184
52145
  };
52185
52146
  function shareUiAssetMime(filePath) {
52186
- const ext = import_node_path30.default.extname(filePath).toLowerCase();
52147
+ const ext = import_node_path33.default.extname(filePath).toLowerCase();
52187
52148
  return SHARE_UI_ASSET_MIME[ext] ?? lookupMime(filePath);
52188
52149
  }
52189
52150
  var DISPATCH_HEARTBEAT_MS = 25e3;
@@ -52270,7 +52231,7 @@ function isValidUploadFileName(fileName) {
52270
52231
  if (fileName === "." || fileName === "..") return false;
52271
52232
  if (fileName.startsWith(".")) return false;
52272
52233
  if (fileName.includes("/") || fileName.includes("\\")) return false;
52273
- return fileName === import_node_path30.default.basename(fileName);
52234
+ return fileName === import_node_path33.default.basename(fileName);
52274
52235
  }
52275
52236
  function createHttpRouter(deps) {
52276
52237
  return async (req, res) => {
@@ -52342,7 +52303,7 @@ function createHttpRouter(deps) {
52342
52303
  }
52343
52304
  let buf;
52344
52305
  try {
52345
- buf = await import_node_fs29.default.promises.readFile(abs);
52306
+ buf = await import_node_fs30.default.promises.readFile(abs);
52346
52307
  } catch {
52347
52308
  sendJson(res, 404, { code: "NOT_FOUND", message: "asset not found" });
52348
52309
  return true;
@@ -52566,7 +52527,7 @@ function createHttpRouter(deps) {
52566
52527
  sendHtml(res, statusByCode[r.code], loader.renderErrorHtml(r.code, msgByCode[r.code]));
52567
52528
  return true;
52568
52529
  }
52569
- sendHtml(res, 200, loader.renderViewerHtml(import_node_path30.default.basename(r.absPath)));
52530
+ sendHtml(res, 200, loader.renderViewerHtml(import_node_path33.default.basename(r.absPath)));
52570
52531
  return true;
52571
52532
  }
52572
52533
  const ctx = deps.authResolver.resolveFromHeader(
@@ -52687,7 +52648,7 @@ function createHttpRouter(deps) {
52687
52648
  return true;
52688
52649
  }
52689
52650
  let absPath;
52690
- if (import_node_path30.default.isAbsolute(pathParam)) {
52651
+ if (import_node_path33.default.isAbsolute(pathParam)) {
52691
52652
  absPath = pathParam;
52692
52653
  } else if (deps.sessionStore) {
52693
52654
  const file = deps.sessionStore.read(sid);
@@ -52695,7 +52656,7 @@ function createHttpRouter(deps) {
52695
52656
  sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sid} not found` });
52696
52657
  return true;
52697
52658
  }
52698
- absPath = import_node_path30.default.join(file.cwd, pathParam);
52659
+ absPath = import_node_path33.default.join(file.cwd, pathParam);
52699
52660
  } else {
52700
52661
  sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "sessionStore not wired" }));
52701
52662
  return true;
@@ -52782,7 +52743,7 @@ function withCtx(ctx, body) {
52782
52743
  function streamFile(res, absPath, logger) {
52783
52744
  let stat;
52784
52745
  try {
52785
- stat = import_node_fs29.default.statSync(absPath);
52746
+ stat = import_node_fs30.default.statSync(absPath);
52786
52747
  } catch (err) {
52787
52748
  const code = err?.code;
52788
52749
  if (code === "ENOENT") {
@@ -52797,7 +52758,7 @@ function streamFile(res, absPath, logger) {
52797
52758
  return;
52798
52759
  }
52799
52760
  const mime = lookupMime(absPath);
52800
- const basename = import_node_path30.default.basename(absPath);
52761
+ const basename = import_node_path33.default.basename(absPath);
52801
52762
  res.writeHead(200, {
52802
52763
  "Content-Type": mime,
52803
52764
  "Content-Length": String(stat.size),
@@ -52805,7 +52766,7 @@ function streamFile(res, absPath, logger) {
52805
52766
  // 防止浏览器把任意 mime 当 html 渲染
52806
52767
  "X-Content-Type-Options": "nosniff"
52807
52768
  });
52808
- const stream = import_node_fs29.default.createReadStream(absPath);
52769
+ const stream = import_node_fs30.default.createReadStream(absPath);
52809
52770
  stream.on("error", (err) => {
52810
52771
  logger?.warn("streamFile read error", { absPath, err: err.message });
52811
52772
  res.destroy();
@@ -52814,8 +52775,8 @@ function streamFile(res, absPath, logger) {
52814
52775
  }
52815
52776
 
52816
52777
  // src/attachment/gc.ts
52817
- var import_node_fs30 = __toESM(require("fs"), 1);
52818
- var import_node_path31 = __toESM(require("path"), 1);
52778
+ var import_node_fs31 = __toESM(require("fs"), 1);
52779
+ var import_node_path34 = __toESM(require("path"), 1);
52819
52780
  var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
52820
52781
  function runAttachmentGc(args) {
52821
52782
  const now = (args.now ?? Date.now)();
@@ -52824,38 +52785,38 @@ function runAttachmentGc(args) {
52824
52785
  for (const { scope, sessionId } of args.sessionScopes) {
52825
52786
  for (const entry of args.groupFileStore.list(scope, sessionId)) {
52826
52787
  if (entry.stale) continue;
52827
- if (import_node_path31.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
52788
+ if (import_node_path34.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
52828
52789
  }
52829
52790
  }
52830
52791
  for (const { scope, sessionId } of args.sessionScopes) {
52831
- const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path31.default.join(
52792
+ const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path34.default.join(
52832
52793
  args.dataDir,
52833
52794
  "sessions",
52834
52795
  ...scopeSubPath(scope).map(safeFileName),
52835
52796
  safeFileName(sessionId)
52836
52797
  );
52837
- const attRoot = import_node_path31.default.join(sessionDir, ".attachments");
52798
+ const attRoot = import_node_path34.default.join(sessionDir, ".attachments");
52838
52799
  let hashDirs;
52839
52800
  try {
52840
- hashDirs = import_node_fs30.default.readdirSync(attRoot);
52801
+ hashDirs = import_node_fs31.default.readdirSync(attRoot);
52841
52802
  } catch (err) {
52842
52803
  if (err.code === "ENOENT") continue;
52843
52804
  args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
52844
52805
  continue;
52845
52806
  }
52846
52807
  for (const hashDir of hashDirs) {
52847
- const hashDirAbs = import_node_path31.default.join(attRoot, hashDir);
52808
+ const hashDirAbs = import_node_path34.default.join(attRoot, hashDir);
52848
52809
  let files;
52849
52810
  try {
52850
- files = import_node_fs30.default.readdirSync(hashDirAbs);
52811
+ files = import_node_fs31.default.readdirSync(hashDirAbs);
52851
52812
  } catch {
52852
52813
  continue;
52853
52814
  }
52854
52815
  for (const name of files) {
52855
- const file = import_node_path31.default.join(hashDirAbs, name);
52816
+ const file = import_node_path34.default.join(hashDirAbs, name);
52856
52817
  let stat;
52857
52818
  try {
52858
- stat = import_node_fs30.default.statSync(file);
52819
+ stat = import_node_fs31.default.statSync(file);
52859
52820
  } catch {
52860
52821
  continue;
52861
52822
  }
@@ -52864,26 +52825,26 @@ function runAttachmentGc(args) {
52864
52825
  if (age < ttlMs) continue;
52865
52826
  if (liveAbs.has(file)) continue;
52866
52827
  try {
52867
- import_node_fs30.default.unlinkSync(file);
52828
+ import_node_fs31.default.unlinkSync(file);
52868
52829
  } catch (err) {
52869
52830
  args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
52870
52831
  }
52871
52832
  }
52872
52833
  try {
52873
- if (import_node_fs30.default.readdirSync(hashDirAbs).length === 0) import_node_fs30.default.rmdirSync(hashDirAbs);
52834
+ if (import_node_fs31.default.readdirSync(hashDirAbs).length === 0) import_node_fs31.default.rmdirSync(hashDirAbs);
52874
52835
  } catch {
52875
52836
  }
52876
52837
  }
52877
52838
  try {
52878
- if (import_node_fs30.default.readdirSync(attRoot).length === 0) import_node_fs30.default.rmdirSync(attRoot);
52839
+ if (import_node_fs31.default.readdirSync(attRoot).length === 0) import_node_fs31.default.rmdirSync(attRoot);
52879
52840
  } catch {
52880
52841
  }
52881
52842
  }
52882
52843
  }
52883
52844
 
52884
52845
  // src/attachment/group.ts
52885
- var import_node_fs31 = __toESM(require("fs"), 1);
52886
- var import_node_path32 = __toESM(require("path"), 1);
52846
+ var import_node_fs32 = __toESM(require("fs"), 1);
52847
+ var import_node_path35 = __toESM(require("path"), 1);
52887
52848
  var import_node_crypto9 = __toESM(require("crypto"), 1);
52888
52849
  init_protocol();
52889
52850
  var GroupFileStore = class {
@@ -52895,11 +52856,11 @@ var GroupFileStore = class {
52895
52856
  this.logger = opts.logger;
52896
52857
  }
52897
52858
  rootForScope(scope) {
52898
- return import_node_path32.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
52859
+ return import_node_path35.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
52899
52860
  }
52900
52861
  /** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
52901
52862
  filePath(scope, sessionId) {
52902
- return import_node_path32.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
52863
+ return import_node_path35.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
52903
52864
  }
52904
52865
  cacheKey(scope, sessionId) {
52905
52866
  return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
@@ -52908,7 +52869,7 @@ var GroupFileStore = class {
52908
52869
  readFile(scope, sessionId) {
52909
52870
  const file = this.filePath(scope, sessionId);
52910
52871
  try {
52911
- const raw = import_node_fs31.default.readFileSync(file, "utf8");
52872
+ const raw = import_node_fs32.default.readFileSync(file, "utf8");
52912
52873
  const parsed = JSON.parse(raw);
52913
52874
  if (!Array.isArray(parsed)) {
52914
52875
  this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
@@ -52934,10 +52895,10 @@ var GroupFileStore = class {
52934
52895
  }
52935
52896
  writeFile(scope, sessionId, entries) {
52936
52897
  const file = this.filePath(scope, sessionId);
52937
- import_node_fs31.default.mkdirSync(import_node_path32.default.dirname(file), { recursive: true });
52898
+ import_node_fs32.default.mkdirSync(import_node_path35.default.dirname(file), { recursive: true });
52938
52899
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
52939
- import_node_fs31.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
52940
- import_node_fs31.default.renameSync(tmp, file);
52900
+ import_node_fs32.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
52901
+ import_node_fs32.default.renameSync(tmp, file);
52941
52902
  }
52942
52903
  /** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
52943
52904
  list(scope, sessionId) {
@@ -53023,10 +52984,10 @@ var GroupFileStore = class {
53023
52984
  };
53024
52985
 
53025
52986
  // src/discovery/state-file.ts
53026
- var import_node_fs32 = __toESM(require("fs"), 1);
53027
- var import_node_path33 = __toESM(require("path"), 1);
52987
+ var import_node_fs33 = __toESM(require("fs"), 1);
52988
+ var import_node_path36 = __toESM(require("path"), 1);
53028
52989
  function defaultStateFilePath(dataDir) {
53029
- return import_node_path33.default.join(dataDir, "state.json");
52990
+ return import_node_path36.default.join(dataDir, "state.json");
53030
52991
  }
53031
52992
  function isPidAlive(pid) {
53032
52993
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -53048,7 +53009,7 @@ var StateFileManager = class {
53048
53009
  }
53049
53010
  read() {
53050
53011
  try {
53051
- const raw = import_node_fs32.default.readFileSync(this.file, "utf8");
53012
+ const raw = import_node_fs33.default.readFileSync(this.file, "utf8");
53052
53013
  const parsed = JSON.parse(raw);
53053
53014
  return parsed;
53054
53015
  } catch {
@@ -53062,20 +53023,20 @@ var StateFileManager = class {
53062
53023
  return { status: "stale", existing };
53063
53024
  }
53064
53025
  write(state) {
53065
- import_node_fs32.default.mkdirSync(import_node_path33.default.dirname(this.file), { recursive: true });
53026
+ import_node_fs33.default.mkdirSync(import_node_path36.default.dirname(this.file), { recursive: true });
53066
53027
  const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
53067
- import_node_fs32.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
53068
- import_node_fs32.default.renameSync(tmp, this.file);
53028
+ import_node_fs33.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
53029
+ import_node_fs33.default.renameSync(tmp, this.file);
53069
53030
  if (process.platform !== "win32") {
53070
53031
  try {
53071
- import_node_fs32.default.chmodSync(this.file, 384);
53032
+ import_node_fs33.default.chmodSync(this.file, 384);
53072
53033
  } catch {
53073
53034
  }
53074
53035
  }
53075
53036
  }
53076
53037
  delete() {
53077
53038
  try {
53078
- import_node_fs32.default.unlinkSync(this.file);
53039
+ import_node_fs33.default.unlinkSync(this.file);
53079
53040
  } catch {
53080
53041
  }
53081
53042
  }
@@ -53089,14 +53050,14 @@ function readSpawnedByDesktopFromEnv(env = process.env) {
53089
53050
  }
53090
53051
 
53091
53052
  // src/tunnel/tunnel-manager.ts
53092
- var import_node_fs36 = __toESM(require("fs"), 1);
53093
- var import_node_path37 = __toESM(require("path"), 1);
53053
+ var import_node_fs37 = __toESM(require("fs"), 1);
53054
+ var import_node_path40 = __toESM(require("path"), 1);
53094
53055
  var import_node_crypto10 = __toESM(require("crypto"), 1);
53095
- var import_node_child_process9 = require("child_process");
53056
+ var import_node_child_process10 = require("child_process");
53096
53057
 
53097
53058
  // src/tunnel/tunnel-store.ts
53098
- var import_node_fs33 = __toESM(require("fs"), 1);
53099
- var import_node_path34 = __toESM(require("path"), 1);
53059
+ var import_node_fs34 = __toESM(require("fs"), 1);
53060
+ var import_node_path37 = __toESM(require("path"), 1);
53100
53061
  var TunnelStore = class {
53101
53062
  constructor(filePath) {
53102
53063
  this.filePath = filePath;
@@ -53104,7 +53065,7 @@ var TunnelStore = class {
53104
53065
  filePath;
53105
53066
  async get() {
53106
53067
  try {
53107
- const raw = await import_node_fs33.default.promises.readFile(this.filePath, "utf8");
53068
+ const raw = await import_node_fs34.default.promises.readFile(this.filePath, "utf8");
53108
53069
  const obj = JSON.parse(raw);
53109
53070
  if (!isPersistedTunnel(obj)) return null;
53110
53071
  return obj;
@@ -53115,22 +53076,22 @@ var TunnelStore = class {
53115
53076
  }
53116
53077
  }
53117
53078
  async set(v2) {
53118
- const dir = import_node_path34.default.dirname(this.filePath);
53119
- await import_node_fs33.default.promises.mkdir(dir, { recursive: true });
53079
+ const dir = import_node_path37.default.dirname(this.filePath);
53080
+ await import_node_fs34.default.promises.mkdir(dir, { recursive: true });
53120
53081
  const data = JSON.stringify(v2, null, 2);
53121
53082
  const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
53122
- await import_node_fs33.default.promises.writeFile(tmp, data, { mode: 384 });
53083
+ await import_node_fs34.default.promises.writeFile(tmp, data, { mode: 384 });
53123
53084
  if (process.platform !== "win32") {
53124
53085
  try {
53125
- await import_node_fs33.default.promises.chmod(tmp, 384);
53086
+ await import_node_fs34.default.promises.chmod(tmp, 384);
53126
53087
  } catch {
53127
53088
  }
53128
53089
  }
53129
- await import_node_fs33.default.promises.rename(tmp, this.filePath);
53090
+ await import_node_fs34.default.promises.rename(tmp, this.filePath);
53130
53091
  }
53131
53092
  async clear() {
53132
53093
  try {
53133
- await import_node_fs33.default.promises.unlink(this.filePath);
53094
+ await import_node_fs34.default.promises.unlink(this.filePath);
53134
53095
  } catch (err) {
53135
53096
  const code = err?.code;
53136
53097
  if (code !== "ENOENT") throw err;
@@ -53225,10 +53186,10 @@ function escape(v2) {
53225
53186
  }
53226
53187
 
53227
53188
  // src/tunnel/frpc-binary.ts
53228
- var import_node_fs34 = __toESM(require("fs"), 1);
53189
+ var import_node_fs35 = __toESM(require("fs"), 1);
53229
53190
  var import_node_os13 = __toESM(require("os"), 1);
53230
- var import_node_path35 = __toESM(require("path"), 1);
53231
- var import_node_child_process7 = require("child_process");
53191
+ var import_node_path38 = __toESM(require("path"), 1);
53192
+ var import_node_child_process8 = require("child_process");
53232
53193
  var import_node_stream3 = require("stream");
53233
53194
  var import_promises5 = require("stream/promises");
53234
53195
  var FRPC_VERSION = "0.68.0";
@@ -53259,20 +53220,20 @@ function frpcDownloadUrl(version2, p2) {
53259
53220
  }
53260
53221
  async function ensureFrpcBinary(opts) {
53261
53222
  if (opts.override) {
53262
- if (!import_node_fs34.default.existsSync(opts.override)) {
53223
+ if (!import_node_fs35.default.existsSync(opts.override)) {
53263
53224
  throw new Error(`frpc binary not found at override path: ${opts.override}`);
53264
53225
  }
53265
53226
  return opts.override;
53266
53227
  }
53267
53228
  const version2 = opts.version ?? FRPC_VERSION;
53268
53229
  const platform = opts.platform ?? detectPlatform();
53269
- const binDir = import_node_path35.default.join(opts.dataDir, "bin");
53270
- import_node_fs34.default.mkdirSync(binDir, { recursive: true });
53230
+ const binDir = import_node_path38.default.join(opts.dataDir, "bin");
53231
+ import_node_fs35.default.mkdirSync(binDir, { recursive: true });
53271
53232
  cleanupStaleArtifacts(binDir);
53272
- const stableBin = import_node_path35.default.join(binDir, "frpc");
53273
- if (import_node_fs34.default.existsSync(stableBin)) return stableBin;
53233
+ const stableBin = import_node_path38.default.join(binDir, "frpc");
53234
+ if (import_node_fs35.default.existsSync(stableBin)) return stableBin;
53274
53235
  const partialBin = `${stableBin}.partial`;
53275
- const tarballPath = import_node_path35.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
53236
+ const tarballPath = import_node_path38.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
53276
53237
  try {
53277
53238
  const url = frpcDownloadUrl(version2, platform);
53278
53239
  await downloadToFile(url, tarballPath, opts.fetchImpl);
@@ -53281,8 +53242,8 @@ async function ensureFrpcBinary(opts) {
53281
53242
  } else {
53282
53243
  await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
53283
53244
  }
53284
- import_node_fs34.default.chmodSync(partialBin, 493);
53285
- import_node_fs34.default.renameSync(partialBin, stableBin);
53245
+ import_node_fs35.default.chmodSync(partialBin, 493);
53246
+ import_node_fs35.default.renameSync(partialBin, stableBin);
53286
53247
  } finally {
53287
53248
  safeUnlink(tarballPath);
53288
53249
  safeUnlink(partialBin);
@@ -53292,15 +53253,15 @@ async function ensureFrpcBinary(opts) {
53292
53253
  function cleanupStaleArtifacts(binDir) {
53293
53254
  let entries;
53294
53255
  try {
53295
- entries = import_node_fs34.default.readdirSync(binDir);
53256
+ entries = import_node_fs35.default.readdirSync(binDir);
53296
53257
  } catch {
53297
53258
  return;
53298
53259
  }
53299
53260
  for (const name of entries) {
53300
53261
  if (name.endsWith(".partial") || name.startsWith("extract-")) {
53301
- const full = import_node_path35.default.join(binDir, name);
53262
+ const full = import_node_path38.default.join(binDir, name);
53302
53263
  try {
53303
- import_node_fs34.default.rmSync(full, { recursive: true, force: true });
53264
+ import_node_fs35.default.rmSync(full, { recursive: true, force: true });
53304
53265
  } catch {
53305
53266
  }
53306
53267
  }
@@ -53308,7 +53269,7 @@ function cleanupStaleArtifacts(binDir) {
53308
53269
  }
53309
53270
  function safeUnlink(p2) {
53310
53271
  try {
53311
- import_node_fs34.default.unlinkSync(p2);
53272
+ import_node_fs35.default.unlinkSync(p2);
53312
53273
  } catch {
53313
53274
  }
53314
53275
  }
@@ -53319,46 +53280,46 @@ async function downloadToFile(url, dest, fetchImpl) {
53319
53280
  if (!res.ok || !res.body) {
53320
53281
  throw new Error(`download failed: ${res.status} ${res.statusText}`);
53321
53282
  }
53322
- const out = import_node_fs34.default.createWriteStream(dest);
53283
+ const out = import_node_fs35.default.createWriteStream(dest);
53323
53284
  const nodeStream = import_node_stream3.Readable.fromWeb(res.body);
53324
53285
  await (0, import_promises5.pipeline)(nodeStream, out);
53325
53286
  }
53326
53287
  async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
53327
- const work = import_node_path35.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
53328
- import_node_fs34.default.mkdirSync(work, { recursive: true });
53288
+ const work = import_node_path38.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
53289
+ import_node_fs35.default.mkdirSync(work, { recursive: true });
53329
53290
  try {
53330
53291
  await new Promise((resolve6, reject) => {
53331
- const proc = (0, import_node_child_process7.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
53292
+ const proc = (0, import_node_child_process8.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
53332
53293
  proc.on("error", reject);
53333
53294
  proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
53334
53295
  });
53335
53296
  const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
53336
- const src = import_node_path35.default.join(work, dirName, "frpc");
53337
- if (!import_node_fs34.default.existsSync(src)) {
53297
+ const src = import_node_path38.default.join(work, dirName, "frpc");
53298
+ if (!import_node_fs35.default.existsSync(src)) {
53338
53299
  throw new Error(`frpc not found inside tarball at ${src}`);
53339
53300
  }
53340
- import_node_fs34.default.copyFileSync(src, destBin);
53301
+ import_node_fs35.default.copyFileSync(src, destBin);
53341
53302
  } finally {
53342
- import_node_fs34.default.rmSync(work, { recursive: true, force: true });
53303
+ import_node_fs35.default.rmSync(work, { recursive: true, force: true });
53343
53304
  }
53344
53305
  }
53345
53306
 
53346
53307
  // src/tunnel/frpc-process.ts
53347
- var import_node_fs35 = __toESM(require("fs"), 1);
53348
- var import_node_path36 = __toESM(require("path"), 1);
53349
- var import_node_child_process8 = require("child_process");
53308
+ var import_node_fs36 = __toESM(require("fs"), 1);
53309
+ var import_node_path39 = __toESM(require("path"), 1);
53310
+ var import_node_child_process9 = require("child_process");
53350
53311
  function frpcPidFilePath(dataDir) {
53351
- return import_node_path36.default.join(dataDir, "frpc.pid");
53312
+ return import_node_path39.default.join(dataDir, "frpc.pid");
53352
53313
  }
53353
53314
  function writeFrpcPid(dataDir, pid) {
53354
53315
  try {
53355
- import_node_fs35.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
53316
+ import_node_fs36.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
53356
53317
  } catch {
53357
53318
  }
53358
53319
  }
53359
53320
  function clearFrpcPid(dataDir) {
53360
53321
  try {
53361
- import_node_fs35.default.unlinkSync(frpcPidFilePath(dataDir));
53322
+ import_node_fs36.default.unlinkSync(frpcPidFilePath(dataDir));
53362
53323
  } catch {
53363
53324
  }
53364
53325
  }
@@ -53374,7 +53335,7 @@ function defaultIsPidAlive(pid) {
53374
53335
  }
53375
53336
  function defaultReadPidFile(file) {
53376
53337
  try {
53377
- return import_node_fs35.default.readFileSync(file, "utf8");
53338
+ return import_node_fs36.default.readFileSync(file, "utf8");
53378
53339
  } catch {
53379
53340
  return null;
53380
53341
  }
@@ -53390,7 +53351,7 @@ function defaultSleep(ms) {
53390
53351
  }
53391
53352
  async function killStaleFrpc(deps) {
53392
53353
  const pidFile = frpcPidFilePath(deps.dataDir);
53393
- const tomlPath = import_node_path36.default.join(deps.dataDir, "frpc.toml");
53354
+ const tomlPath = import_node_path39.default.join(deps.dataDir, "frpc.toml");
53394
53355
  const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
53395
53356
  const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
53396
53357
  const killPid = deps.killPidImpl ?? defaultKillPid;
@@ -53414,7 +53375,7 @@ async function killStaleFrpc(deps) {
53414
53375
  }
53415
53376
  if (victims.size === 0) {
53416
53377
  try {
53417
- import_node_fs35.default.unlinkSync(pidFile);
53378
+ import_node_fs36.default.unlinkSync(pidFile);
53418
53379
  } catch {
53419
53380
  }
53420
53381
  return;
@@ -53425,14 +53386,14 @@ async function killStaleFrpc(deps) {
53425
53386
  }
53426
53387
  await sleep2(deps.reapWaitMs ?? 300);
53427
53388
  try {
53428
- import_node_fs35.default.unlinkSync(pidFile);
53389
+ import_node_fs36.default.unlinkSync(pidFile);
53429
53390
  } catch {
53430
53391
  }
53431
53392
  }
53432
53393
  async function defaultScanFrpcPidsByCmdline(tomlPath, logger) {
53433
53394
  if (process.platform === "win32") return [];
53434
53395
  return new Promise((resolve6) => {
53435
- const ps = (0, import_node_child_process8.spawn)("ps", ["-axo", "pid=,command="], { stdio: ["ignore", "pipe", "ignore"] });
53396
+ const ps = (0, import_node_child_process9.spawn)("ps", ["-axo", "pid=,command="], { stdio: ["ignore", "pipe", "ignore"] });
53436
53397
  let buf = "";
53437
53398
  ps.stdout.on("data", (c) => {
53438
53399
  buf += c.toString();
@@ -53462,7 +53423,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
53462
53423
  var TunnelManager = class {
53463
53424
  constructor(deps) {
53464
53425
  this.deps = deps;
53465
- this.store = deps.store ?? new TunnelStore(import_node_path37.default.join(deps.dataDir, "tunnel.json"));
53426
+ this.store = deps.store ?? new TunnelStore(import_node_path40.default.join(deps.dataDir, "tunnel.json"));
53466
53427
  this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
53467
53428
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
53468
53429
  }
@@ -53589,7 +53550,7 @@ var TunnelManager = class {
53589
53550
  dataDir: this.deps.dataDir,
53590
53551
  override: this.deps.frpcBinaryOverride ?? void 0
53591
53552
  });
53592
- const tomlPath = import_node_path37.default.join(this.deps.dataDir, "frpc.toml");
53553
+ const tomlPath = import_node_path40.default.join(this.deps.dataDir, "frpc.toml");
53593
53554
  const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto10.default.randomBytes(3).toString("hex")}`;
53594
53555
  const toml = buildFrpcToml({
53595
53556
  serverAddr: t.frpsHost,
@@ -53600,14 +53561,14 @@ var TunnelManager = class {
53600
53561
  localPort,
53601
53562
  logLevel: "info"
53602
53563
  });
53603
- await import_node_fs36.default.promises.writeFile(tomlPath, toml, { mode: 384 });
53604
- const proc = (this.deps.spawnImpl ?? import_node_child_process9.spawn)(frpcBin, ["-c", tomlPath], {
53564
+ await import_node_fs37.default.promises.writeFile(tomlPath, toml, { mode: 384 });
53565
+ const proc = (this.deps.spawnImpl ?? import_node_child_process10.spawn)(frpcBin, ["-c", tomlPath], {
53605
53566
  stdio: ["ignore", "pipe", "pipe"]
53606
53567
  });
53607
- const logDir = import_node_path37.default.join(this.deps.dataDir, "log");
53608
- import_node_fs36.default.mkdirSync(logDir, { recursive: true });
53609
- const logFilePath = import_node_path37.default.join(logDir, "frpc.log");
53610
- const logStream = import_node_fs36.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
53568
+ const logDir = import_node_path40.default.join(this.deps.dataDir, "log");
53569
+ import_node_fs37.default.mkdirSync(logDir, { recursive: true });
53570
+ const logFilePath = import_node_path40.default.join(logDir, "frpc.log");
53571
+ const logStream = import_node_fs37.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
53611
53572
  logStream.on("error", () => {
53612
53573
  });
53613
53574
  const tee = (chunk) => {
@@ -53690,27 +53651,27 @@ async function waitForFrpcReady(proc, timeoutMs) {
53690
53651
 
53691
53652
  // src/tunnel/device-key.ts
53692
53653
  var import_node_os14 = __toESM(require("os"), 1);
53693
- var import_node_path38 = __toESM(require("path"), 1);
53654
+ var import_node_path41 = __toESM(require("path"), 1);
53694
53655
  var import_node_crypto11 = __toESM(require("crypto"), 1);
53695
53656
  var DERIVE_SALT = "clawd-tunnel-device-v1";
53696
53657
  function deriveStableDeviceKey(opts = {}) {
53697
53658
  const hostname = opts.hostname ?? import_node_os14.default.hostname();
53698
53659
  const uid = opts.uid ?? (typeof import_node_os14.default.userInfo === "function" ? import_node_os14.default.userInfo().uid : 0);
53699
53660
  const home = opts.home ?? import_node_os14.default.homedir();
53700
- const defaultDataDir = import_node_path38.default.resolve(import_node_path38.default.join(home, ".clawd"));
53701
- const normalizedDataDir = opts.dataDir ? import_node_path38.default.resolve(opts.dataDir) : null;
53661
+ const defaultDataDir = import_node_path41.default.resolve(import_node_path41.default.join(home, ".clawd"));
53662
+ const normalizedDataDir = opts.dataDir ? import_node_path41.default.resolve(opts.dataDir) : null;
53702
53663
  const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
53703
53664
  const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
53704
53665
  return import_node_crypto11.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
53705
53666
  }
53706
53667
 
53707
53668
  // src/auth-store.ts
53708
- var import_node_fs37 = __toESM(require("fs"), 1);
53709
- var import_node_path39 = __toESM(require("path"), 1);
53669
+ var import_node_fs38 = __toESM(require("fs"), 1);
53670
+ var import_node_path42 = __toESM(require("path"), 1);
53710
53671
  var import_node_crypto12 = __toESM(require("crypto"), 1);
53711
53672
  var AUTH_FILE_NAME = "auth.json";
53712
53673
  function authFilePath(dataDir) {
53713
- return import_node_path39.default.join(dataDir, AUTH_FILE_NAME);
53674
+ return import_node_path42.default.join(dataDir, AUTH_FILE_NAME);
53714
53675
  }
53715
53676
  function loadOrCreateAuthFile(opts) {
53716
53677
  const file = authFilePath(opts.dataDir);
@@ -53746,7 +53707,7 @@ function defaultGenerateOwnerPrincipalId() {
53746
53707
  }
53747
53708
  function readAuthFile(file) {
53748
53709
  try {
53749
- const raw = import_node_fs37.default.readFileSync(file, "utf8");
53710
+ const raw = import_node_fs38.default.readFileSync(file, "utf8");
53750
53711
  const parsed = JSON.parse(raw);
53751
53712
  if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
53752
53713
  return null;
@@ -53766,25 +53727,25 @@ function readAuthFile(file) {
53766
53727
  }
53767
53728
  }
53768
53729
  function writeAuthFile(file, content) {
53769
- import_node_fs37.default.mkdirSync(import_node_path39.default.dirname(file), { recursive: true });
53770
- import_node_fs37.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
53730
+ import_node_fs38.default.mkdirSync(import_node_path42.default.dirname(file), { recursive: true });
53731
+ import_node_fs38.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
53771
53732
  try {
53772
- import_node_fs37.default.chmodSync(file, 384);
53733
+ import_node_fs38.default.chmodSync(file, 384);
53773
53734
  } catch {
53774
53735
  }
53775
53736
  }
53776
53737
 
53777
53738
  // src/owner-profile.ts
53778
- var import_node_fs38 = __toESM(require("fs"), 1);
53739
+ var import_node_fs39 = __toESM(require("fs"), 1);
53779
53740
  var import_node_os15 = __toESM(require("os"), 1);
53780
- var import_node_path40 = __toESM(require("path"), 1);
53741
+ var import_node_path43 = __toESM(require("path"), 1);
53781
53742
  var PROFILE_FILENAME = "profile.json";
53782
53743
  function loadOwnerDisplayName(dataDir) {
53783
53744
  const fallback = import_node_os15.default.userInfo().username;
53784
- const profilePath = import_node_path40.default.join(dataDir, PROFILE_FILENAME);
53745
+ const profilePath = import_node_path43.default.join(dataDir, PROFILE_FILENAME);
53785
53746
  let raw;
53786
53747
  try {
53787
- raw = import_node_fs38.default.readFileSync(profilePath, "utf8");
53748
+ raw = import_node_fs39.default.readFileSync(profilePath, "utf8");
53788
53749
  } catch {
53789
53750
  return fallback;
53790
53751
  }
@@ -53807,18 +53768,18 @@ function loadOwnerDisplayName(dataDir) {
53807
53768
  }
53808
53769
 
53809
53770
  // src/feishu-auth/owner-identity-store.ts
53810
- var import_node_fs39 = __toESM(require("fs"), 1);
53811
- var import_node_path41 = __toESM(require("path"), 1);
53771
+ var import_node_fs40 = __toESM(require("fs"), 1);
53772
+ var import_node_path44 = __toESM(require("path"), 1);
53812
53773
  var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
53813
53774
  var OwnerIdentityStore = class {
53814
53775
  file;
53815
53776
  constructor(dataDir) {
53816
- this.file = import_node_path41.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
53777
+ this.file = import_node_path44.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
53817
53778
  }
53818
53779
  read() {
53819
53780
  let raw;
53820
53781
  try {
53821
- raw = import_node_fs39.default.readFileSync(this.file, "utf8");
53782
+ raw = import_node_fs40.default.readFileSync(this.file, "utf8");
53822
53783
  } catch {
53823
53784
  return null;
53824
53785
  }
@@ -53846,16 +53807,16 @@ var OwnerIdentityStore = class {
53846
53807
  };
53847
53808
  }
53848
53809
  write(record) {
53849
- import_node_fs39.default.mkdirSync(import_node_path41.default.dirname(this.file), { recursive: true });
53850
- import_node_fs39.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
53810
+ import_node_fs40.default.mkdirSync(import_node_path44.default.dirname(this.file), { recursive: true });
53811
+ import_node_fs40.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
53851
53812
  try {
53852
- import_node_fs39.default.chmodSync(this.file, 384);
53813
+ import_node_fs40.default.chmodSync(this.file, 384);
53853
53814
  } catch {
53854
53815
  }
53855
53816
  }
53856
53817
  clear() {
53857
53818
  try {
53858
- import_node_fs39.default.unlinkSync(this.file);
53819
+ import_node_fs40.default.unlinkSync(this.file);
53859
53820
  } catch (err) {
53860
53821
  const code = err?.code;
53861
53822
  if (code !== "ENOENT") throw err;
@@ -53992,9 +53953,9 @@ var CentralClientError = class extends Error {
53992
53953
  code;
53993
53954
  cause;
53994
53955
  };
53995
- async function centralRequest(opts, path71, init) {
53956
+ async function centralRequest(opts, path74, init) {
53996
53957
  const f = opts.fetchImpl ?? globalThis.fetch;
53997
- const url = `${opts.api.replace(/\/+$/, "")}${path71}`;
53958
+ const url = `${opts.api.replace(/\/+$/, "")}${path74}`;
53998
53959
  const ctrl = new AbortController();
53999
53960
  const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
54000
53961
  let res;
@@ -54139,7 +54100,7 @@ async function upsertDeviceBinding(opts) {
54139
54100
  }
54140
54101
 
54141
54102
  // src/feishu-auth/verify-token.ts
54142
- var crypto14 = __toESM(require("crypto"), 1);
54103
+ var crypto13 = __toESM(require("crypto"), 1);
54143
54104
  var CONNECT_TOKEN_PREFIX = "clawdtk1";
54144
54105
  function verifyConnectToken(args) {
54145
54106
  const now = args.nowSeconds ?? Math.floor(Date.now() / 1e3);
@@ -54151,7 +54112,7 @@ function verifyConnectToken(args) {
54151
54112
  const data = `${prefix}.${payloadB64}`;
54152
54113
  let signatureValid = false;
54153
54114
  try {
54154
- signatureValid = crypto14.verify(
54115
+ signatureValid = crypto13.verify(
54155
54116
  null,
54156
54117
  Buffer.from(data, "utf8"),
54157
54118
  args.publicKeyPem,
@@ -54189,7 +54150,7 @@ function verifyConnectToken(args) {
54189
54150
 
54190
54151
  // src/feishu-auth/server-key.ts
54191
54152
  var fs50 = __toESM(require("fs"), 1);
54192
- var path50 = __toESM(require("path"), 1);
54153
+ var path53 = __toESM(require("path"), 1);
54193
54154
  var FILE_NAME2 = "server-signing-key.json";
54194
54155
  var ServerKeyStore = class {
54195
54156
  constructor(dataDir) {
@@ -54197,7 +54158,7 @@ var ServerKeyStore = class {
54197
54158
  }
54198
54159
  dataDir;
54199
54160
  filePath() {
54200
- return path50.join(this.dataDir, FILE_NAME2);
54161
+ return path53.join(this.dataDir, FILE_NAME2);
54201
54162
  }
54202
54163
  /** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
54203
54164
  read() {
@@ -54235,12 +54196,12 @@ init_protocol();
54235
54196
  init_protocol();
54236
54197
 
54237
54198
  // src/session/fork.ts
54238
- var import_node_fs40 = __toESM(require("fs"), 1);
54199
+ var import_node_fs41 = __toESM(require("fs"), 1);
54239
54200
  var import_node_os16 = __toESM(require("os"), 1);
54240
- var import_node_path42 = __toESM(require("path"), 1);
54201
+ var import_node_path45 = __toESM(require("path"), 1);
54241
54202
  init_claude_history();
54242
54203
  function readJsonlEntries(file) {
54243
- const raw = import_node_fs40.default.readFileSync(file, "utf8");
54204
+ const raw = import_node_fs41.default.readFileSync(file, "utf8");
54244
54205
  const out = [];
54245
54206
  for (const line of raw.split("\n")) {
54246
54207
  const t = line.trim();
@@ -54253,19 +54214,19 @@ function readJsonlEntries(file) {
54253
54214
  return out;
54254
54215
  }
54255
54216
  function forkSession(input) {
54256
- const baseDir = input.baseDir ?? import_node_path42.default.join(import_node_os16.default.homedir(), ".claude");
54257
- let projectDir = import_node_path42.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
54258
- let sourceFile = import_node_path42.default.join(projectDir, `${input.toolSessionId}.jsonl`);
54259
- if (!import_node_fs40.default.existsSync(sourceFile)) {
54217
+ const baseDir = input.baseDir ?? import_node_path45.default.join(import_node_os16.default.homedir(), ".claude");
54218
+ let projectDir = import_node_path45.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
54219
+ let sourceFile = import_node_path45.default.join(projectDir, `${input.toolSessionId}.jsonl`);
54220
+ if (!import_node_fs41.default.existsSync(sourceFile)) {
54260
54221
  const found = findTranscriptByToolSessionId(
54261
- import_node_path42.default.join(baseDir, "projects"),
54222
+ import_node_path45.default.join(baseDir, "projects"),
54262
54223
  input.toolSessionId
54263
54224
  );
54264
54225
  if (!found) {
54265
54226
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
54266
54227
  }
54267
54228
  sourceFile = found;
54268
- projectDir = import_node_path42.default.dirname(found);
54229
+ projectDir = import_node_path45.default.dirname(found);
54269
54230
  }
54270
54231
  const entries = readJsonlEntries(sourceFile);
54271
54232
  const mainEntries = entries.filter((e) => e.isSidechain !== true);
@@ -54294,9 +54255,9 @@ function forkSession(input) {
54294
54255
  }
54295
54256
  forkedLines.push(JSON.stringify(forked));
54296
54257
  }
54297
- const forkedFilePath = import_node_path42.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
54298
- import_node_fs40.default.mkdirSync(projectDir, { recursive: true });
54299
- import_node_fs40.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
54258
+ const forkedFilePath = import_node_path45.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
54259
+ import_node_fs41.default.mkdirSync(projectDir, { recursive: true });
54260
+ import_node_fs41.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
54300
54261
  return { forkedToolSessionId, forkedFilePath };
54301
54262
  }
54302
54263
 
@@ -54458,21 +54419,34 @@ function buildSessionHandlers(deps) {
54458
54419
  const { broadcast } = await manager.interrupt(args);
54459
54420
  return { response: { type: "session:interrupt", ok: true }, broadcast };
54460
54421
  };
54422
+ const ensureRewindSupported = async (sessionId) => {
54423
+ const file = manager.get({ sessionId }).response;
54424
+ const caps = await getAdapter2(file.tool ?? "claude").capabilities();
54425
+ if (caps.features.rewind === false) {
54426
+ throw new ClawdError(
54427
+ ERROR_CODES.TOOL_NOT_SUPPORTED,
54428
+ `tool ${file.tool ?? "claude"} does not support rewind`
54429
+ );
54430
+ }
54431
+ };
54461
54432
  const rewind = async (frame, _client, ctx) => {
54462
54433
  const args = SessionRewindArgs.parse(frame);
54463
54434
  ensureSessionAccess(ctx, args.sessionId, "send");
54435
+ await ensureRewindSupported(args.sessionId);
54464
54436
  const { response, broadcast } = await manager.rewind(args);
54465
54437
  return { response: { type: "session:rewind", ...response }, broadcast };
54466
54438
  };
54467
54439
  const rewindDiff = async (frame, _client, ctx) => {
54468
54440
  const args = SessionRewindDiffArgs.parse(frame);
54469
54441
  ensureSessionAccess(ctx, args.sessionId, "read");
54442
+ await ensureRewindSupported(args.sessionId);
54470
54443
  const { response } = await manager.rewindDiff(args);
54471
54444
  return { response: { type: "session:rewind-diff", ...response } };
54472
54445
  };
54473
54446
  const rewindableMessageIds = async (frame, _client, ctx) => {
54474
54447
  const args = SessionRewindableMessageIdsArgs.parse(frame);
54475
54448
  ensureSessionAccess(ctx, args.sessionId, "read");
54449
+ await ensureRewindSupported(args.sessionId);
54476
54450
  const { response } = manager.rewindableMessageIds(args);
54477
54451
  return {
54478
54452
  response: { type: "session:rewindable-message-ids", ...response }
@@ -54695,7 +54669,7 @@ function buildPermissionHandlers(deps) {
54695
54669
  }
54696
54670
 
54697
54671
  // src/handlers/history.ts
54698
- var path53 = __toESM(require("path"), 1);
54672
+ var path56 = __toESM(require("path"), 1);
54699
54673
  init_protocol();
54700
54674
 
54701
54675
  // src/session/recent-dirs.ts
@@ -54713,7 +54687,7 @@ function listRecentDirs(store, limit = 50) {
54713
54687
  }
54714
54688
 
54715
54689
  // src/permission/persona-paths.ts
54716
- var path52 = __toESM(require("path"), 1);
54690
+ var path55 = __toESM(require("path"), 1);
54717
54691
  function getAllowedPersonaIds(grants, action) {
54718
54692
  const ids = /* @__PURE__ */ new Set();
54719
54693
  for (const g2 of grants) {
@@ -54726,42 +54700,42 @@ function getAllowedPersonaIds(grants, action) {
54726
54700
  return ids;
54727
54701
  }
54728
54702
  function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
54729
- const target = path52.resolve(absPath);
54703
+ const target = path55.resolve(absPath);
54730
54704
  if (userWorkDir) {
54731
- const u = path52.resolve(userWorkDir);
54732
- const usep = u.endsWith(path52.sep) ? "" : path52.sep;
54705
+ const u = path55.resolve(userWorkDir);
54706
+ const usep = u.endsWith(path55.sep) ? "" : path55.sep;
54733
54707
  if (target === u || target.startsWith(u + usep)) return true;
54734
54708
  }
54735
- const root = path52.resolve(personaRoot);
54736
- const sep3 = root.endsWith(path52.sep) ? "" : path52.sep;
54709
+ const root = path55.resolve(personaRoot);
54710
+ const sep3 = root.endsWith(path55.sep) ? "" : path55.sep;
54737
54711
  if (!target.startsWith(root + sep3)) return false;
54738
- const rel = path52.relative(root, target);
54712
+ const rel = path55.relative(root, target);
54739
54713
  if (!rel || rel.startsWith("..")) return false;
54740
- const personaId = rel.split(path52.sep)[0];
54714
+ const personaId = rel.split(path55.sep)[0];
54741
54715
  if (!personaId) return false;
54742
54716
  const allowed = getAllowedPersonaIds(grants, action);
54743
54717
  if (allowed === "*") return true;
54744
54718
  return allowed.has(personaId);
54745
54719
  }
54746
54720
  function personaIdFromPath(absPath, personaRoot) {
54747
- const root = path52.resolve(personaRoot);
54748
- const target = path52.resolve(absPath);
54749
- const sep3 = root.endsWith(path52.sep) ? "" : path52.sep;
54721
+ const root = path55.resolve(personaRoot);
54722
+ const target = path55.resolve(absPath);
54723
+ const sep3 = root.endsWith(path55.sep) ? "" : path55.sep;
54750
54724
  if (!target.startsWith(root + sep3)) return null;
54751
- const rel = path52.relative(root, target);
54725
+ const rel = path55.relative(root, target);
54752
54726
  if (!rel || rel.startsWith("..")) return null;
54753
- const id = rel.split(path52.sep)[0];
54727
+ const id = rel.split(path55.sep)[0];
54754
54728
  return id || null;
54755
54729
  }
54756
54730
  function isPathWithin(dir, absPath) {
54757
- const d = path52.resolve(dir);
54758
- const t = path52.resolve(absPath);
54759
- const sep3 = d.endsWith(path52.sep) ? "" : path52.sep;
54731
+ const d = path55.resolve(dir);
54732
+ const t = path55.resolve(absPath);
54733
+ const sep3 = d.endsWith(path55.sep) ? "" : path55.sep;
54760
54734
  return t === d || t.startsWith(d + sep3);
54761
54735
  }
54762
54736
  function isPathInGuestBoundary(personaRoot, personaId, userWorkDir, absPath) {
54763
54737
  if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
54764
- return personaIdFromPath(path52.resolve(absPath), personaRoot) === personaId;
54738
+ return personaIdFromPath(path55.resolve(absPath), personaRoot) === personaId;
54765
54739
  }
54766
54740
 
54767
54741
  // src/handlers/history.ts
@@ -54813,7 +54787,7 @@ function buildHistoryHandlers(deps) {
54813
54787
  if (!pid) return false;
54814
54788
  return isGuestPathAllowed(
54815
54789
  ctx.grants,
54816
- path53.join(personaRoot, pid),
54790
+ path56.join(personaRoot, pid),
54817
54791
  personaRoot,
54818
54792
  "read",
54819
54793
  userWorkDir
@@ -54825,7 +54799,7 @@ function buildHistoryHandlers(deps) {
54825
54799
  };
54826
54800
  const list = async (frame, _client, ctx) => {
54827
54801
  const args = HistoryListArgs.parse(frame);
54828
- assertGuestPath(ctx, path53.resolve(args.projectPath), personaRoot, "history:list");
54802
+ assertGuestPath(ctx, path56.resolve(args.projectPath), personaRoot, "history:list");
54829
54803
  const sessions = await history.listSessions(args);
54830
54804
  return { response: { type: "history:list", sessions } };
54831
54805
  };
@@ -54858,13 +54832,13 @@ function buildHistoryHandlers(deps) {
54858
54832
  };
54859
54833
  const subagents = async (frame, _client, ctx) => {
54860
54834
  const args = HistorySubagentsArgs.parse(frame);
54861
- assertGuestPath(ctx, path53.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
54835
+ assertGuestPath(ctx, path56.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
54862
54836
  const subs = await history.listSubagents(args);
54863
54837
  return { response: { type: "history:subagents", subagents: subs } };
54864
54838
  };
54865
54839
  const subagentRead = async (frame, _client, ctx) => {
54866
54840
  const args = HistorySubagentReadArgs.parse(frame);
54867
- assertGuestPath(ctx, path53.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
54841
+ assertGuestPath(ctx, path56.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
54868
54842
  const res = await history.readSubagent(args);
54869
54843
  return { response: { type: "history:subagent-read", ...res } };
54870
54844
  };
@@ -54873,7 +54847,7 @@ function buildHistoryHandlers(deps) {
54873
54847
  if (ctx?.principal.kind === "guest" && personaRoot) {
54874
54848
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
54875
54849
  const filtered = dirs.filter(
54876
- (d) => isGuestPathAllowed(ctx.grants, path53.resolve(d.cwd), personaRoot, "read", userWorkDir)
54850
+ (d) => isGuestPathAllowed(ctx.grants, path56.resolve(d.cwd), personaRoot, "read", userWorkDir)
54877
54851
  );
54878
54852
  return { response: { type: "history:recentDirs", dirs: filtered } };
54879
54853
  }
@@ -54890,7 +54864,7 @@ function buildHistoryHandlers(deps) {
54890
54864
  }
54891
54865
 
54892
54866
  // src/handlers/workspace.ts
54893
- var path54 = __toESM(require("path"), 1);
54867
+ var path57 = __toESM(require("path"), 1);
54894
54868
  var os16 = __toESM(require("os"), 1);
54895
54869
  init_protocol();
54896
54870
  init_protocol();
@@ -54932,22 +54906,22 @@ function buildWorkspaceHandlers(deps) {
54932
54906
  const args = WorkspaceListArgs.parse(frame);
54933
54907
  const isGuest = ctx?.principal.kind === "guest";
54934
54908
  const fallbackCwd = isGuest && personaRoot ? personaRoot : os16.homedir();
54935
- const resolvedCwd = path54.resolve(args.cwd ?? fallbackCwd);
54936
- const target = args.path ? path54.resolve(resolvedCwd, args.path) : resolvedCwd;
54909
+ const resolvedCwd = path57.resolve(args.cwd ?? fallbackCwd);
54910
+ const target = args.path ? path57.resolve(resolvedCwd, args.path) : resolvedCwd;
54937
54911
  assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
54938
54912
  const res = workspace.list({ ...args, cwd: resolvedCwd });
54939
54913
  return { response: { type: "workspace:list", ...res } };
54940
54914
  };
54941
54915
  const read = async (frame, _client, ctx) => {
54942
54916
  const args = WorkspaceReadArgs.parse(frame);
54943
- const target = path54.isAbsolute(args.path) ? path54.resolve(args.path) : path54.resolve(args.cwd, args.path);
54917
+ const target = path57.isAbsolute(args.path) ? path57.resolve(args.path) : path57.resolve(args.cwd, args.path);
54944
54918
  assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
54945
54919
  const res = workspace.read(args);
54946
54920
  return { response: { type: "workspace:read", ...res } };
54947
54921
  };
54948
54922
  const skillsList = async (frame, _client, ctx) => {
54949
54923
  const args = SkillsListArgs.parse(frame);
54950
- const cwdAbs = path54.resolve(args.cwd);
54924
+ const cwdAbs = path57.resolve(args.cwd);
54951
54925
  assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
54952
54926
  const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
54953
54927
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -54959,7 +54933,7 @@ function buildWorkspaceHandlers(deps) {
54959
54933
  };
54960
54934
  const agentsList = async (frame, _client, ctx) => {
54961
54935
  const args = AgentsListArgs.parse(frame);
54962
- const cwdAbs = path54.resolve(args.cwd);
54936
+ const cwdAbs = path57.resolve(args.cwd);
54963
54937
  assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
54964
54938
  if (args.tool === "codex") {
54965
54939
  return { response: { type: "agents:list", agents: [] } };
@@ -54981,20 +54955,20 @@ function buildWorkspaceHandlers(deps) {
54981
54955
  }
54982
54956
 
54983
54957
  // src/handlers/git.ts
54984
- var path56 = __toESM(require("path"), 1);
54958
+ var path59 = __toESM(require("path"), 1);
54985
54959
  init_protocol();
54986
54960
  init_protocol();
54987
54961
 
54988
54962
  // src/workspace/git.ts
54989
- var import_node_child_process10 = require("child_process");
54990
- var import_node_fs41 = __toESM(require("fs"), 1);
54991
- var import_node_path43 = __toESM(require("path"), 1);
54963
+ var import_node_child_process11 = require("child_process");
54964
+ var import_node_fs42 = __toESM(require("fs"), 1);
54965
+ var import_node_path46 = __toESM(require("path"), 1);
54992
54966
  var import_node_util = require("util");
54993
- var pexec = (0, import_node_util.promisify)(import_node_child_process10.execFile);
54967
+ var pexec = (0, import_node_util.promisify)(import_node_child_process11.execFile);
54994
54968
  function normalizePath(p2) {
54995
- const resolved = import_node_path43.default.resolve(p2);
54969
+ const resolved = import_node_path46.default.resolve(p2);
54996
54970
  try {
54997
- return import_node_fs41.default.realpathSync(resolved);
54971
+ return import_node_fs42.default.realpathSync(resolved);
54998
54972
  } catch {
54999
54973
  return resolved;
55000
54974
  }
@@ -55068,7 +55042,7 @@ async function listGitBranches(cwd) {
55068
55042
  function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
55069
55043
  if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
55070
55044
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
55071
- if (!isGuestPathAllowed(ctx.grants, path56.resolve(cwd), personaRoot, "read", userWorkDir)) {
55045
+ if (!isGuestPathAllowed(ctx.grants, path59.resolve(cwd), personaRoot, "read", userWorkDir)) {
55072
55046
  throw new ClawdError(
55073
55047
  ERROR_CODES.UNAUTHORIZED,
55074
55048
  `guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
@@ -55116,45 +55090,6 @@ function buildCapabilitiesHandlers(deps) {
55116
55090
  };
55117
55091
  }
55118
55092
 
55119
- // src/handlers/capability.ts
55120
- init_zod();
55121
- init_protocol();
55122
- var DeleteArgsSchema = external_exports.object({
55123
- capabilityId: external_exports.string().min(1)
55124
- }).strict();
55125
- function buildCapabilityHandlers(deps) {
55126
- const { manager } = deps;
55127
- const list = async () => {
55128
- return {
55129
- response: {
55130
- type: "capability:list",
55131
- capabilities: manager.list().map(stripSecretHash)
55132
- }
55133
- };
55134
- };
55135
- const del = async (frame) => {
55136
- const { type: _type, requestId: _requestId, ...rest } = frame;
55137
- const args = DeleteArgsSchema.parse(rest);
55138
- const result = manager.delete(args.capabilityId);
55139
- if (!result) {
55140
- throw new ClawdError(
55141
- ERROR_CODES.VALIDATION_ERROR,
55142
- `capability not found: ${args.capabilityId}`
55143
- );
55144
- }
55145
- return {
55146
- response: {
55147
- type: "capability:deleted",
55148
- capabilityId: args.capabilityId
55149
- }
55150
- };
55151
- };
55152
- return {
55153
- "capability:list": list,
55154
- "capability:delete": del
55155
- };
55156
- }
55157
-
55158
55093
  // src/handlers/inbox.ts
55159
55094
  init_protocol();
55160
55095
  function resolvePeerDeviceId(ctx, argsPeerDeviceId) {
@@ -55754,7 +55689,7 @@ function buildPersonaHandlers(deps) {
55754
55689
  }
55755
55690
 
55756
55691
  // src/handlers/attachment.ts
55757
- var import_node_path44 = __toESM(require("path"), 1);
55692
+ var import_node_path47 = __toESM(require("path"), 1);
55758
55693
  init_protocol();
55759
55694
  init_protocol();
55760
55695
  var DEFAULT_TTL_SECONDS = 24 * 3600;
@@ -55834,12 +55769,12 @@ function buildAttachmentHandlers(deps) {
55834
55769
  `session ${args.sessionId} scope unresolved`
55835
55770
  );
55836
55771
  }
55837
- const cwdAbs = import_node_path44.default.resolve(sessionFile.cwd);
55838
- const candidateAbs = import_node_path44.default.isAbsolute(args.relPath) ? import_node_path44.default.resolve(args.relPath) : import_node_path44.default.resolve(cwdAbs, args.relPath);
55772
+ const cwdAbs = import_node_path47.default.resolve(sessionFile.cwd);
55773
+ const candidateAbs = import_node_path47.default.isAbsolute(args.relPath) ? import_node_path47.default.resolve(args.relPath) : import_node_path47.default.resolve(cwdAbs, args.relPath);
55839
55774
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
55840
55775
  const entries = deps.groupFileStore.list(scope, args.sessionId);
55841
55776
  const entry = entries.find((e) => {
55842
- const storedAbs = import_node_path44.default.isAbsolute(e.relPath) ? import_node_path44.default.resolve(e.relPath) : import_node_path44.default.resolve(cwdAbs, e.relPath);
55777
+ const storedAbs = import_node_path47.default.isAbsolute(e.relPath) ? import_node_path47.default.resolve(e.relPath) : import_node_path47.default.resolve(cwdAbs, e.relPath);
55843
55778
  return storedAbs === candidateAbs && !e.stale;
55844
55779
  });
55845
55780
  if (!entry) {
@@ -55864,7 +55799,7 @@ function buildAttachmentHandlers(deps) {
55864
55799
  if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
55865
55800
  const f = deps.sessionStore.read(sessionId);
55866
55801
  if (!f) return;
55867
- assertGuestAttachmentPath(ctx, import_node_path44.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
55802
+ assertGuestAttachmentPath(ctx, import_node_path47.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
55868
55803
  }
55869
55804
  const groupAdd = async (frame, _client, ctx) => {
55870
55805
  if (!deps.groupFileStore || !deps.getSessionScope) {
@@ -55879,8 +55814,8 @@ function buildAttachmentHandlers(deps) {
55879
55814
  if (!scope) {
55880
55815
  throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
55881
55816
  }
55882
- const cwdAbs = import_node_path44.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
55883
- const candidateAbs = import_node_path44.default.isAbsolute(args.relPath) ? import_node_path44.default.resolve(args.relPath) : import_node_path44.default.resolve(cwdAbs, args.relPath);
55817
+ const cwdAbs = import_node_path47.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
55818
+ const candidateAbs = import_node_path47.default.isAbsolute(args.relPath) ? import_node_path47.default.resolve(args.relPath) : import_node_path47.default.resolve(cwdAbs, args.relPath);
55884
55819
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
55885
55820
  const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
55886
55821
  const size = 0;
@@ -55939,19 +55874,19 @@ function buildAttachmentHandlers(deps) {
55939
55874
 
55940
55875
  // src/handlers/extension.ts
55941
55876
  var import_promises9 = __toESM(require("fs/promises"), 1);
55942
- var import_node_path49 = __toESM(require("path"), 1);
55877
+ var import_node_path52 = __toESM(require("path"), 1);
55943
55878
  init_protocol();
55944
55879
 
55945
55880
  // src/extension/bundle-zip.ts
55946
55881
  var import_promises6 = __toESM(require("fs/promises"), 1);
55947
- var import_node_path45 = __toESM(require("path"), 1);
55882
+ var import_node_path48 = __toESM(require("path"), 1);
55948
55883
  var import_node_crypto14 = __toESM(require("crypto"), 1);
55949
55884
  var import_jszip2 = __toESM(require_lib3(), 1);
55950
55885
  async function bundleExtensionDir(dir) {
55951
55886
  const entries = await listFilesSorted(dir);
55952
55887
  const zip = new import_jszip2.default();
55953
55888
  for (const rel of entries) {
55954
- const abs = import_node_path45.default.join(dir, rel);
55889
+ const abs = import_node_path48.default.join(dir, rel);
55955
55890
  const content = await import_promises6.default.readFile(abs);
55956
55891
  zip.file(rel, content, { date: FIXED_DATE });
55957
55892
  }
@@ -55972,7 +55907,7 @@ async function listFilesSorted(rootDir) {
55972
55907
  return out;
55973
55908
  }
55974
55909
  async function walk(absRoot, relPrefix, out) {
55975
- const dirAbs = import_node_path45.default.join(absRoot, relPrefix);
55910
+ const dirAbs = import_node_path48.default.join(absRoot, relPrefix);
55976
55911
  const entries = await import_promises6.default.readdir(dirAbs, { withFileTypes: true });
55977
55912
  for (const e of entries) {
55978
55913
  if (IGNORE_BASENAMES.has(e.name)) continue;
@@ -56026,25 +55961,25 @@ function computePublishCheck(args) {
56026
55961
 
56027
55962
  // src/extension/install-flow.ts
56028
55963
  var import_promises7 = __toESM(require("fs/promises"), 1);
56029
- var import_node_path47 = __toESM(require("path"), 1);
55964
+ var import_node_path50 = __toESM(require("path"), 1);
56030
55965
  var import_node_os19 = __toESM(require("os"), 1);
56031
55966
  var import_node_crypto15 = __toESM(require("crypto"), 1);
56032
55967
  var import_jszip3 = __toESM(require_lib3(), 1);
56033
55968
 
56034
55969
  // src/extension/paths.ts
56035
55970
  var import_node_os18 = __toESM(require("os"), 1);
56036
- var import_node_path46 = __toESM(require("path"), 1);
55971
+ var import_node_path49 = __toESM(require("path"), 1);
56037
55972
  function clawdHomeRoot(override) {
56038
- return override ?? process.env.CLAWD_HOME ?? import_node_path46.default.join(import_node_os18.default.homedir(), ".clawd");
55973
+ return override ?? process.env.CLAWD_HOME ?? import_node_path49.default.join(import_node_os18.default.homedir(), ".clawd");
56039
55974
  }
56040
55975
  function extensionsRoot(override) {
56041
- return import_node_path46.default.join(clawdHomeRoot(override), "extensions");
55976
+ return import_node_path49.default.join(clawdHomeRoot(override), "extensions");
56042
55977
  }
56043
55978
  function publishedChannelsFile(override) {
56044
- return import_node_path46.default.join(clawdHomeRoot(override), "extensions-published.json");
55979
+ return import_node_path49.default.join(clawdHomeRoot(override), "extensions-published.json");
56045
55980
  }
56046
55981
  function bundleCacheRoot(override) {
56047
- return import_node_path46.default.join(clawdHomeRoot(override), "extension-bundles");
55982
+ return import_node_path49.default.join(clawdHomeRoot(override), "extension-bundles");
56048
55983
  }
56049
55984
 
56050
55985
  // src/extension/install-flow.ts
@@ -56071,7 +56006,7 @@ async function installFromChannel(args, deps) {
56071
56006
  throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
56072
56007
  }
56073
56008
  for (const name of Object.keys(zip.files)) {
56074
- if (name.includes("..") || name.startsWith("/") || import_node_path47.default.isAbsolute(name)) {
56009
+ if (name.includes("..") || name.startsWith("/") || import_node_path50.default.isAbsolute(name)) {
56075
56010
  throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
56076
56011
  }
56077
56012
  }
@@ -56103,7 +56038,7 @@ async function installFromChannel(args, deps) {
56103
56038
  );
56104
56039
  }
56105
56040
  const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
56106
- const destDir = import_node_path47.default.join(deps.extensionsRoot, localExtId);
56041
+ const destDir = import_node_path50.default.join(deps.extensionsRoot, localExtId);
56107
56042
  let destExists = false;
56108
56043
  try {
56109
56044
  await import_promises7.default.access(destDir);
@@ -56117,16 +56052,16 @@ async function installFromChannel(args, deps) {
56117
56052
  );
56118
56053
  }
56119
56054
  const stage = await import_promises7.default.mkdtemp(
56120
- import_node_path47.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
56055
+ import_node_path50.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
56121
56056
  );
56122
56057
  try {
56123
56058
  for (const [name, entry] of Object.entries(zip.files)) {
56124
- const dest = import_node_path47.default.join(stage, name);
56059
+ const dest = import_node_path50.default.join(stage, name);
56125
56060
  if (entry.dir) {
56126
56061
  await import_promises7.default.mkdir(dest, { recursive: true });
56127
56062
  continue;
56128
56063
  }
56129
- await import_promises7.default.mkdir(import_node_path47.default.dirname(dest), { recursive: true });
56064
+ await import_promises7.default.mkdir(import_node_path50.default.dirname(dest), { recursive: true });
56130
56065
  if (name === "manifest.json") {
56131
56066
  const rewritten = { ...parsed.data, id: localExtId };
56132
56067
  await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -56147,7 +56082,7 @@ async function installFromChannel(args, deps) {
56147
56082
 
56148
56083
  // src/extension/update-flow.ts
56149
56084
  var import_promises8 = __toESM(require("fs/promises"), 1);
56150
- var import_node_path48 = __toESM(require("path"), 1);
56085
+ var import_node_path51 = __toESM(require("path"), 1);
56151
56086
  var import_node_os20 = __toESM(require("os"), 1);
56152
56087
  var import_node_crypto16 = __toESM(require("crypto"), 1);
56153
56088
  var import_jszip4 = __toESM(require_lib3(), 1);
@@ -56164,11 +56099,11 @@ async function updateFromChannel(args, deps) {
56164
56099
  channelRef.extId,
56165
56100
  channelRef.ownerPrincipalId
56166
56101
  );
56167
- const liveDir = import_node_path48.default.join(deps.extensionsRoot, localExtId);
56102
+ const liveDir = import_node_path51.default.join(deps.extensionsRoot, localExtId);
56168
56103
  const prevDir = `${liveDir}.prev`;
56169
56104
  let existingVersion;
56170
56105
  try {
56171
- const raw = await import_promises8.default.readFile(import_node_path48.default.join(liveDir, "manifest.json"), "utf8");
56106
+ const raw = await import_promises8.default.readFile(import_node_path51.default.join(liveDir, "manifest.json"), "utf8");
56172
56107
  const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
56173
56108
  if (!parsed2.success) {
56174
56109
  throw new UpdateError(
@@ -56201,7 +56136,7 @@ async function updateFromChannel(args, deps) {
56201
56136
  throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
56202
56137
  }
56203
56138
  for (const name of Object.keys(zip.files)) {
56204
- if (name.includes("..") || name.startsWith("/") || import_node_path48.default.isAbsolute(name)) {
56139
+ if (name.includes("..") || name.startsWith("/") || import_node_path51.default.isAbsolute(name)) {
56205
56140
  throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
56206
56141
  }
56207
56142
  }
@@ -56236,16 +56171,16 @@ async function updateFromChannel(args, deps) {
56236
56171
  await import_promises8.default.rm(prevDir, { recursive: true, force: true });
56237
56172
  await import_promises8.default.rename(liveDir, prevDir);
56238
56173
  const stage = await import_promises8.default.mkdtemp(
56239
- import_node_path48.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
56174
+ import_node_path51.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
56240
56175
  );
56241
56176
  try {
56242
56177
  for (const [name, entry] of Object.entries(zip.files)) {
56243
- const dest = import_node_path48.default.join(stage, name);
56178
+ const dest = import_node_path51.default.join(stage, name);
56244
56179
  if (entry.dir) {
56245
56180
  await import_promises8.default.mkdir(dest, { recursive: true });
56246
56181
  continue;
56247
56182
  }
56248
- await import_promises8.default.mkdir(import_node_path48.default.dirname(dest), { recursive: true });
56183
+ await import_promises8.default.mkdir(import_node_path51.default.dirname(dest), { recursive: true });
56249
56184
  if (name === "manifest.json") {
56250
56185
  const rewritten = { ...parsed.data, id: localExtId };
56251
56186
  await import_promises8.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -56338,7 +56273,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
56338
56273
  );
56339
56274
  }
56340
56275
  }
56341
- const manifestPath = import_node_path49.default.join(root, extId, "manifest.json");
56276
+ const manifestPath = import_node_path52.default.join(root, extId, "manifest.json");
56342
56277
  const manifest = await readManifest(root, extId);
56343
56278
  const next = { ...manifest, version: newVersion };
56344
56279
  const tmp = `${manifestPath}.tmp`;
@@ -56346,7 +56281,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
56346
56281
  await import_promises9.default.rename(tmp, manifestPath);
56347
56282
  }
56348
56283
  async function readManifest(root, extId) {
56349
- const file = import_node_path49.default.join(root, extId, "manifest.json");
56284
+ const file = import_node_path52.default.join(root, extId, "manifest.json");
56350
56285
  let raw;
56351
56286
  try {
56352
56287
  raw = await import_promises9.default.readFile(file, "utf8");
@@ -56437,7 +56372,7 @@ function buildExtensionHandlers(deps) {
56437
56372
  };
56438
56373
  async function buildSnapshotMeta(extId) {
56439
56374
  const manifest = await readManifest(deps.root, extId);
56440
- const { sha256, buffer } = await bundleExtensionDir(import_node_path49.default.join(deps.root, extId));
56375
+ const { sha256, buffer } = await bundleExtensionDir(import_node_path52.default.join(deps.root, extId));
56441
56376
  return { manifest, contentHash: sha256, buffer };
56442
56377
  }
56443
56378
  const publish = async (frame, _client, ctx) => {
@@ -56618,9 +56553,9 @@ function buildExtensionHandlers(deps) {
56618
56553
  }
56619
56554
 
56620
56555
  // src/app-builder/project-store.ts
56621
- var import_node_fs42 = require("fs");
56622
- var import_node_child_process11 = require("child_process");
56623
- var import_node_path50 = require("path");
56556
+ var import_node_fs43 = require("fs");
56557
+ var import_node_child_process12 = require("child_process");
56558
+ var import_node_path53 = require("path");
56624
56559
  init_protocol();
56625
56560
  var PROJECTS_DIR = "projects";
56626
56561
  var META_FILE = ".clawd-project.json";
@@ -56634,19 +56569,19 @@ var ProjectStore = class {
56634
56569
  root;
56635
56570
  /** projects/<name>/.clawd-project.json 路径 */
56636
56571
  metaPath(name) {
56637
- return (0, import_node_path50.join)(this.projectsRoot(), name, META_FILE);
56572
+ return (0, import_node_path53.join)(this.projectsRoot(), name, META_FILE);
56638
56573
  }
56639
56574
  /** projects/<name>/ 目录路径(cwd 用) */
56640
56575
  projectDir(name) {
56641
- return (0, import_node_path50.join)(this.projectsRoot(), name);
56576
+ return (0, import_node_path53.join)(this.projectsRoot(), name);
56642
56577
  }
56643
56578
  projectsRoot() {
56644
- return (0, import_node_path50.join)(this.root, PROJECTS_DIR);
56579
+ return (0, import_node_path53.join)(this.root, PROJECTS_DIR);
56645
56580
  }
56646
56581
  async list() {
56647
56582
  let entries;
56648
56583
  try {
56649
- entries = await import_node_fs42.promises.readdir(this.projectsRoot());
56584
+ entries = await import_node_fs43.promises.readdir(this.projectsRoot());
56650
56585
  } catch (err) {
56651
56586
  if (err.code === "ENOENT") return [];
56652
56587
  throw err;
@@ -56654,7 +56589,7 @@ var ProjectStore = class {
56654
56589
  const out = [];
56655
56590
  for (const name of entries) {
56656
56591
  try {
56657
- const raw = await import_node_fs42.promises.readFile(this.metaPath(name), "utf8");
56592
+ const raw = await import_node_fs43.promises.readFile(this.metaPath(name), "utf8");
56658
56593
  const json = JSON.parse(raw);
56659
56594
  let migrated = false;
56660
56595
  if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
@@ -56665,7 +56600,7 @@ var ProjectStore = class {
56665
56600
  if (parsed.success) {
56666
56601
  out.push(parsed.data);
56667
56602
  if (migrated) {
56668
- void import_node_fs42.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
56603
+ void import_node_fs43.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
56669
56604
  });
56670
56605
  }
56671
56606
  }
@@ -56709,8 +56644,8 @@ var ProjectStore = class {
56709
56644
  throw new Error(`invalid name "${name}": ${validated.error.message}`);
56710
56645
  }
56711
56646
  const dir = this.projectDir(name);
56712
- await import_node_fs42.promises.mkdir(dir, { recursive: true });
56713
- await import_node_fs42.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
56647
+ await import_node_fs43.promises.mkdir(dir, { recursive: true });
56648
+ await import_node_fs43.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
56714
56649
  return meta;
56715
56650
  }
56716
56651
  /**
@@ -56724,7 +56659,7 @@ var ProjectStore = class {
56724
56659
  async scaffold(name, templateSrcDir, scaffoldScriptPath) {
56725
56660
  const destDir = this.projectDir(name);
56726
56661
  return await new Promise((resolve6, reject) => {
56727
- const child = (0, import_node_child_process11.spawn)("bash", [scaffoldScriptPath, name, templateSrcDir, destDir], {
56662
+ const child = (0, import_node_child_process12.spawn)("bash", [scaffoldScriptPath, name, templateSrcDir, destDir], {
56728
56663
  env: { ...process.env, PATH: process.env.PATH ?? "" },
56729
56664
  stdio: ["ignore", "pipe", "pipe"]
56730
56665
  });
@@ -56753,7 +56688,7 @@ var ProjectStore = class {
56753
56688
  }
56754
56689
  async delete(name) {
56755
56690
  const dir = this.projectDir(name);
56756
- await import_node_fs42.promises.rm(dir, { recursive: true, force: true });
56691
+ await import_node_fs43.promises.rm(dir, { recursive: true, force: true });
56757
56692
  }
56758
56693
  async updatePort(name, newPort) {
56759
56694
  if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
@@ -56769,7 +56704,7 @@ var ProjectStore = class {
56769
56704
  throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
56770
56705
  }
56771
56706
  const updated = { ...target, port: newPort };
56772
- await import_node_fs42.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
56707
+ await import_node_fs43.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
56773
56708
  return updated;
56774
56709
  }
56775
56710
  /**
@@ -56786,7 +56721,7 @@ var ProjectStore = class {
56786
56721
  if (!validated.success) {
56787
56722
  throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
56788
56723
  }
56789
- await import_node_fs42.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
56724
+ await import_node_fs43.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
56790
56725
  return validated.data;
56791
56726
  }
56792
56727
  /**
@@ -56807,7 +56742,7 @@ var ProjectStore = class {
56807
56742
  if (!validated.success) {
56808
56743
  throw new Error(`invalid publishJob: ${validated.error.message}`);
56809
56744
  }
56810
- await import_node_fs42.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
56745
+ await import_node_fs43.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
56811
56746
  return validated.data;
56812
56747
  }
56813
56748
  /** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
@@ -56822,13 +56757,13 @@ var ProjectStore = class {
56822
56757
  if (!validated.success) {
56823
56758
  throw new Error(`failed to clear publishJob: ${validated.error.message}`);
56824
56759
  }
56825
- await import_node_fs42.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
56760
+ await import_node_fs43.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
56826
56761
  return validated.data;
56827
56762
  }
56828
56763
  };
56829
56764
 
56830
56765
  // src/app-builder/kill-port.ts
56831
- var import_node_child_process12 = require("child_process");
56766
+ var import_node_child_process13 = require("child_process");
56832
56767
  async function killPortOccupants(port, ownedPids, logger) {
56833
56768
  let pids;
56834
56769
  try {
@@ -56870,7 +56805,7 @@ async function killPortOccupants(port, ownedPids, logger) {
56870
56805
  }
56871
56806
  function listPidsOnPort(port) {
56872
56807
  return new Promise((resolve6, reject) => {
56873
- (0, import_node_child_process12.execFile)(
56808
+ (0, import_node_child_process13.execFile)(
56874
56809
  "lsof",
56875
56810
  ["-ti", `:${port}`],
56876
56811
  { timeout: 3e3 },
@@ -56942,9 +56877,9 @@ var PublishJobRegistry = class {
56942
56877
  };
56943
56878
 
56944
56879
  // src/app-builder/publish-job-runner.ts
56945
- var import_node_child_process13 = require("child_process");
56946
- var import_node_fs43 = require("fs");
56947
- var import_node_path51 = require("path");
56880
+ var import_node_child_process14 = require("child_process");
56881
+ var import_node_fs44 = require("fs");
56882
+ var import_node_path54 = require("path");
56948
56883
 
56949
56884
  // src/app-builder/publish-stage-parser.ts
56950
56885
  var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
@@ -56971,15 +56906,15 @@ function tailStderrLines(buf, n) {
56971
56906
  // src/app-builder/publish-job-runner.ts
56972
56907
  async function startPublishJob(deps, args) {
56973
56908
  const { registry: registry2, projectDir } = deps;
56974
- const spawn13 = deps.spawnImpl ?? import_node_child_process13.spawn;
56909
+ const spawn13 = deps.spawnImpl ?? import_node_child_process14.spawn;
56975
56910
  if (registry2.has(args.name)) {
56976
56911
  return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
56977
56912
  }
56978
56913
  const projDir = projectDir(args.name);
56979
- const logPath = (0, import_node_path51.join)(projDir, ".publish.log");
56914
+ const logPath = (0, import_node_path54.join)(projDir, ".publish.log");
56980
56915
  let logStream = null;
56981
56916
  try {
56982
- logStream = (0, import_node_fs43.createWriteStream)(logPath, { flags: "w" });
56917
+ logStream = (0, import_node_fs44.createWriteStream)(logPath, { flags: "w" });
56983
56918
  } catch {
56984
56919
  logStream = null;
56985
56920
  }
@@ -57236,8 +57171,8 @@ async function recoverInterruptedJobs(deps) {
57236
57171
 
57237
57172
  // src/handlers/app-builder.ts
57238
57173
  init_protocol();
57239
- var import_node_path52 = require("path");
57240
- var import_node_fs44 = require("fs");
57174
+ var import_node_path55 = require("path");
57175
+ var import_node_fs45 = require("fs");
57241
57176
  var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
57242
57177
  var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
57243
57178
  async function recoverInterruptedPublishJobs(store, logger) {
@@ -57318,7 +57253,7 @@ function buildAppBuilderHandlers(deps) {
57318
57253
  async function listAllUsersProjects() {
57319
57254
  if (!deps.usersRoot || !deps.getStore) return [];
57320
57255
  const getStore = deps.getStore;
57321
- const userIds = await import_node_fs44.promises.readdir(deps.usersRoot).catch(() => []);
57256
+ const userIds = await import_node_fs45.promises.readdir(deps.usersRoot).catch(() => []);
57322
57257
  const perUser = await Promise.all(
57323
57258
  userIds.map((uid) => getStore(uid).list().catch(() => []))
57324
57259
  );
@@ -57394,8 +57329,8 @@ function buildAppBuilderHandlers(deps) {
57394
57329
  const project = await userStore.create(f.name, reservedPorts);
57395
57330
  try {
57396
57331
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
57397
- const templateSrcDir = (0, import_node_path52.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
57398
- const scaffoldScript = (0, import_node_path52.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
57332
+ const templateSrcDir = (0, import_node_path55.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
57333
+ const scaffoldScript = (0, import_node_path55.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
57399
57334
  const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
57400
57335
  deps.logger?.info("app-builder.scaffold.done", {
57401
57336
  name: project.name,
@@ -57616,7 +57551,7 @@ function buildAppBuilderHandlers(deps) {
57616
57551
  await userStore.clearPublishJob(args.name);
57617
57552
  }
57618
57553
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
57619
- const scriptPath = (0, import_node_path52.join)(deps.deployKitRoot, "scripts", "publish.sh");
57554
+ const scriptPath = (0, import_node_path55.join)(deps.deployKitRoot, "scripts", "publish.sh");
57620
57555
  deps.logger?.info("app-builder.publish.start", {
57621
57556
  name: args.name,
57622
57557
  sessionId: boundSession.sessionId,
@@ -57792,7 +57727,7 @@ function buildVisitorHandlers(deps) {
57792
57727
 
57793
57728
  // src/extension/registry.ts
57794
57729
  var import_promises10 = __toESM(require("fs/promises"), 1);
57795
- var import_node_path53 = __toESM(require("path"), 1);
57730
+ var import_node_path56 = __toESM(require("path"), 1);
57796
57731
  async function loadAll(root) {
57797
57732
  let entries;
57798
57733
  try {
@@ -57805,13 +57740,13 @@ async function loadAll(root) {
57805
57740
  for (const ent of entries) {
57806
57741
  if (!ent.isDirectory()) continue;
57807
57742
  if (ent.name.startsWith(".")) continue;
57808
- records.push(await loadOne(import_node_path53.default.join(root, ent.name), ent.name));
57743
+ records.push(await loadOne(import_node_path56.default.join(root, ent.name), ent.name));
57809
57744
  }
57810
57745
  records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
57811
57746
  return records;
57812
57747
  }
57813
57748
  async function loadOne(dir, dirName) {
57814
- const manifestPath = import_node_path53.default.join(dir, "manifest.json");
57749
+ const manifestPath = import_node_path56.default.join(dir, "manifest.json");
57815
57750
  let raw;
57816
57751
  try {
57817
57752
  raw = await import_promises10.default.readFile(manifestPath, "utf8");
@@ -57856,7 +57791,7 @@ async function loadOne(dir, dirName) {
57856
57791
 
57857
57792
  // src/extension/uninstall.ts
57858
57793
  var import_promises11 = __toESM(require("fs/promises"), 1);
57859
- var import_node_path54 = __toESM(require("path"), 1);
57794
+ var import_node_path57 = __toESM(require("path"), 1);
57860
57795
  var UninstallError = class extends Error {
57861
57796
  constructor(code, message) {
57862
57797
  super(message);
@@ -57865,7 +57800,7 @@ var UninstallError = class extends Error {
57865
57800
  code;
57866
57801
  };
57867
57802
  async function uninstall(deps) {
57868
- const dir = import_node_path54.default.join(deps.root, deps.extId);
57803
+ const dir = import_node_path57.default.join(deps.root, deps.extId);
57869
57804
  try {
57870
57805
  await import_promises11.default.access(dir);
57871
57806
  } catch {
@@ -57882,7 +57817,7 @@ var import_node_crypto18 = require("crypto");
57882
57817
  init_protocol();
57883
57818
 
57884
57819
  // src/peer-ops/run-command.ts
57885
- var import_node_child_process14 = require("child_process");
57820
+ var import_node_child_process15 = require("child_process");
57886
57821
  var import_node_os21 = __toESM(require("os"), 1);
57887
57822
  var DEFAULT_TIMEOUT_MS5 = 3e4;
57888
57823
  var MAX_TIMEOUT_MS = 3e5;
@@ -57890,7 +57825,7 @@ var MAX_OUTPUT_BYTES = 1048576;
57890
57825
  function runCommand(command, opts = {}) {
57891
57826
  const timeoutMs = Math.min(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS5, MAX_TIMEOUT_MS);
57892
57827
  return new Promise((resolve6) => {
57893
- const child = (0, import_node_child_process14.spawn)("bash", ["-c", command], { cwd: import_node_os21.default.homedir() });
57828
+ const child = (0, import_node_child_process15.spawn)("bash", ["-c", command], { cwd: import_node_os21.default.homedir() });
57894
57829
  const out = [];
57895
57830
  const err = [];
57896
57831
  let outBytes = 0;
@@ -58022,9 +57957,6 @@ function buildMethodHandlers(deps) {
58022
57957
  personaManager: deps.personaManager,
58023
57958
  personaRegistry: deps.personaRegistry
58024
57959
  }),
58025
- ...buildCapabilityHandlers({
58026
- manager: deps.capabilityManager
58027
- }),
58028
57960
  ...buildInboxHandlers({
58029
57961
  manager: deps.inboxManager,
58030
57962
  sendDmDeps: {
@@ -58060,8 +57992,7 @@ function buildMethodHandlers(deps) {
58060
57992
  ownerPrincipalId: deps.ownerPrincipalId,
58061
57993
  ownerId: deps.ownerId,
58062
57994
  ownerProvider: deps.ownerProvider,
58063
- personaStore: deps.personaStore,
58064
- capabilityManager: deps.capabilityManager
57995
+ personaStore: deps.personaStore
58065
57996
  }),
58066
57997
  ...buildFeishuAuthHandlers(deps.feishuAuth),
58067
57998
  ...buildDeviceHandlers(deps.feishuDevice),
@@ -58104,7 +58035,7 @@ function buildMethodHandlers(deps) {
58104
58035
  }
58105
58036
 
58106
58037
  // src/app-builder/dev-server-supervisor.ts
58107
- var import_node_child_process15 = require("child_process");
58038
+ var import_node_child_process16 = require("child_process");
58108
58039
  var import_node_events2 = require("events");
58109
58040
  var DEFAULT_READY_PATTERN = /Local:\s+https?:\/\/|Nest application successfully started|server listening on/i;
58110
58041
  var DevServerSupervisor = class extends import_node_events2.EventEmitter {
@@ -58141,7 +58072,7 @@ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
58141
58072
  tunnelHost: args.tunnelHost,
58142
58073
  devCommand: cmd
58143
58074
  });
58144
- const child = (0, import_node_child_process15.spawn)("sh", ["-c", cmd], {
58075
+ const child = (0, import_node_child_process16.spawn)("sh", ["-c", cmd], {
58145
58076
  cwd: args.cwd,
58146
58077
  env,
58147
58078
  stdio: "pipe",
@@ -58375,12 +58306,6 @@ var METHOD_GRANT_MAP = {
58375
58306
  // handler 内部按 ctx.principal.kind 决定返回 "合成 owner self capability" 还是
58376
58307
  // "查 capabilityManager 拿 caller 的 guest capability"。
58377
58308
  "whoami": { kind: "public" },
58378
- // ---- capability platform(admin-only) ----
58379
- // 飞书统一身份 Phase 2 (2026-06-01, spec 决策 #12): personal-cap:get / capability:issue /
58380
- // bindPeer 已下线(personal token / 邀请码全链路删除)。capability:list / delete 保留供调试 +
58381
- // 撤销级联 + 清旧 per-peer cap。
58382
- "capability:list": ADMIN_ANY,
58383
- "capability:delete": ADMIN_ANY,
58384
58309
  // ---- inbox: 双投 P2P IM ----
58385
58310
  // 三条都 'public' — 能连上 = 已通过本地验票. handler 内额外校验 guest 的
58386
58311
  // args.peerDeviceId 与 ctx.principal.id(=deviceId) 一致, 防 guest 越权操作别的 DM 桶.
@@ -58584,8 +58509,8 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
58584
58509
  }
58585
58510
 
58586
58511
  // src/extension/runtime.ts
58587
- var import_node_child_process16 = require("child_process");
58588
- var import_node_path55 = __toESM(require("path"), 1);
58512
+ var import_node_child_process17 = require("child_process");
58513
+ var import_node_path58 = __toESM(require("path"), 1);
58589
58514
  var import_promises12 = require("timers/promises");
58590
58515
 
58591
58516
  // src/extension/port-allocator.ts
@@ -58686,13 +58611,13 @@ var Runtime = class {
58686
58611
  /\$CLAWOS_EXT_PORT/g,
58687
58612
  String(port)
58688
58613
  );
58689
- const dir = import_node_path55.default.join(this.root, extId);
58614
+ const dir = import_node_path58.default.join(this.root, extId);
58690
58615
  const env = {
58691
58616
  ...process.env,
58692
58617
  CLAWOS_EXT_PORT: String(port),
58693
58618
  CLAWOS_EXT_ID: extId
58694
58619
  };
58695
- const child = (0, import_node_child_process16.spawn)("sh", ["-c", cmd], {
58620
+ const child = (0, import_node_child_process17.spawn)("sh", ["-c", cmd], {
58696
58621
  cwd: dir,
58697
58622
  env,
58698
58623
  stdio: ["ignore", "pipe", "pipe"],
@@ -58798,7 +58723,7 @@ ${handle.stderrTail}`
58798
58723
 
58799
58724
  // src/extension/published-channels.ts
58800
58725
  var import_promises13 = __toESM(require("fs/promises"), 1);
58801
- var import_node_path56 = __toESM(require("path"), 1);
58726
+ var import_node_path59 = __toESM(require("path"), 1);
58802
58727
  init_zod();
58803
58728
  var PublishedChannelsError = class extends Error {
58804
58729
  constructor(code, message) {
@@ -58897,7 +58822,7 @@ var PublishedChannelStore = class {
58897
58822
  )
58898
58823
  };
58899
58824
  const tmp = `${this.filePath}.tmp`;
58900
- await import_promises13.default.mkdir(import_node_path56.default.dirname(this.filePath), { recursive: true });
58825
+ await import_promises13.default.mkdir(import_node_path59.default.dirname(this.filePath), { recursive: true });
58901
58826
  await import_promises13.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
58902
58827
  await import_promises13.default.rename(tmp, this.filePath);
58903
58828
  }
@@ -58905,7 +58830,7 @@ var PublishedChannelStore = class {
58905
58830
 
58906
58831
  // src/extension/bundle-cache.ts
58907
58832
  var import_promises14 = __toESM(require("fs/promises"), 1);
58908
- var import_node_path57 = __toESM(require("path"), 1);
58833
+ var import_node_path60 = __toESM(require("path"), 1);
58909
58834
  var BundleCache = class {
58910
58835
  constructor(rootDir) {
58911
58836
  this.rootDir = rootDir;
@@ -58914,14 +58839,14 @@ var BundleCache = class {
58914
58839
  /** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
58915
58840
  async write(snapshotHash, buffer) {
58916
58841
  await import_promises14.default.mkdir(this.rootDir, { recursive: true });
58917
- const file = import_node_path57.default.join(this.rootDir, `${snapshotHash}.zip`);
58842
+ const file = import_node_path60.default.join(this.rootDir, `${snapshotHash}.zip`);
58918
58843
  const tmp = `${file}.tmp`;
58919
58844
  await import_promises14.default.writeFile(tmp, buffer, { mode: 384 });
58920
58845
  await import_promises14.default.rename(tmp, file);
58921
58846
  }
58922
58847
  /** Returns the bundle bytes, or null when the file doesn't exist. */
58923
58848
  async read(snapshotHash) {
58924
- const file = import_node_path57.default.join(this.rootDir, `${snapshotHash}.zip`);
58849
+ const file = import_node_path60.default.join(this.rootDir, `${snapshotHash}.zip`);
58925
58850
  try {
58926
58851
  return await import_promises14.default.readFile(file);
58927
58852
  } catch (e) {
@@ -58931,7 +58856,7 @@ var BundleCache = class {
58931
58856
  }
58932
58857
  /** Idempotent — missing file is not an error. */
58933
58858
  async delete(snapshotHash) {
58934
- const file = import_node_path57.default.join(this.rootDir, `${snapshotHash}.zip`);
58859
+ const file = import_node_path60.default.join(this.rootDir, `${snapshotHash}.zip`);
58935
58860
  await import_promises14.default.rm(file, { force: true });
58936
58861
  }
58937
58862
  };
@@ -58954,24 +58879,24 @@ async function startDaemon(config) {
58954
58879
  sampling: logShippingCfg.sampling,
58955
58880
  homeDir: import_node_os22.default.homedir()
58956
58881
  });
58957
- const logDir = import_node_path58.default.join(config.dataDir, "log");
58958
- import_node_fs45.default.mkdirSync(logDir, { recursive: true });
58882
+ const logDir = import_node_path61.default.join(config.dataDir, "log");
58883
+ import_node_fs46.default.mkdirSync(logDir, { recursive: true });
58959
58884
  const logger = createLogger({
58960
58885
  level: config.logLevel,
58961
- file: import_node_path58.default.join(logDir, "clawd.log"),
58886
+ file: import_node_path61.default.join(logDir, "clawd.log"),
58962
58887
  logClient
58963
58888
  });
58964
58889
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
58965
58890
  const screenIdleProbeLogger = createFileOnlyLogger({
58966
- file: import_node_path58.default.join(logDir, "screen-idle-probe.log"),
58891
+ file: import_node_path61.default.join(logDir, "screen-idle-probe.log"),
58967
58892
  level: "debug"
58968
58893
  });
58969
58894
  logger.info("screen-idle probe logger enabled", {
58970
- file: import_node_path58.default.join(logDir, "screen-idle-probe.log")
58895
+ file: import_node_path61.default.join(logDir, "screen-idle-probe.log")
58971
58896
  });
58972
58897
  const CAP_TARGETS = [
58973
- import_node_path58.default.join(logDir, "clawd.log"),
58974
- import_node_path58.default.join(logDir, "screen-idle-probe.log")
58898
+ import_node_path61.default.join(logDir, "clawd.log"),
58899
+ import_node_path61.default.join(logDir, "screen-idle-probe.log")
58975
58900
  ];
58976
58901
  const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
58977
58902
  const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
@@ -59040,32 +58965,13 @@ async function startDaemon(config) {
59040
58965
  }
59041
58966
  } : {}
59042
58967
  });
59043
- const capabilityStore = new CapabilityStore(config.dataDir);
59044
- const capabilityRegistry = new CapabilityRegistry(capabilityStore);
59045
- const capabilityManager = new CapabilityManager(capabilityRegistry, {
59046
- onDeleted: (cap) => {
59047
- const deletedAt = Date.now();
59048
- wsServer?.broadcastToOwners({
59049
- type: "capability:tokenDeleted",
59050
- capabilityId: cap.id,
59051
- deletedAt
59052
- });
59053
- wsServer?.closeConnectionsByGuestId(cap.id);
59054
- const cleanup = cleanupGuestSessionsForCapability(cap, sessionStoreFactory);
59055
- if (cleanup.removed.length > 0) {
59056
- logger.info("capability delete cascade: guest sessions removed", {
59057
- capabilityId: cap.id,
59058
- removedDirs: cleanup.removed
59059
- });
59060
- }
59061
- }
59062
- });
58968
+ await cleanupLegacyCapabilityFiles(config.dataDir, logger);
59063
58969
  migrateDropPersonsDir(config.dataDir);
59064
58970
  let wsServer = null;
59065
58971
  const authGate = authMode === "first-message" ? new AuthGate({
59066
58972
  shouldEnforce: buildShouldEnforce({ tunnel: config.tunnel }),
59067
58973
  // Task 1.7:authenticate 注入路径替代 expectedToken 单 token 比对。
59068
- // owner 路径 constantTimeEqual 防侧信道;guest 路径走 capabilityRegistry.
58974
+ // owner 路径 constantTimeEqual 防侧信道;guest 路径走 verifyConnectToken (Ed25519).
59069
58975
  expectedToken: resolvedAuthToken,
59070
58976
  authenticate: async (t, selfUrl) => {
59071
58977
  const result = await authenticate(t, buildConnectAuthDeps());
@@ -59125,8 +59031,8 @@ async function startDaemon(config) {
59125
59031
  const agents = new AgentsScanner();
59126
59032
  const history = new ClaudeHistoryReader();
59127
59033
  let transport = null;
59128
- const personaStore = new PersonaStore(import_node_path58.default.join(config.dataDir, "personas"));
59129
- const usersRoot = import_node_path58.default.join(config.dataDir, "users");
59034
+ const personaStore = new PersonaStore(import_node_path61.default.join(config.dataDir, "personas"));
59035
+ const usersRoot = import_node_path61.default.join(config.dataDir, "users");
59130
59036
  const defaultsRoot = findDefaultsRoot(logger);
59131
59037
  if (defaultsRoot) {
59132
59038
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -59146,7 +59052,7 @@ async function startDaemon(config) {
59146
59052
  migrateCodexSandbox({ store: personaStore, logger });
59147
59053
  const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
59148
59054
  const dispatchStore = createDispatchStore({
59149
- filePath: import_node_path58.default.join(config.dataDir, "dispatch.json"),
59055
+ filePath: import_node_path61.default.join(config.dataDir, "dispatch.json"),
59150
59056
  now: () => Date.now(),
59151
59057
  onFlushError: (err) => logger.error("dispatch store flush failed", {
59152
59058
  reason: err instanceof Error ? err.message : String(err)
@@ -59161,17 +59067,17 @@ async function startDaemon(config) {
59161
59067
  now: () => Date.now(),
59162
59068
  onCompleted: (sid) => deliverPendingRef?.(sid)
59163
59069
  });
59164
- const here = typeof __dirname === "string" ? __dirname : import_node_path58.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
59070
+ const here = typeof __dirname === "string" ? __dirname : import_node_path61.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
59165
59071
  const mcpConfigs = [];
59166
59072
  const dispatchServerCandidates = [
59167
- import_node_path58.default.join(here, "dispatch", "mcp-server.cjs"),
59073
+ import_node_path61.default.join(here, "dispatch", "mcp-server.cjs"),
59168
59074
  // 生产 dist/index → dist/dispatch/mcp-server.cjs
59169
- import_node_path58.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
59075
+ import_node_path61.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
59170
59076
  // dev tsx src/index → ../dist/dispatch/mcp-server.cjs
59171
59077
  ];
59172
- const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs45.default.existsSync(p2));
59078
+ const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs46.default.existsSync(p2));
59173
59079
  if (dispatchServerScriptPath) {
59174
- const dispatchLogPath = import_node_path58.default.join(logDir, "dispatch-mcp-server.log");
59080
+ const dispatchLogPath = import_node_path61.default.join(logDir, "dispatch-mcp-server.log");
59175
59081
  const dispatchCfgPath = writeDispatchMcpConfig({
59176
59082
  dataDir: config.dataDir,
59177
59083
  serverScriptPath: dispatchServerScriptPath,
@@ -59189,12 +59095,12 @@ async function startDaemon(config) {
59189
59095
  });
59190
59096
  }
59191
59097
  const ticketServerCandidates = [
59192
- import_node_path58.default.join(here, "ticket", "mcp-server.cjs"),
59193
- import_node_path58.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
59098
+ import_node_path61.default.join(here, "ticket", "mcp-server.cjs"),
59099
+ import_node_path61.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
59194
59100
  ];
59195
- const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs45.default.existsSync(p2));
59101
+ const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs46.default.existsSync(p2));
59196
59102
  if (ticketServerScriptPath) {
59197
- const ticketLogPath = import_node_path58.default.join(logDir, "ticket-mcp-server.log");
59103
+ const ticketLogPath = import_node_path61.default.join(logDir, "ticket-mcp-server.log");
59198
59104
  const ticketCfgPath = writeTicketMcpConfig({
59199
59105
  dataDir: config.dataDir,
59200
59106
  serverScriptPath: ticketServerScriptPath,
@@ -59214,12 +59120,12 @@ async function startDaemon(config) {
59214
59120
  });
59215
59121
  }
59216
59122
  const shiftServerCandidates = [
59217
- import_node_path58.default.join(here, "shift", "mcp-server.cjs"),
59218
- import_node_path58.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
59123
+ import_node_path61.default.join(here, "shift", "mcp-server.cjs"),
59124
+ import_node_path61.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
59219
59125
  ];
59220
- const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs45.default.existsSync(p2));
59126
+ const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs46.default.existsSync(p2));
59221
59127
  if (shiftServerScriptPath) {
59222
- const shiftLogPath = import_node_path58.default.join(logDir, "shift-mcp-server.log");
59128
+ const shiftLogPath = import_node_path61.default.join(logDir, "shift-mcp-server.log");
59223
59129
  const shiftCfgPath = await writeShiftMcpConfig({
59224
59130
  dataDir: config.dataDir,
59225
59131
  serverScriptPath: shiftServerScriptPath,
@@ -59238,12 +59144,12 @@ async function startDaemon(config) {
59238
59144
  );
59239
59145
  }
59240
59146
  const inboxServerCandidates = [
59241
- import_node_path58.default.join(here, "inbox", "mcp-server.cjs"),
59242
- import_node_path58.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
59147
+ import_node_path61.default.join(here, "inbox", "mcp-server.cjs"),
59148
+ import_node_path61.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
59243
59149
  ];
59244
- const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs45.default.existsSync(p2));
59150
+ const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs46.default.existsSync(p2));
59245
59151
  if (inboxServerScriptPath) {
59246
- const inboxLogPath = import_node_path58.default.join(logDir, "inbox-mcp-server.log");
59152
+ const inboxLogPath = import_node_path61.default.join(logDir, "inbox-mcp-server.log");
59247
59153
  const inboxCfgPath = await writeInboxMcpConfig({
59248
59154
  dataDir: config.dataDir,
59249
59155
  serverScriptPath: inboxServerScriptPath,
@@ -59262,10 +59168,10 @@ async function startDaemon(config) {
59262
59168
  );
59263
59169
  }
59264
59170
  const peerOpsServerCandidates = [
59265
- import_node_path58.default.join(here, "peer-ops", "mcp-server.cjs"),
59266
- import_node_path58.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
59171
+ import_node_path61.default.join(here, "peer-ops", "mcp-server.cjs"),
59172
+ import_node_path61.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
59267
59173
  ];
59268
- const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs45.default.existsSync(p2));
59174
+ const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs46.default.existsSync(p2));
59269
59175
  if (peerOpsServerScriptPath) {
59270
59176
  const peerOpsCfgPath = writePeerOpsMcpConfig({
59271
59177
  dataDir: config.dataDir,
@@ -59283,7 +59189,7 @@ async function startDaemon(config) {
59283
59189
  );
59284
59190
  }
59285
59191
  const shiftStore = createShiftStore({
59286
- filePath: import_node_path58.default.join(config.dataDir, "shift.json"),
59192
+ filePath: import_node_path61.default.join(config.dataDir, "shift.json"),
59287
59193
  ownerIdProvider: () => ownerPrincipalId,
59288
59194
  now: () => Date.now()
59289
59195
  });
@@ -59305,7 +59211,10 @@ async function startDaemon(config) {
59305
59211
  getAdapter,
59306
59212
  historyReader: history,
59307
59213
  dataDir: config.dataDir,
59308
- personaRoot: import_node_path58.default.join(config.dataDir, "personas"),
59214
+ personaRoot: import_node_path61.default.join(config.dataDir, "personas"),
59215
+ // dispatch / shift 自动化 spawn 按 persona 绑定 tool 起 B session(codex persona 用 codex)。
59216
+ // personaRegistry 声明在 manager 之后(同作用域 const,调用时机在启动完成后,无 TDZ 风险)。
59217
+ getPersonaTool: (personaId) => personaRegistry.get(personaId)?.tool,
59309
59218
  usersRoot,
59310
59219
  personaStore,
59311
59220
  ownerDisplayName,
@@ -59345,10 +59254,10 @@ async function startDaemon(config) {
59345
59254
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
59346
59255
  attachmentGroup: {
59347
59256
  onFileEdit: (input) => {
59348
- const absPath = import_node_path58.default.isAbsolute(input.relPath) ? input.relPath : import_node_path58.default.join(input.cwd, input.relPath);
59257
+ const absPath = import_node_path61.default.isAbsolute(input.relPath) ? input.relPath : import_node_path61.default.join(input.cwd, input.relPath);
59349
59258
  let size = 0;
59350
59259
  try {
59351
- size = import_node_fs45.default.statSync(absPath).size;
59260
+ size = import_node_fs46.default.statSync(absPath).size;
59352
59261
  } catch (err) {
59353
59262
  logger.warn("attachment.onFileEdit stat failed", {
59354
59263
  sessionId: input.sessionId,
@@ -59431,6 +59340,7 @@ async function startDaemon(config) {
59431
59340
  }) : new ClaudeAdapter({ logger, historyReader: new ClaudeHistoryReader() });
59432
59341
  registerAdapter("claude", claudeAdapter);
59433
59342
  registerAdapter("codex", new CodexAdapter({ logger, historyReader: new CodexHistoryReader() }));
59343
+ warmCodexFeatureCache();
59434
59344
  const personaRegistry = new PersonaRegistry(personaStore);
59435
59345
  const personaManager = new PersonaManager({
59436
59346
  store: personaStore,
@@ -59578,15 +59488,13 @@ async function startDaemon(config) {
59578
59488
  // 'persona/<pid>/owner',default 走 'default'。
59579
59489
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
59580
59490
  // guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
59581
- personaRoot: import_node_path58.default.join(config.dataDir, "personas"),
59491
+ personaRoot: import_node_path61.default.join(config.dataDir, "personas"),
59582
59492
  usersRoot
59583
59493
  },
59584
59494
  // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
59585
- personaRoot: import_node_path58.default.join(config.dataDir, "personas"),
59495
+ personaRoot: import_node_path61.default.join(config.dataDir, "personas"),
59586
59496
  // v2 多人 persona 隔离:handler 派生 guest user-dir 放行
59587
59497
  usersRoot,
59588
- // capability:list / delete handler 依赖
59589
- capabilityManager,
59590
59498
  // v2 Phase 6: whoami handler 装在 owner principal.displayName + persona 解析
59591
59499
  ownerDisplayName,
59592
59500
  // 决策 #16: whoami owner.id = 本机 deviceId(路由主键)+ ownerId/provider 归属人
@@ -59596,8 +59504,6 @@ async function startDaemon(config) {
59596
59504
  ownerId,
59597
59505
  ownerProvider,
59598
59506
  personaStore,
59599
- // capability handler 也用 (capability:issue 走 registry / capability:list)
59600
- capabilityRegistry,
59601
59507
  // capability platform v3 inbox: handler 接 manager (post/list/markRead),
59602
59508
  // cascade 接 store (list 统计 deletedInboxEvents).
59603
59509
  inboxManager,
@@ -59693,11 +59599,11 @@ async function startDaemon(config) {
59693
59599
  // 发布上线脚手架化 (spec 2026-06-03 §5.2):
59694
59600
  // appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
59695
59601
  // dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
59696
- appBuilderPersonaRoot: import_node_path58.default.join(config.dataDir, "personas", "persona-app-builder"),
59602
+ appBuilderPersonaRoot: import_node_path61.default.join(config.dataDir, "personas", "persona-app-builder"),
59697
59603
  // 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
59698
- deployKitRoot: import_node_path58.default.join(config.dataDir, "deploy-kit"),
59604
+ deployKitRoot: import_node_path61.default.join(config.dataDir, "deploy-kit"),
59699
59605
  // scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
59700
- resolvePersonaRoot: (personaId) => import_node_path58.default.join(config.dataDir, "personas", personaId),
59606
+ resolvePersonaRoot: (personaId) => import_node_path61.default.join(config.dataDir, "personas", personaId),
59701
59607
  // 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
59702
59608
  // 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
59703
59609
  // 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
@@ -59743,7 +59649,7 @@ async function startDaemon(config) {
59743
59649
  }
59744
59650
  let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
59745
59651
  if (sourceFile && sourceFile.toolSessionId) {
59746
- sourceJsonlPath = import_node_path58.default.join(
59652
+ sourceJsonlPath = import_node_path61.default.join(
59747
59653
  import_node_os22.default.homedir(),
59748
59654
  ".claude",
59749
59655
  "projects",
@@ -59789,7 +59695,9 @@ async function startDaemon(config) {
59789
59695
  });
59790
59696
  },
59791
59697
  // B 角色:判断 targetPersona 是否 public(跨设备授权边界,private 拒)。
59792
- getPersonaPublic: (personaId) => personaRegistry.get(personaId)?.public ?? false
59698
+ getPersonaPublic: (personaId) => personaRegistry.get(personaId)?.public ?? false,
59699
+ // model 校验按 target persona 绑定 tool 选表(claude / codex 模型集不同)
59700
+ getPersonaTool: (personaId) => personaRegistry.get(personaId)?.tool
59793
59701
  });
59794
59702
  handlers = { ...handlers, ...dispatchHandlers };
59795
59703
  const shiftHandlers = buildShiftInternalHandlers({
@@ -59930,18 +59838,11 @@ async function startDaemon(config) {
59930
59838
  ),
59931
59839
  protocolVersion: PROTOCOL_VERSION,
59932
59840
  authGate: authGate ?? void 0,
59933
- // noAuth 模式下仍验 capability token: capability platform 漏洞 远端 client
59934
- // cap token noAuth daemon 时若无此 hook fallback owner ctx, whoami 错返
59935
- // capability.id = ownerPrincipalId, 导致 myCapabilityId 写错 inbox channel 写错.
59936
- // 命中 cap attach guest ctx; 空 token / 未命中 → 保持 noAuth 行为 (handler fallback owner).
59841
+ // T-35: capability path retired;本 hook 只剩 persona web 分享 visitor token 分支:
59842
+ // noAuth localhost daemon 上访客 token 若命中 attach guest ctx,否则 fallback owner
59843
+ // (非 persona-web 场景静默 return null;tunnel 模式走 AuthGate 不经此路)。参数名
59844
+ // tryVerifyCapabilityToken 不再准确,跨 local-ws-server 改名属 followup 清理。
59937
59845
  tryVerifyCapabilityToken: (token) => {
59938
- const v2 = capabilityRegistry.verifyToken(token);
59939
- if (v2.ok) {
59940
- return {
59941
- principal: { id: v2.capability.id, kind: "guest", displayName: v2.capability.displayName },
59942
- grants: v2.capability.grants
59943
- };
59944
- }
59945
59846
  const vis = verifyVisitorToken(authFile.visitorTokenSecret, token, Math.floor(Date.now() / 1e3));
59946
59847
  if (vis.ok) return visitorGuestContext(vis.payload);
59947
59848
  return null;
@@ -60061,8 +59962,8 @@ async function startDaemon(config) {
60061
59962
  const lines = [
60062
59963
  `Tunnel: ${r.url}`,
60063
59964
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
60064
- `Frpc config: ${import_node_path58.default.join(config.dataDir, "frpc.toml")}`,
60065
- `Frpc log: ${import_node_path58.default.join(logDir, "frpc.log")}`
59965
+ `Frpc config: ${import_node_path61.default.join(config.dataDir, "frpc.toml")}`,
59966
+ `Frpc log: ${import_node_path61.default.join(logDir, "frpc.log")}`
60066
59967
  ];
60067
59968
  const width = Math.max(...lines.map((l) => l.length));
60068
59969
  const bar = "\u2550".repeat(width + 4);
@@ -60075,8 +59976,8 @@ ${bar}
60075
59976
 
60076
59977
  `);
60077
59978
  try {
60078
- const connectPath = import_node_path58.default.join(config.dataDir, "connect.txt");
60079
- import_node_fs45.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
59979
+ const connectPath = import_node_path61.default.join(config.dataDir, "connect.txt");
59980
+ import_node_fs46.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
60080
59981
  } catch {
60081
59982
  }
60082
59983
  } catch (err) {
@@ -60148,9 +60049,9 @@ ${bar}
60148
60049
  };
60149
60050
  }
60150
60051
  function migrateDropPersonsDir(dataDir) {
60151
- const dir = import_node_path58.default.join(dataDir, "persons");
60052
+ const dir = import_node_path61.default.join(dataDir, "persons");
60152
60053
  try {
60153
- import_node_fs45.default.rmSync(dir, { recursive: true, force: true });
60054
+ import_node_fs46.default.rmSync(dir, { recursive: true, force: true });
60154
60055
  } catch {
60155
60056
  }
60156
60057
  }