@clawos-dev/clawd 0.2.85-beta.159.97c16b3 → 0.2.85-beta.160.958069f

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.
Files changed (2) hide show
  1. package/dist/cli.cjs +451 -643
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -643,8 +643,8 @@ var init_parseUtil = __esm({
643
643
  init_errors2();
644
644
  init_en();
645
645
  makeIssue = (params) => {
646
- const { data, path: path45, errorMaps, issueData } = params;
647
- const fullPath = [...path45, ...issueData.path || []];
646
+ const { data, path: path44, errorMaps, issueData } = params;
647
+ const fullPath = [...path44, ...issueData.path || []];
648
648
  const fullIssue = {
649
649
  ...issueData,
650
650
  path: fullPath
@@ -955,11 +955,11 @@ var init_types = __esm({
955
955
  init_parseUtil();
956
956
  init_util();
957
957
  ParseInputLazyPath = class {
958
- constructor(parent, value, path45, key) {
958
+ constructor(parent, value, path44, key) {
959
959
  this._cachedPath = [];
960
960
  this.parent = parent;
961
961
  this.data = value;
962
- this._path = path45;
962
+ this._path = path44;
963
963
  this._key = key;
964
964
  }
965
965
  get path() {
@@ -4343,13 +4343,13 @@ var init_zod = __esm({
4343
4343
  });
4344
4344
 
4345
4345
  // ../protocol/src/attachment-schemas.ts
4346
- var TOKEN_ROLES, GROUP_FILE_SOURCES, GroupFileEntrySchema, AttachmentSignUrlArgs, AttachmentSignUrlResponseSchema, AttachmentGroupAddArgs, AttachmentGroupAddResponseSchema, AttachmentGroupRemoveArgs, AttachmentGroupRemoveResponseSchema, AttachmentGroupListArgs, AttachmentGroupListResponseSchema, AttachmentUploadResponseSchema;
4346
+ var TOKEN_ROLES, GROUP_FILE_SOURCES, GroupFileEntrySchema, AttachmentSignUrlArgs, AttachmentSignUrlResponseSchema, AttachmentGroupAddArgs, AttachmentGroupAddResponseSchema, AttachmentGroupRemoveArgs, AttachmentGroupRemoveResponseSchema, AttachmentGroupListArgs, AttachmentGroupListResponseSchema;
4347
4347
  var init_attachment_schemas = __esm({
4348
4348
  "../protocol/src/attachment-schemas.ts"() {
4349
4349
  "use strict";
4350
4350
  init_zod();
4351
4351
  TOKEN_ROLES = ["owner"];
4352
- GROUP_FILE_SOURCES = ["agent", "owner", "user-upload"];
4352
+ GROUP_FILE_SOURCES = ["agent", "owner"];
4353
4353
  GroupFileEntrySchema = external_exports.object({
4354
4354
  /** daemon 派发的稳定 id(用于 RPC remove / UI key) */
4355
4355
  id: external_exports.string().min(1),
@@ -4404,16 +4404,6 @@ var init_attachment_schemas = __esm({
4404
4404
  AttachmentGroupListResponseSchema = external_exports.object({
4405
4405
  entries: external_exports.array(GroupFileEntrySchema)
4406
4406
  });
4407
- AttachmentUploadResponseSchema = external_exports.object({
4408
- /** sha256(bytes) 前 16 位,前端持有的稳定 ref */
4409
- attachmentId: external_exports.string().min(1),
4410
- /** 完整 tunnel signed URL,前端拼 marker / 直接送 Anthropic 都用它 */
4411
- url: external_exports.string().min(1),
4412
- /** 落在 session 目录下的相对路径(后续 group RPC / drawer 展示用) */
4413
- relPath: external_exports.string().min(1),
4414
- sizeBytes: external_exports.number().int().nonnegative(),
4415
- mimeType: external_exports.string().min(1)
4416
- });
4417
4407
  }
4418
4408
  });
4419
4409
 
@@ -5590,8 +5580,8 @@ var require_req = __commonJS({
5590
5580
  if (req.originalUrl) {
5591
5581
  _req.url = req.originalUrl;
5592
5582
  } else {
5593
- const path45 = req.path;
5594
- _req.url = typeof path45 === "string" ? path45 : req.url ? req.url.path || req.url : void 0;
5583
+ const path44 = req.path;
5584
+ _req.url = typeof path44 === "string" ? path44 : req.url ? req.url.path || req.url : void 0;
5595
5585
  }
5596
5586
  if (req.query) {
5597
5587
  _req.query = req.query;
@@ -5756,14 +5746,14 @@ var require_redact = __commonJS({
5756
5746
  }
5757
5747
  return obj;
5758
5748
  }
5759
- function parsePath(path45) {
5749
+ function parsePath(path44) {
5760
5750
  const parts = [];
5761
5751
  let current = "";
5762
5752
  let inBrackets = false;
5763
5753
  let inQuotes = false;
5764
5754
  let quoteChar = "";
5765
- for (let i = 0; i < path45.length; i++) {
5766
- const char = path45[i];
5755
+ for (let i = 0; i < path44.length; i++) {
5756
+ const char = path44[i];
5767
5757
  if (!inBrackets && char === ".") {
5768
5758
  if (current) {
5769
5759
  parts.push(current);
@@ -5894,10 +5884,10 @@ var require_redact = __commonJS({
5894
5884
  return current;
5895
5885
  }
5896
5886
  function redactPaths(obj, paths, censor, remove = false) {
5897
- for (const path45 of paths) {
5898
- const parts = parsePath(path45);
5887
+ for (const path44 of paths) {
5888
+ const parts = parsePath(path44);
5899
5889
  if (parts.includes("*")) {
5900
- redactWildcardPath(obj, parts, censor, path45, remove);
5890
+ redactWildcardPath(obj, parts, censor, path44, remove);
5901
5891
  } else {
5902
5892
  if (remove) {
5903
5893
  removeKey(obj, parts);
@@ -5982,8 +5972,8 @@ var require_redact = __commonJS({
5982
5972
  }
5983
5973
  } else {
5984
5974
  if (afterWildcard.includes("*")) {
5985
- const wrappedCensor = typeof censor === "function" ? (value, path45) => {
5986
- const fullPath = [...pathArray.slice(0, pathLength), ...path45];
5975
+ const wrappedCensor = typeof censor === "function" ? (value, path44) => {
5976
+ const fullPath = [...pathArray.slice(0, pathLength), ...path44];
5987
5977
  return censor(value, fullPath);
5988
5978
  } : censor;
5989
5979
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -6018,8 +6008,8 @@ var require_redact = __commonJS({
6018
6008
  return null;
6019
6009
  }
6020
6010
  const pathStructure = /* @__PURE__ */ new Map();
6021
- for (const path45 of pathsToClone) {
6022
- const parts = parsePath(path45);
6011
+ for (const path44 of pathsToClone) {
6012
+ const parts = parsePath(path44);
6023
6013
  let current = pathStructure;
6024
6014
  for (let i = 0; i < parts.length; i++) {
6025
6015
  const part = parts[i];
@@ -6071,24 +6061,24 @@ var require_redact = __commonJS({
6071
6061
  }
6072
6062
  return cloneSelectively(obj, pathStructure);
6073
6063
  }
6074
- function validatePath(path45) {
6075
- if (typeof path45 !== "string") {
6064
+ function validatePath(path44) {
6065
+ if (typeof path44 !== "string") {
6076
6066
  throw new Error("Paths must be (non-empty) strings");
6077
6067
  }
6078
- if (path45 === "") {
6068
+ if (path44 === "") {
6079
6069
  throw new Error("Invalid redaction path ()");
6080
6070
  }
6081
- if (path45.includes("..")) {
6082
- throw new Error(`Invalid redaction path (${path45})`);
6071
+ if (path44.includes("..")) {
6072
+ throw new Error(`Invalid redaction path (${path44})`);
6083
6073
  }
6084
- if (path45.includes(",")) {
6085
- throw new Error(`Invalid redaction path (${path45})`);
6074
+ if (path44.includes(",")) {
6075
+ throw new Error(`Invalid redaction path (${path44})`);
6086
6076
  }
6087
6077
  let bracketCount = 0;
6088
6078
  let inQuotes = false;
6089
6079
  let quoteChar = "";
6090
- for (let i = 0; i < path45.length; i++) {
6091
- const char = path45[i];
6080
+ for (let i = 0; i < path44.length; i++) {
6081
+ const char = path44[i];
6092
6082
  if ((char === '"' || char === "'") && bracketCount > 0) {
6093
6083
  if (!inQuotes) {
6094
6084
  inQuotes = true;
@@ -6102,20 +6092,20 @@ var require_redact = __commonJS({
6102
6092
  } else if (char === "]" && !inQuotes) {
6103
6093
  bracketCount--;
6104
6094
  if (bracketCount < 0) {
6105
- throw new Error(`Invalid redaction path (${path45})`);
6095
+ throw new Error(`Invalid redaction path (${path44})`);
6106
6096
  }
6107
6097
  }
6108
6098
  }
6109
6099
  if (bracketCount !== 0) {
6110
- throw new Error(`Invalid redaction path (${path45})`);
6100
+ throw new Error(`Invalid redaction path (${path44})`);
6111
6101
  }
6112
6102
  }
6113
6103
  function validatePaths(paths) {
6114
6104
  if (!Array.isArray(paths)) {
6115
6105
  throw new TypeError("paths must be an array");
6116
6106
  }
6117
- for (const path45 of paths) {
6118
- validatePath(path45);
6107
+ for (const path44 of paths) {
6108
+ validatePath(path44);
6119
6109
  }
6120
6110
  }
6121
6111
  function slowRedact(options = {}) {
@@ -6283,8 +6273,8 @@ var require_redaction = __commonJS({
6283
6273
  if (shape[k2] === null) {
6284
6274
  o[k2] = (value) => topCensor(value, [k2]);
6285
6275
  } else {
6286
- const wrappedCensor = typeof censor === "function" ? (value, path45) => {
6287
- return censor(value, [k2, ...path45]);
6276
+ const wrappedCensor = typeof censor === "function" ? (value, path44) => {
6277
+ return censor(value, [k2, ...path44]);
6288
6278
  } : censor;
6289
6279
  o[k2] = Redact({
6290
6280
  paths: shape[k2],
@@ -6502,10 +6492,10 @@ var require_atomic_sleep = __commonJS({
6502
6492
  var require_sonic_boom = __commonJS({
6503
6493
  "../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
6504
6494
  "use strict";
6505
- var fs36 = require("fs");
6495
+ var fs35 = require("fs");
6506
6496
  var EventEmitter2 = require("events");
6507
6497
  var inherits = require("util").inherits;
6508
- var path45 = require("path");
6498
+ var path44 = require("path");
6509
6499
  var sleep = require_atomic_sleep();
6510
6500
  var assert = require("assert");
6511
6501
  var BUSY_WRITE_TIMEOUT = 100;
@@ -6559,20 +6549,20 @@ var require_sonic_boom = __commonJS({
6559
6549
  const mode = sonic.mode;
6560
6550
  if (sonic.sync) {
6561
6551
  try {
6562
- if (sonic.mkdir) fs36.mkdirSync(path45.dirname(file), { recursive: true });
6563
- const fd = fs36.openSync(file, flags, mode);
6552
+ if (sonic.mkdir) fs35.mkdirSync(path44.dirname(file), { recursive: true });
6553
+ const fd = fs35.openSync(file, flags, mode);
6564
6554
  fileOpened(null, fd);
6565
6555
  } catch (err) {
6566
6556
  fileOpened(err);
6567
6557
  throw err;
6568
6558
  }
6569
6559
  } else if (sonic.mkdir) {
6570
- fs36.mkdir(path45.dirname(file), { recursive: true }, (err) => {
6560
+ fs35.mkdir(path44.dirname(file), { recursive: true }, (err) => {
6571
6561
  if (err) return fileOpened(err);
6572
- fs36.open(file, flags, mode, fileOpened);
6562
+ fs35.open(file, flags, mode, fileOpened);
6573
6563
  });
6574
6564
  } else {
6575
- fs36.open(file, flags, mode, fileOpened);
6565
+ fs35.open(file, flags, mode, fileOpened);
6576
6566
  }
6577
6567
  }
6578
6568
  function SonicBoom(opts) {
@@ -6613,8 +6603,8 @@ var require_sonic_boom = __commonJS({
6613
6603
  this.flush = flushBuffer;
6614
6604
  this.flushSync = flushBufferSync;
6615
6605
  this._actualWrite = actualWriteBuffer;
6616
- fsWriteSync = () => fs36.writeSync(this.fd, this._writingBuf);
6617
- fsWrite = () => fs36.write(this.fd, this._writingBuf, this.release);
6606
+ fsWriteSync = () => fs35.writeSync(this.fd, this._writingBuf);
6607
+ fsWrite = () => fs35.write(this.fd, this._writingBuf, this.release);
6618
6608
  } else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
6619
6609
  this._writingBuf = "";
6620
6610
  this.write = write;
@@ -6623,15 +6613,15 @@ var require_sonic_boom = __commonJS({
6623
6613
  this._actualWrite = actualWrite;
6624
6614
  fsWriteSync = () => {
6625
6615
  if (Buffer.isBuffer(this._writingBuf)) {
6626
- return fs36.writeSync(this.fd, this._writingBuf);
6616
+ return fs35.writeSync(this.fd, this._writingBuf);
6627
6617
  }
6628
- return fs36.writeSync(this.fd, this._writingBuf, "utf8");
6618
+ return fs35.writeSync(this.fd, this._writingBuf, "utf8");
6629
6619
  };
6630
6620
  fsWrite = () => {
6631
6621
  if (Buffer.isBuffer(this._writingBuf)) {
6632
- return fs36.write(this.fd, this._writingBuf, this.release);
6622
+ return fs35.write(this.fd, this._writingBuf, this.release);
6633
6623
  }
6634
- return fs36.write(this.fd, this._writingBuf, "utf8", this.release);
6624
+ return fs35.write(this.fd, this._writingBuf, "utf8", this.release);
6635
6625
  };
6636
6626
  } else {
6637
6627
  throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
@@ -6688,7 +6678,7 @@ var require_sonic_boom = __commonJS({
6688
6678
  }
6689
6679
  }
6690
6680
  if (this._fsync) {
6691
- fs36.fsyncSync(this.fd);
6681
+ fs35.fsyncSync(this.fd);
6692
6682
  }
6693
6683
  const len = this._len;
6694
6684
  if (this._reopening) {
@@ -6802,7 +6792,7 @@ var require_sonic_boom = __commonJS({
6802
6792
  const onDrain = () => {
6803
6793
  if (!this._fsync) {
6804
6794
  try {
6805
- fs36.fsync(this.fd, (err) => {
6795
+ fs35.fsync(this.fd, (err) => {
6806
6796
  this._flushPending = false;
6807
6797
  cb(err);
6808
6798
  });
@@ -6904,7 +6894,7 @@ var require_sonic_boom = __commonJS({
6904
6894
  const fd = this.fd;
6905
6895
  this.once("ready", () => {
6906
6896
  if (fd !== this.fd) {
6907
- fs36.close(fd, (err) => {
6897
+ fs35.close(fd, (err) => {
6908
6898
  if (err) {
6909
6899
  return this.emit("error", err);
6910
6900
  }
@@ -6953,7 +6943,7 @@ var require_sonic_boom = __commonJS({
6953
6943
  buf = this._bufs[0];
6954
6944
  }
6955
6945
  try {
6956
- const n = Buffer.isBuffer(buf) ? fs36.writeSync(this.fd, buf) : fs36.writeSync(this.fd, buf, "utf8");
6946
+ const n = Buffer.isBuffer(buf) ? fs35.writeSync(this.fd, buf) : fs35.writeSync(this.fd, buf, "utf8");
6957
6947
  const releasedBufObj = releaseWritingBuf(buf, this._len, n);
6958
6948
  buf = releasedBufObj.writingBuf;
6959
6949
  this._len = releasedBufObj.len;
@@ -6969,7 +6959,7 @@ var require_sonic_boom = __commonJS({
6969
6959
  }
6970
6960
  }
6971
6961
  try {
6972
- fs36.fsyncSync(this.fd);
6962
+ fs35.fsyncSync(this.fd);
6973
6963
  } catch {
6974
6964
  }
6975
6965
  }
@@ -6990,7 +6980,7 @@ var require_sonic_boom = __commonJS({
6990
6980
  buf = mergeBuf(this._bufs[0], this._lens[0]);
6991
6981
  }
6992
6982
  try {
6993
- const n = fs36.writeSync(this.fd, buf);
6983
+ const n = fs35.writeSync(this.fd, buf);
6994
6984
  buf = buf.subarray(n);
6995
6985
  this._len = Math.max(this._len - n, 0);
6996
6986
  if (buf.length <= 0) {
@@ -7018,13 +7008,13 @@ var require_sonic_boom = __commonJS({
7018
7008
  this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
7019
7009
  if (this.sync) {
7020
7010
  try {
7021
- const written = Buffer.isBuffer(this._writingBuf) ? fs36.writeSync(this.fd, this._writingBuf) : fs36.writeSync(this.fd, this._writingBuf, "utf8");
7011
+ const written = Buffer.isBuffer(this._writingBuf) ? fs35.writeSync(this.fd, this._writingBuf) : fs35.writeSync(this.fd, this._writingBuf, "utf8");
7022
7012
  release(null, written);
7023
7013
  } catch (err) {
7024
7014
  release(err);
7025
7015
  }
7026
7016
  } else {
7027
- fs36.write(this.fd, this._writingBuf, release);
7017
+ fs35.write(this.fd, this._writingBuf, release);
7028
7018
  }
7029
7019
  }
7030
7020
  function actualWriteBuffer() {
@@ -7033,7 +7023,7 @@ var require_sonic_boom = __commonJS({
7033
7023
  this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
7034
7024
  if (this.sync) {
7035
7025
  try {
7036
- const written = fs36.writeSync(this.fd, this._writingBuf);
7026
+ const written = fs35.writeSync(this.fd, this._writingBuf);
7037
7027
  release(null, written);
7038
7028
  } catch (err) {
7039
7029
  release(err);
@@ -7042,7 +7032,7 @@ var require_sonic_boom = __commonJS({
7042
7032
  if (kCopyBuffer) {
7043
7033
  this._writingBuf = Buffer.from(this._writingBuf);
7044
7034
  }
7045
- fs36.write(this.fd, this._writingBuf, release);
7035
+ fs35.write(this.fd, this._writingBuf, release);
7046
7036
  }
7047
7037
  }
7048
7038
  function actualClose(sonic) {
@@ -7058,12 +7048,12 @@ var require_sonic_boom = __commonJS({
7058
7048
  sonic._lens = [];
7059
7049
  assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
7060
7050
  try {
7061
- fs36.fsync(sonic.fd, closeWrapped);
7051
+ fs35.fsync(sonic.fd, closeWrapped);
7062
7052
  } catch {
7063
7053
  }
7064
7054
  function closeWrapped() {
7065
7055
  if (sonic.fd !== 1 && sonic.fd !== 2) {
7066
- fs36.close(sonic.fd, done);
7056
+ fs35.close(sonic.fd, done);
7067
7057
  } else {
7068
7058
  done();
7069
7059
  }
@@ -7794,7 +7784,7 @@ var require_transport = __commonJS({
7794
7784
  stream.flushSync();
7795
7785
  }
7796
7786
  function transport(fullOptions) {
7797
- const { pipeline: pipeline3, targets, levels, dedupe, worker = {}, caller = getCallers(), sync = false } = fullOptions;
7787
+ const { pipeline: pipeline2, targets, levels, dedupe, worker = {}, caller = getCallers(), sync = false } = fullOptions;
7798
7788
  const options = {
7799
7789
  ...fullOptions.options
7800
7790
  };
@@ -7822,9 +7812,9 @@ var require_transport = __commonJS({
7822
7812
  };
7823
7813
  });
7824
7814
  });
7825
- } else if (pipeline3) {
7815
+ } else if (pipeline2) {
7826
7816
  target = bundlerOverrides["pino-worker"] || join11(__dirname, "worker.js");
7827
- options.pipelines = [pipeline3.map((dest) => {
7817
+ options.pipelines = [pipeline2.map((dest) => {
7828
7818
  return {
7829
7819
  ...dest,
7830
7820
  target: fixTarget(dest.target)
@@ -9427,7 +9417,7 @@ var require_multistream = __commonJS({
9427
9417
  var require_pino = __commonJS({
9428
9418
  "../node_modules/.pnpm/pino@9.14.0/node_modules/pino/pino.js"(exports2, module2) {
9429
9419
  "use strict";
9430
- var os16 = require("os");
9420
+ var os17 = require("os");
9431
9421
  var stdSerializers = require_pino_std_serializers();
9432
9422
  var caller = require_caller();
9433
9423
  var redaction = require_redaction();
@@ -9474,7 +9464,7 @@ var require_pino = __commonJS({
9474
9464
  } = symbols;
9475
9465
  var { epochTime, nullTime } = time;
9476
9466
  var { pid } = process;
9477
- var hostname = os16.hostname();
9467
+ var hostname = os17.hostname();
9478
9468
  var defaultErrorSerializer = stdSerializers.err;
9479
9469
  var defaultOptions = {
9480
9470
  level: "info",
@@ -10198,11 +10188,11 @@ var init_lib = __esm({
10198
10188
  }
10199
10189
  }
10200
10190
  },
10201
- addToPath: function addToPath(path45, added, removed, oldPosInc, options) {
10202
- var last = path45.lastComponent;
10191
+ addToPath: function addToPath(path44, added, removed, oldPosInc, options) {
10192
+ var last = path44.lastComponent;
10203
10193
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
10204
10194
  return {
10205
- oldPos: path45.oldPos + oldPosInc,
10195
+ oldPos: path44.oldPos + oldPosInc,
10206
10196
  lastComponent: {
10207
10197
  count: last.count + 1,
10208
10198
  added,
@@ -10212,7 +10202,7 @@ var init_lib = __esm({
10212
10202
  };
10213
10203
  } else {
10214
10204
  return {
10215
- oldPos: path45.oldPos + oldPosInc,
10205
+ oldPos: path44.oldPos + oldPosInc,
10216
10206
  lastComponent: {
10217
10207
  count: 1,
10218
10208
  added,
@@ -10643,10 +10633,10 @@ function attachmentToHistoryMessage(o, ts) {
10643
10633
  const memories = raw.map((m2) => {
10644
10634
  if (!m2 || typeof m2 !== "object") return null;
10645
10635
  const rec = m2;
10646
- const path45 = typeof rec.path === "string" ? rec.path : null;
10636
+ const path44 = typeof rec.path === "string" ? rec.path : null;
10647
10637
  const content = typeof rec.content === "string" ? rec.content : null;
10648
- if (!path45 || content == null) return null;
10649
- const entry = { path: path45, content };
10638
+ if (!path44 || content == null) return null;
10639
+ const entry = { path: path44, content };
10650
10640
  if (typeof rec.mtimeMs === "number") entry.mtimeMs = rec.mtimeMs;
10651
10641
  return entry;
10652
10642
  }).filter((m2) => m2 !== null);
@@ -11472,10 +11462,10 @@ function parseAttachment(obj) {
11472
11462
  const memories = raw.map((m2) => {
11473
11463
  if (!m2 || typeof m2 !== "object") return null;
11474
11464
  const rec = m2;
11475
- const path45 = typeof rec.path === "string" ? rec.path : null;
11465
+ const path44 = typeof rec.path === "string" ? rec.path : null;
11476
11466
  const content = typeof rec.content === "string" ? rec.content : null;
11477
- if (!path45 || content == null) return null;
11478
- const out = { path: path45, content };
11467
+ if (!path44 || content == null) return null;
11468
+ const out = { path: path44, content };
11479
11469
  if (typeof rec.mtimeMs === "number") out.mtimeMs = rec.mtimeMs;
11480
11470
  return out;
11481
11471
  }).filter((m2) => m2 !== null);
@@ -20390,7 +20380,7 @@ var require_websocket_server = __commonJS({
20390
20380
  // src/run-case/recorder.ts
20391
20381
  function startRunCaseRecorder(opts) {
20392
20382
  const now = opts.now ?? Date.now;
20393
- const dir = import_node_path31.default.dirname(opts.recordPath);
20383
+ const dir = import_node_path30.default.dirname(opts.recordPath);
20394
20384
  let stream = null;
20395
20385
  let closing = false;
20396
20386
  let closedSettled = false;
@@ -20404,8 +20394,8 @@ function startRunCaseRecorder(opts) {
20404
20394
  });
20405
20395
  const ensureStream = () => {
20406
20396
  if (stream) return stream;
20407
- import_node_fs28.default.mkdirSync(dir, { recursive: true });
20408
- stream = import_node_fs28.default.createWriteStream(opts.recordPath, { flags: "a" });
20397
+ import_node_fs27.default.mkdirSync(dir, { recursive: true });
20398
+ stream = import_node_fs27.default.createWriteStream(opts.recordPath, { flags: "a" });
20409
20399
  stream.on("close", () => closedResolve());
20410
20400
  return stream;
20411
20401
  };
@@ -20430,12 +20420,12 @@ function startRunCaseRecorder(opts) {
20430
20420
  };
20431
20421
  return { tap, close, closed };
20432
20422
  }
20433
- var import_node_fs28, import_node_path31;
20423
+ var import_node_fs27, import_node_path30;
20434
20424
  var init_recorder = __esm({
20435
20425
  "src/run-case/recorder.ts"() {
20436
20426
  "use strict";
20437
- import_node_fs28 = __toESM(require("fs"), 1);
20438
- import_node_path31 = __toESM(require("path"), 1);
20427
+ import_node_fs27 = __toESM(require("fs"), 1);
20428
+ import_node_path30 = __toESM(require("path"), 1);
20439
20429
  }
20440
20430
  });
20441
20431
 
@@ -20478,7 +20468,7 @@ var init_wire = __esm({
20478
20468
  // src/run-case/controller.ts
20479
20469
  async function runController(opts) {
20480
20470
  const now = opts.now ?? Date.now;
20481
- const cwd = opts.cwd ?? (0, import_node_fs29.mkdtempSync)(import_node_path32.default.join(import_node_os14.default.tmpdir(), "clawd-runcase-"));
20471
+ const cwd = opts.cwd ?? (0, import_node_fs28.mkdtempSync)(import_node_path31.default.join(import_node_os15.default.tmpdir(), "clawd-runcase-"));
20482
20472
  const ownsCwd = opts.cwd === void 0;
20483
20473
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
20484
20474
  const spawnCtx = { cwd };
@@ -20639,19 +20629,19 @@ async function runController(opts) {
20639
20629
  if (sigintHandler) process.off("SIGINT", sigintHandler);
20640
20630
  if (ownsCwd) {
20641
20631
  try {
20642
- (0, import_node_fs29.rmSync)(cwd, { recursive: true, force: true });
20632
+ (0, import_node_fs28.rmSync)(cwd, { recursive: true, force: true });
20643
20633
  } catch {
20644
20634
  }
20645
20635
  }
20646
20636
  return exitCode ?? 0;
20647
20637
  }
20648
- var import_node_fs29, import_node_os14, import_node_path32;
20638
+ var import_node_fs28, import_node_os15, import_node_path31;
20649
20639
  var init_controller = __esm({
20650
20640
  "src/run-case/controller.ts"() {
20651
20641
  "use strict";
20652
- import_node_fs29 = require("fs");
20653
- import_node_os14 = __toESM(require("os"), 1);
20654
- import_node_path32 = __toESM(require("path"), 1);
20642
+ import_node_fs28 = require("fs");
20643
+ import_node_os15 = __toESM(require("os"), 1);
20644
+ import_node_path31 = __toESM(require("path"), 1);
20655
20645
  init_claude();
20656
20646
  init_stdout_splitter();
20657
20647
  init_permission_stdio();
@@ -20883,8 +20873,8 @@ Env (advanced):
20883
20873
  `;
20884
20874
 
20885
20875
  // src/index.ts
20886
- var import_node_path30 = __toESM(require("path"), 1);
20887
- var import_node_fs27 = __toESM(require("fs"), 1);
20876
+ var import_node_path29 = __toESM(require("path"), 1);
20877
+ var import_node_fs26 = __toESM(require("fs"), 1);
20888
20878
 
20889
20879
  // src/logger.ts
20890
20880
  var import_node_fs2 = __toESM(require("fs"), 1);
@@ -22619,18 +22609,6 @@ var SessionManager = class {
22619
22609
  (a, b2) => a.updatedAt > b2.updatedAt ? -1 : a.updatedAt < b2.updatedAt ? 1 : 0
22620
22610
  );
22621
22611
  }
22622
- /**
22623
- * 跨 scope 列出所有 owner 视角的 session(default + persona owner + persona guest),
22624
- * 附带派生的 SessionScope。attachment GC tick 用 —— 需要遍历所有活跃 session 的
22625
- * .attachments 目录 + 群文件清单求 liveSet。
22626
- */
22627
- listOwnedSessionScopes() {
22628
- return this.listAllOwned().map((file) => ({
22629
- scope: this.scopeForFile(file),
22630
- sessionId: file.sessionId,
22631
- cwd: file.cwd
22632
- }));
22633
- }
22634
22612
  // 写回 SessionFile,自动根据 ownerPersonaId / creatorPrincipalId 派生写入 scope.
22635
22613
  // 调用方原先都是 `deps.store.write(updated)`,全部走这里收口路由.
22636
22614
  writeOwned(file) {
@@ -25531,11 +25509,18 @@ var AgentsScanner = class {
25531
25509
  };
25532
25510
 
25533
25511
  // src/observer/session-observer.ts
25512
+ var import_node_fs13 = __toESM(require("fs"), 1);
25513
+ var import_node_os9 = __toESM(require("os"), 1);
25514
+ var import_node_path14 = __toESM(require("path"), 1);
25515
+ init_claude_history();
25516
+
25517
+ // src/observer/subagent-meta-observer.ts
25534
25518
  var import_node_fs12 = __toESM(require("fs"), 1);
25535
25519
  var import_node_os8 = __toESM(require("os"), 1);
25536
25520
  var import_node_path13 = __toESM(require("path"), 1);
25537
25521
  init_claude_history();
25538
- var SessionObserver = class {
25522
+ var META_RE = /^agent-([A-Za-z0-9_-]+)\.meta\.json$/;
25523
+ var SubagentMetaObserver = class {
25539
25524
  constructor(opts) {
25540
25525
  this.opts = opts;
25541
25526
  this.home = opts.home ?? import_node_os8.default.homedir();
@@ -25543,16 +25528,132 @@ var SessionObserver = class {
25543
25528
  opts;
25544
25529
  home;
25545
25530
  watches = /* @__PURE__ */ new Map();
25531
+ // public for spec only:测试直接拼路径写假 meta.json;生产 start() 内部自己解析
25532
+ resolveSubagentDir(cwd, toolSessionId) {
25533
+ return import_node_path13.default.join(
25534
+ this.home,
25535
+ ".claude",
25536
+ "projects",
25537
+ cwdToHashDir(cwd),
25538
+ toolSessionId,
25539
+ "subagents"
25540
+ );
25541
+ }
25542
+ start(args) {
25543
+ this.stop(args.sessionId);
25544
+ const dirPath = this.resolveSubagentDir(args.cwd, args.toolSessionId);
25545
+ const w2 = {
25546
+ sessionId: args.sessionId,
25547
+ dirPath,
25548
+ watcher: null,
25549
+ pollTimer: null,
25550
+ emitted: /* @__PURE__ */ new Set()
25551
+ };
25552
+ this.watches.set(args.sessionId, w2);
25553
+ this.scan(w2);
25554
+ this.attachWatcher(w2);
25555
+ w2.pollTimer = setInterval(() => this.scan(w2), 200);
25556
+ return { dirPath };
25557
+ }
25558
+ // fs.watch 必须在目录存在后调用;首次启动时 subagents/ 通常不存在 —— attachWatcher
25559
+ // 失败就交给 pollTimer 200ms 兜底,目录一出现就被下次 poll 捡起来,那时也尝试 attach
25560
+ attachWatcher(w2) {
25561
+ if (w2.watcher) return;
25562
+ try {
25563
+ w2.watcher = import_node_fs12.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
25564
+ if (!name) return;
25565
+ const m2 = META_RE.exec(String(name));
25566
+ if (!m2) return;
25567
+ this.scanOne(w2, String(name));
25568
+ });
25569
+ } catch {
25570
+ }
25571
+ }
25572
+ scan(w2) {
25573
+ let entries;
25574
+ try {
25575
+ entries = import_node_fs12.default.readdirSync(w2.dirPath);
25576
+ } catch {
25577
+ return;
25578
+ }
25579
+ if (!w2.watcher) this.attachWatcher(w2);
25580
+ for (const name of entries) {
25581
+ this.scanOne(w2, name);
25582
+ }
25583
+ }
25584
+ scanOne(w2, name) {
25585
+ const m2 = META_RE.exec(name);
25586
+ if (!m2) return;
25587
+ const agentId = m2[1];
25588
+ if (w2.emitted.has(agentId)) return;
25589
+ const file = import_node_path13.default.join(w2.dirPath, name);
25590
+ let raw;
25591
+ try {
25592
+ raw = import_node_fs12.default.readFileSync(file, "utf8");
25593
+ } catch {
25594
+ return;
25595
+ }
25596
+ let obj;
25597
+ try {
25598
+ obj = JSON.parse(raw);
25599
+ } catch {
25600
+ return;
25601
+ }
25602
+ const toolUseId = typeof obj.toolUseId === "string" ? obj.toolUseId : "";
25603
+ if (!toolUseId) return;
25604
+ w2.emitted.add(agentId);
25605
+ const ev = {
25606
+ kind: "subagent_progress",
25607
+ toolUseId,
25608
+ agentId,
25609
+ status: "started"
25610
+ };
25611
+ if (typeof obj.description === "string" && obj.description) ev.description = obj.description;
25612
+ this.opts.onEvent({ sessionId: w2.sessionId, events: [ev] });
25613
+ }
25614
+ isObserving(sessionId) {
25615
+ return this.watches.has(sessionId);
25616
+ }
25617
+ stop(sessionId) {
25618
+ const w2 = this.watches.get(sessionId);
25619
+ if (!w2) return false;
25620
+ try {
25621
+ w2.watcher?.close();
25622
+ } catch {
25623
+ }
25624
+ if (w2.pollTimer) clearInterval(w2.pollTimer);
25625
+ this.watches.delete(sessionId);
25626
+ return true;
25627
+ }
25628
+ stopAll() {
25629
+ for (const id of [...this.watches.keys()]) this.stop(id);
25630
+ }
25631
+ };
25632
+
25633
+ // src/observer/session-observer.ts
25634
+ var SessionObserver = class {
25635
+ constructor(opts) {
25636
+ this.opts = opts;
25637
+ this.home = opts.home ?? import_node_os9.default.homedir();
25638
+ this.metaObserver = opts.enableSubagentMetaObserver ? new SubagentMetaObserver({ home: this.home, onEvent: opts.onEvent }) : null;
25639
+ }
25640
+ opts;
25641
+ home;
25642
+ watches = /* @__PURE__ */ new Map();
25643
+ // 子 observer:监听 <projectsRoot>/<hashDir>/<toolSessionId>/subagents/ 目录的 meta.json,
25644
+ // 仅 TUI 模式启用——SDK 模式靠 `system.task_started` 帧(claude.ts:466)emit subagent_progress
25645
+ // 已经能解锁 AgentCallLine 抽屉入口,再跑 meta watcher 会让 ring buffer 留双帧
25646
+ metaObserver;
25546
25647
  resolveJsonlPath(cwd, toolSessionId, override) {
25547
25648
  if (override) return override;
25548
- return import_node_path13.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
25649
+ return import_node_path14.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
25549
25650
  }
25550
25651
  start(args) {
25551
25652
  this.stop(args.sessionId);
25552
25653
  const filePath = this.resolveJsonlPath(args.cwd, args.toolSessionId, args.jsonlPath);
25553
25654
  let size = 0;
25554
25655
  try {
25555
- size = import_node_fs12.default.statSync(filePath).size;
25656
+ size = import_node_fs13.default.statSync(filePath).size;
25556
25657
  } catch {
25557
25658
  }
25558
25659
  const w2 = {
@@ -25565,10 +25666,10 @@ var SessionObserver = class {
25565
25666
  adapter: args.adapter
25566
25667
  };
25567
25668
  try {
25568
- import_node_fs12.default.mkdirSync(import_node_path13.default.dirname(filePath), { recursive: true });
25669
+ import_node_fs13.default.mkdirSync(import_node_path14.default.dirname(filePath), { recursive: true });
25569
25670
  } catch {
25570
25671
  }
25571
- w2.watcher = import_node_fs12.default.watch(import_node_path13.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
25672
+ w2.watcher = import_node_fs13.default.watch(import_node_path14.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
25572
25673
  if (!changedName || !filePath.endsWith(changedName)) return;
25573
25674
  this.poll(w2);
25574
25675
  });
@@ -25577,6 +25678,11 @@ var SessionObserver = class {
25577
25678
  this.opts.onStatus?.(args.sessionId, "observing");
25578
25679
  this.hydrateMetaTail(w2);
25579
25680
  this.poll(w2);
25681
+ this.metaObserver?.start({
25682
+ sessionId: args.sessionId,
25683
+ cwd: args.cwd,
25684
+ toolSessionId: args.toolSessionId
25685
+ });
25580
25686
  return { filePath };
25581
25687
  }
25582
25688
  // 读 JSONL 文件尾部 N 行,把每行的 meta_update 字段**合并成一个 patch** 喂给 reducer。
@@ -25586,7 +25692,7 @@ var SessionObserver = class {
25586
25692
  // 异常静默吞,不阻塞 watcher 启动
25587
25693
  hydrateMetaTail(w2, maxLines = 200) {
25588
25694
  try {
25589
- const raw = import_node_fs12.default.readFileSync(w2.filePath, "utf8");
25695
+ const raw = import_node_fs13.default.readFileSync(w2.filePath, "utf8");
25590
25696
  if (!raw) return;
25591
25697
  const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
25592
25698
  if (allLines.length === 0) return;
@@ -25610,7 +25716,7 @@ var SessionObserver = class {
25610
25716
  poll(w2) {
25611
25717
  let size = 0;
25612
25718
  try {
25613
- size = import_node_fs12.default.statSync(w2.filePath).size;
25719
+ size = import_node_fs13.default.statSync(w2.filePath).size;
25614
25720
  } catch {
25615
25721
  return;
25616
25722
  }
@@ -25619,11 +25725,11 @@ var SessionObserver = class {
25619
25725
  w2.buf = "";
25620
25726
  }
25621
25727
  if (size === w2.lastSize) return;
25622
- const fd = import_node_fs12.default.openSync(w2.filePath, "r");
25728
+ const fd = import_node_fs13.default.openSync(w2.filePath, "r");
25623
25729
  try {
25624
25730
  const len = size - w2.lastSize;
25625
25731
  const buf = Buffer.alloc(len);
25626
- import_node_fs12.default.readSync(fd, buf, 0, len, w2.lastSize);
25732
+ import_node_fs13.default.readSync(fd, buf, 0, len, w2.lastSize);
25627
25733
  w2.lastSize = size;
25628
25734
  w2.buf += buf.toString("utf8");
25629
25735
  let newlineIndex;
@@ -25637,7 +25743,7 @@ var SessionObserver = class {
25637
25743
  }
25638
25744
  }
25639
25745
  } finally {
25640
- import_node_fs12.default.closeSync(fd);
25746
+ import_node_fs13.default.closeSync(fd);
25641
25747
  }
25642
25748
  }
25643
25749
  // 解析 JSONL 单行:仅当是主链 user 文本行(非 sidechain / 非 sub-agent / message.role='user'
@@ -25690,11 +25796,13 @@ var SessionObserver = class {
25690
25796
  }
25691
25797
  if (w2.pollTimer) clearInterval(w2.pollTimer);
25692
25798
  this.watches.delete(sessionId);
25799
+ this.metaObserver?.stop(sessionId);
25693
25800
  this.opts.onStatus?.(sessionId, "stopped");
25694
25801
  return true;
25695
25802
  }
25696
25803
  stopAll() {
25697
25804
  for (const id of [...this.watches.keys()]) this.stop(id);
25805
+ this.metaObserver?.stopAll();
25698
25806
  }
25699
25807
  };
25700
25808
 
@@ -26295,14 +26403,14 @@ function authenticate(token, deps) {
26295
26403
  }
26296
26404
 
26297
26405
  // src/permission/capability-store.ts
26298
- var fs15 = __toESM(require("fs"), 1);
26299
- var path18 = __toESM(require("path"), 1);
26406
+ var fs16 = __toESM(require("fs"), 1);
26407
+ var path19 = __toESM(require("path"), 1);
26300
26408
  var CAPABILITIES_FILE_NAME = "capabilities.json";
26301
26409
  var FILE_VERSION = 1;
26302
26410
  var CapabilityStore = class {
26303
26411
  constructor(dataDir) {
26304
26412
  this.dataDir = dataDir;
26305
- fs15.mkdirSync(dataDir, { recursive: true });
26413
+ fs16.mkdirSync(dataDir, { recursive: true });
26306
26414
  this.cache = this.readFromDisk();
26307
26415
  }
26308
26416
  dataDir;
@@ -26326,13 +26434,13 @@ var CapabilityStore = class {
26326
26434
  this.flush();
26327
26435
  }
26328
26436
  filePath() {
26329
- return path18.join(this.dataDir, CAPABILITIES_FILE_NAME);
26437
+ return path19.join(this.dataDir, CAPABILITIES_FILE_NAME);
26330
26438
  }
26331
26439
  readFromDisk() {
26332
26440
  const file = this.filePath();
26333
26441
  let raw;
26334
26442
  try {
26335
- raw = fs15.readFileSync(file, "utf8");
26443
+ raw = fs16.readFileSync(file, "utf8");
26336
26444
  } catch (err) {
26337
26445
  if (err?.code === "ENOENT") return [];
26338
26446
  return [];
@@ -26360,10 +26468,10 @@ var CapabilityStore = class {
26360
26468
  }
26361
26469
  atomicWrite(file, content) {
26362
26470
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
26363
- fs15.writeFileSync(tmp, content, { mode: 384 });
26364
- fs15.renameSync(tmp, file);
26471
+ fs16.writeFileSync(tmp, content, { mode: 384 });
26472
+ fs16.renameSync(tmp, file);
26365
26473
  try {
26366
- fs15.chmodSync(file, 384);
26474
+ fs16.chmodSync(file, 384);
26367
26475
  } catch {
26368
26476
  }
26369
26477
  }
@@ -26456,14 +26564,14 @@ var CapabilityManager = class {
26456
26564
  };
26457
26565
 
26458
26566
  // src/permission/cleanup.ts
26459
- var fs16 = __toESM(require("fs"), 1);
26567
+ var fs17 = __toESM(require("fs"), 1);
26460
26568
  function cleanupGuestSessionsForCapability(cap, factory) {
26461
26569
  const removed = [];
26462
26570
  for (const g2 of cap.grants) {
26463
26571
  if (g2.resource.type !== "persona") continue;
26464
26572
  const dir = factory.vmGuestRoot(g2.resource.id, cap.id);
26465
26573
  try {
26466
- fs16.rmSync(dir, { recursive: true, force: true });
26574
+ fs17.rmSync(dir, { recursive: true, force: true });
26467
26575
  removed.push(dir);
26468
26576
  } catch {
26469
26577
  }
@@ -26472,13 +26580,13 @@ function cleanupGuestSessionsForCapability(cap, factory) {
26472
26580
  }
26473
26581
 
26474
26582
  // src/permission/personal-capability.ts
26475
- var import_node_fs13 = __toESM(require("fs"), 1);
26476
- var import_node_path14 = __toESM(require("path"), 1);
26583
+ var import_node_fs14 = __toESM(require("fs"), 1);
26584
+ var import_node_path15 = __toESM(require("path"), 1);
26477
26585
  var import_node_crypto4 = __toESM(require("crypto"), 1);
26478
26586
  var PERSONAL_CAP_FILE_NAME = "personal-capability.json";
26479
26587
  var PERSONAL_CAP_DISPLAY_NAME = "personal";
26480
26588
  function personalCapFilePath(dataDir) {
26481
- return import_node_path14.default.join(dataDir, PERSONAL_CAP_FILE_NAME);
26589
+ return import_node_path15.default.join(dataDir, PERSONAL_CAP_FILE_NAME);
26482
26590
  }
26483
26591
  function loadOrCreatePersonalCapability(opts) {
26484
26592
  const file = personalCapFilePath(opts.dataDir);
@@ -26508,7 +26616,7 @@ function loadOrCreatePersonalCapability(opts) {
26508
26616
  function readFile(file) {
26509
26617
  let raw;
26510
26618
  try {
26511
- raw = import_node_fs13.default.readFileSync(file, "utf8");
26619
+ raw = import_node_fs14.default.readFileSync(file, "utf8");
26512
26620
  } catch (err) {
26513
26621
  const code = err?.code;
26514
26622
  if (code === "ENOENT") return null;
@@ -26529,10 +26637,10 @@ function readFile(file) {
26529
26637
  return { token: obj.token, capability: capParsed.data };
26530
26638
  }
26531
26639
  function writeFile(file, content) {
26532
- import_node_fs13.default.mkdirSync(import_node_path14.default.dirname(file), { recursive: true });
26533
- import_node_fs13.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
26640
+ import_node_fs14.default.mkdirSync(import_node_path15.default.dirname(file), { recursive: true });
26641
+ import_node_fs14.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
26534
26642
  try {
26535
- import_node_fs13.default.chmodSync(file, 384);
26643
+ import_node_fs14.default.chmodSync(file, 384);
26536
26644
  } catch {
26537
26645
  }
26538
26646
  }
@@ -26547,13 +26655,13 @@ function sha256Hex2(s) {
26547
26655
  }
26548
26656
 
26549
26657
  // src/inbox/inbox-store.ts
26550
- var fs18 = __toESM(require("fs"), 1);
26551
- var path20 = __toESM(require("path"), 1);
26658
+ var fs19 = __toESM(require("fs"), 1);
26659
+ var path21 = __toESM(require("path"), 1);
26552
26660
  var INBOX_SUBDIR = "inbox";
26553
26661
  var InboxStore = class {
26554
26662
  constructor(dataDir) {
26555
26663
  this.dataDir = dataDir;
26556
- fs18.mkdirSync(this.dirPath(), { recursive: true });
26664
+ fs19.mkdirSync(this.dirPath(), { recursive: true });
26557
26665
  }
26558
26666
  dataDir;
26559
26667
  /**
@@ -26565,7 +26673,7 @@ var InboxStore = class {
26565
26673
  const file = this.filePath(peerOwnerId);
26566
26674
  let raw;
26567
26675
  try {
26568
- raw = fs18.readFileSync(file, "utf8");
26676
+ raw = fs19.readFileSync(file, "utf8");
26569
26677
  } catch (err) {
26570
26678
  if (err?.code === "ENOENT") return [];
26571
26679
  return [];
@@ -26581,7 +26689,7 @@ var InboxStore = class {
26581
26689
  const dir = this.dirPath();
26582
26690
  let entries;
26583
26691
  try {
26584
- entries = fs18.readdirSync(dir);
26692
+ entries = fs19.readdirSync(dir);
26585
26693
  } catch (err) {
26586
26694
  if (err?.code === "ENOENT") return [];
26587
26695
  return [];
@@ -26597,9 +26705,9 @@ var InboxStore = class {
26597
26705
  if (existing.some((m2) => m2.id === message.id)) return;
26598
26706
  const file = this.filePath(message.peerOwnerId);
26599
26707
  const line = JSON.stringify(message) + "\n";
26600
- fs18.appendFileSync(file, line, { mode: 384 });
26708
+ fs19.appendFileSync(file, line, { mode: 384 });
26601
26709
  try {
26602
- fs18.chmodSync(file, 384);
26710
+ fs19.chmodSync(file, 384);
26603
26711
  } catch {
26604
26712
  }
26605
26713
  }
@@ -26629,7 +26737,7 @@ var InboxStore = class {
26629
26737
  removeByPeerOwnerId(peerOwnerId) {
26630
26738
  const file = this.filePath(peerOwnerId);
26631
26739
  try {
26632
- fs18.unlinkSync(file);
26740
+ fs19.unlinkSync(file);
26633
26741
  } catch (err) {
26634
26742
  if (err?.code === "ENOENT") return;
26635
26743
  }
@@ -26638,18 +26746,18 @@ var InboxStore = class {
26638
26746
  const file = this.filePath(peerOwnerId);
26639
26747
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
26640
26748
  const content = messages.map((m2) => JSON.stringify(m2)).join("\n") + (messages.length > 0 ? "\n" : "");
26641
- fs18.writeFileSync(tmp, content, { mode: 384 });
26642
- fs18.renameSync(tmp, file);
26749
+ fs19.writeFileSync(tmp, content, { mode: 384 });
26750
+ fs19.renameSync(tmp, file);
26643
26751
  try {
26644
- fs18.chmodSync(file, 384);
26752
+ fs19.chmodSync(file, 384);
26645
26753
  } catch {
26646
26754
  }
26647
26755
  }
26648
26756
  dirPath() {
26649
- return path20.join(this.dataDir, INBOX_SUBDIR);
26757
+ return path21.join(this.dataDir, INBOX_SUBDIR);
26650
26758
  }
26651
26759
  filePath(peerOwnerId) {
26652
- return path20.join(this.dirPath(), `${peerOwnerId}.jsonl`);
26760
+ return path21.join(this.dirPath(), `${peerOwnerId}.jsonl`);
26653
26761
  }
26654
26762
  };
26655
26763
  function parseAllLines(raw) {
@@ -26736,8 +26844,8 @@ var InboxManager = class {
26736
26844
  };
26737
26845
 
26738
26846
  // src/state/received-capability-store.ts
26739
- var fs19 = __toESM(require("fs"), 1);
26740
- var path21 = __toESM(require("path"), 1);
26847
+ var fs20 = __toESM(require("fs"), 1);
26848
+ var path22 = __toESM(require("path"), 1);
26741
26849
  var FILE_NAME = "received-capabilities.json";
26742
26850
  var ReceivedCapabilityStore = class {
26743
26851
  constructor(dataDir) {
@@ -26747,10 +26855,10 @@ var ReceivedCapabilityStore = class {
26747
26855
  caps = /* @__PURE__ */ new Map();
26748
26856
  load() {
26749
26857
  this.caps.clear();
26750
- const file = path21.join(this.dataDir, FILE_NAME);
26858
+ const file = path22.join(this.dataDir, FILE_NAME);
26751
26859
  let raw;
26752
26860
  try {
26753
- raw = fs19.readFileSync(file, "utf8");
26861
+ raw = fs20.readFileSync(file, "utf8");
26754
26862
  } catch (err) {
26755
26863
  if (err?.code !== "ENOENT") this.renameBak(file);
26756
26864
  return;
@@ -26787,20 +26895,20 @@ var ReceivedCapabilityStore = class {
26787
26895
  this.flush();
26788
26896
  }
26789
26897
  flush() {
26790
- const file = path21.join(this.dataDir, FILE_NAME);
26898
+ const file = path22.join(this.dataDir, FILE_NAME);
26791
26899
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
26792
26900
  const content = JSON.stringify(
26793
26901
  { receivedCaps: Array.from(this.caps.values()) },
26794
26902
  null,
26795
26903
  2
26796
26904
  );
26797
- fs19.mkdirSync(this.dataDir, { recursive: true });
26798
- fs19.writeFileSync(tmp, content, { mode: 384 });
26799
- fs19.renameSync(tmp, file);
26905
+ fs20.mkdirSync(this.dataDir, { recursive: true });
26906
+ fs20.writeFileSync(tmp, content, { mode: 384 });
26907
+ fs20.renameSync(tmp, file);
26800
26908
  }
26801
26909
  renameBak(file) {
26802
26910
  try {
26803
- fs19.renameSync(file, `${file}.bak`);
26911
+ fs20.renameSync(file, `${file}.bak`);
26804
26912
  } catch {
26805
26913
  }
26806
26914
  }
@@ -26898,61 +27006,61 @@ async function connectRemote(args) {
26898
27006
  }
26899
27007
 
26900
27008
  // src/migrations/2026-05-20-flatten-sessions.ts
26901
- var fs20 = __toESM(require("fs"), 1);
26902
- var path22 = __toESM(require("path"), 1);
27009
+ var fs21 = __toESM(require("fs"), 1);
27010
+ var path23 = __toESM(require("path"), 1);
26903
27011
  var MIGRATION_FLAG_NAME = ".migration.v1.done";
26904
27012
  function migrateFlattenSessions(opts) {
26905
27013
  const dataDir = opts.dataDir;
26906
27014
  const now = opts.now ?? Date.now;
26907
- const sessionsDir = path22.join(dataDir, "sessions");
26908
- const flagPath = path22.join(sessionsDir, MIGRATION_FLAG_NAME);
27015
+ const sessionsDir = path23.join(dataDir, "sessions");
27016
+ const flagPath = path23.join(sessionsDir, MIGRATION_FLAG_NAME);
26909
27017
  if (existsSync3(flagPath)) {
26910
27018
  return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
26911
27019
  }
26912
27020
  let movedBare = 0;
26913
27021
  let movedVmOwner = 0;
26914
27022
  let archivedListener = 0;
26915
- const defaultDir = path22.join(sessionsDir, "default");
27023
+ const defaultDir = path23.join(sessionsDir, "default");
26916
27024
  if (existsSync3(defaultDir)) {
26917
27025
  for (const entry of readdirSafe(defaultDir)) {
26918
27026
  if (!entry.endsWith(".json")) continue;
26919
- const src = path22.join(defaultDir, entry);
26920
- const dst = path22.join(sessionsDir, entry);
26921
- fs20.renameSync(src, dst);
27027
+ const src = path23.join(defaultDir, entry);
27028
+ const dst = path23.join(sessionsDir, entry);
27029
+ fs21.renameSync(src, dst);
26922
27030
  movedBare += 1;
26923
27031
  }
26924
27032
  rmdirIfEmpty(defaultDir);
26925
27033
  }
26926
27034
  for (const pid of readdirSafe(sessionsDir)) {
26927
- const personaDir = path22.join(sessionsDir, pid);
27035
+ const personaDir = path23.join(sessionsDir, pid);
26928
27036
  if (!isDir(personaDir)) continue;
26929
27037
  if (pid === "default") continue;
26930
- const ownerSrc = path22.join(personaDir, "owner");
27038
+ const ownerSrc = path23.join(personaDir, "owner");
26931
27039
  if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
26932
- const ownerDst = path22.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
26933
- fs20.mkdirSync(ownerDst, { recursive: true });
27040
+ const ownerDst = path23.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
27041
+ fs21.mkdirSync(ownerDst, { recursive: true });
26934
27042
  for (const file of readdirSafe(ownerSrc)) {
26935
27043
  if (!file.endsWith(".json")) continue;
26936
- fs20.renameSync(path22.join(ownerSrc, file), path22.join(ownerDst, file));
27044
+ fs21.renameSync(path23.join(ownerSrc, file), path23.join(ownerDst, file));
26937
27045
  movedVmOwner += 1;
26938
27046
  }
26939
27047
  rmdirIfEmpty(ownerSrc);
26940
27048
  }
26941
- const listenerSrc = path22.join(personaDir, "listener");
27049
+ const listenerSrc = path23.join(personaDir, "listener");
26942
27050
  if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
26943
- const archiveDst = path22.join(dataDir, ".legacy", `listener-${pid}`);
26944
- fs20.mkdirSync(archiveDst, { recursive: true });
27051
+ const archiveDst = path23.join(dataDir, ".legacy", `listener-${pid}`);
27052
+ fs21.mkdirSync(archiveDst, { recursive: true });
26945
27053
  for (const file of readdirSafe(listenerSrc)) {
26946
27054
  if (!file.endsWith(".json")) continue;
26947
- fs20.renameSync(path22.join(listenerSrc, file), path22.join(archiveDst, file));
27055
+ fs21.renameSync(path23.join(listenerSrc, file), path23.join(archiveDst, file));
26948
27056
  archivedListener += 1;
26949
27057
  }
26950
27058
  rmdirIfEmpty(listenerSrc);
26951
27059
  }
26952
27060
  rmdirIfEmpty(personaDir);
26953
27061
  }
26954
- fs20.mkdirSync(sessionsDir, { recursive: true });
26955
- fs20.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
27062
+ fs21.mkdirSync(sessionsDir, { recursive: true });
27063
+ fs21.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
26956
27064
  return {
26957
27065
  skipped: false,
26958
27066
  flagWritten: true,
@@ -26963,7 +27071,7 @@ function migrateFlattenSessions(opts) {
26963
27071
  }
26964
27072
  function existsSync3(p2) {
26965
27073
  try {
26966
- fs20.statSync(p2);
27074
+ fs21.statSync(p2);
26967
27075
  return true;
26968
27076
  } catch {
26969
27077
  return false;
@@ -26971,21 +27079,21 @@ function existsSync3(p2) {
26971
27079
  }
26972
27080
  function isDir(p2) {
26973
27081
  try {
26974
- return fs20.statSync(p2).isDirectory();
27082
+ return fs21.statSync(p2).isDirectory();
26975
27083
  } catch {
26976
27084
  return false;
26977
27085
  }
26978
27086
  }
26979
27087
  function readdirSafe(p2) {
26980
27088
  try {
26981
- return fs20.readdirSync(p2);
27089
+ return fs21.readdirSync(p2);
26982
27090
  } catch {
26983
27091
  return [];
26984
27092
  }
26985
27093
  }
26986
27094
  function rmdirIfEmpty(p2) {
26987
27095
  try {
26988
- fs20.rmdirSync(p2);
27096
+ fs21.rmdirSync(p2);
26989
27097
  } catch {
26990
27098
  }
26991
27099
  }
@@ -26995,7 +27103,7 @@ var import_node_fs15 = __toESM(require("fs"), 1);
26995
27103
  var import_node_path17 = __toESM(require("path"), 1);
26996
27104
 
26997
27105
  // src/attachment/mime.ts
26998
- var import_node_path15 = __toESM(require("path"), 1);
27106
+ var import_node_path16 = __toESM(require("path"), 1);
26999
27107
  var TEXT_PLAIN = "text/plain; charset=utf-8";
27000
27108
  var EXT_TO_NATIVE_MIME = {
27001
27109
  // 图片
@@ -27102,7 +27210,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
27102
27210
  ".mk"
27103
27211
  ]);
27104
27212
  function lookupMime(filePathOrName) {
27105
- const ext = import_node_path15.default.extname(filePathOrName).toLowerCase();
27213
+ const ext = import_node_path16.default.extname(filePathOrName).toLowerCase();
27106
27214
  if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
27107
27215
  if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
27108
27216
  return "application/octet-stream";
@@ -27170,118 +27278,7 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
27170
27278
  return { ok: true, absPath };
27171
27279
  }
27172
27280
 
27173
- // src/attachment/upload.ts
27174
- var import_node_fs14 = __toESM(require("fs"), 1);
27175
- var import_node_path16 = __toESM(require("path"), 1);
27176
- var import_node_crypto6 = __toESM(require("crypto"), 1);
27177
- var import_promises = require("stream/promises");
27178
- var UploadError = class extends Error {
27179
- constructor(code, message) {
27180
- super(message);
27181
- this.code = code;
27182
- this.name = "UploadError";
27183
- }
27184
- code;
27185
- };
27186
- var HASH_PREFIX_LEN = 16;
27187
- async function writeUploadedAttachment(args) {
27188
- const attachmentsRoot = import_node_path16.default.join(args.sessionDir, ".attachments");
27189
- try {
27190
- import_node_fs14.default.mkdirSync(attachmentsRoot, { recursive: true });
27191
- } catch (err) {
27192
- throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
27193
- }
27194
- const hasher = import_node_crypto6.default.createHash("sha256");
27195
- let actualSize = 0;
27196
- const tmpPath = import_node_path16.default.join(
27197
- attachmentsRoot,
27198
- `.upload-${process.pid}-${Date.now()}-${import_node_crypto6.default.randomBytes(4).toString("hex")}`
27199
- );
27200
- try {
27201
- await (0, import_promises.pipeline)(
27202
- args.body,
27203
- async function* (source) {
27204
- for await (const chunk of source) {
27205
- const buf = chunk;
27206
- actualSize += buf.length;
27207
- hasher.update(buf);
27208
- yield buf;
27209
- }
27210
- },
27211
- import_node_fs14.default.createWriteStream(tmpPath, { mode: 384 })
27212
- );
27213
- } catch (err) {
27214
- try {
27215
- import_node_fs14.default.unlinkSync(tmpPath);
27216
- } catch {
27217
- }
27218
- throw new UploadError("STORAGE_ERROR", `write failed: ${err.message}`);
27219
- }
27220
- if (actualSize !== args.contentLength) {
27221
- try {
27222
- import_node_fs14.default.unlinkSync(tmpPath);
27223
- } catch {
27224
- }
27225
- throw new UploadError(
27226
- "SIZE_MISMATCH",
27227
- `expected ${args.contentLength} bytes, got ${actualSize}`
27228
- );
27229
- }
27230
- const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
27231
- const hashDir = import_node_path16.default.join(attachmentsRoot, attachmentId);
27232
- let finalFileName;
27233
- let hashDirExists = false;
27234
- try {
27235
- hashDirExists = import_node_fs14.default.statSync(hashDir).isDirectory();
27236
- } catch {
27237
- }
27238
- if (hashDirExists) {
27239
- const existing = import_node_fs14.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
27240
- finalFileName = existing[0] ?? args.fileName;
27241
- try {
27242
- import_node_fs14.default.unlinkSync(tmpPath);
27243
- } catch {
27244
- }
27245
- } else {
27246
- try {
27247
- import_node_fs14.default.mkdirSync(hashDir, { recursive: true });
27248
- finalFileName = args.fileName;
27249
- import_node_fs14.default.renameSync(tmpPath, import_node_path16.default.join(hashDir, finalFileName));
27250
- } catch (err) {
27251
- try {
27252
- import_node_fs14.default.unlinkSync(tmpPath);
27253
- } catch {
27254
- }
27255
- throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
27256
- }
27257
- }
27258
- const absPath = import_node_path16.default.join(hashDir, finalFileName);
27259
- const relPath = import_node_path16.default.relative(args.sessionDir, absPath);
27260
- args.groupFileStore.upsert(args.scope, args.sessionId, {
27261
- relPath: absPath,
27262
- // 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
27263
- from: "user-upload",
27264
- label: args.fileName,
27265
- size: actualSize,
27266
- mime: args.mimeType
27267
- });
27268
- return { attachmentId, absPath, relPath, sizeBytes: actualSize, mimeType: args.mimeType };
27269
- }
27270
-
27271
27281
  // src/transport/http-router.ts
27272
- var UPLOAD_MIME_ALLOW = /* @__PURE__ */ new Set([
27273
- "image/png",
27274
- "image/jpeg",
27275
- "image/gif",
27276
- "image/webp",
27277
- "application/pdf"
27278
- ]);
27279
- var UPLOAD_MIME_ALLOW_PREFIX = ["text/"];
27280
- var UPLOAD_MAX_BYTES = 25 * 1024 * 1024;
27281
- function isUploadMimeAllowed(mime) {
27282
- if (UPLOAD_MIME_ALLOW.has(mime)) return true;
27283
- return UPLOAD_MIME_ALLOW_PREFIX.some((p2) => mime.startsWith(p2));
27284
- }
27285
27282
  function createHttpRouter(deps) {
27286
27283
  return async (req, res) => {
27287
27284
  const url = parseUrl(req.url);
@@ -27295,7 +27292,7 @@ function createHttpRouter(deps) {
27295
27292
  });
27296
27293
  return true;
27297
27294
  }
27298
- if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/") && url.pathname !== "/attachments/upload") {
27295
+ if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/")) {
27299
27296
  return false;
27300
27297
  }
27301
27298
  if (url.pathname.startsWith("/files/") && req.method === "GET") {
@@ -27335,99 +27332,6 @@ function createHttpRouter(deps) {
27335
27332
  sendJson(res, 401, { code: "UNAUTHORIZED", message: "missing or invalid bearer token" });
27336
27333
  return true;
27337
27334
  }
27338
- if (url.pathname === "/attachments/upload" && req.method === "POST") {
27339
- if (ctx.role !== "owner") {
27340
- sendJson(res, 403, { code: "FORBIDDEN", message: "owner only" });
27341
- return true;
27342
- }
27343
- const up = deps.attachmentUpload;
27344
- if (!up) {
27345
- sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "attachment upload not wired" });
27346
- return true;
27347
- }
27348
- const sessionId = url.searchParams.get("sessionId");
27349
- const fileName = url.searchParams.get("fileName");
27350
- const mimeType = url.searchParams.get("mimeType");
27351
- const contentLengthRaw = req.headers["content-length"];
27352
- if (!sessionId || !fileName || !mimeType || !contentLengthRaw) {
27353
- sendJson(res, 400, {
27354
- code: "INVALID_PARAM",
27355
- message: "missing sessionId/fileName/mimeType/Content-Length"
27356
- });
27357
- return true;
27358
- }
27359
- const contentLength = Number.parseInt(contentLengthRaw, 10);
27360
- if (!Number.isFinite(contentLength) || contentLength < 0) {
27361
- sendJson(res, 400, { code: "INVALID_PARAM", message: "malformed Content-Length" });
27362
- return true;
27363
- }
27364
- if (contentLength > UPLOAD_MAX_BYTES) {
27365
- sendJson(res, 413, {
27366
- code: "FILE_TOO_LARGE",
27367
- message: `max ${UPLOAD_MAX_BYTES} bytes`
27368
- });
27369
- return true;
27370
- }
27371
- if (!isUploadMimeAllowed(mimeType)) {
27372
- sendJson(res, 400, {
27373
- code: "UNSUPPORTED_MIME",
27374
- message: `mime ${mimeType} not allowed`
27375
- });
27376
- return true;
27377
- }
27378
- const sessionDir = up.getSessionDir(sessionId);
27379
- const scope = up.getSessionScope(sessionId);
27380
- if (!sessionDir || !scope) {
27381
- sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sessionId} not found` });
27382
- return true;
27383
- }
27384
- const httpBaseUrl = up.getHttpBaseUrl();
27385
- if (!httpBaseUrl) {
27386
- sendJson(res, 503, {
27387
- code: "TUNNEL_NOT_READY",
27388
- message: "httpBaseUrl unavailable"
27389
- });
27390
- return true;
27391
- }
27392
- const secret = deps.getSignSecret?.();
27393
- if (!secret) {
27394
- sendJson(res, 501, {
27395
- code: "NOT_IMPLEMENTED",
27396
- message: "sign secret unavailable (noAuth?)"
27397
- });
27398
- return true;
27399
- }
27400
- try {
27401
- const result = await writeUploadedAttachment({
27402
- sessionDir,
27403
- sessionId,
27404
- scope,
27405
- fileName,
27406
- mimeType,
27407
- contentLength,
27408
- body: req,
27409
- groupFileStore: up.groupFileStore
27410
- });
27411
- const parts = signUrlParts(secret, result.absPath, null);
27412
- const fullUrl = buildSignedFileUrl(httpBaseUrl, parts);
27413
- sendJson(res, 200, {
27414
- attachmentId: result.attachmentId,
27415
- url: fullUrl,
27416
- relPath: result.relPath,
27417
- sizeBytes: result.sizeBytes,
27418
- mimeType: result.mimeType
27419
- });
27420
- } catch (err) {
27421
- if (err instanceof UploadError) {
27422
- const status = err.code === "SIZE_MISMATCH" ? 400 : 500;
27423
- sendJson(res, status, { code: err.code, message: err.message });
27424
- } else {
27425
- deps.logger?.warn("attachment upload failed", { err: err.message });
27426
- sendJson(res, 500, { code: "STORAGE_ERROR", message: err.message });
27427
- }
27428
- }
27429
- return true;
27430
- }
27431
27335
  const sessionFilesMatch = url.pathname.match(/^\/session\/([^/]+)\/files$/);
27432
27336
  if (sessionFilesMatch && req.method === "GET") {
27433
27337
  const sid = sessionFilesMatch[1];
@@ -27509,78 +27413,10 @@ function streamFile(res, absPath, logger) {
27509
27413
  stream.pipe(res);
27510
27414
  }
27511
27415
 
27512
- // src/attachment/gc.ts
27416
+ // src/attachment/group.ts
27513
27417
  var import_node_fs16 = __toESM(require("fs"), 1);
27514
27418
  var import_node_path18 = __toESM(require("path"), 1);
27515
- var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
27516
- function runAttachmentGc(args) {
27517
- const now = (args.now ?? Date.now)();
27518
- const ttlMs = args.ttlMs ?? DEFAULT_TTL_MS;
27519
- const liveAbs = /* @__PURE__ */ new Set();
27520
- for (const { scope, sessionId } of args.sessionScopes) {
27521
- for (const entry of args.groupFileStore.list(scope, sessionId)) {
27522
- if (entry.stale) continue;
27523
- if (import_node_path18.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
27524
- }
27525
- }
27526
- for (const { scope, sessionId } of args.sessionScopes) {
27527
- const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path18.default.join(
27528
- args.dataDir,
27529
- "sessions",
27530
- ...scopeSubPath(scope).map(safeFileName),
27531
- safeFileName(sessionId)
27532
- );
27533
- const attRoot = import_node_path18.default.join(sessionDir, ".attachments");
27534
- let hashDirs;
27535
- try {
27536
- hashDirs = import_node_fs16.default.readdirSync(attRoot);
27537
- } catch (err) {
27538
- if (err.code === "ENOENT") continue;
27539
- args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
27540
- continue;
27541
- }
27542
- for (const hashDir of hashDirs) {
27543
- const hashDirAbs = import_node_path18.default.join(attRoot, hashDir);
27544
- let files;
27545
- try {
27546
- files = import_node_fs16.default.readdirSync(hashDirAbs);
27547
- } catch {
27548
- continue;
27549
- }
27550
- for (const name of files) {
27551
- const file = import_node_path18.default.join(hashDirAbs, name);
27552
- let stat;
27553
- try {
27554
- stat = import_node_fs16.default.statSync(file);
27555
- } catch {
27556
- continue;
27557
- }
27558
- if (!stat.isFile()) continue;
27559
- const age = now - stat.mtimeMs;
27560
- if (age < ttlMs) continue;
27561
- if (liveAbs.has(file)) continue;
27562
- try {
27563
- import_node_fs16.default.unlinkSync(file);
27564
- } catch (err) {
27565
- args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
27566
- }
27567
- }
27568
- try {
27569
- if (import_node_fs16.default.readdirSync(hashDirAbs).length === 0) import_node_fs16.default.rmdirSync(hashDirAbs);
27570
- } catch {
27571
- }
27572
- }
27573
- try {
27574
- if (import_node_fs16.default.readdirSync(attRoot).length === 0) import_node_fs16.default.rmdirSync(attRoot);
27575
- } catch {
27576
- }
27577
- }
27578
- }
27579
-
27580
- // src/attachment/group.ts
27581
- var import_node_fs17 = __toESM(require("fs"), 1);
27582
- var import_node_path19 = __toESM(require("path"), 1);
27583
- var import_node_crypto7 = __toESM(require("crypto"), 1);
27419
+ var import_node_crypto6 = __toESM(require("crypto"), 1);
27584
27420
  init_protocol();
27585
27421
  var GroupFileStore = class {
27586
27422
  dataDir;
@@ -27591,11 +27427,11 @@ var GroupFileStore = class {
27591
27427
  this.logger = opts.logger;
27592
27428
  }
27593
27429
  rootForScope(scope) {
27594
- return import_node_path19.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
27430
+ return import_node_path18.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
27595
27431
  }
27596
27432
  /** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
27597
27433
  filePath(scope, sessionId) {
27598
- return import_node_path19.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
27434
+ return import_node_path18.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
27599
27435
  }
27600
27436
  cacheKey(scope, sessionId) {
27601
27437
  return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
@@ -27604,7 +27440,7 @@ var GroupFileStore = class {
27604
27440
  readFile(scope, sessionId) {
27605
27441
  const file = this.filePath(scope, sessionId);
27606
27442
  try {
27607
- const raw = import_node_fs17.default.readFileSync(file, "utf8");
27443
+ const raw = import_node_fs16.default.readFileSync(file, "utf8");
27608
27444
  const parsed = JSON.parse(raw);
27609
27445
  if (!Array.isArray(parsed)) {
27610
27446
  this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
@@ -27630,10 +27466,10 @@ var GroupFileStore = class {
27630
27466
  }
27631
27467
  writeFile(scope, sessionId, entries) {
27632
27468
  const file = this.filePath(scope, sessionId);
27633
- import_node_fs17.default.mkdirSync(import_node_path19.default.dirname(file), { recursive: true });
27469
+ import_node_fs16.default.mkdirSync(import_node_path18.default.dirname(file), { recursive: true });
27634
27470
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
27635
- import_node_fs17.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
27636
- import_node_fs17.default.renameSync(tmp, file);
27471
+ import_node_fs16.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
27472
+ import_node_fs16.default.renameSync(tmp, file);
27637
27473
  }
27638
27474
  /** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
27639
27475
  list(scope, sessionId) {
@@ -27669,7 +27505,7 @@ var GroupFileStore = class {
27669
27505
  entries[idx] = next;
27670
27506
  } else {
27671
27507
  next = {
27672
- id: `gf-${import_node_crypto7.default.randomBytes(6).toString("base64url")}`,
27508
+ id: `gf-${import_node_crypto6.default.randomBytes(6).toString("base64url")}`,
27673
27509
  relPath: input.relPath,
27674
27510
  from: input.from,
27675
27511
  label: input.label,
@@ -27719,10 +27555,10 @@ var GroupFileStore = class {
27719
27555
  };
27720
27556
 
27721
27557
  // src/discovery/state-file.ts
27722
- var import_node_fs18 = __toESM(require("fs"), 1);
27723
- var import_node_path20 = __toESM(require("path"), 1);
27558
+ var import_node_fs17 = __toESM(require("fs"), 1);
27559
+ var import_node_path19 = __toESM(require("path"), 1);
27724
27560
  function defaultStateFilePath(dataDir) {
27725
- return import_node_path20.default.join(dataDir, "state.json");
27561
+ return import_node_path19.default.join(dataDir, "state.json");
27726
27562
  }
27727
27563
  function isPidAlive(pid) {
27728
27564
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -27744,7 +27580,7 @@ var StateFileManager = class {
27744
27580
  }
27745
27581
  read() {
27746
27582
  try {
27747
- const raw = import_node_fs18.default.readFileSync(this.file, "utf8");
27583
+ const raw = import_node_fs17.default.readFileSync(this.file, "utf8");
27748
27584
  const parsed = JSON.parse(raw);
27749
27585
  return parsed;
27750
27586
  } catch {
@@ -27758,34 +27594,34 @@ var StateFileManager = class {
27758
27594
  return { status: "stale", existing };
27759
27595
  }
27760
27596
  write(state) {
27761
- import_node_fs18.default.mkdirSync(import_node_path20.default.dirname(this.file), { recursive: true });
27597
+ import_node_fs17.default.mkdirSync(import_node_path19.default.dirname(this.file), { recursive: true });
27762
27598
  const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
27763
- import_node_fs18.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
27764
- import_node_fs18.default.renameSync(tmp, this.file);
27599
+ import_node_fs17.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
27600
+ import_node_fs17.default.renameSync(tmp, this.file);
27765
27601
  if (process.platform !== "win32") {
27766
27602
  try {
27767
- import_node_fs18.default.chmodSync(this.file, 384);
27603
+ import_node_fs17.default.chmodSync(this.file, 384);
27768
27604
  } catch {
27769
27605
  }
27770
27606
  }
27771
27607
  }
27772
27608
  delete() {
27773
27609
  try {
27774
- import_node_fs18.default.unlinkSync(this.file);
27610
+ import_node_fs17.default.unlinkSync(this.file);
27775
27611
  } catch {
27776
27612
  }
27777
27613
  }
27778
27614
  };
27779
27615
 
27780
27616
  // src/tunnel/tunnel-manager.ts
27781
- var import_node_fs22 = __toESM(require("fs"), 1);
27782
- var import_node_path24 = __toESM(require("path"), 1);
27783
- var import_node_crypto8 = __toESM(require("crypto"), 1);
27617
+ var import_node_fs21 = __toESM(require("fs"), 1);
27618
+ var import_node_path23 = __toESM(require("path"), 1);
27619
+ var import_node_crypto7 = __toESM(require("crypto"), 1);
27784
27620
  var import_node_child_process5 = require("child_process");
27785
27621
 
27786
27622
  // src/tunnel/tunnel-store.ts
27787
- var import_node_fs19 = __toESM(require("fs"), 1);
27788
- var import_node_path21 = __toESM(require("path"), 1);
27623
+ var import_node_fs18 = __toESM(require("fs"), 1);
27624
+ var import_node_path20 = __toESM(require("path"), 1);
27789
27625
  var TunnelStore = class {
27790
27626
  constructor(filePath) {
27791
27627
  this.filePath = filePath;
@@ -27793,7 +27629,7 @@ var TunnelStore = class {
27793
27629
  filePath;
27794
27630
  async get() {
27795
27631
  try {
27796
- const raw = await import_node_fs19.default.promises.readFile(this.filePath, "utf8");
27632
+ const raw = await import_node_fs18.default.promises.readFile(this.filePath, "utf8");
27797
27633
  const obj = JSON.parse(raw);
27798
27634
  if (!isPersistedTunnel(obj)) return null;
27799
27635
  return obj;
@@ -27804,22 +27640,22 @@ var TunnelStore = class {
27804
27640
  }
27805
27641
  }
27806
27642
  async set(v2) {
27807
- const dir = import_node_path21.default.dirname(this.filePath);
27808
- await import_node_fs19.default.promises.mkdir(dir, { recursive: true });
27643
+ const dir = import_node_path20.default.dirname(this.filePath);
27644
+ await import_node_fs18.default.promises.mkdir(dir, { recursive: true });
27809
27645
  const data = JSON.stringify(v2, null, 2);
27810
27646
  const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
27811
- await import_node_fs19.default.promises.writeFile(tmp, data, { mode: 384 });
27647
+ await import_node_fs18.default.promises.writeFile(tmp, data, { mode: 384 });
27812
27648
  if (process.platform !== "win32") {
27813
27649
  try {
27814
- await import_node_fs19.default.promises.chmod(tmp, 384);
27650
+ await import_node_fs18.default.promises.chmod(tmp, 384);
27815
27651
  } catch {
27816
27652
  }
27817
27653
  }
27818
- await import_node_fs19.default.promises.rename(tmp, this.filePath);
27654
+ await import_node_fs18.default.promises.rename(tmp, this.filePath);
27819
27655
  }
27820
27656
  async clear() {
27821
27657
  try {
27822
- await import_node_fs19.default.promises.unlink(this.filePath);
27658
+ await import_node_fs18.default.promises.unlink(this.filePath);
27823
27659
  } catch (err) {
27824
27660
  const code = err?.code;
27825
27661
  if (code !== "ENOENT") throw err;
@@ -27914,12 +27750,12 @@ function escape(v2) {
27914
27750
  }
27915
27751
 
27916
27752
  // src/tunnel/frpc-binary.ts
27917
- var import_node_fs20 = __toESM(require("fs"), 1);
27918
- var import_node_os9 = __toESM(require("os"), 1);
27919
- var import_node_path22 = __toESM(require("path"), 1);
27753
+ var import_node_fs19 = __toESM(require("fs"), 1);
27754
+ var import_node_os10 = __toESM(require("os"), 1);
27755
+ var import_node_path21 = __toESM(require("path"), 1);
27920
27756
  var import_node_child_process3 = require("child_process");
27921
27757
  var import_node_stream2 = require("stream");
27922
- var import_promises2 = require("stream/promises");
27758
+ var import_promises = require("stream/promises");
27923
27759
  var FRPC_VERSION = "0.68.0";
27924
27760
  var UnsupportedPlatformError = class extends Error {
27925
27761
  constructor(platform, arch) {
@@ -27948,20 +27784,20 @@ function frpcDownloadUrl(version2, p2) {
27948
27784
  }
27949
27785
  async function ensureFrpcBinary(opts) {
27950
27786
  if (opts.override) {
27951
- if (!import_node_fs20.default.existsSync(opts.override)) {
27787
+ if (!import_node_fs19.default.existsSync(opts.override)) {
27952
27788
  throw new Error(`frpc binary not found at override path: ${opts.override}`);
27953
27789
  }
27954
27790
  return opts.override;
27955
27791
  }
27956
27792
  const version2 = opts.version ?? FRPC_VERSION;
27957
27793
  const platform = opts.platform ?? detectPlatform();
27958
- const binDir = import_node_path22.default.join(opts.dataDir, "bin");
27959
- import_node_fs20.default.mkdirSync(binDir, { recursive: true });
27794
+ const binDir = import_node_path21.default.join(opts.dataDir, "bin");
27795
+ import_node_fs19.default.mkdirSync(binDir, { recursive: true });
27960
27796
  cleanupStaleArtifacts(binDir);
27961
- const stableBin = import_node_path22.default.join(binDir, "frpc");
27962
- if (import_node_fs20.default.existsSync(stableBin)) return stableBin;
27797
+ const stableBin = import_node_path21.default.join(binDir, "frpc");
27798
+ if (import_node_fs19.default.existsSync(stableBin)) return stableBin;
27963
27799
  const partialBin = `${stableBin}.partial`;
27964
- const tarballPath = import_node_path22.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
27800
+ const tarballPath = import_node_path21.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
27965
27801
  try {
27966
27802
  const url = frpcDownloadUrl(version2, platform);
27967
27803
  await downloadToFile(url, tarballPath, opts.fetchImpl);
@@ -27970,8 +27806,8 @@ async function ensureFrpcBinary(opts) {
27970
27806
  } else {
27971
27807
  await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
27972
27808
  }
27973
- import_node_fs20.default.chmodSync(partialBin, 493);
27974
- import_node_fs20.default.renameSync(partialBin, stableBin);
27809
+ import_node_fs19.default.chmodSync(partialBin, 493);
27810
+ import_node_fs19.default.renameSync(partialBin, stableBin);
27975
27811
  } finally {
27976
27812
  safeUnlink(tarballPath);
27977
27813
  safeUnlink(partialBin);
@@ -27981,15 +27817,15 @@ async function ensureFrpcBinary(opts) {
27981
27817
  function cleanupStaleArtifacts(binDir) {
27982
27818
  let entries;
27983
27819
  try {
27984
- entries = import_node_fs20.default.readdirSync(binDir);
27820
+ entries = import_node_fs19.default.readdirSync(binDir);
27985
27821
  } catch {
27986
27822
  return;
27987
27823
  }
27988
27824
  for (const name of entries) {
27989
27825
  if (name.endsWith(".partial") || name.startsWith("extract-")) {
27990
- const full = import_node_path22.default.join(binDir, name);
27826
+ const full = import_node_path21.default.join(binDir, name);
27991
27827
  try {
27992
- import_node_fs20.default.rmSync(full, { recursive: true, force: true });
27828
+ import_node_fs19.default.rmSync(full, { recursive: true, force: true });
27993
27829
  } catch {
27994
27830
  }
27995
27831
  }
@@ -27997,7 +27833,7 @@ function cleanupStaleArtifacts(binDir) {
27997
27833
  }
27998
27834
  function safeUnlink(p2) {
27999
27835
  try {
28000
- import_node_fs20.default.unlinkSync(p2);
27836
+ import_node_fs19.default.unlinkSync(p2);
28001
27837
  } catch {
28002
27838
  }
28003
27839
  }
@@ -28008,13 +27844,13 @@ async function downloadToFile(url, dest, fetchImpl) {
28008
27844
  if (!res.ok || !res.body) {
28009
27845
  throw new Error(`download failed: ${res.status} ${res.statusText}`);
28010
27846
  }
28011
- const out = import_node_fs20.default.createWriteStream(dest);
27847
+ const out = import_node_fs19.default.createWriteStream(dest);
28012
27848
  const nodeStream = import_node_stream2.Readable.fromWeb(res.body);
28013
- await (0, import_promises2.pipeline)(nodeStream, out);
27849
+ await (0, import_promises.pipeline)(nodeStream, out);
28014
27850
  }
28015
27851
  async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
28016
- const work = import_node_path22.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
28017
- import_node_fs20.default.mkdirSync(work, { recursive: true });
27852
+ const work = import_node_path21.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
27853
+ import_node_fs19.default.mkdirSync(work, { recursive: true });
28018
27854
  try {
28019
27855
  await new Promise((resolve6, reject) => {
28020
27856
  const proc = (0, import_node_child_process3.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
@@ -28022,32 +27858,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
28022
27858
  proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
28023
27859
  });
28024
27860
  const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
28025
- const src = import_node_path22.default.join(work, dirName, "frpc");
28026
- if (!import_node_fs20.default.existsSync(src)) {
27861
+ const src = import_node_path21.default.join(work, dirName, "frpc");
27862
+ if (!import_node_fs19.default.existsSync(src)) {
28027
27863
  throw new Error(`frpc not found inside tarball at ${src}`);
28028
27864
  }
28029
- import_node_fs20.default.copyFileSync(src, destBin);
27865
+ import_node_fs19.default.copyFileSync(src, destBin);
28030
27866
  } finally {
28031
- import_node_fs20.default.rmSync(work, { recursive: true, force: true });
27867
+ import_node_fs19.default.rmSync(work, { recursive: true, force: true });
28032
27868
  }
28033
27869
  }
28034
27870
 
28035
27871
  // src/tunnel/frpc-process.ts
28036
- var import_node_fs21 = __toESM(require("fs"), 1);
28037
- var import_node_path23 = __toESM(require("path"), 1);
27872
+ var import_node_fs20 = __toESM(require("fs"), 1);
27873
+ var import_node_path22 = __toESM(require("path"), 1);
28038
27874
  var import_node_child_process4 = require("child_process");
28039
27875
  function frpcPidFilePath(dataDir) {
28040
- return import_node_path23.default.join(dataDir, "frpc.pid");
27876
+ return import_node_path22.default.join(dataDir, "frpc.pid");
28041
27877
  }
28042
27878
  function writeFrpcPid(dataDir, pid) {
28043
27879
  try {
28044
- import_node_fs21.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
27880
+ import_node_fs20.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
28045
27881
  } catch {
28046
27882
  }
28047
27883
  }
28048
27884
  function clearFrpcPid(dataDir) {
28049
27885
  try {
28050
- import_node_fs21.default.unlinkSync(frpcPidFilePath(dataDir));
27886
+ import_node_fs20.default.unlinkSync(frpcPidFilePath(dataDir));
28051
27887
  } catch {
28052
27888
  }
28053
27889
  }
@@ -28063,7 +27899,7 @@ function defaultIsPidAlive(pid) {
28063
27899
  }
28064
27900
  function defaultReadPidFile(file) {
28065
27901
  try {
28066
- return import_node_fs21.default.readFileSync(file, "utf8");
27902
+ return import_node_fs20.default.readFileSync(file, "utf8");
28067
27903
  } catch {
28068
27904
  return null;
28069
27905
  }
@@ -28079,7 +27915,7 @@ function defaultSleep(ms) {
28079
27915
  }
28080
27916
  async function killStaleFrpc(deps) {
28081
27917
  const pidFile = frpcPidFilePath(deps.dataDir);
28082
- const tomlPath = import_node_path23.default.join(deps.dataDir, "frpc.toml");
27918
+ const tomlPath = import_node_path22.default.join(deps.dataDir, "frpc.toml");
28083
27919
  const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
28084
27920
  const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
28085
27921
  const killPid = deps.killPidImpl ?? defaultKillPid;
@@ -28103,7 +27939,7 @@ async function killStaleFrpc(deps) {
28103
27939
  }
28104
27940
  if (victims.size === 0) {
28105
27941
  try {
28106
- import_node_fs21.default.unlinkSync(pidFile);
27942
+ import_node_fs20.default.unlinkSync(pidFile);
28107
27943
  } catch {
28108
27944
  }
28109
27945
  return;
@@ -28114,7 +27950,7 @@ async function killStaleFrpc(deps) {
28114
27950
  }
28115
27951
  await sleep(deps.reapWaitMs ?? 300);
28116
27952
  try {
28117
- import_node_fs21.default.unlinkSync(pidFile);
27953
+ import_node_fs20.default.unlinkSync(pidFile);
28118
27954
  } catch {
28119
27955
  }
28120
27956
  }
@@ -28151,7 +27987,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
28151
27987
  var TunnelManager = class {
28152
27988
  constructor(deps) {
28153
27989
  this.deps = deps;
28154
- this.store = deps.store ?? new TunnelStore(import_node_path24.default.join(deps.dataDir, "tunnel.json"));
27990
+ this.store = deps.store ?? new TunnelStore(import_node_path23.default.join(deps.dataDir, "tunnel.json"));
28155
27991
  this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
28156
27992
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
28157
27993
  }
@@ -28278,8 +28114,8 @@ var TunnelManager = class {
28278
28114
  dataDir: this.deps.dataDir,
28279
28115
  override: this.deps.frpcBinaryOverride ?? void 0
28280
28116
  });
28281
- const tomlPath = import_node_path24.default.join(this.deps.dataDir, "frpc.toml");
28282
- const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto8.default.randomBytes(3).toString("hex")}`;
28117
+ const tomlPath = import_node_path23.default.join(this.deps.dataDir, "frpc.toml");
28118
+ const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto7.default.randomBytes(3).toString("hex")}`;
28283
28119
  const toml = buildFrpcToml({
28284
28120
  serverAddr: t.frpsHost,
28285
28121
  serverPort: t.frpsPort,
@@ -28289,12 +28125,12 @@ var TunnelManager = class {
28289
28125
  localPort,
28290
28126
  logLevel: "info"
28291
28127
  });
28292
- await import_node_fs22.default.promises.writeFile(tomlPath, toml, { mode: 384 });
28128
+ await import_node_fs21.default.promises.writeFile(tomlPath, toml, { mode: 384 });
28293
28129
  const proc = (this.deps.spawnImpl ?? import_node_child_process5.spawn)(frpcBin, ["-c", tomlPath], {
28294
28130
  stdio: ["ignore", "pipe", "pipe"]
28295
28131
  });
28296
- const logFilePath = import_node_path24.default.join(this.deps.dataDir, "frpc.log");
28297
- const logStream = import_node_fs22.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
28132
+ const logFilePath = import_node_path23.default.join(this.deps.dataDir, "frpc.log");
28133
+ const logStream = import_node_fs21.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
28298
28134
  logStream.on("error", () => {
28299
28135
  });
28300
28136
  const tee = (chunk) => {
@@ -28376,23 +28212,23 @@ async function waitForFrpcReady(proc, timeoutMs) {
28376
28212
  }
28377
28213
 
28378
28214
  // src/tunnel/device-key.ts
28379
- var import_node_os10 = __toESM(require("os"), 1);
28380
- var import_node_crypto9 = __toESM(require("crypto"), 1);
28215
+ var import_node_os11 = __toESM(require("os"), 1);
28216
+ var import_node_crypto8 = __toESM(require("crypto"), 1);
28381
28217
  var DERIVE_SALT = "clawd-tunnel-device-v1";
28382
28218
  function deriveStableDeviceKey(opts = {}) {
28383
- const hostname = opts.hostname ?? import_node_os10.default.hostname();
28384
- const uid = opts.uid ?? (typeof import_node_os10.default.userInfo === "function" ? import_node_os10.default.userInfo().uid : 0);
28219
+ const hostname = opts.hostname ?? import_node_os11.default.hostname();
28220
+ const uid = opts.uid ?? (typeof import_node_os11.default.userInfo === "function" ? import_node_os11.default.userInfo().uid : 0);
28385
28221
  const input = `${hostname}::${uid}`;
28386
- return import_node_crypto9.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
28222
+ return import_node_crypto8.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
28387
28223
  }
28388
28224
 
28389
28225
  // src/auth-store.ts
28390
- var import_node_fs23 = __toESM(require("fs"), 1);
28391
- var import_node_path25 = __toESM(require("path"), 1);
28392
- var import_node_crypto10 = __toESM(require("crypto"), 1);
28226
+ var import_node_fs22 = __toESM(require("fs"), 1);
28227
+ var import_node_path24 = __toESM(require("path"), 1);
28228
+ var import_node_crypto9 = __toESM(require("crypto"), 1);
28393
28229
  var AUTH_FILE_NAME = "auth.json";
28394
28230
  function authFilePath(dataDir) {
28395
- return import_node_path25.default.join(dataDir, AUTH_FILE_NAME);
28231
+ return import_node_path24.default.join(dataDir, AUTH_FILE_NAME);
28396
28232
  }
28397
28233
  function loadOrCreateAuthFile(opts) {
28398
28234
  const file = authFilePath(opts.dataDir);
@@ -28417,14 +28253,14 @@ function loadOrCreateAuthFile(opts) {
28417
28253
  return next;
28418
28254
  }
28419
28255
  function defaultGenerateToken2() {
28420
- return import_node_crypto10.default.randomBytes(32).toString("base64url");
28256
+ return import_node_crypto9.default.randomBytes(32).toString("base64url");
28421
28257
  }
28422
28258
  function defaultGenerateOwnerPrincipalId() {
28423
- return `owner-${import_node_crypto10.default.randomUUID()}`;
28259
+ return `owner-${import_node_crypto9.default.randomUUID()}`;
28424
28260
  }
28425
28261
  function readAuthFile(file) {
28426
28262
  try {
28427
- const raw = import_node_fs23.default.readFileSync(file, "utf8");
28263
+ const raw = import_node_fs22.default.readFileSync(file, "utf8");
28428
28264
  const parsed = JSON.parse(raw);
28429
28265
  if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
28430
28266
  return null;
@@ -28442,25 +28278,25 @@ function readAuthFile(file) {
28442
28278
  }
28443
28279
  }
28444
28280
  function writeAuthFile(file, content) {
28445
- import_node_fs23.default.mkdirSync(import_node_path25.default.dirname(file), { recursive: true });
28446
- import_node_fs23.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
28281
+ import_node_fs22.default.mkdirSync(import_node_path24.default.dirname(file), { recursive: true });
28282
+ import_node_fs22.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
28447
28283
  try {
28448
- import_node_fs23.default.chmodSync(file, 384);
28284
+ import_node_fs22.default.chmodSync(file, 384);
28449
28285
  } catch {
28450
28286
  }
28451
28287
  }
28452
28288
 
28453
28289
  // src/owner-profile.ts
28454
- var import_node_fs24 = __toESM(require("fs"), 1);
28455
- var import_node_os11 = __toESM(require("os"), 1);
28456
- var import_node_path26 = __toESM(require("path"), 1);
28290
+ var import_node_fs23 = __toESM(require("fs"), 1);
28291
+ var import_node_os12 = __toESM(require("os"), 1);
28292
+ var import_node_path25 = __toESM(require("path"), 1);
28457
28293
  var PROFILE_FILENAME = "profile.json";
28458
28294
  function loadOwnerDisplayName(dataDir) {
28459
- const fallback = import_node_os11.default.userInfo().username;
28460
- const profilePath = import_node_path26.default.join(dataDir, PROFILE_FILENAME);
28295
+ const fallback = import_node_os12.default.userInfo().username;
28296
+ const profilePath = import_node_path25.default.join(dataDir, PROFILE_FILENAME);
28461
28297
  let raw;
28462
28298
  try {
28463
- raw = import_node_fs24.default.readFileSync(profilePath, "utf8");
28299
+ raw = import_node_fs23.default.readFileSync(profilePath, "utf8");
28464
28300
  } catch {
28465
28301
  return fallback;
28466
28302
  }
@@ -28489,12 +28325,12 @@ init_protocol();
28489
28325
  init_protocol();
28490
28326
 
28491
28327
  // src/session/fork.ts
28492
- var import_node_fs25 = __toESM(require("fs"), 1);
28493
- var import_node_os12 = __toESM(require("os"), 1);
28494
- var import_node_path27 = __toESM(require("path"), 1);
28328
+ var import_node_fs24 = __toESM(require("fs"), 1);
28329
+ var import_node_os13 = __toESM(require("os"), 1);
28330
+ var import_node_path26 = __toESM(require("path"), 1);
28495
28331
  init_claude_history();
28496
28332
  function readJsonlEntries(file) {
28497
- const raw = import_node_fs25.default.readFileSync(file, "utf8");
28333
+ const raw = import_node_fs24.default.readFileSync(file, "utf8");
28498
28334
  const out = [];
28499
28335
  for (const line of raw.split("\n")) {
28500
28336
  const t = line.trim();
@@ -28507,10 +28343,10 @@ function readJsonlEntries(file) {
28507
28343
  return out;
28508
28344
  }
28509
28345
  function forkSession(input) {
28510
- const baseDir = input.baseDir ?? import_node_path27.default.join(import_node_os12.default.homedir(), ".claude");
28511
- const projectDir = import_node_path27.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
28512
- const sourceFile = import_node_path27.default.join(projectDir, `${input.toolSessionId}.jsonl`);
28513
- if (!import_node_fs25.default.existsSync(sourceFile)) {
28346
+ const baseDir = input.baseDir ?? import_node_path26.default.join(import_node_os13.default.homedir(), ".claude");
28347
+ const projectDir = import_node_path26.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
28348
+ const sourceFile = import_node_path26.default.join(projectDir, `${input.toolSessionId}.jsonl`);
28349
+ if (!import_node_fs24.default.existsSync(sourceFile)) {
28514
28350
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
28515
28351
  }
28516
28352
  const entries = readJsonlEntries(sourceFile);
@@ -28540,9 +28376,9 @@ function forkSession(input) {
28540
28376
  }
28541
28377
  forkedLines.push(JSON.stringify(forked));
28542
28378
  }
28543
- const forkedFilePath = import_node_path27.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
28544
- import_node_fs25.default.mkdirSync(projectDir, { recursive: true });
28545
- import_node_fs25.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
28379
+ const forkedFilePath = import_node_path26.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
28380
+ import_node_fs24.default.mkdirSync(projectDir, { recursive: true });
28381
+ import_node_fs24.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
28546
28382
  return { forkedToolSessionId, forkedFilePath };
28547
28383
  }
28548
28384
 
@@ -28874,7 +28710,7 @@ function buildPermissionHandlers(deps) {
28874
28710
  }
28875
28711
 
28876
28712
  // src/handlers/history.ts
28877
- var path37 = __toESM(require("path"), 1);
28713
+ var path36 = __toESM(require("path"), 1);
28878
28714
  init_protocol();
28879
28715
 
28880
28716
  // src/session/recent-dirs.ts
@@ -28892,7 +28728,7 @@ function listRecentDirs(store, limit = 50) {
28892
28728
  }
28893
28729
 
28894
28730
  // src/permission/persona-paths.ts
28895
- var path36 = __toESM(require("path"), 1);
28731
+ var path35 = __toESM(require("path"), 1);
28896
28732
  function getAllowedPersonaIds(grants, action) {
28897
28733
  const ids = /* @__PURE__ */ new Set();
28898
28734
  for (const g2 of grants) {
@@ -28905,26 +28741,26 @@ function getAllowedPersonaIds(grants, action) {
28905
28741
  return ids;
28906
28742
  }
28907
28743
  function isGuestPathAllowed(grants, absPath, personaRoot, action = "read") {
28908
- const root = path36.resolve(personaRoot);
28909
- const target = path36.resolve(absPath);
28910
- const sep3 = root.endsWith(path36.sep) ? "" : path36.sep;
28744
+ const root = path35.resolve(personaRoot);
28745
+ const target = path35.resolve(absPath);
28746
+ const sep3 = root.endsWith(path35.sep) ? "" : path35.sep;
28911
28747
  if (!target.startsWith(root + sep3)) return false;
28912
- const rel = path36.relative(root, target);
28748
+ const rel = path35.relative(root, target);
28913
28749
  if (!rel || rel.startsWith("..")) return false;
28914
- const personaId = rel.split(path36.sep)[0];
28750
+ const personaId = rel.split(path35.sep)[0];
28915
28751
  if (!personaId) return false;
28916
28752
  const allowed = getAllowedPersonaIds(grants, action);
28917
28753
  if (allowed === "*") return true;
28918
28754
  return allowed.has(personaId);
28919
28755
  }
28920
28756
  function personaIdFromPath(absPath, personaRoot) {
28921
- const root = path36.resolve(personaRoot);
28922
- const target = path36.resolve(absPath);
28923
- const sep3 = root.endsWith(path36.sep) ? "" : path36.sep;
28757
+ const root = path35.resolve(personaRoot);
28758
+ const target = path35.resolve(absPath);
28759
+ const sep3 = root.endsWith(path35.sep) ? "" : path35.sep;
28924
28760
  if (!target.startsWith(root + sep3)) return null;
28925
- const rel = path36.relative(root, target);
28761
+ const rel = path35.relative(root, target);
28926
28762
  if (!rel || rel.startsWith("..")) return null;
28927
- const id = rel.split(path36.sep)[0];
28763
+ const id = rel.split(path35.sep)[0];
28928
28764
  return id || null;
28929
28765
  }
28930
28766
 
@@ -28949,7 +28785,7 @@ function buildHistoryHandlers(deps) {
28949
28785
  if (!pid) return false;
28950
28786
  return isGuestPathAllowed(
28951
28787
  ctx.grants,
28952
- path37.join(personaRoot, pid),
28788
+ path36.join(personaRoot, pid),
28953
28789
  personaRoot,
28954
28790
  "read"
28955
28791
  );
@@ -28960,7 +28796,7 @@ function buildHistoryHandlers(deps) {
28960
28796
  };
28961
28797
  const list = async (frame, _client, ctx) => {
28962
28798
  const args = HistoryListArgs.parse(frame);
28963
- assertGuestPath(ctx, path37.resolve(args.projectPath), personaRoot, "history:list");
28799
+ assertGuestPath(ctx, path36.resolve(args.projectPath), personaRoot, "history:list");
28964
28800
  const sessions = await history.listSessions(args);
28965
28801
  return { response: { type: "history:list", sessions } };
28966
28802
  };
@@ -28992,13 +28828,13 @@ function buildHistoryHandlers(deps) {
28992
28828
  };
28993
28829
  const subagents = async (frame, _client, ctx) => {
28994
28830
  const args = HistorySubagentsArgs.parse(frame);
28995
- assertGuestPath(ctx, path37.resolve(args.cwd), personaRoot, "history:subagents");
28831
+ assertGuestPath(ctx, path36.resolve(args.cwd), personaRoot, "history:subagents");
28996
28832
  const subs = await history.listSubagents(args);
28997
28833
  return { response: { type: "history:subagents", subagents: subs } };
28998
28834
  };
28999
28835
  const subagentRead = async (frame, _client, ctx) => {
29000
28836
  const args = HistorySubagentReadArgs.parse(frame);
29001
- assertGuestPath(ctx, path37.resolve(args.cwd), personaRoot, "history:subagent-read");
28837
+ assertGuestPath(ctx, path36.resolve(args.cwd), personaRoot, "history:subagent-read");
29002
28838
  const res = await history.readSubagent(args);
29003
28839
  return { response: { type: "history:subagent-read", ...res } };
29004
28840
  };
@@ -29006,7 +28842,7 @@ function buildHistoryHandlers(deps) {
29006
28842
  const dirs = listRecentDirs(store);
29007
28843
  if (ctx?.principal.kind === "guest" && personaRoot) {
29008
28844
  const filtered = dirs.filter(
29009
- (d) => isGuestPathAllowed(ctx.grants, path37.resolve(d.cwd), personaRoot, "read")
28845
+ (d) => isGuestPathAllowed(ctx.grants, path36.resolve(d.cwd), personaRoot, "read")
29010
28846
  );
29011
28847
  return { response: { type: "history:recentDirs", dirs: filtered } };
29012
28848
  }
@@ -29023,8 +28859,8 @@ function buildHistoryHandlers(deps) {
29023
28859
  }
29024
28860
 
29025
28861
  // src/handlers/workspace.ts
29026
- var path38 = __toESM(require("path"), 1);
29027
- var os13 = __toESM(require("os"), 1);
28862
+ var path37 = __toESM(require("path"), 1);
28863
+ var os14 = __toESM(require("os"), 1);
29028
28864
  init_protocol();
29029
28865
  init_protocol();
29030
28866
  function buildEnabledPluginNames(personaManager, personaId) {
@@ -29063,23 +28899,23 @@ function buildWorkspaceHandlers(deps) {
29063
28899
  const list = async (frame, _client, ctx) => {
29064
28900
  const args = WorkspaceListArgs.parse(frame);
29065
28901
  const isGuest = ctx?.principal.kind === "guest";
29066
- const fallbackCwd = isGuest && personaRoot ? personaRoot : os13.homedir();
29067
- const resolvedCwd = path38.resolve(args.cwd ?? fallbackCwd);
29068
- const target = args.path ? path38.resolve(resolvedCwd, args.path) : resolvedCwd;
28902
+ const fallbackCwd = isGuest && personaRoot ? personaRoot : os14.homedir();
28903
+ const resolvedCwd = path37.resolve(args.cwd ?? fallbackCwd);
28904
+ const target = args.path ? path37.resolve(resolvedCwd, args.path) : resolvedCwd;
29069
28905
  assertGuestPath2(ctx, target, personaRoot, "workspace:list");
29070
28906
  const res = workspace.list({ ...args, cwd: resolvedCwd });
29071
28907
  return { response: { type: "workspace:list", ...res } };
29072
28908
  };
29073
28909
  const read = async (frame, _client, ctx) => {
29074
28910
  const args = WorkspaceReadArgs.parse(frame);
29075
- const target = path38.isAbsolute(args.path) ? path38.resolve(args.path) : path38.resolve(args.cwd, args.path);
28911
+ const target = path37.isAbsolute(args.path) ? path37.resolve(args.path) : path37.resolve(args.cwd, args.path);
29076
28912
  assertGuestPath2(ctx, target, personaRoot, "workspace:read");
29077
28913
  const res = workspace.read(args);
29078
28914
  return { response: { type: "workspace:read", ...res } };
29079
28915
  };
29080
28916
  const skillsList = async (frame, _client, ctx) => {
29081
28917
  const args = SkillsListArgs.parse(frame);
29082
- const cwdAbs = path38.resolve(args.cwd);
28918
+ const cwdAbs = path37.resolve(args.cwd);
29083
28919
  assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list");
29084
28920
  const list2 = skills.list(args);
29085
28921
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -29091,7 +28927,7 @@ function buildWorkspaceHandlers(deps) {
29091
28927
  };
29092
28928
  const agentsList = async (frame, _client, ctx) => {
29093
28929
  const args = AgentsListArgs.parse(frame);
29094
- const cwdAbs = path38.resolve(args.cwd);
28930
+ const cwdAbs = path37.resolve(args.cwd);
29095
28931
  assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list");
29096
28932
  const list2 = agents.list(args);
29097
28933
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -29110,20 +28946,20 @@ function buildWorkspaceHandlers(deps) {
29110
28946
  }
29111
28947
 
29112
28948
  // src/handlers/git.ts
29113
- var path40 = __toESM(require("path"), 1);
28949
+ var path39 = __toESM(require("path"), 1);
29114
28950
  init_protocol();
29115
28951
  init_protocol();
29116
28952
 
29117
28953
  // src/workspace/git.ts
29118
28954
  var import_node_child_process6 = require("child_process");
29119
- var import_node_fs26 = __toESM(require("fs"), 1);
29120
- var import_node_path28 = __toESM(require("path"), 1);
28955
+ var import_node_fs25 = __toESM(require("fs"), 1);
28956
+ var import_node_path27 = __toESM(require("path"), 1);
29121
28957
  var import_node_util = require("util");
29122
28958
  var pexec = (0, import_node_util.promisify)(import_node_child_process6.execFile);
29123
28959
  function normalizePath(p2) {
29124
- const resolved = import_node_path28.default.resolve(p2);
28960
+ const resolved = import_node_path27.default.resolve(p2);
29125
28961
  try {
29126
- return import_node_fs26.default.realpathSync(resolved);
28962
+ return import_node_fs25.default.realpathSync(resolved);
29127
28963
  } catch {
29128
28964
  return resolved;
29129
28965
  }
@@ -29196,7 +29032,7 @@ async function listGitBranches(cwd) {
29196
29032
  // src/handlers/git.ts
29197
29033
  function assertGuestCwd(ctx, cwd, personaRoot, method) {
29198
29034
  if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
29199
- if (!isGuestPathAllowed(ctx.grants, path40.resolve(cwd), personaRoot, "read")) {
29035
+ if (!isGuestPathAllowed(ctx.grants, path39.resolve(cwd), personaRoot, "read")) {
29200
29036
  throw new ClawdError(
29201
29037
  ERROR_CODES.UNAUTHORIZED,
29202
29038
  `guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
@@ -29559,7 +29395,7 @@ function buildWhoamiHandler(deps) {
29559
29395
  }
29560
29396
 
29561
29397
  // src/handlers/meta.ts
29562
- var import_node_os13 = __toESM(require("os"), 1);
29398
+ var import_node_os14 = __toESM(require("os"), 1);
29563
29399
  init_protocol();
29564
29400
 
29565
29401
  // src/version.ts
@@ -29589,7 +29425,7 @@ function buildReadyFrame(deps, client) {
29589
29425
  return {
29590
29426
  version,
29591
29427
  protocolVersion: PROTOCOL_VERSION,
29592
- hostname: import_node_os13.default.hostname(),
29428
+ hostname: import_node_os14.default.hostname(),
29593
29429
  os: process.platform,
29594
29430
  tools,
29595
29431
  runningSessions: info.runningSessions,
@@ -29683,7 +29519,7 @@ function buildPersonaHandlers(deps) {
29683
29519
  }
29684
29520
 
29685
29521
  // src/handlers/attachment.ts
29686
- var import_node_path29 = __toESM(require("path"), 1);
29522
+ var import_node_path28 = __toESM(require("path"), 1);
29687
29523
  init_protocol();
29688
29524
  init_protocol();
29689
29525
  var DEFAULT_TTL_SECONDS = 24 * 3600;
@@ -29737,12 +29573,12 @@ function buildAttachmentHandlers(deps) {
29737
29573
  `session ${args.sessionId} scope unresolved`
29738
29574
  );
29739
29575
  }
29740
- const cwdAbs = import_node_path29.default.resolve(sessionFile.cwd);
29741
- const candidateAbs = import_node_path29.default.isAbsolute(args.relPath) ? import_node_path29.default.resolve(args.relPath) : import_node_path29.default.resolve(cwdAbs, args.relPath);
29576
+ const cwdAbs = import_node_path28.default.resolve(sessionFile.cwd);
29577
+ const candidateAbs = import_node_path28.default.isAbsolute(args.relPath) ? import_node_path28.default.resolve(args.relPath) : import_node_path28.default.resolve(cwdAbs, args.relPath);
29742
29578
  assertGuestAttachmentPath(ctx, candidateAbs, deps.personaRoot, "attachment.signUrl");
29743
29579
  const entries = deps.groupFileStore.list(scope, args.sessionId);
29744
29580
  const entry = entries.find((e) => {
29745
- const storedAbs = import_node_path29.default.isAbsolute(e.relPath) ? import_node_path29.default.resolve(e.relPath) : import_node_path29.default.resolve(cwdAbs, e.relPath);
29581
+ const storedAbs = import_node_path28.default.isAbsolute(e.relPath) ? import_node_path28.default.resolve(e.relPath) : import_node_path28.default.resolve(cwdAbs, e.relPath);
29746
29582
  return storedAbs === candidateAbs && !e.stale;
29747
29583
  });
29748
29584
  if (!entry) {
@@ -29766,7 +29602,7 @@ function buildAttachmentHandlers(deps) {
29766
29602
  if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
29767
29603
  const f = deps.sessionStore.read(sessionId);
29768
29604
  if (!f) return;
29769
- assertGuestAttachmentPath(ctx, import_node_path29.default.resolve(f.cwd), deps.personaRoot, method);
29605
+ assertGuestAttachmentPath(ctx, import_node_path28.default.resolve(f.cwd), deps.personaRoot, method);
29770
29606
  }
29771
29607
  const groupAdd = async (frame, _client, ctx) => {
29772
29608
  if (!deps.groupFileStore || !deps.getSessionScope) {
@@ -29807,7 +29643,7 @@ function buildAttachmentHandlers(deps) {
29807
29643
  }
29808
29644
  const entries = deps.groupFileStore.list(scope, parsed.data.sessionId);
29809
29645
  const target = entries.find((e) => e.relPath === parsed.data.relPath);
29810
- if (target?.from === "owner" || target?.from === "user-upload") {
29646
+ if (target?.from === "owner") {
29811
29647
  deps.groupFileStore.remove(scope, parsed.data.sessionId, parsed.data.relPath);
29812
29648
  } else {
29813
29649
  deps.groupFileStore.markStale(scope, parsed.data.sessionId, parsed.data.relPath);
@@ -30003,7 +29839,7 @@ function computeGrantForFrame(method, frame) {
30003
29839
  async function startDaemon(config) {
30004
29840
  const logger = createLogger({
30005
29841
  level: config.logLevel,
30006
- file: import_node_path30.default.join(config.dataDir, "clawd.log")
29842
+ file: import_node_path29.default.join(config.dataDir, "clawd.log")
30007
29843
  });
30008
29844
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
30009
29845
  const stateMgr = new StateFileManager({ dataDir: config.dataDir });
@@ -30090,7 +29926,7 @@ async function startDaemon(config) {
30090
29926
  const agents = new AgentsScanner();
30091
29927
  const history = new ClaudeHistoryReader();
30092
29928
  let transport = null;
30093
- const personaStore = new PersonaStore(import_node_path30.default.join(config.dataDir, "personas"));
29929
+ const personaStore = new PersonaStore(import_node_path29.default.join(config.dataDir, "personas"));
30094
29930
  const defaultsRoot = findDefaultsRoot();
30095
29931
  if (defaultsRoot) {
30096
29932
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -30107,7 +29943,7 @@ async function startDaemon(config) {
30107
29943
  getAdapter,
30108
29944
  historyReader: history,
30109
29945
  dataDir: config.dataDir,
30110
- personaRoot: import_node_path30.default.join(config.dataDir, "personas"),
29946
+ personaRoot: import_node_path29.default.join(config.dataDir, "personas"),
30111
29947
  personaStore,
30112
29948
  ownerDisplayName,
30113
29949
  ownerPrincipalId,
@@ -30131,10 +29967,10 @@ async function startDaemon(config) {
30131
29967
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
30132
29968
  attachmentGroup: {
30133
29969
  onFileEdit: (input) => {
30134
- const absPath = import_node_path30.default.isAbsolute(input.relPath) ? input.relPath : import_node_path30.default.join(input.cwd, input.relPath);
29970
+ const absPath = import_node_path29.default.isAbsolute(input.relPath) ? input.relPath : import_node_path29.default.join(input.cwd, input.relPath);
30135
29971
  let size = 0;
30136
29972
  try {
30137
- size = import_node_fs27.default.statSync(absPath).size;
29973
+ size = import_node_fs26.default.statSync(absPath).size;
30138
29974
  } catch (err) {
30139
29975
  logger.warn("attachment.onFileEdit stat failed", {
30140
29976
  sessionId: input.sessionId,
@@ -30160,6 +29996,11 @@ async function startDaemon(config) {
30160
29996
  }
30161
29997
  });
30162
29998
  const observer = new SessionObserver({
29999
+ // TUI 模式 pty 跑 CC 不发 SDK system.task_started 帧 → UI pending 期 progress.agentId
30000
+ // 永远 undefined,子 agent 调用过程没有抽屉入口。开 meta observer 后从 subagents 目录
30001
+ // 新出现的 agent-*.meta.json 派生 subagent_progress(started) 补这块缺口。SDK 模式仍走
30002
+ // task_started 路径,避免双 emit 让 ring buffer 双帧。
30003
+ enableSubagentMetaObserver: config.mode === "tui",
30163
30004
  onEvent: ({ sessionId, events }) => {
30164
30005
  manager.feedObserverEvents(sessionId, events);
30165
30006
  },
@@ -30247,10 +30088,10 @@ async function startDaemon(config) {
30247
30088
  // 'persona/<pid>/owner',default 走 'default'。
30248
30089
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
30249
30090
  // guest path guard:candidate 必须在 personaRoot 子树下
30250
- personaRoot: import_node_path30.default.join(config.dataDir, "personas")
30091
+ personaRoot: import_node_path29.default.join(config.dataDir, "personas")
30251
30092
  },
30252
30093
  // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
30253
- personaRoot: import_node_path30.default.join(config.dataDir, "personas"),
30094
+ personaRoot: import_node_path29.default.join(config.dataDir, "personas"),
30254
30095
  // capability:list / delete handler 依赖
30255
30096
  capabilityManager,
30256
30097
  // personal-cap:get 返回的 shareBaseUrl 用此 base URL:
@@ -30286,19 +30127,7 @@ async function startDaemon(config) {
30286
30127
  sessionStore: store,
30287
30128
  // /files HMAC verify 用 auth.json 的 signSecret 字段(与 attachment.signUrl 同源)。
30288
30129
  // --auth-token CLI 模式没 signSecret → 路由返 501,sign URL 功能整体禁用。
30289
- getSignSecret: () => authFile?.signSecret ?? null,
30290
- // chatinput 附件上传(POST /attachments/upload,spec §6.1)。upload pipeline
30291
- // 在 session.cwd 下的 .attachments/ 落盘;signSecret 与 /files HMAC 同源;
30292
- // tunnel 未启时返 503 TUNNEL_NOT_READY 让前端显式提示。
30293
- attachmentUpload: {
30294
- groupFileStore,
30295
- getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
30296
- getSessionDir: (sid) => {
30297
- const f = manager.findOwnedSession(sid);
30298
- return f ? f.cwd : null;
30299
- },
30300
- getHttpBaseUrl: () => currentTunnelUrl ? getHttpBaseUrl() : null
30301
- }
30130
+ getSignSecret: () => authFile?.signSecret ?? null
30302
30131
  });
30303
30132
  wsServer = new LocalWsServer({
30304
30133
  host: config.host,
@@ -30463,8 +30292,8 @@ async function startDaemon(config) {
30463
30292
  const lines = [
30464
30293
  `Tunnel: ${r.url}`,
30465
30294
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
30466
- `Frpc config: ${import_node_path30.default.join(config.dataDir, "frpc.toml")}`,
30467
- `Frpc log: ${import_node_path30.default.join(config.dataDir, "frpc.log")}`
30295
+ `Frpc config: ${import_node_path29.default.join(config.dataDir, "frpc.toml")}`,
30296
+ `Frpc log: ${import_node_path29.default.join(config.dataDir, "frpc.log")}`
30468
30297
  ];
30469
30298
  const width = Math.max(...lines.map((l) => l.length));
30470
30299
  const bar = "\u2550".repeat(width + 4);
@@ -30477,8 +30306,8 @@ ${bar}
30477
30306
 
30478
30307
  `);
30479
30308
  try {
30480
- const connectPath = import_node_path30.default.join(config.dataDir, "connect.txt");
30481
- import_node_fs27.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
30309
+ const connectPath = import_node_path29.default.join(config.dataDir, "connect.txt");
30310
+ import_node_fs26.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
30482
30311
  } catch {
30483
30312
  }
30484
30313
  } catch (err) {
@@ -30503,29 +30332,8 @@ ${bar}
30503
30332
  logger.warn("tunnel unavailable, degraded to local mode", { reason: tunnelError });
30504
30333
  }
30505
30334
  }
30506
- const tickAttachmentGc = () => {
30507
- try {
30508
- runAttachmentGc({
30509
- dataDir: config.dataDir,
30510
- groupFileStore,
30511
- sessionScopes: manager.listOwnedSessionScopes().map(({ scope, sessionId }) => ({
30512
- scope,
30513
- sessionId
30514
- })),
30515
- // 注入 cwd 让 GC 直接定位 .attachments 目录(与 upload 落盘点一致)
30516
- getSessionCwd: (sid) => manager.findOwnedSession(sid)?.cwd ?? null,
30517
- logger
30518
- });
30519
- } catch (err) {
30520
- logger.warn("attachment gc tick failed", { err: err.message });
30521
- }
30522
- };
30523
- setImmediate(tickAttachmentGc);
30524
- const attachmentGcInterval = setInterval(tickAttachmentGc, 24 * 3600 * 1e3);
30525
- attachmentGcInterval.unref();
30526
30335
  const shutdown = async () => {
30527
30336
  logger.info("stopping clawd");
30528
- clearInterval(attachmentGcInterval);
30529
30337
  observer.stopAll();
30530
30338
  manager.stopAll();
30531
30339
  if (tunnelMgr) {
@@ -30543,9 +30351,9 @@ ${bar}
30543
30351
  };
30544
30352
  }
30545
30353
  function migrateDropPersonsDir(dataDir) {
30546
- const dir = import_node_path30.default.join(dataDir, "persons");
30354
+ const dir = import_node_path29.default.join(dataDir, "persons");
30547
30355
  try {
30548
- import_node_fs27.default.rmSync(dir, { recursive: true, force: true });
30356
+ import_node_fs26.default.rmSync(dir, { recursive: true, force: true });
30549
30357
  } catch {
30550
30358
  }
30551
30359
  }