@clawos-dev/clawd 0.2.23-beta.34.136a698 → 0.2.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.cjs +306 -293
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -293,8 +293,8 @@ var require_req = __commonJS({
293
293
  if (req.originalUrl) {
294
294
  _req.url = req.originalUrl;
295
295
  } else {
296
- const path17 = req.path;
297
- _req.url = typeof path17 === "string" ? path17 : req.url ? req.url.path || req.url : void 0;
296
+ const path18 = req.path;
297
+ _req.url = typeof path18 === "string" ? path18 : req.url ? req.url.path || req.url : void 0;
298
298
  }
299
299
  if (req.query) {
300
300
  _req.query = req.query;
@@ -459,14 +459,14 @@ var require_redact = __commonJS({
459
459
  }
460
460
  return obj;
461
461
  }
462
- function parsePath(path17) {
462
+ function parsePath(path18) {
463
463
  const parts = [];
464
464
  let current = "";
465
465
  let inBrackets = false;
466
466
  let inQuotes = false;
467
467
  let quoteChar = "";
468
- for (let i = 0; i < path17.length; i++) {
469
- const char = path17[i];
468
+ for (let i = 0; i < path18.length; i++) {
469
+ const char = path18[i];
470
470
  if (!inBrackets && char === ".") {
471
471
  if (current) {
472
472
  parts.push(current);
@@ -597,10 +597,10 @@ var require_redact = __commonJS({
597
597
  return current;
598
598
  }
599
599
  function redactPaths(obj, paths, censor, remove = false) {
600
- for (const path17 of paths) {
601
- const parts = parsePath(path17);
600
+ for (const path18 of paths) {
601
+ const parts = parsePath(path18);
602
602
  if (parts.includes("*")) {
603
- redactWildcardPath(obj, parts, censor, path17, remove);
603
+ redactWildcardPath(obj, parts, censor, path18, remove);
604
604
  } else {
605
605
  if (remove) {
606
606
  removeKey(obj, parts);
@@ -685,8 +685,8 @@ var require_redact = __commonJS({
685
685
  }
686
686
  } else {
687
687
  if (afterWildcard.includes("*")) {
688
- const wrappedCensor = typeof censor === "function" ? (value, path17) => {
689
- const fullPath = [...pathArray.slice(0, pathLength), ...path17];
688
+ const wrappedCensor = typeof censor === "function" ? (value, path18) => {
689
+ const fullPath = [...pathArray.slice(0, pathLength), ...path18];
690
690
  return censor(value, fullPath);
691
691
  } : censor;
692
692
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -721,8 +721,8 @@ var require_redact = __commonJS({
721
721
  return null;
722
722
  }
723
723
  const pathStructure = /* @__PURE__ */ new Map();
724
- for (const path17 of pathsToClone) {
725
- const parts = parsePath(path17);
724
+ for (const path18 of pathsToClone) {
725
+ const parts = parsePath(path18);
726
726
  let current = pathStructure;
727
727
  for (let i = 0; i < parts.length; i++) {
728
728
  const part = parts[i];
@@ -774,24 +774,24 @@ var require_redact = __commonJS({
774
774
  }
775
775
  return cloneSelectively(obj, pathStructure);
776
776
  }
777
- function validatePath(path17) {
778
- if (typeof path17 !== "string") {
777
+ function validatePath(path18) {
778
+ if (typeof path18 !== "string") {
779
779
  throw new Error("Paths must be (non-empty) strings");
780
780
  }
781
- if (path17 === "") {
781
+ if (path18 === "") {
782
782
  throw new Error("Invalid redaction path ()");
783
783
  }
784
- if (path17.includes("..")) {
785
- throw new Error(`Invalid redaction path (${path17})`);
784
+ if (path18.includes("..")) {
785
+ throw new Error(`Invalid redaction path (${path18})`);
786
786
  }
787
- if (path17.includes(",")) {
788
- throw new Error(`Invalid redaction path (${path17})`);
787
+ if (path18.includes(",")) {
788
+ throw new Error(`Invalid redaction path (${path18})`);
789
789
  }
790
790
  let bracketCount = 0;
791
791
  let inQuotes = false;
792
792
  let quoteChar = "";
793
- for (let i = 0; i < path17.length; i++) {
794
- const char = path17[i];
793
+ for (let i = 0; i < path18.length; i++) {
794
+ const char = path18[i];
795
795
  if ((char === '"' || char === "'") && bracketCount > 0) {
796
796
  if (!inQuotes) {
797
797
  inQuotes = true;
@@ -805,20 +805,20 @@ var require_redact = __commonJS({
805
805
  } else if (char === "]" && !inQuotes) {
806
806
  bracketCount--;
807
807
  if (bracketCount < 0) {
808
- throw new Error(`Invalid redaction path (${path17})`);
808
+ throw new Error(`Invalid redaction path (${path18})`);
809
809
  }
810
810
  }
811
811
  }
812
812
  if (bracketCount !== 0) {
813
- throw new Error(`Invalid redaction path (${path17})`);
813
+ throw new Error(`Invalid redaction path (${path18})`);
814
814
  }
815
815
  }
816
816
  function validatePaths(paths) {
817
817
  if (!Array.isArray(paths)) {
818
818
  throw new TypeError("paths must be an array");
819
819
  }
820
- for (const path17 of paths) {
821
- validatePath(path17);
820
+ for (const path18 of paths) {
821
+ validatePath(path18);
822
822
  }
823
823
  }
824
824
  function slowRedact(options = {}) {
@@ -986,8 +986,8 @@ var require_redaction = __commonJS({
986
986
  if (shape[k] === null) {
987
987
  o[k] = (value) => topCensor(value, [k]);
988
988
  } else {
989
- const wrappedCensor = typeof censor === "function" ? (value, path17) => {
990
- return censor(value, [k, ...path17]);
989
+ const wrappedCensor = typeof censor === "function" ? (value, path18) => {
990
+ return censor(value, [k, ...path18]);
991
991
  } : censor;
992
992
  o[k] = Redact({
993
993
  paths: shape[k],
@@ -1205,10 +1205,10 @@ var require_atomic_sleep = __commonJS({
1205
1205
  var require_sonic_boom = __commonJS({
1206
1206
  "../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
1207
1207
  "use strict";
1208
- var fs18 = require("fs");
1208
+ var fs19 = require("fs");
1209
1209
  var EventEmitter = require("events");
1210
1210
  var inherits = require("util").inherits;
1211
- var path17 = require("path");
1211
+ var path18 = require("path");
1212
1212
  var sleep = require_atomic_sleep();
1213
1213
  var assert = require("assert");
1214
1214
  var BUSY_WRITE_TIMEOUT = 100;
@@ -1262,20 +1262,20 @@ var require_sonic_boom = __commonJS({
1262
1262
  const mode = sonic.mode;
1263
1263
  if (sonic.sync) {
1264
1264
  try {
1265
- if (sonic.mkdir) fs18.mkdirSync(path17.dirname(file), { recursive: true });
1266
- const fd = fs18.openSync(file, flags, mode);
1265
+ if (sonic.mkdir) fs19.mkdirSync(path18.dirname(file), { recursive: true });
1266
+ const fd = fs19.openSync(file, flags, mode);
1267
1267
  fileOpened(null, fd);
1268
1268
  } catch (err) {
1269
1269
  fileOpened(err);
1270
1270
  throw err;
1271
1271
  }
1272
1272
  } else if (sonic.mkdir) {
1273
- fs18.mkdir(path17.dirname(file), { recursive: true }, (err) => {
1273
+ fs19.mkdir(path18.dirname(file), { recursive: true }, (err) => {
1274
1274
  if (err) return fileOpened(err);
1275
- fs18.open(file, flags, mode, fileOpened);
1275
+ fs19.open(file, flags, mode, fileOpened);
1276
1276
  });
1277
1277
  } else {
1278
- fs18.open(file, flags, mode, fileOpened);
1278
+ fs19.open(file, flags, mode, fileOpened);
1279
1279
  }
1280
1280
  }
1281
1281
  function SonicBoom(opts) {
@@ -1316,8 +1316,8 @@ var require_sonic_boom = __commonJS({
1316
1316
  this.flush = flushBuffer;
1317
1317
  this.flushSync = flushBufferSync;
1318
1318
  this._actualWrite = actualWriteBuffer;
1319
- fsWriteSync = () => fs18.writeSync(this.fd, this._writingBuf);
1320
- fsWrite = () => fs18.write(this.fd, this._writingBuf, this.release);
1319
+ fsWriteSync = () => fs19.writeSync(this.fd, this._writingBuf);
1320
+ fsWrite = () => fs19.write(this.fd, this._writingBuf, this.release);
1321
1321
  } else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
1322
1322
  this._writingBuf = "";
1323
1323
  this.write = write;
@@ -1326,15 +1326,15 @@ var require_sonic_boom = __commonJS({
1326
1326
  this._actualWrite = actualWrite;
1327
1327
  fsWriteSync = () => {
1328
1328
  if (Buffer.isBuffer(this._writingBuf)) {
1329
- return fs18.writeSync(this.fd, this._writingBuf);
1329
+ return fs19.writeSync(this.fd, this._writingBuf);
1330
1330
  }
1331
- return fs18.writeSync(this.fd, this._writingBuf, "utf8");
1331
+ return fs19.writeSync(this.fd, this._writingBuf, "utf8");
1332
1332
  };
1333
1333
  fsWrite = () => {
1334
1334
  if (Buffer.isBuffer(this._writingBuf)) {
1335
- return fs18.write(this.fd, this._writingBuf, this.release);
1335
+ return fs19.write(this.fd, this._writingBuf, this.release);
1336
1336
  }
1337
- return fs18.write(this.fd, this._writingBuf, "utf8", this.release);
1337
+ return fs19.write(this.fd, this._writingBuf, "utf8", this.release);
1338
1338
  };
1339
1339
  } else {
1340
1340
  throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
@@ -1391,7 +1391,7 @@ var require_sonic_boom = __commonJS({
1391
1391
  }
1392
1392
  }
1393
1393
  if (this._fsync) {
1394
- fs18.fsyncSync(this.fd);
1394
+ fs19.fsyncSync(this.fd);
1395
1395
  }
1396
1396
  const len = this._len;
1397
1397
  if (this._reopening) {
@@ -1505,7 +1505,7 @@ var require_sonic_boom = __commonJS({
1505
1505
  const onDrain = () => {
1506
1506
  if (!this._fsync) {
1507
1507
  try {
1508
- fs18.fsync(this.fd, (err) => {
1508
+ fs19.fsync(this.fd, (err) => {
1509
1509
  this._flushPending = false;
1510
1510
  cb(err);
1511
1511
  });
@@ -1607,7 +1607,7 @@ var require_sonic_boom = __commonJS({
1607
1607
  const fd = this.fd;
1608
1608
  this.once("ready", () => {
1609
1609
  if (fd !== this.fd) {
1610
- fs18.close(fd, (err) => {
1610
+ fs19.close(fd, (err) => {
1611
1611
  if (err) {
1612
1612
  return this.emit("error", err);
1613
1613
  }
@@ -1656,7 +1656,7 @@ var require_sonic_boom = __commonJS({
1656
1656
  buf = this._bufs[0];
1657
1657
  }
1658
1658
  try {
1659
- const n = Buffer.isBuffer(buf) ? fs18.writeSync(this.fd, buf) : fs18.writeSync(this.fd, buf, "utf8");
1659
+ const n = Buffer.isBuffer(buf) ? fs19.writeSync(this.fd, buf) : fs19.writeSync(this.fd, buf, "utf8");
1660
1660
  const releasedBufObj = releaseWritingBuf(buf, this._len, n);
1661
1661
  buf = releasedBufObj.writingBuf;
1662
1662
  this._len = releasedBufObj.len;
@@ -1672,7 +1672,7 @@ var require_sonic_boom = __commonJS({
1672
1672
  }
1673
1673
  }
1674
1674
  try {
1675
- fs18.fsyncSync(this.fd);
1675
+ fs19.fsyncSync(this.fd);
1676
1676
  } catch {
1677
1677
  }
1678
1678
  }
@@ -1693,7 +1693,7 @@ var require_sonic_boom = __commonJS({
1693
1693
  buf = mergeBuf(this._bufs[0], this._lens[0]);
1694
1694
  }
1695
1695
  try {
1696
- const n = fs18.writeSync(this.fd, buf);
1696
+ const n = fs19.writeSync(this.fd, buf);
1697
1697
  buf = buf.subarray(n);
1698
1698
  this._len = Math.max(this._len - n, 0);
1699
1699
  if (buf.length <= 0) {
@@ -1721,13 +1721,13 @@ var require_sonic_boom = __commonJS({
1721
1721
  this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
1722
1722
  if (this.sync) {
1723
1723
  try {
1724
- const written = Buffer.isBuffer(this._writingBuf) ? fs18.writeSync(this.fd, this._writingBuf) : fs18.writeSync(this.fd, this._writingBuf, "utf8");
1724
+ const written = Buffer.isBuffer(this._writingBuf) ? fs19.writeSync(this.fd, this._writingBuf) : fs19.writeSync(this.fd, this._writingBuf, "utf8");
1725
1725
  release(null, written);
1726
1726
  } catch (err) {
1727
1727
  release(err);
1728
1728
  }
1729
1729
  } else {
1730
- fs18.write(this.fd, this._writingBuf, release);
1730
+ fs19.write(this.fd, this._writingBuf, release);
1731
1731
  }
1732
1732
  }
1733
1733
  function actualWriteBuffer() {
@@ -1736,7 +1736,7 @@ var require_sonic_boom = __commonJS({
1736
1736
  this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
1737
1737
  if (this.sync) {
1738
1738
  try {
1739
- const written = fs18.writeSync(this.fd, this._writingBuf);
1739
+ const written = fs19.writeSync(this.fd, this._writingBuf);
1740
1740
  release(null, written);
1741
1741
  } catch (err) {
1742
1742
  release(err);
@@ -1745,7 +1745,7 @@ var require_sonic_boom = __commonJS({
1745
1745
  if (kCopyBuffer) {
1746
1746
  this._writingBuf = Buffer.from(this._writingBuf);
1747
1747
  }
1748
- fs18.write(this.fd, this._writingBuf, release);
1748
+ fs19.write(this.fd, this._writingBuf, release);
1749
1749
  }
1750
1750
  }
1751
1751
  function actualClose(sonic) {
@@ -1761,12 +1761,12 @@ var require_sonic_boom = __commonJS({
1761
1761
  sonic._lens = [];
1762
1762
  assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
1763
1763
  try {
1764
- fs18.fsync(sonic.fd, closeWrapped);
1764
+ fs19.fsync(sonic.fd, closeWrapped);
1765
1765
  } catch {
1766
1766
  }
1767
1767
  function closeWrapped() {
1768
1768
  if (sonic.fd !== 1 && sonic.fd !== 2) {
1769
- fs18.close(sonic.fd, done);
1769
+ fs19.close(sonic.fd, done);
1770
1770
  } else {
1771
1771
  done();
1772
1772
  }
@@ -8075,8 +8075,8 @@ Env (advanced):
8075
8075
  `;
8076
8076
 
8077
8077
  // src/index.ts
8078
- var import_node_path16 = __toESM(require("path"), 1);
8079
- var import_node_fs17 = __toESM(require("fs"), 1);
8078
+ var import_node_path17 = __toESM(require("path"), 1);
8079
+ var import_node_fs18 = __toESM(require("fs"), 1);
8080
8080
 
8081
8081
  // src/logger.ts
8082
8082
  var import_node_fs2 = __toESM(require("fs"), 1);
@@ -8144,7 +8144,6 @@ var METHOD_NAMES = [
8144
8144
  "session:subscribe",
8145
8145
  "session:unsubscribe",
8146
8146
  "session:pin",
8147
- "session:reorderPins",
8148
8147
  "permission:respond",
8149
8148
  // AskUserQuestion 表单回写:UI 答完所有 question 后调用,daemon 把答案合并进 updated_input
8150
8149
  // 写一条 control_response 到 CC stdin(详见 events.ts session:question JSDoc)
@@ -8687,8 +8686,8 @@ function getErrorMap() {
8687
8686
 
8688
8687
  // ../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
8689
8688
  var makeIssue = (params) => {
8690
- const { data, path: path17, errorMaps, issueData } = params;
8691
- const fullPath = [...path17, ...issueData.path || []];
8689
+ const { data, path: path18, errorMaps, issueData } = params;
8690
+ const fullPath = [...path18, ...issueData.path || []];
8692
8691
  const fullIssue = {
8693
8692
  ...issueData,
8694
8693
  path: fullPath
@@ -8804,11 +8803,11 @@ var errorUtil;
8804
8803
 
8805
8804
  // ../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
8806
8805
  var ParseInputLazyPath = class {
8807
- constructor(parent, value, path17, key) {
8806
+ constructor(parent, value, path18, key) {
8808
8807
  this._cachedPath = [];
8809
8808
  this.parent = parent;
8810
8809
  this.data = value;
8811
- this._path = path17;
8810
+ this._path = path18;
8812
8811
  this._key = key;
8813
8812
  }
8814
8813
  get path() {
@@ -12327,9 +12326,6 @@ var SessionFileSchema = external_exports.object({
12327
12326
  permissionRules: external_exports.array(AllowRuleSchema).optional(),
12328
12327
  // Sidebar 置顶时间戳;null/undefined 表示未置顶
12329
12328
  pinnedAt: external_exports.number().int().nonnegative().nullable().optional(),
12330
- // pinned 行手动拖拽顺序;越小越靠前。session:reorderPins 整体重写所有 pinned 的此字段。
12331
- // 缺失时 view-state 排序回退到 pinnedAt desc(旧数据兼容)
12332
- pinSortOrder: external_exports.number().int().nullable().optional(),
12333
12329
  // 用户在 NewSessionDialog 或 Edit modal 选择的 icon 标识;UI 端做 enum 兜底
12334
12330
  iconKey: external_exports.string().optional(),
12335
12331
  // NewSessionDialog 勾选 worktree 创建的 session 才会持久化这两个字段
@@ -12661,9 +12657,6 @@ var SessionPinArgs = external_exports.object({
12661
12657
  sessionId: external_exports.string().min(1),
12662
12658
  pinned: external_exports.boolean()
12663
12659
  });
12664
- var SessionReorderPinsArgs = external_exports.object({
12665
- orderedIds: external_exports.array(external_exports.string().min(1)).min(1)
12666
- });
12667
12660
  var GitRootArgs = external_exports.object({ cwd: external_exports.string().min(1) });
12668
12661
  var GitRootResponseSchema = external_exports.object({
12669
12662
  isGitRoot: external_exports.boolean(),
@@ -12819,7 +12812,7 @@ var SessionStore = class {
12819
12812
  };
12820
12813
 
12821
12814
  // src/session/manager.ts
12822
- var import_node_fs4 = __toESM(require("fs"), 1);
12815
+ var import_node_fs5 = __toESM(require("fs"), 1);
12823
12816
 
12824
12817
  // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
12825
12818
  var byteToHex = [];
@@ -13408,6 +13401,58 @@ function reduceSession(state, input, deps) {
13408
13401
  }
13409
13402
  }
13410
13403
 
13404
+ // src/ipc-recorder.ts
13405
+ var import_node_fs4 = __toESM(require("fs"), 1);
13406
+ var import_node_path4 = __toESM(require("path"), 1);
13407
+ function tsForFilename(ms) {
13408
+ return new Date(ms).toISOString().replace(/[:.]/g, "-");
13409
+ }
13410
+ function startRecorder(opts) {
13411
+ if (opts.env.CLAWD_RECORD_IPC !== "1") return null;
13412
+ if (!opts.dataDir) return null;
13413
+ if (!opts.sessionId || opts.sessionId.includes("..") || opts.sessionId.includes("/") || opts.sessionId.includes("\\") || opts.sessionId.startsWith(".")) {
13414
+ return null;
13415
+ }
13416
+ const now = opts.now ?? Date.now;
13417
+ const dir = import_node_path4.default.join(opts.dataDir, "ipc-recordings", opts.sessionId);
13418
+ const filePath = import_node_path4.default.join(dir, `${tsForFilename(now())}.jsonl`);
13419
+ let stream = null;
13420
+ let closedResolve;
13421
+ const closed = new Promise((resolve) => {
13422
+ closedResolve = resolve;
13423
+ });
13424
+ let exited = false;
13425
+ const ensureStream = () => {
13426
+ if (stream) return stream;
13427
+ import_node_fs4.default.mkdirSync(dir, { recursive: true });
13428
+ stream = import_node_fs4.default.createWriteStream(filePath, { flags: "a" });
13429
+ stream.on("close", () => closedResolve());
13430
+ return stream;
13431
+ };
13432
+ const writeLine = (s, chunk) => {
13433
+ if (exited) return;
13434
+ const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
13435
+ const line = JSON.stringify({ ts: now(), stream: s, chunk: text }) + "\n";
13436
+ ensureStream().write(line);
13437
+ };
13438
+ opts.proc.stdout?.on("data", (chunk) => writeLine("stdout", chunk));
13439
+ opts.proc.stderr?.on("data", (chunk) => writeLine("stderr", chunk));
13440
+ opts.proc.on("exit", () => {
13441
+ exited = true;
13442
+ if (stream) {
13443
+ stream.end();
13444
+ } else {
13445
+ closedResolve();
13446
+ }
13447
+ });
13448
+ return {
13449
+ tapStdinWrite(chunk) {
13450
+ writeLine("stdin", chunk);
13451
+ },
13452
+ closed
13453
+ };
13454
+ }
13455
+
13411
13456
  // src/session/runner.ts
13412
13457
  var DEFAULT_CONTROL_REQUEST_TIMEOUT_MS = 1e4;
13413
13458
  function encodeAllowWithInputControlResponse(requestId, updatedInput) {
@@ -13438,6 +13483,8 @@ var SessionRunner = class {
13438
13483
  pendingControlRequests = /* @__PURE__ */ new Map();
13439
13484
  // waitUntilStopped 排队的 resolve 回调;reducer 把 procAlive 翻成 false 后批量触发
13440
13485
  stopWaiters = [];
13486
+ // IPC recorder(CLAWD_RECORD_IPC=1 时启用);null 表示当前 spawn 未启用 / 已退出
13487
+ recorder = null;
13441
13488
  getState() {
13442
13489
  return this.state;
13443
13490
  }
@@ -13527,6 +13574,7 @@ var SessionRunner = class {
13527
13574
  this.pendingControlRequests.set(requestId, { resolve, reject, timer });
13528
13575
  try {
13529
13576
  proc.stdin?.write(payload);
13577
+ this.recorder?.tapStdinWrite(payload);
13530
13578
  } catch (err) {
13531
13579
  clearTimeout(timer);
13532
13580
  this.pendingControlRequests.delete(requestId);
@@ -13582,10 +13630,14 @@ var SessionRunner = class {
13582
13630
  break;
13583
13631
  case "write-stdin":
13584
13632
  this.proc?.stdin?.write(effect.payload);
13633
+ this.recorder?.tapStdinWrite(effect.payload);
13585
13634
  break;
13586
- case "send-control-response-allow-with-input":
13587
- this.proc?.stdin?.write(encodeAllowWithInputControlResponse(effect.requestId, effect.updatedInput));
13635
+ case "send-control-response-allow-with-input": {
13636
+ const payload = encodeAllowWithInputControlResponse(effect.requestId, effect.updatedInput);
13637
+ this.proc?.stdin?.write(payload);
13638
+ this.recorder?.tapStdinWrite(payload);
13588
13639
  break;
13640
+ }
13589
13641
  case "persist-file":
13590
13642
  try {
13591
13643
  this.hooks.store.write(effect.file);
@@ -13615,6 +13667,13 @@ var SessionRunner = class {
13615
13667
  const proc = this.hooks.spawnOverride ? this.hooks.spawnOverride(ctx) : this.hooks.adapter.spawn(ctx);
13616
13668
  this.proc = proc;
13617
13669
  this.stdoutBuf = "";
13670
+ this.recorder = startRecorder({
13671
+ proc,
13672
+ sessionId: this.state.file.sessionId,
13673
+ dataDir: this.hooks.dataDir,
13674
+ env: this.hooks.recordEnv ?? process.env,
13675
+ now: this.hooks.now
13676
+ });
13618
13677
  proc.stdout?.on("data", (chunk) => {
13619
13678
  this.stdoutBuf += typeof chunk === "string" ? chunk : chunk.toString("utf8");
13620
13679
  let idx;
@@ -13631,6 +13690,7 @@ var SessionRunner = class {
13631
13690
  });
13632
13691
  proc.on("exit", (code) => {
13633
13692
  this.proc = null;
13693
+ this.recorder = null;
13634
13694
  this.rejectAllPending(new Error("session gone"));
13635
13695
  this.input({ kind: "proc-exit", code });
13636
13696
  });
@@ -13757,7 +13817,8 @@ var SessionManager = class {
13757
13817
  now: this.deps.now,
13758
13818
  bufferCap: this.deps.bufferCap,
13759
13819
  // adapter 自己持有模型表 + 兜底逻辑(contains / opus-1M / [1m] 等),manager 不再 cache 转发
13760
- resolveContextWindow: (tool, modelId) => this.deps.getAdapter(tool).resolveContextWindow(modelId)
13820
+ resolveContextWindow: (tool, modelId) => this.deps.getAdapter(tool).resolveContextWindow(modelId),
13821
+ dataDir: this.deps.dataDir
13761
13822
  });
13762
13823
  return runner;
13763
13824
  }
@@ -13788,7 +13849,7 @@ var SessionManager = class {
13788
13849
  // ---- 命令方法:均返回 { response, broadcast[] },由 dispatcher 聚合 ----
13789
13850
  create(args) {
13790
13851
  try {
13791
- const stat = import_node_fs4.default.statSync(args.cwd);
13852
+ const stat = import_node_fs5.default.statSync(args.cwd);
13792
13853
  if (!stat.isDirectory()) throw new Error("not dir");
13793
13854
  } catch {
13794
13855
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `cwd not a directory: ${args.cwd}`);
@@ -13829,49 +13890,6 @@ var SessionManager = class {
13829
13890
  this.deps.store.write(updated);
13830
13891
  return { response: updated, broadcast: [] };
13831
13892
  }
13832
- // sidebar 拖拽完成 → 整体重写 pinned root 的 pinSortOrder(按 orderedIds 下标 0,1,2...)
13833
- // orderedIds 必须正好覆盖所有当前 pinned root(既不能漏,也不能含 unpinned id)
13834
- reorderPins(args) {
13835
- const all = this.deps.store.list();
13836
- const currentPinnedIds = new Set(
13837
- all.filter((f) => typeof f.pinnedAt === "number").map((f) => f.sessionId)
13838
- );
13839
- const incoming = new Set(args.orderedIds);
13840
- if (incoming.size !== args.orderedIds.length) {
13841
- throw new Error("reorderPins: orderedIds contains duplicate ids");
13842
- }
13843
- if (currentPinnedIds.size !== incoming.size || [...currentPinnedIds].some((id) => !incoming.has(id))) {
13844
- throw new Error(
13845
- "reorderPins: orderedIds must cover exactly the current pinned root set"
13846
- );
13847
- }
13848
- const broadcast = [];
13849
- const updatedFiles = [];
13850
- args.orderedIds.forEach((sessionId, index) => {
13851
- const runner = this.runners.get(sessionId);
13852
- if (runner) {
13853
- const { value, broadcast: b } = this.withCollector(() => {
13854
- runner.input({
13855
- kind: "command",
13856
- command: { kind: "update", patch: { pinSortOrder: index } }
13857
- });
13858
- return runner.getState().file;
13859
- });
13860
- updatedFiles.push(value);
13861
- broadcast.push(...b);
13862
- } else {
13863
- const existing = this.getFile(sessionId);
13864
- const updated = {
13865
- ...existing,
13866
- pinSortOrder: index,
13867
- updatedAt: nowIso2(this.deps)
13868
- };
13869
- this.deps.store.write(updated);
13870
- updatedFiles.push(updated);
13871
- }
13872
- });
13873
- return { response: { sessions: updatedFiles }, broadcast };
13874
- }
13875
13893
  list() {
13876
13894
  return { response: { sessions: this.deps.store.list() }, broadcast: [] };
13877
13895
  }
@@ -14277,14 +14295,14 @@ var SessionManager = class {
14277
14295
  // src/tools/claude.ts
14278
14296
  var import_node_child_process = require("child_process");
14279
14297
  var import_node_child_process2 = require("child_process");
14280
- var import_node_fs6 = __toESM(require("fs"), 1);
14298
+ var import_node_fs7 = __toESM(require("fs"), 1);
14281
14299
  var import_node_os3 = __toESM(require("os"), 1);
14282
- var import_node_path5 = __toESM(require("path"), 1);
14300
+ var import_node_path6 = __toESM(require("path"), 1);
14283
14301
 
14284
14302
  // src/tools/claude-history.ts
14285
- var import_node_fs5 = __toESM(require("fs"), 1);
14303
+ var import_node_fs6 = __toESM(require("fs"), 1);
14286
14304
  var import_node_os2 = __toESM(require("os"), 1);
14287
- var import_node_path4 = __toESM(require("path"), 1);
14305
+ var import_node_path5 = __toESM(require("path"), 1);
14288
14306
 
14289
14307
  // ../node_modules/.pnpm/diff@7.0.0/node_modules/diff/lib/index.mjs
14290
14308
  function Diff() {
@@ -14388,11 +14406,11 @@ Diff.prototype = {
14388
14406
  }
14389
14407
  }
14390
14408
  },
14391
- addToPath: function addToPath(path17, added, removed, oldPosInc, options) {
14392
- var last = path17.lastComponent;
14409
+ addToPath: function addToPath(path18, added, removed, oldPosInc, options) {
14410
+ var last = path18.lastComponent;
14393
14411
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
14394
14412
  return {
14395
- oldPos: path17.oldPos + oldPosInc,
14413
+ oldPos: path18.oldPos + oldPosInc,
14396
14414
  lastComponent: {
14397
14415
  count: last.count + 1,
14398
14416
  added,
@@ -14402,7 +14420,7 @@ Diff.prototype = {
14402
14420
  };
14403
14421
  } else {
14404
14422
  return {
14405
- oldPos: path17.oldPos + oldPosInc,
14423
+ oldPos: path18.oldPos + oldPosInc,
14406
14424
  lastComponent: {
14407
14425
  count: 1,
14408
14426
  added,
@@ -15061,7 +15079,7 @@ function hashDirToCwd(hash) {
15061
15079
  }
15062
15080
  function safeStatMtime(p) {
15063
15081
  try {
15064
- return import_node_fs5.default.statSync(p).mtimeMs;
15082
+ return import_node_fs6.default.statSync(p).mtimeMs;
15065
15083
  } catch {
15066
15084
  return 0;
15067
15085
  }
@@ -15069,7 +15087,7 @@ function safeStatMtime(p) {
15069
15087
  function readJsonlLines(file) {
15070
15088
  let raw;
15071
15089
  try {
15072
- raw = import_node_fs5.default.readFileSync(file, "utf8");
15090
+ raw = import_node_fs6.default.readFileSync(file, "utf8");
15073
15091
  } catch (err) {
15074
15092
  if (err.code === "ENOENT") return [];
15075
15093
  throw err;
@@ -15261,10 +15279,10 @@ function attachmentToHistoryMessage(o, ts) {
15261
15279
  const memories = raw.map((m) => {
15262
15280
  if (!m || typeof m !== "object") return null;
15263
15281
  const rec = m;
15264
- const path17 = typeof rec.path === "string" ? rec.path : null;
15282
+ const path18 = typeof rec.path === "string" ? rec.path : null;
15265
15283
  const content = typeof rec.content === "string" ? rec.content : null;
15266
- if (!path17 || content == null) return null;
15267
- const entry = { path: path17, content };
15284
+ if (!path18 || content == null) return null;
15285
+ const entry = { path: path18, content };
15268
15286
  if (typeof rec.mtimeMs === "number") entry.mtimeMs = rec.mtimeMs;
15269
15287
  return entry;
15270
15288
  }).filter((m) => m !== null);
@@ -15300,8 +15318,8 @@ function attachmentDeferredToolsText(a) {
15300
15318
  function readBackupContent(fileHistoryRoot, toolSessionId, backupFileName) {
15301
15319
  if (backupFileName === null) return null;
15302
15320
  try {
15303
- return import_node_fs5.default.readFileSync(
15304
- import_node_path4.default.join(fileHistoryRoot, toolSessionId, backupFileName),
15321
+ return import_node_fs6.default.readFileSync(
15322
+ import_node_path5.default.join(fileHistoryRoot, toolSessionId, backupFileName),
15305
15323
  "utf8"
15306
15324
  );
15307
15325
  } catch {
@@ -15310,7 +15328,7 @@ function readBackupContent(fileHistoryRoot, toolSessionId, backupFileName) {
15310
15328
  }
15311
15329
  function readCurrentContent(filePath) {
15312
15330
  try {
15313
- return import_node_fs5.default.readFileSync(filePath, "utf8");
15331
+ return import_node_fs6.default.readFileSync(filePath, "utf8");
15314
15332
  } catch (err) {
15315
15333
  if (err.code === "ENOENT") return null;
15316
15334
  return null;
@@ -15322,14 +15340,14 @@ var ClaudeHistoryReader = class {
15322
15340
  // 每次 user 提交前 trackEdit 拷一份,作为 rewind 回退目标
15323
15341
  fileHistoryRoot;
15324
15342
  constructor(opts = {}) {
15325
- const base = opts.baseDir ?? import_node_path4.default.join(import_node_os2.default.homedir(), ".claude");
15326
- this.projectsRoot = import_node_path4.default.join(base, "projects");
15327
- this.fileHistoryRoot = import_node_path4.default.join(base, "file-history");
15343
+ const base = opts.baseDir ?? import_node_path5.default.join(import_node_os2.default.homedir(), ".claude");
15344
+ this.projectsRoot = import_node_path5.default.join(base, "projects");
15345
+ this.fileHistoryRoot = import_node_path5.default.join(base, "file-history");
15328
15346
  }
15329
15347
  async listProjects() {
15330
15348
  let entries;
15331
15349
  try {
15332
- entries = import_node_fs5.default.readdirSync(this.projectsRoot, { withFileTypes: true });
15350
+ entries = import_node_fs6.default.readdirSync(this.projectsRoot, { withFileTypes: true });
15333
15351
  } catch (err) {
15334
15352
  if (err.code === "ENOENT") return [];
15335
15353
  throw err;
@@ -15337,9 +15355,9 @@ var ClaudeHistoryReader = class {
15337
15355
  const out = [];
15338
15356
  for (const ent of entries) {
15339
15357
  if (!ent.isDirectory()) continue;
15340
- const dir = import_node_path4.default.join(this.projectsRoot, ent.name);
15341
- const files = import_node_fs5.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
15342
- const updatedAtMs = files.reduce((m, f) => Math.max(m, safeStatMtime(import_node_path4.default.join(dir, f))), 0);
15358
+ const dir = import_node_path5.default.join(this.projectsRoot, ent.name);
15359
+ const files = import_node_fs6.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
15360
+ const updatedAtMs = files.reduce((m, f) => Math.max(m, safeStatMtime(import_node_path5.default.join(dir, f))), 0);
15343
15361
  out.push({
15344
15362
  projectPath: hashDirToCwd(ent.name),
15345
15363
  hashDir: ent.name,
@@ -15351,17 +15369,17 @@ var ClaudeHistoryReader = class {
15351
15369
  return out;
15352
15370
  }
15353
15371
  async listSessions(args) {
15354
- const dir = import_node_path4.default.join(this.projectsRoot, cwdToHashDir(args.projectPath));
15372
+ const dir = import_node_path5.default.join(this.projectsRoot, cwdToHashDir(args.projectPath));
15355
15373
  let files;
15356
15374
  try {
15357
- files = import_node_fs5.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
15375
+ files = import_node_fs6.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
15358
15376
  } catch (err) {
15359
15377
  if (err.code === "ENOENT") return [];
15360
15378
  throw err;
15361
15379
  }
15362
15380
  const out = [];
15363
15381
  for (const f of files) {
15364
- const full = import_node_path4.default.join(dir, f);
15382
+ const full = import_node_path5.default.join(dir, f);
15365
15383
  const toolSessionId = f.slice(0, -".jsonl".length);
15366
15384
  const lines = readJsonlLines(full);
15367
15385
  let summary = "";
@@ -15416,7 +15434,7 @@ var ClaudeHistoryReader = class {
15416
15434
  return out;
15417
15435
  }
15418
15436
  async read(args) {
15419
- const file = import_node_path4.default.join(
15437
+ const file = import_node_path5.default.join(
15420
15438
  this.projectsRoot,
15421
15439
  cwdToHashDir(args.cwd),
15422
15440
  `${args.toolSessionId}.jsonl`
@@ -15449,7 +15467,7 @@ var ClaudeHistoryReader = class {
15449
15467
  // 独立目录路径:<projectsRoot>/<cwdHash>/<toolSessionId>/subagents/*.jsonl
15450
15468
  // 返回 null 表示目录不存在(调用方回退旧实现);返回空数组表示目录存在但无 jsonl
15451
15469
  listSubagentsFromDirectory(cwd, toolSessionId) {
15452
- const dir = import_node_path4.default.join(
15470
+ const dir = import_node_path5.default.join(
15453
15471
  this.projectsRoot,
15454
15472
  cwdToHashDir(cwd),
15455
15473
  toolSessionId,
@@ -15457,7 +15475,7 @@ var ClaudeHistoryReader = class {
15457
15475
  );
15458
15476
  let entries;
15459
15477
  try {
15460
- entries = import_node_fs5.default.readdirSync(dir, { withFileTypes: true });
15478
+ entries = import_node_fs6.default.readdirSync(dir, { withFileTypes: true });
15461
15479
  } catch (err) {
15462
15480
  if (err.code === "ENOENT") return null;
15463
15481
  return null;
@@ -15467,7 +15485,7 @@ var ClaudeHistoryReader = class {
15467
15485
  if (!e.isFile()) continue;
15468
15486
  if (!e.name.startsWith("agent-") || !e.name.endsWith(".jsonl")) continue;
15469
15487
  const subagentId = e.name.slice("agent-".length, -".jsonl".length);
15470
- const filePath = import_node_path4.default.join(dir, e.name);
15488
+ const filePath = import_node_path5.default.join(dir, e.name);
15471
15489
  const lines = readJsonlLines(filePath);
15472
15490
  let firstText = "";
15473
15491
  let messageCount = 0;
@@ -15484,7 +15502,7 @@ var ClaudeHistoryReader = class {
15484
15502
  return out;
15485
15503
  }
15486
15504
  listSubagentsFromMainJsonl(cwd, toolSessionId) {
15487
- const file = import_node_path4.default.join(
15505
+ const file = import_node_path5.default.join(
15488
15506
  this.projectsRoot,
15489
15507
  cwdToHashDir(cwd),
15490
15508
  `${toolSessionId}.jsonl`
@@ -15519,7 +15537,7 @@ var ClaudeHistoryReader = class {
15519
15537
  }
15520
15538
  // 独立文件路径:agent-<subagentId>.jsonl;文件不存在返回 null 让调用方回退旧实现
15521
15539
  readSubagentFromFile(cwd, toolSessionId, subagentId) {
15522
- const file = import_node_path4.default.join(
15540
+ const file = import_node_path5.default.join(
15523
15541
  this.projectsRoot,
15524
15542
  cwdToHashDir(cwd),
15525
15543
  toolSessionId,
@@ -15528,7 +15546,7 @@ var ClaudeHistoryReader = class {
15528
15546
  );
15529
15547
  let exists = false;
15530
15548
  try {
15531
- exists = import_node_fs5.default.statSync(file).isFile();
15549
+ exists = import_node_fs6.default.statSync(file).isFile();
15532
15550
  } catch {
15533
15551
  return null;
15534
15552
  }
@@ -15547,7 +15565,7 @@ var ClaudeHistoryReader = class {
15547
15565
  * "那一刻每个 tracked 文件对应的 backup 文件名"
15548
15566
  */
15549
15567
  readFileHistorySnapshots(args) {
15550
- const file = import_node_path4.default.join(
15568
+ const file = import_node_path5.default.join(
15551
15569
  this.projectsRoot,
15552
15570
  cwdToHashDir(args.cwd),
15553
15571
  `${args.toolSessionId}.jsonl`
@@ -15592,7 +15610,7 @@ var ClaudeHistoryReader = class {
15592
15610
  for (const [anchorId, target] of snapshots) {
15593
15611
  let hasAny = false;
15594
15612
  for (const [rawPath, backup] of Object.entries(target)) {
15595
- const absPath = import_node_path4.default.isAbsolute(rawPath) ? rawPath : import_node_path4.default.join(args.cwd, rawPath);
15613
+ const absPath = import_node_path5.default.isAbsolute(rawPath) ? rawPath : import_node_path5.default.join(args.cwd, rawPath);
15596
15614
  const backupContent = readBackupContent(
15597
15615
  this.fileHistoryRoot,
15598
15616
  args.toolSessionId,
@@ -15632,7 +15650,7 @@ var ClaudeHistoryReader = class {
15632
15650
  let totalInsertions = 0;
15633
15651
  let totalDeletions = 0;
15634
15652
  for (const [rawPath, backup] of Object.entries(target)) {
15635
- const absPath = import_node_path4.default.isAbsolute(rawPath) ? rawPath : import_node_path4.default.join(args.cwd, rawPath);
15653
+ const absPath = import_node_path5.default.isAbsolute(rawPath) ? rawPath : import_node_path5.default.join(args.cwd, rawPath);
15636
15654
  const backupContent = readBackupContent(
15637
15655
  this.fileHistoryRoot,
15638
15656
  args.toolSessionId,
@@ -15679,7 +15697,7 @@ var ClaudeHistoryReader = class {
15679
15697
  };
15680
15698
  }
15681
15699
  readSubagentFromMainJsonl(cwd, toolSessionId, subagentId) {
15682
- const file = import_node_path4.default.join(
15700
+ const file = import_node_path5.default.join(
15683
15701
  this.projectsRoot,
15684
15702
  cwdToHashDir(cwd),
15685
15703
  `${toolSessionId}.jsonl`
@@ -15701,27 +15719,27 @@ var ClaudeHistoryReader = class {
15701
15719
  // src/tools/claude.ts
15702
15720
  function macOSDesktopCandidates(home) {
15703
15721
  return [
15704
- import_node_path5.default.join(home, "Applications", "Claude.app", "Contents", "Resources", "app.asar.unpacked", "node_modules", "@anthropic-ai", "claude-code", "cli.js"),
15722
+ import_node_path6.default.join(home, "Applications", "Claude.app", "Contents", "Resources", "app.asar.unpacked", "node_modules", "@anthropic-ai", "claude-code", "cli.js"),
15705
15723
  "/Applications/Claude.app/Contents/Resources/app.asar.unpacked/node_modules/@anthropic-ai/claude-code/cli.js"
15706
15724
  ];
15707
15725
  }
15708
15726
  function probeViaWhich() {
15709
15727
  try {
15710
15728
  const out = (0, import_node_child_process2.execFileSync)("which", ["claude"], { encoding: "utf8" }).trim();
15711
- if (out && import_node_fs6.default.existsSync(out)) return out;
15729
+ if (out && import_node_fs7.default.existsSync(out)) return out;
15712
15730
  } catch {
15713
15731
  }
15714
15732
  return null;
15715
15733
  }
15716
15734
  async function probeClaude(env = process.env, home = import_node_os3.default.homedir()) {
15717
- if (env.CLAUDE_BIN && import_node_fs6.default.existsSync(env.CLAUDE_BIN)) {
15735
+ if (env.CLAUDE_BIN && import_node_fs7.default.existsSync(env.CLAUDE_BIN)) {
15718
15736
  return { available: true, path: env.CLAUDE_BIN };
15719
15737
  }
15720
15738
  const w = probeViaWhich();
15721
15739
  if (w) return { available: true, path: w };
15722
15740
  if (process.platform === "darwin") {
15723
15741
  for (const candidate of macOSDesktopCandidates(home)) {
15724
- if (import_node_fs6.default.existsSync(candidate)) {
15742
+ if (import_node_fs7.default.existsSync(candidate)) {
15725
15743
  return { available: true, path: candidate };
15726
15744
  }
15727
15745
  }
@@ -16032,10 +16050,10 @@ function parseAttachment(obj) {
16032
16050
  const memories = raw.map((m) => {
16033
16051
  if (!m || typeof m !== "object") return null;
16034
16052
  const rec = m;
16035
- const path17 = typeof rec.path === "string" ? rec.path : null;
16053
+ const path18 = typeof rec.path === "string" ? rec.path : null;
16036
16054
  const content = typeof rec.content === "string" ? rec.content : null;
16037
- if (!path17 || content == null) return null;
16038
- const out = { path: path17, content };
16055
+ if (!path18 || content == null) return null;
16056
+ const out = { path: path18, content };
16039
16057
  if (typeof rec.mtimeMs === "number") out.mtimeMs = rec.mtimeMs;
16040
16058
  return out;
16041
16059
  }).filter((m) => m !== null);
@@ -16263,22 +16281,22 @@ var ClaudeAdapter = class {
16263
16281
  };
16264
16282
 
16265
16283
  // src/workspace/browser.ts
16266
- var import_node_fs7 = __toESM(require("fs"), 1);
16284
+ var import_node_fs8 = __toESM(require("fs"), 1);
16267
16285
  var import_node_os4 = __toESM(require("os"), 1);
16268
- var import_node_path6 = __toESM(require("path"), 1);
16286
+ var import_node_path7 = __toESM(require("path"), 1);
16269
16287
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
16270
16288
  function resolveInsideCwd(cwd, subpath) {
16271
- const absCwd = import_node_path6.default.resolve(cwd);
16272
- const joined = import_node_path6.default.resolve(absCwd, subpath ?? ".");
16273
- const rel = import_node_path6.default.relative(absCwd, joined);
16274
- if (rel.startsWith("..") || import_node_path6.default.isAbsolute(rel)) {
16289
+ const absCwd = import_node_path7.default.resolve(cwd);
16290
+ const joined = import_node_path7.default.resolve(absCwd, subpath ?? ".");
16291
+ const rel = import_node_path7.default.relative(absCwd, joined);
16292
+ if (rel.startsWith("..") || import_node_path7.default.isAbsolute(rel)) {
16275
16293
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `path escapes cwd: ${subpath}`);
16276
16294
  }
16277
16295
  return joined;
16278
16296
  }
16279
16297
  function ensureCwd(cwd) {
16280
16298
  try {
16281
- const stat = import_node_fs7.default.statSync(cwd);
16299
+ const stat = import_node_fs8.default.statSync(cwd);
16282
16300
  if (!stat.isDirectory()) {
16283
16301
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `not a directory: ${cwd}`);
16284
16302
  }
@@ -16292,7 +16310,7 @@ var WorkspaceBrowser = class {
16292
16310
  const cwd = args.cwd && args.cwd.length > 0 ? args.cwd : import_node_os4.default.homedir();
16293
16311
  ensureCwd(cwd);
16294
16312
  const full = resolveInsideCwd(cwd, args.path);
16295
- const dirents = import_node_fs7.default.readdirSync(full, { withFileTypes: true });
16313
+ const dirents = import_node_fs8.default.readdirSync(full, { withFileTypes: true });
16296
16314
  const entries = [];
16297
16315
  for (const d of dirents) {
16298
16316
  if (!args.showHidden && d.name.startsWith(".")) continue;
@@ -16302,7 +16320,7 @@ var WorkspaceBrowser = class {
16302
16320
  mtime: ""
16303
16321
  };
16304
16322
  try {
16305
- const st = import_node_fs7.default.statSync(import_node_path6.default.join(full, d.name));
16323
+ const st = import_node_fs8.default.statSync(import_node_path7.default.join(full, d.name));
16306
16324
  entry.mtime = new Date(st.mtimeMs).toISOString();
16307
16325
  if (d.isFile()) entry.size = st.size;
16308
16326
  } catch {
@@ -16318,14 +16336,14 @@ var WorkspaceBrowser = class {
16318
16336
  read(args) {
16319
16337
  ensureCwd(args.cwd);
16320
16338
  const full = resolveInsideCwd(args.cwd, args.path);
16321
- const st = import_node_fs7.default.statSync(full);
16339
+ const st = import_node_fs8.default.statSync(full);
16322
16340
  if (!st.isFile()) {
16323
16341
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `not a file: ${args.path}`);
16324
16342
  }
16325
16343
  if (st.size > MAX_FILE_BYTES) {
16326
16344
  throw new ClawdError(ERROR_CODES.FILE_TOO_LARGE, `file > ${MAX_FILE_BYTES} bytes`);
16327
16345
  }
16328
- const buf = import_node_fs7.default.readFileSync(full);
16346
+ const buf = import_node_fs8.default.readFileSync(full);
16329
16347
  const isBinary = buf.includes(0);
16330
16348
  if (isBinary) {
16331
16349
  return {
@@ -16347,9 +16365,9 @@ var WorkspaceBrowser = class {
16347
16365
  };
16348
16366
 
16349
16367
  // src/skills/scanner.ts
16350
- var import_node_fs8 = __toESM(require("fs"), 1);
16368
+ var import_node_fs9 = __toESM(require("fs"), 1);
16351
16369
  var import_node_os5 = __toESM(require("os"), 1);
16352
- var import_node_path7 = __toESM(require("path"), 1);
16370
+ var import_node_path8 = __toESM(require("path"), 1);
16353
16371
  function parseFrontmatter(content) {
16354
16372
  if (!content.startsWith("---")) return { name: "", description: "" };
16355
16373
  const end = content.indexOf("---", 3);
@@ -16385,7 +16403,7 @@ function parseFrontmatter(content) {
16385
16403
  }
16386
16404
  function isDirLikeSync(p) {
16387
16405
  try {
16388
- return import_node_fs8.default.statSync(p).isDirectory();
16406
+ return import_node_fs9.default.statSync(p).isDirectory();
16389
16407
  } catch {
16390
16408
  return false;
16391
16409
  }
@@ -16393,19 +16411,19 @@ function isDirLikeSync(p) {
16393
16411
  function scanSkillDir(dir, source, seen, out, pluginName) {
16394
16412
  let entries;
16395
16413
  try {
16396
- entries = import_node_fs8.default.readdirSync(dir, { withFileTypes: true });
16414
+ entries = import_node_fs9.default.readdirSync(dir, { withFileTypes: true });
16397
16415
  } catch {
16398
16416
  return;
16399
16417
  }
16400
16418
  for (const ent of entries) {
16401
- const entryPath = import_node_path7.default.join(dir, ent.name);
16419
+ const entryPath = import_node_path8.default.join(dir, ent.name);
16402
16420
  if (!ent.isDirectory() && !(ent.isSymbolicLink() && isDirLikeSync(entryPath))) continue;
16403
16421
  let content;
16404
16422
  try {
16405
- content = import_node_fs8.default.readFileSync(import_node_path7.default.join(entryPath, "SKILL.md"), "utf8");
16423
+ content = import_node_fs9.default.readFileSync(import_node_path8.default.join(entryPath, "SKILL.md"), "utf8");
16406
16424
  } catch {
16407
16425
  try {
16408
- content = import_node_fs8.default.readFileSync(import_node_path7.default.join(entryPath, "skill.md"), "utf8");
16426
+ content = import_node_fs9.default.readFileSync(import_node_path8.default.join(entryPath, "skill.md"), "utf8");
16409
16427
  } catch {
16410
16428
  continue;
16411
16429
  }
@@ -16423,26 +16441,26 @@ function scanSkillDir(dir, source, seen, out, pluginName) {
16423
16441
  function scanCommandDir(dir, source, seen, out, pluginName) {
16424
16442
  let entries;
16425
16443
  try {
16426
- entries = import_node_fs8.default.readdirSync(dir, { withFileTypes: true });
16444
+ entries = import_node_fs9.default.readdirSync(dir, { withFileTypes: true });
16427
16445
  } catch {
16428
16446
  return;
16429
16447
  }
16430
16448
  for (const ent of entries) {
16431
- const entryPath = import_node_path7.default.join(dir, ent.name);
16449
+ const entryPath = import_node_path8.default.join(dir, ent.name);
16432
16450
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync(entryPath)) {
16433
16451
  const ns = ent.name;
16434
16452
  let subEntries;
16435
16453
  try {
16436
- subEntries = import_node_fs8.default.readdirSync(entryPath, { withFileTypes: true });
16454
+ subEntries = import_node_fs9.default.readdirSync(entryPath, { withFileTypes: true });
16437
16455
  } catch {
16438
16456
  continue;
16439
16457
  }
16440
16458
  for (const se of subEntries) {
16441
16459
  if (!se.name.endsWith(".md")) continue;
16442
- const sePath = import_node_path7.default.join(entryPath, se.name);
16460
+ const sePath = import_node_path8.default.join(entryPath, se.name);
16443
16461
  let content;
16444
16462
  try {
16445
- content = import_node_fs8.default.readFileSync(sePath, "utf8");
16463
+ content = import_node_fs9.default.readFileSync(sePath, "utf8");
16446
16464
  } catch {
16447
16465
  continue;
16448
16466
  }
@@ -16459,7 +16477,7 @@ function scanCommandDir(dir, source, seen, out, pluginName) {
16459
16477
  } else if (ent.name.endsWith(".md")) {
16460
16478
  let content;
16461
16479
  try {
16462
- content = import_node_fs8.default.readFileSync(entryPath, "utf8");
16480
+ content = import_node_fs9.default.readFileSync(entryPath, "utf8");
16463
16481
  } catch {
16464
16482
  continue;
16465
16483
  }
@@ -16475,10 +16493,10 @@ function scanCommandDir(dir, source, seen, out, pluginName) {
16475
16493
  }
16476
16494
  }
16477
16495
  function readInstalledPlugins(home) {
16478
- const file = import_node_path7.default.join(home, ".claude", "plugins", "installed_plugins.json");
16496
+ const file = import_node_path8.default.join(home, ".claude", "plugins", "installed_plugins.json");
16479
16497
  let raw;
16480
16498
  try {
16481
- raw = import_node_fs8.default.readFileSync(file, "utf8");
16499
+ raw = import_node_fs9.default.readFileSync(file, "utf8");
16482
16500
  } catch {
16483
16501
  return [];
16484
16502
  }
@@ -16516,14 +16534,14 @@ var SkillsScanner = class {
16516
16534
  list(args) {
16517
16535
  const seen = /* @__PURE__ */ new Set();
16518
16536
  const out = [];
16519
- scanSkillDir(import_node_path7.default.join(this.home, ".claude", "skills"), "global", seen, out);
16520
- scanCommandDir(import_node_path7.default.join(this.home, ".claude", "commands"), "global", seen, out);
16521
- scanSkillDir(import_node_path7.default.join(args.cwd, ".claude", "skills"), "project", seen, out);
16522
- scanCommandDir(import_node_path7.default.join(args.cwd, ".claude", "commands"), "project", seen, out);
16537
+ scanSkillDir(import_node_path8.default.join(this.home, ".claude", "skills"), "global", seen, out);
16538
+ scanCommandDir(import_node_path8.default.join(this.home, ".claude", "commands"), "global", seen, out);
16539
+ scanSkillDir(import_node_path8.default.join(args.cwd, ".claude", "skills"), "project", seen, out);
16540
+ scanCommandDir(import_node_path8.default.join(args.cwd, ".claude", "commands"), "project", seen, out);
16523
16541
  const plugins = [...readInstalledPlugins(this.home), ...this.extraPluginRoots];
16524
16542
  for (const { name, root } of plugins) {
16525
- scanSkillDir(import_node_path7.default.join(root, "skills"), "plugin", seen, out, name);
16526
- scanCommandDir(import_node_path7.default.join(root, "commands"), "plugin", seen, out, name);
16543
+ scanSkillDir(import_node_path8.default.join(root, "skills"), "plugin", seen, out, name);
16544
+ scanCommandDir(import_node_path8.default.join(root, "commands"), "plugin", seen, out, name);
16527
16545
  }
16528
16546
  out.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
16529
16547
  return out;
@@ -16531,9 +16549,9 @@ var SkillsScanner = class {
16531
16549
  };
16532
16550
 
16533
16551
  // src/observer/session-observer.ts
16534
- var import_node_fs9 = __toESM(require("fs"), 1);
16552
+ var import_node_fs10 = __toESM(require("fs"), 1);
16535
16553
  var import_node_os6 = __toESM(require("os"), 1);
16536
- var import_node_path8 = __toESM(require("path"), 1);
16554
+ var import_node_path9 = __toESM(require("path"), 1);
16537
16555
  var SessionObserver = class {
16538
16556
  constructor(opts) {
16539
16557
  this.opts = opts;
@@ -16544,14 +16562,14 @@ var SessionObserver = class {
16544
16562
  watches = /* @__PURE__ */ new Map();
16545
16563
  resolveJsonlPath(cwd, toolSessionId, override) {
16546
16564
  if (override) return override;
16547
- return import_node_path8.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
16565
+ return import_node_path9.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
16548
16566
  }
16549
16567
  start(args) {
16550
16568
  this.stop(args.sessionId);
16551
16569
  const filePath = this.resolveJsonlPath(args.cwd, args.toolSessionId, args.jsonlPath);
16552
16570
  let size = 0;
16553
16571
  try {
16554
- size = import_node_fs9.default.statSync(filePath).size;
16572
+ size = import_node_fs10.default.statSync(filePath).size;
16555
16573
  } catch {
16556
16574
  }
16557
16575
  const w = {
@@ -16564,10 +16582,10 @@ var SessionObserver = class {
16564
16582
  adapter: args.adapter
16565
16583
  };
16566
16584
  try {
16567
- import_node_fs9.default.mkdirSync(import_node_path8.default.dirname(filePath), { recursive: true });
16585
+ import_node_fs10.default.mkdirSync(import_node_path9.default.dirname(filePath), { recursive: true });
16568
16586
  } catch {
16569
16587
  }
16570
- w.watcher = import_node_fs9.default.watch(import_node_path8.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
16588
+ w.watcher = import_node_fs10.default.watch(import_node_path9.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
16571
16589
  if (!changedName || !filePath.endsWith(changedName)) return;
16572
16590
  this.poll(w);
16573
16591
  });
@@ -16582,7 +16600,7 @@ var SessionObserver = class {
16582
16600
  // reducer.shallowEqMeta diff 让重复 patch 静默吞掉;异常静默吞,不阻塞 watcher 启动
16583
16601
  hydrateMetaTail(w, maxLines = 200) {
16584
16602
  try {
16585
- const raw = import_node_fs9.default.readFileSync(w.filePath, "utf8");
16603
+ const raw = import_node_fs10.default.readFileSync(w.filePath, "utf8");
16586
16604
  if (!raw) return;
16587
16605
  const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
16588
16606
  if (allLines.length === 0) return;
@@ -16603,7 +16621,7 @@ var SessionObserver = class {
16603
16621
  poll(w) {
16604
16622
  let size = 0;
16605
16623
  try {
16606
- size = import_node_fs9.default.statSync(w.filePath).size;
16624
+ size = import_node_fs10.default.statSync(w.filePath).size;
16607
16625
  } catch {
16608
16626
  return;
16609
16627
  }
@@ -16612,11 +16630,11 @@ var SessionObserver = class {
16612
16630
  w.buf = "";
16613
16631
  }
16614
16632
  if (size === w.lastSize) return;
16615
- const fd = import_node_fs9.default.openSync(w.filePath, "r");
16633
+ const fd = import_node_fs10.default.openSync(w.filePath, "r");
16616
16634
  try {
16617
16635
  const len = size - w.lastSize;
16618
16636
  const buf = Buffer.alloc(len);
16619
- import_node_fs9.default.readSync(fd, buf, 0, len, w.lastSize);
16637
+ import_node_fs10.default.readSync(fd, buf, 0, len, w.lastSize);
16620
16638
  w.lastSize = size;
16621
16639
  w.buf += buf.toString("utf8");
16622
16640
  let newlineIndex;
@@ -16630,7 +16648,7 @@ var SessionObserver = class {
16630
16648
  this.maybeReportUserMessage(w.sessionId, line);
16631
16649
  }
16632
16650
  } finally {
16633
- import_node_fs9.default.closeSync(fd);
16651
+ import_node_fs10.default.closeSync(fd);
16634
16652
  }
16635
16653
  }
16636
16654
  // 解析 JSONL 单行:仅当是主链 user 文本行(非 sidechain / 非 sub-agent / message.role='user'
@@ -17045,10 +17063,10 @@ function isLocalhost(addr) {
17045
17063
  }
17046
17064
 
17047
17065
  // src/discovery/state-file.ts
17048
- var import_node_fs10 = __toESM(require("fs"), 1);
17049
- var import_node_path9 = __toESM(require("path"), 1);
17066
+ var import_node_fs11 = __toESM(require("fs"), 1);
17067
+ var import_node_path10 = __toESM(require("path"), 1);
17050
17068
  function defaultStateFilePath(dataDir) {
17051
- return import_node_path9.default.join(dataDir, "state.json");
17069
+ return import_node_path10.default.join(dataDir, "state.json");
17052
17070
  }
17053
17071
  function isPidAlive(pid) {
17054
17072
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -17070,7 +17088,7 @@ var StateFileManager = class {
17070
17088
  }
17071
17089
  read() {
17072
17090
  try {
17073
- const raw = import_node_fs10.default.readFileSync(this.file, "utf8");
17091
+ const raw = import_node_fs11.default.readFileSync(this.file, "utf8");
17074
17092
  const parsed = JSON.parse(raw);
17075
17093
  return parsed;
17076
17094
  } catch {
@@ -17084,34 +17102,34 @@ var StateFileManager = class {
17084
17102
  return { status: "stale", existing };
17085
17103
  }
17086
17104
  write(state) {
17087
- import_node_fs10.default.mkdirSync(import_node_path9.default.dirname(this.file), { recursive: true });
17105
+ import_node_fs11.default.mkdirSync(import_node_path10.default.dirname(this.file), { recursive: true });
17088
17106
  const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
17089
- import_node_fs10.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
17090
- import_node_fs10.default.renameSync(tmp, this.file);
17107
+ import_node_fs11.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
17108
+ import_node_fs11.default.renameSync(tmp, this.file);
17091
17109
  if (process.platform !== "win32") {
17092
17110
  try {
17093
- import_node_fs10.default.chmodSync(this.file, 384);
17111
+ import_node_fs11.default.chmodSync(this.file, 384);
17094
17112
  } catch {
17095
17113
  }
17096
17114
  }
17097
17115
  }
17098
17116
  delete() {
17099
17117
  try {
17100
- import_node_fs10.default.unlinkSync(this.file);
17118
+ import_node_fs11.default.unlinkSync(this.file);
17101
17119
  } catch {
17102
17120
  }
17103
17121
  }
17104
17122
  };
17105
17123
 
17106
17124
  // src/tunnel/tunnel-manager.ts
17107
- var import_node_fs13 = __toESM(require("fs"), 1);
17108
- var import_node_path12 = __toESM(require("path"), 1);
17125
+ var import_node_fs14 = __toESM(require("fs"), 1);
17126
+ var import_node_path13 = __toESM(require("path"), 1);
17109
17127
  var import_node_crypto3 = __toESM(require("crypto"), 1);
17110
17128
  var import_node_child_process4 = require("child_process");
17111
17129
 
17112
17130
  // src/tunnel/tunnel-store.ts
17113
- var import_node_fs11 = __toESM(require("fs"), 1);
17114
- var import_node_path10 = __toESM(require("path"), 1);
17131
+ var import_node_fs12 = __toESM(require("fs"), 1);
17132
+ var import_node_path11 = __toESM(require("path"), 1);
17115
17133
  var TunnelStore = class {
17116
17134
  constructor(filePath) {
17117
17135
  this.filePath = filePath;
@@ -17119,7 +17137,7 @@ var TunnelStore = class {
17119
17137
  filePath;
17120
17138
  async get() {
17121
17139
  try {
17122
- const raw = await import_node_fs11.default.promises.readFile(this.filePath, "utf8");
17140
+ const raw = await import_node_fs12.default.promises.readFile(this.filePath, "utf8");
17123
17141
  const obj = JSON.parse(raw);
17124
17142
  if (!isPersistedTunnel(obj)) return null;
17125
17143
  return obj;
@@ -17130,22 +17148,22 @@ var TunnelStore = class {
17130
17148
  }
17131
17149
  }
17132
17150
  async set(v) {
17133
- const dir = import_node_path10.default.dirname(this.filePath);
17134
- await import_node_fs11.default.promises.mkdir(dir, { recursive: true });
17151
+ const dir = import_node_path11.default.dirname(this.filePath);
17152
+ await import_node_fs12.default.promises.mkdir(dir, { recursive: true });
17135
17153
  const data = JSON.stringify(v, null, 2);
17136
17154
  const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
17137
- await import_node_fs11.default.promises.writeFile(tmp, data, { mode: 384 });
17155
+ await import_node_fs12.default.promises.writeFile(tmp, data, { mode: 384 });
17138
17156
  if (process.platform !== "win32") {
17139
17157
  try {
17140
- await import_node_fs11.default.promises.chmod(tmp, 384);
17158
+ await import_node_fs12.default.promises.chmod(tmp, 384);
17141
17159
  } catch {
17142
17160
  }
17143
17161
  }
17144
- await import_node_fs11.default.promises.rename(tmp, this.filePath);
17162
+ await import_node_fs12.default.promises.rename(tmp, this.filePath);
17145
17163
  }
17146
17164
  async clear() {
17147
17165
  try {
17148
- await import_node_fs11.default.promises.unlink(this.filePath);
17166
+ await import_node_fs12.default.promises.unlink(this.filePath);
17149
17167
  } catch (err) {
17150
17168
  const code = err?.code;
17151
17169
  if (code !== "ENOENT") throw err;
@@ -17240,9 +17258,9 @@ function escape(v) {
17240
17258
  }
17241
17259
 
17242
17260
  // src/tunnel/frpc-binary.ts
17243
- var import_node_fs12 = __toESM(require("fs"), 1);
17261
+ var import_node_fs13 = __toESM(require("fs"), 1);
17244
17262
  var import_node_os7 = __toESM(require("os"), 1);
17245
- var import_node_path11 = __toESM(require("path"), 1);
17263
+ var import_node_path12 = __toESM(require("path"), 1);
17246
17264
  var import_node_child_process3 = require("child_process");
17247
17265
  var import_node_stream = require("stream");
17248
17266
  var import_promises = require("stream/promises");
@@ -17274,20 +17292,20 @@ function frpcDownloadUrl(version2, p) {
17274
17292
  }
17275
17293
  async function ensureFrpcBinary(opts) {
17276
17294
  if (opts.override) {
17277
- if (!import_node_fs12.default.existsSync(opts.override)) {
17295
+ if (!import_node_fs13.default.existsSync(opts.override)) {
17278
17296
  throw new Error(`frpc binary not found at override path: ${opts.override}`);
17279
17297
  }
17280
17298
  return opts.override;
17281
17299
  }
17282
17300
  const version2 = opts.version ?? FRPC_VERSION;
17283
17301
  const platform = opts.platform ?? detectPlatform();
17284
- const binDir = import_node_path11.default.join(opts.dataDir, "bin");
17285
- import_node_fs12.default.mkdirSync(binDir, { recursive: true });
17302
+ const binDir = import_node_path12.default.join(opts.dataDir, "bin");
17303
+ import_node_fs13.default.mkdirSync(binDir, { recursive: true });
17286
17304
  cleanupStaleArtifacts(binDir);
17287
- const stableBin = import_node_path11.default.join(binDir, "frpc");
17288
- if (import_node_fs12.default.existsSync(stableBin)) return stableBin;
17305
+ const stableBin = import_node_path12.default.join(binDir, "frpc");
17306
+ if (import_node_fs13.default.existsSync(stableBin)) return stableBin;
17289
17307
  const partialBin = `${stableBin}.partial`;
17290
- const tarballPath = import_node_path11.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
17308
+ const tarballPath = import_node_path12.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
17291
17309
  try {
17292
17310
  const url = frpcDownloadUrl(version2, platform);
17293
17311
  await downloadToFile(url, tarballPath, opts.fetchImpl);
@@ -17296,8 +17314,8 @@ async function ensureFrpcBinary(opts) {
17296
17314
  } else {
17297
17315
  await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
17298
17316
  }
17299
- import_node_fs12.default.chmodSync(partialBin, 493);
17300
- import_node_fs12.default.renameSync(partialBin, stableBin);
17317
+ import_node_fs13.default.chmodSync(partialBin, 493);
17318
+ import_node_fs13.default.renameSync(partialBin, stableBin);
17301
17319
  } finally {
17302
17320
  safeUnlink(tarballPath);
17303
17321
  safeUnlink(partialBin);
@@ -17307,15 +17325,15 @@ async function ensureFrpcBinary(opts) {
17307
17325
  function cleanupStaleArtifacts(binDir) {
17308
17326
  let entries;
17309
17327
  try {
17310
- entries = import_node_fs12.default.readdirSync(binDir);
17328
+ entries = import_node_fs13.default.readdirSync(binDir);
17311
17329
  } catch {
17312
17330
  return;
17313
17331
  }
17314
17332
  for (const name of entries) {
17315
17333
  if (name.endsWith(".partial") || name.startsWith("extract-")) {
17316
- const full = import_node_path11.default.join(binDir, name);
17334
+ const full = import_node_path12.default.join(binDir, name);
17317
17335
  try {
17318
- import_node_fs12.default.rmSync(full, { recursive: true, force: true });
17336
+ import_node_fs13.default.rmSync(full, { recursive: true, force: true });
17319
17337
  } catch {
17320
17338
  }
17321
17339
  }
@@ -17323,7 +17341,7 @@ function cleanupStaleArtifacts(binDir) {
17323
17341
  }
17324
17342
  function safeUnlink(p) {
17325
17343
  try {
17326
- import_node_fs12.default.unlinkSync(p);
17344
+ import_node_fs13.default.unlinkSync(p);
17327
17345
  } catch {
17328
17346
  }
17329
17347
  }
@@ -17334,13 +17352,13 @@ async function downloadToFile(url, dest, fetchImpl) {
17334
17352
  if (!res.ok || !res.body) {
17335
17353
  throw new Error(`download failed: ${res.status} ${res.statusText}`);
17336
17354
  }
17337
- const out = import_node_fs12.default.createWriteStream(dest);
17355
+ const out = import_node_fs13.default.createWriteStream(dest);
17338
17356
  const nodeStream = import_node_stream.Readable.fromWeb(res.body);
17339
17357
  await (0, import_promises.pipeline)(nodeStream, out);
17340
17358
  }
17341
17359
  async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
17342
- const work = import_node_path11.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
17343
- import_node_fs12.default.mkdirSync(work, { recursive: true });
17360
+ const work = import_node_path12.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
17361
+ import_node_fs13.default.mkdirSync(work, { recursive: true });
17344
17362
  try {
17345
17363
  await new Promise((resolve, reject) => {
17346
17364
  const proc = (0, import_node_child_process3.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
@@ -17348,13 +17366,13 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
17348
17366
  proc.on("exit", (code) => code === 0 ? resolve() : reject(new Error(`tar exited ${code}`)));
17349
17367
  });
17350
17368
  const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
17351
- const src = import_node_path11.default.join(work, dirName, "frpc");
17352
- if (!import_node_fs12.default.existsSync(src)) {
17369
+ const src = import_node_path12.default.join(work, dirName, "frpc");
17370
+ if (!import_node_fs13.default.existsSync(src)) {
17353
17371
  throw new Error(`frpc not found inside tarball at ${src}`);
17354
17372
  }
17355
- import_node_fs12.default.copyFileSync(src, destBin);
17373
+ import_node_fs13.default.copyFileSync(src, destBin);
17356
17374
  } finally {
17357
- import_node_fs12.default.rmSync(work, { recursive: true, force: true });
17375
+ import_node_fs13.default.rmSync(work, { recursive: true, force: true });
17358
17376
  }
17359
17377
  }
17360
17378
 
@@ -17363,7 +17381,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
17363
17381
  var TunnelManager = class {
17364
17382
  constructor(deps) {
17365
17383
  this.deps = deps;
17366
- this.store = deps.store ?? new TunnelStore(import_node_path12.default.join(deps.dataDir, "tunnel.json"));
17384
+ this.store = deps.store ?? new TunnelStore(import_node_path13.default.join(deps.dataDir, "tunnel.json"));
17367
17385
  this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
17368
17386
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
17369
17387
  }
@@ -17480,7 +17498,7 @@ var TunnelManager = class {
17480
17498
  dataDir: this.deps.dataDir,
17481
17499
  override: this.deps.frpcBinaryOverride ?? void 0
17482
17500
  });
17483
- const tomlPath = import_node_path12.default.join(this.deps.dataDir, "frpc.toml");
17501
+ const tomlPath = import_node_path13.default.join(this.deps.dataDir, "frpc.toml");
17484
17502
  const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto3.default.randomBytes(3).toString("hex")}`;
17485
17503
  const toml = buildFrpcToml({
17486
17504
  serverAddr: t.frpsHost,
@@ -17491,12 +17509,12 @@ var TunnelManager = class {
17491
17509
  localPort,
17492
17510
  logLevel: "info"
17493
17511
  });
17494
- await import_node_fs13.default.promises.writeFile(tomlPath, toml, { mode: 384 });
17512
+ await import_node_fs14.default.promises.writeFile(tomlPath, toml, { mode: 384 });
17495
17513
  const proc = (this.deps.spawnImpl ?? import_node_child_process4.spawn)(frpcBin, ["-c", tomlPath], {
17496
17514
  stdio: ["ignore", "pipe", "pipe"]
17497
17515
  });
17498
- const logFilePath = import_node_path12.default.join(this.deps.dataDir, "frpc.log");
17499
- const logStream = import_node_fs13.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
17516
+ const logFilePath = import_node_path13.default.join(this.deps.dataDir, "frpc.log");
17517
+ const logStream = import_node_fs14.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
17500
17518
  logStream.on("error", () => {
17501
17519
  });
17502
17520
  const tee = (chunk) => {
@@ -17588,12 +17606,12 @@ function deriveStableDeviceKey(opts = {}) {
17588
17606
  }
17589
17607
 
17590
17608
  // src/auth-store.ts
17591
- var import_node_fs14 = __toESM(require("fs"), 1);
17592
- var import_node_path13 = __toESM(require("path"), 1);
17609
+ var import_node_fs15 = __toESM(require("fs"), 1);
17610
+ var import_node_path14 = __toESM(require("path"), 1);
17593
17611
  var import_node_crypto5 = __toESM(require("crypto"), 1);
17594
17612
  var AUTH_FILE_NAME = "auth.json";
17595
17613
  function authFilePath(dataDir) {
17596
- return import_node_path13.default.join(dataDir, AUTH_FILE_NAME);
17614
+ return import_node_path14.default.join(dataDir, AUTH_FILE_NAME);
17597
17615
  }
17598
17616
  function loadOrCreateAuthToken(opts) {
17599
17617
  const file = authFilePath(opts.dataDir);
@@ -17609,7 +17627,7 @@ function defaultGenerate() {
17609
17627
  }
17610
17628
  function readAuthFile(file) {
17611
17629
  try {
17612
- const raw = import_node_fs14.default.readFileSync(file, "utf8");
17630
+ const raw = import_node_fs15.default.readFileSync(file, "utf8");
17613
17631
  const parsed = JSON.parse(raw);
17614
17632
  if (typeof parsed?.token === "string" && parsed.token.length > 0) {
17615
17633
  return {
@@ -17625,20 +17643,20 @@ function readAuthFile(file) {
17625
17643
  }
17626
17644
  }
17627
17645
  function writeAuthFile(file, content) {
17628
- import_node_fs14.default.mkdirSync(import_node_path13.default.dirname(file), { recursive: true });
17629
- import_node_fs14.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
17646
+ import_node_fs15.default.mkdirSync(import_node_path14.default.dirname(file), { recursive: true });
17647
+ import_node_fs15.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
17630
17648
  try {
17631
- import_node_fs14.default.chmodSync(file, 384);
17649
+ import_node_fs15.default.chmodSync(file, 384);
17632
17650
  } catch {
17633
17651
  }
17634
17652
  }
17635
17653
 
17636
17654
  // src/session/fork.ts
17637
- var import_node_fs15 = __toESM(require("fs"), 1);
17655
+ var import_node_fs16 = __toESM(require("fs"), 1);
17638
17656
  var import_node_os9 = __toESM(require("os"), 1);
17639
- var import_node_path14 = __toESM(require("path"), 1);
17657
+ var import_node_path15 = __toESM(require("path"), 1);
17640
17658
  function readJsonlEntries(file) {
17641
- const raw = import_node_fs15.default.readFileSync(file, "utf8");
17659
+ const raw = import_node_fs16.default.readFileSync(file, "utf8");
17642
17660
  const out = [];
17643
17661
  for (const line of raw.split("\n")) {
17644
17662
  const t = line.trim();
@@ -17651,10 +17669,10 @@ function readJsonlEntries(file) {
17651
17669
  return out;
17652
17670
  }
17653
17671
  function forkSession(input) {
17654
- const baseDir = input.baseDir ?? import_node_path14.default.join(import_node_os9.default.homedir(), ".claude");
17655
- const projectDir = import_node_path14.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
17656
- const sourceFile = import_node_path14.default.join(projectDir, `${input.toolSessionId}.jsonl`);
17657
- if (!import_node_fs15.default.existsSync(sourceFile)) {
17672
+ const baseDir = input.baseDir ?? import_node_path15.default.join(import_node_os9.default.homedir(), ".claude");
17673
+ const projectDir = import_node_path15.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
17674
+ const sourceFile = import_node_path15.default.join(projectDir, `${input.toolSessionId}.jsonl`);
17675
+ if (!import_node_fs16.default.existsSync(sourceFile)) {
17658
17676
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
17659
17677
  }
17660
17678
  const entries = readJsonlEntries(sourceFile);
@@ -17684,9 +17702,9 @@ function forkSession(input) {
17684
17702
  }
17685
17703
  forkedLines.push(JSON.stringify(forked));
17686
17704
  }
17687
- const forkedFilePath = import_node_path14.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
17688
- import_node_fs15.default.mkdirSync(projectDir, { recursive: true });
17689
- import_node_fs15.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
17705
+ const forkedFilePath = import_node_path15.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
17706
+ import_node_fs16.default.mkdirSync(projectDir, { recursive: true });
17707
+ import_node_fs16.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
17690
17708
  return { forkedToolSessionId, forkedFilePath };
17691
17709
  }
17692
17710
 
@@ -17807,11 +17825,6 @@ function buildSessionHandlers(deps) {
17807
17825
  const { response, broadcast } = manager.pin(args);
17808
17826
  return { response: { type: "session:info", ...response }, broadcast };
17809
17827
  };
17810
- const reorderPins = async (frame) => {
17811
- const args = SessionReorderPinsArgs.parse(frame);
17812
- const { response, broadcast } = manager.reorderPins(args);
17813
- return { response: { type: "session:reorderPins", ...response }, broadcast };
17814
- };
17815
17828
  const answerQuestion = async (frame) => {
17816
17829
  const args = AnswerQuestionArgs.parse(frame);
17817
17830
  const { response, broadcast } = manager.answerQuestion(args);
@@ -17837,7 +17850,6 @@ function buildSessionHandlers(deps) {
17837
17850
  "session:subscribe": subscribe,
17838
17851
  "session:unsubscribe": unsubscribe,
17839
17852
  "session:pin": pin,
17840
- "session:reorderPins": reorderPins,
17841
17853
  "session:answerQuestion": answerQuestion
17842
17854
  };
17843
17855
  }
@@ -17948,9 +17960,9 @@ function buildWorkspaceHandlers(deps) {
17948
17960
 
17949
17961
  // src/workspace/git.ts
17950
17962
  var import_node_child_process5 = require("child_process");
17951
- var import_node_fs16 = __toESM(require("fs"), 1);
17963
+ var import_node_fs17 = __toESM(require("fs"), 1);
17952
17964
  var import_node_os10 = __toESM(require("os"), 1);
17953
- var import_node_path15 = __toESM(require("path"), 1);
17965
+ var import_node_path16 = __toESM(require("path"), 1);
17954
17966
  var import_node_util = require("util");
17955
17967
  var pexec = (0, import_node_util.promisify)(import_node_child_process5.execFile);
17956
17968
  function formatChildProcessError(err) {
@@ -17965,9 +17977,9 @@ function formatChildProcessError(err) {
17965
17977
  return e.message ?? "unknown error";
17966
17978
  }
17967
17979
  function normalizePath(p) {
17968
- const resolved = import_node_path15.default.resolve(p);
17980
+ const resolved = import_node_path16.default.resolve(p);
17969
17981
  try {
17970
- return import_node_fs16.default.realpathSync(resolved);
17982
+ return import_node_fs17.default.realpathSync(resolved);
17971
17983
  } catch {
17972
17984
  return resolved;
17973
17985
  }
@@ -18068,13 +18080,13 @@ function flattenToDirName(branch) {
18068
18080
  }
18069
18081
  function encodeClaudeProjectDir(absPath) {
18070
18082
  if (!absPath || typeof absPath !== "string") return "";
18071
- let canonical = import_node_path15.default.resolve(absPath);
18083
+ let canonical = import_node_path16.default.resolve(absPath);
18072
18084
  try {
18073
- canonical = import_node_fs16.default.realpathSync(canonical);
18085
+ canonical = import_node_fs17.default.realpathSync(canonical);
18074
18086
  } catch {
18075
18087
  try {
18076
- const parent = import_node_fs16.default.realpathSync(import_node_path15.default.dirname(canonical));
18077
- canonical = import_node_path15.default.join(parent, import_node_path15.default.basename(canonical));
18088
+ const parent = import_node_fs17.default.realpathSync(import_node_path16.default.dirname(canonical));
18089
+ canonical = import_node_path16.default.join(parent, import_node_path16.default.basename(canonical));
18078
18090
  } catch {
18079
18091
  }
18080
18092
  }
@@ -18098,11 +18110,11 @@ async function createWorktree(input) {
18098
18110
  if (!isGitRoot) {
18099
18111
  throw new Error(`\u76EE\u5F55 ${cwd} \u4E0D\u662F git repo \u6839`);
18100
18112
  }
18101
- const parent = import_node_path15.default.dirname(import_node_path15.default.resolve(cwd));
18102
- if (parent === "/" || parent === import_node_path15.default.resolve(cwd)) {
18113
+ const parent = import_node_path16.default.dirname(import_node_path16.default.resolve(cwd));
18114
+ if (parent === "/" || parent === import_node_path16.default.resolve(cwd)) {
18103
18115
  throw new Error("repo \u5728\u78C1\u76D8\u6839\u76EE\u5F55\uFF0C\u65E0\u6CD5\u5728\u540C\u7EA7\u521B\u5EFA worktree");
18104
18116
  }
18105
- const worktreeRoot = import_node_path15.default.join(parent, dirName);
18117
+ const worktreeRoot = import_node_path16.default.join(parent, dirName);
18106
18118
  try {
18107
18119
  await pexec("git", ["-C", cwd, "rev-parse", "--verify", `refs/heads/${baseBranch}`], {
18108
18120
  timeout: 3e3
@@ -18119,7 +18131,7 @@ async function createWorktree(input) {
18119
18131
  const msg = err.message;
18120
18132
  if (msg.startsWith("\u5206\u652F ")) throw err;
18121
18133
  }
18122
- if (import_node_fs16.default.existsSync(worktreeRoot)) {
18134
+ if (import_node_fs17.default.existsSync(worktreeRoot)) {
18123
18135
  throw new Error(`\u76EE\u5F55 ${worktreeRoot} \u5DF2\u5B58\u5728\uFF0C\u8BF7\u6362\u4E00\u4E2A label \u6216\u6E05\u7406\u540E\u91CD\u8BD5`);
18124
18136
  }
18125
18137
  try {
@@ -18147,8 +18159,8 @@ async function removeWorktree(input) {
18147
18159
  );
18148
18160
  const gitCommonDir = stdout.trim();
18149
18161
  if (!gitCommonDir) throw new Error("empty git-common-dir");
18150
- const absGitCommon = import_node_path15.default.isAbsolute(gitCommonDir) ? gitCommonDir : import_node_path15.default.resolve(worktreeRoot, gitCommonDir);
18151
- repoRoot = import_node_path15.default.dirname(absGitCommon);
18162
+ const absGitCommon = import_node_path16.default.isAbsolute(gitCommonDir) ? gitCommonDir : import_node_path16.default.resolve(worktreeRoot, gitCommonDir);
18163
+ repoRoot = import_node_path16.default.dirname(absGitCommon);
18152
18164
  } catch {
18153
18165
  repoRoot = null;
18154
18166
  }
@@ -18160,7 +18172,7 @@ async function removeWorktree(input) {
18160
18172
  } catch (err) {
18161
18173
  const stderr = err.stderr ?? "";
18162
18174
  const lower = stderr.toLowerCase();
18163
- const vanished = lower.includes("not a working tree") || lower.includes("is not a working tree") || !import_node_fs16.default.existsSync(worktreeRoot);
18175
+ const vanished = lower.includes("not a working tree") || lower.includes("is not a working tree") || !import_node_fs17.default.existsSync(worktreeRoot);
18164
18176
  if (!vanished) {
18165
18177
  throw new Error(`\u6E05\u7406 worktree \u5931\u8D25\uFF1A${formatChildProcessError(err)}`);
18166
18178
  }
@@ -18179,10 +18191,10 @@ async function removeWorktree(input) {
18179
18191
  try {
18180
18192
  const encoded = encodeClaudeProjectDir(worktreeRoot);
18181
18193
  if (encoded) {
18182
- const projectsRoot = import_node_path15.default.join(import_node_os10.default.homedir(), ".claude", "projects");
18183
- const target = import_node_path15.default.resolve(projectsRoot, encoded);
18184
- if (target.startsWith(projectsRoot + import_node_path15.default.sep) && target !== projectsRoot) {
18185
- import_node_fs16.default.rmSync(target, { recursive: true, force: true });
18194
+ const projectsRoot = import_node_path16.default.join(import_node_os10.default.homedir(), ".claude", "projects");
18195
+ const target = import_node_path16.default.resolve(projectsRoot, encoded);
18196
+ if (target.startsWith(projectsRoot + import_node_path16.default.sep) && target !== projectsRoot) {
18197
+ import_node_fs17.default.rmSync(target, { recursive: true, force: true });
18186
18198
  }
18187
18199
  }
18188
18200
  } catch {
@@ -18309,7 +18321,7 @@ function buildMethodHandlers(deps) {
18309
18321
  async function startDaemon(config) {
18310
18322
  const logger = createLogger({
18311
18323
  level: config.logLevel,
18312
- file: import_node_path16.default.join(config.dataDir, "clawd.log")
18324
+ file: import_node_path17.default.join(config.dataDir, "clawd.log")
18313
18325
  });
18314
18326
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
18315
18327
  const stateMgr = new StateFileManager({ dataDir: config.dataDir });
@@ -18349,6 +18361,7 @@ async function startDaemon(config) {
18349
18361
  logger,
18350
18362
  getAdapter,
18351
18363
  historyReader: history,
18364
+ dataDir: config.dataDir,
18352
18365
  broadcastFrame: (frame, target) => {
18353
18366
  if (target === "all") {
18354
18367
  transport?.broadcastAll(frame);
@@ -18471,8 +18484,8 @@ async function startDaemon(config) {
18471
18484
  const lines = [
18472
18485
  `Tunnel: ${r.url}`,
18473
18486
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
18474
- `Frpc config: ${import_node_path16.default.join(config.dataDir, "frpc.toml")}`,
18475
- `Frpc log: ${import_node_path16.default.join(config.dataDir, "frpc.log")}`
18487
+ `Frpc config: ${import_node_path17.default.join(config.dataDir, "frpc.toml")}`,
18488
+ `Frpc log: ${import_node_path17.default.join(config.dataDir, "frpc.log")}`
18476
18489
  ];
18477
18490
  const width = Math.max(...lines.map((l) => l.length));
18478
18491
  const bar = "\u2550".repeat(width + 4);
@@ -18485,8 +18498,8 @@ ${bar}
18485
18498
 
18486
18499
  `);
18487
18500
  try {
18488
- const connectPath = import_node_path16.default.join(config.dataDir, "connect.txt");
18489
- import_node_fs17.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
18501
+ const connectPath = import_node_path17.default.join(config.dataDir, "connect.txt");
18502
+ import_node_fs18.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
18490
18503
  } catch {
18491
18504
  }
18492
18505
  } catch (err) {