@clawos-dev/clawd 0.2.182 → 0.2.183-beta.365.f527c19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -157,6 +157,12 @@ var init_methods = __esm({
157
157
  // 写入路径:device:connect 落 store + 自动反向(决策 #11);list / remove 作联系人列表读删。
158
158
  "contact:list",
159
159
  "contact:remove",
160
+ // ---- visitor:* (web 访客 · persona web 分享,spec 2026-06-24-persona-web-share-design) ----
161
+ // owner-only:列出登录访问过本机 public persona 的 web 访客(零安装、飞书登录、daemon 自签
162
+ // visitor token)。存储 ~/.clawd/visitors.json(VisitorStore),登录(exchange)即 upsert;
163
+ // 访客身份用 TTC unique_id,会话按 creatorPrincipalId='visitor-<id>' 进现有会话用户分组。
164
+ // 访客换 token 走 HTTP-only 公开路由 POST /share/exchange(非 WS method)。
165
+ "visitor:list",
160
166
  // ---- whoami (v2 capability platform) ----
161
167
  // 任何 authed connection 都能调;返回 owner 显示名 + 当前 capability wire 形态 +
162
168
  // grants 对应的 persona 元数据 (id + displayName)。UI 端 AddRemotePersonaDialog
@@ -727,8 +733,8 @@ var init_parseUtil = __esm({
727
733
  init_errors2();
728
734
  init_en();
729
735
  makeIssue = (params) => {
730
- const { data, path: path63, errorMaps, issueData } = params;
731
- const fullPath = [...path63, ...issueData.path || []];
736
+ const { data, path: path66, errorMaps, issueData } = params;
737
+ const fullPath = [...path66, ...issueData.path || []];
732
738
  const fullIssue = {
733
739
  ...issueData,
734
740
  path: fullPath
@@ -1039,11 +1045,11 @@ var init_types = __esm({
1039
1045
  init_parseUtil();
1040
1046
  init_util();
1041
1047
  ParseInputLazyPath = class {
1042
- constructor(parent, value, path63, key) {
1048
+ constructor(parent, value, path66, key) {
1043
1049
  this._cachedPath = [];
1044
1050
  this.parent = parent;
1045
1051
  this.data = value;
1046
- this._path = path63;
1052
+ this._path = path66;
1047
1053
  this._key = key;
1048
1054
  }
1049
1055
  get path() {
@@ -6037,6 +6043,32 @@ var init_log = __esm({
6037
6043
  }
6038
6044
  });
6039
6045
 
6046
+ // ../protocol/src/visitor-schemas.ts
6047
+ var VisitorTokenPayloadSchema, VisitorRecordSchema, VisitorListResultSchema;
6048
+ var init_visitor_schemas = __esm({
6049
+ "../protocol/src/visitor-schemas.ts"() {
6050
+ "use strict";
6051
+ init_zod();
6052
+ VisitorTokenPayloadSchema = external_exports.object({
6053
+ v: external_exports.literal(1),
6054
+ visitorId: external_exports.string().min(1),
6055
+ displayName: external_exports.string(),
6056
+ provider: external_exports.string(),
6057
+ iat: external_exports.number(),
6058
+ exp: external_exports.number()
6059
+ });
6060
+ VisitorRecordSchema = external_exports.object({
6061
+ visitorId: external_exports.string(),
6062
+ displayName: external_exports.string(),
6063
+ avatarUrl: external_exports.string().optional(),
6064
+ provider: external_exports.string(),
6065
+ firstSeen: external_exports.number(),
6066
+ lastSeen: external_exports.number()
6067
+ });
6068
+ VisitorListResultSchema = external_exports.object({ visitors: external_exports.array(VisitorRecordSchema) });
6069
+ }
6070
+ });
6071
+
6040
6072
  // ../protocol/src/runtime.ts
6041
6073
  var init_runtime = __esm({
6042
6074
  "../protocol/src/runtime.ts"() {
@@ -6057,6 +6089,7 @@ var init_runtime = __esm({
6057
6089
  init_feishu_auth();
6058
6090
  init_dispatch();
6059
6091
  init_log();
6092
+ init_visitor_schemas();
6060
6093
  }
6061
6094
  });
6062
6095
 
@@ -6331,8 +6364,8 @@ var require_req = __commonJS({
6331
6364
  if (req.originalUrl) {
6332
6365
  _req.url = req.originalUrl;
6333
6366
  } else {
6334
- const path63 = req.path;
6335
- _req.url = typeof path63 === "string" ? path63 : req.url ? req.url.path || req.url : void 0;
6367
+ const path66 = req.path;
6368
+ _req.url = typeof path66 === "string" ? path66 : req.url ? req.url.path || req.url : void 0;
6336
6369
  }
6337
6370
  if (req.query) {
6338
6371
  _req.query = req.query;
@@ -6497,14 +6530,14 @@ var require_redact = __commonJS({
6497
6530
  }
6498
6531
  return obj;
6499
6532
  }
6500
- function parsePath(path63) {
6533
+ function parsePath(path66) {
6501
6534
  const parts = [];
6502
6535
  let current = "";
6503
6536
  let inBrackets = false;
6504
6537
  let inQuotes = false;
6505
6538
  let quoteChar = "";
6506
- for (let i = 0; i < path63.length; i++) {
6507
- const char = path63[i];
6539
+ for (let i = 0; i < path66.length; i++) {
6540
+ const char = path66[i];
6508
6541
  if (!inBrackets && char === ".") {
6509
6542
  if (current) {
6510
6543
  parts.push(current);
@@ -6635,10 +6668,10 @@ var require_redact = __commonJS({
6635
6668
  return current;
6636
6669
  }
6637
6670
  function redactPaths(obj, paths, censor, remove = false) {
6638
- for (const path63 of paths) {
6639
- const parts = parsePath(path63);
6671
+ for (const path66 of paths) {
6672
+ const parts = parsePath(path66);
6640
6673
  if (parts.includes("*")) {
6641
- redactWildcardPath(obj, parts, censor, path63, remove);
6674
+ redactWildcardPath(obj, parts, censor, path66, remove);
6642
6675
  } else {
6643
6676
  if (remove) {
6644
6677
  removeKey(obj, parts);
@@ -6723,8 +6756,8 @@ var require_redact = __commonJS({
6723
6756
  }
6724
6757
  } else {
6725
6758
  if (afterWildcard.includes("*")) {
6726
- const wrappedCensor = typeof censor === "function" ? (value, path63) => {
6727
- const fullPath = [...pathArray.slice(0, pathLength), ...path63];
6759
+ const wrappedCensor = typeof censor === "function" ? (value, path66) => {
6760
+ const fullPath = [...pathArray.slice(0, pathLength), ...path66];
6728
6761
  return censor(value, fullPath);
6729
6762
  } : censor;
6730
6763
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -6759,8 +6792,8 @@ var require_redact = __commonJS({
6759
6792
  return null;
6760
6793
  }
6761
6794
  const pathStructure = /* @__PURE__ */ new Map();
6762
- for (const path63 of pathsToClone) {
6763
- const parts = parsePath(path63);
6795
+ for (const path66 of pathsToClone) {
6796
+ const parts = parsePath(path66);
6764
6797
  let current = pathStructure;
6765
6798
  for (let i = 0; i < parts.length; i++) {
6766
6799
  const part = parts[i];
@@ -6812,24 +6845,24 @@ var require_redact = __commonJS({
6812
6845
  }
6813
6846
  return cloneSelectively(obj, pathStructure);
6814
6847
  }
6815
- function validatePath(path63) {
6816
- if (typeof path63 !== "string") {
6848
+ function validatePath(path66) {
6849
+ if (typeof path66 !== "string") {
6817
6850
  throw new Error("Paths must be (non-empty) strings");
6818
6851
  }
6819
- if (path63 === "") {
6852
+ if (path66 === "") {
6820
6853
  throw new Error("Invalid redaction path ()");
6821
6854
  }
6822
- if (path63.includes("..")) {
6823
- throw new Error(`Invalid redaction path (${path63})`);
6855
+ if (path66.includes("..")) {
6856
+ throw new Error(`Invalid redaction path (${path66})`);
6824
6857
  }
6825
- if (path63.includes(",")) {
6826
- throw new Error(`Invalid redaction path (${path63})`);
6858
+ if (path66.includes(",")) {
6859
+ throw new Error(`Invalid redaction path (${path66})`);
6827
6860
  }
6828
6861
  let bracketCount = 0;
6829
6862
  let inQuotes = false;
6830
6863
  let quoteChar = "";
6831
- for (let i = 0; i < path63.length; i++) {
6832
- const char = path63[i];
6864
+ for (let i = 0; i < path66.length; i++) {
6865
+ const char = path66[i];
6833
6866
  if ((char === '"' || char === "'") && bracketCount > 0) {
6834
6867
  if (!inQuotes) {
6835
6868
  inQuotes = true;
@@ -6843,20 +6876,20 @@ var require_redact = __commonJS({
6843
6876
  } else if (char === "]" && !inQuotes) {
6844
6877
  bracketCount--;
6845
6878
  if (bracketCount < 0) {
6846
- throw new Error(`Invalid redaction path (${path63})`);
6879
+ throw new Error(`Invalid redaction path (${path66})`);
6847
6880
  }
6848
6881
  }
6849
6882
  }
6850
6883
  if (bracketCount !== 0) {
6851
- throw new Error(`Invalid redaction path (${path63})`);
6884
+ throw new Error(`Invalid redaction path (${path66})`);
6852
6885
  }
6853
6886
  }
6854
6887
  function validatePaths(paths) {
6855
6888
  if (!Array.isArray(paths)) {
6856
6889
  throw new TypeError("paths must be an array");
6857
6890
  }
6858
- for (const path63 of paths) {
6859
- validatePath(path63);
6891
+ for (const path66 of paths) {
6892
+ validatePath(path66);
6860
6893
  }
6861
6894
  }
6862
6895
  function slowRedact(options = {}) {
@@ -7024,8 +7057,8 @@ var require_redaction = __commonJS({
7024
7057
  if (shape[k2] === null) {
7025
7058
  o[k2] = (value) => topCensor(value, [k2]);
7026
7059
  } else {
7027
- const wrappedCensor = typeof censor === "function" ? (value, path63) => {
7028
- return censor(value, [k2, ...path63]);
7060
+ const wrappedCensor = typeof censor === "function" ? (value, path66) => {
7061
+ return censor(value, [k2, ...path66]);
7029
7062
  } : censor;
7030
7063
  o[k2] = Redact({
7031
7064
  paths: shape[k2],
@@ -7243,10 +7276,10 @@ var require_atomic_sleep = __commonJS({
7243
7276
  var require_sonic_boom = __commonJS({
7244
7277
  "../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
7245
7278
  "use strict";
7246
- var fs57 = require("fs");
7279
+ var fs60 = require("fs");
7247
7280
  var EventEmitter3 = require("events");
7248
7281
  var inherits = require("util").inherits;
7249
- var path63 = require("path");
7282
+ var path66 = require("path");
7250
7283
  var sleep2 = require_atomic_sleep();
7251
7284
  var assert = require("assert");
7252
7285
  var BUSY_WRITE_TIMEOUT = 100;
@@ -7300,20 +7333,20 @@ var require_sonic_boom = __commonJS({
7300
7333
  const mode = sonic.mode;
7301
7334
  if (sonic.sync) {
7302
7335
  try {
7303
- if (sonic.mkdir) fs57.mkdirSync(path63.dirname(file), { recursive: true });
7304
- const fd = fs57.openSync(file, flags, mode);
7336
+ if (sonic.mkdir) fs60.mkdirSync(path66.dirname(file), { recursive: true });
7337
+ const fd = fs60.openSync(file, flags, mode);
7305
7338
  fileOpened(null, fd);
7306
7339
  } catch (err) {
7307
7340
  fileOpened(err);
7308
7341
  throw err;
7309
7342
  }
7310
7343
  } else if (sonic.mkdir) {
7311
- fs57.mkdir(path63.dirname(file), { recursive: true }, (err) => {
7344
+ fs60.mkdir(path66.dirname(file), { recursive: true }, (err) => {
7312
7345
  if (err) return fileOpened(err);
7313
- fs57.open(file, flags, mode, fileOpened);
7346
+ fs60.open(file, flags, mode, fileOpened);
7314
7347
  });
7315
7348
  } else {
7316
- fs57.open(file, flags, mode, fileOpened);
7349
+ fs60.open(file, flags, mode, fileOpened);
7317
7350
  }
7318
7351
  }
7319
7352
  function SonicBoom(opts) {
@@ -7354,8 +7387,8 @@ var require_sonic_boom = __commonJS({
7354
7387
  this.flush = flushBuffer;
7355
7388
  this.flushSync = flushBufferSync;
7356
7389
  this._actualWrite = actualWriteBuffer;
7357
- fsWriteSync = () => fs57.writeSync(this.fd, this._writingBuf);
7358
- fsWrite = () => fs57.write(this.fd, this._writingBuf, this.release);
7390
+ fsWriteSync = () => fs60.writeSync(this.fd, this._writingBuf);
7391
+ fsWrite = () => fs60.write(this.fd, this._writingBuf, this.release);
7359
7392
  } else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
7360
7393
  this._writingBuf = "";
7361
7394
  this.write = write;
@@ -7364,15 +7397,15 @@ var require_sonic_boom = __commonJS({
7364
7397
  this._actualWrite = actualWrite;
7365
7398
  fsWriteSync = () => {
7366
7399
  if (Buffer.isBuffer(this._writingBuf)) {
7367
- return fs57.writeSync(this.fd, this._writingBuf);
7400
+ return fs60.writeSync(this.fd, this._writingBuf);
7368
7401
  }
7369
- return fs57.writeSync(this.fd, this._writingBuf, "utf8");
7402
+ return fs60.writeSync(this.fd, this._writingBuf, "utf8");
7370
7403
  };
7371
7404
  fsWrite = () => {
7372
7405
  if (Buffer.isBuffer(this._writingBuf)) {
7373
- return fs57.write(this.fd, this._writingBuf, this.release);
7406
+ return fs60.write(this.fd, this._writingBuf, this.release);
7374
7407
  }
7375
- return fs57.write(this.fd, this._writingBuf, "utf8", this.release);
7408
+ return fs60.write(this.fd, this._writingBuf, "utf8", this.release);
7376
7409
  };
7377
7410
  } else {
7378
7411
  throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
@@ -7429,7 +7462,7 @@ var require_sonic_boom = __commonJS({
7429
7462
  }
7430
7463
  }
7431
7464
  if (this._fsync) {
7432
- fs57.fsyncSync(this.fd);
7465
+ fs60.fsyncSync(this.fd);
7433
7466
  }
7434
7467
  const len = this._len;
7435
7468
  if (this._reopening) {
@@ -7543,7 +7576,7 @@ var require_sonic_boom = __commonJS({
7543
7576
  const onDrain = () => {
7544
7577
  if (!this._fsync) {
7545
7578
  try {
7546
- fs57.fsync(this.fd, (err) => {
7579
+ fs60.fsync(this.fd, (err) => {
7547
7580
  this._flushPending = false;
7548
7581
  cb(err);
7549
7582
  });
@@ -7645,7 +7678,7 @@ var require_sonic_boom = __commonJS({
7645
7678
  const fd = this.fd;
7646
7679
  this.once("ready", () => {
7647
7680
  if (fd !== this.fd) {
7648
- fs57.close(fd, (err) => {
7681
+ fs60.close(fd, (err) => {
7649
7682
  if (err) {
7650
7683
  return this.emit("error", err);
7651
7684
  }
@@ -7694,7 +7727,7 @@ var require_sonic_boom = __commonJS({
7694
7727
  buf = this._bufs[0];
7695
7728
  }
7696
7729
  try {
7697
- const n = Buffer.isBuffer(buf) ? fs57.writeSync(this.fd, buf) : fs57.writeSync(this.fd, buf, "utf8");
7730
+ const n = Buffer.isBuffer(buf) ? fs60.writeSync(this.fd, buf) : fs60.writeSync(this.fd, buf, "utf8");
7698
7731
  const releasedBufObj = releaseWritingBuf(buf, this._len, n);
7699
7732
  buf = releasedBufObj.writingBuf;
7700
7733
  this._len = releasedBufObj.len;
@@ -7710,7 +7743,7 @@ var require_sonic_boom = __commonJS({
7710
7743
  }
7711
7744
  }
7712
7745
  try {
7713
- fs57.fsyncSync(this.fd);
7746
+ fs60.fsyncSync(this.fd);
7714
7747
  } catch {
7715
7748
  }
7716
7749
  }
@@ -7731,7 +7764,7 @@ var require_sonic_boom = __commonJS({
7731
7764
  buf = mergeBuf(this._bufs[0], this._lens[0]);
7732
7765
  }
7733
7766
  try {
7734
- const n = fs57.writeSync(this.fd, buf);
7767
+ const n = fs60.writeSync(this.fd, buf);
7735
7768
  buf = buf.subarray(n);
7736
7769
  this._len = Math.max(this._len - n, 0);
7737
7770
  if (buf.length <= 0) {
@@ -7759,13 +7792,13 @@ var require_sonic_boom = __commonJS({
7759
7792
  this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
7760
7793
  if (this.sync) {
7761
7794
  try {
7762
- const written = Buffer.isBuffer(this._writingBuf) ? fs57.writeSync(this.fd, this._writingBuf) : fs57.writeSync(this.fd, this._writingBuf, "utf8");
7795
+ const written = Buffer.isBuffer(this._writingBuf) ? fs60.writeSync(this.fd, this._writingBuf) : fs60.writeSync(this.fd, this._writingBuf, "utf8");
7763
7796
  release(null, written);
7764
7797
  } catch (err) {
7765
7798
  release(err);
7766
7799
  }
7767
7800
  } else {
7768
- fs57.write(this.fd, this._writingBuf, release);
7801
+ fs60.write(this.fd, this._writingBuf, release);
7769
7802
  }
7770
7803
  }
7771
7804
  function actualWriteBuffer() {
@@ -7774,7 +7807,7 @@ var require_sonic_boom = __commonJS({
7774
7807
  this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
7775
7808
  if (this.sync) {
7776
7809
  try {
7777
- const written = fs57.writeSync(this.fd, this._writingBuf);
7810
+ const written = fs60.writeSync(this.fd, this._writingBuf);
7778
7811
  release(null, written);
7779
7812
  } catch (err) {
7780
7813
  release(err);
@@ -7783,7 +7816,7 @@ var require_sonic_boom = __commonJS({
7783
7816
  if (kCopyBuffer) {
7784
7817
  this._writingBuf = Buffer.from(this._writingBuf);
7785
7818
  }
7786
- fs57.write(this.fd, this._writingBuf, release);
7819
+ fs60.write(this.fd, this._writingBuf, release);
7787
7820
  }
7788
7821
  }
7789
7822
  function actualClose(sonic) {
@@ -7799,12 +7832,12 @@ var require_sonic_boom = __commonJS({
7799
7832
  sonic._lens = [];
7800
7833
  assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
7801
7834
  try {
7802
- fs57.fsync(sonic.fd, closeWrapped);
7835
+ fs60.fsync(sonic.fd, closeWrapped);
7803
7836
  } catch {
7804
7837
  }
7805
7838
  function closeWrapped() {
7806
7839
  if (sonic.fd !== 1 && sonic.fd !== 2) {
7807
- fs57.close(sonic.fd, done);
7840
+ fs60.close(sonic.fd, done);
7808
7841
  } else {
7809
7842
  done();
7810
7843
  }
@@ -10939,11 +10972,11 @@ var init_lib = __esm({
10939
10972
  }
10940
10973
  }
10941
10974
  },
10942
- addToPath: function addToPath(path63, added, removed, oldPosInc, options) {
10943
- var last = path63.lastComponent;
10975
+ addToPath: function addToPath(path66, added, removed, oldPosInc, options) {
10976
+ var last = path66.lastComponent;
10944
10977
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
10945
10978
  return {
10946
- oldPos: path63.oldPos + oldPosInc,
10979
+ oldPos: path66.oldPos + oldPosInc,
10947
10980
  lastComponent: {
10948
10981
  count: last.count + 1,
10949
10982
  added,
@@ -10953,7 +10986,7 @@ var init_lib = __esm({
10953
10986
  };
10954
10987
  } else {
10955
10988
  return {
10956
- oldPos: path63.oldPos + oldPosInc,
10989
+ oldPos: path66.oldPos + oldPosInc,
10957
10990
  lastComponent: {
10958
10991
  count: 1,
10959
10992
  added,
@@ -11199,7 +11232,7 @@ function hashDirToCwd(hash) {
11199
11232
  }
11200
11233
  function safeStatMtime(p2) {
11201
11234
  try {
11202
- return import_node_fs10.default.statSync(p2).mtimeMs;
11235
+ return import_node_fs13.default.statSync(p2).mtimeMs;
11203
11236
  } catch {
11204
11237
  return 0;
11205
11238
  }
@@ -11207,7 +11240,7 @@ function safeStatMtime(p2) {
11207
11240
  function readJsonlLines(file) {
11208
11241
  let raw;
11209
11242
  try {
11210
- raw = import_node_fs10.default.readFileSync(file, "utf8");
11243
+ raw = import_node_fs13.default.readFileSync(file, "utf8");
11211
11244
  } catch (err) {
11212
11245
  if (err.code === "ENOENT") return [];
11213
11246
  throw err;
@@ -11399,10 +11432,10 @@ function attachmentToHistoryMessage(o, ts) {
11399
11432
  const memories = raw.map((m2) => {
11400
11433
  if (!m2 || typeof m2 !== "object") return null;
11401
11434
  const rec3 = m2;
11402
- const path63 = typeof rec3.path === "string" ? rec3.path : null;
11435
+ const path66 = typeof rec3.path === "string" ? rec3.path : null;
11403
11436
  const content = typeof rec3.content === "string" ? rec3.content : null;
11404
- if (!path63 || content == null) return null;
11405
- const entry = { path: path63, content };
11437
+ if (!path66 || content == null) return null;
11438
+ const entry = { path: path66, content };
11406
11439
  if (typeof rec3.mtimeMs === "number") entry.mtimeMs = rec3.mtimeMs;
11407
11440
  return entry;
11408
11441
  }).filter((m2) => m2 !== null);
@@ -11438,8 +11471,8 @@ function attachmentDeferredToolsText(a) {
11438
11471
  function readBackupContent(fileHistoryRoot, toolSessionId, backupFileName) {
11439
11472
  if (backupFileName === null) return null;
11440
11473
  try {
11441
- return import_node_fs10.default.readFileSync(
11442
- import_node_path9.default.join(fileHistoryRoot, toolSessionId, backupFileName),
11474
+ return import_node_fs13.default.readFileSync(
11475
+ import_node_path12.default.join(fileHistoryRoot, toolSessionId, backupFileName),
11443
11476
  "utf8"
11444
11477
  );
11445
11478
  } catch {
@@ -11448,19 +11481,19 @@ function readBackupContent(fileHistoryRoot, toolSessionId, backupFileName) {
11448
11481
  }
11449
11482
  function readCurrentContent(filePath) {
11450
11483
  try {
11451
- return import_node_fs10.default.readFileSync(filePath, "utf8");
11484
+ return import_node_fs13.default.readFileSync(filePath, "utf8");
11452
11485
  } catch (err) {
11453
11486
  if (err.code === "ENOENT") return null;
11454
11487
  return null;
11455
11488
  }
11456
11489
  }
11457
- var import_node_fs10, import_node_os5, import_node_path9, TASK_NOTIFICATION_RE, TASK_ID_RE, TOOL_USE_ID_RE, SLASH_COMMAND_RE, LOCAL_COMMAND_RE, SYSTEM_REMINDER_RE, OPENSPEC_BLOCK_RE, SKILL_HINT_RE, ATTACHMENT_SILENT_SUBTYPES, ClaudeHistoryReader;
11490
+ var import_node_fs13, import_node_os5, import_node_path12, TASK_NOTIFICATION_RE, TASK_ID_RE, TOOL_USE_ID_RE, SLASH_COMMAND_RE, LOCAL_COMMAND_RE, SYSTEM_REMINDER_RE, OPENSPEC_BLOCK_RE, SKILL_HINT_RE, ATTACHMENT_SILENT_SUBTYPES, ClaudeHistoryReader;
11458
11491
  var init_claude_history = __esm({
11459
11492
  "src/tools/claude-history.ts"() {
11460
11493
  "use strict";
11461
- import_node_fs10 = __toESM(require("fs"), 1);
11494
+ import_node_fs13 = __toESM(require("fs"), 1);
11462
11495
  import_node_os5 = __toESM(require("os"), 1);
11463
- import_node_path9 = __toESM(require("path"), 1);
11496
+ import_node_path12 = __toESM(require("path"), 1);
11464
11497
  init_lib();
11465
11498
  init_tool_result_extra();
11466
11499
  TASK_NOTIFICATION_RE = /<task-notification\b[\s\S]*?<\/task-notification>/i;
@@ -11484,14 +11517,14 @@ var init_claude_history = __esm({
11484
11517
  // 每次 user 提交前 trackEdit 拷一份,作为 rewind 回退目标
11485
11518
  fileHistoryRoot;
11486
11519
  constructor(opts = {}) {
11487
- const base = opts.baseDir ?? import_node_path9.default.join(import_node_os5.default.homedir(), ".claude");
11488
- this.projectsRoot = import_node_path9.default.join(base, "projects");
11489
- this.fileHistoryRoot = import_node_path9.default.join(base, "file-history");
11520
+ const base = opts.baseDir ?? import_node_path12.default.join(import_node_os5.default.homedir(), ".claude");
11521
+ this.projectsRoot = import_node_path12.default.join(base, "projects");
11522
+ this.fileHistoryRoot = import_node_path12.default.join(base, "file-history");
11490
11523
  }
11491
11524
  async listProjects() {
11492
11525
  let entries;
11493
11526
  try {
11494
- entries = import_node_fs10.default.readdirSync(this.projectsRoot, { withFileTypes: true });
11527
+ entries = import_node_fs13.default.readdirSync(this.projectsRoot, { withFileTypes: true });
11495
11528
  } catch (err) {
11496
11529
  if (err.code === "ENOENT") return [];
11497
11530
  throw err;
@@ -11499,9 +11532,9 @@ var init_claude_history = __esm({
11499
11532
  const out = [];
11500
11533
  for (const ent of entries) {
11501
11534
  if (!ent.isDirectory()) continue;
11502
- const dir = import_node_path9.default.join(this.projectsRoot, ent.name);
11503
- const files = import_node_fs10.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
11504
- const updatedAtMs = files.reduce((m2, f) => Math.max(m2, safeStatMtime(import_node_path9.default.join(dir, f))), 0);
11535
+ const dir = import_node_path12.default.join(this.projectsRoot, ent.name);
11536
+ const files = import_node_fs13.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
11537
+ const updatedAtMs = files.reduce((m2, f) => Math.max(m2, safeStatMtime(import_node_path12.default.join(dir, f))), 0);
11505
11538
  out.push({
11506
11539
  projectPath: hashDirToCwd(ent.name),
11507
11540
  hashDir: ent.name,
@@ -11513,17 +11546,17 @@ var init_claude_history = __esm({
11513
11546
  return out;
11514
11547
  }
11515
11548
  async listSessions(args) {
11516
- const dir = import_node_path9.default.join(this.projectsRoot, cwdToHashDir(args.projectPath));
11549
+ const dir = import_node_path12.default.join(this.projectsRoot, cwdToHashDir(args.projectPath));
11517
11550
  let files;
11518
11551
  try {
11519
- files = import_node_fs10.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
11552
+ files = import_node_fs13.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
11520
11553
  } catch (err) {
11521
11554
  if (err.code === "ENOENT") return [];
11522
11555
  throw err;
11523
11556
  }
11524
11557
  const out = [];
11525
11558
  for (const f of files) {
11526
- const full = import_node_path9.default.join(dir, f);
11559
+ const full = import_node_path12.default.join(dir, f);
11527
11560
  const toolSessionId = f.slice(0, -".jsonl".length);
11528
11561
  const lines = readJsonlLines(full);
11529
11562
  let summary = "";
@@ -11578,7 +11611,7 @@ var init_claude_history = __esm({
11578
11611
  return out;
11579
11612
  }
11580
11613
  async read(args) {
11581
- const file = import_node_path9.default.join(
11614
+ const file = import_node_path12.default.join(
11582
11615
  this.projectsRoot,
11583
11616
  cwdToHashDir(args.cwd),
11584
11617
  `${args.toolSessionId}.jsonl`
@@ -11615,7 +11648,7 @@ var init_claude_history = __esm({
11615
11648
  // 独立目录路径:<projectsRoot>/<cwdHash>/<toolSessionId>/subagents/*.jsonl
11616
11649
  // 返回 null 表示目录不存在(调用方回退旧实现);返回空数组表示目录存在但无 jsonl
11617
11650
  listSubagentsFromDirectory(cwd, toolSessionId) {
11618
- const dir = import_node_path9.default.join(
11651
+ const dir = import_node_path12.default.join(
11619
11652
  this.projectsRoot,
11620
11653
  cwdToHashDir(cwd),
11621
11654
  toolSessionId,
@@ -11623,7 +11656,7 @@ var init_claude_history = __esm({
11623
11656
  );
11624
11657
  let entries;
11625
11658
  try {
11626
- entries = import_node_fs10.default.readdirSync(dir, { withFileTypes: true });
11659
+ entries = import_node_fs13.default.readdirSync(dir, { withFileTypes: true });
11627
11660
  } catch (err) {
11628
11661
  if (err.code === "ENOENT") return null;
11629
11662
  return null;
@@ -11633,7 +11666,7 @@ var init_claude_history = __esm({
11633
11666
  if (!e.isFile()) continue;
11634
11667
  if (!e.name.startsWith("agent-") || !e.name.endsWith(".jsonl")) continue;
11635
11668
  const subagentId = e.name.slice("agent-".length, -".jsonl".length);
11636
- const filePath = import_node_path9.default.join(dir, e.name);
11669
+ const filePath = import_node_path12.default.join(dir, e.name);
11637
11670
  const lines = readJsonlLines(filePath);
11638
11671
  let firstText = "";
11639
11672
  let messageCount = 0;
@@ -11650,7 +11683,7 @@ var init_claude_history = __esm({
11650
11683
  return out;
11651
11684
  }
11652
11685
  listSubagentsFromMainJsonl(cwd, toolSessionId) {
11653
- const file = import_node_path9.default.join(
11686
+ const file = import_node_path12.default.join(
11654
11687
  this.projectsRoot,
11655
11688
  cwdToHashDir(cwd),
11656
11689
  `${toolSessionId}.jsonl`
@@ -11685,7 +11718,7 @@ var init_claude_history = __esm({
11685
11718
  }
11686
11719
  // 独立文件路径:agent-<subagentId>.jsonl;文件不存在返回 null 让调用方回退旧实现
11687
11720
  readSubagentFromFile(cwd, toolSessionId, subagentId) {
11688
- const file = import_node_path9.default.join(
11721
+ const file = import_node_path12.default.join(
11689
11722
  this.projectsRoot,
11690
11723
  cwdToHashDir(cwd),
11691
11724
  toolSessionId,
@@ -11694,7 +11727,7 @@ var init_claude_history = __esm({
11694
11727
  );
11695
11728
  let exists = false;
11696
11729
  try {
11697
- exists = import_node_fs10.default.statSync(file).isFile();
11730
+ exists = import_node_fs13.default.statSync(file).isFile();
11698
11731
  } catch {
11699
11732
  return null;
11700
11733
  }
@@ -11713,7 +11746,7 @@ var init_claude_history = __esm({
11713
11746
  * "那一刻每个 tracked 文件对应的 backup 文件名"
11714
11747
  */
11715
11748
  readFileHistorySnapshots(args) {
11716
- const file = import_node_path9.default.join(
11749
+ const file = import_node_path12.default.join(
11717
11750
  this.projectsRoot,
11718
11751
  cwdToHashDir(args.cwd),
11719
11752
  `${args.toolSessionId}.jsonl`
@@ -11758,7 +11791,7 @@ var init_claude_history = __esm({
11758
11791
  for (const [anchorId, target] of snapshots) {
11759
11792
  let hasAny = false;
11760
11793
  for (const [rawPath, backup] of Object.entries(target)) {
11761
- const absPath = import_node_path9.default.isAbsolute(rawPath) ? rawPath : import_node_path9.default.join(args.cwd, rawPath);
11794
+ const absPath = import_node_path12.default.isAbsolute(rawPath) ? rawPath : import_node_path12.default.join(args.cwd, rawPath);
11762
11795
  const backupContent = readBackupContent(
11763
11796
  this.fileHistoryRoot,
11764
11797
  args.toolSessionId,
@@ -11798,7 +11831,7 @@ var init_claude_history = __esm({
11798
11831
  let totalInsertions = 0;
11799
11832
  let totalDeletions = 0;
11800
11833
  for (const [rawPath, backup] of Object.entries(target)) {
11801
- const absPath = import_node_path9.default.isAbsolute(rawPath) ? rawPath : import_node_path9.default.join(args.cwd, rawPath);
11834
+ const absPath = import_node_path12.default.isAbsolute(rawPath) ? rawPath : import_node_path12.default.join(args.cwd, rawPath);
11802
11835
  const backupContent = readBackupContent(
11803
11836
  this.fileHistoryRoot,
11804
11837
  args.toolSessionId,
@@ -11845,7 +11878,7 @@ var init_claude_history = __esm({
11845
11878
  };
11846
11879
  }
11847
11880
  readSubagentFromMainJsonl(cwd, toolSessionId, subagentId) {
11848
- const file = import_node_path9.default.join(
11881
+ const file = import_node_path12.default.join(
11849
11882
  this.projectsRoot,
11850
11883
  cwdToHashDir(cwd),
11851
11884
  `${toolSessionId}.jsonl`
@@ -11870,13 +11903,13 @@ var init_claude_history = __esm({
11870
11903
  function probeViaWhich() {
11871
11904
  try {
11872
11905
  const out = (0, import_node_child_process2.execFileSync)("which", ["claude"], { encoding: "utf8" }).trim();
11873
- if (out && import_node_fs11.default.existsSync(out)) return out;
11906
+ if (out && import_node_fs14.default.existsSync(out)) return out;
11874
11907
  } catch {
11875
11908
  }
11876
11909
  return null;
11877
11910
  }
11878
11911
  async function probeClaude(env = process.env) {
11879
- if (env.CLAUDE_BIN && import_node_fs11.default.existsSync(env.CLAUDE_BIN)) {
11912
+ if (env.CLAUDE_BIN && import_node_fs14.default.existsSync(env.CLAUDE_BIN)) {
11880
11913
  return { available: true, path: env.CLAUDE_BIN };
11881
11914
  }
11882
11915
  const w2 = probeViaWhich();
@@ -12211,10 +12244,10 @@ function parseAttachment(obj) {
12211
12244
  const memories = raw.map((m2) => {
12212
12245
  if (!m2 || typeof m2 !== "object") return null;
12213
12246
  const rec3 = m2;
12214
- const path63 = typeof rec3.path === "string" ? rec3.path : null;
12247
+ const path66 = typeof rec3.path === "string" ? rec3.path : null;
12215
12248
  const content = typeof rec3.content === "string" ? rec3.content : null;
12216
- if (!path63 || content == null) return null;
12217
- const out = { path: path63, content };
12249
+ if (!path66 || content == null) return null;
12250
+ const out = { path: path66, content };
12218
12251
  if (typeof rec3.mtimeMs === "number") out.mtimeMs = rec3.mtimeMs;
12219
12252
  return out;
12220
12253
  }).filter((m2) => m2 !== null);
@@ -12318,13 +12351,13 @@ function encodeClaudeStdin(text) {
12318
12351
  };
12319
12352
  return JSON.stringify(frame) + "\n";
12320
12353
  }
12321
- var import_node_child_process, import_node_child_process2, import_node_fs11, ATTACHMENT_SILENT_SUBTYPES2, unknownTypeHandler, ATTACHMENT_RE, IMAGE_EXT_MIME, CLAUDE_MODELS, CLAUDE_PERMISSION_MODES, CLAUDE_CAPABILITIES, ClaudeAdapter;
12354
+ var import_node_child_process, import_node_child_process2, import_node_fs14, ATTACHMENT_SILENT_SUBTYPES2, unknownTypeHandler, ATTACHMENT_RE, IMAGE_EXT_MIME, CLAUDE_MODELS, CLAUDE_PERMISSION_MODES, CLAUDE_CAPABILITIES, ClaudeAdapter;
12322
12355
  var init_claude = __esm({
12323
12356
  "src/tools/claude.ts"() {
12324
12357
  "use strict";
12325
12358
  import_node_child_process = require("child_process");
12326
12359
  import_node_child_process2 = require("child_process");
12327
- import_node_fs11 = __toESM(require("fs"), 1);
12360
+ import_node_fs14 = __toESM(require("fs"), 1);
12328
12361
  init_protocol();
12329
12362
  init_claude_history();
12330
12363
  init_tool_result_extra();
@@ -33178,8 +33211,8 @@ var require_utils = __commonJS({
33178
33211
  var result = transform[inputType][outputType](input);
33179
33212
  return result;
33180
33213
  };
33181
- exports2.resolve = function(path63) {
33182
- var parts = path63.split("/");
33214
+ exports2.resolve = function(path66) {
33215
+ var parts = path66.split("/");
33183
33216
  var result = [];
33184
33217
  for (var index = 0; index < parts.length; index++) {
33185
33218
  var part = parts[index];
@@ -39032,18 +39065,18 @@ var require_object = __commonJS({
39032
39065
  var object = new ZipObject(name, zipObjectContent, o);
39033
39066
  this.files[name] = object;
39034
39067
  };
39035
- var parentFolder = function(path63) {
39036
- if (path63.slice(-1) === "/") {
39037
- path63 = path63.substring(0, path63.length - 1);
39068
+ var parentFolder = function(path66) {
39069
+ if (path66.slice(-1) === "/") {
39070
+ path66 = path66.substring(0, path66.length - 1);
39038
39071
  }
39039
- var lastSlash = path63.lastIndexOf("/");
39040
- return lastSlash > 0 ? path63.substring(0, lastSlash) : "";
39072
+ var lastSlash = path66.lastIndexOf("/");
39073
+ return lastSlash > 0 ? path66.substring(0, lastSlash) : "";
39041
39074
  };
39042
- var forceTrailingSlash = function(path63) {
39043
- if (path63.slice(-1) !== "/") {
39044
- path63 += "/";
39075
+ var forceTrailingSlash = function(path66) {
39076
+ if (path66.slice(-1) !== "/") {
39077
+ path66 += "/";
39045
39078
  }
39046
- return path63;
39079
+ return path66;
39047
39080
  };
39048
39081
  var folderAdd = function(name, createFolders) {
39049
39082
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
@@ -40045,7 +40078,7 @@ var require_lib3 = __commonJS({
40045
40078
  // src/run-case/recorder.ts
40046
40079
  function startRunCaseRecorder(opts) {
40047
40080
  const now = opts.now ?? Date.now;
40048
- const dir = import_node_path51.default.dirname(opts.recordPath);
40081
+ const dir = import_node_path54.default.dirname(opts.recordPath);
40049
40082
  let stream = null;
40050
40083
  let closing = false;
40051
40084
  let closedSettled = false;
@@ -40059,8 +40092,8 @@ function startRunCaseRecorder(opts) {
40059
40092
  });
40060
40093
  const ensureStream = () => {
40061
40094
  if (stream) return stream;
40062
- import_node_fs39.default.mkdirSync(dir, { recursive: true });
40063
- stream = import_node_fs39.default.createWriteStream(opts.recordPath, { flags: "a" });
40095
+ import_node_fs42.default.mkdirSync(dir, { recursive: true });
40096
+ stream = import_node_fs42.default.createWriteStream(opts.recordPath, { flags: "a" });
40064
40097
  stream.on("close", () => closedResolve());
40065
40098
  return stream;
40066
40099
  };
@@ -40085,12 +40118,12 @@ function startRunCaseRecorder(opts) {
40085
40118
  };
40086
40119
  return { tap, close, closed };
40087
40120
  }
40088
- var import_node_fs39, import_node_path51;
40121
+ var import_node_fs42, import_node_path54;
40089
40122
  var init_recorder = __esm({
40090
40123
  "src/run-case/recorder.ts"() {
40091
40124
  "use strict";
40092
- import_node_fs39 = __toESM(require("fs"), 1);
40093
- import_node_path51 = __toESM(require("path"), 1);
40125
+ import_node_fs42 = __toESM(require("fs"), 1);
40126
+ import_node_path54 = __toESM(require("path"), 1);
40094
40127
  }
40095
40128
  });
40096
40129
 
@@ -40133,7 +40166,7 @@ var init_wire = __esm({
40133
40166
  // src/run-case/controller.ts
40134
40167
  async function runController(opts) {
40135
40168
  const now = opts.now ?? Date.now;
40136
- const cwd = opts.cwd ?? (0, import_node_fs40.mkdtempSync)(import_node_path52.default.join(import_node_os21.default.tmpdir(), "clawd-runcase-"));
40169
+ const cwd = opts.cwd ?? (0, import_node_fs43.mkdtempSync)(import_node_path55.default.join(import_node_os21.default.tmpdir(), "clawd-runcase-"));
40137
40170
  const ownsCwd = opts.cwd === void 0;
40138
40171
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
40139
40172
  const spawnCtx = { cwd };
@@ -40294,19 +40327,19 @@ async function runController(opts) {
40294
40327
  if (sigintHandler) process.off("SIGINT", sigintHandler);
40295
40328
  if (ownsCwd) {
40296
40329
  try {
40297
- (0, import_node_fs40.rmSync)(cwd, { recursive: true, force: true });
40330
+ (0, import_node_fs43.rmSync)(cwd, { recursive: true, force: true });
40298
40331
  } catch {
40299
40332
  }
40300
40333
  }
40301
40334
  return exitCode ?? 0;
40302
40335
  }
40303
- var import_node_fs40, import_node_os21, import_node_path52;
40336
+ var import_node_fs43, import_node_os21, import_node_path55;
40304
40337
  var init_controller = __esm({
40305
40338
  "src/run-case/controller.ts"() {
40306
40339
  "use strict";
40307
- import_node_fs40 = require("fs");
40340
+ import_node_fs43 = require("fs");
40308
40341
  import_node_os21 = __toESM(require("os"), 1);
40309
- import_node_path52 = __toESM(require("path"), 1);
40342
+ import_node_path55 = __toESM(require("path"), 1);
40310
40343
  init_claude();
40311
40344
  init_stdout_splitter();
40312
40345
  init_permission_stdio();
@@ -40568,8 +40601,8 @@ Env (advanced):
40568
40601
  `;
40569
40602
 
40570
40603
  // src/index.ts
40571
- var import_node_path50 = __toESM(require("path"), 1);
40572
- var import_node_fs38 = __toESM(require("fs"), 1);
40604
+ var import_node_path53 = __toESM(require("path"), 1);
40605
+ var import_node_fs41 = __toESM(require("fs"), 1);
40573
40606
  var import_node_os20 = __toESM(require("os"), 1);
40574
40607
 
40575
40608
  // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
@@ -40620,7 +40653,7 @@ function v4(options, buf, offset) {
40620
40653
  var v4_default = v4;
40621
40654
 
40622
40655
  // src/index.ts
40623
- var import_node_url3 = require("url");
40656
+ var import_node_url4 = require("url");
40624
40657
 
40625
40658
  // src/logger.ts
40626
40659
  var import_node_fs2 = __toESM(require("fs"), 1);
@@ -45229,14 +45262,6 @@ var DEFAULT_PERSONAS = [
45229
45262
  public: false,
45230
45263
  sandboxProfile: DEFAULT_BYPASS_PROFILE
45231
45264
  },
45232
- {
45233
- personaId: "persona-builder",
45234
- label: "\u4EBA\u683C\u8BBE\u8BA1\u5E08",
45235
- model: "opus",
45236
- iconKey: "reading",
45237
- public: false,
45238
- sandboxProfile: DEFAULT_BYPASS_PROFILE
45239
- },
45240
45265
  {
45241
45266
  personaId: "persona-bug-fixer",
45242
45267
  label: "Bug \u4FEE\u590D\u5E08",
@@ -45572,12 +45597,187 @@ function tryLoadViewerAssets(logger) {
45572
45597
  return null;
45573
45598
  }
45574
45599
 
45600
+ // src/share-ui/load.ts
45601
+ var import_node_fs11 = __toESM(require("fs"), 1);
45602
+ var import_node_path10 = __toESM(require("path"), 1);
45603
+ var import_node_url3 = require("url");
45604
+
45605
+ // src/share-ui/asset-loader.ts
45606
+ var import_node_fs10 = __toESM(require("fs"), 1);
45607
+ var import_node_path9 = __toESM(require("path"), 1);
45608
+ function createShareUiAssetLoader(opts) {
45609
+ let indexHtml;
45610
+ try {
45611
+ indexHtml = import_node_fs10.default.readFileSync(opts.indexHtmlPath, "utf8");
45612
+ } catch (err) {
45613
+ throw new Error(`share-ui index.html not found at ${opts.indexHtmlPath}: ${err.message}`);
45614
+ }
45615
+ const assetsDir = import_node_path9.default.resolve(opts.assetsDir);
45616
+ return {
45617
+ renderIndexHtml() {
45618
+ return indexHtml;
45619
+ },
45620
+ resolveAssetPath(rel) {
45621
+ const resolved = import_node_path9.default.resolve(assetsDir, rel);
45622
+ if (resolved !== assetsDir && !resolved.startsWith(assetsDir + import_node_path9.default.sep)) return null;
45623
+ try {
45624
+ if (import_node_fs10.default.statSync(resolved).isFile()) return resolved;
45625
+ } catch {
45626
+ }
45627
+ return null;
45628
+ }
45629
+ };
45630
+ }
45631
+
45632
+ // src/share-ui/load.ts
45633
+ var import_meta3 = {};
45634
+ function tryLoadShareUi(logger) {
45635
+ const candidates = [];
45636
+ try {
45637
+ const here = import_node_path10.default.dirname((0, import_node_url3.fileURLToPath)(import_meta3.url));
45638
+ candidates.push(here);
45639
+ candidates.push(import_node_path10.default.resolve(here, ".."));
45640
+ candidates.push(import_node_path10.default.resolve(here, "..", "..", "dist"));
45641
+ } catch {
45642
+ }
45643
+ if (process.argv[1]) {
45644
+ candidates.push(import_node_path10.default.dirname(process.argv[1]));
45645
+ }
45646
+ for (const root of candidates) {
45647
+ const shareUiDir = import_node_path10.default.join(root, "share-ui");
45648
+ const indexHtmlPath = import_node_path10.default.join(shareUiDir, "guest.html");
45649
+ const assetsDir = import_node_path10.default.join(shareUiDir, "assets");
45650
+ try {
45651
+ if (import_node_fs11.default.statSync(indexHtmlPath).isFile() && import_node_fs11.default.statSync(assetsDir).isDirectory()) {
45652
+ logger?.info("share-ui.assets-root.resolved", { root: shareUiDir });
45653
+ return createShareUiAssetLoader({ indexHtmlPath, assetsDir });
45654
+ }
45655
+ } catch {
45656
+ }
45657
+ }
45658
+ logger?.warn("share-ui.assets-not-found", {
45659
+ candidates,
45660
+ hint: "run `pnpm -F @clawos/ext-clawd-ui build && pnpm -F @clawos-dev/clawd build`"
45661
+ });
45662
+ return null;
45663
+ }
45664
+
45665
+ // src/visitor/visitor-token.ts
45666
+ var import_node_crypto4 = __toESM(require("crypto"), 1);
45667
+
45668
+ // ../protocol/src/index.ts
45669
+ init_runtime();
45670
+
45671
+ // src/visitor/visitor-token.ts
45672
+ function hmac(secret, body) {
45673
+ return import_node_crypto4.default.createHmac("sha256", secret).update(body).digest("base64url");
45674
+ }
45675
+ function signVisitorToken(secret, payload) {
45676
+ const body = Buffer.from(JSON.stringify(payload)).toString("base64url");
45677
+ return `${body}.${hmac(secret, body)}`;
45678
+ }
45679
+ function verifyVisitorToken(secret, token, now) {
45680
+ const dot = token.indexOf(".");
45681
+ if (dot <= 0) return { ok: false, reason: "BAD_FORMAT" };
45682
+ const body = token.slice(0, dot);
45683
+ const sig = token.slice(dot + 1);
45684
+ const expected = hmac(secret, body);
45685
+ if (sig.length !== expected.length) return { ok: false, reason: "BAD_SIGNATURE" };
45686
+ if (!import_node_crypto4.default.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
45687
+ return { ok: false, reason: "BAD_SIGNATURE" };
45688
+ }
45689
+ let payload;
45690
+ try {
45691
+ const parsed = VisitorTokenPayloadSchema.safeParse(
45692
+ JSON.parse(Buffer.from(body, "base64url").toString("utf8"))
45693
+ );
45694
+ if (!parsed.success) return { ok: false, reason: "BAD_FORMAT" };
45695
+ payload = parsed.data;
45696
+ } catch {
45697
+ return { ok: false, reason: "BAD_FORMAT" };
45698
+ }
45699
+ if (now >= payload.exp) return { ok: false, reason: "EXPIRED" };
45700
+ return { ok: true, payload };
45701
+ }
45702
+
45703
+ // src/visitor/visitor-login.ts
45704
+ function buildVisitorLogin(deps) {
45705
+ return async function exchangeVisitorToken(args) {
45706
+ let id;
45707
+ try {
45708
+ id = await deps.fetchUserInfo(args.ttcToken);
45709
+ } catch {
45710
+ return { ok: false, reason: "BAD_TTC_TOKEN" };
45711
+ }
45712
+ deps.visitorStore.upsert({
45713
+ visitorId: id.ownerId,
45714
+ displayName: id.displayName,
45715
+ provider: id.provider,
45716
+ ...id.avatarUrl ? { avatarUrl: id.avatarUrl } : {},
45717
+ now: deps.now()
45718
+ });
45719
+ const iat = Math.floor(deps.now() / 1e3);
45720
+ const visitorToken = signVisitorToken(deps.getSecret(), {
45721
+ v: 1,
45722
+ visitorId: id.ownerId,
45723
+ displayName: id.displayName,
45724
+ provider: id.provider,
45725
+ iat,
45726
+ exp: iat + deps.ttlSeconds
45727
+ });
45728
+ return { ok: true, visitorToken };
45729
+ };
45730
+ }
45731
+
45732
+ // src/visitor/visitor-store.ts
45733
+ var import_node_fs12 = __toESM(require("fs"), 1);
45734
+ var import_node_path11 = __toESM(require("path"), 1);
45735
+ function createVisitorStore(opts) {
45736
+ const file = import_node_path11.default.join(opts.dir, "visitors.json");
45737
+ const read = () => {
45738
+ try {
45739
+ return JSON.parse(import_node_fs12.default.readFileSync(file, "utf8"));
45740
+ } catch {
45741
+ return [];
45742
+ }
45743
+ };
45744
+ const write = (rows) => {
45745
+ import_node_fs12.default.mkdirSync(opts.dir, { recursive: true });
45746
+ import_node_fs12.default.writeFileSync(file, JSON.stringify(rows, null, 2));
45747
+ };
45748
+ return {
45749
+ upsert(r) {
45750
+ const rows = read();
45751
+ const ex = rows.find((x) => x.visitorId === r.visitorId);
45752
+ if (ex) {
45753
+ ex.displayName = r.displayName;
45754
+ ex.provider = r.provider;
45755
+ ex.lastSeen = r.now;
45756
+ if (r.avatarUrl) ex.avatarUrl = r.avatarUrl;
45757
+ } else {
45758
+ rows.push({
45759
+ visitorId: r.visitorId,
45760
+ displayName: r.displayName,
45761
+ provider: r.provider,
45762
+ ...r.avatarUrl ? { avatarUrl: r.avatarUrl } : {},
45763
+ firstSeen: r.now,
45764
+ lastSeen: r.now
45765
+ });
45766
+ }
45767
+ write(rows);
45768
+ },
45769
+ list() {
45770
+ return read();
45771
+ }
45772
+ };
45773
+ }
45774
+
45575
45775
  // src/index.ts
45576
45776
  init_claude();
45577
45777
 
45578
45778
  // src/tools/codex.ts
45579
45779
  var import_node_child_process4 = require("child_process");
45580
- var import_node_fs12 = __toESM(require("fs"), 1);
45780
+ var import_node_fs15 = __toESM(require("fs"), 1);
45581
45781
 
45582
45782
  // src/tools/codex-session.ts
45583
45783
  var import_node_child_process3 = require("child_process");
@@ -45825,8 +46025,8 @@ function turnStartInput(text) {
45825
46025
  const items = [];
45826
46026
  let leftover = text;
45827
46027
  for (const m2 of text.matchAll(SKILL_RE)) {
45828
- const [marker, name, path63] = m2;
45829
- items.push({ type: "skill", name, path: path63 });
46028
+ const [marker, name, path66] = m2;
46029
+ items.push({ type: "skill", name, path: path66 });
45830
46030
  leftover = leftover.replace(marker, "");
45831
46031
  }
45832
46032
  for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
@@ -46010,10 +46210,10 @@ function sanitizeCodexModel(model) {
46010
46210
  return CODEX_MODELS.some((m2) => m2.id === model) ? model : void 0;
46011
46211
  }
46012
46212
  async function probeCodex(env = process.env) {
46013
- if (env.CODEX_BIN && import_node_fs12.default.existsSync(env.CODEX_BIN)) return { available: true, path: env.CODEX_BIN };
46213
+ if (env.CODEX_BIN && import_node_fs15.default.existsSync(env.CODEX_BIN)) return { available: true, path: env.CODEX_BIN };
46014
46214
  try {
46015
46215
  const out = (0, import_node_child_process4.execFileSync)("which", ["codex"], { encoding: "utf8" }).trim();
46016
- if (out && import_node_fs12.default.existsSync(out)) return { available: true, path: out };
46216
+ if (out && import_node_fs15.default.existsSync(out)) return { available: true, path: out };
46017
46217
  } catch {
46018
46218
  }
46019
46219
  return { available: false };
@@ -46058,9 +46258,9 @@ var CodexAdapter = class {
46058
46258
  };
46059
46259
 
46060
46260
  // src/tools/claude-tui.ts
46061
- var import_node_fs13 = __toESM(require("fs"), 1);
46261
+ var import_node_fs16 = __toESM(require("fs"), 1);
46062
46262
  var import_node_os6 = __toESM(require("os"), 1);
46063
- var import_node_path10 = __toESM(require("path"), 1);
46263
+ var import_node_path13 = __toESM(require("path"), 1);
46064
46264
  var import_headless = __toESM(require_xterm_headless(), 1);
46065
46265
 
46066
46266
  // ../node_modules/.pnpm/@xterm+addon-serialize@0.14.0/node_modules/@xterm/addon-serialize/lib/addon-serialize.mjs
@@ -47194,9 +47394,9 @@ function buildTuiSpawnArgs(ctx, isResume = false) {
47194
47394
  function jsonlExistsForCtx(ctx) {
47195
47395
  if (!ctx.toolSessionId) return false;
47196
47396
  const home = import_node_os6.default.homedir();
47197
- const file = import_node_path10.default.join(home, ".claude", "projects", cwdToHashDir(ctx.cwd), `${ctx.toolSessionId}.jsonl`);
47397
+ const file = import_node_path13.default.join(home, ".claude", "projects", cwdToHashDir(ctx.cwd), `${ctx.toolSessionId}.jsonl`);
47198
47398
  try {
47199
- return import_node_fs13.default.statSync(file).isFile();
47399
+ return import_node_fs16.default.statSync(file).isFile();
47200
47400
  } catch {
47201
47401
  return false;
47202
47402
  }
@@ -47265,10 +47465,10 @@ var PersonaDispatchManager = class {
47265
47465
  };
47266
47466
 
47267
47467
  // src/dispatch/mcp-config.ts
47268
- var import_node_fs14 = __toESM(require("fs"), 1);
47269
- var import_node_path11 = __toESM(require("path"), 1);
47468
+ var import_node_fs17 = __toESM(require("fs"), 1);
47469
+ var import_node_path14 = __toESM(require("path"), 1);
47270
47470
  function dispatchMcpConfigPath(dataDir) {
47271
- return import_node_path11.default.join(dataDir, "dispatch.mcp.json");
47471
+ return import_node_path14.default.join(dataDir, "dispatch.mcp.json");
47272
47472
  }
47273
47473
  function writeDispatchMcpConfig(args) {
47274
47474
  const cfgPath = dispatchMcpConfigPath(args.dataDir);
@@ -47283,16 +47483,16 @@ function writeDispatchMcpConfig(args) {
47283
47483
  }
47284
47484
  }
47285
47485
  };
47286
- import_node_fs14.default.mkdirSync(args.dataDir, { recursive: true });
47287
- import_node_fs14.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
47486
+ import_node_fs17.default.mkdirSync(args.dataDir, { recursive: true });
47487
+ import_node_fs17.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
47288
47488
  return cfgPath;
47289
47489
  }
47290
47490
 
47291
47491
  // src/ticket/mcp-config.ts
47292
- var import_node_fs15 = __toESM(require("fs"), 1);
47293
- var import_node_path12 = __toESM(require("path"), 1);
47492
+ var import_node_fs18 = __toESM(require("fs"), 1);
47493
+ var import_node_path15 = __toESM(require("path"), 1);
47294
47494
  function ticketMcpConfigPath(dataDir) {
47295
- return import_node_path12.default.join(dataDir, "ticket.mcp.json");
47495
+ return import_node_path15.default.join(dataDir, "ticket.mcp.json");
47296
47496
  }
47297
47497
  function writeTicketMcpConfig(args) {
47298
47498
  const cfgPath = ticketMcpConfigPath(args.dataDir);
@@ -47313,16 +47513,16 @@ function writeTicketMcpConfig(args) {
47313
47513
  }
47314
47514
  }
47315
47515
  };
47316
- import_node_fs15.default.mkdirSync(args.dataDir, { recursive: true });
47317
- import_node_fs15.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
47516
+ import_node_fs18.default.mkdirSync(args.dataDir, { recursive: true });
47517
+ import_node_fs18.default.writeFileSync(cfgPath, JSON.stringify(content, null, 2), "utf8");
47318
47518
  return cfgPath;
47319
47519
  }
47320
47520
 
47321
47521
  // src/shift/mcp-config.ts
47322
- var import_node_fs16 = __toESM(require("fs"), 1);
47323
- var import_node_path13 = __toESM(require("path"), 1);
47522
+ var import_node_fs19 = __toESM(require("fs"), 1);
47523
+ var import_node_path16 = __toESM(require("path"), 1);
47324
47524
  function shiftMcpConfigPath(dataDir) {
47325
- return import_node_path13.default.join(dataDir, "shift.mcp.json");
47525
+ return import_node_path16.default.join(dataDir, "shift.mcp.json");
47326
47526
  }
47327
47527
  async function writeShiftMcpConfig(args) {
47328
47528
  const cfgPath = shiftMcpConfigPath(args.dataDir);
@@ -47337,21 +47537,21 @@ async function writeShiftMcpConfig(args) {
47337
47537
  }
47338
47538
  }
47339
47539
  };
47340
- await import_node_fs16.default.promises.mkdir(args.dataDir, { recursive: true });
47341
- await import_node_fs16.default.promises.writeFile(cfgPath, JSON.stringify(content, null, 2), "utf8");
47540
+ await import_node_fs19.default.promises.mkdir(args.dataDir, { recursive: true });
47541
+ await import_node_fs19.default.promises.writeFile(cfgPath, JSON.stringify(content, null, 2), "utf8");
47342
47542
  return cfgPath;
47343
47543
  }
47344
47544
 
47345
47545
  // src/shift/store.ts
47346
47546
  var import_promises = __toESM(require("fs/promises"), 1);
47347
- var import_node_path14 = __toESM(require("path"), 1);
47348
- var import_node_crypto4 = require("crypto");
47547
+ var import_node_path17 = __toESM(require("path"), 1);
47548
+ var import_node_crypto5 = require("crypto");
47349
47549
 
47350
47550
  // src/shift/constants.ts
47351
47551
  var MAX_RUNS_PER_SHIFT = 30;
47352
47552
  var DEFAULT_TIMEOUT_MS = 30 * 6e4;
47353
47553
  var CONSECUTIVE_ERROR_THRESHOLD = 5;
47354
- var TICK_INTERVAL_MS = 1e4;
47554
+ var TICK_INTERVAL_MS = 1e3;
47355
47555
  var STORE_FLUSH_DEBOUNCE_MS = 500;
47356
47556
  var CLOCK_JUMP_THRESHOLD_MS = 5 * 6e4;
47357
47557
 
@@ -47413,7 +47613,7 @@ function createShiftStore(deps) {
47413
47613
  flushTimer = null;
47414
47614
  }
47415
47615
  const content = { version: 1, shifts };
47416
- await import_promises.default.mkdir(import_node_path14.default.dirname(deps.filePath), { recursive: true });
47616
+ await import_promises.default.mkdir(import_node_path17.default.dirname(deps.filePath), { recursive: true });
47417
47617
  const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
47418
47618
  await import_promises.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
47419
47619
  await import_promises.default.rename(tmp, deps.filePath);
@@ -47441,7 +47641,7 @@ function createShiftStore(deps) {
47441
47641
  const nextRunAtMs = computeNextRunAtMs(input.schedule, now) ?? void 0;
47442
47642
  const shift = {
47443
47643
  ...input,
47444
- id: (0, import_node_crypto4.randomUUID)(),
47644
+ id: (0, import_node_crypto5.randomUUID)(),
47445
47645
  createdAtMs: now,
47446
47646
  updatedAtMs: now,
47447
47647
  state: { nextRunAtMs },
@@ -48184,13 +48384,13 @@ function mapSkillsListResponse(res) {
48184
48384
  const r = s ?? {};
48185
48385
  const name = str3(r.name);
48186
48386
  if (!name) continue;
48187
- const path63 = str3(r.path);
48387
+ const path66 = str3(r.path);
48188
48388
  const description = str3(r.description);
48189
48389
  const isPlugin = name.includes(":");
48190
48390
  out.push({
48191
48391
  name,
48192
48392
  source: isPlugin ? "plugin" : "project",
48193
- ...path63 ? { path: path63 } : {},
48393
+ ...path66 ? { path: path66 } : {},
48194
48394
  ...description ? { description } : {},
48195
48395
  ...isPlugin ? { plugin: name.split(":")[0] } : {}
48196
48396
  });
@@ -48229,23 +48429,23 @@ async function listCodexSkills(cwd, deps = {}) {
48229
48429
  }
48230
48430
 
48231
48431
  // src/workspace/browser.ts
48232
- var import_node_fs17 = __toESM(require("fs"), 1);
48432
+ var import_node_fs20 = __toESM(require("fs"), 1);
48233
48433
  var import_node_os7 = __toESM(require("os"), 1);
48234
- var import_node_path15 = __toESM(require("path"), 1);
48434
+ var import_node_path18 = __toESM(require("path"), 1);
48235
48435
  init_protocol();
48236
48436
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
48237
48437
  function resolveInsideCwd(cwd, subpath) {
48238
- const absCwd = import_node_path15.default.resolve(cwd);
48239
- const joined = import_node_path15.default.resolve(absCwd, subpath ?? ".");
48240
- const rel = import_node_path15.default.relative(absCwd, joined);
48241
- if (rel.startsWith("..") || import_node_path15.default.isAbsolute(rel)) {
48438
+ const absCwd = import_node_path18.default.resolve(cwd);
48439
+ const joined = import_node_path18.default.resolve(absCwd, subpath ?? ".");
48440
+ const rel = import_node_path18.default.relative(absCwd, joined);
48441
+ if (rel.startsWith("..") || import_node_path18.default.isAbsolute(rel)) {
48242
48442
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `path escapes cwd: ${subpath}`);
48243
48443
  }
48244
48444
  return joined;
48245
48445
  }
48246
48446
  function ensureCwd(cwd) {
48247
48447
  try {
48248
- const stat = import_node_fs17.default.statSync(cwd);
48448
+ const stat = import_node_fs20.default.statSync(cwd);
48249
48449
  if (!stat.isDirectory()) {
48250
48450
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `not a directory: ${cwd}`);
48251
48451
  }
@@ -48259,7 +48459,7 @@ var WorkspaceBrowser = class {
48259
48459
  const cwd = args.cwd && args.cwd.length > 0 ? args.cwd : import_node_os7.default.homedir();
48260
48460
  ensureCwd(cwd);
48261
48461
  const full = resolveInsideCwd(cwd, args.path);
48262
- const dirents = import_node_fs17.default.readdirSync(full, { withFileTypes: true });
48462
+ const dirents = import_node_fs20.default.readdirSync(full, { withFileTypes: true });
48263
48463
  const entries = [];
48264
48464
  for (const d of dirents) {
48265
48465
  if (!args.showHidden && d.name.startsWith(".")) continue;
@@ -48269,7 +48469,7 @@ var WorkspaceBrowser = class {
48269
48469
  mtime: ""
48270
48470
  };
48271
48471
  try {
48272
- const st = import_node_fs17.default.statSync(import_node_path15.default.join(full, d.name));
48472
+ const st = import_node_fs20.default.statSync(import_node_path18.default.join(full, d.name));
48273
48473
  entry.mtime = new Date(st.mtimeMs).toISOString();
48274
48474
  if (d.isFile()) entry.size = st.size;
48275
48475
  } catch {
@@ -48285,14 +48485,14 @@ var WorkspaceBrowser = class {
48285
48485
  read(args) {
48286
48486
  ensureCwd(args.cwd);
48287
48487
  const full = resolveInsideCwd(args.cwd, args.path);
48288
- const st = import_node_fs17.default.statSync(full);
48488
+ const st = import_node_fs20.default.statSync(full);
48289
48489
  if (!st.isFile()) {
48290
48490
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `not a file: ${args.path}`);
48291
48491
  }
48292
48492
  if (st.size > MAX_FILE_BYTES) {
48293
48493
  throw new ClawdError(ERROR_CODES.FILE_TOO_LARGE, `file > ${MAX_FILE_BYTES} bytes`);
48294
48494
  }
48295
- const buf = import_node_fs17.default.readFileSync(full);
48495
+ const buf = import_node_fs20.default.readFileSync(full);
48296
48496
  const isBinary = buf.includes(0);
48297
48497
  if (isBinary) {
48298
48498
  return {
@@ -48314,20 +48514,20 @@ var WorkspaceBrowser = class {
48314
48514
  };
48315
48515
 
48316
48516
  // src/skills/agents-scanner.ts
48317
- var import_node_fs18 = __toESM(require("fs"), 1);
48517
+ var import_node_fs21 = __toESM(require("fs"), 1);
48318
48518
  var import_node_os8 = __toESM(require("os"), 1);
48319
- var import_node_path16 = __toESM(require("path"), 1);
48519
+ var import_node_path19 = __toESM(require("path"), 1);
48320
48520
  var DEFAULT_POLICY_DIR_DARWIN = "/Library/Application Support/ClaudeCode/.claude/agents";
48321
48521
  function isDirLikeSync2(p2) {
48322
48522
  try {
48323
- return import_node_fs18.default.statSync(p2).isDirectory();
48523
+ return import_node_fs21.default.statSync(p2).isDirectory();
48324
48524
  } catch {
48325
48525
  return false;
48326
48526
  }
48327
48527
  }
48328
48528
  function fileExistsSync(p2) {
48329
48529
  try {
48330
- return import_node_fs18.default.statSync(p2).isFile();
48530
+ return import_node_fs21.default.statSync(p2).isFile();
48331
48531
  } catch {
48332
48532
  return false;
48333
48533
  }
@@ -48335,7 +48535,7 @@ function fileExistsSync(p2) {
48335
48535
  function parseAgentFile(filePath) {
48336
48536
  let content;
48337
48537
  try {
48338
- content = import_node_fs18.default.readFileSync(filePath, "utf8");
48538
+ content = import_node_fs21.default.readFileSync(filePath, "utf8");
48339
48539
  } catch {
48340
48540
  return {};
48341
48541
  }
@@ -48348,16 +48548,16 @@ function parseAgentFile(filePath) {
48348
48548
  function scanAgentsDir(dir, source, seen, out) {
48349
48549
  let entries;
48350
48550
  try {
48351
- entries = import_node_fs18.default.readdirSync(dir, { withFileTypes: true });
48551
+ entries = import_node_fs21.default.readdirSync(dir, { withFileTypes: true });
48352
48552
  } catch {
48353
48553
  return;
48354
48554
  }
48355
48555
  for (const ent of entries) {
48356
48556
  if (!ent.name.endsWith(".md")) continue;
48357
- if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path16.default.join(dir, ent.name)))) {
48557
+ if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path19.default.join(dir, ent.name)))) {
48358
48558
  continue;
48359
48559
  }
48360
- const filePath = import_node_path16.default.join(dir, ent.name);
48560
+ const filePath = import_node_path19.default.join(dir, ent.name);
48361
48561
  const baseName = ent.name.replace(/\.md$/, "");
48362
48562
  if (seen.has(baseName)) continue;
48363
48563
  seen.add(baseName);
@@ -48375,12 +48575,12 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
48375
48575
  function walk2(dir, namespaces) {
48376
48576
  let entries;
48377
48577
  try {
48378
- entries = import_node_fs18.default.readdirSync(dir, { withFileTypes: true });
48578
+ entries = import_node_fs21.default.readdirSync(dir, { withFileTypes: true });
48379
48579
  } catch {
48380
48580
  return;
48381
48581
  }
48382
48582
  for (const ent of entries) {
48383
- const childPath = import_node_path16.default.join(dir, ent.name);
48583
+ const childPath = import_node_path19.default.join(dir, ent.name);
48384
48584
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync2(childPath)) {
48385
48585
  walk2(childPath, [...namespaces, ent.name]);
48386
48586
  continue;
@@ -48405,13 +48605,13 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
48405
48605
  walk2(root, []);
48406
48606
  }
48407
48607
  function readInstalledPlugins2(home) {
48408
- const pluginsDir = import_node_path16.default.join(home, ".claude", "plugins");
48409
- const v2 = import_node_path16.default.join(pluginsDir, "installed_plugins_v2.json");
48410
- const v1 = import_node_path16.default.join(pluginsDir, "installed_plugins.json");
48608
+ const pluginsDir = import_node_path19.default.join(home, ".claude", "plugins");
48609
+ const v2 = import_node_path19.default.join(pluginsDir, "installed_plugins_v2.json");
48610
+ const v1 = import_node_path19.default.join(pluginsDir, "installed_plugins.json");
48411
48611
  let raw = null;
48412
48612
  for (const candidate of [v2, v1]) {
48413
48613
  try {
48414
- raw = import_node_fs18.default.readFileSync(candidate, "utf8");
48614
+ raw = import_node_fs21.default.readFileSync(candidate, "utf8");
48415
48615
  break;
48416
48616
  } catch {
48417
48617
  }
@@ -48436,19 +48636,19 @@ function readInstalledPlugins2(home) {
48436
48636
  return out;
48437
48637
  }
48438
48638
  function walkUpProjectAgentsDirs(startCwd, home, seen, out) {
48439
- let cur = import_node_path16.default.resolve(startCwd);
48440
- const fsRoot = import_node_path16.default.parse(cur).root;
48639
+ let cur = import_node_path19.default.resolve(startCwd);
48640
+ const fsRoot = import_node_path19.default.parse(cur).root;
48441
48641
  while (true) {
48442
- scanAgentsDir(import_node_path16.default.join(cur, ".claude", "agents"), "project", seen, out);
48642
+ scanAgentsDir(import_node_path19.default.join(cur, ".claude", "agents"), "project", seen, out);
48443
48643
  let hasGit = false;
48444
48644
  try {
48445
- hasGit = import_node_fs18.default.existsSync(import_node_path16.default.join(cur, ".git"));
48645
+ hasGit = import_node_fs21.default.existsSync(import_node_path19.default.join(cur, ".git"));
48446
48646
  } catch {
48447
48647
  }
48448
48648
  if (hasGit) return;
48449
48649
  if (cur === home) return;
48450
48650
  if (cur === fsRoot) return;
48451
- const parent = import_node_path16.default.dirname(cur);
48651
+ const parent = import_node_path19.default.dirname(cur);
48452
48652
  if (parent === cur) return;
48453
48653
  cur = parent;
48454
48654
  }
@@ -48483,7 +48683,7 @@ var AgentsScanner = class {
48483
48683
  }
48484
48684
  const fsBlock = [];
48485
48685
  scanAgentsDir(
48486
- import_node_path16.default.join(this.home, ".claude", "agents"),
48686
+ import_node_path19.default.join(this.home, ".claude", "agents"),
48487
48687
  "global",
48488
48688
  seen,
48489
48689
  fsBlock
@@ -48497,7 +48697,7 @@ var AgentsScanner = class {
48497
48697
  ...this.extraPluginRoots
48498
48698
  ];
48499
48699
  for (const { name, root } of plugins) {
48500
- const agentsRoot = import_node_path16.default.join(root, "agents");
48700
+ const agentsRoot = import_node_path19.default.join(root, "agents");
48501
48701
  scanPluginAgentsTree(agentsRoot, name, seen, fsBlock);
48502
48702
  }
48503
48703
  return [...builtinBlock, ...fsBlock];
@@ -48505,15 +48705,15 @@ var AgentsScanner = class {
48505
48705
  };
48506
48706
 
48507
48707
  // src/observer/session-observer.ts
48508
- var import_node_fs20 = __toESM(require("fs"), 1);
48708
+ var import_node_fs23 = __toESM(require("fs"), 1);
48509
48709
  var import_node_os10 = __toESM(require("os"), 1);
48510
- var import_node_path18 = __toESM(require("path"), 1);
48710
+ var import_node_path21 = __toESM(require("path"), 1);
48511
48711
  init_claude_history();
48512
48712
 
48513
48713
  // src/observer/subagent-meta-observer.ts
48514
- var import_node_fs19 = __toESM(require("fs"), 1);
48714
+ var import_node_fs22 = __toESM(require("fs"), 1);
48515
48715
  var import_node_os9 = __toESM(require("os"), 1);
48516
- var import_node_path17 = __toESM(require("path"), 1);
48716
+ var import_node_path20 = __toESM(require("path"), 1);
48517
48717
  init_claude_history();
48518
48718
  var META_RE = /^agent-([A-Za-z0-9_-]+)\.meta\.json$/;
48519
48719
  var SubagentMetaObserver = class {
@@ -48526,7 +48726,7 @@ var SubagentMetaObserver = class {
48526
48726
  watches = /* @__PURE__ */ new Map();
48527
48727
  // public for spec only:测试直接拼路径写假 meta.json;生产 start() 内部自己解析
48528
48728
  resolveSubagentDir(cwd, toolSessionId) {
48529
- return import_node_path17.default.join(
48729
+ return import_node_path20.default.join(
48530
48730
  this.home,
48531
48731
  ".claude",
48532
48732
  "projects",
@@ -48556,7 +48756,7 @@ var SubagentMetaObserver = class {
48556
48756
  attachWatcher(w2) {
48557
48757
  if (w2.watcher) return;
48558
48758
  try {
48559
- w2.watcher = import_node_fs19.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
48759
+ w2.watcher = import_node_fs22.default.watch(w2.dirPath, { persistent: false }, (_evt, name) => {
48560
48760
  if (!name) return;
48561
48761
  const m2 = META_RE.exec(String(name));
48562
48762
  if (!m2) return;
@@ -48568,7 +48768,7 @@ var SubagentMetaObserver = class {
48568
48768
  scan(w2) {
48569
48769
  let entries;
48570
48770
  try {
48571
- entries = import_node_fs19.default.readdirSync(w2.dirPath);
48771
+ entries = import_node_fs22.default.readdirSync(w2.dirPath);
48572
48772
  } catch {
48573
48773
  return;
48574
48774
  }
@@ -48582,10 +48782,10 @@ var SubagentMetaObserver = class {
48582
48782
  if (!m2) return;
48583
48783
  const agentId = m2[1];
48584
48784
  if (w2.emitted.has(agentId)) return;
48585
- const file = import_node_path17.default.join(w2.dirPath, name);
48785
+ const file = import_node_path20.default.join(w2.dirPath, name);
48586
48786
  let raw;
48587
48787
  try {
48588
- raw = import_node_fs19.default.readFileSync(file, "utf8");
48788
+ raw = import_node_fs22.default.readFileSync(file, "utf8");
48589
48789
  } catch {
48590
48790
  return;
48591
48791
  }
@@ -48642,14 +48842,14 @@ var SessionObserver = class {
48642
48842
  metaObserver;
48643
48843
  resolveJsonlPath(cwd, toolSessionId, override) {
48644
48844
  if (override) return override;
48645
- return import_node_path18.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
48845
+ return import_node_path21.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
48646
48846
  }
48647
48847
  start(args) {
48648
48848
  this.stop(args.sessionId);
48649
48849
  const filePath = this.resolveJsonlPath(args.cwd, args.toolSessionId, args.jsonlPath);
48650
48850
  let size = 0;
48651
48851
  try {
48652
- size = import_node_fs20.default.statSync(filePath).size;
48852
+ size = import_node_fs23.default.statSync(filePath).size;
48653
48853
  } catch {
48654
48854
  }
48655
48855
  const w2 = {
@@ -48663,10 +48863,10 @@ var SessionObserver = class {
48663
48863
  prevIsRejectSentinel: false
48664
48864
  };
48665
48865
  try {
48666
- import_node_fs20.default.mkdirSync(import_node_path18.default.dirname(filePath), { recursive: true });
48866
+ import_node_fs23.default.mkdirSync(import_node_path21.default.dirname(filePath), { recursive: true });
48667
48867
  } catch {
48668
48868
  }
48669
- w2.watcher = import_node_fs20.default.watch(import_node_path18.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
48869
+ w2.watcher = import_node_fs23.default.watch(import_node_path21.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
48670
48870
  if (!changedName || !filePath.endsWith(changedName)) return;
48671
48871
  this.poll(w2);
48672
48872
  });
@@ -48689,7 +48889,7 @@ var SessionObserver = class {
48689
48889
  // 异常静默吞,不阻塞 watcher 启动
48690
48890
  hydrateMetaTail(w2, maxLines = 200) {
48691
48891
  try {
48692
- const raw = import_node_fs20.default.readFileSync(w2.filePath, "utf8");
48892
+ const raw = import_node_fs23.default.readFileSync(w2.filePath, "utf8");
48693
48893
  if (!raw) return;
48694
48894
  const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
48695
48895
  if (allLines.length === 0) return;
@@ -48713,7 +48913,7 @@ var SessionObserver = class {
48713
48913
  poll(w2) {
48714
48914
  let size = 0;
48715
48915
  try {
48716
- size = import_node_fs20.default.statSync(w2.filePath).size;
48916
+ size = import_node_fs23.default.statSync(w2.filePath).size;
48717
48917
  } catch {
48718
48918
  return;
48719
48919
  }
@@ -48722,11 +48922,11 @@ var SessionObserver = class {
48722
48922
  w2.buf = "";
48723
48923
  }
48724
48924
  if (size === w2.lastSize) return;
48725
- const fd = import_node_fs20.default.openSync(w2.filePath, "r");
48925
+ const fd = import_node_fs23.default.openSync(w2.filePath, "r");
48726
48926
  try {
48727
48927
  const len = size - w2.lastSize;
48728
48928
  const buf = Buffer.alloc(len);
48729
- import_node_fs20.default.readSync(fd, buf, 0, len, w2.lastSize);
48929
+ import_node_fs23.default.readSync(fd, buf, 0, len, w2.lastSize);
48730
48930
  w2.lastSize = size;
48731
48931
  w2.buf += buf.toString("utf8");
48732
48932
  let newlineIndex;
@@ -48749,7 +48949,7 @@ var SessionObserver = class {
48749
48949
  }
48750
48950
  }
48751
48951
  } finally {
48752
- import_node_fs20.default.closeSync(fd);
48952
+ import_node_fs23.default.closeSync(fd);
48753
48953
  }
48754
48954
  }
48755
48955
  // 解析 JSONL 单行:仅当是主链 user 文本行(非 sidechain / 非 sub-agent / message.role='user'
@@ -49493,9 +49693,6 @@ function constantTimeEqual2(a, b2) {
49493
49693
  return diff2 === 0;
49494
49694
  }
49495
49695
 
49496
- // ../protocol/src/index.ts
49497
- init_runtime();
49498
-
49499
49696
  // src/transport/connection-context.ts
49500
49697
  function ownerContext(ownerPrincipalId, displayName) {
49501
49698
  return {
@@ -49514,11 +49711,26 @@ function connectGuestContext(subDevice, subOwner, provider, displayName) {
49514
49711
  provider
49515
49712
  };
49516
49713
  }
49714
+ function visitorGuestContext(p2) {
49715
+ return {
49716
+ principal: { id: `visitor-${p2.visitorId}`, kind: "guest", displayName: p2.displayName },
49717
+ grants: PERSONAL_CAP_GRANTS.map((g2) => ({
49718
+ resource: { ...g2.resource },
49719
+ actions: [...g2.actions]
49720
+ })),
49721
+ ownerId: p2.visitorId,
49722
+ provider: p2.provider
49723
+ };
49724
+ }
49517
49725
  async function authenticate(token, deps) {
49518
49726
  if (!token) return { ok: false, code: "NO_TOKEN" };
49519
49727
  if (deps.isOwnerToken(token)) {
49520
49728
  return { ok: true, context: ownerContext(deps.ownerPrincipalId, deps.ownerDisplayName) };
49521
49729
  }
49730
+ if (deps.verifyVisitorToken) {
49731
+ const vr = deps.verifyVisitorToken(token);
49732
+ if (vr.ok) return { ok: true, context: visitorGuestContext(vr.payload) };
49733
+ }
49522
49734
  if (!deps.verifyConnectToken) return { ok: false, code: "BAD_TOKEN" };
49523
49735
  const result = deps.verifyConnectToken(token);
49524
49736
  if (!result.ok) {
@@ -49533,14 +49745,14 @@ async function authenticate(token, deps) {
49533
49745
  }
49534
49746
 
49535
49747
  // src/permission/capability-store.ts
49536
- var fs24 = __toESM(require("fs"), 1);
49537
- var path23 = __toESM(require("path"), 1);
49748
+ var fs27 = __toESM(require("fs"), 1);
49749
+ var path26 = __toESM(require("path"), 1);
49538
49750
  var CAPABILITIES_FILE_NAME = "capabilities.json";
49539
49751
  var FILE_VERSION = 1;
49540
49752
  var CapabilityStore = class {
49541
49753
  constructor(dataDir) {
49542
49754
  this.dataDir = dataDir;
49543
- fs24.mkdirSync(dataDir, { recursive: true });
49755
+ fs27.mkdirSync(dataDir, { recursive: true });
49544
49756
  this.cache = this.readFromDisk();
49545
49757
  }
49546
49758
  dataDir;
@@ -49564,13 +49776,13 @@ var CapabilityStore = class {
49564
49776
  this.flush();
49565
49777
  }
49566
49778
  filePath() {
49567
- return path23.join(this.dataDir, CAPABILITIES_FILE_NAME);
49779
+ return path26.join(this.dataDir, CAPABILITIES_FILE_NAME);
49568
49780
  }
49569
49781
  readFromDisk() {
49570
49782
  const file = this.filePath();
49571
49783
  let raw;
49572
49784
  try {
49573
- raw = fs24.readFileSync(file, "utf8");
49785
+ raw = fs27.readFileSync(file, "utf8");
49574
49786
  } catch (err) {
49575
49787
  if (err?.code === "ENOENT") return [];
49576
49788
  return [];
@@ -49598,17 +49810,17 @@ var CapabilityStore = class {
49598
49810
  }
49599
49811
  atomicWrite(file, content) {
49600
49812
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
49601
- fs24.writeFileSync(tmp, content, { mode: 384 });
49602
- fs24.renameSync(tmp, file);
49813
+ fs27.writeFileSync(tmp, content, { mode: 384 });
49814
+ fs27.renameSync(tmp, file);
49603
49815
  try {
49604
- fs24.chmodSync(file, 384);
49816
+ fs27.chmodSync(file, 384);
49605
49817
  } catch {
49606
49818
  }
49607
49819
  }
49608
49820
  };
49609
49821
 
49610
49822
  // src/permission/capability-registry.ts
49611
- var crypto4 = __toESM(require("crypto"), 1);
49823
+ var crypto5 = __toESM(require("crypto"), 1);
49612
49824
  var CapabilityRegistry = class {
49613
49825
  constructor(store, now = () => Date.now()) {
49614
49826
  this.store = store;
@@ -49661,7 +49873,7 @@ var CapabilityRegistry = class {
49661
49873
  }
49662
49874
  };
49663
49875
  function sha256Hex(s) {
49664
- return crypto4.createHash("sha256").update(s).digest("hex");
49876
+ return crypto5.createHash("sha256").update(s).digest("hex");
49665
49877
  }
49666
49878
 
49667
49879
  // src/permission/capability-manager.ts
@@ -49694,14 +49906,14 @@ var CapabilityManager = class {
49694
49906
  };
49695
49907
 
49696
49908
  // src/permission/cleanup.ts
49697
- var fs25 = __toESM(require("fs"), 1);
49909
+ var fs28 = __toESM(require("fs"), 1);
49698
49910
  function cleanupGuestSessionsForCapability(cap, factory) {
49699
49911
  const removed = [];
49700
49912
  for (const g2 of cap.grants) {
49701
49913
  if (g2.resource.type !== "persona") continue;
49702
49914
  const dir = factory.vmGuestRoot(g2.resource.id, cap.id);
49703
49915
  try {
49704
- fs25.rmSync(dir, { recursive: true, force: true });
49916
+ fs28.rmSync(dir, { recursive: true, force: true });
49705
49917
  removed.push(dir);
49706
49918
  } catch {
49707
49919
  }
@@ -49710,13 +49922,13 @@ function cleanupGuestSessionsForCapability(cap, factory) {
49710
49922
  }
49711
49923
 
49712
49924
  // src/inbox/inbox-store.ts
49713
- var fs26 = __toESM(require("fs"), 1);
49714
- var path24 = __toESM(require("path"), 1);
49925
+ var fs29 = __toESM(require("fs"), 1);
49926
+ var path27 = __toESM(require("path"), 1);
49715
49927
  var INBOX_SUBDIR = "inbox";
49716
49928
  var InboxStore = class {
49717
49929
  constructor(dataDir) {
49718
49930
  this.dataDir = dataDir;
49719
- fs26.mkdirSync(this.dirPath(), { recursive: true });
49931
+ fs29.mkdirSync(this.dirPath(), { recursive: true });
49720
49932
  }
49721
49933
  dataDir;
49722
49934
  /**
@@ -49728,7 +49940,7 @@ var InboxStore = class {
49728
49940
  const file = this.filePath(peerDeviceId);
49729
49941
  let raw;
49730
49942
  try {
49731
- raw = fs26.readFileSync(file, "utf8");
49943
+ raw = fs29.readFileSync(file, "utf8");
49732
49944
  } catch (err) {
49733
49945
  if (err?.code === "ENOENT") return [];
49734
49946
  return [];
@@ -49744,7 +49956,7 @@ var InboxStore = class {
49744
49956
  const dir = this.dirPath();
49745
49957
  let entries;
49746
49958
  try {
49747
- entries = fs26.readdirSync(dir);
49959
+ entries = fs29.readdirSync(dir);
49748
49960
  } catch (err) {
49749
49961
  if (err?.code === "ENOENT") return [];
49750
49962
  return [];
@@ -49760,9 +49972,9 @@ var InboxStore = class {
49760
49972
  if (existing.some((m2) => m2.id === message.id)) return;
49761
49973
  const file = this.filePath(message.peerDeviceId);
49762
49974
  const line = JSON.stringify(message) + "\n";
49763
- fs26.appendFileSync(file, line, { mode: 384 });
49975
+ fs29.appendFileSync(file, line, { mode: 384 });
49764
49976
  try {
49765
- fs26.chmodSync(file, 384);
49977
+ fs29.chmodSync(file, 384);
49766
49978
  } catch {
49767
49979
  }
49768
49980
  }
@@ -49792,7 +50004,7 @@ var InboxStore = class {
49792
50004
  removeByPeerDeviceId(peerDeviceId) {
49793
50005
  const file = this.filePath(peerDeviceId);
49794
50006
  try {
49795
- fs26.unlinkSync(file);
50007
+ fs29.unlinkSync(file);
49796
50008
  } catch (err) {
49797
50009
  if (err?.code === "ENOENT") return;
49798
50010
  }
@@ -49801,18 +50013,18 @@ var InboxStore = class {
49801
50013
  const file = this.filePath(peerDeviceId);
49802
50014
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
49803
50015
  const content = messages.map((m2) => JSON.stringify(m2)).join("\n") + (messages.length > 0 ? "\n" : "");
49804
- fs26.writeFileSync(tmp, content, { mode: 384 });
49805
- fs26.renameSync(tmp, file);
50016
+ fs29.writeFileSync(tmp, content, { mode: 384 });
50017
+ fs29.renameSync(tmp, file);
49806
50018
  try {
49807
- fs26.chmodSync(file, 384);
50019
+ fs29.chmodSync(file, 384);
49808
50020
  } catch {
49809
50021
  }
49810
50022
  }
49811
50023
  dirPath() {
49812
- return path24.join(this.dataDir, INBOX_SUBDIR);
50024
+ return path27.join(this.dataDir, INBOX_SUBDIR);
49813
50025
  }
49814
50026
  filePath(peerDeviceId) {
49815
- return path24.join(this.dirPath(), `${peerDeviceId}.jsonl`);
50027
+ return path27.join(this.dirPath(), `${peerDeviceId}.jsonl`);
49816
50028
  }
49817
50029
  };
49818
50030
  function parseAllLines(raw) {
@@ -49899,8 +50111,8 @@ var InboxManager = class {
49899
50111
  };
49900
50112
 
49901
50113
  // src/state/contact-store.ts
49902
- var fs27 = __toESM(require("fs"), 1);
49903
- var path25 = __toESM(require("path"), 1);
50114
+ var fs30 = __toESM(require("fs"), 1);
50115
+ var path28 = __toESM(require("path"), 1);
49904
50116
  var FILE_NAME = "contacts.json";
49905
50117
  var ContactStore = class {
49906
50118
  constructor(dataDir) {
@@ -49910,10 +50122,10 @@ var ContactStore = class {
49910
50122
  contacts = /* @__PURE__ */ new Map();
49911
50123
  load() {
49912
50124
  this.contacts.clear();
49913
- const file = path25.join(this.dataDir, FILE_NAME);
50125
+ const file = path28.join(this.dataDir, FILE_NAME);
49914
50126
  let raw;
49915
50127
  try {
49916
- raw = fs27.readFileSync(file, "utf8");
50128
+ raw = fs30.readFileSync(file, "utf8");
49917
50129
  } catch (err) {
49918
50130
  if (err?.code !== "ENOENT") this.renameBak(file);
49919
50131
  return;
@@ -49956,27 +50168,27 @@ var ContactStore = class {
49956
50168
  return existed;
49957
50169
  }
49958
50170
  flush() {
49959
- const file = path25.join(this.dataDir, FILE_NAME);
50171
+ const file = path28.join(this.dataDir, FILE_NAME);
49960
50172
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
49961
50173
  const content = JSON.stringify(
49962
50174
  { contacts: Array.from(this.contacts.values()) },
49963
50175
  null,
49964
50176
  2
49965
50177
  );
49966
- fs27.mkdirSync(this.dataDir, { recursive: true });
49967
- fs27.writeFileSync(tmp, content, { mode: 384 });
49968
- fs27.renameSync(tmp, file);
50178
+ fs30.mkdirSync(this.dataDir, { recursive: true });
50179
+ fs30.writeFileSync(tmp, content, { mode: 384 });
50180
+ fs30.renameSync(tmp, file);
49969
50181
  }
49970
50182
  renameBak(file) {
49971
50183
  try {
49972
- fs27.renameSync(file, `${file}.bak`);
50184
+ fs30.renameSync(file, `${file}.bak`);
49973
50185
  } catch {
49974
50186
  }
49975
50187
  }
49976
50188
  };
49977
50189
 
49978
50190
  // src/contact/connect-remote.ts
49979
- var crypto5 = __toESM(require("crypto"), 1);
50191
+ var crypto6 = __toESM(require("crypto"), 1);
49980
50192
  var HANDSHAKE_TIMEOUT_MS = 5e3;
49981
50193
  var RPC_TIMEOUT_MS = 5e3;
49982
50194
  async function connectRemote(args) {
@@ -50040,7 +50252,7 @@ async function connectRemote(args) {
50040
50252
  ws.once("close", onClose);
50041
50253
  });
50042
50254
  function call(method, payload) {
50043
- const requestId = crypto5.randomUUID();
50255
+ const requestId = crypto6.randomUUID();
50044
50256
  return new Promise((resolve6, reject) => {
50045
50257
  const onMessage = (raw) => {
50046
50258
  let f;
@@ -50119,61 +50331,61 @@ async function autoReverseContact(args) {
50119
50331
  }
50120
50332
 
50121
50333
  // src/migrations/2026-05-20-flatten-sessions.ts
50122
- var fs28 = __toESM(require("fs"), 1);
50123
- var path26 = __toESM(require("path"), 1);
50334
+ var fs31 = __toESM(require("fs"), 1);
50335
+ var path29 = __toESM(require("path"), 1);
50124
50336
  var MIGRATION_FLAG_NAME = ".migration.v1.done";
50125
50337
  function migrateFlattenSessions(opts) {
50126
50338
  const dataDir = opts.dataDir;
50127
50339
  const now = opts.now ?? Date.now;
50128
- const sessionsDir = path26.join(dataDir, "sessions");
50129
- const flagPath = path26.join(sessionsDir, MIGRATION_FLAG_NAME);
50340
+ const sessionsDir = path29.join(dataDir, "sessions");
50341
+ const flagPath = path29.join(sessionsDir, MIGRATION_FLAG_NAME);
50130
50342
  if (existsSync3(flagPath)) {
50131
50343
  return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
50132
50344
  }
50133
50345
  let movedBare = 0;
50134
50346
  let movedVmOwner = 0;
50135
50347
  let archivedListener = 0;
50136
- const defaultDir = path26.join(sessionsDir, "default");
50348
+ const defaultDir = path29.join(sessionsDir, "default");
50137
50349
  if (existsSync3(defaultDir)) {
50138
50350
  for (const entry of readdirSafe(defaultDir)) {
50139
50351
  if (!entry.endsWith(".json")) continue;
50140
- const src = path26.join(defaultDir, entry);
50141
- const dst = path26.join(sessionsDir, entry);
50142
- fs28.renameSync(src, dst);
50352
+ const src = path29.join(defaultDir, entry);
50353
+ const dst = path29.join(sessionsDir, entry);
50354
+ fs31.renameSync(src, dst);
50143
50355
  movedBare += 1;
50144
50356
  }
50145
50357
  rmdirIfEmpty(defaultDir);
50146
50358
  }
50147
50359
  for (const pid of readdirSafe(sessionsDir)) {
50148
- const personaDir = path26.join(sessionsDir, pid);
50360
+ const personaDir = path29.join(sessionsDir, pid);
50149
50361
  if (!isDir(personaDir)) continue;
50150
50362
  if (pid === "default") continue;
50151
- const ownerSrc = path26.join(personaDir, "owner");
50363
+ const ownerSrc = path29.join(personaDir, "owner");
50152
50364
  if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
50153
- const ownerDst = path26.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
50154
- fs28.mkdirSync(ownerDst, { recursive: true });
50365
+ const ownerDst = path29.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
50366
+ fs31.mkdirSync(ownerDst, { recursive: true });
50155
50367
  for (const file of readdirSafe(ownerSrc)) {
50156
50368
  if (!file.endsWith(".json")) continue;
50157
- fs28.renameSync(path26.join(ownerSrc, file), path26.join(ownerDst, file));
50369
+ fs31.renameSync(path29.join(ownerSrc, file), path29.join(ownerDst, file));
50158
50370
  movedVmOwner += 1;
50159
50371
  }
50160
50372
  rmdirIfEmpty(ownerSrc);
50161
50373
  }
50162
- const listenerSrc = path26.join(personaDir, "listener");
50374
+ const listenerSrc = path29.join(personaDir, "listener");
50163
50375
  if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
50164
- const archiveDst = path26.join(dataDir, ".legacy", `listener-${pid}`);
50165
- fs28.mkdirSync(archiveDst, { recursive: true });
50376
+ const archiveDst = path29.join(dataDir, ".legacy", `listener-${pid}`);
50377
+ fs31.mkdirSync(archiveDst, { recursive: true });
50166
50378
  for (const file of readdirSafe(listenerSrc)) {
50167
50379
  if (!file.endsWith(".json")) continue;
50168
- fs28.renameSync(path26.join(listenerSrc, file), path26.join(archiveDst, file));
50380
+ fs31.renameSync(path29.join(listenerSrc, file), path29.join(archiveDst, file));
50169
50381
  archivedListener += 1;
50170
50382
  }
50171
50383
  rmdirIfEmpty(listenerSrc);
50172
50384
  }
50173
50385
  rmdirIfEmpty(personaDir);
50174
50386
  }
50175
- fs28.mkdirSync(sessionsDir, { recursive: true });
50176
- fs28.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
50387
+ fs31.mkdirSync(sessionsDir, { recursive: true });
50388
+ fs31.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
50177
50389
  return {
50178
50390
  skipped: false,
50179
50391
  flagWritten: true,
@@ -50184,7 +50396,7 @@ function migrateFlattenSessions(opts) {
50184
50396
  }
50185
50397
  function existsSync3(p2) {
50186
50398
  try {
50187
- fs28.statSync(p2);
50399
+ fs31.statSync(p2);
50188
50400
  return true;
50189
50401
  } catch {
50190
50402
  return false;
@@ -50192,31 +50404,31 @@ function existsSync3(p2) {
50192
50404
  }
50193
50405
  function isDir(p2) {
50194
50406
  try {
50195
- return fs28.statSync(p2).isDirectory();
50407
+ return fs31.statSync(p2).isDirectory();
50196
50408
  } catch {
50197
50409
  return false;
50198
50410
  }
50199
50411
  }
50200
50412
  function readdirSafe(p2) {
50201
50413
  try {
50202
- return fs28.readdirSync(p2);
50414
+ return fs31.readdirSync(p2);
50203
50415
  } catch {
50204
50416
  return [];
50205
50417
  }
50206
50418
  }
50207
50419
  function rmdirIfEmpty(p2) {
50208
50420
  try {
50209
- fs28.rmdirSync(p2);
50421
+ fs31.rmdirSync(p2);
50210
50422
  } catch {
50211
50423
  }
50212
50424
  }
50213
50425
 
50214
50426
  // src/transport/http-router.ts
50215
- var import_node_fs22 = __toESM(require("fs"), 1);
50216
- var import_node_path22 = __toESM(require("path"), 1);
50427
+ var import_node_fs25 = __toESM(require("fs"), 1);
50428
+ var import_node_path25 = __toESM(require("path"), 1);
50217
50429
 
50218
50430
  // src/attachment/mime.ts
50219
- var import_node_path19 = __toESM(require("path"), 1);
50431
+ var import_node_path22 = __toESM(require("path"), 1);
50220
50432
  var TEXT_PLAIN = "text/plain; charset=utf-8";
50221
50433
  var EXT_TO_NATIVE_MIME = {
50222
50434
  // 图片
@@ -50323,14 +50535,14 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
50323
50535
  ".mk"
50324
50536
  ]);
50325
50537
  function lookupMime(filePathOrName) {
50326
- const ext = import_node_path19.default.extname(filePathOrName).toLowerCase();
50538
+ const ext = import_node_path22.default.extname(filePathOrName).toLowerCase();
50327
50539
  if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
50328
50540
  if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
50329
50541
  return "application/octet-stream";
50330
50542
  }
50331
50543
 
50332
50544
  // src/attachment/sign-url.ts
50333
- var import_node_crypto5 = __toESM(require("crypto"), 1);
50545
+ var import_node_crypto6 = __toESM(require("crypto"), 1);
50334
50546
  var HMAC_ALGO = "sha256";
50335
50547
  function base64urlEncode(buf) {
50336
50548
  const b2 = typeof buf === "string" ? Buffer.from(buf, "utf8") : buf;
@@ -50347,7 +50559,7 @@ function decodeAbsPathFromUrl(encoded) {
50347
50559
  }
50348
50560
  function computeSig(secret, absPath, e) {
50349
50561
  const msg = e === null ? absPath : `${absPath}|${e}`;
50350
- return import_node_crypto5.default.createHmac(HMAC_ALGO, secret).update(msg).digest();
50562
+ return import_node_crypto6.default.createHmac(HMAC_ALGO, secret).update(msg).digest();
50351
50563
  }
50352
50564
  function signUrlParts(secret, absPath, ttlSeconds, now = Date.now) {
50353
50565
  const e = ttlSeconds === null ? null : Math.floor(now() / 1e3) + ttlSeconds;
@@ -50382,7 +50594,7 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
50382
50594
  if (provided.length !== expected.length) {
50383
50595
  return { ok: false, code: "BAD_SIG" };
50384
50596
  }
50385
- if (!import_node_crypto5.default.timingSafeEqual(provided, expected)) {
50597
+ if (!import_node_crypto6.default.timingSafeEqual(provided, expected)) {
50386
50598
  return { ok: false, code: "BAD_SIG" };
50387
50599
  }
50388
50600
  if (e !== null && now() / 1e3 > e) {
@@ -50392,9 +50604,9 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
50392
50604
  }
50393
50605
 
50394
50606
  // src/attachment/upload.ts
50395
- var import_node_fs21 = __toESM(require("fs"), 1);
50396
- var import_node_path20 = __toESM(require("path"), 1);
50397
- var import_node_crypto6 = __toESM(require("crypto"), 1);
50607
+ var import_node_fs24 = __toESM(require("fs"), 1);
50608
+ var import_node_path23 = __toESM(require("path"), 1);
50609
+ var import_node_crypto7 = __toESM(require("crypto"), 1);
50398
50610
  var import_promises2 = require("stream/promises");
50399
50611
  var UploadError = class extends Error {
50400
50612
  constructor(code, message) {
@@ -50405,24 +50617,24 @@ var UploadError = class extends Error {
50405
50617
  code;
50406
50618
  };
50407
50619
  function assertValidFileName(fileName) {
50408
- if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path20.default.basename(fileName)) {
50620
+ if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path23.default.basename(fileName)) {
50409
50621
  throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
50410
50622
  }
50411
50623
  }
50412
50624
  var HASH_PREFIX_LEN = 16;
50413
50625
  async function writeUploadedAttachment(args) {
50414
50626
  assertValidFileName(args.fileName);
50415
- const attachmentsRoot = import_node_path20.default.join(args.sessionDir, ".attachments");
50627
+ const attachmentsRoot = import_node_path23.default.join(args.sessionDir, ".attachments");
50416
50628
  try {
50417
- import_node_fs21.default.mkdirSync(attachmentsRoot, { recursive: true });
50629
+ import_node_fs24.default.mkdirSync(attachmentsRoot, { recursive: true });
50418
50630
  } catch (err) {
50419
50631
  throw new UploadError("STORAGE_ERROR", `mkdir failed: ${err.message}`);
50420
50632
  }
50421
- const hasher = import_node_crypto6.default.createHash("sha256");
50633
+ const hasher = import_node_crypto7.default.createHash("sha256");
50422
50634
  let actualSize = 0;
50423
- const tmpPath = import_node_path20.default.join(
50635
+ const tmpPath = import_node_path23.default.join(
50424
50636
  attachmentsRoot,
50425
- `.upload-${process.pid}-${Date.now()}-${import_node_crypto6.default.randomBytes(4).toString("hex")}`
50637
+ `.upload-${process.pid}-${Date.now()}-${import_node_crypto7.default.randomBytes(4).toString("hex")}`
50426
50638
  );
50427
50639
  try {
50428
50640
  await (0, import_promises2.pipeline)(
@@ -50435,18 +50647,18 @@ async function writeUploadedAttachment(args) {
50435
50647
  yield buf;
50436
50648
  }
50437
50649
  },
50438
- import_node_fs21.default.createWriteStream(tmpPath, { mode: 384 })
50650
+ import_node_fs24.default.createWriteStream(tmpPath, { mode: 384 })
50439
50651
  );
50440
50652
  } catch (err) {
50441
50653
  try {
50442
- import_node_fs21.default.unlinkSync(tmpPath);
50654
+ import_node_fs24.default.unlinkSync(tmpPath);
50443
50655
  } catch {
50444
50656
  }
50445
50657
  throw new UploadError("STORAGE_ERROR", `write failed: ${err.message}`);
50446
50658
  }
50447
50659
  if (actualSize !== args.contentLength) {
50448
50660
  try {
50449
- import_node_fs21.default.unlinkSync(tmpPath);
50661
+ import_node_fs24.default.unlinkSync(tmpPath);
50450
50662
  } catch {
50451
50663
  }
50452
50664
  throw new UploadError(
@@ -50455,35 +50667,35 @@ async function writeUploadedAttachment(args) {
50455
50667
  );
50456
50668
  }
50457
50669
  const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
50458
- const hashDir = import_node_path20.default.join(attachmentsRoot, attachmentId);
50670
+ const hashDir = import_node_path23.default.join(attachmentsRoot, attachmentId);
50459
50671
  let finalFileName;
50460
50672
  let hashDirExists = false;
50461
50673
  try {
50462
- hashDirExists = import_node_fs21.default.statSync(hashDir).isDirectory();
50674
+ hashDirExists = import_node_fs24.default.statSync(hashDir).isDirectory();
50463
50675
  } catch {
50464
50676
  }
50465
50677
  if (hashDirExists) {
50466
- const existing = import_node_fs21.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
50678
+ const existing = import_node_fs24.default.readdirSync(hashDir).filter((n) => !n.startsWith("."));
50467
50679
  finalFileName = existing[0] ?? args.fileName;
50468
50680
  try {
50469
- import_node_fs21.default.unlinkSync(tmpPath);
50681
+ import_node_fs24.default.unlinkSync(tmpPath);
50470
50682
  } catch {
50471
50683
  }
50472
50684
  } else {
50473
50685
  try {
50474
- import_node_fs21.default.mkdirSync(hashDir, { recursive: true });
50686
+ import_node_fs24.default.mkdirSync(hashDir, { recursive: true });
50475
50687
  finalFileName = args.fileName;
50476
- import_node_fs21.default.renameSync(tmpPath, import_node_path20.default.join(hashDir, finalFileName));
50688
+ import_node_fs24.default.renameSync(tmpPath, import_node_path23.default.join(hashDir, finalFileName));
50477
50689
  } catch (err) {
50478
50690
  try {
50479
- import_node_fs21.default.unlinkSync(tmpPath);
50691
+ import_node_fs24.default.unlinkSync(tmpPath);
50480
50692
  } catch {
50481
50693
  }
50482
50694
  throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
50483
50695
  }
50484
50696
  }
50485
- const absPath = import_node_path20.default.join(hashDir, finalFileName);
50486
- const relPath = import_node_path20.default.relative(args.sessionDir, absPath);
50697
+ const absPath = import_node_path23.default.join(hashDir, finalFileName);
50698
+ const relPath = import_node_path23.default.relative(args.sessionDir, absPath);
50487
50699
  args.groupFileStore.upsert(args.scope, args.sessionId, {
50488
50700
  relPath: absPath,
50489
50701
  // 存绝对路径,与现有 agent 入清单的形态一致(attachment.ts:144 双形态兼容)
@@ -50497,7 +50709,7 @@ async function writeUploadedAttachment(args) {
50497
50709
 
50498
50710
  // src/extension/import.ts
50499
50711
  var import_promises3 = __toESM(require("fs/promises"), 1);
50500
- var import_node_path21 = __toESM(require("path"), 1);
50712
+ var import_node_path24 = __toESM(require("path"), 1);
50501
50713
  var import_node_os11 = __toESM(require("os"), 1);
50502
50714
  var import_jszip = __toESM(require_lib3(), 1);
50503
50715
  var ImportError = class extends Error {
@@ -50515,7 +50727,7 @@ async function importZip(buf, root) {
50515
50727
  throw new ImportError("ZIP_INVALID", `failed to load zip: ${e.message}`);
50516
50728
  }
50517
50729
  for (const name of Object.keys(zip.files)) {
50518
- if (name.includes("..") || name.startsWith("/") || import_node_path21.default.isAbsolute(name)) {
50730
+ if (name.includes("..") || name.startsWith("/") || import_node_path24.default.isAbsolute(name)) {
50519
50731
  throw new ImportError("ZIP_INVALID", `unsafe zip entry path: ${name}`);
50520
50732
  }
50521
50733
  }
@@ -50548,7 +50760,7 @@ async function importZip(buf, root) {
50548
50760
  );
50549
50761
  }
50550
50762
  }
50551
- const destDir = import_node_path21.default.join(root, manifest.id);
50763
+ const destDir = import_node_path24.default.join(root, manifest.id);
50552
50764
  let destExists = false;
50553
50765
  try {
50554
50766
  await import_promises3.default.access(destDir);
@@ -50558,15 +50770,15 @@ async function importZip(buf, root) {
50558
50770
  if (destExists) {
50559
50771
  throw new ImportError("ALREADY_EXISTS", `extension ${manifest.id} already installed`);
50560
50772
  }
50561
- const stage = await import_promises3.default.mkdtemp(import_node_path21.default.join(import_node_os11.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
50773
+ const stage = await import_promises3.default.mkdtemp(import_node_path24.default.join(import_node_os11.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
50562
50774
  try {
50563
50775
  for (const [name, entry] of Object.entries(zip.files)) {
50564
- const dest = import_node_path21.default.join(stage, name);
50776
+ const dest = import_node_path24.default.join(stage, name);
50565
50777
  if (entry.dir) {
50566
50778
  await import_promises3.default.mkdir(dest, { recursive: true });
50567
50779
  continue;
50568
50780
  }
50569
- await import_promises3.default.mkdir(import_node_path21.default.dirname(dest), { recursive: true });
50781
+ await import_promises3.default.mkdir(import_node_path24.default.dirname(dest), { recursive: true });
50570
50782
  const content = await entry.async("nodebuffer");
50571
50783
  await import_promises3.default.writeFile(dest, content);
50572
50784
  }
@@ -50583,6 +50795,23 @@ async function importZip(buf, root) {
50583
50795
  // src/transport/http-router.ts
50584
50796
  var RPC_ROUTE_PREFIX = "/api/rpc/";
50585
50797
  var RPC_BODY_MAX_BYTES = 1 * 1024 * 1024;
50798
+ var SHARE_UI_ASSETS_PREFIX = "/share-ui/assets/";
50799
+ var SHARE_UI_ASSET_MIME = {
50800
+ ".js": "text/javascript; charset=utf-8",
50801
+ ".mjs": "text/javascript; charset=utf-8",
50802
+ ".css": "text/css; charset=utf-8",
50803
+ ".json": "application/json; charset=utf-8",
50804
+ ".map": "application/json; charset=utf-8",
50805
+ ".svg": "image/svg+xml",
50806
+ ".woff": "font/woff",
50807
+ ".woff2": "font/woff2",
50808
+ ".ttf": "font/ttf",
50809
+ ".wasm": "application/wasm"
50810
+ };
50811
+ function shareUiAssetMime(filePath) {
50812
+ const ext = import_node_path25.default.extname(filePath).toLowerCase();
50813
+ return SHARE_UI_ASSET_MIME[ext] ?? lookupMime(filePath);
50814
+ }
50586
50815
  var DISPATCH_HEARTBEAT_MS = 25e3;
50587
50816
  var DISPATCH_HOLD_METHOD = "personaDispatch:run";
50588
50817
  var AUTHED_RPC_ROUTE_PREFIX = "/rpc/";
@@ -50667,7 +50896,7 @@ function isValidUploadFileName(fileName) {
50667
50896
  if (fileName === "." || fileName === "..") return false;
50668
50897
  if (fileName.startsWith(".")) return false;
50669
50898
  if (fileName.includes("/") || fileName.includes("\\")) return false;
50670
- return fileName === import_node_path22.default.basename(fileName);
50899
+ return fileName === import_node_path25.default.basename(fileName);
50671
50900
  }
50672
50901
  function createHttpRouter(deps) {
50673
50902
  return async (req, res) => {
@@ -50701,6 +50930,59 @@ function createHttpRouter(deps) {
50701
50930
  res.end(html);
50702
50931
  return true;
50703
50932
  }
50933
+ if (url.pathname === "/share/exchange" && req.method === "OPTIONS") {
50934
+ res.writeHead(204, AUTHED_RPC_CORS_HEADERS);
50935
+ res.end();
50936
+ return true;
50937
+ }
50938
+ if (url.pathname === "/share/exchange" && req.method === "POST") {
50939
+ if (!deps.visitorExchange) {
50940
+ sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "visitor exchange not wired" }, AUTHED_RPC_CORS_HEADERS);
50941
+ return true;
50942
+ }
50943
+ let body;
50944
+ try {
50945
+ body = await readJsonBody(req, RPC_BODY_MAX_BYTES);
50946
+ } catch (err) {
50947
+ sendJson(res, 400, { code: "INVALID_BODY", message: err instanceof Error ? err.message : String(err) }, AUTHED_RPC_CORS_HEADERS);
50948
+ return true;
50949
+ }
50950
+ const r = await deps.visitorExchange({ ttcToken: body.ttcToken ?? "" });
50951
+ if (r.ok) sendJson(res, 200, { visitorToken: r.visitorToken }, AUTHED_RPC_CORS_HEADERS);
50952
+ else sendJson(res, 403, { code: r.reason }, AUTHED_RPC_CORS_HEADERS);
50953
+ return true;
50954
+ }
50955
+ if ((url.pathname === "/s" || url.pathname.startsWith("/s/")) && req.method === "GET") {
50956
+ if (!deps.shareUiAssetLoader) {
50957
+ sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "share-ui assets not loaded" });
50958
+ return true;
50959
+ }
50960
+ sendHtml(res, 200, deps.shareUiAssetLoader.renderIndexHtml());
50961
+ return true;
50962
+ }
50963
+ if (url.pathname.startsWith(SHARE_UI_ASSETS_PREFIX) && req.method === "GET") {
50964
+ const abs = deps.shareUiAssetLoader?.resolveAssetPath(url.pathname.slice(SHARE_UI_ASSETS_PREFIX.length));
50965
+ if (!abs) {
50966
+ sendJson(res, 404, { code: "NOT_FOUND", message: "asset not found" });
50967
+ return true;
50968
+ }
50969
+ let buf;
50970
+ try {
50971
+ buf = await import_node_fs25.default.promises.readFile(abs);
50972
+ } catch {
50973
+ sendJson(res, 404, { code: "NOT_FOUND", message: "asset not found" });
50974
+ return true;
50975
+ }
50976
+ res.writeHead(200, {
50977
+ "Content-Type": shareUiAssetMime(abs),
50978
+ "Content-Length": String(buf.length),
50979
+ // vite 产物文件名带内容 hash,可永久强缓存
50980
+ "Cache-Control": "public, max-age=31536000, immutable",
50981
+ "X-Content-Type-Options": "nosniff"
50982
+ });
50983
+ res.end(buf);
50984
+ return true;
50985
+ }
50704
50986
  if (url.pathname.startsWith(AUTHED_RPC_ROUTE_PREFIX)) {
50705
50987
  if (req.method === "OPTIONS") {
50706
50988
  res.writeHead(204, AUTHED_RPC_CORS_HEADERS);
@@ -50910,7 +51192,7 @@ function createHttpRouter(deps) {
50910
51192
  sendHtml(res, statusByCode[r.code], loader.renderErrorHtml(r.code, msgByCode[r.code]));
50911
51193
  return true;
50912
51194
  }
50913
- sendHtml(res, 200, loader.renderViewerHtml(import_node_path22.default.basename(r.absPath)));
51195
+ sendHtml(res, 200, loader.renderViewerHtml(import_node_path25.default.basename(r.absPath)));
50914
51196
  return true;
50915
51197
  }
50916
51198
  const ctx = deps.authResolver.resolveFromHeader(
@@ -51031,7 +51313,7 @@ function createHttpRouter(deps) {
51031
51313
  return true;
51032
51314
  }
51033
51315
  let absPath;
51034
- if (import_node_path22.default.isAbsolute(pathParam)) {
51316
+ if (import_node_path25.default.isAbsolute(pathParam)) {
51035
51317
  absPath = pathParam;
51036
51318
  } else if (deps.sessionStore) {
51037
51319
  const file = deps.sessionStore.read(sid);
@@ -51039,7 +51321,7 @@ function createHttpRouter(deps) {
51039
51321
  sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sid} not found` });
51040
51322
  return true;
51041
51323
  }
51042
- absPath = import_node_path22.default.join(file.cwd, pathParam);
51324
+ absPath = import_node_path25.default.join(file.cwd, pathParam);
51043
51325
  } else {
51044
51326
  sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "sessionStore not wired" }));
51045
51327
  return true;
@@ -51126,7 +51408,7 @@ function withCtx(ctx, body) {
51126
51408
  function streamFile(res, absPath, logger) {
51127
51409
  let stat;
51128
51410
  try {
51129
- stat = import_node_fs22.default.statSync(absPath);
51411
+ stat = import_node_fs25.default.statSync(absPath);
51130
51412
  } catch (err) {
51131
51413
  const code = err?.code;
51132
51414
  if (code === "ENOENT") {
@@ -51141,7 +51423,7 @@ function streamFile(res, absPath, logger) {
51141
51423
  return;
51142
51424
  }
51143
51425
  const mime = lookupMime(absPath);
51144
- const basename = import_node_path22.default.basename(absPath);
51426
+ const basename = import_node_path25.default.basename(absPath);
51145
51427
  res.writeHead(200, {
51146
51428
  "Content-Type": mime,
51147
51429
  "Content-Length": String(stat.size),
@@ -51149,7 +51431,7 @@ function streamFile(res, absPath, logger) {
51149
51431
  // 防止浏览器把任意 mime 当 html 渲染
51150
51432
  "X-Content-Type-Options": "nosniff"
51151
51433
  });
51152
- const stream = import_node_fs22.default.createReadStream(absPath);
51434
+ const stream = import_node_fs25.default.createReadStream(absPath);
51153
51435
  stream.on("error", (err) => {
51154
51436
  logger?.warn("streamFile read error", { absPath, err: err.message });
51155
51437
  res.destroy();
@@ -51158,8 +51440,8 @@ function streamFile(res, absPath, logger) {
51158
51440
  }
51159
51441
 
51160
51442
  // src/attachment/gc.ts
51161
- var import_node_fs23 = __toESM(require("fs"), 1);
51162
- var import_node_path23 = __toESM(require("path"), 1);
51443
+ var import_node_fs26 = __toESM(require("fs"), 1);
51444
+ var import_node_path26 = __toESM(require("path"), 1);
51163
51445
  var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
51164
51446
  function runAttachmentGc(args) {
51165
51447
  const now = (args.now ?? Date.now)();
@@ -51168,38 +51450,38 @@ function runAttachmentGc(args) {
51168
51450
  for (const { scope, sessionId } of args.sessionScopes) {
51169
51451
  for (const entry of args.groupFileStore.list(scope, sessionId)) {
51170
51452
  if (entry.stale) continue;
51171
- if (import_node_path23.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
51453
+ if (import_node_path26.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
51172
51454
  }
51173
51455
  }
51174
51456
  for (const { scope, sessionId } of args.sessionScopes) {
51175
- const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path23.default.join(
51457
+ const sessionDir = args.getSessionCwd?.(sessionId) ?? import_node_path26.default.join(
51176
51458
  args.dataDir,
51177
51459
  "sessions",
51178
51460
  ...scopeSubPath(scope).map(safeFileName),
51179
51461
  safeFileName(sessionId)
51180
51462
  );
51181
- const attRoot = import_node_path23.default.join(sessionDir, ".attachments");
51463
+ const attRoot = import_node_path26.default.join(sessionDir, ".attachments");
51182
51464
  let hashDirs;
51183
51465
  try {
51184
- hashDirs = import_node_fs23.default.readdirSync(attRoot);
51466
+ hashDirs = import_node_fs26.default.readdirSync(attRoot);
51185
51467
  } catch (err) {
51186
51468
  if (err.code === "ENOENT") continue;
51187
51469
  args.logger?.warn("attachment gc: readdir failed", { attRoot, err: err.message });
51188
51470
  continue;
51189
51471
  }
51190
51472
  for (const hashDir of hashDirs) {
51191
- const hashDirAbs = import_node_path23.default.join(attRoot, hashDir);
51473
+ const hashDirAbs = import_node_path26.default.join(attRoot, hashDir);
51192
51474
  let files;
51193
51475
  try {
51194
- files = import_node_fs23.default.readdirSync(hashDirAbs);
51476
+ files = import_node_fs26.default.readdirSync(hashDirAbs);
51195
51477
  } catch {
51196
51478
  continue;
51197
51479
  }
51198
51480
  for (const name of files) {
51199
- const file = import_node_path23.default.join(hashDirAbs, name);
51481
+ const file = import_node_path26.default.join(hashDirAbs, name);
51200
51482
  let stat;
51201
51483
  try {
51202
- stat = import_node_fs23.default.statSync(file);
51484
+ stat = import_node_fs26.default.statSync(file);
51203
51485
  } catch {
51204
51486
  continue;
51205
51487
  }
@@ -51208,27 +51490,27 @@ function runAttachmentGc(args) {
51208
51490
  if (age < ttlMs) continue;
51209
51491
  if (liveAbs.has(file)) continue;
51210
51492
  try {
51211
- import_node_fs23.default.unlinkSync(file);
51493
+ import_node_fs26.default.unlinkSync(file);
51212
51494
  } catch (err) {
51213
51495
  args.logger?.warn("attachment gc: unlink failed", { file, err: err.message });
51214
51496
  }
51215
51497
  }
51216
51498
  try {
51217
- if (import_node_fs23.default.readdirSync(hashDirAbs).length === 0) import_node_fs23.default.rmdirSync(hashDirAbs);
51499
+ if (import_node_fs26.default.readdirSync(hashDirAbs).length === 0) import_node_fs26.default.rmdirSync(hashDirAbs);
51218
51500
  } catch {
51219
51501
  }
51220
51502
  }
51221
51503
  try {
51222
- if (import_node_fs23.default.readdirSync(attRoot).length === 0) import_node_fs23.default.rmdirSync(attRoot);
51504
+ if (import_node_fs26.default.readdirSync(attRoot).length === 0) import_node_fs26.default.rmdirSync(attRoot);
51223
51505
  } catch {
51224
51506
  }
51225
51507
  }
51226
51508
  }
51227
51509
 
51228
51510
  // src/attachment/group.ts
51229
- var import_node_fs24 = __toESM(require("fs"), 1);
51230
- var import_node_path24 = __toESM(require("path"), 1);
51231
- var import_node_crypto7 = __toESM(require("crypto"), 1);
51511
+ var import_node_fs27 = __toESM(require("fs"), 1);
51512
+ var import_node_path27 = __toESM(require("path"), 1);
51513
+ var import_node_crypto8 = __toESM(require("crypto"), 1);
51232
51514
  init_protocol();
51233
51515
  var GroupFileStore = class {
51234
51516
  dataDir;
@@ -51239,11 +51521,11 @@ var GroupFileStore = class {
51239
51521
  this.logger = opts.logger;
51240
51522
  }
51241
51523
  rootForScope(scope) {
51242
- return import_node_path24.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
51524
+ return import_node_path27.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
51243
51525
  }
51244
51526
  /** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
51245
51527
  filePath(scope, sessionId) {
51246
- return import_node_path24.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
51528
+ return import_node_path27.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
51247
51529
  }
51248
51530
  cacheKey(scope, sessionId) {
51249
51531
  return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
@@ -51252,7 +51534,7 @@ var GroupFileStore = class {
51252
51534
  readFile(scope, sessionId) {
51253
51535
  const file = this.filePath(scope, sessionId);
51254
51536
  try {
51255
- const raw = import_node_fs24.default.readFileSync(file, "utf8");
51537
+ const raw = import_node_fs27.default.readFileSync(file, "utf8");
51256
51538
  const parsed = JSON.parse(raw);
51257
51539
  if (!Array.isArray(parsed)) {
51258
51540
  this.logger?.warn("GroupFileStore.readFile: not an array; resetting session entries", {
@@ -51278,10 +51560,10 @@ var GroupFileStore = class {
51278
51560
  }
51279
51561
  writeFile(scope, sessionId, entries) {
51280
51562
  const file = this.filePath(scope, sessionId);
51281
- import_node_fs24.default.mkdirSync(import_node_path24.default.dirname(file), { recursive: true });
51563
+ import_node_fs27.default.mkdirSync(import_node_path27.default.dirname(file), { recursive: true });
51282
51564
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
51283
- import_node_fs24.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
51284
- import_node_fs24.default.renameSync(tmp, file);
51565
+ import_node_fs27.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
51566
+ import_node_fs27.default.renameSync(tmp, file);
51285
51567
  }
51286
51568
  /** 拉一份当前 session 的清单。读盘 → cache;之后调用复用 cache */
51287
51569
  list(scope, sessionId) {
@@ -51317,7 +51599,7 @@ var GroupFileStore = class {
51317
51599
  entries[idx] = next;
51318
51600
  } else {
51319
51601
  next = {
51320
- id: `gf-${import_node_crypto7.default.randomBytes(6).toString("base64url")}`,
51602
+ id: `gf-${import_node_crypto8.default.randomBytes(6).toString("base64url")}`,
51321
51603
  relPath: input.relPath,
51322
51604
  from: input.from,
51323
51605
  label: input.label,
@@ -51367,10 +51649,10 @@ var GroupFileStore = class {
51367
51649
  };
51368
51650
 
51369
51651
  // src/discovery/state-file.ts
51370
- var import_node_fs25 = __toESM(require("fs"), 1);
51371
- var import_node_path25 = __toESM(require("path"), 1);
51652
+ var import_node_fs28 = __toESM(require("fs"), 1);
51653
+ var import_node_path28 = __toESM(require("path"), 1);
51372
51654
  function defaultStateFilePath(dataDir) {
51373
- return import_node_path25.default.join(dataDir, "state.json");
51655
+ return import_node_path28.default.join(dataDir, "state.json");
51374
51656
  }
51375
51657
  function isPidAlive(pid) {
51376
51658
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -51392,7 +51674,7 @@ var StateFileManager = class {
51392
51674
  }
51393
51675
  read() {
51394
51676
  try {
51395
- const raw = import_node_fs25.default.readFileSync(this.file, "utf8");
51677
+ const raw = import_node_fs28.default.readFileSync(this.file, "utf8");
51396
51678
  const parsed = JSON.parse(raw);
51397
51679
  return parsed;
51398
51680
  } catch {
@@ -51406,20 +51688,20 @@ var StateFileManager = class {
51406
51688
  return { status: "stale", existing };
51407
51689
  }
51408
51690
  write(state) {
51409
- import_node_fs25.default.mkdirSync(import_node_path25.default.dirname(this.file), { recursive: true });
51691
+ import_node_fs28.default.mkdirSync(import_node_path28.default.dirname(this.file), { recursive: true });
51410
51692
  const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
51411
- import_node_fs25.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
51412
- import_node_fs25.default.renameSync(tmp, this.file);
51693
+ import_node_fs28.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
51694
+ import_node_fs28.default.renameSync(tmp, this.file);
51413
51695
  if (process.platform !== "win32") {
51414
51696
  try {
51415
- import_node_fs25.default.chmodSync(this.file, 384);
51697
+ import_node_fs28.default.chmodSync(this.file, 384);
51416
51698
  } catch {
51417
51699
  }
51418
51700
  }
51419
51701
  }
51420
51702
  delete() {
51421
51703
  try {
51422
- import_node_fs25.default.unlinkSync(this.file);
51704
+ import_node_fs28.default.unlinkSync(this.file);
51423
51705
  } catch {
51424
51706
  }
51425
51707
  }
@@ -51434,14 +51716,14 @@ function readDaemonSourceFromEnv(env = process.env) {
51434
51716
  }
51435
51717
 
51436
51718
  // src/tunnel/tunnel-manager.ts
51437
- var import_node_fs29 = __toESM(require("fs"), 1);
51438
- var import_node_path29 = __toESM(require("path"), 1);
51439
- var import_node_crypto8 = __toESM(require("crypto"), 1);
51719
+ var import_node_fs32 = __toESM(require("fs"), 1);
51720
+ var import_node_path32 = __toESM(require("path"), 1);
51721
+ var import_node_crypto9 = __toESM(require("crypto"), 1);
51440
51722
  var import_node_child_process9 = require("child_process");
51441
51723
 
51442
51724
  // src/tunnel/tunnel-store.ts
51443
- var import_node_fs26 = __toESM(require("fs"), 1);
51444
- var import_node_path26 = __toESM(require("path"), 1);
51725
+ var import_node_fs29 = __toESM(require("fs"), 1);
51726
+ var import_node_path29 = __toESM(require("path"), 1);
51445
51727
  var TunnelStore = class {
51446
51728
  constructor(filePath) {
51447
51729
  this.filePath = filePath;
@@ -51449,7 +51731,7 @@ var TunnelStore = class {
51449
51731
  filePath;
51450
51732
  async get() {
51451
51733
  try {
51452
- const raw = await import_node_fs26.default.promises.readFile(this.filePath, "utf8");
51734
+ const raw = await import_node_fs29.default.promises.readFile(this.filePath, "utf8");
51453
51735
  const obj = JSON.parse(raw);
51454
51736
  if (!isPersistedTunnel(obj)) return null;
51455
51737
  return obj;
@@ -51460,22 +51742,22 @@ var TunnelStore = class {
51460
51742
  }
51461
51743
  }
51462
51744
  async set(v2) {
51463
- const dir = import_node_path26.default.dirname(this.filePath);
51464
- await import_node_fs26.default.promises.mkdir(dir, { recursive: true });
51745
+ const dir = import_node_path29.default.dirname(this.filePath);
51746
+ await import_node_fs29.default.promises.mkdir(dir, { recursive: true });
51465
51747
  const data = JSON.stringify(v2, null, 2);
51466
51748
  const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
51467
- await import_node_fs26.default.promises.writeFile(tmp, data, { mode: 384 });
51749
+ await import_node_fs29.default.promises.writeFile(tmp, data, { mode: 384 });
51468
51750
  if (process.platform !== "win32") {
51469
51751
  try {
51470
- await import_node_fs26.default.promises.chmod(tmp, 384);
51752
+ await import_node_fs29.default.promises.chmod(tmp, 384);
51471
51753
  } catch {
51472
51754
  }
51473
51755
  }
51474
- await import_node_fs26.default.promises.rename(tmp, this.filePath);
51756
+ await import_node_fs29.default.promises.rename(tmp, this.filePath);
51475
51757
  }
51476
51758
  async clear() {
51477
51759
  try {
51478
- await import_node_fs26.default.promises.unlink(this.filePath);
51760
+ await import_node_fs29.default.promises.unlink(this.filePath);
51479
51761
  } catch (err) {
51480
51762
  const code = err?.code;
51481
51763
  if (code !== "ENOENT") throw err;
@@ -51570,9 +51852,9 @@ function escape(v2) {
51570
51852
  }
51571
51853
 
51572
51854
  // src/tunnel/frpc-binary.ts
51573
- var import_node_fs27 = __toESM(require("fs"), 1);
51855
+ var import_node_fs30 = __toESM(require("fs"), 1);
51574
51856
  var import_node_os12 = __toESM(require("os"), 1);
51575
- var import_node_path27 = __toESM(require("path"), 1);
51857
+ var import_node_path30 = __toESM(require("path"), 1);
51576
51858
  var import_node_child_process7 = require("child_process");
51577
51859
  var import_node_stream3 = require("stream");
51578
51860
  var import_promises4 = require("stream/promises");
@@ -51604,20 +51886,20 @@ function frpcDownloadUrl(version2, p2) {
51604
51886
  }
51605
51887
  async function ensureFrpcBinary(opts) {
51606
51888
  if (opts.override) {
51607
- if (!import_node_fs27.default.existsSync(opts.override)) {
51889
+ if (!import_node_fs30.default.existsSync(opts.override)) {
51608
51890
  throw new Error(`frpc binary not found at override path: ${opts.override}`);
51609
51891
  }
51610
51892
  return opts.override;
51611
51893
  }
51612
51894
  const version2 = opts.version ?? FRPC_VERSION;
51613
51895
  const platform = opts.platform ?? detectPlatform();
51614
- const binDir = import_node_path27.default.join(opts.dataDir, "bin");
51615
- import_node_fs27.default.mkdirSync(binDir, { recursive: true });
51896
+ const binDir = import_node_path30.default.join(opts.dataDir, "bin");
51897
+ import_node_fs30.default.mkdirSync(binDir, { recursive: true });
51616
51898
  cleanupStaleArtifacts(binDir);
51617
- const stableBin = import_node_path27.default.join(binDir, "frpc");
51618
- if (import_node_fs27.default.existsSync(stableBin)) return stableBin;
51899
+ const stableBin = import_node_path30.default.join(binDir, "frpc");
51900
+ if (import_node_fs30.default.existsSync(stableBin)) return stableBin;
51619
51901
  const partialBin = `${stableBin}.partial`;
51620
- const tarballPath = import_node_path27.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
51902
+ const tarballPath = import_node_path30.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
51621
51903
  try {
51622
51904
  const url = frpcDownloadUrl(version2, platform);
51623
51905
  await downloadToFile(url, tarballPath, opts.fetchImpl);
@@ -51626,8 +51908,8 @@ async function ensureFrpcBinary(opts) {
51626
51908
  } else {
51627
51909
  await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
51628
51910
  }
51629
- import_node_fs27.default.chmodSync(partialBin, 493);
51630
- import_node_fs27.default.renameSync(partialBin, stableBin);
51911
+ import_node_fs30.default.chmodSync(partialBin, 493);
51912
+ import_node_fs30.default.renameSync(partialBin, stableBin);
51631
51913
  } finally {
51632
51914
  safeUnlink(tarballPath);
51633
51915
  safeUnlink(partialBin);
@@ -51637,15 +51919,15 @@ async function ensureFrpcBinary(opts) {
51637
51919
  function cleanupStaleArtifacts(binDir) {
51638
51920
  let entries;
51639
51921
  try {
51640
- entries = import_node_fs27.default.readdirSync(binDir);
51922
+ entries = import_node_fs30.default.readdirSync(binDir);
51641
51923
  } catch {
51642
51924
  return;
51643
51925
  }
51644
51926
  for (const name of entries) {
51645
51927
  if (name.endsWith(".partial") || name.startsWith("extract-")) {
51646
- const full = import_node_path27.default.join(binDir, name);
51928
+ const full = import_node_path30.default.join(binDir, name);
51647
51929
  try {
51648
- import_node_fs27.default.rmSync(full, { recursive: true, force: true });
51930
+ import_node_fs30.default.rmSync(full, { recursive: true, force: true });
51649
51931
  } catch {
51650
51932
  }
51651
51933
  }
@@ -51653,7 +51935,7 @@ function cleanupStaleArtifacts(binDir) {
51653
51935
  }
51654
51936
  function safeUnlink(p2) {
51655
51937
  try {
51656
- import_node_fs27.default.unlinkSync(p2);
51938
+ import_node_fs30.default.unlinkSync(p2);
51657
51939
  } catch {
51658
51940
  }
51659
51941
  }
@@ -51664,13 +51946,13 @@ async function downloadToFile(url, dest, fetchImpl) {
51664
51946
  if (!res.ok || !res.body) {
51665
51947
  throw new Error(`download failed: ${res.status} ${res.statusText}`);
51666
51948
  }
51667
- const out = import_node_fs27.default.createWriteStream(dest);
51949
+ const out = import_node_fs30.default.createWriteStream(dest);
51668
51950
  const nodeStream = import_node_stream3.Readable.fromWeb(res.body);
51669
51951
  await (0, import_promises4.pipeline)(nodeStream, out);
51670
51952
  }
51671
51953
  async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
51672
- const work = import_node_path27.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
51673
- import_node_fs27.default.mkdirSync(work, { recursive: true });
51954
+ const work = import_node_path30.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
51955
+ import_node_fs30.default.mkdirSync(work, { recursive: true });
51674
51956
  try {
51675
51957
  await new Promise((resolve6, reject) => {
51676
51958
  const proc = (0, import_node_child_process7.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
@@ -51678,32 +51960,32 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
51678
51960
  proc.on("exit", (code) => code === 0 ? resolve6() : reject(new Error(`tar exited ${code}`)));
51679
51961
  });
51680
51962
  const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
51681
- const src = import_node_path27.default.join(work, dirName, "frpc");
51682
- if (!import_node_fs27.default.existsSync(src)) {
51963
+ const src = import_node_path30.default.join(work, dirName, "frpc");
51964
+ if (!import_node_fs30.default.existsSync(src)) {
51683
51965
  throw new Error(`frpc not found inside tarball at ${src}`);
51684
51966
  }
51685
- import_node_fs27.default.copyFileSync(src, destBin);
51967
+ import_node_fs30.default.copyFileSync(src, destBin);
51686
51968
  } finally {
51687
- import_node_fs27.default.rmSync(work, { recursive: true, force: true });
51969
+ import_node_fs30.default.rmSync(work, { recursive: true, force: true });
51688
51970
  }
51689
51971
  }
51690
51972
 
51691
51973
  // src/tunnel/frpc-process.ts
51692
- var import_node_fs28 = __toESM(require("fs"), 1);
51693
- var import_node_path28 = __toESM(require("path"), 1);
51974
+ var import_node_fs31 = __toESM(require("fs"), 1);
51975
+ var import_node_path31 = __toESM(require("path"), 1);
51694
51976
  var import_node_child_process8 = require("child_process");
51695
51977
  function frpcPidFilePath(dataDir) {
51696
- return import_node_path28.default.join(dataDir, "frpc.pid");
51978
+ return import_node_path31.default.join(dataDir, "frpc.pid");
51697
51979
  }
51698
51980
  function writeFrpcPid(dataDir, pid) {
51699
51981
  try {
51700
- import_node_fs28.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
51982
+ import_node_fs31.default.writeFileSync(frpcPidFilePath(dataDir), String(pid), { mode: 384 });
51701
51983
  } catch {
51702
51984
  }
51703
51985
  }
51704
51986
  function clearFrpcPid(dataDir) {
51705
51987
  try {
51706
- import_node_fs28.default.unlinkSync(frpcPidFilePath(dataDir));
51988
+ import_node_fs31.default.unlinkSync(frpcPidFilePath(dataDir));
51707
51989
  } catch {
51708
51990
  }
51709
51991
  }
@@ -51719,7 +52001,7 @@ function defaultIsPidAlive(pid) {
51719
52001
  }
51720
52002
  function defaultReadPidFile(file) {
51721
52003
  try {
51722
- return import_node_fs28.default.readFileSync(file, "utf8");
52004
+ return import_node_fs31.default.readFileSync(file, "utf8");
51723
52005
  } catch {
51724
52006
  return null;
51725
52007
  }
@@ -51735,7 +52017,7 @@ function defaultSleep(ms) {
51735
52017
  }
51736
52018
  async function killStaleFrpc(deps) {
51737
52019
  const pidFile = frpcPidFilePath(deps.dataDir);
51738
- const tomlPath = import_node_path28.default.join(deps.dataDir, "frpc.toml");
52020
+ const tomlPath = import_node_path31.default.join(deps.dataDir, "frpc.toml");
51739
52021
  const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
51740
52022
  const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
51741
52023
  const killPid = deps.killPidImpl ?? defaultKillPid;
@@ -51759,7 +52041,7 @@ async function killStaleFrpc(deps) {
51759
52041
  }
51760
52042
  if (victims.size === 0) {
51761
52043
  try {
51762
- import_node_fs28.default.unlinkSync(pidFile);
52044
+ import_node_fs31.default.unlinkSync(pidFile);
51763
52045
  } catch {
51764
52046
  }
51765
52047
  return;
@@ -51770,7 +52052,7 @@ async function killStaleFrpc(deps) {
51770
52052
  }
51771
52053
  await sleep2(deps.reapWaitMs ?? 300);
51772
52054
  try {
51773
- import_node_fs28.default.unlinkSync(pidFile);
52055
+ import_node_fs31.default.unlinkSync(pidFile);
51774
52056
  } catch {
51775
52057
  }
51776
52058
  }
@@ -51807,7 +52089,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
51807
52089
  var TunnelManager = class {
51808
52090
  constructor(deps) {
51809
52091
  this.deps = deps;
51810
- this.store = deps.store ?? new TunnelStore(import_node_path29.default.join(deps.dataDir, "tunnel.json"));
52092
+ this.store = deps.store ?? new TunnelStore(import_node_path32.default.join(deps.dataDir, "tunnel.json"));
51811
52093
  this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
51812
52094
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
51813
52095
  }
@@ -51934,8 +52216,8 @@ var TunnelManager = class {
51934
52216
  dataDir: this.deps.dataDir,
51935
52217
  override: this.deps.frpcBinaryOverride ?? void 0
51936
52218
  });
51937
- const tomlPath = import_node_path29.default.join(this.deps.dataDir, "frpc.toml");
51938
- const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto8.default.randomBytes(3).toString("hex")}`;
52219
+ const tomlPath = import_node_path32.default.join(this.deps.dataDir, "frpc.toml");
52220
+ const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto9.default.randomBytes(3).toString("hex")}`;
51939
52221
  const toml = buildFrpcToml({
51940
52222
  serverAddr: t.frpsHost,
51941
52223
  serverPort: t.frpsPort,
@@ -51945,12 +52227,12 @@ var TunnelManager = class {
51945
52227
  localPort,
51946
52228
  logLevel: "info"
51947
52229
  });
51948
- await import_node_fs29.default.promises.writeFile(tomlPath, toml, { mode: 384 });
52230
+ await import_node_fs32.default.promises.writeFile(tomlPath, toml, { mode: 384 });
51949
52231
  const proc = (this.deps.spawnImpl ?? import_node_child_process9.spawn)(frpcBin, ["-c", tomlPath], {
51950
52232
  stdio: ["ignore", "pipe", "pipe"]
51951
52233
  });
51952
- const logFilePath = import_node_path29.default.join(this.deps.dataDir, "frpc.log");
51953
- const logStream = import_node_fs29.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
52234
+ const logFilePath = import_node_path32.default.join(this.deps.dataDir, "frpc.log");
52235
+ const logStream = import_node_fs32.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
51954
52236
  logStream.on("error", () => {
51955
52237
  });
51956
52238
  const tee = (chunk) => {
@@ -52033,27 +52315,27 @@ async function waitForFrpcReady(proc, timeoutMs) {
52033
52315
 
52034
52316
  // src/tunnel/device-key.ts
52035
52317
  var import_node_os13 = __toESM(require("os"), 1);
52036
- var import_node_path30 = __toESM(require("path"), 1);
52037
- var import_node_crypto9 = __toESM(require("crypto"), 1);
52318
+ var import_node_path33 = __toESM(require("path"), 1);
52319
+ var import_node_crypto10 = __toESM(require("crypto"), 1);
52038
52320
  var DERIVE_SALT = "clawd-tunnel-device-v1";
52039
52321
  function deriveStableDeviceKey(opts = {}) {
52040
52322
  const hostname = opts.hostname ?? import_node_os13.default.hostname();
52041
52323
  const uid = opts.uid ?? (typeof import_node_os13.default.userInfo === "function" ? import_node_os13.default.userInfo().uid : 0);
52042
52324
  const home = opts.home ?? import_node_os13.default.homedir();
52043
- const defaultDataDir = import_node_path30.default.resolve(import_node_path30.default.join(home, ".clawd"));
52044
- const normalizedDataDir = opts.dataDir ? import_node_path30.default.resolve(opts.dataDir) : null;
52325
+ const defaultDataDir = import_node_path33.default.resolve(import_node_path33.default.join(home, ".clawd"));
52326
+ const normalizedDataDir = opts.dataDir ? import_node_path33.default.resolve(opts.dataDir) : null;
52045
52327
  const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
52046
52328
  const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
52047
- return import_node_crypto9.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
52329
+ return import_node_crypto10.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
52048
52330
  }
52049
52331
 
52050
52332
  // src/auth-store.ts
52051
- var import_node_fs30 = __toESM(require("fs"), 1);
52052
- var import_node_path31 = __toESM(require("path"), 1);
52053
- var import_node_crypto10 = __toESM(require("crypto"), 1);
52333
+ var import_node_fs33 = __toESM(require("fs"), 1);
52334
+ var import_node_path34 = __toESM(require("path"), 1);
52335
+ var import_node_crypto11 = __toESM(require("crypto"), 1);
52054
52336
  var AUTH_FILE_NAME = "auth.json";
52055
52337
  function authFilePath(dataDir) {
52056
- return import_node_path31.default.join(dataDir, AUTH_FILE_NAME);
52338
+ return import_node_path34.default.join(dataDir, AUTH_FILE_NAME);
52057
52339
  }
52058
52340
  function loadOrCreateAuthFile(opts) {
52059
52341
  const file = authFilePath(opts.dataDir);
@@ -52061,10 +52343,11 @@ function loadOrCreateAuthFile(opts) {
52061
52343
  const genId = opts.genOwnerPrincipalId ?? defaultGenerateOwnerPrincipalId;
52062
52344
  const now = opts.now ?? (() => /* @__PURE__ */ new Date());
52063
52345
  const existing = readAuthFile(file);
52064
- if (existing && existing.token && existing.signSecret && existing.ownerPrincipalId && existing.deviceId && existing.deviceId === existing.ownerPrincipalId) {
52346
+ if (existing && existing.token && existing.signSecret && existing.visitorTokenSecret && existing.ownerPrincipalId && existing.deviceId && existing.deviceId === existing.ownerPrincipalId) {
52065
52347
  return {
52066
52348
  token: existing.token,
52067
52349
  signSecret: existing.signSecret,
52350
+ visitorTokenSecret: existing.visitorTokenSecret,
52068
52351
  ownerPrincipalId: existing.ownerPrincipalId,
52069
52352
  deviceId: existing.deviceId,
52070
52353
  createdAt: existing.createdAt ?? (/* @__PURE__ */ new Date(0)).toISOString()
@@ -52072,22 +52355,23 @@ function loadOrCreateAuthFile(opts) {
52072
52355
  }
52073
52356
  const token = existing?.token || generate();
52074
52357
  const signSecret = existing?.signSecret || generate();
52358
+ const visitorTokenSecret = existing?.visitorTokenSecret || generate();
52075
52359
  const ownerPrincipalId = existing?.ownerPrincipalId || genId();
52076
52360
  const deviceId = ownerPrincipalId;
52077
52361
  const createdAt = existing?.createdAt || now().toISOString();
52078
- const next = { token, signSecret, ownerPrincipalId, deviceId, createdAt };
52362
+ const next = { token, signSecret, visitorTokenSecret, ownerPrincipalId, deviceId, createdAt };
52079
52363
  writeAuthFile(file, next);
52080
52364
  return next;
52081
52365
  }
52082
52366
  function defaultGenerateToken() {
52083
- return import_node_crypto10.default.randomBytes(32).toString("base64url");
52367
+ return import_node_crypto11.default.randomBytes(32).toString("base64url");
52084
52368
  }
52085
52369
  function defaultGenerateOwnerPrincipalId() {
52086
- return `owner-${import_node_crypto10.default.randomUUID()}`;
52370
+ return `owner-${import_node_crypto11.default.randomUUID()}`;
52087
52371
  }
52088
52372
  function readAuthFile(file) {
52089
52373
  try {
52090
- const raw = import_node_fs30.default.readFileSync(file, "utf8");
52374
+ const raw = import_node_fs33.default.readFileSync(file, "utf8");
52091
52375
  const parsed = JSON.parse(raw);
52092
52376
  if (typeof parsed?.token !== "string" || parsed.token.length === 0) {
52093
52377
  return null;
@@ -52095,6 +52379,7 @@ function readAuthFile(file) {
52095
52379
  return {
52096
52380
  token: parsed.token,
52097
52381
  signSecret: typeof parsed.signSecret === "string" && parsed.signSecret.length > 0 ? parsed.signSecret : void 0,
52382
+ visitorTokenSecret: typeof parsed.visitorTokenSecret === "string" && parsed.visitorTokenSecret.length > 0 ? parsed.visitorTokenSecret : void 0,
52098
52383
  ownerPrincipalId: typeof parsed.ownerPrincipalId === "string" && parsed.ownerPrincipalId.length > 0 ? parsed.ownerPrincipalId : void 0,
52099
52384
  deviceId: typeof parsed.deviceId === "string" && parsed.deviceId.length > 0 ? parsed.deviceId : void 0,
52100
52385
  createdAt: typeof parsed.createdAt === "string" ? parsed.createdAt : void 0
@@ -52106,25 +52391,25 @@ function readAuthFile(file) {
52106
52391
  }
52107
52392
  }
52108
52393
  function writeAuthFile(file, content) {
52109
- import_node_fs30.default.mkdirSync(import_node_path31.default.dirname(file), { recursive: true });
52110
- import_node_fs30.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
52394
+ import_node_fs33.default.mkdirSync(import_node_path34.default.dirname(file), { recursive: true });
52395
+ import_node_fs33.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
52111
52396
  try {
52112
- import_node_fs30.default.chmodSync(file, 384);
52397
+ import_node_fs33.default.chmodSync(file, 384);
52113
52398
  } catch {
52114
52399
  }
52115
52400
  }
52116
52401
 
52117
52402
  // src/owner-profile.ts
52118
- var import_node_fs31 = __toESM(require("fs"), 1);
52403
+ var import_node_fs34 = __toESM(require("fs"), 1);
52119
52404
  var import_node_os14 = __toESM(require("os"), 1);
52120
- var import_node_path32 = __toESM(require("path"), 1);
52405
+ var import_node_path35 = __toESM(require("path"), 1);
52121
52406
  var PROFILE_FILENAME = "profile.json";
52122
52407
  function loadOwnerDisplayName(dataDir) {
52123
52408
  const fallback = import_node_os14.default.userInfo().username;
52124
- const profilePath = import_node_path32.default.join(dataDir, PROFILE_FILENAME);
52409
+ const profilePath = import_node_path35.default.join(dataDir, PROFILE_FILENAME);
52125
52410
  let raw;
52126
52411
  try {
52127
- raw = import_node_fs31.default.readFileSync(profilePath, "utf8");
52412
+ raw = import_node_fs34.default.readFileSync(profilePath, "utf8");
52128
52413
  } catch {
52129
52414
  return fallback;
52130
52415
  }
@@ -52147,18 +52432,18 @@ function loadOwnerDisplayName(dataDir) {
52147
52432
  }
52148
52433
 
52149
52434
  // src/feishu-auth/owner-identity-store.ts
52150
- var import_node_fs32 = __toESM(require("fs"), 1);
52151
- var import_node_path33 = __toESM(require("path"), 1);
52435
+ var import_node_fs35 = __toESM(require("fs"), 1);
52436
+ var import_node_path36 = __toESM(require("path"), 1);
52152
52437
  var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
52153
52438
  var OwnerIdentityStore = class {
52154
52439
  file;
52155
52440
  constructor(dataDir) {
52156
- this.file = import_node_path33.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
52441
+ this.file = import_node_path36.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
52157
52442
  }
52158
52443
  read() {
52159
52444
  let raw;
52160
52445
  try {
52161
- raw = import_node_fs32.default.readFileSync(this.file, "utf8");
52446
+ raw = import_node_fs35.default.readFileSync(this.file, "utf8");
52162
52447
  } catch {
52163
52448
  return null;
52164
52449
  }
@@ -52186,16 +52471,16 @@ var OwnerIdentityStore = class {
52186
52471
  };
52187
52472
  }
52188
52473
  write(record) {
52189
- import_node_fs32.default.mkdirSync(import_node_path33.default.dirname(this.file), { recursive: true });
52190
- import_node_fs32.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
52474
+ import_node_fs35.default.mkdirSync(import_node_path36.default.dirname(this.file), { recursive: true });
52475
+ import_node_fs35.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
52191
52476
  try {
52192
- import_node_fs32.default.chmodSync(this.file, 384);
52477
+ import_node_fs35.default.chmodSync(this.file, 384);
52193
52478
  } catch {
52194
52479
  }
52195
52480
  }
52196
52481
  clear() {
52197
52482
  try {
52198
- import_node_fs32.default.unlinkSync(this.file);
52483
+ import_node_fs35.default.unlinkSync(this.file);
52199
52484
  } catch (err) {
52200
52485
  const code = err?.code;
52201
52486
  if (code !== "ENOENT") throw err;
@@ -52204,7 +52489,7 @@ var OwnerIdentityStore = class {
52204
52489
  };
52205
52490
 
52206
52491
  // src/feishu-auth/login-flow.ts
52207
- var import_node_crypto11 = __toESM(require("crypto"), 1);
52492
+ var import_node_crypto12 = __toESM(require("crypto"), 1);
52208
52493
  var STATE_TTL_MS = 5 * 60 * 1e3;
52209
52494
  var LoginFlow = class {
52210
52495
  constructor(deps) {
@@ -52213,7 +52498,7 @@ var LoginFlow = class {
52213
52498
  deps;
52214
52499
  pendingStates = /* @__PURE__ */ new Map();
52215
52500
  start() {
52216
- const state = import_node_crypto11.default.randomBytes(16).toString("base64url");
52501
+ const state = import_node_crypto12.default.randomBytes(16).toString("base64url");
52217
52502
  const now = (this.deps.now ?? Date.now)();
52218
52503
  this.pendingStates.set(state, now);
52219
52504
  this.gcExpired(now);
@@ -52316,9 +52601,9 @@ var CentralClientError = class extends Error {
52316
52601
  code;
52317
52602
  cause;
52318
52603
  };
52319
- async function centralRequest(opts, path63, init) {
52604
+ async function centralRequest(opts, path66, init) {
52320
52605
  const f = opts.fetchImpl ?? globalThis.fetch;
52321
- const url = `${opts.api.replace(/\/+$/, "")}${path63}`;
52606
+ const url = `${opts.api.replace(/\/+$/, "")}${path66}`;
52322
52607
  const ctrl = new AbortController();
52323
52608
  const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
52324
52609
  let res;
@@ -52411,7 +52696,7 @@ async function centralListDevices(opts) {
52411
52696
  }
52412
52697
 
52413
52698
  // src/feishu-auth/verify-token.ts
52414
- var crypto13 = __toESM(require("crypto"), 1);
52699
+ var crypto14 = __toESM(require("crypto"), 1);
52415
52700
  var CONNECT_TOKEN_PREFIX = "clawdtk1";
52416
52701
  function verifyConnectToken(args) {
52417
52702
  const now = args.nowSeconds ?? Math.floor(Date.now() / 1e3);
@@ -52423,7 +52708,7 @@ function verifyConnectToken(args) {
52423
52708
  const data = `${prefix}.${payloadB64}`;
52424
52709
  let signatureValid = false;
52425
52710
  try {
52426
- signatureValid = crypto13.verify(
52711
+ signatureValid = crypto14.verify(
52427
52712
  null,
52428
52713
  Buffer.from(data, "utf8"),
52429
52714
  args.publicKeyPem,
@@ -52460,8 +52745,8 @@ function verifyConnectToken(args) {
52460
52745
  }
52461
52746
 
52462
52747
  // src/feishu-auth/server-key.ts
52463
- var fs42 = __toESM(require("fs"), 1);
52464
- var path42 = __toESM(require("path"), 1);
52748
+ var fs45 = __toESM(require("fs"), 1);
52749
+ var path45 = __toESM(require("path"), 1);
52465
52750
  var FILE_NAME2 = "server-signing-key.json";
52466
52751
  var ServerKeyStore = class {
52467
52752
  constructor(dataDir) {
@@ -52469,12 +52754,12 @@ var ServerKeyStore = class {
52469
52754
  }
52470
52755
  dataDir;
52471
52756
  filePath() {
52472
- return path42.join(this.dataDir, FILE_NAME2);
52757
+ return path45.join(this.dataDir, FILE_NAME2);
52473
52758
  }
52474
52759
  /** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
52475
52760
  read() {
52476
52761
  try {
52477
- const raw = fs42.readFileSync(this.filePath(), "utf8");
52762
+ const raw = fs45.readFileSync(this.filePath(), "utf8");
52478
52763
  const parsed = JSON.parse(raw);
52479
52764
  if (typeof parsed.publicKeyPem === "string" && parsed.publicKeyPem.includes("PUBLIC KEY")) {
52480
52765
  return parsed.publicKeyPem;
@@ -52489,12 +52774,12 @@ var ServerKeyStore = class {
52489
52774
  publicKeyPem,
52490
52775
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
52491
52776
  };
52492
- fs42.mkdirSync(this.dataDir, { recursive: true });
52493
- fs42.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
52777
+ fs45.mkdirSync(this.dataDir, { recursive: true });
52778
+ fs45.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
52494
52779
  }
52495
52780
  clear() {
52496
52781
  try {
52497
- fs42.unlinkSync(this.filePath());
52782
+ fs45.unlinkSync(this.filePath());
52498
52783
  } catch {
52499
52784
  }
52500
52785
  }
@@ -52507,12 +52792,12 @@ init_protocol();
52507
52792
  init_protocol();
52508
52793
 
52509
52794
  // src/session/fork.ts
52510
- var import_node_fs33 = __toESM(require("fs"), 1);
52795
+ var import_node_fs36 = __toESM(require("fs"), 1);
52511
52796
  var import_node_os15 = __toESM(require("os"), 1);
52512
- var import_node_path34 = __toESM(require("path"), 1);
52797
+ var import_node_path37 = __toESM(require("path"), 1);
52513
52798
  init_claude_history();
52514
52799
  function readJsonlEntries(file) {
52515
- const raw = import_node_fs33.default.readFileSync(file, "utf8");
52800
+ const raw = import_node_fs36.default.readFileSync(file, "utf8");
52516
52801
  const out = [];
52517
52802
  for (const line of raw.split("\n")) {
52518
52803
  const t = line.trim();
@@ -52525,10 +52810,10 @@ function readJsonlEntries(file) {
52525
52810
  return out;
52526
52811
  }
52527
52812
  function forkSession(input) {
52528
- const baseDir = input.baseDir ?? import_node_path34.default.join(import_node_os15.default.homedir(), ".claude");
52529
- const projectDir = import_node_path34.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
52530
- const sourceFile = import_node_path34.default.join(projectDir, `${input.toolSessionId}.jsonl`);
52531
- if (!import_node_fs33.default.existsSync(sourceFile)) {
52813
+ const baseDir = input.baseDir ?? import_node_path37.default.join(import_node_os15.default.homedir(), ".claude");
52814
+ const projectDir = import_node_path37.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
52815
+ const sourceFile = import_node_path37.default.join(projectDir, `${input.toolSessionId}.jsonl`);
52816
+ if (!import_node_fs36.default.existsSync(sourceFile)) {
52532
52817
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
52533
52818
  }
52534
52819
  const entries = readJsonlEntries(sourceFile);
@@ -52558,9 +52843,9 @@ function forkSession(input) {
52558
52843
  }
52559
52844
  forkedLines.push(JSON.stringify(forked));
52560
52845
  }
52561
- const forkedFilePath = import_node_path34.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
52562
- import_node_fs33.default.mkdirSync(projectDir, { recursive: true });
52563
- import_node_fs33.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
52846
+ const forkedFilePath = import_node_path37.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
52847
+ import_node_fs36.default.mkdirSync(projectDir, { recursive: true });
52848
+ import_node_fs36.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
52564
52849
  return { forkedToolSessionId, forkedFilePath };
52565
52850
  }
52566
52851
 
@@ -52912,7 +53197,7 @@ function buildPermissionHandlers(deps) {
52912
53197
  }
52913
53198
 
52914
53199
  // src/handlers/history.ts
52915
- var path45 = __toESM(require("path"), 1);
53200
+ var path48 = __toESM(require("path"), 1);
52916
53201
  init_protocol();
52917
53202
 
52918
53203
  // src/session/recent-dirs.ts
@@ -52930,7 +53215,7 @@ function listRecentDirs(store, limit = 50) {
52930
53215
  }
52931
53216
 
52932
53217
  // src/permission/persona-paths.ts
52933
- var path44 = __toESM(require("path"), 1);
53218
+ var path47 = __toESM(require("path"), 1);
52934
53219
  function getAllowedPersonaIds(grants, action) {
52935
53220
  const ids = /* @__PURE__ */ new Set();
52936
53221
  for (const g2 of grants) {
@@ -52943,42 +53228,42 @@ function getAllowedPersonaIds(grants, action) {
52943
53228
  return ids;
52944
53229
  }
52945
53230
  function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
52946
- const target = path44.resolve(absPath);
53231
+ const target = path47.resolve(absPath);
52947
53232
  if (userWorkDir) {
52948
- const u = path44.resolve(userWorkDir);
52949
- const usep = u.endsWith(path44.sep) ? "" : path44.sep;
53233
+ const u = path47.resolve(userWorkDir);
53234
+ const usep = u.endsWith(path47.sep) ? "" : path47.sep;
52950
53235
  if (target === u || target.startsWith(u + usep)) return true;
52951
53236
  }
52952
- const root = path44.resolve(personaRoot);
52953
- const sep3 = root.endsWith(path44.sep) ? "" : path44.sep;
53237
+ const root = path47.resolve(personaRoot);
53238
+ const sep3 = root.endsWith(path47.sep) ? "" : path47.sep;
52954
53239
  if (!target.startsWith(root + sep3)) return false;
52955
- const rel = path44.relative(root, target);
53240
+ const rel = path47.relative(root, target);
52956
53241
  if (!rel || rel.startsWith("..")) return false;
52957
- const personaId = rel.split(path44.sep)[0];
53242
+ const personaId = rel.split(path47.sep)[0];
52958
53243
  if (!personaId) return false;
52959
53244
  const allowed = getAllowedPersonaIds(grants, action);
52960
53245
  if (allowed === "*") return true;
52961
53246
  return allowed.has(personaId);
52962
53247
  }
52963
53248
  function personaIdFromPath(absPath, personaRoot) {
52964
- const root = path44.resolve(personaRoot);
52965
- const target = path44.resolve(absPath);
52966
- const sep3 = root.endsWith(path44.sep) ? "" : path44.sep;
53249
+ const root = path47.resolve(personaRoot);
53250
+ const target = path47.resolve(absPath);
53251
+ const sep3 = root.endsWith(path47.sep) ? "" : path47.sep;
52967
53252
  if (!target.startsWith(root + sep3)) return null;
52968
- const rel = path44.relative(root, target);
53253
+ const rel = path47.relative(root, target);
52969
53254
  if (!rel || rel.startsWith("..")) return null;
52970
- const id = rel.split(path44.sep)[0];
53255
+ const id = rel.split(path47.sep)[0];
52971
53256
  return id || null;
52972
53257
  }
52973
53258
  function isPathWithin(dir, absPath) {
52974
- const d = path44.resolve(dir);
52975
- const t = path44.resolve(absPath);
52976
- const sep3 = d.endsWith(path44.sep) ? "" : path44.sep;
53259
+ const d = path47.resolve(dir);
53260
+ const t = path47.resolve(absPath);
53261
+ const sep3 = d.endsWith(path47.sep) ? "" : path47.sep;
52977
53262
  return t === d || t.startsWith(d + sep3);
52978
53263
  }
52979
53264
  function isPathInGuestBoundary(personaRoot, personaId, userWorkDir, absPath) {
52980
53265
  if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
52981
- return personaIdFromPath(path44.resolve(absPath), personaRoot) === personaId;
53266
+ return personaIdFromPath(path47.resolve(absPath), personaRoot) === personaId;
52982
53267
  }
52983
53268
 
52984
53269
  // src/handlers/history.ts
@@ -53004,7 +53289,7 @@ function buildHistoryHandlers(deps) {
53004
53289
  if (!pid) return false;
53005
53290
  return isGuestPathAllowed(
53006
53291
  ctx.grants,
53007
- path45.join(personaRoot, pid),
53292
+ path48.join(personaRoot, pid),
53008
53293
  personaRoot,
53009
53294
  "read",
53010
53295
  userWorkDir
@@ -53016,7 +53301,7 @@ function buildHistoryHandlers(deps) {
53016
53301
  };
53017
53302
  const list = async (frame, _client, ctx) => {
53018
53303
  const args = HistoryListArgs.parse(frame);
53019
- assertGuestPath(ctx, path45.resolve(args.projectPath), personaRoot, "history:list");
53304
+ assertGuestPath(ctx, path48.resolve(args.projectPath), personaRoot, "history:list");
53020
53305
  const sessions = await history.listSessions(args);
53021
53306
  return { response: { type: "history:list", sessions } };
53022
53307
  };
@@ -53048,13 +53333,13 @@ function buildHistoryHandlers(deps) {
53048
53333
  };
53049
53334
  const subagents = async (frame, _client, ctx) => {
53050
53335
  const args = HistorySubagentsArgs.parse(frame);
53051
- assertGuestPath(ctx, path45.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
53336
+ assertGuestPath(ctx, path48.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
53052
53337
  const subs = await history.listSubagents(args);
53053
53338
  return { response: { type: "history:subagents", subagents: subs } };
53054
53339
  };
53055
53340
  const subagentRead = async (frame, _client, ctx) => {
53056
53341
  const args = HistorySubagentReadArgs.parse(frame);
53057
- assertGuestPath(ctx, path45.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
53342
+ assertGuestPath(ctx, path48.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
53058
53343
  const res = await history.readSubagent(args);
53059
53344
  return { response: { type: "history:subagent-read", ...res } };
53060
53345
  };
@@ -53063,7 +53348,7 @@ function buildHistoryHandlers(deps) {
53063
53348
  if (ctx?.principal.kind === "guest" && personaRoot) {
53064
53349
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
53065
53350
  const filtered = dirs.filter(
53066
- (d) => isGuestPathAllowed(ctx.grants, path45.resolve(d.cwd), personaRoot, "read", userWorkDir)
53351
+ (d) => isGuestPathAllowed(ctx.grants, path48.resolve(d.cwd), personaRoot, "read", userWorkDir)
53067
53352
  );
53068
53353
  return { response: { type: "history:recentDirs", dirs: filtered } };
53069
53354
  }
@@ -53080,7 +53365,7 @@ function buildHistoryHandlers(deps) {
53080
53365
  }
53081
53366
 
53082
53367
  // src/handlers/workspace.ts
53083
- var path46 = __toESM(require("path"), 1);
53368
+ var path49 = __toESM(require("path"), 1);
53084
53369
  var os15 = __toESM(require("os"), 1);
53085
53370
  init_protocol();
53086
53371
  init_protocol();
@@ -53122,22 +53407,22 @@ function buildWorkspaceHandlers(deps) {
53122
53407
  const args = WorkspaceListArgs.parse(frame);
53123
53408
  const isGuest = ctx?.principal.kind === "guest";
53124
53409
  const fallbackCwd = isGuest && personaRoot ? personaRoot : os15.homedir();
53125
- const resolvedCwd = path46.resolve(args.cwd ?? fallbackCwd);
53126
- const target = args.path ? path46.resolve(resolvedCwd, args.path) : resolvedCwd;
53410
+ const resolvedCwd = path49.resolve(args.cwd ?? fallbackCwd);
53411
+ const target = args.path ? path49.resolve(resolvedCwd, args.path) : resolvedCwd;
53127
53412
  assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
53128
53413
  const res = workspace.list({ ...args, cwd: resolvedCwd });
53129
53414
  return { response: { type: "workspace:list", ...res } };
53130
53415
  };
53131
53416
  const read = async (frame, _client, ctx) => {
53132
53417
  const args = WorkspaceReadArgs.parse(frame);
53133
- const target = path46.isAbsolute(args.path) ? path46.resolve(args.path) : path46.resolve(args.cwd, args.path);
53418
+ const target = path49.isAbsolute(args.path) ? path49.resolve(args.path) : path49.resolve(args.cwd, args.path);
53134
53419
  assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
53135
53420
  const res = workspace.read(args);
53136
53421
  return { response: { type: "workspace:read", ...res } };
53137
53422
  };
53138
53423
  const skillsList = async (frame, _client, ctx) => {
53139
53424
  const args = SkillsListArgs.parse(frame);
53140
- const cwdAbs = path46.resolve(args.cwd);
53425
+ const cwdAbs = path49.resolve(args.cwd);
53141
53426
  assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
53142
53427
  const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
53143
53428
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -53149,7 +53434,7 @@ function buildWorkspaceHandlers(deps) {
53149
53434
  };
53150
53435
  const agentsList = async (frame, _client, ctx) => {
53151
53436
  const args = AgentsListArgs.parse(frame);
53152
- const cwdAbs = path46.resolve(args.cwd);
53437
+ const cwdAbs = path49.resolve(args.cwd);
53153
53438
  assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
53154
53439
  if (args.tool === "codex") {
53155
53440
  return { response: { type: "agents:list", agents: [] } };
@@ -53171,20 +53456,20 @@ function buildWorkspaceHandlers(deps) {
53171
53456
  }
53172
53457
 
53173
53458
  // src/handlers/git.ts
53174
- var path48 = __toESM(require("path"), 1);
53459
+ var path51 = __toESM(require("path"), 1);
53175
53460
  init_protocol();
53176
53461
  init_protocol();
53177
53462
 
53178
53463
  // src/workspace/git.ts
53179
53464
  var import_node_child_process10 = require("child_process");
53180
- var import_node_fs34 = __toESM(require("fs"), 1);
53181
- var import_node_path35 = __toESM(require("path"), 1);
53465
+ var import_node_fs37 = __toESM(require("fs"), 1);
53466
+ var import_node_path38 = __toESM(require("path"), 1);
53182
53467
  var import_node_util = require("util");
53183
53468
  var pexec = (0, import_node_util.promisify)(import_node_child_process10.execFile);
53184
53469
  function normalizePath(p2) {
53185
- const resolved = import_node_path35.default.resolve(p2);
53470
+ const resolved = import_node_path38.default.resolve(p2);
53186
53471
  try {
53187
- return import_node_fs34.default.realpathSync(resolved);
53472
+ return import_node_fs37.default.realpathSync(resolved);
53188
53473
  } catch {
53189
53474
  return resolved;
53190
53475
  }
@@ -53258,7 +53543,7 @@ async function listGitBranches(cwd) {
53258
53543
  function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
53259
53544
  if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
53260
53545
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
53261
- if (!isGuestPathAllowed(ctx.grants, path48.resolve(cwd), personaRoot, "read", userWorkDir)) {
53546
+ if (!isGuestPathAllowed(ctx.grants, path51.resolve(cwd), personaRoot, "read", userWorkDir)) {
53262
53547
  throw new ClawdError(
53263
53548
  ERROR_CODES.UNAUTHORIZED,
53264
53549
  `guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
@@ -53776,7 +54061,7 @@ function buildPersonaHandlers(deps) {
53776
54061
  }
53777
54062
 
53778
54063
  // src/handlers/attachment.ts
53779
- var import_node_path36 = __toESM(require("path"), 1);
54064
+ var import_node_path39 = __toESM(require("path"), 1);
53780
54065
  init_protocol();
53781
54066
  init_protocol();
53782
54067
  var DEFAULT_TTL_SECONDS = 24 * 3600;
@@ -53856,12 +54141,12 @@ function buildAttachmentHandlers(deps) {
53856
54141
  `session ${args.sessionId} scope unresolved`
53857
54142
  );
53858
54143
  }
53859
- const cwdAbs = import_node_path36.default.resolve(sessionFile.cwd);
53860
- const candidateAbs = import_node_path36.default.isAbsolute(args.relPath) ? import_node_path36.default.resolve(args.relPath) : import_node_path36.default.resolve(cwdAbs, args.relPath);
54144
+ const cwdAbs = import_node_path39.default.resolve(sessionFile.cwd);
54145
+ const candidateAbs = import_node_path39.default.isAbsolute(args.relPath) ? import_node_path39.default.resolve(args.relPath) : import_node_path39.default.resolve(cwdAbs, args.relPath);
53861
54146
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
53862
54147
  const entries = deps.groupFileStore.list(scope, args.sessionId);
53863
54148
  const entry = entries.find((e) => {
53864
- const storedAbs = import_node_path36.default.isAbsolute(e.relPath) ? import_node_path36.default.resolve(e.relPath) : import_node_path36.default.resolve(cwdAbs, e.relPath);
54149
+ const storedAbs = import_node_path39.default.isAbsolute(e.relPath) ? import_node_path39.default.resolve(e.relPath) : import_node_path39.default.resolve(cwdAbs, e.relPath);
53865
54150
  return storedAbs === candidateAbs && !e.stale;
53866
54151
  });
53867
54152
  if (!entry) {
@@ -53886,7 +54171,7 @@ function buildAttachmentHandlers(deps) {
53886
54171
  if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
53887
54172
  const f = deps.sessionStore.read(sessionId);
53888
54173
  if (!f) return;
53889
- assertGuestAttachmentPath(ctx, import_node_path36.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
54174
+ assertGuestAttachmentPath(ctx, import_node_path39.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
53890
54175
  }
53891
54176
  const groupAdd = async (frame, _client, ctx) => {
53892
54177
  if (!deps.groupFileStore || !deps.getSessionScope) {
@@ -53901,8 +54186,8 @@ function buildAttachmentHandlers(deps) {
53901
54186
  if (!scope) {
53902
54187
  throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
53903
54188
  }
53904
- const cwdAbs = import_node_path36.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
53905
- const candidateAbs = import_node_path36.default.isAbsolute(args.relPath) ? import_node_path36.default.resolve(args.relPath) : import_node_path36.default.resolve(cwdAbs, args.relPath);
54189
+ const cwdAbs = import_node_path39.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
54190
+ const candidateAbs = import_node_path39.default.isAbsolute(args.relPath) ? import_node_path39.default.resolve(args.relPath) : import_node_path39.default.resolve(cwdAbs, args.relPath);
53906
54191
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
53907
54192
  const from = ctx?.principal.kind === "owner" ? "owner" : "agent";
53908
54193
  const size = 0;
@@ -53961,19 +54246,19 @@ function buildAttachmentHandlers(deps) {
53961
54246
 
53962
54247
  // src/handlers/extension.ts
53963
54248
  var import_promises8 = __toESM(require("fs/promises"), 1);
53964
- var import_node_path41 = __toESM(require("path"), 1);
54249
+ var import_node_path44 = __toESM(require("path"), 1);
53965
54250
  init_protocol();
53966
54251
 
53967
54252
  // src/extension/bundle-zip.ts
53968
54253
  var import_promises5 = __toESM(require("fs/promises"), 1);
53969
- var import_node_path37 = __toESM(require("path"), 1);
53970
- var import_node_crypto12 = __toESM(require("crypto"), 1);
54254
+ var import_node_path40 = __toESM(require("path"), 1);
54255
+ var import_node_crypto13 = __toESM(require("crypto"), 1);
53971
54256
  var import_jszip2 = __toESM(require_lib3(), 1);
53972
54257
  async function bundleExtensionDir(dir) {
53973
54258
  const entries = await listFilesSorted(dir);
53974
54259
  const zip = new import_jszip2.default();
53975
54260
  for (const rel of entries) {
53976
- const abs = import_node_path37.default.join(dir, rel);
54261
+ const abs = import_node_path40.default.join(dir, rel);
53977
54262
  const content = await import_promises5.default.readFile(abs);
53978
54263
  zip.file(rel, content, { date: FIXED_DATE });
53979
54264
  }
@@ -53982,7 +54267,7 @@ async function bundleExtensionDir(dir) {
53982
54267
  compression: "DEFLATE",
53983
54268
  compressionOptions: { level: 6 }
53984
54269
  });
53985
- const sha256 = import_node_crypto12.default.createHash("sha256").update(buffer).digest("hex");
54270
+ const sha256 = import_node_crypto13.default.createHash("sha256").update(buffer).digest("hex");
53986
54271
  return { buffer, sha256 };
53987
54272
  }
53988
54273
  var FIXED_DATE = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
@@ -53994,7 +54279,7 @@ async function listFilesSorted(rootDir) {
53994
54279
  return out;
53995
54280
  }
53996
54281
  async function walk(absRoot, relPrefix, out) {
53997
- const dirAbs = import_node_path37.default.join(absRoot, relPrefix);
54282
+ const dirAbs = import_node_path40.default.join(absRoot, relPrefix);
53998
54283
  const entries = await import_promises5.default.readdir(dirAbs, { withFileTypes: true });
53999
54284
  for (const e of entries) {
54000
54285
  if (IGNORE_BASENAMES.has(e.name)) continue;
@@ -54048,25 +54333,25 @@ function computePublishCheck(args) {
54048
54333
 
54049
54334
  // src/extension/install-flow.ts
54050
54335
  var import_promises6 = __toESM(require("fs/promises"), 1);
54051
- var import_node_path39 = __toESM(require("path"), 1);
54336
+ var import_node_path42 = __toESM(require("path"), 1);
54052
54337
  var import_node_os18 = __toESM(require("os"), 1);
54053
- var import_node_crypto13 = __toESM(require("crypto"), 1);
54338
+ var import_node_crypto14 = __toESM(require("crypto"), 1);
54054
54339
  var import_jszip3 = __toESM(require_lib3(), 1);
54055
54340
 
54056
54341
  // src/extension/paths.ts
54057
54342
  var import_node_os17 = __toESM(require("os"), 1);
54058
- var import_node_path38 = __toESM(require("path"), 1);
54343
+ var import_node_path41 = __toESM(require("path"), 1);
54059
54344
  function clawdHomeRoot(override) {
54060
- return override ?? process.env.CLAWD_HOME ?? import_node_path38.default.join(import_node_os17.default.homedir(), ".clawd");
54345
+ return override ?? process.env.CLAWD_HOME ?? import_node_path41.default.join(import_node_os17.default.homedir(), ".clawd");
54061
54346
  }
54062
54347
  function extensionsRoot(override) {
54063
- return import_node_path38.default.join(clawdHomeRoot(override), "extensions");
54348
+ return import_node_path41.default.join(clawdHomeRoot(override), "extensions");
54064
54349
  }
54065
54350
  function publishedChannelsFile(override) {
54066
- return import_node_path38.default.join(clawdHomeRoot(override), "extensions-published.json");
54351
+ return import_node_path41.default.join(clawdHomeRoot(override), "extensions-published.json");
54067
54352
  }
54068
54353
  function bundleCacheRoot(override) {
54069
- return import_node_path38.default.join(clawdHomeRoot(override), "extension-bundles");
54354
+ return import_node_path41.default.join(clawdHomeRoot(override), "extension-bundles");
54070
54355
  }
54071
54356
 
54072
54357
  // src/extension/install-flow.ts
@@ -54079,7 +54364,7 @@ var InstallError = class extends Error {
54079
54364
  };
54080
54365
  async function installFromChannel(args, deps) {
54081
54366
  const { channelRef, snapshotHash, bundleZip } = args;
54082
- const computed = import_node_crypto13.default.createHash("sha256").update(bundleZip).digest("hex");
54367
+ const computed = import_node_crypto14.default.createHash("sha256").update(bundleZip).digest("hex");
54083
54368
  if (computed !== snapshotHash) {
54084
54369
  throw new InstallError(
54085
54370
  "HASH_MISMATCH",
@@ -54093,7 +54378,7 @@ async function installFromChannel(args, deps) {
54093
54378
  throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
54094
54379
  }
54095
54380
  for (const name of Object.keys(zip.files)) {
54096
- if (name.includes("..") || name.startsWith("/") || import_node_path39.default.isAbsolute(name)) {
54381
+ if (name.includes("..") || name.startsWith("/") || import_node_path42.default.isAbsolute(name)) {
54097
54382
  throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
54098
54383
  }
54099
54384
  }
@@ -54125,7 +54410,7 @@ async function installFromChannel(args, deps) {
54125
54410
  );
54126
54411
  }
54127
54412
  const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
54128
- const destDir = import_node_path39.default.join(deps.extensionsRoot, localExtId);
54413
+ const destDir = import_node_path42.default.join(deps.extensionsRoot, localExtId);
54129
54414
  let destExists = false;
54130
54415
  try {
54131
54416
  await import_promises6.default.access(destDir);
@@ -54139,16 +54424,16 @@ async function installFromChannel(args, deps) {
54139
54424
  );
54140
54425
  }
54141
54426
  const stage = await import_promises6.default.mkdtemp(
54142
- import_node_path39.default.join(import_node_os18.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
54427
+ import_node_path42.default.join(import_node_os18.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
54143
54428
  );
54144
54429
  try {
54145
54430
  for (const [name, entry] of Object.entries(zip.files)) {
54146
- const dest = import_node_path39.default.join(stage, name);
54431
+ const dest = import_node_path42.default.join(stage, name);
54147
54432
  if (entry.dir) {
54148
54433
  await import_promises6.default.mkdir(dest, { recursive: true });
54149
54434
  continue;
54150
54435
  }
54151
- await import_promises6.default.mkdir(import_node_path39.default.dirname(dest), { recursive: true });
54436
+ await import_promises6.default.mkdir(import_node_path42.default.dirname(dest), { recursive: true });
54152
54437
  if (name === "manifest.json") {
54153
54438
  const rewritten = { ...parsed.data, id: localExtId };
54154
54439
  await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -54169,9 +54454,9 @@ async function installFromChannel(args, deps) {
54169
54454
 
54170
54455
  // src/extension/update-flow.ts
54171
54456
  var import_promises7 = __toESM(require("fs/promises"), 1);
54172
- var import_node_path40 = __toESM(require("path"), 1);
54457
+ var import_node_path43 = __toESM(require("path"), 1);
54173
54458
  var import_node_os19 = __toESM(require("os"), 1);
54174
- var import_node_crypto14 = __toESM(require("crypto"), 1);
54459
+ var import_node_crypto15 = __toESM(require("crypto"), 1);
54175
54460
  var import_jszip4 = __toESM(require_lib3(), 1);
54176
54461
  var UpdateError = class extends Error {
54177
54462
  constructor(code, message) {
@@ -54186,11 +54471,11 @@ async function updateFromChannel(args, deps) {
54186
54471
  channelRef.extId,
54187
54472
  channelRef.ownerPrincipalId
54188
54473
  );
54189
- const liveDir = import_node_path40.default.join(deps.extensionsRoot, localExtId);
54474
+ const liveDir = import_node_path43.default.join(deps.extensionsRoot, localExtId);
54190
54475
  const prevDir = `${liveDir}.prev`;
54191
54476
  let existingVersion;
54192
54477
  try {
54193
- const raw = await import_promises7.default.readFile(import_node_path40.default.join(liveDir, "manifest.json"), "utf8");
54478
+ const raw = await import_promises7.default.readFile(import_node_path43.default.join(liveDir, "manifest.json"), "utf8");
54194
54479
  const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
54195
54480
  if (!parsed2.success) {
54196
54481
  throw new UpdateError(
@@ -54209,7 +54494,7 @@ async function updateFromChannel(args, deps) {
54209
54494
  if (e instanceof UpdateError) throw e;
54210
54495
  throw e;
54211
54496
  }
54212
- const computed = import_node_crypto14.default.createHash("sha256").update(bundleZip).digest("hex");
54497
+ const computed = import_node_crypto15.default.createHash("sha256").update(bundleZip).digest("hex");
54213
54498
  if (computed !== snapshotHash) {
54214
54499
  throw new UpdateError(
54215
54500
  "HASH_MISMATCH",
@@ -54223,7 +54508,7 @@ async function updateFromChannel(args, deps) {
54223
54508
  throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
54224
54509
  }
54225
54510
  for (const name of Object.keys(zip.files)) {
54226
- if (name.includes("..") || name.startsWith("/") || import_node_path40.default.isAbsolute(name)) {
54511
+ if (name.includes("..") || name.startsWith("/") || import_node_path43.default.isAbsolute(name)) {
54227
54512
  throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
54228
54513
  }
54229
54514
  }
@@ -54258,16 +54543,16 @@ async function updateFromChannel(args, deps) {
54258
54543
  await import_promises7.default.rm(prevDir, { recursive: true, force: true });
54259
54544
  await import_promises7.default.rename(liveDir, prevDir);
54260
54545
  const stage = await import_promises7.default.mkdtemp(
54261
- import_node_path40.default.join(import_node_os19.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
54546
+ import_node_path43.default.join(import_node_os19.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
54262
54547
  );
54263
54548
  try {
54264
54549
  for (const [name, entry] of Object.entries(zip.files)) {
54265
- const dest = import_node_path40.default.join(stage, name);
54550
+ const dest = import_node_path43.default.join(stage, name);
54266
54551
  if (entry.dir) {
54267
54552
  await import_promises7.default.mkdir(dest, { recursive: true });
54268
54553
  continue;
54269
54554
  }
54270
- await import_promises7.default.mkdir(import_node_path40.default.dirname(dest), { recursive: true });
54555
+ await import_promises7.default.mkdir(import_node_path43.default.dirname(dest), { recursive: true });
54271
54556
  if (name === "manifest.json") {
54272
54557
  const rewritten = { ...parsed.data, id: localExtId };
54273
54558
  await import_promises7.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
@@ -54360,7 +54645,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
54360
54645
  );
54361
54646
  }
54362
54647
  }
54363
- const manifestPath = import_node_path41.default.join(root, extId, "manifest.json");
54648
+ const manifestPath = import_node_path44.default.join(root, extId, "manifest.json");
54364
54649
  const manifest = await readManifest(root, extId);
54365
54650
  const next = { ...manifest, version: newVersion };
54366
54651
  const tmp = `${manifestPath}.tmp`;
@@ -54368,7 +54653,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
54368
54653
  await import_promises8.default.rename(tmp, manifestPath);
54369
54654
  }
54370
54655
  async function readManifest(root, extId) {
54371
- const file = import_node_path41.default.join(root, extId, "manifest.json");
54656
+ const file = import_node_path44.default.join(root, extId, "manifest.json");
54372
54657
  let raw;
54373
54658
  try {
54374
54659
  raw = await import_promises8.default.readFile(file, "utf8");
@@ -54459,7 +54744,7 @@ function buildExtensionHandlers(deps) {
54459
54744
  };
54460
54745
  async function buildSnapshotMeta(extId) {
54461
54746
  const manifest = await readManifest(deps.root, extId);
54462
- const { sha256, buffer } = await bundleExtensionDir(import_node_path41.default.join(deps.root, extId));
54747
+ const { sha256, buffer } = await bundleExtensionDir(import_node_path44.default.join(deps.root, extId));
54463
54748
  return { manifest, contentHash: sha256, buffer };
54464
54749
  }
54465
54750
  const publish = async (frame, _client, ctx) => {
@@ -54640,9 +54925,9 @@ function buildExtensionHandlers(deps) {
54640
54925
  }
54641
54926
 
54642
54927
  // src/app-builder/project-store.ts
54643
- var import_node_fs35 = require("fs");
54928
+ var import_node_fs38 = require("fs");
54644
54929
  var import_node_child_process11 = require("child_process");
54645
- var import_node_path42 = require("path");
54930
+ var import_node_path45 = require("path");
54646
54931
  init_protocol();
54647
54932
  var PROJECTS_DIR = "projects";
54648
54933
  var META_FILE = ".clawd-project.json";
@@ -54656,19 +54941,19 @@ var ProjectStore = class {
54656
54941
  root;
54657
54942
  /** projects/<name>/.clawd-project.json 路径 */
54658
54943
  metaPath(name) {
54659
- return (0, import_node_path42.join)(this.projectsRoot(), name, META_FILE);
54944
+ return (0, import_node_path45.join)(this.projectsRoot(), name, META_FILE);
54660
54945
  }
54661
54946
  /** projects/<name>/ 目录路径(cwd 用) */
54662
54947
  projectDir(name) {
54663
- return (0, import_node_path42.join)(this.projectsRoot(), name);
54948
+ return (0, import_node_path45.join)(this.projectsRoot(), name);
54664
54949
  }
54665
54950
  projectsRoot() {
54666
- return (0, import_node_path42.join)(this.root, PROJECTS_DIR);
54951
+ return (0, import_node_path45.join)(this.root, PROJECTS_DIR);
54667
54952
  }
54668
54953
  async list() {
54669
54954
  let entries;
54670
54955
  try {
54671
- entries = await import_node_fs35.promises.readdir(this.projectsRoot());
54956
+ entries = await import_node_fs38.promises.readdir(this.projectsRoot());
54672
54957
  } catch (err) {
54673
54958
  if (err.code === "ENOENT") return [];
54674
54959
  throw err;
@@ -54676,7 +54961,7 @@ var ProjectStore = class {
54676
54961
  const out = [];
54677
54962
  for (const name of entries) {
54678
54963
  try {
54679
- const raw = await import_node_fs35.promises.readFile(this.metaPath(name), "utf8");
54964
+ const raw = await import_node_fs38.promises.readFile(this.metaPath(name), "utf8");
54680
54965
  const json = JSON.parse(raw);
54681
54966
  let migrated = false;
54682
54967
  if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
@@ -54687,7 +54972,7 @@ var ProjectStore = class {
54687
54972
  if (parsed.success) {
54688
54973
  out.push(parsed.data);
54689
54974
  if (migrated) {
54690
- void import_node_fs35.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
54975
+ void import_node_fs38.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
54691
54976
  });
54692
54977
  }
54693
54978
  }
@@ -54731,8 +55016,8 @@ var ProjectStore = class {
54731
55016
  throw new Error(`invalid name "${name}": ${validated.error.message}`);
54732
55017
  }
54733
55018
  const dir = this.projectDir(name);
54734
- await import_node_fs35.promises.mkdir(dir, { recursive: true });
54735
- await import_node_fs35.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
55019
+ await import_node_fs38.promises.mkdir(dir, { recursive: true });
55020
+ await import_node_fs38.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
54736
55021
  return meta;
54737
55022
  }
54738
55023
  /**
@@ -54775,7 +55060,7 @@ var ProjectStore = class {
54775
55060
  }
54776
55061
  async delete(name) {
54777
55062
  const dir = this.projectDir(name);
54778
- await import_node_fs35.promises.rm(dir, { recursive: true, force: true });
55063
+ await import_node_fs38.promises.rm(dir, { recursive: true, force: true });
54779
55064
  }
54780
55065
  async updatePort(name, newPort) {
54781
55066
  if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
@@ -54791,7 +55076,7 @@ var ProjectStore = class {
54791
55076
  throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
54792
55077
  }
54793
55078
  const updated = { ...target, port: newPort };
54794
- await import_node_fs35.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
55079
+ await import_node_fs38.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
54795
55080
  return updated;
54796
55081
  }
54797
55082
  /**
@@ -54808,7 +55093,7 @@ var ProjectStore = class {
54808
55093
  if (!validated.success) {
54809
55094
  throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
54810
55095
  }
54811
- await import_node_fs35.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
55096
+ await import_node_fs38.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
54812
55097
  return validated.data;
54813
55098
  }
54814
55099
  /**
@@ -54829,7 +55114,7 @@ var ProjectStore = class {
54829
55114
  if (!validated.success) {
54830
55115
  throw new Error(`invalid publishJob: ${validated.error.message}`);
54831
55116
  }
54832
- await import_node_fs35.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
55117
+ await import_node_fs38.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
54833
55118
  return validated.data;
54834
55119
  }
54835
55120
  /** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
@@ -54844,7 +55129,7 @@ var ProjectStore = class {
54844
55129
  if (!validated.success) {
54845
55130
  throw new Error(`failed to clear publishJob: ${validated.error.message}`);
54846
55131
  }
54847
- await import_node_fs35.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
55132
+ await import_node_fs38.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
54848
55133
  return validated.data;
54849
55134
  }
54850
55135
  };
@@ -54914,7 +55199,7 @@ function listPidsOnPort(port) {
54914
55199
  }
54915
55200
 
54916
55201
  // src/app-builder/publish-registry.ts
54917
- var import_node_crypto15 = require("crypto");
55202
+ var import_node_crypto16 = require("crypto");
54918
55203
  var PublishJobRegistry = class {
54919
55204
  jobs = /* @__PURE__ */ new Map();
54920
55205
  has(name) {
@@ -54931,7 +55216,7 @@ var PublishJobRegistry = class {
54931
55216
  if (this.jobs.has(args.name)) {
54932
55217
  throw new Error(`already publishing: ${args.name}`);
54933
55218
  }
54934
- const jobId = args.jobId ?? `job-${(0, import_node_crypto15.randomUUID)()}`;
55219
+ const jobId = args.jobId ?? `job-${(0, import_node_crypto16.randomUUID)()}`;
54935
55220
  this.jobs.set(args.name, {
54936
55221
  jobId,
54937
55222
  name: args.name,
@@ -54965,8 +55250,8 @@ var PublishJobRegistry = class {
54965
55250
 
54966
55251
  // src/app-builder/publish-job-runner.ts
54967
55252
  var import_node_child_process13 = require("child_process");
54968
- var import_node_fs36 = require("fs");
54969
- var import_node_path43 = require("path");
55253
+ var import_node_fs39 = require("fs");
55254
+ var import_node_path46 = require("path");
54970
55255
 
54971
55256
  // src/app-builder/publish-stage-parser.ts
54972
55257
  var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
@@ -54998,10 +55283,10 @@ async function startPublishJob(deps, args) {
54998
55283
  return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
54999
55284
  }
55000
55285
  const projDir = projectDir(args.name);
55001
- const logPath = (0, import_node_path43.join)(projDir, ".publish.log");
55286
+ const logPath = (0, import_node_path46.join)(projDir, ".publish.log");
55002
55287
  let logStream = null;
55003
55288
  try {
55004
- logStream = (0, import_node_fs36.createWriteStream)(logPath, { flags: "w" });
55289
+ logStream = (0, import_node_fs39.createWriteStream)(logPath, { flags: "w" });
55005
55290
  } catch {
55006
55291
  logStream = null;
55007
55292
  }
@@ -55258,8 +55543,8 @@ async function recoverInterruptedJobs(deps) {
55258
55543
 
55259
55544
  // src/handlers/app-builder.ts
55260
55545
  init_protocol();
55261
- var import_node_path44 = require("path");
55262
- var import_node_fs37 = require("fs");
55546
+ var import_node_path47 = require("path");
55547
+ var import_node_fs40 = require("fs");
55263
55548
  var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
55264
55549
  var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
55265
55550
  async function recoverInterruptedPublishJobs(store, logger) {
@@ -55340,7 +55625,7 @@ function buildAppBuilderHandlers(deps) {
55340
55625
  async function listAllUsersProjects() {
55341
55626
  if (!deps.usersRoot || !deps.getStore) return [];
55342
55627
  const getStore = deps.getStore;
55343
- const userIds = await import_node_fs37.promises.readdir(deps.usersRoot).catch(() => []);
55628
+ const userIds = await import_node_fs40.promises.readdir(deps.usersRoot).catch(() => []);
55344
55629
  const perUser = await Promise.all(
55345
55630
  userIds.map((uid) => getStore(uid).list().catch(() => []))
55346
55631
  );
@@ -55416,8 +55701,8 @@ function buildAppBuilderHandlers(deps) {
55416
55701
  const project = await userStore.create(f.name, reservedPorts);
55417
55702
  try {
55418
55703
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
55419
- const templateSrcDir = (0, import_node_path44.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
55420
- const scaffoldScript = (0, import_node_path44.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
55704
+ const templateSrcDir = (0, import_node_path47.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
55705
+ const scaffoldScript = (0, import_node_path47.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
55421
55706
  const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
55422
55707
  deps.logger?.info("app-builder.scaffold.done", {
55423
55708
  name: project.name,
@@ -55638,7 +55923,7 @@ function buildAppBuilderHandlers(deps) {
55638
55923
  await userStore.clearPublishJob(args.name);
55639
55924
  }
55640
55925
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
55641
- const scriptPath = (0, import_node_path44.join)(deps.deployKitRoot, "scripts", "publish.sh");
55926
+ const scriptPath = (0, import_node_path47.join)(deps.deployKitRoot, "scripts", "publish.sh");
55642
55927
  deps.logger?.info("app-builder.publish.start", {
55643
55928
  name: args.name,
55644
55929
  sessionId: boundSession.sessionId,
@@ -55745,16 +56030,11 @@ function daemonShiftToUiShift(d, _nowMs) {
55745
56030
  }
55746
56031
  if (!d.enabled) noteParts.push("(disabled)");
55747
56032
  const note = noteParts.join(" \u2014 ");
55748
- const runs = d.runs.map((r) => ({
55749
- startedAtMs: r.startedAtMs,
55750
- ...r.sessionId ? { sessionId: r.sessionId } : {}
55751
- }));
55752
56033
  return {
55753
56034
  id: d.id,
55754
56035
  agentId: d.targetPersona,
55755
56036
  schedule: d.schedule,
55756
- note,
55757
- ...runs.length > 0 ? { runs } : {}
56037
+ note
55758
56038
  };
55759
56039
  }
55760
56040
  function buildShiftHandlers(deps) {
@@ -55780,9 +56060,34 @@ function buildShiftHandlers(deps) {
55780
56060
  };
55781
56061
  }
55782
56062
 
56063
+ // src/handlers/visitor.ts
56064
+ init_protocol();
56065
+ function ensureOwner3(ctx) {
56066
+ if (!ctx || ctx.principal.kind !== "owner") {
56067
+ throw new ClawdError(
56068
+ ERROR_CODES.UNAUTHORIZED,
56069
+ "UNAUTHORIZED: visitor:* requires owner ctx"
56070
+ );
56071
+ }
56072
+ }
56073
+ function buildVisitorHandlers(deps) {
56074
+ const list = async (_frame, _client, ctx) => {
56075
+ ensureOwner3(ctx);
56076
+ return {
56077
+ response: {
56078
+ type: "visitor:list",
56079
+ visitors: deps.visitorStore.list()
56080
+ }
56081
+ };
56082
+ };
56083
+ return {
56084
+ "visitor:list": list
56085
+ };
56086
+ }
56087
+
55783
56088
  // src/extension/registry.ts
55784
56089
  var import_promises9 = __toESM(require("fs/promises"), 1);
55785
- var import_node_path45 = __toESM(require("path"), 1);
56090
+ var import_node_path48 = __toESM(require("path"), 1);
55786
56091
  async function loadAll(root) {
55787
56092
  let entries;
55788
56093
  try {
@@ -55795,13 +56100,13 @@ async function loadAll(root) {
55795
56100
  for (const ent of entries) {
55796
56101
  if (!ent.isDirectory()) continue;
55797
56102
  if (ent.name.startsWith(".")) continue;
55798
- records.push(await loadOne(import_node_path45.default.join(root, ent.name), ent.name));
56103
+ records.push(await loadOne(import_node_path48.default.join(root, ent.name), ent.name));
55799
56104
  }
55800
56105
  records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
55801
56106
  return records;
55802
56107
  }
55803
56108
  async function loadOne(dir, dirName) {
55804
- const manifestPath = import_node_path45.default.join(dir, "manifest.json");
56109
+ const manifestPath = import_node_path48.default.join(dir, "manifest.json");
55805
56110
  let raw;
55806
56111
  try {
55807
56112
  raw = await import_promises9.default.readFile(manifestPath, "utf8");
@@ -55846,7 +56151,7 @@ async function loadOne(dir, dirName) {
55846
56151
 
55847
56152
  // src/extension/uninstall.ts
55848
56153
  var import_promises10 = __toESM(require("fs/promises"), 1);
55849
- var import_node_path46 = __toESM(require("path"), 1);
56154
+ var import_node_path49 = __toESM(require("path"), 1);
55850
56155
  var UninstallError = class extends Error {
55851
56156
  constructor(code, message) {
55852
56157
  super(message);
@@ -55855,7 +56160,7 @@ var UninstallError = class extends Error {
55855
56160
  code;
55856
56161
  };
55857
56162
  async function uninstall(deps) {
55858
- const dir = import_node_path46.default.join(deps.root, deps.extId);
56163
+ const dir = import_node_path49.default.join(deps.root, deps.extId);
55859
56164
  try {
55860
56165
  await import_promises10.default.access(dir);
55861
56166
  } catch {
@@ -55936,7 +56241,8 @@ function buildMethodHandlers(deps) {
55936
56241
  shiftStore: deps.shiftStore,
55937
56242
  ownerIdProvider: deps.shiftOwnerIdProvider,
55938
56243
  now: () => Date.now()
55939
- })
56244
+ }),
56245
+ ...buildVisitorHandlers({ visitorStore: deps.visitorStore })
55940
56246
  };
55941
56247
  }
55942
56248
 
@@ -56229,6 +56535,9 @@ var METHOD_GRANT_MAP = {
56229
56535
  // 写入路径在 device:connect / 自动反向;list / remove 作联系人读 / 删,owner-only。
56230
56536
  "contact:list": ADMIN_ANY,
56231
56537
  "contact:remove": ADMIN_ANY,
56538
+ // ---- visitor:* (访客名单,owner-only) ----
56539
+ // owner 看完整访客名单(含没开会话的);guest 不可调(handler 内再 assertOwner 兜底)。
56540
+ "visitor:list": ADMIN_ANY,
56232
56541
  // ---- session:* / chat:* 业务方法(v2 Phase 8 两层模型)----
56233
56542
  // dispatcher 不验资源,handler 内按 ctx + frame.args 反查 ownerPersonaId 调 assertGrant。
56234
56543
  // owner 自动通过(ctx 自带 '*':'admin' grant 一切 match);guest 在被授权 persona 内可调。
@@ -56407,7 +56716,7 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
56407
56716
 
56408
56717
  // src/extension/runtime.ts
56409
56718
  var import_node_child_process15 = require("child_process");
56410
- var import_node_path47 = __toESM(require("path"), 1);
56719
+ var import_node_path50 = __toESM(require("path"), 1);
56411
56720
  var import_promises11 = require("timers/promises");
56412
56721
 
56413
56722
  // src/extension/port-allocator.ts
@@ -56508,7 +56817,7 @@ var Runtime = class {
56508
56817
  /\$CLAWOS_EXT_PORT/g,
56509
56818
  String(port)
56510
56819
  );
56511
- const dir = import_node_path47.default.join(this.root, extId);
56820
+ const dir = import_node_path50.default.join(this.root, extId);
56512
56821
  const env = {
56513
56822
  ...process.env,
56514
56823
  CLAWOS_EXT_PORT: String(port),
@@ -56620,7 +56929,7 @@ ${handle.stderrTail}`
56620
56929
 
56621
56930
  // src/extension/published-channels.ts
56622
56931
  var import_promises12 = __toESM(require("fs/promises"), 1);
56623
- var import_node_path48 = __toESM(require("path"), 1);
56932
+ var import_node_path51 = __toESM(require("path"), 1);
56624
56933
  init_zod();
56625
56934
  var PublishedChannelsError = class extends Error {
56626
56935
  constructor(code, message) {
@@ -56719,7 +57028,7 @@ var PublishedChannelStore = class {
56719
57028
  )
56720
57029
  };
56721
57030
  const tmp = `${this.filePath}.tmp`;
56722
- await import_promises12.default.mkdir(import_node_path48.default.dirname(this.filePath), { recursive: true });
57031
+ await import_promises12.default.mkdir(import_node_path51.default.dirname(this.filePath), { recursive: true });
56723
57032
  await import_promises12.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
56724
57033
  await import_promises12.default.rename(tmp, this.filePath);
56725
57034
  }
@@ -56727,7 +57036,7 @@ var PublishedChannelStore = class {
56727
57036
 
56728
57037
  // src/extension/bundle-cache.ts
56729
57038
  var import_promises13 = __toESM(require("fs/promises"), 1);
56730
- var import_node_path49 = __toESM(require("path"), 1);
57039
+ var import_node_path52 = __toESM(require("path"), 1);
56731
57040
  var BundleCache = class {
56732
57041
  constructor(rootDir) {
56733
57042
  this.rootDir = rootDir;
@@ -56736,14 +57045,14 @@ var BundleCache = class {
56736
57045
  /** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
56737
57046
  async write(snapshotHash, buffer) {
56738
57047
  await import_promises13.default.mkdir(this.rootDir, { recursive: true });
56739
- const file = import_node_path49.default.join(this.rootDir, `${snapshotHash}.zip`);
57048
+ const file = import_node_path52.default.join(this.rootDir, `${snapshotHash}.zip`);
56740
57049
  const tmp = `${file}.tmp`;
56741
57050
  await import_promises13.default.writeFile(tmp, buffer, { mode: 384 });
56742
57051
  await import_promises13.default.rename(tmp, file);
56743
57052
  }
56744
57053
  /** Returns the bundle bytes, or null when the file doesn't exist. */
56745
57054
  async read(snapshotHash) {
56746
- const file = import_node_path49.default.join(this.rootDir, `${snapshotHash}.zip`);
57055
+ const file = import_node_path52.default.join(this.rootDir, `${snapshotHash}.zip`);
56747
57056
  try {
56748
57057
  return await import_promises13.default.readFile(file);
56749
57058
  } catch (e) {
@@ -56753,13 +57062,13 @@ var BundleCache = class {
56753
57062
  }
56754
57063
  /** Idempotent — missing file is not an error. */
56755
57064
  async delete(snapshotHash) {
56756
- const file = import_node_path49.default.join(this.rootDir, `${snapshotHash}.zip`);
57065
+ const file = import_node_path52.default.join(this.rootDir, `${snapshotHash}.zip`);
56757
57066
  await import_promises13.default.rm(file, { force: true });
56758
57067
  }
56759
57068
  };
56760
57069
 
56761
57070
  // src/index.ts
56762
- var import_meta5 = {};
57071
+ var import_meta6 = {};
56763
57072
  async function startDaemon(config) {
56764
57073
  const authFile = loadOrCreateAuthFile({ dataDir: config.dataDir });
56765
57074
  const logShippingCfg = config.logShipping ?? { endpoint: null, sampling: {}, mode: "off" };
@@ -56778,7 +57087,7 @@ async function startDaemon(config) {
56778
57087
  });
56779
57088
  const logger = createLogger({
56780
57089
  level: config.logLevel,
56781
- file: import_node_path50.default.join(config.dataDir, "clawd.log"),
57090
+ file: import_node_path53.default.join(config.dataDir, "clawd.log"),
56782
57091
  logClient
56783
57092
  });
56784
57093
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
@@ -56815,6 +57124,12 @@ async function startDaemon(config) {
56815
57124
  isOwnerToken: (x) => resolvedAuthToken != null && constantTimeEqual(x, resolvedAuthToken),
56816
57125
  ownerPrincipalId,
56817
57126
  ownerDisplayName,
57127
+ // persona web 分享:访客 visitor token = daemon 自签 HMAC(无 aud),独立于飞书 connect token
57128
+ // 基建——secret 恒存在(auth-store backfill),故无条件注入。验签 ok → guest ctx(persona wildcard)。
57129
+ verifyVisitorToken: (t) => {
57130
+ const r = verifyVisitorToken(authFile.visitorTokenSecret, t, Math.floor(Date.now() / 1e3));
57131
+ return r.ok ? { ok: true, payload: r.payload } : { ok: false };
57132
+ },
56818
57133
  ...feishuActive ? {
56819
57134
  verifyConnectToken: (token) => {
56820
57135
  const publicKeyPem = serverKeyStore.read();
@@ -56912,8 +57227,8 @@ async function startDaemon(config) {
56912
57227
  const agents = new AgentsScanner();
56913
57228
  const history = new ClaudeHistoryReader();
56914
57229
  let transport = null;
56915
- const personaStore = new PersonaStore(import_node_path50.default.join(config.dataDir, "personas"));
56916
- const usersRoot = import_node_path50.default.join(config.dataDir, "users");
57230
+ const personaStore = new PersonaStore(import_node_path53.default.join(config.dataDir, "personas"));
57231
+ const usersRoot = import_node_path53.default.join(config.dataDir, "users");
56917
57232
  const defaultsRoot = findDefaultsRoot(logger);
56918
57233
  if (defaultsRoot) {
56919
57234
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -56933,17 +57248,17 @@ async function startDaemon(config) {
56933
57248
  migrateCodexSandbox({ store: personaStore, logger });
56934
57249
  const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
56935
57250
  const personaDispatchManager = new PersonaDispatchManager({ genId: () => v4_default() });
56936
- const here = typeof __dirname === "string" ? __dirname : import_node_path50.default.dirname((0, import_node_url3.fileURLToPath)(import_meta5.url));
57251
+ const here = typeof __dirname === "string" ? __dirname : import_node_path53.default.dirname((0, import_node_url4.fileURLToPath)(import_meta6.url));
56937
57252
  const dispatchServerCandidates = [
56938
- import_node_path50.default.join(here, "dispatch", "mcp-server.cjs"),
57253
+ import_node_path53.default.join(here, "dispatch", "mcp-server.cjs"),
56939
57254
  // 生产 dist/index → dist/dispatch/mcp-server.cjs
56940
- import_node_path50.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
57255
+ import_node_path53.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
56941
57256
  // dev tsx src/index → ../dist/dispatch/mcp-server.cjs
56942
57257
  ];
56943
- const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs38.default.existsSync(p2));
57258
+ const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs41.default.existsSync(p2));
56944
57259
  let dispatchMcpConfigPath2;
56945
57260
  if (dispatchServerScriptPath) {
56946
- const dispatchLogPath = import_node_path50.default.join(config.dataDir, "dispatch-mcp-server.log");
57261
+ const dispatchLogPath = import_node_path53.default.join(config.dataDir, "dispatch-mcp-server.log");
56947
57262
  dispatchMcpConfigPath2 = writeDispatchMcpConfig({
56948
57263
  dataDir: config.dataDir,
56949
57264
  serverScriptPath: dispatchServerScriptPath,
@@ -56960,15 +57275,15 @@ async function startDaemon(config) {
56960
57275
  });
56961
57276
  }
56962
57277
  const ticketServerCandidates = [
56963
- import_node_path50.default.join(here, "ticket", "mcp-server.cjs"),
56964
- import_node_path50.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
57278
+ import_node_path53.default.join(here, "ticket", "mcp-server.cjs"),
57279
+ import_node_path53.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
56965
57280
  ];
56966
- const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs38.default.existsSync(p2));
57281
+ const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs41.default.existsSync(p2));
56967
57282
  const ticketOwnerUnionId = feishuIdentity?.identity.unionId ?? "";
56968
57283
  const ticketOwnerName = feishuIdentity?.identity.displayName ?? "";
56969
57284
  let ticketMcpConfigPath2;
56970
57285
  if (ticketServerScriptPath && ticketOwnerUnionId) {
56971
- const ticketLogPath = import_node_path50.default.join(config.dataDir, "ticket-mcp-server.log");
57286
+ const ticketLogPath = import_node_path53.default.join(config.dataDir, "ticket-mcp-server.log");
56972
57287
  ticketMcpConfigPath2 = writeTicketMcpConfig({
56973
57288
  dataDir: config.dataDir,
56974
57289
  serverScriptPath: ticketServerScriptPath,
@@ -56989,13 +57304,13 @@ async function startDaemon(config) {
56989
57304
  });
56990
57305
  }
56991
57306
  const shiftServerCandidates = [
56992
- import_node_path50.default.join(here, "shift", "mcp-server.cjs"),
56993
- import_node_path50.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
57307
+ import_node_path53.default.join(here, "shift", "mcp-server.cjs"),
57308
+ import_node_path53.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
56994
57309
  ];
56995
- const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs38.default.existsSync(p2));
57310
+ const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs41.default.existsSync(p2));
56996
57311
  let shiftMcpConfigPath2;
56997
57312
  if (shiftServerScriptPath) {
56998
- const shiftLogPath = import_node_path50.default.join(config.dataDir, "shift-mcp-server.log");
57313
+ const shiftLogPath = import_node_path53.default.join(config.dataDir, "shift-mcp-server.log");
56999
57314
  shiftMcpConfigPath2 = await writeShiftMcpConfig({
57000
57315
  dataDir: config.dataDir,
57001
57316
  serverScriptPath: shiftServerScriptPath,
@@ -57013,7 +57328,7 @@ async function startDaemon(config) {
57013
57328
  );
57014
57329
  }
57015
57330
  const shiftStore = createShiftStore({
57016
- filePath: import_node_path50.default.join(config.dataDir, "shift.json"),
57331
+ filePath: import_node_path53.default.join(config.dataDir, "shift.json"),
57017
57332
  ownerIdProvider: () => ownerPrincipalId,
57018
57333
  now: () => Date.now()
57019
57334
  });
@@ -57031,7 +57346,7 @@ async function startDaemon(config) {
57031
57346
  getAdapter,
57032
57347
  historyReader: history,
57033
57348
  dataDir: config.dataDir,
57034
- personaRoot: import_node_path50.default.join(config.dataDir, "personas"),
57349
+ personaRoot: import_node_path53.default.join(config.dataDir, "personas"),
57035
57350
  usersRoot,
57036
57351
  personaStore,
57037
57352
  ownerDisplayName,
@@ -57072,10 +57387,10 @@ async function startDaemon(config) {
57072
57387
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
57073
57388
  attachmentGroup: {
57074
57389
  onFileEdit: (input) => {
57075
- const absPath = import_node_path50.default.isAbsolute(input.relPath) ? input.relPath : import_node_path50.default.join(input.cwd, input.relPath);
57390
+ const absPath = import_node_path53.default.isAbsolute(input.relPath) ? input.relPath : import_node_path53.default.join(input.cwd, input.relPath);
57076
57391
  let size = 0;
57077
57392
  try {
57078
- size = import_node_fs38.default.statSync(absPath).size;
57393
+ size = import_node_fs41.default.statSync(absPath).size;
57079
57394
  } catch (err) {
57080
57395
  logger.warn("attachment.onFileEdit stat failed", {
57081
57396
  sessionId: input.sessionId,
@@ -57208,6 +57523,14 @@ async function startDaemon(config) {
57208
57523
  ttcAuthBase: TTC_HOSTS.auth,
57209
57524
  getCallbackUrl: () => `http://127.0.0.1:${config.port}/auth/callback`
57210
57525
  });
57526
+ const visitorStore = createVisitorStore({ dir: config.dataDir });
57527
+ const exchangeVisitorToken = buildVisitorLogin({
57528
+ getSecret: () => authFile.visitorTokenSecret,
57529
+ fetchUserInfo: (ttcToken) => fetchTtcUserInfo({ apiBase: TTC_HOSTS.api, token: ttcToken }),
57530
+ visitorStore,
57531
+ now: () => Date.now(),
57532
+ ttlSeconds: 7 * 24 * 3600
57533
+ });
57211
57534
  const getProjectStore = (userId) => new ProjectStore(deriveUserWorkDir(userId, usersRoot));
57212
57535
  const appBuilderStore = getProjectStore(ownerPrincipalId);
57213
57536
  recoverInterruptedPublishJobs(appBuilderStore, logger).catch((err) => {
@@ -57265,11 +57588,11 @@ async function startDaemon(config) {
57265
57588
  // 'persona/<pid>/owner',default 走 'default'。
57266
57589
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
57267
57590
  // guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
57268
- personaRoot: import_node_path50.default.join(config.dataDir, "personas"),
57591
+ personaRoot: import_node_path53.default.join(config.dataDir, "personas"),
57269
57592
  usersRoot
57270
57593
  },
57271
57594
  // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
57272
- personaRoot: import_node_path50.default.join(config.dataDir, "personas"),
57595
+ personaRoot: import_node_path53.default.join(config.dataDir, "personas"),
57273
57596
  // v2 多人 persona 隔离:handler 派生 guest user-dir 放行
57274
57597
  usersRoot,
57275
57598
  // capability:list / delete handler 依赖
@@ -57376,11 +57699,11 @@ async function startDaemon(config) {
57376
57699
  // 发布上线脚手架化 (spec 2026-06-03 §5.2):
57377
57700
  // appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
57378
57701
  // dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
57379
- appBuilderPersonaRoot: import_node_path50.default.join(config.dataDir, "personas", "persona-app-builder"),
57702
+ appBuilderPersonaRoot: import_node_path53.default.join(config.dataDir, "personas", "persona-app-builder"),
57380
57703
  // 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
57381
- deployKitRoot: import_node_path50.default.join(config.dataDir, "deploy-kit"),
57704
+ deployKitRoot: import_node_path53.default.join(config.dataDir, "deploy-kit"),
57382
57705
  // scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
57383
- resolvePersonaRoot: (personaId) => import_node_path50.default.join(config.dataDir, "personas", personaId),
57706
+ resolvePersonaRoot: (personaId) => import_node_path53.default.join(config.dataDir, "personas", personaId),
57384
57707
  // 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
57385
57708
  // 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
57386
57709
  // 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
@@ -57403,7 +57726,9 @@ async function startDaemon(config) {
57403
57726
  // shift-internal:* MCP loopback handler)。owner 过滤用 ownerPrincipalId(跨
57404
57727
  // daemon 稳定身份),跟我们 ShiftStore boot 时用的 provider 一致。
57405
57728
  shiftStore,
57406
- shiftOwnerIdProvider: () => ownerPrincipalId
57729
+ shiftOwnerIdProvider: () => ownerPrincipalId,
57730
+ // persona web 分享:visitor:list owner RPC 读访客名单(同 exchange upsert 的实例)
57731
+ visitorStore
57407
57732
  });
57408
57733
  let handlers = makeHandlers();
57409
57734
  const dispatchHandlers = buildPersonaDispatchHandlers({
@@ -57421,7 +57746,7 @@ async function startDaemon(config) {
57421
57746
  }
57422
57747
  let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
57423
57748
  if (sourceFile && sourceFile.toolSessionId) {
57424
- sourceJsonlPath = import_node_path50.default.join(
57749
+ sourceJsonlPath = import_node_path53.default.join(
57425
57750
  import_node_os20.default.homedir(),
57426
57751
  ".claude",
57427
57752
  "projects",
@@ -57510,6 +57835,7 @@ async function startDaemon(config) {
57510
57835
  }
57511
57836
  };
57512
57837
  const viewerAssetLoader = tryLoadViewerAssets(logger);
57838
+ const shareUiAssetLoader = tryLoadShareUi(logger);
57513
57839
  const httpRouter = createHttpRouter({
57514
57840
  authResolver,
57515
57841
  daemonVersion: version,
@@ -57518,6 +57844,9 @@ async function startDaemon(config) {
57518
57844
  rpcDispatcher,
57519
57845
  authedRpc,
57520
57846
  ...viewerAssetLoader ? { viewerAssetLoader } : {},
57847
+ // persona web 分享:访客换 token(POST /share/exchange)+ guest SPA serve(GET /s)
57848
+ visitorExchange: exchangeVisitorToken,
57849
+ ...shareUiAssetLoader ? { shareUiAssetLoader } : {},
57521
57850
  // /files HMAC verify 用 auth.json 的 signSecret 字段(与 attachment.signUrl 同源)。
57522
57851
  // --auth-token CLI 模式没 signSecret → 路由返 501,sign URL 功能整体禁用。
57523
57852
  getSignSecret: () => authFile?.signSecret ?? null,
@@ -57592,11 +57921,15 @@ async function startDaemon(config) {
57592
57921
  // 命中 cap → attach guest ctx; 空 token / 未命中 → 保持 noAuth 行为 (handler fallback owner).
57593
57922
  tryVerifyCapabilityToken: (token) => {
57594
57923
  const v2 = capabilityRegistry.verifyToken(token);
57595
- if (!v2.ok) return null;
57596
- return {
57597
- principal: { id: v2.capability.id, kind: "guest", displayName: v2.capability.displayName },
57598
- grants: v2.capability.grants
57599
- };
57924
+ if (v2.ok) {
57925
+ return {
57926
+ principal: { id: v2.capability.id, kind: "guest", displayName: v2.capability.displayName },
57927
+ grants: v2.capability.grants
57928
+ };
57929
+ }
57930
+ const vis = verifyVisitorToken(authFile.visitorTokenSecret, token, Math.floor(Date.now() / 1e3));
57931
+ if (vis.ok) return visitorGuestContext(vis.payload);
57932
+ return null;
57600
57933
  },
57601
57934
  // file-sharing HTTP 路由复用 daemon 同端口(spec §5 第 3 条);router 自己处理 auth + 404
57602
57935
  httpRequestHandler: httpRouter,
@@ -57713,8 +58046,8 @@ async function startDaemon(config) {
57713
58046
  const lines = [
57714
58047
  `Tunnel: ${r.url}`,
57715
58048
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
57716
- `Frpc config: ${import_node_path50.default.join(config.dataDir, "frpc.toml")}`,
57717
- `Frpc log: ${import_node_path50.default.join(config.dataDir, "frpc.log")}`
58049
+ `Frpc config: ${import_node_path53.default.join(config.dataDir, "frpc.toml")}`,
58050
+ `Frpc log: ${import_node_path53.default.join(config.dataDir, "frpc.log")}`
57718
58051
  ];
57719
58052
  const width = Math.max(...lines.map((l) => l.length));
57720
58053
  const bar = "\u2550".repeat(width + 4);
@@ -57727,8 +58060,8 @@ ${bar}
57727
58060
 
57728
58061
  `);
57729
58062
  try {
57730
- const connectPath = import_node_path50.default.join(config.dataDir, "connect.txt");
57731
- import_node_fs38.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
58063
+ const connectPath = import_node_path53.default.join(config.dataDir, "connect.txt");
58064
+ import_node_fs41.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
57732
58065
  } catch {
57733
58066
  }
57734
58067
  } catch (err) {
@@ -57800,9 +58133,9 @@ ${bar}
57800
58133
  };
57801
58134
  }
57802
58135
  function migrateDropPersonsDir(dataDir) {
57803
- const dir = import_node_path50.default.join(dataDir, "persons");
58136
+ const dir = import_node_path53.default.join(dataDir, "persons");
57804
58137
  try {
57805
- import_node_fs38.default.rmSync(dir, { recursive: true, force: true });
58138
+ import_node_fs41.default.rmSync(dir, { recursive: true, force: true });
57806
58139
  } catch {
57807
58140
  }
57808
58141
  }