@clawos-dev/clawd 0.2.292 → 0.2.294

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
@@ -450,8 +450,8 @@ var init_parseUtil = __esm({
450
450
  init_errors();
451
451
  init_en();
452
452
  makeIssue = (params) => {
453
- const { data, path: path78, errorMaps, issueData } = params;
454
- const fullPath = [...path78, ...issueData.path || []];
453
+ const { data, path: path79, errorMaps, issueData } = params;
454
+ const fullPath = [...path79, ...issueData.path || []];
455
455
  const fullIssue = {
456
456
  ...issueData,
457
457
  path: fullPath
@@ -762,11 +762,11 @@ var init_types = __esm({
762
762
  init_parseUtil();
763
763
  init_util();
764
764
  ParseInputLazyPath = class {
765
- constructor(parent, value, path78, key) {
765
+ constructor(parent, value, path79, key) {
766
766
  this._cachedPath = [];
767
767
  this.parent = parent;
768
768
  this.data = value;
769
- this._path = path78;
769
+ this._path = path79;
770
770
  this._key = key;
771
771
  }
772
772
  get path() {
@@ -7034,8 +7034,10 @@ var init_schemas = __esm({
7034
7034
  * 由 daemon lark-bot-connector 模块写入,UI 只读(sidebar 按此字段隐藏,Drawer 群清单按此聚合)。
7035
7035
  */
7036
7036
  larkChatId: external_exports.string().min(1).optional(),
7037
- /** 飞书群名快照(展示用,随事件里的群名更新,可能滞后于飞书侧改名) */
7037
+ /** 飞书会话名快照(展示用):群会话为群名、私聊会话为对方用户名;随事件更新,可能滞后 */
7038
7038
  larkChatName: external_exports.string().min(1).optional(),
7039
+ /** lark 会话类型(spec 2026-08-06 P9):p2p 与群的 chat_id 形态无法区分,UI 文案分支需要。可选=兼容老数据(缺省按 group 展示) */
7040
+ larkChatType: external_exports.enum(["p2p", "group"]).optional(),
7039
7041
  createdAt: external_exports.string().min(1),
7040
7042
  updatedAt: external_exports.string().min(1)
7041
7043
  });
@@ -8769,8 +8771,8 @@ var require_req = __commonJS({
8769
8771
  if (req.originalUrl) {
8770
8772
  _req.url = req.originalUrl;
8771
8773
  } else {
8772
- const path78 = req.path;
8773
- _req.url = typeof path78 === "string" ? path78 : req.url ? req.url.path || req.url : void 0;
8774
+ const path79 = req.path;
8775
+ _req.url = typeof path79 === "string" ? path79 : req.url ? req.url.path || req.url : void 0;
8774
8776
  }
8775
8777
  if (req.query) {
8776
8778
  _req.query = req.query;
@@ -8935,14 +8937,14 @@ var require_redact = __commonJS({
8935
8937
  }
8936
8938
  return obj;
8937
8939
  }
8938
- function parsePath(path78) {
8940
+ function parsePath(path79) {
8939
8941
  const parts = [];
8940
8942
  let current = "";
8941
8943
  let inBrackets = false;
8942
8944
  let inQuotes = false;
8943
8945
  let quoteChar = "";
8944
- for (let i = 0; i < path78.length; i++) {
8945
- const char = path78[i];
8946
+ for (let i = 0; i < path79.length; i++) {
8947
+ const char = path79[i];
8946
8948
  if (!inBrackets && char === ".") {
8947
8949
  if (current) {
8948
8950
  parts.push(current);
@@ -9073,10 +9075,10 @@ var require_redact = __commonJS({
9073
9075
  return current;
9074
9076
  }
9075
9077
  function redactPaths(obj, paths, censor, remove = false) {
9076
- for (const path78 of paths) {
9077
- const parts = parsePath(path78);
9078
+ for (const path79 of paths) {
9079
+ const parts = parsePath(path79);
9078
9080
  if (parts.includes("*")) {
9079
- redactWildcardPath(obj, parts, censor, path78, remove);
9081
+ redactWildcardPath(obj, parts, censor, path79, remove);
9080
9082
  } else {
9081
9083
  if (remove) {
9082
9084
  removeKey(obj, parts);
@@ -9161,8 +9163,8 @@ var require_redact = __commonJS({
9161
9163
  }
9162
9164
  } else {
9163
9165
  if (afterWildcard.includes("*")) {
9164
- const wrappedCensor = typeof censor === "function" ? (value, path78) => {
9165
- const fullPath = [...pathArray.slice(0, pathLength), ...path78];
9166
+ const wrappedCensor = typeof censor === "function" ? (value, path79) => {
9167
+ const fullPath = [...pathArray.slice(0, pathLength), ...path79];
9166
9168
  return censor(value, fullPath);
9167
9169
  } : censor;
9168
9170
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -9197,8 +9199,8 @@ var require_redact = __commonJS({
9197
9199
  return null;
9198
9200
  }
9199
9201
  const pathStructure = /* @__PURE__ */ new Map();
9200
- for (const path78 of pathsToClone) {
9201
- const parts = parsePath(path78);
9202
+ for (const path79 of pathsToClone) {
9203
+ const parts = parsePath(path79);
9202
9204
  let current = pathStructure;
9203
9205
  for (let i = 0; i < parts.length; i++) {
9204
9206
  const part = parts[i];
@@ -9250,24 +9252,24 @@ var require_redact = __commonJS({
9250
9252
  }
9251
9253
  return cloneSelectively(obj, pathStructure);
9252
9254
  }
9253
- function validatePath(path78) {
9254
- if (typeof path78 !== "string") {
9255
+ function validatePath(path79) {
9256
+ if (typeof path79 !== "string") {
9255
9257
  throw new Error("Paths must be (non-empty) strings");
9256
9258
  }
9257
- if (path78 === "") {
9259
+ if (path79 === "") {
9258
9260
  throw new Error("Invalid redaction path ()");
9259
9261
  }
9260
- if (path78.includes("..")) {
9261
- throw new Error(`Invalid redaction path (${path78})`);
9262
+ if (path79.includes("..")) {
9263
+ throw new Error(`Invalid redaction path (${path79})`);
9262
9264
  }
9263
- if (path78.includes(",")) {
9264
- throw new Error(`Invalid redaction path (${path78})`);
9265
+ if (path79.includes(",")) {
9266
+ throw new Error(`Invalid redaction path (${path79})`);
9265
9267
  }
9266
9268
  let bracketCount = 0;
9267
9269
  let inQuotes = false;
9268
9270
  let quoteChar = "";
9269
- for (let i = 0; i < path78.length; i++) {
9270
- const char = path78[i];
9271
+ for (let i = 0; i < path79.length; i++) {
9272
+ const char = path79[i];
9271
9273
  if ((char === '"' || char === "'") && bracketCount > 0) {
9272
9274
  if (!inQuotes) {
9273
9275
  inQuotes = true;
@@ -9281,20 +9283,20 @@ var require_redact = __commonJS({
9281
9283
  } else if (char === "]" && !inQuotes) {
9282
9284
  bracketCount--;
9283
9285
  if (bracketCount < 0) {
9284
- throw new Error(`Invalid redaction path (${path78})`);
9286
+ throw new Error(`Invalid redaction path (${path79})`);
9285
9287
  }
9286
9288
  }
9287
9289
  }
9288
9290
  if (bracketCount !== 0) {
9289
- throw new Error(`Invalid redaction path (${path78})`);
9291
+ throw new Error(`Invalid redaction path (${path79})`);
9290
9292
  }
9291
9293
  }
9292
9294
  function validatePaths(paths) {
9293
9295
  if (!Array.isArray(paths)) {
9294
9296
  throw new TypeError("paths must be an array");
9295
9297
  }
9296
- for (const path78 of paths) {
9297
- validatePath(path78);
9298
+ for (const path79 of paths) {
9299
+ validatePath(path79);
9298
9300
  }
9299
9301
  }
9300
9302
  function slowRedact(options = {}) {
@@ -9462,8 +9464,8 @@ var require_redaction = __commonJS({
9462
9464
  if (shape[k2] === null) {
9463
9465
  o[k2] = (value) => topCensor(value, [k2]);
9464
9466
  } else {
9465
- const wrappedCensor = typeof censor === "function" ? (value, path78) => {
9466
- return censor(value, [k2, ...path78]);
9467
+ const wrappedCensor = typeof censor === "function" ? (value, path79) => {
9468
+ return censor(value, [k2, ...path79]);
9467
9469
  } : censor;
9468
9470
  o[k2] = Redact({
9469
9471
  paths: shape[k2],
@@ -9681,10 +9683,10 @@ var require_atomic_sleep = __commonJS({
9681
9683
  var require_sonic_boom = __commonJS({
9682
9684
  "../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
9683
9685
  "use strict";
9684
- var fs68 = require("fs");
9686
+ var fs69 = require("fs");
9685
9687
  var EventEmitter3 = require("events");
9686
9688
  var inherits = require("util").inherits;
9687
- var path78 = require("path");
9689
+ var path79 = require("path");
9688
9690
  var sleep2 = require_atomic_sleep();
9689
9691
  var assert = require("assert");
9690
9692
  var BUSY_WRITE_TIMEOUT = 100;
@@ -9738,20 +9740,20 @@ var require_sonic_boom = __commonJS({
9738
9740
  const mode = sonic.mode;
9739
9741
  if (sonic.sync) {
9740
9742
  try {
9741
- if (sonic.mkdir) fs68.mkdirSync(path78.dirname(file), { recursive: true });
9742
- const fd = fs68.openSync(file, flags, mode);
9743
+ if (sonic.mkdir) fs69.mkdirSync(path79.dirname(file), { recursive: true });
9744
+ const fd = fs69.openSync(file, flags, mode);
9743
9745
  fileOpened(null, fd);
9744
9746
  } catch (err) {
9745
9747
  fileOpened(err);
9746
9748
  throw err;
9747
9749
  }
9748
9750
  } else if (sonic.mkdir) {
9749
- fs68.mkdir(path78.dirname(file), { recursive: true }, (err) => {
9751
+ fs69.mkdir(path79.dirname(file), { recursive: true }, (err) => {
9750
9752
  if (err) return fileOpened(err);
9751
- fs68.open(file, flags, mode, fileOpened);
9753
+ fs69.open(file, flags, mode, fileOpened);
9752
9754
  });
9753
9755
  } else {
9754
- fs68.open(file, flags, mode, fileOpened);
9756
+ fs69.open(file, flags, mode, fileOpened);
9755
9757
  }
9756
9758
  }
9757
9759
  function SonicBoom(opts) {
@@ -9792,8 +9794,8 @@ var require_sonic_boom = __commonJS({
9792
9794
  this.flush = flushBuffer;
9793
9795
  this.flushSync = flushBufferSync;
9794
9796
  this._actualWrite = actualWriteBuffer;
9795
- fsWriteSync = () => fs68.writeSync(this.fd, this._writingBuf);
9796
- fsWrite = () => fs68.write(this.fd, this._writingBuf, this.release);
9797
+ fsWriteSync = () => fs69.writeSync(this.fd, this._writingBuf);
9798
+ fsWrite = () => fs69.write(this.fd, this._writingBuf, this.release);
9797
9799
  } else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
9798
9800
  this._writingBuf = "";
9799
9801
  this.write = write;
@@ -9802,15 +9804,15 @@ var require_sonic_boom = __commonJS({
9802
9804
  this._actualWrite = actualWrite;
9803
9805
  fsWriteSync = () => {
9804
9806
  if (Buffer.isBuffer(this._writingBuf)) {
9805
- return fs68.writeSync(this.fd, this._writingBuf);
9807
+ return fs69.writeSync(this.fd, this._writingBuf);
9806
9808
  }
9807
- return fs68.writeSync(this.fd, this._writingBuf, "utf8");
9809
+ return fs69.writeSync(this.fd, this._writingBuf, "utf8");
9808
9810
  };
9809
9811
  fsWrite = () => {
9810
9812
  if (Buffer.isBuffer(this._writingBuf)) {
9811
- return fs68.write(this.fd, this._writingBuf, this.release);
9813
+ return fs69.write(this.fd, this._writingBuf, this.release);
9812
9814
  }
9813
- return fs68.write(this.fd, this._writingBuf, "utf8", this.release);
9815
+ return fs69.write(this.fd, this._writingBuf, "utf8", this.release);
9814
9816
  };
9815
9817
  } else {
9816
9818
  throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
@@ -9867,7 +9869,7 @@ var require_sonic_boom = __commonJS({
9867
9869
  }
9868
9870
  }
9869
9871
  if (this._fsync) {
9870
- fs68.fsyncSync(this.fd);
9872
+ fs69.fsyncSync(this.fd);
9871
9873
  }
9872
9874
  const len = this._len;
9873
9875
  if (this._reopening) {
@@ -9981,7 +9983,7 @@ var require_sonic_boom = __commonJS({
9981
9983
  const onDrain = () => {
9982
9984
  if (!this._fsync) {
9983
9985
  try {
9984
- fs68.fsync(this.fd, (err) => {
9986
+ fs69.fsync(this.fd, (err) => {
9985
9987
  this._flushPending = false;
9986
9988
  cb(err);
9987
9989
  });
@@ -10083,7 +10085,7 @@ var require_sonic_boom = __commonJS({
10083
10085
  const fd = this.fd;
10084
10086
  this.once("ready", () => {
10085
10087
  if (fd !== this.fd) {
10086
- fs68.close(fd, (err) => {
10088
+ fs69.close(fd, (err) => {
10087
10089
  if (err) {
10088
10090
  return this.emit("error", err);
10089
10091
  }
@@ -10132,7 +10134,7 @@ var require_sonic_boom = __commonJS({
10132
10134
  buf = this._bufs[0];
10133
10135
  }
10134
10136
  try {
10135
- const n = Buffer.isBuffer(buf) ? fs68.writeSync(this.fd, buf) : fs68.writeSync(this.fd, buf, "utf8");
10137
+ const n = Buffer.isBuffer(buf) ? fs69.writeSync(this.fd, buf) : fs69.writeSync(this.fd, buf, "utf8");
10136
10138
  const releasedBufObj = releaseWritingBuf(buf, this._len, n);
10137
10139
  buf = releasedBufObj.writingBuf;
10138
10140
  this._len = releasedBufObj.len;
@@ -10148,7 +10150,7 @@ var require_sonic_boom = __commonJS({
10148
10150
  }
10149
10151
  }
10150
10152
  try {
10151
- fs68.fsyncSync(this.fd);
10153
+ fs69.fsyncSync(this.fd);
10152
10154
  } catch {
10153
10155
  }
10154
10156
  }
@@ -10169,7 +10171,7 @@ var require_sonic_boom = __commonJS({
10169
10171
  buf = mergeBuf(this._bufs[0], this._lens[0]);
10170
10172
  }
10171
10173
  try {
10172
- const n = fs68.writeSync(this.fd, buf);
10174
+ const n = fs69.writeSync(this.fd, buf);
10173
10175
  buf = buf.subarray(n);
10174
10176
  this._len = Math.max(this._len - n, 0);
10175
10177
  if (buf.length <= 0) {
@@ -10197,13 +10199,13 @@ var require_sonic_boom = __commonJS({
10197
10199
  this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
10198
10200
  if (this.sync) {
10199
10201
  try {
10200
- const written = Buffer.isBuffer(this._writingBuf) ? fs68.writeSync(this.fd, this._writingBuf) : fs68.writeSync(this.fd, this._writingBuf, "utf8");
10202
+ const written = Buffer.isBuffer(this._writingBuf) ? fs69.writeSync(this.fd, this._writingBuf) : fs69.writeSync(this.fd, this._writingBuf, "utf8");
10201
10203
  release(null, written);
10202
10204
  } catch (err) {
10203
10205
  release(err);
10204
10206
  }
10205
10207
  } else {
10206
- fs68.write(this.fd, this._writingBuf, release);
10208
+ fs69.write(this.fd, this._writingBuf, release);
10207
10209
  }
10208
10210
  }
10209
10211
  function actualWriteBuffer() {
@@ -10212,7 +10214,7 @@ var require_sonic_boom = __commonJS({
10212
10214
  this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
10213
10215
  if (this.sync) {
10214
10216
  try {
10215
- const written = fs68.writeSync(this.fd, this._writingBuf);
10217
+ const written = fs69.writeSync(this.fd, this._writingBuf);
10216
10218
  release(null, written);
10217
10219
  } catch (err) {
10218
10220
  release(err);
@@ -10221,7 +10223,7 @@ var require_sonic_boom = __commonJS({
10221
10223
  if (kCopyBuffer) {
10222
10224
  this._writingBuf = Buffer.from(this._writingBuf);
10223
10225
  }
10224
- fs68.write(this.fd, this._writingBuf, release);
10226
+ fs69.write(this.fd, this._writingBuf, release);
10225
10227
  }
10226
10228
  }
10227
10229
  function actualClose(sonic) {
@@ -10237,12 +10239,12 @@ var require_sonic_boom = __commonJS({
10237
10239
  sonic._lens = [];
10238
10240
  assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
10239
10241
  try {
10240
- fs68.fsync(sonic.fd, closeWrapped);
10242
+ fs69.fsync(sonic.fd, closeWrapped);
10241
10243
  } catch {
10242
10244
  }
10243
10245
  function closeWrapped() {
10244
10246
  if (sonic.fd !== 1 && sonic.fd !== 2) {
10245
- fs68.close(sonic.fd, done);
10247
+ fs69.close(sonic.fd, done);
10246
10248
  } else {
10247
10249
  done();
10248
10250
  }
@@ -13377,11 +13379,11 @@ var init_lib = __esm({
13377
13379
  }
13378
13380
  }
13379
13381
  },
13380
- addToPath: function addToPath(path78, added, removed, oldPosInc, options) {
13381
- var last = path78.lastComponent;
13382
+ addToPath: function addToPath(path79, added, removed, oldPosInc, options) {
13383
+ var last = path79.lastComponent;
13382
13384
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
13383
13385
  return {
13384
- oldPos: path78.oldPos + oldPosInc,
13386
+ oldPos: path79.oldPos + oldPosInc,
13385
13387
  lastComponent: {
13386
13388
  count: last.count + 1,
13387
13389
  added,
@@ -13391,7 +13393,7 @@ var init_lib = __esm({
13391
13393
  };
13392
13394
  } else {
13393
13395
  return {
13394
- oldPos: path78.oldPos + oldPosInc,
13396
+ oldPos: path79.oldPos + oldPosInc,
13395
13397
  lastComponent: {
13396
13398
  count: 1,
13397
13399
  added,
@@ -13900,10 +13902,10 @@ function attachmentToHistoryMessage(o, ts) {
13900
13902
  const memories = raw.map((m2) => {
13901
13903
  if (!m2 || typeof m2 !== "object") return null;
13902
13904
  const rec3 = m2;
13903
- const path78 = typeof rec3.path === "string" ? rec3.path : null;
13905
+ const path79 = typeof rec3.path === "string" ? rec3.path : null;
13904
13906
  const content = typeof rec3.content === "string" ? rec3.content : null;
13905
- if (!path78 || content == null) return null;
13906
- const entry = { path: path78, content };
13907
+ if (!path79 || content == null) return null;
13908
+ const entry = { path: path79, content };
13907
13909
  if (typeof rec3.mtimeMs === "number") entry.mtimeMs = rec3.mtimeMs;
13908
13910
  return entry;
13909
13911
  }).filter((m2) => m2 !== null);
@@ -14720,10 +14722,10 @@ function parseAttachment(obj) {
14720
14722
  const memories = raw.map((m2) => {
14721
14723
  if (!m2 || typeof m2 !== "object") return null;
14722
14724
  const rec3 = m2;
14723
- const path78 = typeof rec3.path === "string" ? rec3.path : null;
14725
+ const path79 = typeof rec3.path === "string" ? rec3.path : null;
14724
14726
  const content = typeof rec3.content === "string" ? rec3.content : null;
14725
- if (!path78 || content == null) return null;
14726
- const out = { path: path78, content };
14727
+ if (!path79 || content == null) return null;
14728
+ const out = { path: path79, content };
14727
14729
  if (typeof rec3.mtimeMs === "number") out.mtimeMs = rec3.mtimeMs;
14728
14730
  return out;
14729
14731
  }).filter((m2) => m2 !== null);
@@ -35692,8 +35694,8 @@ var require_utils = __commonJS({
35692
35694
  var result = transform[inputType][outputType](input);
35693
35695
  return result;
35694
35696
  };
35695
- exports2.resolve = function(path78) {
35696
- var parts = path78.split("/");
35697
+ exports2.resolve = function(path79) {
35698
+ var parts = path79.split("/");
35697
35699
  var result = [];
35698
35700
  for (var index = 0; index < parts.length; index++) {
35699
35701
  var part = parts[index];
@@ -41546,18 +41548,18 @@ var require_object = __commonJS({
41546
41548
  var object = new ZipObject(name, zipObjectContent, o);
41547
41549
  this.files[name] = object;
41548
41550
  };
41549
- var parentFolder = function(path78) {
41550
- if (path78.slice(-1) === "/") {
41551
- path78 = path78.substring(0, path78.length - 1);
41551
+ var parentFolder = function(path79) {
41552
+ if (path79.slice(-1) === "/") {
41553
+ path79 = path79.substring(0, path79.length - 1);
41552
41554
  }
41553
- var lastSlash = path78.lastIndexOf("/");
41554
- return lastSlash > 0 ? path78.substring(0, lastSlash) : "";
41555
+ var lastSlash = path79.lastIndexOf("/");
41556
+ return lastSlash > 0 ? path79.substring(0, lastSlash) : "";
41555
41557
  };
41556
- var forceTrailingSlash = function(path78) {
41557
- if (path78.slice(-1) !== "/") {
41558
- path78 += "/";
41558
+ var forceTrailingSlash = function(path79) {
41559
+ if (path79.slice(-1) !== "/") {
41560
+ path79 += "/";
41559
41561
  }
41560
- return path78;
41562
+ return path79;
41561
41563
  };
41562
41564
  var folderAdd = function(name, createFolders) {
41563
41565
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
@@ -42559,7 +42561,7 @@ var require_lib3 = __commonJS({
42559
42561
  // src/run-case/recorder.ts
42560
42562
  function startRunCaseRecorder(opts) {
42561
42563
  const now = opts.now ?? Date.now;
42562
- const dir = import_node_path65.default.dirname(opts.recordPath);
42564
+ const dir = import_node_path66.default.dirname(opts.recordPath);
42563
42565
  let stream = null;
42564
42566
  let closing = false;
42565
42567
  let closedSettled = false;
@@ -42573,8 +42575,8 @@ function startRunCaseRecorder(opts) {
42573
42575
  });
42574
42576
  const ensureStream = () => {
42575
42577
  if (stream) return stream;
42576
- import_node_fs49.default.mkdirSync(dir, { recursive: true });
42577
- stream = import_node_fs49.default.createWriteStream(opts.recordPath, { flags: "a" });
42578
+ import_node_fs50.default.mkdirSync(dir, { recursive: true });
42579
+ stream = import_node_fs50.default.createWriteStream(opts.recordPath, { flags: "a" });
42578
42580
  stream.on("close", () => closedResolve());
42579
42581
  return stream;
42580
42582
  };
@@ -42599,12 +42601,12 @@ function startRunCaseRecorder(opts) {
42599
42601
  };
42600
42602
  return { tap, close, closed };
42601
42603
  }
42602
- var import_node_fs49, import_node_path65;
42604
+ var import_node_fs50, import_node_path66;
42603
42605
  var init_recorder = __esm({
42604
42606
  "src/run-case/recorder.ts"() {
42605
42607
  "use strict";
42606
- import_node_fs49 = __toESM(require("fs"), 1);
42607
- import_node_path65 = __toESM(require("path"), 1);
42608
+ import_node_fs50 = __toESM(require("fs"), 1);
42609
+ import_node_path66 = __toESM(require("path"), 1);
42608
42610
  }
42609
42611
  });
42610
42612
 
@@ -42647,7 +42649,7 @@ var init_wire = __esm({
42647
42649
  // src/run-case/controller.ts
42648
42650
  async function runController(opts) {
42649
42651
  const now = opts.now ?? Date.now;
42650
- const cwd = opts.cwd ?? (0, import_node_fs50.mkdtempSync)(import_node_path66.default.join(import_node_os23.default.tmpdir(), "clawd-runcase-"));
42652
+ const cwd = opts.cwd ?? (0, import_node_fs51.mkdtempSync)(import_node_path67.default.join(import_node_os23.default.tmpdir(), "clawd-runcase-"));
42651
42653
  const ownsCwd = opts.cwd === void 0;
42652
42654
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
42653
42655
  const spawnCtx = { cwd };
@@ -42808,19 +42810,19 @@ async function runController(opts) {
42808
42810
  if (sigintHandler) process.off("SIGINT", sigintHandler);
42809
42811
  if (ownsCwd) {
42810
42812
  try {
42811
- (0, import_node_fs50.rmSync)(cwd, { recursive: true, force: true });
42813
+ (0, import_node_fs51.rmSync)(cwd, { recursive: true, force: true });
42812
42814
  } catch {
42813
42815
  }
42814
42816
  }
42815
42817
  return exitCode ?? 0;
42816
42818
  }
42817
- var import_node_fs50, import_node_os23, import_node_path66;
42819
+ var import_node_fs51, import_node_os23, import_node_path67;
42818
42820
  var init_controller = __esm({
42819
42821
  "src/run-case/controller.ts"() {
42820
42822
  "use strict";
42821
- import_node_fs50 = require("fs");
42823
+ import_node_fs51 = require("fs");
42822
42824
  import_node_os23 = __toESM(require("os"), 1);
42823
- import_node_path66 = __toESM(require("path"), 1);
42825
+ import_node_path67 = __toESM(require("path"), 1);
42824
42826
  init_claude();
42825
42827
  init_stdout_splitter();
42826
42828
  init_permission_stdio();
@@ -43095,8 +43097,8 @@ Env (advanced):
43095
43097
  `;
43096
43098
 
43097
43099
  // src/index.ts
43098
- var import_node_path64 = __toESM(require("path"), 1);
43099
- var import_node_fs48 = __toESM(require("fs"), 1);
43100
+ var import_node_path65 = __toESM(require("path"), 1);
43101
+ var import_node_fs49 = __toESM(require("fs"), 1);
43100
43102
  var import_node_os22 = __toESM(require("os"), 1);
43101
43103
 
43102
43104
  // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
@@ -43776,9 +43778,14 @@ init_protocol();
43776
43778
  var import_node_os2 = require("os");
43777
43779
  var FORCED_DENY_READ = ["~/"];
43778
43780
  function personaEditDeny(personaDir) {
43781
+ return `Edit(${tildePath(personaDir)}/**)`;
43782
+ }
43783
+ function personaReadAllow(personaDir) {
43784
+ return `Read(${tildePath(personaDir)}/**)`;
43785
+ }
43786
+ function tildePath(p2) {
43779
43787
  const home = (0, import_node_os2.homedir)();
43780
- const rel = personaDir.startsWith(`${home}/`) ? `~${personaDir.slice(home.length)}` : personaDir;
43781
- return `Edit(${rel}/**)`;
43788
+ return p2.startsWith(`${home}/`) ? `~${p2.slice(home.length)}` : p2;
43782
43789
  }
43783
43790
  function unionArr(a, b2) {
43784
43791
  const out = [...a ?? []];
@@ -43794,8 +43801,8 @@ function buildGuestSettingsV1() {
43794
43801
  return {
43795
43802
  permissions: {
43796
43803
  defaultMode: "dontAsk"
43797
- // persona-dir 的 Edit deny 由 composeGuestSandbox spawnCwd 注入 '~/<persona>/**' 绝对规则
43798
- // (base 不放相对 './'——会随 getCwd 漂移误拦项目,见 personaEditDeny)。
43804
+ // persona-dir 的 Edit deny 由 store.writeSandboxSettings 落盘注入 '~/<persona>/**' 绝对规则
43805
+ // (base 模板不放相对 './'——会随 getCwd 漂移误拦项目,见 personaEditDeny)。
43799
43806
  },
43800
43807
  sandbox: {
43801
43808
  enabled: true,
@@ -43808,7 +43815,7 @@ function buildGuestSettingsV1() {
43808
43815
  denyWrite: [],
43809
43816
  // **不放 '~/'**(写是白名单制,home 自动不可写);persona(cwd) 由 compose 强制 deny
43810
43817
  allowRead: [],
43811
- // userWorkDir + persona cwd 由 compose 强制注入,profile 加工具链精确路径
43818
+ // persona 读权由 store.writeSandboxSettings 落盘注入;userWorkDir 由 compose 注入;profile 加工具链精确路径
43812
43819
  allowWrite: []
43813
43820
  }
43814
43821
  }
@@ -43846,8 +43853,9 @@ function composeGuestSandbox(base, userWorkDir, spawnCwd) {
43846
43853
  s.sandbox.allowUnsandboxedCommands = false;
43847
43854
  fsv.denyRead = unionArr(fsv.denyRead, FORCED_DENY_READ);
43848
43855
  fsv.denyWrite = unionArr((fsv.denyWrite ?? []).filter((p2) => p2 !== "~/"), [spawnCwd]);
43849
- fsv.allowRead = unionArr(fsv.allowRead, [userWorkDir, spawnCwd]);
43856
+ fsv.allowRead = unionArr(fsv.allowRead, [userWorkDir]);
43850
43857
  fsv.allowWrite = unionArr(fsv.allowWrite, [userWorkDir]);
43858
+ s.permissions.allow = unionArr(s.permissions.allow, [`Read(${tildePath(userWorkDir)}/**)`]);
43851
43859
  return s;
43852
43860
  }
43853
43861
 
@@ -44088,6 +44096,8 @@ function buildConnectionPrompt(args) {
44088
44096
  let line = `\u4F60\u7684\u6587\u4EF6\u5DE5\u4F5C\u76EE\u5F55\u662F ${args.workDir}\uFF0C\u8FD9\u4E2A\u76EE\u5F55\u5BF9\u4F60\u53EF\u8BFB\u53EF\u5199\uFF0C\u6587\u4EF6\u7684\u8BFB\u53D6/\u521B\u5EFA/\u4FEE\u6539\u90FD\u5728\u8FD9\u91CC\u8FDB\u884C\u3002`;
44089
44097
  if (args.mode === "guest") {
44090
44098
  line += "persona \u76EE\u5F55\u5BF9\u4F60\u53EA\u8BFB\uFF0C\u4E0D\u8981\u5F80\u91CC\u5199\u3002";
44099
+ } else {
44100
+ line += "persona \u76EE\u5F55\u5B58\u653E\u7684\u662F\u4EBA\u683C\u914D\u7F6E\uFF1B\u5DE5\u4F5C\u4EA7\u7269\u3001\u4E34\u65F6\u6587\u4EF6\u3001\u4E0B\u8F7D\u5185\u5BB9\u9ED8\u8BA4\u5199\u5230\u4E0A\u9762\u7684\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u4E0D\u8981\u843D\u5728 persona \u76EE\u5F55\u91CC\uFF08\u66F4\u65B0\u4EBA\u683C\u914D\u7F6E\u672C\u8EAB\u9664\u5916\uFF09\u3002";
44091
44101
  }
44092
44102
  parts.push(line);
44093
44103
  }
@@ -47177,8 +47187,9 @@ var SessionManager = class {
47177
47187
  // guest 三件套:creatorPrincipalId=capId(scopeForFile 回落防线)、
47178
47188
  // creatorDisplayName(reducer guest 分支硬性要求,缺则 spawn 抛)
47179
47189
  creatorPrincipalId: capId,
47180
- creatorDisplayName: `\u98DE\u4E66\u7FA4\xB7${args.chatName ?? args.chatId}`,
47190
+ creatorDisplayName: `${(args.chatType ?? "group") === "p2p" ? "\u98DE\u4E66\u79C1\u804A" : "\u98DE\u4E66\u7FA4"}\xB7${args.chatName ?? args.chatId}`,
47181
47191
  larkChatId: args.chatId,
47192
+ larkChatType: args.chatType ?? "group",
47182
47193
  ...args.chatName ? { larkChatName: args.chatName } : {},
47183
47194
  createdAt: now,
47184
47195
  updatedAt: now
@@ -47736,21 +47747,47 @@ var PersonaStore = class {
47736
47747
  * 覆盖式写 persona 级 sandbox 配置(seed 给有 profile 的 persona 写 base⊕profile;owner 之后可手编)。
47737
47748
  * 这份是 guest 沙箱的 **base 真源**:manager spawn 时 composeGuestSandbox 读它 + 叠 sandbox 强制底座
47738
47749
  * (enabled/denyRead/denyWrite/userWorkDir owner 削弱不了)→ 写 per-guest 文件给 CC。
47739
- * 写入时强制注入本 persona Edit-deny('~/<persona>/**' 按实际 persona dir 算)+ 剥掉历史相对 'Edit(./**)',
47740
- * 让落盘 base 就是 source of truth(compose 不再运行时替换 permission deny)。
47750
+ * 写入时强制注入本 persona 的**基线**(Edit-deny + allowRead,见 injectPersonaBaseline),
47751
+ * 让落盘 base 就是 source of truth(compose 不再运行时替换 permission deny / 注入 persona 读权)。
47741
47752
  */
47742
47753
  writeSandboxSettings(personaId2, settings) {
47743
47754
  this.atomicWrite(
47744
47755
  this.sandboxSettingsPath(personaId2),
47745
- JSON.stringify(this.injectPersonaEditDeny(personaId2, settings), null, 2)
47756
+ JSON.stringify(this.injectPersonaBaseline(personaId2, settings), null, 2)
47746
47757
  );
47747
47758
  }
47748
- /** 把 persona Edit-deny(绝对 '~/<persona>/**')并进 settings.permissions.deny,剥掉历史相对 'Edit(./**)'。 */
47749
- injectPersonaEditDeny(personaId2, settings) {
47759
+ /**
47760
+ * persona 基线注入:Edit-deny(绝对 '~/<persona>/**')并进 permissions.deny + 剥掉历史相对
47761
+ * 'Edit(./**)';persona 目录 '~/' 路径 union 进 sandbox.filesystem.allowRead(guest 读权
47762
+ * 落盘即真源,compose 不再运行时注入——spec 2026-08-06-persona-workdir-discipline §1-§2);
47763
+ * `Read(~/<persona>/**)` union 进 permissions.allow——OS 沙箱层可读只放行了 Bash,CC 自带
47764
+ * Read 工具在最严格 base(dontAsk + 无 allow)下仍被拒,工具层与 OS 层必须同构放行
47765
+ * (guest「自由读 persona 目录、只写自己 userWorkDir」,spec 2026-08-06-lark-dm §P10 通用化)。
47766
+ */
47767
+ injectPersonaBaseline(personaId2, settings) {
47768
+ const dir = this.personaDir(personaId2);
47750
47769
  const s = settings;
47751
- const rule = personaEditDeny(this.personaDir(personaId2));
47752
- const prev = (s.permissions?.deny ?? []).filter((r) => r !== "Edit(./**)" && r !== rule);
47753
- return { ...s, permissions: { ...s.permissions ?? {}, deny: [...prev, rule] } };
47770
+ const rule = personaEditDeny(dir);
47771
+ const rawDeny = s.permissions && typeof s.permissions === "object" ? s.permissions.deny : void 0;
47772
+ const prevDeny = (Array.isArray(rawDeny) ? rawDeny : []).filter((r) => r !== "Edit(./**)" && r !== rule);
47773
+ const readRule = personaReadAllow(dir);
47774
+ const rawAllow = s.permissions && typeof s.permissions === "object" ? s.permissions.allow : void 0;
47775
+ const prevAllow = (Array.isArray(rawAllow) ? rawAllow : []).filter((r) => r !== readRule);
47776
+ const readPath = tildePath(dir);
47777
+ const sandbox = s.sandbox && typeof s.sandbox === "object" && !Array.isArray(s.sandbox) ? s.sandbox : {};
47778
+ const rawFs = sandbox.filesystem;
47779
+ const filesystem = rawFs && typeof rawFs === "object" && !Array.isArray(rawFs) ? rawFs : {};
47780
+ const rawRead = filesystem.allowRead;
47781
+ const prevRead = (Array.isArray(rawRead) ? rawRead : []).filter((p2) => p2 !== readPath);
47782
+ return {
47783
+ ...s,
47784
+ permissions: {
47785
+ ...s.permissions && typeof s.permissions === "object" ? s.permissions : {},
47786
+ allow: [...prevAllow, readRule],
47787
+ deny: [...prevDeny, rule]
47788
+ },
47789
+ sandbox: { ...sandbox, filesystem: { ...filesystem, allowRead: [...prevRead, readPath] } }
47790
+ };
47754
47791
  }
47755
47792
  codexSandboxSettingsPath(personaId2) {
47756
47793
  return path12.join(this.personaDir(personaId2), ".clawd", "codex-sandbox.json");
@@ -48615,6 +48652,38 @@ function migrateCodexSandbox(args) {
48615
48652
  }
48616
48653
  if (n > 0) args.logger.info("persona.codex-sandbox.done", { migrated: n });
48617
48654
  }
48655
+ function migrateSandboxAllowRead(args) {
48656
+ let n = 0;
48657
+ for (const personaId2 of args.store.list()) {
48658
+ try {
48659
+ const readPath = tildePath(args.store.personaDirPath(personaId2));
48660
+ if (!fs12.existsSync(args.store.sandboxSettingsPath(personaId2))) {
48661
+ args.store.writeSandboxSettings(personaId2, buildGuestSettingsV1());
48662
+ n++;
48663
+ continue;
48664
+ }
48665
+ const raw = args.store.readSandboxSettings(personaId2);
48666
+ if (raw === null) {
48667
+ args.logger.warn("persona.sandbox.allow-read.corrupt-skip", { personaId: personaId2 });
48668
+ continue;
48669
+ }
48670
+ const allowRead = raw.sandbox?.filesystem?.allowRead;
48671
+ const permAllow = raw.permissions?.allow;
48672
+ const readRule = personaReadAllow(args.store.personaDirPath(personaId2));
48673
+ if (Array.isArray(allowRead) && allowRead.includes(readPath) && Array.isArray(permAllow) && permAllow.includes(readRule))
48674
+ continue;
48675
+ args.store.writeSandboxSettings(personaId2, raw);
48676
+ n++;
48677
+ args.logger.info("persona.sandbox.allow-read.migrated", { personaId: personaId2 });
48678
+ } catch (err) {
48679
+ args.logger.warn("persona.sandbox.allow-read.failed", {
48680
+ personaId: personaId2,
48681
+ error: err instanceof Error ? err.message : String(err)
48682
+ });
48683
+ }
48684
+ }
48685
+ if (n > 0) args.logger.info("persona.sandbox.allow-read.done", { migrated: n });
48686
+ }
48618
48687
  var PERSONA_BUILDER_ID = "persona-builder";
48619
48688
  var PERSONA_BUILDER_TOMBSTONE_MARKER = "<!-- clawd:persona-builder-retired -->";
48620
48689
  var PERSONA_BUILDER_TOMBSTONE = `${PERSONA_BUILDER_TOMBSTONE_MARKER}
@@ -49249,8 +49318,8 @@ function turnStartInput(text) {
49249
49318
  const items = [];
49250
49319
  let leftover = text;
49251
49320
  for (const m2 of text.matchAll(SKILL_RE)) {
49252
- const [marker, name, path78] = m2;
49253
- items.push({ type: "skill", name, path: path78 });
49321
+ const [marker, name, path79] = m2;
49322
+ items.push({ type: "skill", name, path: path79 });
49254
49323
  leftover = leftover.replace(marker, "");
49255
49324
  }
49256
49325
  for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
@@ -51944,14 +52013,62 @@ function createLarkBotCloudClient(opts) {
51944
52013
  async claimEvents() {
51945
52014
  const json = await request("POST", "/api/lark-bot/events/claim");
51946
52015
  return json.items ?? [];
52016
+ },
52017
+ async fetchResource(appId, messageId, fileKey, maxBytes) {
52018
+ const ac = new AbortController();
52019
+ const timer = setTimeout(() => ac.abort(), opts.timeoutMs ?? 15e3);
52020
+ const qs = new URLSearchParams({
52021
+ appId,
52022
+ messageId,
52023
+ fileKey,
52024
+ ...maxBytes ? { maxBytes: String(maxBytes) } : {}
52025
+ }).toString();
52026
+ let res;
52027
+ try {
52028
+ res = await f(`${base}/api/lark-bot/resource?${qs}`, {
52029
+ method: "GET",
52030
+ signal: ac.signal,
52031
+ headers: {
52032
+ authorization: `Bearer ${opts.apiKey}`,
52033
+ "x-did": encodeURIComponent(opts.deviceId),
52034
+ "x-name": encodeURIComponent(opts.displayName),
52035
+ "x-scene": "daemon"
52036
+ }
52037
+ });
52038
+ } catch (err) {
52039
+ throw new LarkBotCloudError(`lark-bot cloud resource fetch failed: ${err.message}`);
52040
+ } finally {
52041
+ clearTimeout(timer);
52042
+ }
52043
+ if (!res.ok) {
52044
+ throw new LarkBotCloudError(`lark-bot cloud resource: HTTP ${res.status}`, res.status);
52045
+ }
52046
+ const data = Buffer.from(await res.arrayBuffer());
52047
+ const contentType = res.headers.get("content-type") ?? "application/octet-stream";
52048
+ return { data, contentType };
51947
52049
  }
51948
52050
  };
51949
52051
  }
51950
52052
 
51951
52053
  // src/lark-bot-connector/chat-router.ts
52054
+ var import_node_fs27 = __toESM(require("fs"), 1);
52055
+ var import_node_path28 = __toESM(require("path"), 1);
51952
52056
  init_protocol();
51953
52057
  var ERROR_REPLY_TEXT = "\u5904\u7406\u51FA\u9519\u4E86\uFF0C\u8BF7\u91CD\u8BD5";
51954
52058
  var DEFAULT_TURN_TIMEOUT_MS = 10 * 60 * 1e3;
52059
+ var MAX_IMAGE_BYTES = 8 * 1024 * 1024;
52060
+ var EXT_BY_MIME = {
52061
+ "image/png": "png",
52062
+ "image/jpeg": "jpg",
52063
+ "image/gif": "gif",
52064
+ "image/webp": "webp"
52065
+ };
52066
+ var IMAGE_OVERSIZE_TEXT = "[\u56FE\u7247\u6D88\u606F\uFF0C\u8D85\u51FA\u5927\u5C0F\u9650\u5236]";
52067
+ var IMAGE_UNAVAILABLE_TEXT = "[\u56FE\u7247\u6D88\u606F\uFF0C\u6682\u4E0D\u652F\u6301\u67E5\u770B]";
52068
+ function safeMediaName(raw) {
52069
+ const cleaned = raw.replace(/[^a-zA-Z0-9_\-.]/g, "_").replace(/^\.+/, (d) => "_".repeat(d.length));
52070
+ return (cleaned || "msg").replace(/\.\./g, "__");
52071
+ }
51955
52072
  function createLarkChatRouter(deps) {
51956
52073
  const timeoutMs = deps.turnTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS;
51957
52074
  const replyDelays = deps.replyRetryDelaysMs ?? [2e3, 8e3, 3e4];
@@ -51964,7 +52081,34 @@ function createLarkChatRouter(deps) {
51964
52081
  return;
51965
52082
  }
51966
52083
  const speaker = env.senderName || env.senderOpenId || "\u672A\u77E5\u6210\u5458";
51967
- const body = env.text?.trim() ? env.text : "[\u975E\u6587\u672C\u6D88\u606F]";
52084
+ const imageLines = [];
52085
+ for (const [i, key] of (env.imageKeys ?? []).entries()) {
52086
+ try {
52087
+ if (!deps.fetchResource || !deps.mediaRootFor) throw new Error("resource pipeline not wired");
52088
+ const r = await deps.fetchResource(env.appId, env.messageId, key, MAX_IMAGE_BYTES);
52089
+ if (r.data.byteLength > MAX_IMAGE_BYTES) {
52090
+ deps.logger?.warn(`larkBot image oversize ${key}: ${r.data.byteLength}B`);
52091
+ imageLines.push(IMAGE_OVERSIZE_TEXT);
52092
+ continue;
52093
+ }
52094
+ const ext = EXT_BY_MIME[r.contentType];
52095
+ if (!ext) {
52096
+ deps.logger?.warn(`larkBot image unsupported mime ${key}: ${r.contentType}`);
52097
+ imageLines.push(IMAGE_UNAVAILABLE_TEXT);
52098
+ continue;
52099
+ }
52100
+ const dir = deps.mediaRootFor(env.chatId);
52101
+ import_node_fs27.default.mkdirSync(dir, { recursive: true });
52102
+ const p2 = import_node_path28.default.join(dir, `${safeMediaName(env.messageId)}-${i}.${ext}`);
52103
+ import_node_fs27.default.writeFileSync(p2, r.data);
52104
+ imageLines.push(`[\u56FE\u7247: ${p2}]\uFF08\u7528 Read \u5DE5\u5177\u67E5\u770B\uFF09`);
52105
+ } catch (err) {
52106
+ deps.logger?.warn(`larkBot image fetch failed ${key}: ${err.message}`);
52107
+ imageLines.push(IMAGE_UNAVAILABLE_TEXT);
52108
+ }
52109
+ }
52110
+ const rawBody = [env.text?.trim(), ...imageLines].filter(Boolean).join("\n");
52111
+ const body = rawBody || "[\u975E\u6587\u672C\u6D88\u606F]";
51968
52112
  const text = `${body}
51969
52113
 
51970
52114
  ${formatLarkSpeakerReminder(speaker)}`;
@@ -51972,6 +52116,7 @@ ${formatLarkSpeakerReminder(speaker)}`;
51972
52116
  personaId: env.personaId,
51973
52117
  chatId: env.chatId,
51974
52118
  chatName: env.chatName,
52119
+ chatType: env.chatType ?? "group",
51975
52120
  senderOpenId: env.senderOpenId ?? "",
51976
52121
  senderName: env.senderName ?? env.senderOpenId ?? "\u672A\u77E5\u6210\u5458",
51977
52122
  text
@@ -52734,7 +52879,7 @@ var CodexHistoryReader = class {
52734
52879
  };
52735
52880
 
52736
52881
  // src/tools/codex-skills.ts
52737
- var import_node_path28 = __toESM(require("path"), 1);
52882
+ var import_node_path29 = __toESM(require("path"), 1);
52738
52883
  function str3(v2) {
52739
52884
  return typeof v2 === "string" ? v2 : void 0;
52740
52885
  }
@@ -52749,13 +52894,13 @@ function mapSkillsListResponse(res) {
52749
52894
  const r = s ?? {};
52750
52895
  const name = str3(r.name);
52751
52896
  if (!name) continue;
52752
- const path78 = str3(r.path);
52897
+ const path79 = str3(r.path);
52753
52898
  const description = str3(r.description);
52754
52899
  const isPlugin = name.includes(":");
52755
52900
  out.push({
52756
52901
  name,
52757
52902
  source: isPlugin ? "plugin" : "project",
52758
- ...path78 ? { path: path78 } : {},
52903
+ ...path79 ? { path: path79 } : {},
52759
52904
  ...description ? { description } : {},
52760
52905
  ...isPlugin ? { plugin: name.split(":")[0] } : {}
52761
52906
  });
@@ -52771,30 +52916,30 @@ async function listCodexSkills(cwd, deps = {}) {
52771
52916
  // 先写 extraRoots 再写 list(stdio 按写入顺序处理,fire-and-forget 不 await 回包;
52772
52917
  // codex-session.ts ready 序列同款,两处保持一致)。失败不阻塞 list。
52773
52918
  beforeRequest: (client) => {
52774
- void client.request("skills/extraRoots/set", { extraRoots: [import_node_path28.default.join(cwd, ".claude", "skills")] }).catch(() => void 0);
52919
+ void client.request("skills/extraRoots/set", { extraRoots: [import_node_path29.default.join(cwd, ".claude", "skills")] }).catch(() => void 0);
52775
52920
  }
52776
52921
  });
52777
52922
  return mapSkillsListResponse(res);
52778
52923
  }
52779
52924
 
52780
52925
  // src/workspace/browser.ts
52781
- var import_node_fs27 = __toESM(require("fs"), 1);
52926
+ var import_node_fs28 = __toESM(require("fs"), 1);
52782
52927
  var import_node_os8 = __toESM(require("os"), 1);
52783
- var import_node_path29 = __toESM(require("path"), 1);
52928
+ var import_node_path30 = __toESM(require("path"), 1);
52784
52929
  init_protocol();
52785
52930
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
52786
52931
  function resolveInsideCwd(cwd, subpath) {
52787
- const absCwd = import_node_path29.default.resolve(cwd);
52788
- const joined = import_node_path29.default.resolve(absCwd, subpath ?? ".");
52789
- const rel = import_node_path29.default.relative(absCwd, joined);
52790
- if (rel.startsWith("..") || import_node_path29.default.isAbsolute(rel)) {
52932
+ const absCwd = import_node_path30.default.resolve(cwd);
52933
+ const joined = import_node_path30.default.resolve(absCwd, subpath ?? ".");
52934
+ const rel = import_node_path30.default.relative(absCwd, joined);
52935
+ if (rel.startsWith("..") || import_node_path30.default.isAbsolute(rel)) {
52791
52936
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `path escapes cwd: ${subpath}`);
52792
52937
  }
52793
52938
  return joined;
52794
52939
  }
52795
52940
  function ensureCwd(cwd) {
52796
52941
  try {
52797
- const stat = import_node_fs27.default.statSync(cwd);
52942
+ const stat = import_node_fs28.default.statSync(cwd);
52798
52943
  if (!stat.isDirectory()) {
52799
52944
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `not a directory: ${cwd}`);
52800
52945
  }
@@ -52808,7 +52953,7 @@ var WorkspaceBrowser = class {
52808
52953
  const cwd = args.cwd && args.cwd.length > 0 ? args.cwd : import_node_os8.default.homedir();
52809
52954
  ensureCwd(cwd);
52810
52955
  const full = resolveInsideCwd(cwd, args.path);
52811
- const dirents = import_node_fs27.default.readdirSync(full, { withFileTypes: true });
52956
+ const dirents = import_node_fs28.default.readdirSync(full, { withFileTypes: true });
52812
52957
  const entries = [];
52813
52958
  for (const d of dirents) {
52814
52959
  if (!args.showHidden && d.name.startsWith(".")) continue;
@@ -52818,7 +52963,7 @@ var WorkspaceBrowser = class {
52818
52963
  mtime: ""
52819
52964
  };
52820
52965
  try {
52821
- const st = import_node_fs27.default.statSync(import_node_path29.default.join(full, d.name));
52966
+ const st = import_node_fs28.default.statSync(import_node_path30.default.join(full, d.name));
52822
52967
  entry.mtime = new Date(st.mtimeMs).toISOString();
52823
52968
  if (d.isFile()) entry.size = st.size;
52824
52969
  } catch {
@@ -52834,14 +52979,14 @@ var WorkspaceBrowser = class {
52834
52979
  read(args) {
52835
52980
  ensureCwd(args.cwd);
52836
52981
  const full = resolveInsideCwd(args.cwd, args.path);
52837
- const st = import_node_fs27.default.statSync(full);
52982
+ const st = import_node_fs28.default.statSync(full);
52838
52983
  if (!st.isFile()) {
52839
52984
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `not a file: ${args.path}`);
52840
52985
  }
52841
52986
  if (st.size > MAX_FILE_BYTES) {
52842
52987
  throw new ClawdError(ERROR_CODES.FILE_TOO_LARGE, `file > ${MAX_FILE_BYTES} bytes`);
52843
52988
  }
52844
- const buf = import_node_fs27.default.readFileSync(full);
52989
+ const buf = import_node_fs28.default.readFileSync(full);
52845
52990
  const isBinary = buf.includes(0);
52846
52991
  if (isBinary) {
52847
52992
  return {
@@ -52863,20 +53008,20 @@ var WorkspaceBrowser = class {
52863
53008
  };
52864
53009
 
52865
53010
  // src/skills/agents-scanner.ts
52866
- var import_node_fs28 = __toESM(require("fs"), 1);
53011
+ var import_node_fs29 = __toESM(require("fs"), 1);
52867
53012
  var import_node_os9 = __toESM(require("os"), 1);
52868
- var import_node_path30 = __toESM(require("path"), 1);
53013
+ var import_node_path31 = __toESM(require("path"), 1);
52869
53014
  var DEFAULT_POLICY_DIR_DARWIN = "/Library/Application Support/ClaudeCode/.claude/agents";
52870
53015
  function isDirLikeSync2(p2) {
52871
53016
  try {
52872
- return import_node_fs28.default.statSync(p2).isDirectory();
53017
+ return import_node_fs29.default.statSync(p2).isDirectory();
52873
53018
  } catch {
52874
53019
  return false;
52875
53020
  }
52876
53021
  }
52877
53022
  function fileExistsSync(p2) {
52878
53023
  try {
52879
- return import_node_fs28.default.statSync(p2).isFile();
53024
+ return import_node_fs29.default.statSync(p2).isFile();
52880
53025
  } catch {
52881
53026
  return false;
52882
53027
  }
@@ -52884,7 +53029,7 @@ function fileExistsSync(p2) {
52884
53029
  function parseAgentFile(filePath) {
52885
53030
  let content;
52886
53031
  try {
52887
- content = import_node_fs28.default.readFileSync(filePath, "utf8");
53032
+ content = import_node_fs29.default.readFileSync(filePath, "utf8");
52888
53033
  } catch {
52889
53034
  return {};
52890
53035
  }
@@ -52897,16 +53042,16 @@ function parseAgentFile(filePath) {
52897
53042
  function scanAgentsDir(dir, source, seen, out) {
52898
53043
  let entries;
52899
53044
  try {
52900
- entries = import_node_fs28.default.readdirSync(dir, { withFileTypes: true });
53045
+ entries = import_node_fs29.default.readdirSync(dir, { withFileTypes: true });
52901
53046
  } catch {
52902
53047
  return;
52903
53048
  }
52904
53049
  for (const ent of entries) {
52905
53050
  if (!ent.name.endsWith(".md")) continue;
52906
- if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path30.default.join(dir, ent.name)))) {
53051
+ if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path31.default.join(dir, ent.name)))) {
52907
53052
  continue;
52908
53053
  }
52909
- const filePath = import_node_path30.default.join(dir, ent.name);
53054
+ const filePath = import_node_path31.default.join(dir, ent.name);
52910
53055
  const baseName = ent.name.replace(/\.md$/, "");
52911
53056
  if (seen.has(baseName)) continue;
52912
53057
  seen.add(baseName);
@@ -52924,12 +53069,12 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
52924
53069
  function walk2(dir, namespaces) {
52925
53070
  let entries;
52926
53071
  try {
52927
- entries = import_node_fs28.default.readdirSync(dir, { withFileTypes: true });
53072
+ entries = import_node_fs29.default.readdirSync(dir, { withFileTypes: true });
52928
53073
  } catch {
52929
53074
  return;
52930
53075
  }
52931
53076
  for (const ent of entries) {
52932
- const childPath = import_node_path30.default.join(dir, ent.name);
53077
+ const childPath = import_node_path31.default.join(dir, ent.name);
52933
53078
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync2(childPath)) {
52934
53079
  walk2(childPath, [...namespaces, ent.name]);
52935
53080
  continue;
@@ -52954,13 +53099,13 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
52954
53099
  walk2(root, []);
52955
53100
  }
52956
53101
  function readInstalledPlugins2(home) {
52957
- const pluginsDir = import_node_path30.default.join(home, ".claude", "plugins");
52958
- const v2 = import_node_path30.default.join(pluginsDir, "installed_plugins_v2.json");
52959
- const v1 = import_node_path30.default.join(pluginsDir, "installed_plugins.json");
53102
+ const pluginsDir = import_node_path31.default.join(home, ".claude", "plugins");
53103
+ const v2 = import_node_path31.default.join(pluginsDir, "installed_plugins_v2.json");
53104
+ const v1 = import_node_path31.default.join(pluginsDir, "installed_plugins.json");
52960
53105
  let raw = null;
52961
53106
  for (const candidate of [v2, v1]) {
52962
53107
  try {
52963
- raw = import_node_fs28.default.readFileSync(candidate, "utf8");
53108
+ raw = import_node_fs29.default.readFileSync(candidate, "utf8");
52964
53109
  break;
52965
53110
  } catch {
52966
53111
  }
@@ -52985,19 +53130,19 @@ function readInstalledPlugins2(home) {
52985
53130
  return out;
52986
53131
  }
52987
53132
  function walkUpProjectAgentsDirs(startCwd, home, seen, out) {
52988
- let cur = import_node_path30.default.resolve(startCwd);
52989
- const fsRoot = import_node_path30.default.parse(cur).root;
53133
+ let cur = import_node_path31.default.resolve(startCwd);
53134
+ const fsRoot = import_node_path31.default.parse(cur).root;
52990
53135
  while (true) {
52991
- scanAgentsDir(import_node_path30.default.join(cur, ".claude", "agents"), "project", seen, out);
53136
+ scanAgentsDir(import_node_path31.default.join(cur, ".claude", "agents"), "project", seen, out);
52992
53137
  let hasGit = false;
52993
53138
  try {
52994
- hasGit = import_node_fs28.default.existsSync(import_node_path30.default.join(cur, ".git"));
53139
+ hasGit = import_node_fs29.default.existsSync(import_node_path31.default.join(cur, ".git"));
52995
53140
  } catch {
52996
53141
  }
52997
53142
  if (hasGit) return;
52998
53143
  if (cur === home) return;
52999
53144
  if (cur === fsRoot) return;
53000
- const parent = import_node_path30.default.dirname(cur);
53145
+ const parent = import_node_path31.default.dirname(cur);
53001
53146
  if (parent === cur) return;
53002
53147
  cur = parent;
53003
53148
  }
@@ -53032,7 +53177,7 @@ var AgentsScanner = class {
53032
53177
  }
53033
53178
  const fsBlock = [];
53034
53179
  scanAgentsDir(
53035
- import_node_path30.default.join(this.home, ".claude", "agents"),
53180
+ import_node_path31.default.join(this.home, ".claude", "agents"),
53036
53181
  "global",
53037
53182
  seen,
53038
53183
  fsBlock
@@ -53046,7 +53191,7 @@ var AgentsScanner = class {
53046
53191
  ...this.extraPluginRoots
53047
53192
  ];
53048
53193
  for (const { name, root } of plugins) {
53049
- const agentsRoot = import_node_path30.default.join(root, "agents");
53194
+ const agentsRoot = import_node_path31.default.join(root, "agents");
53050
53195
  scanPluginAgentsTree(agentsRoot, name, seen, fsBlock);
53051
53196
  }
53052
53197
  return [...builtinBlock, ...fsBlock];
@@ -53054,15 +53199,15 @@ var AgentsScanner = class {
53054
53199
  };
53055
53200
 
53056
53201
  // src/observer/session-observer.ts
53057
- var import_node_fs30 = __toESM(require("fs"), 1);
53202
+ var import_node_fs31 = __toESM(require("fs"), 1);
53058
53203
  var import_node_os11 = __toESM(require("os"), 1);
53059
- var import_node_path32 = __toESM(require("path"), 1);
53204
+ var import_node_path33 = __toESM(require("path"), 1);
53060
53205
  init_claude_history();
53061
53206
 
53062
53207
  // src/observer/subagent-meta-observer.ts
53063
- var import_node_fs29 = __toESM(require("fs"), 1);
53208
+ var import_node_fs30 = __toESM(require("fs"), 1);
53064
53209
  var import_node_os10 = __toESM(require("os"), 1);
53065
- var import_node_path31 = __toESM(require("path"), 1);
53210
+ var import_node_path32 = __toESM(require("path"), 1);
53066
53211
  init_claude_history();
53067
53212
  var META_RE = /^agent-([A-Za-z0-9_-]+)\.meta\.json$/;
53068
53213
  var SubagentMetaObserver = class {
@@ -53075,7 +53220,7 @@ var SubagentMetaObserver = class {
53075
53220
  watches = /* @__PURE__ */ new Map();
53076
53221
  // public for spec only:测试直接拼路径写假 meta.json;生产 start() 内部自己解析
53077
53222
  resolveSubagentDir(cwd, toolSessionId) {
53078
- return import_node_path31.default.join(
53223
+ return import_node_path32.default.join(
53079
53224
  this.home,
53080
53225
  ".claude",
53081
53226
  "projects",
@@ -53105,7 +53250,7 @@ var SubagentMetaObserver = class {
53105
53250
  attachWatcher(w2) {
53106
53251
  if (w2.watcher) return;
53107
53252
  try {
53108
- w2.watcher = import_node_fs29.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
53253
+ w2.watcher = import_node_fs30.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
53109
53254
  if (!name) return;
53110
53255
  const m2 = META_RE.exec(String(name));
53111
53256
  if (!m2) return;
@@ -53117,7 +53262,7 @@ var SubagentMetaObserver = class {
53117
53262
  scan(w2) {
53118
53263
  let entries;
53119
53264
  try {
53120
- entries = import_node_fs29.default.readdirSync(w2.dirPath);
53265
+ entries = import_node_fs30.default.readdirSync(w2.dirPath);
53121
53266
  } catch {
53122
53267
  return;
53123
53268
  }
@@ -53131,10 +53276,10 @@ var SubagentMetaObserver = class {
53131
53276
  if (!m2) return;
53132
53277
  const agentId = m2[1];
53133
53278
  if (w2.emitted.has(agentId)) return;
53134
- const file = import_node_path31.default.join(w2.dirPath, name);
53279
+ const file = import_node_path32.default.join(w2.dirPath, name);
53135
53280
  let raw;
53136
53281
  try {
53137
- raw = import_node_fs29.default.readFileSync(file, "utf8");
53282
+ raw = import_node_fs30.default.readFileSync(file, "utf8");
53138
53283
  } catch {
53139
53284
  return;
53140
53285
  }
@@ -53191,14 +53336,14 @@ var SessionObserver = class {
53191
53336
  metaObserver;
53192
53337
  resolveJsonlPath(cwd, toolSessionId, override) {
53193
53338
  if (override) return override;
53194
- return import_node_path32.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
53339
+ return import_node_path33.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
53195
53340
  }
53196
53341
  start(args) {
53197
53342
  this.stop(args.sessionId);
53198
53343
  const filePath = this.resolveJsonlPath(args.cwd, args.toolSessionId, args.jsonlPath);
53199
53344
  let size = 0;
53200
53345
  try {
53201
- size = import_node_fs30.default.statSync(filePath).size;
53346
+ size = import_node_fs31.default.statSync(filePath).size;
53202
53347
  } catch {
53203
53348
  }
53204
53349
  const w2 = {
@@ -53211,16 +53356,16 @@ var SessionObserver = class {
53211
53356
  adapter: args.adapter,
53212
53357
  prevIsRejectSentinel: false,
53213
53358
  toolSessionId: args.toolSessionId,
53214
- sawFile: import_node_fs30.default.existsSync(filePath),
53359
+ sawFile: import_node_fs31.default.existsSync(filePath),
53215
53360
  lastRelocateScanMs: 0,
53216
53361
  pendingCwdDerive: false,
53217
53362
  lastDeriveMs: 0
53218
53363
  };
53219
53364
  try {
53220
- import_node_fs30.default.mkdirSync(import_node_path32.default.dirname(filePath), { recursive: true });
53365
+ import_node_fs31.default.mkdirSync(import_node_path33.default.dirname(filePath), { recursive: true });
53221
53366
  } catch {
53222
53367
  }
53223
- w2.watcher = import_node_fs30.default.watch(import_node_path32.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
53368
+ w2.watcher = import_node_fs31.default.watch(import_node_path33.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
53224
53369
  if (!changedName || !w2.filePath.endsWith(changedName)) return;
53225
53370
  this.poll(w2);
53226
53371
  });
@@ -53243,7 +53388,7 @@ var SessionObserver = class {
53243
53388
  // 异常静默吞,不阻塞 watcher 启动
53244
53389
  hydrateMetaTail(w2, maxLines = 200) {
53245
53390
  try {
53246
- const raw = import_node_fs30.default.readFileSync(w2.filePath, "utf8");
53391
+ const raw = import_node_fs31.default.readFileSync(w2.filePath, "utf8");
53247
53392
  if (!raw) return;
53248
53393
  const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
53249
53394
  if (allLines.length === 0) return;
@@ -53267,7 +53412,7 @@ var SessionObserver = class {
53267
53412
  poll(w2) {
53268
53413
  let size = 0;
53269
53414
  try {
53270
- size = import_node_fs30.default.statSync(w2.filePath).size;
53415
+ size = import_node_fs31.default.statSync(w2.filePath).size;
53271
53416
  } catch {
53272
53417
  if (w2.sawFile) this.maybeRelocate(w2);
53273
53418
  return;
@@ -53278,11 +53423,11 @@ var SessionObserver = class {
53278
53423
  w2.buf = "";
53279
53424
  }
53280
53425
  if (size === w2.lastSize) return;
53281
- const fd = import_node_fs30.default.openSync(w2.filePath, "r");
53426
+ const fd = import_node_fs31.default.openSync(w2.filePath, "r");
53282
53427
  try {
53283
53428
  const len = size - w2.lastSize;
53284
53429
  const buf = Buffer.alloc(len);
53285
- import_node_fs30.default.readSync(fd, buf, 0, len, w2.lastSize);
53430
+ import_node_fs31.default.readSync(fd, buf, 0, len, w2.lastSize);
53286
53431
  w2.lastSize = size;
53287
53432
  w2.buf += buf.toString("utf8");
53288
53433
  let newlineIndex;
@@ -53305,7 +53450,7 @@ var SessionObserver = class {
53305
53450
  }
53306
53451
  }
53307
53452
  } finally {
53308
- import_node_fs30.default.closeSync(fd);
53453
+ import_node_fs31.default.closeSync(fd);
53309
53454
  }
53310
53455
  if (w2.pendingCwdDerive) this.maybeDeriveCwd(w2);
53311
53456
  }
@@ -53317,7 +53462,7 @@ var SessionObserver = class {
53317
53462
  const now = Date.now();
53318
53463
  if (now - w2.lastRelocateScanMs < 1e3) return;
53319
53464
  w2.lastRelocateScanMs = now;
53320
- const projectsRoot = import_node_path32.default.join(this.home, ".claude", "projects");
53465
+ const projectsRoot = import_node_path33.default.join(this.home, ".claude", "projects");
53321
53466
  const found = findTranscriptByToolSessionId(projectsRoot, w2.toolSessionId);
53322
53467
  if (!found || found === w2.filePath) return;
53323
53468
  try {
@@ -53326,7 +53471,7 @@ var SessionObserver = class {
53326
53471
  }
53327
53472
  w2.filePath = found;
53328
53473
  try {
53329
- w2.watcher = import_node_fs30.default.watch(import_node_path32.default.dirname(found), { persistent: false }, (_event, changedName) => {
53474
+ w2.watcher = import_node_fs31.default.watch(import_node_path33.default.dirname(found), { persistent: false }, (_event, changedName) => {
53330
53475
  if (!changedName || !w2.filePath.endsWith(changedName)) return;
53331
53476
  this.poll(w2);
53332
53477
  });
@@ -54121,8 +54266,8 @@ async function authenticate(token, deps) {
54121
54266
  }
54122
54267
 
54123
54268
  // src/legacy-cleanup.ts
54124
- var fs35 = __toESM(require("fs/promises"), 1);
54125
- var path37 = __toESM(require("path"), 1);
54269
+ var fs36 = __toESM(require("fs/promises"), 1);
54270
+ var path38 = __toESM(require("path"), 1);
54126
54271
  var LEGACY_FILES = [
54127
54272
  "personal-capability.json",
54128
54273
  "received-capabilities.json",
@@ -54131,9 +54276,9 @@ var LEGACY_FILES = [
54131
54276
  async function cleanupLegacyCapabilityFiles(dataDir, logger) {
54132
54277
  const removed = [];
54133
54278
  for (const name of LEGACY_FILES) {
54134
- const p2 = path37.join(dataDir, name);
54279
+ const p2 = path38.join(dataDir, name);
54135
54280
  try {
54136
- await fs35.unlink(p2);
54281
+ await fs36.unlink(p2);
54137
54282
  removed.push(name);
54138
54283
  logger.debug("legacy capability file removed", { file: name });
54139
54284
  } catch (err) {
@@ -54145,13 +54290,13 @@ async function cleanupLegacyCapabilityFiles(dataDir, logger) {
54145
54290
  }
54146
54291
 
54147
54292
  // src/inbox/inbox-store.ts
54148
- var fs36 = __toESM(require("fs"), 1);
54149
- var path38 = __toESM(require("path"), 1);
54293
+ var fs37 = __toESM(require("fs"), 1);
54294
+ var path39 = __toESM(require("path"), 1);
54150
54295
  var INBOX_SUBDIR = "inbox";
54151
54296
  var InboxStore = class {
54152
54297
  constructor(dataDir) {
54153
54298
  this.dataDir = dataDir;
54154
- fs36.mkdirSync(this.dirPath(), { recursive: true });
54299
+ fs37.mkdirSync(this.dirPath(), { recursive: true });
54155
54300
  }
54156
54301
  dataDir;
54157
54302
  /**
@@ -54163,7 +54308,7 @@ var InboxStore = class {
54163
54308
  const file = this.filePath(peerDeviceId);
54164
54309
  let raw;
54165
54310
  try {
54166
- raw = fs36.readFileSync(file, "utf8");
54311
+ raw = fs37.readFileSync(file, "utf8");
54167
54312
  } catch (err) {
54168
54313
  if (err?.code === "ENOENT") return [];
54169
54314
  return [];
@@ -54179,7 +54324,7 @@ var InboxStore = class {
54179
54324
  const dir = this.dirPath();
54180
54325
  let entries;
54181
54326
  try {
54182
- entries = fs36.readdirSync(dir);
54327
+ entries = fs37.readdirSync(dir);
54183
54328
  } catch (err) {
54184
54329
  if (err?.code === "ENOENT") return [];
54185
54330
  return [];
@@ -54195,9 +54340,9 @@ var InboxStore = class {
54195
54340
  if (existing.some((m2) => m2.id === message.id)) return;
54196
54341
  const file = this.filePath(message.peerDeviceId);
54197
54342
  const line = JSON.stringify(message) + "\n";
54198
- fs36.appendFileSync(file, line, { mode: 384 });
54343
+ fs37.appendFileSync(file, line, { mode: 384 });
54199
54344
  try {
54200
- fs36.chmodSync(file, 384);
54345
+ fs37.chmodSync(file, 384);
54201
54346
  } catch {
54202
54347
  }
54203
54348
  }
@@ -54227,7 +54372,7 @@ var InboxStore = class {
54227
54372
  removeByPeerDeviceId(peerDeviceId) {
54228
54373
  const file = this.filePath(peerDeviceId);
54229
54374
  try {
54230
- fs36.unlinkSync(file);
54375
+ fs37.unlinkSync(file);
54231
54376
  } catch (err) {
54232
54377
  if (err?.code === "ENOENT") return;
54233
54378
  }
@@ -54236,18 +54381,18 @@ var InboxStore = class {
54236
54381
  const file = this.filePath(peerDeviceId);
54237
54382
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
54238
54383
  const content = messages.map((m2) => JSON.stringify(m2)).join("\n") + (messages.length > 0 ? "\n" : "");
54239
- fs36.writeFileSync(tmp, content, { mode: 384 });
54240
- fs36.renameSync(tmp, file);
54384
+ fs37.writeFileSync(tmp, content, { mode: 384 });
54385
+ fs37.renameSync(tmp, file);
54241
54386
  try {
54242
- fs36.chmodSync(file, 384);
54387
+ fs37.chmodSync(file, 384);
54243
54388
  } catch {
54244
54389
  }
54245
54390
  }
54246
54391
  dirPath() {
54247
- return path38.join(this.dataDir, INBOX_SUBDIR);
54392
+ return path39.join(this.dataDir, INBOX_SUBDIR);
54248
54393
  }
54249
54394
  filePath(peerDeviceId) {
54250
- return path38.join(this.dirPath(), `${peerDeviceId}.jsonl`);
54395
+ return path39.join(this.dirPath(), `${peerDeviceId}.jsonl`);
54251
54396
  }
54252
54397
  };
54253
54398
  function parseAllLines(raw) {
@@ -54335,8 +54480,8 @@ var InboxManager = class {
54335
54480
  };
54336
54481
 
54337
54482
  // src/state/contact-store.ts
54338
- var fs37 = __toESM(require("fs"), 1);
54339
- var path39 = __toESM(require("path"), 1);
54483
+ var fs38 = __toESM(require("fs"), 1);
54484
+ var path40 = __toESM(require("path"), 1);
54340
54485
  var FILE_NAME = "contacts.json";
54341
54486
  var ContactStore = class {
54342
54487
  constructor(dataDir) {
@@ -54346,10 +54491,10 @@ var ContactStore = class {
54346
54491
  contacts = /* @__PURE__ */ new Map();
54347
54492
  load() {
54348
54493
  this.contacts.clear();
54349
- const file = path39.join(this.dataDir, FILE_NAME);
54494
+ const file = path40.join(this.dataDir, FILE_NAME);
54350
54495
  let raw;
54351
54496
  try {
54352
- raw = fs37.readFileSync(file, "utf8");
54497
+ raw = fs38.readFileSync(file, "utf8");
54353
54498
  } catch (err) {
54354
54499
  if (err?.code !== "ENOENT") this.renameBak(file);
54355
54500
  return;
@@ -54418,20 +54563,20 @@ var ContactStore = class {
54418
54563
  return true;
54419
54564
  }
54420
54565
  flush() {
54421
- const file = path39.join(this.dataDir, FILE_NAME);
54566
+ const file = path40.join(this.dataDir, FILE_NAME);
54422
54567
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
54423
54568
  const content = JSON.stringify(
54424
54569
  { contacts: Array.from(this.contacts.values()) },
54425
54570
  null,
54426
54571
  2
54427
54572
  );
54428
- fs37.mkdirSync(this.dataDir, { recursive: true });
54429
- fs37.writeFileSync(tmp, content, { mode: 384 });
54430
- fs37.renameSync(tmp, file);
54573
+ fs38.mkdirSync(this.dataDir, { recursive: true });
54574
+ fs38.writeFileSync(tmp, content, { mode: 384 });
54575
+ fs38.renameSync(tmp, file);
54431
54576
  }
54432
54577
  renameBak(file) {
54433
54578
  try {
54434
- fs37.renameSync(file, `${file}.bak`);
54579
+ fs38.renameSync(file, `${file}.bak`);
54435
54580
  } catch {
54436
54581
  }
54437
54582
  }
@@ -54539,61 +54684,61 @@ async function connectRemote(args) {
54539
54684
  }
54540
54685
 
54541
54686
  // src/migrations/2026-05-20-flatten-sessions.ts
54542
- var fs38 = __toESM(require("fs"), 1);
54543
- var path40 = __toESM(require("path"), 1);
54687
+ var fs39 = __toESM(require("fs"), 1);
54688
+ var path41 = __toESM(require("path"), 1);
54544
54689
  var MIGRATION_FLAG_NAME = ".migration.v1.done";
54545
54690
  function migrateFlattenSessions(opts) {
54546
54691
  const dataDir = opts.dataDir;
54547
54692
  const now = opts.now ?? Date.now;
54548
- const sessionsDir = path40.join(dataDir, "sessions");
54549
- const flagPath = path40.join(sessionsDir, MIGRATION_FLAG_NAME);
54693
+ const sessionsDir = path41.join(dataDir, "sessions");
54694
+ const flagPath = path41.join(sessionsDir, MIGRATION_FLAG_NAME);
54550
54695
  if (existsSync3(flagPath)) {
54551
54696
  return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
54552
54697
  }
54553
54698
  let movedBare = 0;
54554
54699
  let movedVmOwner = 0;
54555
54700
  let archivedListener = 0;
54556
- const defaultDir = path40.join(sessionsDir, "default");
54701
+ const defaultDir = path41.join(sessionsDir, "default");
54557
54702
  if (existsSync3(defaultDir)) {
54558
54703
  for (const entry of readdirSafe(defaultDir)) {
54559
54704
  if (!entry.endsWith(".json")) continue;
54560
- const src = path40.join(defaultDir, entry);
54561
- const dst = path40.join(sessionsDir, entry);
54562
- fs38.renameSync(src, dst);
54705
+ const src = path41.join(defaultDir, entry);
54706
+ const dst = path41.join(sessionsDir, entry);
54707
+ fs39.renameSync(src, dst);
54563
54708
  movedBare += 1;
54564
54709
  }
54565
54710
  rmdirIfEmpty(defaultDir);
54566
54711
  }
54567
54712
  for (const pid of readdirSafe(sessionsDir)) {
54568
- const personaDir = path40.join(sessionsDir, pid);
54713
+ const personaDir = path41.join(sessionsDir, pid);
54569
54714
  if (!isDir(personaDir)) continue;
54570
54715
  if (pid === "default") continue;
54571
- const ownerSrc = path40.join(personaDir, "owner");
54716
+ const ownerSrc = path41.join(personaDir, "owner");
54572
54717
  if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
54573
- const ownerDst = path40.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
54574
- fs38.mkdirSync(ownerDst, { recursive: true });
54718
+ const ownerDst = path41.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
54719
+ fs39.mkdirSync(ownerDst, { recursive: true });
54575
54720
  for (const file of readdirSafe(ownerSrc)) {
54576
54721
  if (!file.endsWith(".json")) continue;
54577
- fs38.renameSync(path40.join(ownerSrc, file), path40.join(ownerDst, file));
54722
+ fs39.renameSync(path41.join(ownerSrc, file), path41.join(ownerDst, file));
54578
54723
  movedVmOwner += 1;
54579
54724
  }
54580
54725
  rmdirIfEmpty(ownerSrc);
54581
54726
  }
54582
- const listenerSrc = path40.join(personaDir, "listener");
54727
+ const listenerSrc = path41.join(personaDir, "listener");
54583
54728
  if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
54584
- const archiveDst = path40.join(dataDir, ".legacy", `listener-${pid}`);
54585
- fs38.mkdirSync(archiveDst, { recursive: true });
54729
+ const archiveDst = path41.join(dataDir, ".legacy", `listener-${pid}`);
54730
+ fs39.mkdirSync(archiveDst, { recursive: true });
54586
54731
  for (const file of readdirSafe(listenerSrc)) {
54587
54732
  if (!file.endsWith(".json")) continue;
54588
- fs38.renameSync(path40.join(listenerSrc, file), path40.join(archiveDst, file));
54733
+ fs39.renameSync(path41.join(listenerSrc, file), path41.join(archiveDst, file));
54589
54734
  archivedListener += 1;
54590
54735
  }
54591
54736
  rmdirIfEmpty(listenerSrc);
54592
54737
  }
54593
54738
  rmdirIfEmpty(personaDir);
54594
54739
  }
54595
- fs38.mkdirSync(sessionsDir, { recursive: true });
54596
- fs38.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
54740
+ fs39.mkdirSync(sessionsDir, { recursive: true });
54741
+ fs39.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
54597
54742
  return {
54598
54743
  skipped: false,
54599
54744
  flagWritten: true,
@@ -54604,7 +54749,7 @@ function migrateFlattenSessions(opts) {
54604
54749
  }
54605
54750
  function existsSync3(p2) {
54606
54751
  try {
54607
- fs38.statSync(p2);
54752
+ fs39.statSync(p2);
54608
54753
  return true;
54609
54754
  } catch {
54610
54755
  return false;
@@ -54612,31 +54757,31 @@ function existsSync3(p2) {
54612
54757
  }
54613
54758
  function isDir(p2) {
54614
54759
  try {
54615
- return fs38.statSync(p2).isDirectory();
54760
+ return fs39.statSync(p2).isDirectory();
54616
54761
  } catch {
54617
54762
  return false;
54618
54763
  }
54619
54764
  }
54620
54765
  function readdirSafe(p2) {
54621
54766
  try {
54622
- return fs38.readdirSync(p2);
54767
+ return fs39.readdirSync(p2);
54623
54768
  } catch {
54624
54769
  return [];
54625
54770
  }
54626
54771
  }
54627
54772
  function rmdirIfEmpty(p2) {
54628
54773
  try {
54629
- fs38.rmdirSync(p2);
54774
+ fs39.rmdirSync(p2);
54630
54775
  } catch {
54631
54776
  }
54632
54777
  }
54633
54778
 
54634
54779
  // src/transport/http-router.ts
54635
- var import_node_fs32 = __toESM(require("fs"), 1);
54636
- var import_node_path36 = __toESM(require("path"), 1);
54780
+ var import_node_fs33 = __toESM(require("fs"), 1);
54781
+ var import_node_path37 = __toESM(require("path"), 1);
54637
54782
 
54638
54783
  // src/attachment/mime.ts
54639
- var import_node_path33 = __toESM(require("path"), 1);
54784
+ var import_node_path34 = __toESM(require("path"), 1);
54640
54785
  var TEXT_PLAIN = "text/plain; charset=utf-8";
54641
54786
  var EXT_TO_NATIVE_MIME = {
54642
54787
  // 图片
@@ -54743,7 +54888,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
54743
54888
  ".mk"
54744
54889
  ]);
54745
54890
  function lookupMime(filePathOrName) {
54746
- const ext = import_node_path33.default.extname(filePathOrName).toLowerCase();
54891
+ const ext = import_node_path34.default.extname(filePathOrName).toLowerCase();
54747
54892
  if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
54748
54893
  if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
54749
54894
  return "application/octet-stream";
@@ -54813,8 +54958,8 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
54813
54958
  }
54814
54959
 
54815
54960
  // src/attachment/upload.ts
54816
- var import_node_fs31 = __toESM(require("fs"), 1);
54817
- var import_node_path34 = __toESM(require("path"), 1);
54961
+ var import_node_fs32 = __toESM(require("fs"), 1);
54962
+ var import_node_path35 = __toESM(require("path"), 1);
54818
54963
  var import_node_crypto8 = __toESM(require("crypto"), 1);
54819
54964
  var import_promises3 = require("stream/promises");
54820
54965
  var UploadError = class extends Error {
@@ -54826,22 +54971,22 @@ var UploadError = class extends Error {
54826
54971
  code;
54827
54972
  };
54828
54973
  function assertValidFileName(fileName) {
54829
- if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path34.default.basename(fileName)) {
54974
+ if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path35.default.basename(fileName)) {
54830
54975
  throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
54831
54976
  }
54832
54977
  }
54833
54978
  var HASH_PREFIX_LEN = 16;
54834
54979
  async function writeUploadedAttachment(args) {
54835
54980
  assertValidFileName(args.fileName);
54836
- const attachmentsRoot = import_node_path34.default.join(args.sessionDir, ".attachments");
54981
+ const attachmentsRoot = import_node_path35.default.join(args.baseDir, ".attachments");
54837
54982
  try {
54838
- import_node_fs31.default.mkdirSync(attachmentsRoot, { recursive: true });
54983
+ import_node_fs32.default.mkdirSync(attachmentsRoot, { recursive: true });
54839
54984
  } catch (err) {
54840
54985
  throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
54841
54986
  }
54842
54987
  const hasher = import_node_crypto8.default.createHash("sha256");
54843
54988
  let actualSize = 0;
54844
- const tmpPath = import_node_path34.default.join(
54989
+ const tmpPath = import_node_path35.default.join(
54845
54990
  attachmentsRoot,
54846
54991
  `.upload-${process.pid}-${Date.now()}-${import_node_crypto8.default.randomBytes(4).toString("hex")}`
54847
54992
  );
@@ -54856,18 +55001,18 @@ async function writeUploadedAttachment(args) {
54856
55001
  yield buf;
54857
55002
  }
54858
55003
  },
54859
- import_node_fs31.default.createWriteStream(tmpPath, { mode: 384 })
55004
+ import_node_fs32.default.createWriteStream(tmpPath, { mode: 384 })
54860
55005
  );
54861
55006
  } catch (err) {
54862
55007
  try {
54863
- import_node_fs31.default.unlinkSync(tmpPath);
55008
+ import_node_fs32.default.unlinkSync(tmpPath);
54864
55009
  } catch {
54865
55010
  }
54866
55011
  throw new UploadError("STORAGE_ERROR", `write failed: ${err.message}`);
54867
55012
  }
54868
55013
  if (actualSize !== args.contentLength) {
54869
55014
  try {
54870
- import_node_fs31.default.unlinkSync(tmpPath);
55015
+ import_node_fs32.default.unlinkSync(tmpPath);
54871
55016
  } catch {
54872
55017
  }
54873
55018
  throw new UploadError(
@@ -54876,35 +55021,35 @@ async function writeUploadedAttachment(args) {
54876
55021
  );
54877
55022
  }
54878
55023
  const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
54879
- const hashDir = import_node_path34.default.join(attachmentsRoot, attachmentId);
55024
+ const hashDir = import_node_path35.default.join(attachmentsRoot, attachmentId);
54880
55025
  let finalFileName;
54881
55026
  let hashDirExists = false;
54882
55027
  try {
54883
- hashDirExists = import_node_fs31.default.statSync(hashDir).isDirectory();
55028
+ hashDirExists = import_node_fs32.default.statSync(hashDir).isDirectory();
54884
55029
  } catch {
54885
55030
  }
54886
55031
  if (hashDirExists) {
54887
- const existing = import_node_fs31.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
55032
+ const existing = import_node_fs32.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
54888
55033
  finalFileName = existing[0] ?? args.fileName;
54889
55034
  try {
54890
- import_node_fs31.default.unlinkSync(tmpPath);
55035
+ import_node_fs32.default.unlinkSync(tmpPath);
54891
55036
  } catch {
54892
55037
  }
54893
55038
  } else {
54894
55039
  try {
54895
- import_node_fs31.default.mkdirSync(hashDir, { recursive: true });
55040
+ import_node_fs32.default.mkdirSync(hashDir, { recursive: true });
54896
55041
  finalFileName = args.fileName;
54897
- import_node_fs31.default.renameSync(tmpPath, import_node_path34.default.join(hashDir, finalFileName));
55042
+ import_node_fs32.default.renameSync(tmpPath, import_node_path35.default.join(hashDir, finalFileName));
54898
55043
  } catch (err) {
54899
55044
  try {
54900
- import_node_fs31.default.unlinkSync(tmpPath);
55045
+ import_node_fs32.default.unlinkSync(tmpPath);
54901
55046
  } catch {
54902
55047
  }
54903
55048
  throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
54904
55049
  }
54905
55050
  }
54906
- const absPath = import_node_path34.default.join(hashDir, finalFileName);
54907
- const relPath = import_node_path34.default.relative(args.sessionDir, absPath);
55051
+ const absPath = import_node_path35.default.join(hashDir, finalFileName);
55052
+ const relPath = absPath;
54908
55053
  args.groupFileStore.upsert(args.scope, args.sessionId, {
54909
55054
  relPath: absPath,
54910
55055
  // 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
@@ -54918,7 +55063,7 @@ async function writeUploadedAttachment(args) {
54918
55063
 
54919
55064
  // src/extension/import.ts
54920
55065
  var import_promises4 = __toESM(require("fs/promises"), 1);
54921
- var import_node_path35 = __toESM(require("path"), 1);
55066
+ var import_node_path36 = __toESM(require("path"), 1);
54922
55067
  var import_node_os12 = __toESM(require("os"), 1);
54923
55068
  var import_jszip = __toESM(require_lib3(), 1);
54924
55069
  var ImportError = class extends Error {
@@ -54936,7 +55081,7 @@ async function importZip(buf, root) {
54936
55081
  throw new ImportError("ZIP_INVALID", `failed to load zip: ${e.message}`);
54937
55082
  }
54938
55083
  for (const name of Object.keys(zip.files)) {
54939
- if (name.includes("..") || name.startsWith("/") || import_node_path35.default.isAbsolute(name)) {
55084
+ if (name.includes("..") || name.startsWith("/") || import_node_path36.default.isAbsolute(name)) {
54940
55085
  throw new ImportError("ZIP_INVALID", `unsafe zip entry path: ${name}`);
54941
55086
  }
54942
55087
  }
@@ -54969,7 +55114,7 @@ async function importZip(buf, root) {
54969
55114
  );
54970
55115
  }
54971
55116
  }
54972
- const destDir = import_node_path35.default.join(root, manifest.id);
55117
+ const destDir = import_node_path36.default.join(root, manifest.id);
54973
55118
  let destExists = false;
54974
55119
  try {
54975
55120
  await import_promises4.default.access(destDir);
@@ -54979,15 +55124,15 @@ async function importZip(buf, root) {
54979
55124
  if (destExists) {
54980
55125
  throw new ImportError("ALREADY_EXISTS", `extension ${manifest.id} already installed`);
54981
55126
  }
54982
- const stage = await import_promises4.default.mkdtemp(import_node_path35.default.join(import_node_os12.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
55127
+ const stage = await import_promises4.default.mkdtemp(import_node_path36.default.join(import_node_os12.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
54983
55128
  try {
54984
55129
  for (const [name, entry] of Object.entries(zip.files)) {
54985
- const dest = import_node_path35.default.join(stage, name);
55130
+ const dest = import_node_path36.default.join(stage, name);
54986
55131
  if (entry.dir) {
54987
55132
  await import_promises4.default.mkdir(dest, { recursive: true });
54988
55133
  continue;
54989
55134
  }
54990
- await import_promises4.default.mkdir(import_node_path35.default.dirname(dest), { recursive: true });
55135
+ await import_promises4.default.mkdir(import_node_path36.default.dirname(dest), { recursive: true });
54991
55136
  const content = await entry.async("nodebuffer");
54992
55137
  await import_promises4.default.writeFile(dest, content);
54993
55138
  }
@@ -55017,7 +55162,7 @@ var SHARE_UI_ASSET_MIME = {
55017
55162
  ".wasm": "application/wasm"
55018
55163
  };
55019
55164
  function shareUiAssetMime(filePath) {
55020
- const ext = import_node_path36.default.extname(filePath).toLowerCase();
55165
+ const ext = import_node_path37.default.extname(filePath).toLowerCase();
55021
55166
  return SHARE_UI_ASSET_MIME[ext] ?? lookupMime(filePath);
55022
55167
  }
55023
55168
  var AUTHED_RPC_ROUTE_PREFIX = "/rpc/";
@@ -55116,7 +55261,7 @@ function isValidUploadFileName(fileName) {
55116
55261
  if (fileName === "." || fileName === "..") return false;
55117
55262
  if (fileName.startsWith(".")) return false;
55118
55263
  if (fileName.includes("/") || fileName.includes("\\")) return false;
55119
- return fileName === import_node_path36.default.basename(fileName);
55264
+ return fileName === import_node_path37.default.basename(fileName);
55120
55265
  }
55121
55266
  function createHttpRouter(deps) {
55122
55267
  return async (req, res) => {
@@ -55204,7 +55349,7 @@ function createHttpRouter(deps) {
55204
55349
  }
55205
55350
  let buf;
55206
55351
  try {
55207
- buf = await import_node_fs32.default.promises.readFile(abs);
55352
+ buf = await import_node_fs33.default.promises.readFile(abs);
55208
55353
  } catch {
55209
55354
  sendJson(res, 404, { code: "NOT_FOUND", message: "asset not found" });
55210
55355
  return true;
@@ -55352,7 +55497,7 @@ function createHttpRouter(deps) {
55352
55497
  sendHtml(res, statusByCode[r.code], loader.renderErrorHtml(r.code, msgByCode[r.code]));
55353
55498
  return true;
55354
55499
  }
55355
- sendHtml(res, 200, loader.renderViewerHtml(import_node_path36.default.basename(r.absPath)));
55500
+ sendHtml(res, 200, loader.renderViewerHtml(import_node_path37.default.basename(r.absPath)));
55356
55501
  return true;
55357
55502
  }
55358
55503
  const ctx = deps.authResolver.resolveFromHeader(
@@ -55411,9 +55556,9 @@ function createHttpRouter(deps) {
55411
55556
  });
55412
55557
  return true;
55413
55558
  }
55414
- const sessionDir = up.getSessionDir(sessionId);
55559
+ const baseDir = up.getAttachmentBaseDir(sessionId);
55415
55560
  const scope = up.getSessionScope(sessionId);
55416
- if (!sessionDir || !scope) {
55561
+ if (!baseDir || !scope) {
55417
55562
  sendUploadJson(404, { code: "NOT_FOUND", message: `session ${sessionId} not found` });
55418
55563
  return true;
55419
55564
  }
@@ -55435,7 +55580,7 @@ function createHttpRouter(deps) {
55435
55580
  }
55436
55581
  try {
55437
55582
  const result = await writeUploadedAttachment({
55438
- sessionDir,
55583
+ baseDir,
55439
55584
  sessionId,
55440
55585
  scope,
55441
55586
  fileName,
@@ -55473,7 +55618,7 @@ function createHttpRouter(deps) {
55473
55618
  return true;
55474
55619
  }
55475
55620
  let absPath;
55476
- if (import_node_path36.default.isAbsolute(pathParam)) {
55621
+ if (import_node_path37.default.isAbsolute(pathParam)) {
55477
55622
  absPath = pathParam;
55478
55623
  } else if (deps.sessionStore) {
55479
55624
  const file = deps.sessionStore.read(sid);
@@ -55481,7 +55626,7 @@ function createHttpRouter(deps) {
55481
55626
  sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sid} not found` });
55482
55627
  return true;
55483
55628
  }
55484
- absPath = import_node_path36.default.join(file.cwd, pathParam);
55629
+ absPath = import_node_path37.default.join(file.cwd, pathParam);
55485
55630
  } else {
55486
55631
  sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "sessionStore not wired" }));
55487
55632
  return true;
@@ -55568,7 +55713,7 @@ function withCtx(ctx, body) {
55568
55713
  function streamFile(res, absPath, logger) {
55569
55714
  let stat;
55570
55715
  try {
55571
- stat = import_node_fs32.default.statSync(absPath);
55716
+ stat = import_node_fs33.default.statSync(absPath);
55572
55717
  } catch (err) {
55573
55718
  const code = err?.code;
55574
55719
  if (code === "ENOENT") {
@@ -55583,7 +55728,7 @@ function streamFile(res, absPath, logger) {
55583
55728
  return;
55584
55729
  }
55585
55730
  const mime = lookupMime(absPath);
55586
- const basename = import_node_path36.default.basename(absPath);
55731
+ const basename = import_node_path37.default.basename(absPath);
55587
55732
  res.writeHead(200, {
55588
55733
  "Content-Type": mime,
55589
55734
  "Content-Length": String(stat.size),
@@ -55591,7 +55736,7 @@ function streamFile(res, absPath, logger) {
55591
55736
  // 防止浏览器把任意 mime 当 html 渲染
55592
55737
  "X-Content-Type-Options": "nosniff"
55593
55738
  });
55594
- const stream = import_node_fs32.default.createReadStream(absPath);
55739
+ const stream = import_node_fs33.default.createReadStream(absPath);
55595
55740
  stream.on("error", (err) => {
55596
55741
  logger?.warn("streamFile read error", { absPath, err: err.message });
55597
55742
  res.destroy();
@@ -55600,8 +55745,8 @@ function streamFile(res, absPath, logger) {
55600
55745
  }
55601
55746
 
55602
55747
  // src/attachment/gc.ts
55603
- var import_node_fs33 = __toESM(require("fs"), 1);
55604
- var import_node_path37 = __toESM(require("path"), 1);
55748
+ var import_node_fs34 = __toESM(require("fs"), 1);
55749
+ var import_node_path38 = __toESM(require("path"), 1);
55605
55750
  var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
55606
55751
  function runAttachmentGc(args) {
55607
55752
  const now = (args.now ?? Date.now)();
@@ -55610,66 +55755,75 @@ function runAttachmentGc(args) {
55610
55755
  for (const { scope, sessionId } of args.sessionScopes) {
55611
55756
  for (const entry of args.groupFileStore.list(scope, sessionId)) {
55612
55757
  if (entry.stale) continue;
55613
- if (import_node_path37.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
55758
+ if (import_node_path38.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
55614
55759
  }
55615
55760
  }
55761
+ const visited = /* @__PURE__ */ new Set();
55616
55762
  for (const { scope, sessionId } of args.sessionScopes) {
55617
- const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path37.default.join(
55763
+ const legacyDir = args.getSessionCwd?.(sessionId) ?? import_node_path38.default.join(
55618
55764
  args.dataDir,
55619
55765
  "sessions",
55620
55766
  ...scopeSubPath(scope).map(safeFileName),
55621
55767
  safeFileName(sessionId)
55622
55768
  );
55623
- const attRoot = import_node_path37.default.join(sessionDir, ".attachments");
55624
- let hashDirs;
55625
- try {
55626
- hashDirs = import_node_fs33.default.readdirSync(attRoot);
55627
- } catch (err) {
55628
- if (err.code === "ENOENT") continue;
55629
- args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
55630
- continue;
55631
- }
55632
- for (const hashDir of hashDirs) {
55633
- const hashDirAbs = import_node_path37.default.join(attRoot, hashDir);
55634
- let files;
55769
+ const userWorkDir = args.getUserWorkDir?.(sessionId) ?? null;
55770
+ const roots = [legacyDir, ...userWorkDir ? [userWorkDir] : []].filter((r) => {
55771
+ if (visited.has(r)) return false;
55772
+ visited.add(r);
55773
+ return true;
55774
+ });
55775
+ for (const rootDir of roots) {
55776
+ const attRoot = import_node_path38.default.join(rootDir, ".attachments");
55777
+ let hashDirs;
55635
55778
  try {
55636
- files = import_node_fs33.default.readdirSync(hashDirAbs);
55637
- } catch {
55779
+ hashDirs = import_node_fs34.default.readdirSync(attRoot);
55780
+ } catch (err) {
55781
+ if (err.code === "ENOENT") continue;
55782
+ args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
55638
55783
  continue;
55639
55784
  }
55640
- for (const name of files) {
55641
- const file = import_node_path37.default.join(hashDirAbs, name);
55642
- let stat;
55785
+ for (const hashDir of hashDirs) {
55786
+ const hashDirAbs = import_node_path38.default.join(attRoot, hashDir);
55787
+ let files;
55643
55788
  try {
55644
- stat = import_node_fs33.default.statSync(file);
55789
+ files = import_node_fs34.default.readdirSync(hashDirAbs);
55645
55790
  } catch {
55646
55791
  continue;
55647
55792
  }
55648
- if (!stat.isFile()) continue;
55649
- const age = now - stat.mtimeMs;
55650
- if (age < ttlMs) continue;
55651
- if (liveAbs.has(file)) continue;
55793
+ for (const name of files) {
55794
+ const file = import_node_path38.default.join(hashDirAbs, name);
55795
+ let stat;
55796
+ try {
55797
+ stat = import_node_fs34.default.statSync(file);
55798
+ } catch {
55799
+ continue;
55800
+ }
55801
+ if (!stat.isFile()) continue;
55802
+ const age = now - stat.mtimeMs;
55803
+ if (age < ttlMs) continue;
55804
+ if (liveAbs.has(file)) continue;
55805
+ try {
55806
+ import_node_fs34.default.unlinkSync(file);
55807
+ } catch (err) {
55808
+ args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
55809
+ }
55810
+ }
55652
55811
  try {
55653
- import_node_fs33.default.unlinkSync(file);
55654
- } catch (err) {
55655
- args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
55812
+ if (import_node_fs34.default.readdirSync(hashDirAbs).length === 0) import_node_fs34.default.rmdirSync(hashDirAbs);
55813
+ } catch {
55656
55814
  }
55657
55815
  }
55658
55816
  try {
55659
- if (import_node_fs33.default.readdirSync(hashDirAbs).length === 0) import_node_fs33.default.rmdirSync(hashDirAbs);
55817
+ if (import_node_fs34.default.readdirSync(attRoot).length === 0) import_node_fs34.default.rmdirSync(attRoot);
55660
55818
  } catch {
55661
55819
  }
55662
55820
  }
55663
- try {
55664
- if (import_node_fs33.default.readdirSync(attRoot).length === 0) import_node_fs33.default.rmdirSync(attRoot);
55665
- } catch {
55666
- }
55667
55821
  }
55668
55822
  }
55669
55823
 
55670
55824
  // src/attachment/group.ts
55671
- var import_node_fs34 = __toESM(require("fs"), 1);
55672
- var import_node_path38 = __toESM(require("path"), 1);
55825
+ var import_node_fs35 = __toESM(require("fs"), 1);
55826
+ var import_node_path39 = __toESM(require("path"), 1);
55673
55827
  var import_node_crypto9 = __toESM(require("crypto"), 1);
55674
55828
  init_protocol();
55675
55829
  var GroupFileStore = class {
@@ -55681,11 +55835,11 @@ var GroupFileStore = class {
55681
55835
  this.logger = opts.logger;
55682
55836
  }
55683
55837
  rootForScope(scope) {
55684
- return import_node_path38.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
55838
+ return import_node_path39.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
55685
55839
  }
55686
55840
  /** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
55687
55841
  filePath(scope, sessionId) {
55688
- return import_node_path38.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
55842
+ return import_node_path39.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
55689
55843
  }
55690
55844
  cacheKey(scope, sessionId) {
55691
55845
  return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
@@ -55694,7 +55848,7 @@ var GroupFileStore = class {
55694
55848
  readFile(scope, sessionId) {
55695
55849
  const file = this.filePath(scope, sessionId);
55696
55850
  try {
55697
- const raw = import_node_fs34.default.readFileSync(file, "utf8");
55851
+ const raw = import_node_fs35.default.readFileSync(file, "utf8");
55698
55852
  const parsed = JSON.parse(raw);
55699
55853
  if (!Array.isArray(parsed)) {
55700
55854
  this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
@@ -55720,10 +55874,10 @@ var GroupFileStore = class {
55720
55874
  }
55721
55875
  writeFile(scope, sessionId, entries) {
55722
55876
  const file = this.filePath(scope, sessionId);
55723
- import_node_fs34.default.mkdirSync(import_node_path38.default.dirname(file), { recursive: true });
55877
+ import_node_fs35.default.mkdirSync(import_node_path39.default.dirname(file), { recursive: true });
55724
55878
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
55725
- import_node_fs34.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
55726
- import_node_fs34.default.renameSync(tmp, file);
55879
+ import_node_fs35.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
55880
+ import_node_fs35.default.renameSync(tmp, file);
55727
55881
  }
55728
55882
  /** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
55729
55883
  list(scope, sessionId) {
@@ -55809,10 +55963,10 @@ var GroupFileStore = class {
55809
55963
  };
55810
55964
 
55811
55965
  // src/discovery/state-file.ts
55812
- var import_node_fs35 = __toESM(require("fs"), 1);
55813
- var import_node_path39 = __toESM(require("path"), 1);
55966
+ var import_node_fs36 = __toESM(require("fs"), 1);
55967
+ var import_node_path40 = __toESM(require("path"), 1);
55814
55968
  function defaultStateFilePath(dataDir) {
55815
- return import_node_path39.default.join(dataDir, "state.json");
55969
+ return import_node_path40.default.join(dataDir, "state.json");
55816
55970
  }
55817
55971
  function isPidAlive(pid) {
55818
55972
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -55834,7 +55988,7 @@ var StateFileManager = class {
55834
55988
  }
55835
55989
  read() {
55836
55990
  try {
55837
- const raw = import_node_fs35.default.readFileSync(this.file, "utf8");
55991
+ const raw = import_node_fs36.default.readFileSync(this.file, "utf8");
55838
55992
  const parsed = JSON.parse(raw);
55839
55993
  return parsed;
55840
55994
  } catch {
@@ -55848,20 +56002,20 @@ var StateFileManager = class {
55848
56002
  return { status: "stale", existing };
55849
56003
  }
55850
56004
  write(state) {
55851
- import_node_fs35.default.mkdirSync(import_node_path39.default.dirname(this.file), { recursive: true });
56005
+ import_node_fs36.default.mkdirSync(import_node_path40.default.dirname(this.file), { recursive: true });
55852
56006
  const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
55853
- import_node_fs35.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
55854
- import_node_fs35.default.renameSync(tmp, this.file);
56007
+ import_node_fs36.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
56008
+ import_node_fs36.default.renameSync(tmp, this.file);
55855
56009
  if (process.platform !== "win32") {
55856
56010
  try {
55857
- import_node_fs35.default.chmodSync(this.file, 384);
56011
+ import_node_fs36.default.chmodSync(this.file, 384);
55858
56012
  } catch {
55859
56013
  }
55860
56014
  }
55861
56015
  }
55862
56016
  delete() {
55863
56017
  try {
55864
- import_node_fs35.default.unlinkSync(this.file);
56018
+ import_node_fs36.default.unlinkSync(this.file);
55865
56019
  } catch {
55866
56020
  }
55867
56021
  }
@@ -55875,14 +56029,14 @@ function readSpawnedByDesktopFromEnv(env = process.env) {
55875
56029
  }
55876
56030
 
55877
56031
  // src/tunnel/tunnel-manager.ts
55878
- var import_node_fs39 = __toESM(require("fs"), 1);
55879
- var import_node_path43 = __toESM(require("path"), 1);
56032
+ var import_node_fs40 = __toESM(require("fs"), 1);
56033
+ var import_node_path44 = __toESM(require("path"), 1);
55880
56034
  var import_node_crypto10 = __toESM(require("crypto"), 1);
55881
56035
  var import_node_child_process9 = require("child_process");
55882
56036
 
55883
56037
  // src/tunnel/tunnel-store.ts
55884
- var import_node_fs36 = __toESM(require("fs"), 1);
55885
- var import_node_path40 = __toESM(require("path"), 1);
56038
+ var import_node_fs37 = __toESM(require("fs"), 1);
56039
+ var import_node_path41 = __toESM(require("path"), 1);
55886
56040
  var TunnelStore = class {
55887
56041
  constructor(filePath) {
55888
56042
  this.filePath = filePath;
@@ -55890,7 +56044,7 @@ var TunnelStore = class {
55890
56044
  filePath;
55891
56045
  async get() {
55892
56046
  try {
55893
- const raw = await import_node_fs36.default.promises.readFile(this.filePath, "utf8");
56047
+ const raw = await import_node_fs37.default.promises.readFile(this.filePath, "utf8");
55894
56048
  const obj = JSON.parse(raw);
55895
56049
  if (!isPersistedTunnel(obj)) return null;
55896
56050
  return obj;
@@ -55901,22 +56055,22 @@ var TunnelStore = class {
55901
56055
  }
55902
56056
  }
55903
56057
  async set(v2) {
55904
- const dir = import_node_path40.default.dirname(this.filePath);
55905
- await import_node_fs36.default.promises.mkdir(dir, { recursive: true });
56058
+ const dir = import_node_path41.default.dirname(this.filePath);
56059
+ await import_node_fs37.default.promises.mkdir(dir, { recursive: true });
55906
56060
  const data = JSON.stringify(v2, null, 2);
55907
56061
  const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
55908
- await import_node_fs36.default.promises.writeFile(tmp, data, { mode: 384 });
56062
+ await import_node_fs37.default.promises.writeFile(tmp, data, { mode: 384 });
55909
56063
  if (process.platform !== "win32") {
55910
56064
  try {
55911
- await import_node_fs36.default.promises.chmod(tmp, 384);
56065
+ await import_node_fs37.default.promises.chmod(tmp, 384);
55912
56066
  } catch {
55913
56067
  }
55914
56068
  }
55915
- await import_node_fs36.default.promises.rename(tmp, this.filePath);
56069
+ await import_node_fs37.default.promises.rename(tmp, this.filePath);
55916
56070
  }
55917
56071
  async clear() {
55918
56072
  try {
55919
- await import_node_fs36.default.promises.unlink(this.filePath);
56073
+ await import_node_fs37.default.promises.unlink(this.filePath);
55920
56074
  } catch (err) {
55921
56075
  const code = err?.code;
55922
56076
  if (code !== "ENOENT") throw err;
@@ -56011,9 +56165,9 @@ function escape(v2) {
56011
56165
  }
56012
56166
 
56013
56167
  // src/tunnel/frpc-binary.ts
56014
- var import_node_fs37 = __toESM(require("fs"), 1);
56168
+ var import_node_fs38 = __toESM(require("fs"), 1);
56015
56169
  var import_node_os13 = __toESM(require("os"), 1);
56016
- var import_node_path41 = __toESM(require("path"), 1);
56170
+ var import_node_path42 = __toESM(require("path"), 1);
56017
56171
  var import_node_child_process7 = require("child_process");
56018
56172
  var import_node_stream3 = require("stream");
56019
56173
  var import_promises5 = require("stream/promises");
@@ -56045,20 +56199,20 @@ function frpcDownloadUrl(version2, p2) {
56045
56199
  }
56046
56200
  async function ensureFrpcBinary(opts) {
56047
56201
  if (opts.override) {
56048
- if (!import_node_fs37.default.existsSync(opts.override)) {
56202
+ if (!import_node_fs38.default.existsSync(opts.override)) {
56049
56203
  throw new Error(`frpc binary not found at override path: ${opts.override}`);
56050
56204
  }
56051
56205
  return opts.override;
56052
56206
  }
56053
56207
  const version2 = opts.version ?? FRPC_VERSION;
56054
56208
  const platform = opts.platform ?? detectPlatform();
56055
- const binDir = import_node_path41.default.join(opts.dataDir, "bin");
56056
- import_node_fs37.default.mkdirSync(binDir, { recursive: true });
56209
+ const binDir = import_node_path42.default.join(opts.dataDir, "bin");
56210
+ import_node_fs38.default.mkdirSync(binDir, { recursive: true });
56057
56211
  cleanupStaleArtifacts(binDir);
56058
- const stableBin = import_node_path41.default.join(binDir, "frpc");
56059
- if (import_node_fs37.default.existsSync(stableBin)) return stableBin;
56212
+ const stableBin = import_node_path42.default.join(binDir, "frpc");
56213
+ if (import_node_fs38.default.existsSync(stableBin)) return stableBin;
56060
56214
  const partialBin = `${stableBin}.partial`;
56061
- const tarballPath = import_node_path41.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
56215
+ const tarballPath = import_node_path42.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
56062
56216
  try {
56063
56217
  const url = frpcDownloadUrl(version2, platform);
56064
56218
  await downloadToFile(url, tarballPath, opts.fetchImpl);
@@ -56067,8 +56221,8 @@ async function ensureFrpcBinary(opts) {
56067
56221
  } else {
56068
56222
  await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
56069
56223
  }
56070
- import_node_fs37.default.chmodSync(partialBin, 493);
56071
- import_node_fs37.default.renameSync(partialBin, stableBin);
56224
+ import_node_fs38.default.chmodSync(partialBin, 493);
56225
+ import_node_fs38.default.renameSync(partialBin, stableBin);
56072
56226
  } finally {
56073
56227
  safeUnlink(tarballPath);
56074
56228
  safeUnlink(partialBin);
@@ -56078,15 +56232,15 @@ async function ensureFrpcBinary(opts) {
56078
56232
  function cleanupStaleArtifacts(binDir) {
56079
56233
  let entries;
56080
56234
  try {
56081
- entries = import_node_fs37.default.readdirSync(binDir);
56235
+ entries = import_node_fs38.default.readdirSync(binDir);
56082
56236
  } catch {
56083
56237
  return;
56084
56238
  }
56085
56239
  for (const name of entries) {
56086
56240
  if (name.endsWith(".partial") || name.startsWith("extract-")) {
56087
- const full = import_node_path41.default.join(binDir, name);
56241
+ const full = import_node_path42.default.join(binDir, name);
56088
56242
  try {
56089
- import_node_fs37.default.rmSync(full, { recursive: true, force: true });
56243
+ import_node_fs38.default.rmSync(full, { recursive: true, force: true });
56090
56244
  } catch {
56091
56245
  }
56092
56246
  }
@@ -56094,7 +56248,7 @@ function cleanupStaleArtifacts(binDir) {
56094
56248
  }
56095
56249
  function safeUnlink(p2) {
56096
56250
  try {
56097
- import_node_fs37.default.unlinkSync(p2);
56251
+ import_node_fs38.default.unlinkSync(p2);
56098
56252
  } catch {
56099
56253
  }
56100
56254
  }
@@ -56105,13 +56259,13 @@ async function downloadToFile(url, dest, fetchImpl) {
56105
56259
  if (!res.ok || !res.body) {
56106
56260
  throw new Error(`download failed: ${res.status} ${res.statusText}`);
56107
56261
  }
56108
- const out = import_node_fs37.default.createWriteStream(dest);
56262
+ const out = import_node_fs38.default.createWriteStream(dest);
56109
56263
  const nodeStream = import_node_stream3.Readable.fromWeb(res.body);
56110
56264
  await (0, import_promises5.pipeline)(nodeStream, out);
56111
56265
  }
56112
56266
  async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
56113
- const work = import_node_path41.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
56114
- import_node_fs37.default.mkdirSync(work, { recursive: true });
56267
+ const work = import_node_path42.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
56268
+ import_node_fs38.default.mkdirSync(work, { recursive: true });
56115
56269
  try {
56116
56270
  await new Promise((resolve6, reject) => {
56117
56271
  const proc = (0, import_node_child_process7.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
@@ -56119,32 +56273,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
56119
56273
  proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
56120
56274
  });
56121
56275
  const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
56122
- const src = import_node_path41.default.join(work, dirName, "frpc");
56123
- if (!import_node_fs37.default.existsSync(src)) {
56276
+ const src = import_node_path42.default.join(work, dirName, "frpc");
56277
+ if (!import_node_fs38.default.existsSync(src)) {
56124
56278
  throw new Error(`frpc not found inside tarball at ${src}`);
56125
56279
  }
56126
- import_node_fs37.default.copyFileSync(src, destBin);
56280
+ import_node_fs38.default.copyFileSync(src, destBin);
56127
56281
  } finally {
56128
- import_node_fs37.default.rmSync(work, { recursive: true, force: true });
56282
+ import_node_fs38.default.rmSync(work, { recursive: true, force: true });
56129
56283
  }
56130
56284
  }
56131
56285
 
56132
56286
  // src/tunnel/frpc-process.ts
56133
- var import_node_fs38 = __toESM(require("fs"), 1);
56134
- var import_node_path42 = __toESM(require("path"), 1);
56287
+ var import_node_fs39 = __toESM(require("fs"), 1);
56288
+ var import_node_path43 = __toESM(require("path"), 1);
56135
56289
  var import_node_child_process8 = require("child_process");
56136
56290
  function frpcPidFilePath(dataDir) {
56137
- return import_node_path42.default.join(dataDir, "frpc.pid");
56291
+ return import_node_path43.default.join(dataDir, "frpc.pid");
56138
56292
  }
56139
56293
  function writeFrpcPid(dataDir, pid) {
56140
56294
  try {
56141
- import_node_fs38.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
56295
+ import_node_fs39.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
56142
56296
  } catch {
56143
56297
  }
56144
56298
  }
56145
56299
  function clearFrpcPid(dataDir) {
56146
56300
  try {
56147
- import_node_fs38.default.unlinkSync(frpcPidFilePath(dataDir));
56301
+ import_node_fs39.default.unlinkSync(frpcPidFilePath(dataDir));
56148
56302
  } catch {
56149
56303
  }
56150
56304
  }
@@ -56160,7 +56314,7 @@ function defaultIsPidAlive(pid) {
56160
56314
  }
56161
56315
  function defaultReadPidFile(file) {
56162
56316
  try {
56163
- return import_node_fs38.default.readFileSync(file, "utf8");
56317
+ return import_node_fs39.default.readFileSync(file, "utf8");
56164
56318
  } catch {
56165
56319
  return null;
56166
56320
  }
@@ -56176,7 +56330,7 @@ function defaultSleep2(ms) {
56176
56330
  }
56177
56331
  async function killStaleFrpc(deps) {
56178
56332
  const pidFile = frpcPidFilePath(deps.dataDir);
56179
- const tomlPath = import_node_path42.default.join(deps.dataDir, "frpc.toml");
56333
+ const tomlPath = import_node_path43.default.join(deps.dataDir, "frpc.toml");
56180
56334
  const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
56181
56335
  const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
56182
56336
  const killPid = deps.killPidImpl ?? defaultKillPid;
@@ -56200,7 +56354,7 @@ async function killStaleFrpc(deps) {
56200
56354
  }
56201
56355
  if (victims.size === 0) {
56202
56356
  try {
56203
- import_node_fs38.default.unlinkSync(pidFile);
56357
+ import_node_fs39.default.unlinkSync(pidFile);
56204
56358
  } catch {
56205
56359
  }
56206
56360
  return;
@@ -56211,7 +56365,7 @@ async function killStaleFrpc(deps) {
56211
56365
  }
56212
56366
  await sleep2(deps.reapWaitMs ?? 300);
56213
56367
  try {
56214
- import_node_fs38.default.unlinkSync(pidFile);
56368
+ import_node_fs39.default.unlinkSync(pidFile);
56215
56369
  } catch {
56216
56370
  }
56217
56371
  }
@@ -56248,7 +56402,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
56248
56402
  var TunnelManager = class {
56249
56403
  constructor(deps) {
56250
56404
  this.deps = deps;
56251
- this.store = deps.store ?? new TunnelStore(import_node_path43.default.join(deps.dataDir, "tunnel.json"));
56405
+ this.store = deps.store ?? new TunnelStore(import_node_path44.default.join(deps.dataDir, "tunnel.json"));
56252
56406
  this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
56253
56407
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
56254
56408
  }
@@ -56375,7 +56529,7 @@ var TunnelManager = class {
56375
56529
  dataDir: this.deps.dataDir,
56376
56530
  override: this.deps.frpcBinaryOverride ?? void 0
56377
56531
  });
56378
- const tomlPath = import_node_path43.default.join(this.deps.dataDir, "frpc.toml");
56532
+ const tomlPath = import_node_path44.default.join(this.deps.dataDir, "frpc.toml");
56379
56533
  const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto10.default.randomBytes(3).toString("hex")}`;
56380
56534
  const toml = buildFrpcToml({
56381
56535
  serverAddr: t.frpsHost,
@@ -56386,14 +56540,14 @@ var TunnelManager = class {
56386
56540
  localPort,
56387
56541
  logLevel: "info"
56388
56542
  });
56389
- await import_node_fs39.default.promises.writeFile(tomlPath, toml, { mode: 384 });
56543
+ await import_node_fs40.default.promises.writeFile(tomlPath, toml, { mode: 384 });
56390
56544
  const proc = (this.deps.spawnImpl ?? import_node_child_process9.spawn)(frpcBin, ["-c", tomlPath], {
56391
56545
  stdio: ["ignore", "pipe", "pipe"]
56392
56546
  });
56393
- const logDir = import_node_path43.default.join(this.deps.dataDir, "log");
56394
- import_node_fs39.default.mkdirSync(logDir, { recursive: true });
56395
- const logFilePath = import_node_path43.default.join(logDir, "frpc.log");
56396
- const logStream = import_node_fs39.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
56547
+ const logDir = import_node_path44.default.join(this.deps.dataDir, "log");
56548
+ import_node_fs40.default.mkdirSync(logDir, { recursive: true });
56549
+ const logFilePath = import_node_path44.default.join(logDir, "frpc.log");
56550
+ const logStream = import_node_fs40.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
56397
56551
  logStream.on("error", () => {
56398
56552
  });
56399
56553
  const tee = (chunk) => {
@@ -56476,27 +56630,27 @@ async function waitForFrpcReady(proc, timeoutMs) {
56476
56630
 
56477
56631
  // src/tunnel/device-key.ts
56478
56632
  var import_node_os14 = __toESM(require("os"), 1);
56479
- var import_node_path44 = __toESM(require("path"), 1);
56633
+ var import_node_path45 = __toESM(require("path"), 1);
56480
56634
  var import_node_crypto11 = __toESM(require("crypto"), 1);
56481
56635
  var DERIVE_SALT = "clawd-tunnel-device-v1";
56482
56636
  function deriveStableDeviceKey(opts = {}) {
56483
56637
  const hostname = opts.hostname ?? import_node_os14.default.hostname();
56484
56638
  const uid = opts.uid ?? (typeof import_node_os14.default.userInfo === "function" ? import_node_os14.default.userInfo().uid : 0);
56485
56639
  const home = opts.home ?? import_node_os14.default.homedir();
56486
- const defaultDataDir = import_node_path44.default.resolve(import_node_path44.default.join(home, ".clawd"));
56487
- const normalizedDataDir = opts.dataDir ? import_node_path44.default.resolve(opts.dataDir) : null;
56640
+ const defaultDataDir = import_node_path45.default.resolve(import_node_path45.default.join(home, ".clawd"));
56641
+ const normalizedDataDir = opts.dataDir ? import_node_path45.default.resolve(opts.dataDir) : null;
56488
56642
  const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
56489
56643
  const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
56490
56644
  return import_node_crypto11.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
56491
56645
  }
56492
56646
 
56493
56647
  // src/auth-store.ts
56494
- var import_node_fs40 = __toESM(require("fs"), 1);
56495
- var import_node_path45 = __toESM(require("path"), 1);
56648
+ var import_node_fs41 = __toESM(require("fs"), 1);
56649
+ var import_node_path46 = __toESM(require("path"), 1);
56496
56650
  var import_node_crypto12 = __toESM(require("crypto"), 1);
56497
56651
  var AUTH_FILE_NAME = "auth.json";
56498
56652
  function authFilePath(dataDir) {
56499
- return import_node_path45.default.join(dataDir, AUTH_FILE_NAME);
56653
+ return import_node_path46.default.join(dataDir, AUTH_FILE_NAME);
56500
56654
  }
56501
56655
  function loadOrCreateAuthFile(opts) {
56502
56656
  const file = authFilePath(opts.dataDir);
@@ -56528,7 +56682,7 @@ function defaultGenerateOwnerPrincipalId() {
56528
56682
  }
56529
56683
  function readAuthFile(file) {
56530
56684
  try {
56531
- const raw = import_node_fs40.default.readFileSync(file, "utf8");
56685
+ const raw = import_node_fs41.default.readFileSync(file, "utf8");
56532
56686
  const parsed = JSON.parse(raw);
56533
56687
  if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
56534
56688
  return null;
@@ -56548,25 +56702,25 @@ function readAuthFile(file) {
56548
56702
  }
56549
56703
  }
56550
56704
  function writeAuthFile(file, content) {
56551
- import_node_fs40.default.mkdirSync(import_node_path45.default.dirname(file), { recursive: true });
56552
- import_node_fs40.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
56705
+ import_node_fs41.default.mkdirSync(import_node_path46.default.dirname(file), { recursive: true });
56706
+ import_node_fs41.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
56553
56707
  try {
56554
- import_node_fs40.default.chmodSync(file, 384);
56708
+ import_node_fs41.default.chmodSync(file, 384);
56555
56709
  } catch {
56556
56710
  }
56557
56711
  }
56558
56712
 
56559
56713
  // src/owner-profile.ts
56560
- var import_node_fs41 = __toESM(require("fs"), 1);
56714
+ var import_node_fs42 = __toESM(require("fs"), 1);
56561
56715
  var import_node_os15 = __toESM(require("os"), 1);
56562
- var import_node_path46 = __toESM(require("path"), 1);
56716
+ var import_node_path47 = __toESM(require("path"), 1);
56563
56717
  var PROFILE_FILENAME = "profile.json";
56564
56718
  function loadOwnerDisplayName(dataDir) {
56565
56719
  const fallback = import_node_os15.default.userInfo().username;
56566
- const profilePath = import_node_path46.default.join(dataDir, PROFILE_FILENAME);
56720
+ const profilePath = import_node_path47.default.join(dataDir, PROFILE_FILENAME);
56567
56721
  let raw;
56568
56722
  try {
56569
- raw = import_node_fs41.default.readFileSync(profilePath, "utf8");
56723
+ raw = import_node_fs42.default.readFileSync(profilePath, "utf8");
56570
56724
  } catch {
56571
56725
  return fallback;
56572
56726
  }
@@ -56589,18 +56743,18 @@ function loadOwnerDisplayName(dataDir) {
56589
56743
  }
56590
56744
 
56591
56745
  // src/feishu-auth/owner-identity-store.ts
56592
- var import_node_fs42 = __toESM(require("fs"), 1);
56593
- var import_node_path47 = __toESM(require("path"), 1);
56746
+ var import_node_fs43 = __toESM(require("fs"), 1);
56747
+ var import_node_path48 = __toESM(require("path"), 1);
56594
56748
  var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
56595
56749
  var OwnerIdentityStore = class {
56596
56750
  file;
56597
56751
  constructor(dataDir) {
56598
- this.file = import_node_path47.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
56752
+ this.file = import_node_path48.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
56599
56753
  }
56600
56754
  read() {
56601
56755
  let raw;
56602
56756
  try {
56603
- raw = import_node_fs42.default.readFileSync(this.file, "utf8");
56757
+ raw = import_node_fs43.default.readFileSync(this.file, "utf8");
56604
56758
  } catch {
56605
56759
  return null;
56606
56760
  }
@@ -56628,16 +56782,16 @@ var OwnerIdentityStore = class {
56628
56782
  };
56629
56783
  }
56630
56784
  write(record) {
56631
- import_node_fs42.default.mkdirSync(import_node_path47.default.dirname(this.file), { recursive: true });
56632
- import_node_fs42.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
56785
+ import_node_fs43.default.mkdirSync(import_node_path48.default.dirname(this.file), { recursive: true });
56786
+ import_node_fs43.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
56633
56787
  try {
56634
- import_node_fs42.default.chmodSync(this.file, 384);
56788
+ import_node_fs43.default.chmodSync(this.file, 384);
56635
56789
  } catch {
56636
56790
  }
56637
56791
  }
56638
56792
  clear() {
56639
56793
  try {
56640
- import_node_fs42.default.unlinkSync(this.file);
56794
+ import_node_fs43.default.unlinkSync(this.file);
56641
56795
  } catch (err) {
56642
56796
  const code = err?.code;
56643
56797
  if (code !== "ENOENT") throw err;
@@ -56774,9 +56928,9 @@ var CentralClientError = class extends Error {
56774
56928
  code;
56775
56929
  cause;
56776
56930
  };
56777
- async function centralRequest(opts, path78, init) {
56931
+ async function centralRequest(opts, path79, init) {
56778
56932
  const f = opts.fetchImpl ?? globalThis.fetch;
56779
- const url = `${opts.api.replace(/\/+$/, "")}${path78}`;
56933
+ const url = `${opts.api.replace(/\/+$/, "")}${path79}`;
56780
56934
  const ctrl = new AbortController();
56781
56935
  const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
56782
56936
  let res;
@@ -56970,8 +57124,8 @@ function verifyConnectToken(args) {
56970
57124
  }
56971
57125
 
56972
57126
  // src/feishu-auth/server-key.ts
56973
- var fs52 = __toESM(require("fs"), 1);
56974
- var path56 = __toESM(require("path"), 1);
57127
+ var fs53 = __toESM(require("fs"), 1);
57128
+ var path57 = __toESM(require("path"), 1);
56975
57129
  var FILE_NAME2 = "server-signing-key.json";
56976
57130
  var ServerKeyStore = class {
56977
57131
  constructor(dataDir) {
@@ -56979,12 +57133,12 @@ var ServerKeyStore = class {
56979
57133
  }
56980
57134
  dataDir;
56981
57135
  filePath() {
56982
- return path56.join(this.dataDir, FILE_NAME2);
57136
+ return path57.join(this.dataDir, FILE_NAME2);
56983
57137
  }
56984
57138
  /** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
56985
57139
  read() {
56986
57140
  try {
56987
- const raw = fs52.readFileSync(this.filePath(), "utf8");
57141
+ const raw = fs53.readFileSync(this.filePath(), "utf8");
56988
57142
  const parsed = JSON.parse(raw);
56989
57143
  if (typeof parsed.publicKeyPem === "string" && parsed.publicKeyPem.includes("PUBLIC KEY")) {
56990
57144
  return parsed.publicKeyPem;
@@ -56999,12 +57153,12 @@ var ServerKeyStore = class {
56999
57153
  publicKeyPem,
57000
57154
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
57001
57155
  };
57002
- fs52.mkdirSync(this.dataDir, { recursive: true });
57003
- fs52.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
57156
+ fs53.mkdirSync(this.dataDir, { recursive: true });
57157
+ fs53.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
57004
57158
  }
57005
57159
  clear() {
57006
57160
  try {
57007
- fs52.unlinkSync(this.filePath());
57161
+ fs53.unlinkSync(this.filePath());
57008
57162
  } catch {
57009
57163
  }
57010
57164
  }
@@ -57017,12 +57171,12 @@ init_protocol();
57017
57171
  init_protocol();
57018
57172
 
57019
57173
  // src/session/fork.ts
57020
- var import_node_fs43 = __toESM(require("fs"), 1);
57174
+ var import_node_fs44 = __toESM(require("fs"), 1);
57021
57175
  var import_node_os16 = __toESM(require("os"), 1);
57022
- var import_node_path48 = __toESM(require("path"), 1);
57176
+ var import_node_path49 = __toESM(require("path"), 1);
57023
57177
  init_claude_history();
57024
57178
  function readJsonlEntries(file) {
57025
- const raw = import_node_fs43.default.readFileSync(file, "utf8");
57179
+ const raw = import_node_fs44.default.readFileSync(file, "utf8");
57026
57180
  const out = [];
57027
57181
  for (const line of raw.split("\n")) {
57028
57182
  const t = line.trim();
@@ -57035,19 +57189,19 @@ function readJsonlEntries(file) {
57035
57189
  return out;
57036
57190
  }
57037
57191
  function forkSession(input) {
57038
- const baseDir = input.baseDir ?? import_node_path48.default.join(import_node_os16.default.homedir(), ".claude");
57039
- let projectDir = import_node_path48.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
57040
- let sourceFile = import_node_path48.default.join(projectDir, `${input.toolSessionId}.jsonl`);
57041
- if (!import_node_fs43.default.existsSync(sourceFile)) {
57192
+ const baseDir = input.baseDir ?? import_node_path49.default.join(import_node_os16.default.homedir(), ".claude");
57193
+ let projectDir = import_node_path49.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
57194
+ let sourceFile = import_node_path49.default.join(projectDir, `${input.toolSessionId}.jsonl`);
57195
+ if (!import_node_fs44.default.existsSync(sourceFile)) {
57042
57196
  const found = findTranscriptByToolSessionId(
57043
- import_node_path48.default.join(baseDir, "projects"),
57197
+ import_node_path49.default.join(baseDir, "projects"),
57044
57198
  input.toolSessionId
57045
57199
  );
57046
57200
  if (!found) {
57047
57201
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
57048
57202
  }
57049
57203
  sourceFile = found;
57050
- projectDir = import_node_path48.default.dirname(found);
57204
+ projectDir = import_node_path49.default.dirname(found);
57051
57205
  }
57052
57206
  const entries = readJsonlEntries(sourceFile);
57053
57207
  const mainEntries = entries.filter((e) => e.isSidechain !== true);
@@ -57076,10 +57230,10 @@ function forkSession(input) {
57076
57230
  }
57077
57231
  forkedLines.push(JSON.stringify(forked));
57078
57232
  }
57079
- const outDir = input.targetCwd ? import_node_path48.default.join(baseDir, "projects", cwdToHashDir(input.targetCwd)) : projectDir;
57080
- const forkedFilePath = import_node_path48.default.join(outDir, `${forkedToolSessionId}.jsonl`);
57081
- import_node_fs43.default.mkdirSync(outDir, { recursive: true });
57082
- import_node_fs43.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
57233
+ const outDir = input.targetCwd ? import_node_path49.default.join(baseDir, "projects", cwdToHashDir(input.targetCwd)) : projectDir;
57234
+ const forkedFilePath = import_node_path49.default.join(outDir, `${forkedToolSessionId}.jsonl`);
57235
+ import_node_fs44.default.mkdirSync(outDir, { recursive: true });
57236
+ import_node_fs44.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
57083
57237
  return { forkedToolSessionId, forkedFilePath };
57084
57238
  }
57085
57239
 
@@ -57538,7 +57692,7 @@ function buildPermissionHandlers(deps) {
57538
57692
  }
57539
57693
 
57540
57694
  // src/handlers/history.ts
57541
- var path59 = __toESM(require("path"), 1);
57695
+ var path60 = __toESM(require("path"), 1);
57542
57696
  init_protocol();
57543
57697
 
57544
57698
  // src/session/recent-dirs.ts
@@ -57556,7 +57710,7 @@ function listRecentDirs(store, limit = 50) {
57556
57710
  }
57557
57711
 
57558
57712
  // src/permission/persona-paths.ts
57559
- var path58 = __toESM(require("path"), 1);
57713
+ var path59 = __toESM(require("path"), 1);
57560
57714
  function getAllowedPersonaIds(grants, action) {
57561
57715
  const ids = /* @__PURE__ */ new Set();
57562
57716
  for (const g2 of grants) {
@@ -57569,42 +57723,42 @@ function getAllowedPersonaIds(grants, action) {
57569
57723
  return ids;
57570
57724
  }
57571
57725
  function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
57572
- const target = path58.resolve(absPath);
57726
+ const target = path59.resolve(absPath);
57573
57727
  if (userWorkDir) {
57574
- const u = path58.resolve(userWorkDir);
57575
- const usep = u.endsWith(path58.sep) ? "" : path58.sep;
57728
+ const u = path59.resolve(userWorkDir);
57729
+ const usep = u.endsWith(path59.sep) ? "" : path59.sep;
57576
57730
  if (target === u || target.startsWith(u + usep)) return true;
57577
57731
  }
57578
- const root = path58.resolve(personaRoot);
57579
- const sep3 = root.endsWith(path58.sep) ? "" : path58.sep;
57732
+ const root = path59.resolve(personaRoot);
57733
+ const sep3 = root.endsWith(path59.sep) ? "" : path59.sep;
57580
57734
  if (!target.startsWith(root + sep3)) return false;
57581
- const rel = path58.relative(root, target);
57735
+ const rel = path59.relative(root, target);
57582
57736
  if (!rel || rel.startsWith("..")) return false;
57583
- const personaId2 = rel.split(path58.sep)[0];
57737
+ const personaId2 = rel.split(path59.sep)[0];
57584
57738
  if (!personaId2) return false;
57585
57739
  const allowed = getAllowedPersonaIds(grants, action);
57586
57740
  if (allowed === "*") return true;
57587
57741
  return allowed.has(personaId2);
57588
57742
  }
57589
57743
  function personaIdFromPath(absPath, personaRoot) {
57590
- const root = path58.resolve(personaRoot);
57591
- const target = path58.resolve(absPath);
57592
- const sep3 = root.endsWith(path58.sep) ? "" : path58.sep;
57744
+ const root = path59.resolve(personaRoot);
57745
+ const target = path59.resolve(absPath);
57746
+ const sep3 = root.endsWith(path59.sep) ? "" : path59.sep;
57593
57747
  if (!target.startsWith(root + sep3)) return null;
57594
- const rel = path58.relative(root, target);
57748
+ const rel = path59.relative(root, target);
57595
57749
  if (!rel || rel.startsWith("..")) return null;
57596
- const id = rel.split(path58.sep)[0];
57750
+ const id = rel.split(path59.sep)[0];
57597
57751
  return id || null;
57598
57752
  }
57599
57753
  function isPathWithin(dir, absPath) {
57600
- const d = path58.resolve(dir);
57601
- const t = path58.resolve(absPath);
57602
- const sep3 = d.endsWith(path58.sep) ? "" : path58.sep;
57754
+ const d = path59.resolve(dir);
57755
+ const t = path59.resolve(absPath);
57756
+ const sep3 = d.endsWith(path59.sep) ? "" : path59.sep;
57603
57757
  return t === d || t.startsWith(d + sep3);
57604
57758
  }
57605
57759
  function isPathInGuestBoundary(personaRoot, personaId2, userWorkDir, absPath) {
57606
57760
  if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
57607
- return personaIdFromPath(path58.resolve(absPath), personaRoot) === personaId2;
57761
+ return personaIdFromPath(path59.resolve(absPath), personaRoot) === personaId2;
57608
57762
  }
57609
57763
 
57610
57764
  // src/handlers/history.ts
@@ -57656,7 +57810,7 @@ function buildHistoryHandlers(deps) {
57656
57810
  if (!pid) return false;
57657
57811
  return isGuestPathAllowed(
57658
57812
  ctx.grants,
57659
- path59.join(personaRoot, pid),
57813
+ path60.join(personaRoot, pid),
57660
57814
  personaRoot,
57661
57815
  "read",
57662
57816
  userWorkDir
@@ -57668,7 +57822,7 @@ function buildHistoryHandlers(deps) {
57668
57822
  };
57669
57823
  const list = async (frame, _client, ctx) => {
57670
57824
  const args = HistoryListArgs.parse(frame);
57671
- assertGuestPath(ctx, path59.resolve(args.projectPath), personaRoot, "history:list", usersRoot);
57825
+ assertGuestPath(ctx, path60.resolve(args.projectPath), personaRoot, "history:list", usersRoot);
57672
57826
  const sessions = await history.listSessions(args);
57673
57827
  return { response: { type: "history:list", sessions } };
57674
57828
  };
@@ -57712,14 +57866,14 @@ function buildHistoryHandlers(deps) {
57712
57866
  };
57713
57867
  const subagents = async (frame, _client, ctx) => {
57714
57868
  const args = HistorySubagentsArgs.parse(frame);
57715
- assertGuestPath(ctx, path59.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
57869
+ assertGuestPath(ctx, path60.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
57716
57870
  assertGuestOwnsToolSession(ctx, args.toolSessionId, "history:subagents");
57717
57871
  const subs = await history.listSubagents(args);
57718
57872
  return { response: { type: "history:subagents", subagents: subs } };
57719
57873
  };
57720
57874
  const subagentRead = async (frame, _client, ctx) => {
57721
57875
  const args = HistorySubagentReadArgs.parse(frame);
57722
- assertGuestPath(ctx, path59.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
57876
+ assertGuestPath(ctx, path60.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
57723
57877
  assertGuestOwnsToolSession(ctx, args.toolSessionId, "history:subagent-read");
57724
57878
  const res = await history.readSubagent(args);
57725
57879
  return { response: { type: "history:subagent-read", ...res } };
@@ -57729,7 +57883,7 @@ function buildHistoryHandlers(deps) {
57729
57883
  if (ctx?.principal.kind === "guest" && personaRoot) {
57730
57884
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
57731
57885
  const filtered = dirs.filter(
57732
- (d) => isGuestPathAllowed(ctx.grants, path59.resolve(d.cwd), personaRoot, "read", userWorkDir)
57886
+ (d) => isGuestPathAllowed(ctx.grants, path60.resolve(d.cwd), personaRoot, "read", userWorkDir)
57733
57887
  );
57734
57888
  return { response: { type: "history:recentDirs", dirs: filtered } };
57735
57889
  }
@@ -57746,7 +57900,7 @@ function buildHistoryHandlers(deps) {
57746
57900
  }
57747
57901
 
57748
57902
  // src/handlers/workspace.ts
57749
- var path60 = __toESM(require("path"), 1);
57903
+ var path61 = __toESM(require("path"), 1);
57750
57904
  var os16 = __toESM(require("os"), 1);
57751
57905
  init_protocol();
57752
57906
  init_protocol();
@@ -57788,22 +57942,22 @@ function buildWorkspaceHandlers(deps) {
57788
57942
  const args = WorkspaceListArgs.parse(frame);
57789
57943
  const isGuest = ctx?.principal.kind === "guest";
57790
57944
  const fallbackCwd = isGuest && personaRoot ? personaRoot : os16.homedir();
57791
- const resolvedCwd = path60.resolve(args.cwd ?? fallbackCwd);
57792
- const target = args.path ? path60.resolve(resolvedCwd, args.path) : resolvedCwd;
57945
+ const resolvedCwd = path61.resolve(args.cwd ?? fallbackCwd);
57946
+ const target = args.path ? path61.resolve(resolvedCwd, args.path) : resolvedCwd;
57793
57947
  assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
57794
57948
  const res = workspace.list({ ...args, cwd: resolvedCwd });
57795
57949
  return { response: { type: "workspace:list", ...res } };
57796
57950
  };
57797
57951
  const read = async (frame, _client, ctx) => {
57798
57952
  const args = WorkspaceReadArgs.parse(frame);
57799
- const target = path60.isAbsolute(args.path) ? path60.resolve(args.path) : path60.resolve(args.cwd, args.path);
57953
+ const target = path61.isAbsolute(args.path) ? path61.resolve(args.path) : path61.resolve(args.cwd, args.path);
57800
57954
  assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
57801
57955
  const res = workspace.read(args);
57802
57956
  return { response: { type: "workspace:read", ...res } };
57803
57957
  };
57804
57958
  const skillsList = async (frame, _client, ctx) => {
57805
57959
  const args = SkillsListArgs.parse(frame);
57806
- const cwdAbs = path60.resolve(args.cwd);
57960
+ const cwdAbs = path61.resolve(args.cwd);
57807
57961
  assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
57808
57962
  const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
57809
57963
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -57815,7 +57969,7 @@ function buildWorkspaceHandlers(deps) {
57815
57969
  };
57816
57970
  const agentsList = async (frame, _client, ctx) => {
57817
57971
  const args = AgentsListArgs.parse(frame);
57818
- const cwdAbs = path60.resolve(args.cwd);
57972
+ const cwdAbs = path61.resolve(args.cwd);
57819
57973
  assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
57820
57974
  if (args.tool === "codex") {
57821
57975
  return { response: { type: "agents:list", agents: [] } };
@@ -57837,20 +57991,20 @@ function buildWorkspaceHandlers(deps) {
57837
57991
  }
57838
57992
 
57839
57993
  // src/handlers/git.ts
57840
- var path62 = __toESM(require("path"), 1);
57994
+ var path63 = __toESM(require("path"), 1);
57841
57995
  init_protocol();
57842
57996
  init_protocol();
57843
57997
 
57844
57998
  // src/workspace/git.ts
57845
57999
  var import_node_child_process10 = require("child_process");
57846
- var import_node_fs44 = __toESM(require("fs"), 1);
57847
- var import_node_path49 = __toESM(require("path"), 1);
58000
+ var import_node_fs45 = __toESM(require("fs"), 1);
58001
+ var import_node_path50 = __toESM(require("path"), 1);
57848
58002
  var import_node_util = require("util");
57849
58003
  var pexec = (0, import_node_util.promisify)(import_node_child_process10.execFile);
57850
58004
  function normalizePath(p2) {
57851
- const resolved = import_node_path49.default.resolve(p2);
58005
+ const resolved = import_node_path50.default.resolve(p2);
57852
58006
  try {
57853
- return import_node_fs44.default.realpathSync(resolved);
58007
+ return import_node_fs45.default.realpathSync(resolved);
57854
58008
  } catch {
57855
58009
  return resolved;
57856
58010
  }
@@ -57924,7 +58078,7 @@ async function listGitBranches(cwd) {
57924
58078
  function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
57925
58079
  if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
57926
58080
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
57927
- if (!isGuestPathAllowed(ctx.grants, path62.resolve(cwd), personaRoot, "read", userWorkDir)) {
58081
+ if (!isGuestPathAllowed(ctx.grants, path63.resolve(cwd), personaRoot, "read", userWorkDir)) {
57928
58082
  throw new ClawdError(
57929
58083
  ERROR_CODES.UNAUTHORIZED,
57930
58084
  `guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
@@ -58789,21 +58943,21 @@ function computeMethodAccess(args) {
58789
58943
  }
58790
58944
 
58791
58945
  // src/version.ts
58792
- var version = "0.2.292".length > 0 ? "0.2.292" : "dev";
58946
+ var version = "0.2.294".length > 0 ? "0.2.294" : "dev";
58793
58947
 
58794
58948
  // src/cli-probe/probe.ts
58795
- var fs55 = __toESM(require("fs"), 1);
58796
- var path63 = __toESM(require("path"), 1);
58949
+ var fs56 = __toESM(require("fs"), 1);
58950
+ var path64 = __toESM(require("path"), 1);
58797
58951
  var PKG_NAME = "@clawos-dev/clawd";
58798
58952
  var BIN_NAME = "clawd";
58799
58953
  var MAX_PARENT_HOPS = 5;
58800
58954
  function probeGlobalCli(pathEnv) {
58801
58955
  if (!pathEnv) return null;
58802
- for (const dir of pathEnv.split(path63.delimiter)) {
58956
+ for (const dir of pathEnv.split(path64.delimiter)) {
58803
58957
  if (!dir) continue;
58804
58958
  let real;
58805
58959
  try {
58806
- real = fs55.realpathSync(path63.join(dir, BIN_NAME));
58960
+ real = fs56.realpathSync(path64.join(dir, BIN_NAME));
58807
58961
  } catch {
58808
58962
  continue;
58809
58963
  }
@@ -58813,15 +58967,15 @@ function probeGlobalCli(pathEnv) {
58813
58967
  return null;
58814
58968
  }
58815
58969
  function readOwnPackageVersion(entryPath) {
58816
- let dir = path63.dirname(entryPath);
58970
+ let dir = path64.dirname(entryPath);
58817
58971
  for (let i = 0; i < MAX_PARENT_HOPS; i++) {
58818
58972
  try {
58819
- const raw = fs55.readFileSync(path63.join(dir, "package.json"), "utf8");
58973
+ const raw = fs56.readFileSync(path64.join(dir, "package.json"), "utf8");
58820
58974
  const pkg = JSON.parse(raw);
58821
58975
  if (pkg.name !== PKG_NAME) return null;
58822
58976
  return typeof pkg.version === "string" && pkg.version ? pkg.version : null;
58823
58977
  } catch {
58824
- const parent = path63.dirname(dir);
58978
+ const parent = path64.dirname(dir);
58825
58979
  if (parent === dir) return null;
58826
58980
  dir = parent;
58827
58981
  }
@@ -58979,7 +59133,7 @@ function buildPersonaHandlers(deps) {
58979
59133
  }
58980
59134
 
58981
59135
  // src/handlers/attachment.ts
58982
- var import_node_path50 = __toESM(require("path"), 1);
59136
+ var import_node_path51 = __toESM(require("path"), 1);
58983
59137
  init_protocol();
58984
59138
  init_protocol();
58985
59139
  var DEFAULT_TTL_SECONDS = 24 * 3600;
@@ -59059,12 +59213,12 @@ function buildAttachmentHandlers(deps) {
59059
59213
  `session ${args.sessionId} scope unresolved`
59060
59214
  );
59061
59215
  }
59062
- const cwdAbs = import_node_path50.default.resolve(sessionFile.cwd);
59063
- const candidateAbs = import_node_path50.default.isAbsolute(args.relPath) ? import_node_path50.default.resolve(args.relPath) : import_node_path50.default.resolve(cwdAbs, args.relPath);
59216
+ const cwdAbs = import_node_path51.default.resolve(sessionFile.cwd);
59217
+ const candidateAbs = import_node_path51.default.isAbsolute(args.relPath) ? import_node_path51.default.resolve(args.relPath) : import_node_path51.default.resolve(cwdAbs, args.relPath);
59064
59218
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
59065
59219
  const entries = deps.groupFileStore.list(scope, args.sessionId);
59066
59220
  const entry = entries.find((e) => {
59067
- const storedAbs = import_node_path50.default.isAbsolute(e.relPath) ? import_node_path50.default.resolve(e.relPath) : import_node_path50.default.resolve(cwdAbs, e.relPath);
59221
+ const storedAbs = import_node_path51.default.isAbsolute(e.relPath) ? import_node_path51.default.resolve(e.relPath) : import_node_path51.default.resolve(cwdAbs, e.relPath);
59068
59222
  return storedAbs === candidateAbs && !e.stale;
59069
59223
  });
59070
59224
  if (!entry) {
@@ -59089,7 +59243,7 @@ function buildAttachmentHandlers(deps) {
59089
59243
  if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
59090
59244
  const f = deps.sessionStore.read(sessionId);
59091
59245
  if (!f) return;
59092
- assertGuestAttachmentPath(ctx, import_node_path50.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
59246
+ assertGuestAttachmentPath(ctx, import_node_path51.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
59093
59247
  }
59094
59248
  const groupAdd = async (frame, _client, ctx) => {
59095
59249
  if (!deps.groupFileStore || !deps.getSessionScope) {
@@ -59104,8 +59258,8 @@ function buildAttachmentHandlers(deps) {
59104
59258
  if (!scope) {
59105
59259
  throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
59106
59260
  }
59107
- const cwdAbs = import_node_path50.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
59108
- const candidateAbs = import_node_path50.default.isAbsolute(args.relPath) ? import_node_path50.default.resolve(args.relPath) : import_node_path50.default.resolve(cwdAbs, args.relPath);
59261
+ const cwdAbs = import_node_path51.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
59262
+ const candidateAbs = import_node_path51.default.isAbsolute(args.relPath) ? import_node_path51.default.resolve(args.relPath) : import_node_path51.default.resolve(cwdAbs, args.relPath);
59109
59263
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
59110
59264
  const from = args.origin === "agent" ? "agent" : ctx?.principal.kind === "owner" ? "owner" : "agent";
59111
59265
  const size = 0;
@@ -59164,19 +59318,19 @@ function buildAttachmentHandlers(deps) {
59164
59318
 
59165
59319
  // src/handlers/extension.ts
59166
59320
  var import_promises9 = __toESM(require("fs/promises"), 1);
59167
- var import_node_path55 = __toESM(require("path"), 1);
59321
+ var import_node_path56 = __toESM(require("path"), 1);
59168
59322
  init_protocol();
59169
59323
 
59170
59324
  // src/extension/bundle-zip.ts
59171
59325
  var import_promises6 = __toESM(require("fs/promises"), 1);
59172
- var import_node_path51 = __toESM(require("path"), 1);
59326
+ var import_node_path52 = __toESM(require("path"), 1);
59173
59327
  var import_node_crypto14 = __toESM(require("crypto"), 1);
59174
59328
  var import_jszip2 = __toESM(require_lib3(), 1);
59175
59329
  async function bundleExtensionDir(dir) {
59176
59330
  const entries = await listFilesSorted(dir);
59177
59331
  const zip = new import_jszip2.default();
59178
59332
  for (const rel of entries) {
59179
- const abs = import_node_path51.default.join(dir, rel);
59333
+ const abs = import_node_path52.default.join(dir, rel);
59180
59334
  const content = await import_promises6.default.readFile(abs);
59181
59335
  zip.file(rel, content, { date: FIXED_DATE });
59182
59336
  }
@@ -59197,7 +59351,7 @@ async function listFilesSorted(rootDir) {
59197
59351
  return out;
59198
59352
  }
59199
59353
  async function walk(absRoot, relPrefix, out) {
59200
- const dirAbs = import_node_path51.default.join(absRoot, relPrefix);
59354
+ const dirAbs = import_node_path52.default.join(absRoot, relPrefix);
59201
59355
  const entries = await import_promises6.default.readdir(dirAbs, { withFileTypes: true });
59202
59356
  for (const e of entries) {
59203
59357
  if (IGNORE_BASENAMES.has(e.name)) continue;
@@ -59251,25 +59405,25 @@ function computePublishCheck(args) {
59251
59405
 
59252
59406
  // src/extension/install-flow.ts
59253
59407
  var import_promises7 = __toESM(require("fs/promises"), 1);
59254
- var import_node_path53 = __toESM(require("path"), 1);
59408
+ var import_node_path54 = __toESM(require("path"), 1);
59255
59409
  var import_node_os19 = __toESM(require("os"), 1);
59256
59410
  var import_node_crypto15 = __toESM(require("crypto"), 1);
59257
59411
  var import_jszip3 = __toESM(require_lib3(), 1);
59258
59412
 
59259
59413
  // src/extension/paths.ts
59260
59414
  var import_node_os18 = __toESM(require("os"), 1);
59261
- var import_node_path52 = __toESM(require("path"), 1);
59415
+ var import_node_path53 = __toESM(require("path"), 1);
59262
59416
  function clawdHomeRoot(override) {
59263
- return override ?? process.env.CLAWD_HOME ?? import_node_path52.default.join(import_node_os18.default.homedir(), ".clawd");
59417
+ return override ?? process.env.CLAWD_HOME ?? import_node_path53.default.join(import_node_os18.default.homedir(), ".clawd");
59264
59418
  }
59265
59419
  function extensionsRoot(override) {
59266
- return import_node_path52.default.join(clawdHomeRoot(override), "extensions");
59420
+ return import_node_path53.default.join(clawdHomeRoot(override), "extensions");
59267
59421
  }
59268
59422
  function publishedChannelsFile(override) {
59269
- return import_node_path52.default.join(clawdHomeRoot(override), "extensions-published.json");
59423
+ return import_node_path53.default.join(clawdHomeRoot(override), "extensions-published.json");
59270
59424
  }
59271
59425
  function bundleCacheRoot(override) {
59272
- return import_node_path52.default.join(clawdHomeRoot(override), "extension-bundles");
59426
+ return import_node_path53.default.join(clawdHomeRoot(override), "extension-bundles");
59273
59427
  }
59274
59428
 
59275
59429
  // src/extension/install-flow.ts
@@ -59296,7 +59450,7 @@ async function installFromChannel(args, deps) {
59296
59450
  throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
59297
59451
  }
59298
59452
  for (const name of Object.keys(zip.files)) {
59299
- if (name.includes("..") || name.startsWith("/") || import_node_path53.default.isAbsolute(name)) {
59453
+ if (name.includes("..") || name.startsWith("/") || import_node_path54.default.isAbsolute(name)) {
59300
59454
  throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
59301
59455
  }
59302
59456
  }
@@ -59328,7 +59482,7 @@ async function installFromChannel(args, deps) {
59328
59482
  );
59329
59483
  }
59330
59484
  const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
59331
- const destDir = import_node_path53.default.join(deps.extensionsRoot, localExtId);
59485
+ const destDir = import_node_path54.default.join(deps.extensionsRoot, localExtId);
59332
59486
  let destExists = false;
59333
59487
  try {
59334
59488
  await import_promises7.default.access(destDir);
@@ -59342,16 +59496,16 @@ async function installFromChannel(args, deps) {
59342
59496
  );
59343
59497
  }
59344
59498
  const stage = await import_promises7.default.mkdtemp(
59345
- import_node_path53.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
59499
+ import_node_path54.default.join(import_node_os19.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
59346
59500
  );
59347
59501
  try {
59348
59502
  for (const [name, entry] of Object.entries(zip.files)) {
59349
- const dest = import_node_path53.default.join(stage, name);
59503
+ const dest = import_node_path54.default.join(stage, name);
59350
59504
  if (entry.dir) {
59351
59505
  await import_promises7.default.mkdir(dest, { recursive: true });
59352
59506
  continue;
59353
59507
  }
59354
- await import_promises7.default.mkdir(import_node_path53.default.dirname(dest), { recursive: true });
59508
+ await import_promises7.default.mkdir(import_node_path54.default.dirname(dest), { recursive: true });
59355
59509
  if (name === "manifest.json") {
59356
59510
  const rewritten = { ...parsed.data, id: localExtId };
59357
59511
  await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -59372,7 +59526,7 @@ async function installFromChannel(args, deps) {
59372
59526
 
59373
59527
  // src/extension/update-flow.ts
59374
59528
  var import_promises8 = __toESM(require("fs/promises"), 1);
59375
- var import_node_path54 = __toESM(require("path"), 1);
59529
+ var import_node_path55 = __toESM(require("path"), 1);
59376
59530
  var import_node_os20 = __toESM(require("os"), 1);
59377
59531
  var import_node_crypto16 = __toESM(require("crypto"), 1);
59378
59532
  var import_jszip4 = __toESM(require_lib3(), 1);
@@ -59389,11 +59543,11 @@ async function updateFromChannel(args, deps) {
59389
59543
  channelRef.extId,
59390
59544
  channelRef.ownerPrincipalId
59391
59545
  );
59392
- const liveDir = import_node_path54.default.join(deps.extensionsRoot, localExtId);
59546
+ const liveDir = import_node_path55.default.join(deps.extensionsRoot, localExtId);
59393
59547
  const prevDir = `${liveDir}.prev`;
59394
59548
  let existingVersion;
59395
59549
  try {
59396
- const raw = await import_promises8.default.readFile(import_node_path54.default.join(liveDir, "manifest.json"), "utf8");
59550
+ const raw = await import_promises8.default.readFile(import_node_path55.default.join(liveDir, "manifest.json"), "utf8");
59397
59551
  const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
59398
59552
  if (!parsed2.success) {
59399
59553
  throw new UpdateError(
@@ -59426,7 +59580,7 @@ async function updateFromChannel(args, deps) {
59426
59580
  throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
59427
59581
  }
59428
59582
  for (const name of Object.keys(zip.files)) {
59429
- if (name.includes("..") || name.startsWith("/") || import_node_path54.default.isAbsolute(name)) {
59583
+ if (name.includes("..") || name.startsWith("/") || import_node_path55.default.isAbsolute(name)) {
59430
59584
  throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
59431
59585
  }
59432
59586
  }
@@ -59461,16 +59615,16 @@ async function updateFromChannel(args, deps) {
59461
59615
  await import_promises8.default.rm(prevDir, { recursive: true, force: true });
59462
59616
  await import_promises8.default.rename(liveDir, prevDir);
59463
59617
  const stage = await import_promises8.default.mkdtemp(
59464
- import_node_path54.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
59618
+ import_node_path55.default.join(import_node_os20.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
59465
59619
  );
59466
59620
  try {
59467
59621
  for (const [name, entry] of Object.entries(zip.files)) {
59468
- const dest = import_node_path54.default.join(stage, name);
59622
+ const dest = import_node_path55.default.join(stage, name);
59469
59623
  if (entry.dir) {
59470
59624
  await import_promises8.default.mkdir(dest, { recursive: true });
59471
59625
  continue;
59472
59626
  }
59473
- await import_promises8.default.mkdir(import_node_path54.default.dirname(dest), { recursive: true });
59627
+ await import_promises8.default.mkdir(import_node_path55.default.dirname(dest), { recursive: true });
59474
59628
  if (name === "manifest.json") {
59475
59629
  const rewritten = { ...parsed.data, id: localExtId };
59476
59630
  await import_promises8.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -59537,7 +59691,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
59537
59691
  );
59538
59692
  }
59539
59693
  }
59540
- const manifestPath = import_node_path55.default.join(root, extId, "manifest.json");
59694
+ const manifestPath = import_node_path56.default.join(root, extId, "manifest.json");
59541
59695
  const manifest = await readManifest(root, extId);
59542
59696
  const next = { ...manifest, version: newVersion };
59543
59697
  const tmp = `${manifestPath}.tmp`;
@@ -59545,7 +59699,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
59545
59699
  await import_promises9.default.rename(tmp, manifestPath);
59546
59700
  }
59547
59701
  async function readManifest(root, extId) {
59548
- const file = import_node_path55.default.join(root, extId, "manifest.json");
59702
+ const file = import_node_path56.default.join(root, extId, "manifest.json");
59549
59703
  let raw;
59550
59704
  try {
59551
59705
  raw = await import_promises9.default.readFile(file, "utf8");
@@ -59629,7 +59783,7 @@ function buildExtensionHandlers(deps) {
59629
59783
  };
59630
59784
  async function buildSnapshotMeta(extId) {
59631
59785
  const manifest = await readManifest(deps.root, extId);
59632
- const { sha256, buffer } = await bundleExtensionDir(import_node_path55.default.join(deps.root, extId));
59786
+ const { sha256, buffer } = await bundleExtensionDir(import_node_path56.default.join(deps.root, extId));
59633
59787
  return { manifest, contentHash: sha256, buffer };
59634
59788
  }
59635
59789
  const publish = async (frame, _client, ctx) => {
@@ -59804,9 +59958,9 @@ function buildExtensionHandlers(deps) {
59804
59958
  }
59805
59959
 
59806
59960
  // src/app-builder/project-store.ts
59807
- var import_node_fs45 = require("fs");
59961
+ var import_node_fs46 = require("fs");
59808
59962
  var import_node_child_process11 = require("child_process");
59809
- var import_node_path56 = require("path");
59963
+ var import_node_path57 = require("path");
59810
59964
  init_protocol();
59811
59965
  var PROJECTS_DIR = "projects";
59812
59966
  var META_FILE = ".clawd-project.json";
@@ -59820,19 +59974,19 @@ var ProjectStore = class {
59820
59974
  root;
59821
59975
  /** projects/<name>/.clawd-project.json 路径 */
59822
59976
  metaPath(name) {
59823
- return (0, import_node_path56.join)(this.projectsRoot(), name, META_FILE);
59977
+ return (0, import_node_path57.join)(this.projectsRoot(), name, META_FILE);
59824
59978
  }
59825
59979
  /** projects/<name>/ 目录路径(cwd 用) */
59826
59980
  projectDir(name) {
59827
- return (0, import_node_path56.join)(this.projectsRoot(), name);
59981
+ return (0, import_node_path57.join)(this.projectsRoot(), name);
59828
59982
  }
59829
59983
  projectsRoot() {
59830
- return (0, import_node_path56.join)(this.root, PROJECTS_DIR);
59984
+ return (0, import_node_path57.join)(this.root, PROJECTS_DIR);
59831
59985
  }
59832
59986
  async list() {
59833
59987
  let entries;
59834
59988
  try {
59835
- entries = await import_node_fs45.promises.readdir(this.projectsRoot());
59989
+ entries = await import_node_fs46.promises.readdir(this.projectsRoot());
59836
59990
  } catch (err) {
59837
59991
  if (err.code === "ENOENT") return [];
59838
59992
  throw err;
@@ -59840,7 +59994,7 @@ var ProjectStore = class {
59840
59994
  const out = [];
59841
59995
  for (const name of entries) {
59842
59996
  try {
59843
- const raw = await import_node_fs45.promises.readFile(this.metaPath(name), "utf8");
59997
+ const raw = await import_node_fs46.promises.readFile(this.metaPath(name), "utf8");
59844
59998
  const json = JSON.parse(raw);
59845
59999
  let migrated = false;
59846
60000
  if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
@@ -59851,7 +60005,7 @@ var ProjectStore = class {
59851
60005
  if (parsed.success) {
59852
60006
  out.push(parsed.data);
59853
60007
  if (migrated) {
59854
- void import_node_fs45.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
60008
+ void import_node_fs46.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
59855
60009
  });
59856
60010
  }
59857
60011
  }
@@ -59895,8 +60049,8 @@ var ProjectStore = class {
59895
60049
  throw new Error(`invalid name "${name}": ${validated.error.message}`);
59896
60050
  }
59897
60051
  const dir = this.projectDir(name);
59898
- await import_node_fs45.promises.mkdir(dir, { recursive: true });
59899
- await import_node_fs45.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
60052
+ await import_node_fs46.promises.mkdir(dir, { recursive: true });
60053
+ await import_node_fs46.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
59900
60054
  return meta;
59901
60055
  }
59902
60056
  /**
@@ -59939,7 +60093,7 @@ var ProjectStore = class {
59939
60093
  }
59940
60094
  async delete(name) {
59941
60095
  const dir = this.projectDir(name);
59942
- await import_node_fs45.promises.rm(dir, { recursive: true, force: true });
60096
+ await import_node_fs46.promises.rm(dir, { recursive: true, force: true });
59943
60097
  }
59944
60098
  async updatePort(name, newPort) {
59945
60099
  if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
@@ -59955,7 +60109,7 @@ var ProjectStore = class {
59955
60109
  throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
59956
60110
  }
59957
60111
  const updated = { ...target, port: newPort };
59958
- await import_node_fs45.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
60112
+ await import_node_fs46.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
59959
60113
  return updated;
59960
60114
  }
59961
60115
  /**
@@ -59972,7 +60126,7 @@ var ProjectStore = class {
59972
60126
  if (!validated.success) {
59973
60127
  throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
59974
60128
  }
59975
- await import_node_fs45.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
60129
+ await import_node_fs46.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
59976
60130
  return validated.data;
59977
60131
  }
59978
60132
  /**
@@ -59993,7 +60147,7 @@ var ProjectStore = class {
59993
60147
  if (!validated.success) {
59994
60148
  throw new Error(`invalid publishJob: ${validated.error.message}`);
59995
60149
  }
59996
- await import_node_fs45.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
60150
+ await import_node_fs46.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
59997
60151
  return validated.data;
59998
60152
  }
59999
60153
  /** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
@@ -60008,7 +60162,7 @@ var ProjectStore = class {
60008
60162
  if (!validated.success) {
60009
60163
  throw new Error(`failed to clear publishJob: ${validated.error.message}`);
60010
60164
  }
60011
- await import_node_fs45.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
60165
+ await import_node_fs46.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
60012
60166
  return validated.data;
60013
60167
  }
60014
60168
  };
@@ -60129,8 +60283,8 @@ var PublishJobRegistry = class {
60129
60283
 
60130
60284
  // src/app-builder/publish-job-runner.ts
60131
60285
  var import_node_child_process13 = require("child_process");
60132
- var import_node_fs46 = require("fs");
60133
- var import_node_path57 = require("path");
60286
+ var import_node_fs47 = require("fs");
60287
+ var import_node_path58 = require("path");
60134
60288
 
60135
60289
  // src/app-builder/publish-stage-parser.ts
60136
60290
  var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
@@ -60162,10 +60316,10 @@ async function startPublishJob(deps, args) {
60162
60316
  return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
60163
60317
  }
60164
60318
  const projDir = projectDir(args.name);
60165
- const logPath = (0, import_node_path57.join)(projDir, ".publish.log");
60319
+ const logPath = (0, import_node_path58.join)(projDir, ".publish.log");
60166
60320
  let logStream = null;
60167
60321
  try {
60168
- logStream = (0, import_node_fs46.createWriteStream)(logPath, { flags: "w" });
60322
+ logStream = (0, import_node_fs47.createWriteStream)(logPath, { flags: "w" });
60169
60323
  } catch {
60170
60324
  logStream = null;
60171
60325
  }
@@ -60422,8 +60576,8 @@ async function recoverInterruptedJobs(deps) {
60422
60576
 
60423
60577
  // src/handlers/app-builder.ts
60424
60578
  init_protocol();
60425
- var import_node_path58 = require("path");
60426
- var import_node_fs47 = require("fs");
60579
+ var import_node_path59 = require("path");
60580
+ var import_node_fs48 = require("fs");
60427
60581
  var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
60428
60582
  var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
60429
60583
  async function recoverInterruptedPublishJobs(store, logger) {
@@ -60504,7 +60658,7 @@ function buildAppBuilderHandlers(deps) {
60504
60658
  async function listAllUsersProjects() {
60505
60659
  if (!deps.usersRoot || !deps.getStore) return [];
60506
60660
  const getStore = deps.getStore;
60507
- const userIds = await import_node_fs47.promises.readdir(deps.usersRoot).catch(() => []);
60661
+ const userIds = await import_node_fs48.promises.readdir(deps.usersRoot).catch(() => []);
60508
60662
  const perUser = await Promise.all(
60509
60663
  userIds.map((uid) => getStore(uid).list().catch(() => []))
60510
60664
  );
@@ -60573,8 +60727,8 @@ function buildAppBuilderHandlers(deps) {
60573
60727
  const project = await userStore.create(f.name, reservedPorts);
60574
60728
  try {
60575
60729
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
60576
- const templateSrcDir = (0, import_node_path58.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
60577
- const scaffoldScript = (0, import_node_path58.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
60730
+ const templateSrcDir = (0, import_node_path59.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
60731
+ const scaffoldScript = (0, import_node_path59.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
60578
60732
  const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
60579
60733
  deps.logger?.info("app-builder.scaffold.done", {
60580
60734
  name: project.name,
@@ -60778,7 +60932,7 @@ function buildAppBuilderHandlers(deps) {
60778
60932
  await userStore.clearPublishJob(args.name);
60779
60933
  }
60780
60934
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
60781
- const scriptPath = (0, import_node_path58.join)(deps.deployKitRoot, "scripts", "publish.sh");
60935
+ const scriptPath = (0, import_node_path59.join)(deps.deployKitRoot, "scripts", "publish.sh");
60782
60936
  deps.logger?.info("app-builder.publish.start", {
60783
60937
  name: args.name,
60784
60938
  sessionId: boundSession.sessionId,
@@ -60954,7 +61108,7 @@ function buildVisitorHandlers(deps) {
60954
61108
 
60955
61109
  // src/extension/registry.ts
60956
61110
  var import_promises10 = __toESM(require("fs/promises"), 1);
60957
- var import_node_path59 = __toESM(require("path"), 1);
61111
+ var import_node_path60 = __toESM(require("path"), 1);
60958
61112
  async function loadAll(root) {
60959
61113
  let entries;
60960
61114
  try {
@@ -60967,13 +61121,13 @@ async function loadAll(root) {
60967
61121
  for (const ent of entries) {
60968
61122
  if (!ent.isDirectory()) continue;
60969
61123
  if (ent.name.startsWith(".")) continue;
60970
- records.push(await loadOne(import_node_path59.default.join(root, ent.name), ent.name));
61124
+ records.push(await loadOne(import_node_path60.default.join(root, ent.name), ent.name));
60971
61125
  }
60972
61126
  records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
60973
61127
  return records;
60974
61128
  }
60975
61129
  async function loadOne(dir, dirName) {
60976
- const manifestPath = import_node_path59.default.join(dir, "manifest.json");
61130
+ const manifestPath = import_node_path60.default.join(dir, "manifest.json");
60977
61131
  let raw;
60978
61132
  try {
60979
61133
  raw = await import_promises10.default.readFile(manifestPath, "utf8");
@@ -61018,7 +61172,7 @@ async function loadOne(dir, dirName) {
61018
61172
 
61019
61173
  // src/extension/uninstall.ts
61020
61174
  var import_promises11 = __toESM(require("fs/promises"), 1);
61021
- var import_node_path60 = __toESM(require("path"), 1);
61175
+ var import_node_path61 = __toESM(require("path"), 1);
61022
61176
  var UninstallError = class extends Error {
61023
61177
  constructor(code, message) {
61024
61178
  super(message);
@@ -61027,7 +61181,7 @@ var UninstallError = class extends Error {
61027
61181
  code;
61028
61182
  };
61029
61183
  async function uninstall(deps) {
61030
- const dir = import_node_path60.default.join(deps.root, deps.extId);
61184
+ const dir = import_node_path61.default.join(deps.root, deps.extId);
61031
61185
  try {
61032
61186
  await import_promises11.default.access(dir);
61033
61187
  } catch {
@@ -61557,7 +61711,7 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
61557
61711
 
61558
61712
  // src/extension/runtime.ts
61559
61713
  var import_node_child_process16 = require("child_process");
61560
- var import_node_path61 = __toESM(require("path"), 1);
61714
+ var import_node_path62 = __toESM(require("path"), 1);
61561
61715
  var import_promises12 = require("timers/promises");
61562
61716
 
61563
61717
  // src/extension/port-allocator.ts
@@ -61658,7 +61812,7 @@ var Runtime = class {
61658
61812
  /\$CLAWOS_EXT_PORT/g,
61659
61813
  String(port)
61660
61814
  );
61661
- const dir = import_node_path61.default.join(this.root, extId);
61815
+ const dir = import_node_path62.default.join(this.root, extId);
61662
61816
  const env = {
61663
61817
  ...process.env,
61664
61818
  CLAWOS_EXT_PORT: String(port),
@@ -61770,7 +61924,7 @@ ${handle.stderrTail}`
61770
61924
 
61771
61925
  // src/extension/published-channels.ts
61772
61926
  var import_promises13 = __toESM(require("fs/promises"), 1);
61773
- var import_node_path62 = __toESM(require("path"), 1);
61927
+ var import_node_path63 = __toESM(require("path"), 1);
61774
61928
  init_zod();
61775
61929
  var PublishedChannelsError = class extends Error {
61776
61930
  constructor(code, message) {
@@ -61869,7 +62023,7 @@ var PublishedChannelStore = class {
61869
62023
  )
61870
62024
  };
61871
62025
  const tmp = `${this.filePath}.tmp`;
61872
- await import_promises13.default.mkdir(import_node_path62.default.dirname(this.filePath), { recursive: true });
62026
+ await import_promises13.default.mkdir(import_node_path63.default.dirname(this.filePath), { recursive: true });
61873
62027
  await import_promises13.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
61874
62028
  await import_promises13.default.rename(tmp, this.filePath);
61875
62029
  }
@@ -61877,7 +62031,7 @@ var PublishedChannelStore = class {
61877
62031
 
61878
62032
  // src/extension/bundle-cache.ts
61879
62033
  var import_promises14 = __toESM(require("fs/promises"), 1);
61880
- var import_node_path63 = __toESM(require("path"), 1);
62034
+ var import_node_path64 = __toESM(require("path"), 1);
61881
62035
  var BundleCache = class {
61882
62036
  constructor(rootDir) {
61883
62037
  this.rootDir = rootDir;
@@ -61886,14 +62040,14 @@ var BundleCache = class {
61886
62040
  /** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
61887
62041
  async write(snapshotHash, buffer) {
61888
62042
  await import_promises14.default.mkdir(this.rootDir, { recursive: true });
61889
- const file = import_node_path63.default.join(this.rootDir, `${snapshotHash}.zip`);
62043
+ const file = import_node_path64.default.join(this.rootDir, `${snapshotHash}.zip`);
61890
62044
  const tmp = `${file}.tmp`;
61891
62045
  await import_promises14.default.writeFile(tmp, buffer, { mode: 384 });
61892
62046
  await import_promises14.default.rename(tmp, file);
61893
62047
  }
61894
62048
  /** Returns the bundle bytes, or null when the file doesn't exist. */
61895
62049
  async read(snapshotHash) {
61896
- const file = import_node_path63.default.join(this.rootDir, `${snapshotHash}.zip`);
62050
+ const file = import_node_path64.default.join(this.rootDir, `${snapshotHash}.zip`);
61897
62051
  try {
61898
62052
  return await import_promises14.default.readFile(file);
61899
62053
  } catch (e) {
@@ -61903,7 +62057,7 @@ var BundleCache = class {
61903
62057
  }
61904
62058
  /** Idempotent — missing file is not an error. */
61905
62059
  async delete(snapshotHash) {
61906
- const file = import_node_path63.default.join(this.rootDir, `${snapshotHash}.zip`);
62060
+ const file = import_node_path64.default.join(this.rootDir, `${snapshotHash}.zip`);
61907
62061
  await import_promises14.default.rm(file, { force: true });
61908
62062
  }
61909
62063
  };
@@ -61926,24 +62080,24 @@ async function startDaemon(config) {
61926
62080
  sampling: logShippingCfg.sampling,
61927
62081
  homeDir: import_node_os22.default.homedir()
61928
62082
  });
61929
- const logDir = import_node_path64.default.join(config.dataDir, "log");
61930
- import_node_fs48.default.mkdirSync(logDir, { recursive: true });
62083
+ const logDir = import_node_path65.default.join(config.dataDir, "log");
62084
+ import_node_fs49.default.mkdirSync(logDir, { recursive: true });
61931
62085
  const logger = createLogger({
61932
62086
  level: config.logLevel,
61933
- file: import_node_path64.default.join(logDir, "clawd.log"),
62087
+ file: import_node_path65.default.join(logDir, "clawd.log"),
61934
62088
  logClient
61935
62089
  });
61936
62090
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
61937
62091
  const screenIdleProbeLogger = createFileOnlyLogger({
61938
- file: import_node_path64.default.join(logDir, "screen-idle-probe.log"),
62092
+ file: import_node_path65.default.join(logDir, "screen-idle-probe.log"),
61939
62093
  level: "debug"
61940
62094
  });
61941
62095
  logger.info("screen-idle probe logger enabled", {
61942
- file: import_node_path64.default.join(logDir, "screen-idle-probe.log")
62096
+ file: import_node_path65.default.join(logDir, "screen-idle-probe.log")
61943
62097
  });
61944
62098
  const CAP_TARGETS = [
61945
- import_node_path64.default.join(logDir, "clawd.log"),
61946
- import_node_path64.default.join(logDir, "screen-idle-probe.log")
62099
+ import_node_path65.default.join(logDir, "clawd.log"),
62100
+ import_node_path65.default.join(logDir, "screen-idle-probe.log")
61947
62101
  ];
61948
62102
  const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
61949
62103
  const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
@@ -62039,8 +62193,8 @@ async function startDaemon(config) {
62039
62193
  const agents = new AgentsScanner();
62040
62194
  const history = new ClaudeHistoryReader();
62041
62195
  let transport = null;
62042
- const personaStore = new PersonaStore(import_node_path64.default.join(config.dataDir, "personas"));
62043
- const usersRoot = import_node_path64.default.join(config.dataDir, "users");
62196
+ const personaStore = new PersonaStore(import_node_path65.default.join(config.dataDir, "personas"));
62197
+ const usersRoot = import_node_path65.default.join(config.dataDir, "users");
62044
62198
  const defaultsRoot = findDefaultsRoot(logger);
62045
62199
  if (defaultsRoot) {
62046
62200
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -62058,10 +62212,11 @@ async function startDaemon(config) {
62058
62212
  refreshSandboxSettings({ store: personaStore, logger });
62059
62213
  migrateAgentsMirror({ store: personaStore, logger });
62060
62214
  migrateCodexSandbox({ store: personaStore, logger });
62215
+ migrateSandboxAllowRead({ store: personaStore, logger });
62061
62216
  retirePersonaBuilder({ store: personaStore, logger });
62062
62217
  const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
62063
62218
  const dispatchStore = createDispatchStore({
62064
- filePath: import_node_path64.default.join(config.dataDir, "dispatch.json"),
62219
+ filePath: import_node_path65.default.join(config.dataDir, "dispatch.json"),
62065
62220
  now: () => Date.now(),
62066
62221
  onFlushError: (err) => logger.error("dispatch store flush failed", {
62067
62222
  reason: err instanceof Error ? err.message : String(err)
@@ -62076,18 +62231,18 @@ async function startDaemon(config) {
62076
62231
  now: () => Date.now(),
62077
62232
  onCompleted: (sid) => deliverPendingRef?.(sid)
62078
62233
  });
62079
- const here = typeof __dirname === "string" ? __dirname : import_node_path64.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
62234
+ const here = typeof __dirname === "string" ? __dirname : import_node_path65.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
62080
62235
  const mcpConfigs = [];
62081
62236
  const mcpDaemonUrl = `http://127.0.0.1:${config.port}`;
62082
62237
  const dispatchServerCandidates = [
62083
- import_node_path64.default.join(here, "dispatch", "mcp-server.cjs"),
62238
+ import_node_path65.default.join(here, "dispatch", "mcp-server.cjs"),
62084
62239
  // 生产 dist/index → dist/dispatch/mcp-server.cjs
62085
- import_node_path64.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
62240
+ import_node_path65.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
62086
62241
  // dev tsx src/index → ../dist/dispatch/mcp-server.cjs
62087
62242
  ];
62088
- const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs48.default.existsSync(p2));
62243
+ const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs49.default.existsSync(p2));
62089
62244
  if (dispatchServerScriptPath) {
62090
- const dispatchLogPath = import_node_path64.default.join(logDir, "dispatch-mcp-server.log");
62245
+ const dispatchLogPath = import_node_path65.default.join(logDir, "dispatch-mcp-server.log");
62091
62246
  const dispatchCfgPath = writeDispatchMcpConfig({
62092
62247
  dataDir: config.dataDir,
62093
62248
  serverScriptPath: dispatchServerScriptPath,
@@ -62107,12 +62262,12 @@ async function startDaemon(config) {
62107
62262
  });
62108
62263
  }
62109
62264
  const ticketServerCandidates = [
62110
- import_node_path64.default.join(here, "ticket", "mcp-server.cjs"),
62111
- import_node_path64.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
62265
+ import_node_path65.default.join(here, "ticket", "mcp-server.cjs"),
62266
+ import_node_path65.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
62112
62267
  ];
62113
- const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs48.default.existsSync(p2));
62268
+ const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs49.default.existsSync(p2));
62114
62269
  if (ticketServerScriptPath) {
62115
- const ticketLogPath = import_node_path64.default.join(logDir, "ticket-mcp-server.log");
62270
+ const ticketLogPath = import_node_path65.default.join(logDir, "ticket-mcp-server.log");
62116
62271
  const ticketCfgPath = writeTicketMcpConfig({
62117
62272
  dataDir: config.dataDir,
62118
62273
  serverScriptPath: ticketServerScriptPath,
@@ -62134,12 +62289,12 @@ async function startDaemon(config) {
62134
62289
  });
62135
62290
  }
62136
62291
  const shiftServerCandidates = [
62137
- import_node_path64.default.join(here, "shift", "mcp-server.cjs"),
62138
- import_node_path64.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
62292
+ import_node_path65.default.join(here, "shift", "mcp-server.cjs"),
62293
+ import_node_path65.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
62139
62294
  ];
62140
- const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs48.default.existsSync(p2));
62295
+ const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs49.default.existsSync(p2));
62141
62296
  if (shiftServerScriptPath) {
62142
- const shiftLogPath = import_node_path64.default.join(logDir, "shift-mcp-server.log");
62297
+ const shiftLogPath = import_node_path65.default.join(logDir, "shift-mcp-server.log");
62143
62298
  const shiftCfgPath = await writeShiftMcpConfig({
62144
62299
  dataDir: config.dataDir,
62145
62300
  serverScriptPath: shiftServerScriptPath,
@@ -62160,12 +62315,12 @@ async function startDaemon(config) {
62160
62315
  );
62161
62316
  }
62162
62317
  const inboxServerCandidates = [
62163
- import_node_path64.default.join(here, "inbox", "mcp-server.cjs"),
62164
- import_node_path64.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
62318
+ import_node_path65.default.join(here, "inbox", "mcp-server.cjs"),
62319
+ import_node_path65.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
62165
62320
  ];
62166
- const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs48.default.existsSync(p2));
62321
+ const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs49.default.existsSync(p2));
62167
62322
  if (inboxServerScriptPath) {
62168
- const inboxLogPath = import_node_path64.default.join(logDir, "inbox-mcp-server.log");
62323
+ const inboxLogPath = import_node_path65.default.join(logDir, "inbox-mcp-server.log");
62169
62324
  const inboxCfgPath = await writeInboxMcpConfig({
62170
62325
  dataDir: config.dataDir,
62171
62326
  serverScriptPath: inboxServerScriptPath,
@@ -62186,10 +62341,10 @@ async function startDaemon(config) {
62186
62341
  );
62187
62342
  }
62188
62343
  const peerOpsServerCandidates = [
62189
- import_node_path64.default.join(here, "peer-ops", "mcp-server.cjs"),
62190
- import_node_path64.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
62344
+ import_node_path65.default.join(here, "peer-ops", "mcp-server.cjs"),
62345
+ import_node_path65.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
62191
62346
  ];
62192
- const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs48.default.existsSync(p2));
62347
+ const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs49.default.existsSync(p2));
62193
62348
  if (peerOpsServerScriptPath) {
62194
62349
  const peerOpsCfgPath = writePeerOpsMcpConfig({
62195
62350
  dataDir: config.dataDir,
@@ -62209,12 +62364,12 @@ async function startDaemon(config) {
62209
62364
  );
62210
62365
  }
62211
62366
  const rpcToolServerCandidates = [
62212
- import_node_path64.default.join(here, "rpc-tool", "mcp-server.cjs"),
62213
- import_node_path64.default.join(here, "..", "dist", "rpc-tool", "mcp-server.cjs")
62367
+ import_node_path65.default.join(here, "rpc-tool", "mcp-server.cjs"),
62368
+ import_node_path65.default.join(here, "..", "dist", "rpc-tool", "mcp-server.cjs")
62214
62369
  ];
62215
- const rpcToolServerScriptPath = rpcToolServerCandidates.find((p2) => import_node_fs48.default.existsSync(p2));
62370
+ const rpcToolServerScriptPath = rpcToolServerCandidates.find((p2) => import_node_fs49.default.existsSync(p2));
62216
62371
  if (rpcToolServerScriptPath) {
62217
- const rpcToolLogPath = import_node_path64.default.join(logDir, "rpc-tool-mcp-server.log");
62372
+ const rpcToolLogPath = import_node_path65.default.join(logDir, "rpc-tool-mcp-server.log");
62218
62373
  const rpcToolCfgPath = writeRpcToolMcpConfig({
62219
62374
  dataDir: config.dataDir,
62220
62375
  serverScriptPath: rpcToolServerScriptPath,
@@ -62235,7 +62390,7 @@ async function startDaemon(config) {
62235
62390
  );
62236
62391
  }
62237
62392
  const shiftStore = createShiftStore({
62238
- filePath: import_node_path64.default.join(config.dataDir, "shift.json"),
62393
+ filePath: import_node_path65.default.join(config.dataDir, "shift.json"),
62239
62394
  ownerIdProvider: () => ownerPrincipalId,
62240
62395
  now: () => Date.now()
62241
62396
  });
@@ -62257,7 +62412,7 @@ async function startDaemon(config) {
62257
62412
  getAdapter,
62258
62413
  historyReader: history,
62259
62414
  dataDir: config.dataDir,
62260
- personaRoot: import_node_path64.default.join(config.dataDir, "personas"),
62415
+ personaRoot: import_node_path65.default.join(config.dataDir, "personas"),
62261
62416
  // dispatch / shift 自动化 spawn 按 persona 绑定 tool 起 B session(codex persona 用 codex)。
62262
62417
  // personaRegistry 声明在 manager 之后(同作用域 const,调用时机在启动完成后,无 TDZ 风险)。
62263
62418
  getPersonaTool: (personaId2) => personaRegistry.get(personaId2)?.tool,
@@ -62308,10 +62463,10 @@ async function startDaemon(config) {
62308
62463
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
62309
62464
  attachmentGroup: {
62310
62465
  onFileEdit: (input) => {
62311
- const absPath = import_node_path64.default.isAbsolute(input.relPath) ? input.relPath : import_node_path64.default.join(input.cwd, input.relPath);
62466
+ const absPath = import_node_path65.default.isAbsolute(input.relPath) ? input.relPath : import_node_path65.default.join(input.cwd, input.relPath);
62312
62467
  let size = 0;
62313
62468
  try {
62314
- size = import_node_fs48.default.statSync(absPath).size;
62469
+ size = import_node_fs49.default.statSync(absPath).size;
62315
62470
  } catch (err) {
62316
62471
  logger.warn("attachment.onFileEdit stat failed", {
62317
62472
  sessionId: input.sessionId,
@@ -62487,6 +62642,12 @@ async function startDaemon(config) {
62487
62642
  });
62488
62643
  const getProjectStore = (userId) => new ProjectStore(deriveUserWorkDir(userId, usersRoot));
62489
62644
  const appBuilderStore = getProjectStore(ownerPrincipalId);
62645
+ const attachmentUserWorkDir = (sid) => {
62646
+ const scope = manager.findOwnedSessionScope(sid);
62647
+ if (scope?.kind !== "persona") return null;
62648
+ const userId = scope.mode === "owner" ? ownerPrincipalId : scope.capId;
62649
+ return deriveUserWorkDir(userId, usersRoot);
62650
+ };
62490
62651
  recoverInterruptedPublishJobs(appBuilderStore, logger).catch((err) => {
62491
62652
  logger.warn("app-builder.publish.recover-startup-failed", {
62492
62653
  error: err instanceof Error ? err.message : String(err)
@@ -62552,11 +62713,11 @@ async function startDaemon(config) {
62552
62713
  // 'persona/<pid>/owner',default 走 'default'。
62553
62714
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
62554
62715
  // guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
62555
- personaRoot: import_node_path64.default.join(config.dataDir, "personas"),
62716
+ personaRoot: import_node_path65.default.join(config.dataDir, "personas"),
62556
62717
  usersRoot
62557
62718
  },
62558
62719
  // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
62559
- personaRoot: import_node_path64.default.join(config.dataDir, "personas"),
62720
+ personaRoot: import_node_path65.default.join(config.dataDir, "personas"),
62560
62721
  // v2 多人 persona 隔离:handler 派生 guest user-dir 放行
62561
62722
  usersRoot,
62562
62723
  // v2 Phase 6: whoami handler 装在 owner principal.displayName + persona 解析
@@ -62682,11 +62843,11 @@ async function startDaemon(config) {
62682
62843
  // 发布上线脚手架化 (spec 2026-06-03 §5.2):
62683
62844
  // appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
62684
62845
  // dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
62685
- appBuilderPersonaRoot: import_node_path64.default.join(config.dataDir, "personas", "persona-app-builder"),
62846
+ appBuilderPersonaRoot: import_node_path65.default.join(config.dataDir, "personas", "persona-app-builder"),
62686
62847
  // 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
62687
- deployKitRoot: import_node_path64.default.join(config.dataDir, "deploy-kit"),
62848
+ deployKitRoot: import_node_path65.default.join(config.dataDir, "deploy-kit"),
62688
62849
  // scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
62689
- resolvePersonaRoot: (personaId2) => import_node_path64.default.join(config.dataDir, "personas", personaId2),
62850
+ resolvePersonaRoot: (personaId2) => import_node_path65.default.join(config.dataDir, "personas", personaId2),
62690
62851
  // 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
62691
62852
  // 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
62692
62853
  // 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
@@ -62789,7 +62950,7 @@ async function startDaemon(config) {
62789
62950
  });
62790
62951
  shiftScheduler.start();
62791
62952
  logger.info("shift scheduler started", { shiftCount: shiftStore.list().length });
62792
- const larkBotStore = createLarkBotBindingStore(import_node_path64.default.join(config.dataDir, "personas"));
62953
+ const larkBotStore = createLarkBotBindingStore(import_node_path65.default.join(config.dataDir, "personas"));
62793
62954
  const larkBotCloud = createLarkBotCloudClient({
62794
62955
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
62795
62956
  apiKey: resolveClawdTicketsApiKey(),
@@ -62800,6 +62961,9 @@ async function startDaemon(config) {
62800
62961
  manager,
62801
62962
  cloud: larkBotCloud,
62802
62963
  store: larkBotStore,
62964
+ fetchResource: (appId, messageId, fileKey, maxBytes) => larkBotCloud.fetchResource(appId, messageId, fileKey, maxBytes),
62965
+ // 图片落 guest userWorkDir 下(capId = lark-<chat_id>):CC 经 --add-dir + allowRead carve 可 Read
62966
+ mediaRootFor: (chatId) => import_node_path65.default.join(deriveUserWorkDir(`lark-${chatId}`, usersRoot), "lark-media"),
62803
62967
  logger: {
62804
62968
  warn: (msg) => logger.warn(msg),
62805
62969
  info: (msg) => logger.info(msg)
@@ -62879,15 +63043,17 @@ async function startDaemon(config) {
62879
63043
  larkBotEvent: larkBotEventEndpoint,
62880
63044
  // /files HMAC verify 用 auth.json token(与 attachment.signUrl 同源)。
62881
63045
  getSignSecret: () => authFile.token,
62882
- // chatinput 附件上传(POST /attachments/upload,spec §6.1)。upload pipeline
62883
- // session.cwd 下的 .attachments/ 落盘;签名 secret /files HMAC 同源;
62884
- // tunnel 未启时返 503 TUNNEL_NOT_READY 让前端显式提示。
63046
+ // chatinput 附件上传(POST /attachments/upload,spec §6.1 + 2026-08-06 §5)。落盘根按
63047
+ // scope 切换:persona 会话 连接者 userWorkDir(与 connection-prompt 宣称的工作目录一致,
63048
+ // guest persona 目录的读权不再连带看到 owner 上传的附件);非 persona 会话 → session.cwd
63049
+ // (项目目录本就是用户地盘)。签名 secret 与 /files HMAC 同源;tunnel 未启时返 503。
62885
63050
  attachmentUpload: {
62886
63051
  groupFileStore,
62887
63052
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
62888
- getSessionDir: (sid) => {
63053
+ getAttachmentBaseDir: (sid) => {
62889
63054
  const f = manager.findOwnedSession(sid);
62890
- return f ? f.cwd : null;
63055
+ if (!f) return null;
63056
+ return attachmentUserWorkDir(sid) ?? f.cwd;
62891
63057
  },
62892
63058
  getHttpBaseUrl: () => currentTunnelUrl ? getHttpBaseUrl() : null
62893
63059
  },
@@ -63080,8 +63246,8 @@ async function startDaemon(config) {
63080
63246
  const lines = [
63081
63247
  `Tunnel: ${r.url}`,
63082
63248
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
63083
- `Frpc config: ${import_node_path64.default.join(config.dataDir, "frpc.toml")}`,
63084
- `Frpc log: ${import_node_path64.default.join(logDir, "frpc.log")}`
63249
+ `Frpc config: ${import_node_path65.default.join(config.dataDir, "frpc.toml")}`,
63250
+ `Frpc log: ${import_node_path65.default.join(logDir, "frpc.log")}`
63085
63251
  ];
63086
63252
  const width = Math.max(...lines.map((l) => l.length));
63087
63253
  const bar = "\u2550".repeat(width + 4);
@@ -63094,8 +63260,8 @@ ${bar}
63094
63260
 
63095
63261
  `);
63096
63262
  try {
63097
- const connectPath = import_node_path64.default.join(config.dataDir, "connect.txt");
63098
- import_node_fs48.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
63263
+ const connectPath = import_node_path65.default.join(config.dataDir, "connect.txt");
63264
+ import_node_fs49.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
63099
63265
  } catch {
63100
63266
  }
63101
63267
  } catch (err) {
@@ -63131,8 +63297,10 @@ ${bar}
63131
63297
  scope,
63132
63298
  sessionId
63133
63299
  })),
63134
- // 注入 cwd GC 直接定位 .attachments 目录(与 upload 落盘点一致)
63300
+ // 双根扫描(spec 2026-08-06 §5):legacy cwd 根(迁移前落盘点,30 天自然老化)+
63301
+ // userWorkDir 根(迁移后 upload 落盘点,与 getAttachmentBaseDir 同源派生)
63135
63302
  getSessionCwd: (sid) => manager.findOwnedSession(sid)?.cwd ?? null,
63303
+ getUserWorkDir: attachmentUserWorkDir,
63136
63304
  logger
63137
63305
  });
63138
63306
  } catch (err) {
@@ -63167,9 +63335,9 @@ ${bar}
63167
63335
  };
63168
63336
  }
63169
63337
  function migrateDropPersonsDir(dataDir) {
63170
- const dir = import_node_path64.default.join(dataDir, "persons");
63338
+ const dir = import_node_path65.default.join(dataDir, "persons");
63171
63339
  try {
63172
- import_node_fs48.default.rmSync(dir, { recursive: true, force: true });
63340
+ import_node_fs49.default.rmSync(dir, { recursive: true, force: true });
63173
63341
  } catch {
63174
63342
  }
63175
63343
  }