@clawos-dev/clawd 0.2.25-beta.35.0347a17 → 0.2.25

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 +362 -237
  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,6 +8144,7 @@ var METHOD_NAMES = [
8144
8144
  "session:subscribe",
8145
8145
  "session:unsubscribe",
8146
8146
  "session:pin",
8147
+ "session:reorderPins",
8147
8148
  "permission:respond",
8148
8149
  // AskUserQuestion 表单回写:UI 答完所有 question 后调用,daemon 把答案合并进 updated_input
8149
8150
  // 写一条 control_response 到 CC stdin(详见 events.ts session:question JSDoc)
@@ -8686,8 +8687,8 @@ function getErrorMap() {
8686
8687
 
8687
8688
  // ../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
8688
8689
  var makeIssue = (params) => {
8689
- const { data, path: path17, errorMaps, issueData } = params;
8690
- const fullPath = [...path17, ...issueData.path || []];
8690
+ const { data, path: path18, errorMaps, issueData } = params;
8691
+ const fullPath = [...path18, ...issueData.path || []];
8691
8692
  const fullIssue = {
8692
8693
  ...issueData,
8693
8694
  path: fullPath
@@ -8803,11 +8804,11 @@ var errorUtil;
8803
8804
 
8804
8805
  // ../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
8805
8806
  var ParseInputLazyPath = class {
8806
- constructor(parent, value, path17, key) {
8807
+ constructor(parent, value, path18, key) {
8807
8808
  this._cachedPath = [];
8808
8809
  this.parent = parent;
8809
8810
  this.data = value;
8810
- this._path = path17;
8811
+ this._path = path18;
8811
8812
  this._key = key;
8812
8813
  }
8813
8814
  get path() {
@@ -12326,6 +12327,9 @@ var SessionFileSchema = external_exports.object({
12326
12327
  permissionRules: external_exports.array(AllowRuleSchema).optional(),
12327
12328
  // Sidebar 置顶时间戳;null/undefined 表示未置顶
12328
12329
  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(),
12329
12333
  // 用户在 NewSessionDialog 或 Edit modal 选择的 icon 标识;UI 端做 enum 兜底
12330
12334
  iconKey: external_exports.string().optional(),
12331
12335
  // NewSessionDialog 勾选 worktree 创建的 session 才会持久化这两个字段
@@ -12657,6 +12661,9 @@ var SessionPinArgs = external_exports.object({
12657
12661
  sessionId: external_exports.string().min(1),
12658
12662
  pinned: external_exports.boolean()
12659
12663
  });
12664
+ var SessionReorderPinsArgs = external_exports.object({
12665
+ orderedIds: external_exports.array(external_exports.string().min(1)).min(1)
12666
+ });
12660
12667
  var GitRootArgs = external_exports.object({ cwd: external_exports.string().min(1) });
12661
12668
  var GitRootResponseSchema = external_exports.object({
12662
12669
  isGitRoot: external_exports.boolean(),
@@ -12812,7 +12819,7 @@ var SessionStore = class {
12812
12819
  };
12813
12820
 
12814
12821
  // src/session/manager.ts
12815
- var import_node_fs4 = __toESM(require("fs"), 1);
12822
+ var import_node_fs5 = __toESM(require("fs"), 1);
12816
12823
 
12817
12824
  // ../node_modules/.pnpm/uuid@10.0.0/node_modules/uuid/dist/esm-node/stringify.js
12818
12825
  var byteToHex = [];
@@ -13413,6 +13420,58 @@ function splitStdoutChunk(buf, chunk) {
13413
13420
  return { newBuf: next, lines };
13414
13421
  }
13415
13422
 
13423
+ // src/ipc-recorder.ts
13424
+ var import_node_fs4 = __toESM(require("fs"), 1);
13425
+ var import_node_path4 = __toESM(require("path"), 1);
13426
+ function tsForFilename(ms) {
13427
+ return new Date(ms).toISOString().replace(/[:.]/g, "-");
13428
+ }
13429
+ function startRecorder(opts) {
13430
+ if (opts.env.CLAWD_RECORD_IPC !== "1") return null;
13431
+ if (!opts.dataDir) return null;
13432
+ if (!opts.sessionId || opts.sessionId.includes("..") || opts.sessionId.includes("/") || opts.sessionId.includes("\\") || opts.sessionId.startsWith(".")) {
13433
+ return null;
13434
+ }
13435
+ const now = opts.now ?? Date.now;
13436
+ const dir = import_node_path4.default.join(opts.dataDir, "ipc-recordings", opts.sessionId);
13437
+ const filePath = import_node_path4.default.join(dir, `${tsForFilename(now())}.jsonl`);
13438
+ let stream = null;
13439
+ let closedResolve;
13440
+ const closed = new Promise((resolve) => {
13441
+ closedResolve = resolve;
13442
+ });
13443
+ let exited = false;
13444
+ const ensureStream = () => {
13445
+ if (stream) return stream;
13446
+ import_node_fs4.default.mkdirSync(dir, { recursive: true });
13447
+ stream = import_node_fs4.default.createWriteStream(filePath, { flags: "a" });
13448
+ stream.on("close", () => closedResolve());
13449
+ return stream;
13450
+ };
13451
+ const writeLine = (s, chunk) => {
13452
+ if (exited) return;
13453
+ const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
13454
+ const line = JSON.stringify({ ts: now(), stream: s, chunk: text }) + "\n";
13455
+ ensureStream().write(line);
13456
+ };
13457
+ opts.proc.stdout?.on("data", (chunk) => writeLine("stdout", chunk));
13458
+ opts.proc.stderr?.on("data", (chunk) => writeLine("stderr", chunk));
13459
+ opts.proc.on("exit", () => {
13460
+ exited = true;
13461
+ if (stream) {
13462
+ stream.end();
13463
+ } else {
13464
+ closedResolve();
13465
+ }
13466
+ });
13467
+ return {
13468
+ tapStdinWrite(chunk) {
13469
+ writeLine("stdin", chunk);
13470
+ },
13471
+ closed
13472
+ };
13473
+ }
13474
+
13416
13475
  // src/session/runner.ts
13417
13476
  var DEFAULT_CONTROL_REQUEST_TIMEOUT_MS = 1e4;
13418
13477
  function encodeAllowWithInputControlResponse(requestId, updatedInput) {
@@ -13443,6 +13502,8 @@ var SessionRunner = class {
13443
13502
  pendingControlRequests = /* @__PURE__ */ new Map();
13444
13503
  // waitUntilStopped 排队的 resolve 回调;reducer 把 procAlive 翻成 false 后批量触发
13445
13504
  stopWaiters = [];
13505
+ // IPC recorder(CLAWD_RECORD_IPC=1 时启用);null 表示当前 spawn 未启用 / 已退出
13506
+ recorder = null;
13446
13507
  getState() {
13447
13508
  return this.state;
13448
13509
  }
@@ -13532,6 +13593,7 @@ var SessionRunner = class {
13532
13593
  this.pendingControlRequests.set(requestId, { resolve, reject, timer });
13533
13594
  try {
13534
13595
  proc.stdin?.write(payload);
13596
+ this.recorder?.tapStdinWrite(payload);
13535
13597
  } catch (err) {
13536
13598
  clearTimeout(timer);
13537
13599
  this.pendingControlRequests.delete(requestId);
@@ -13587,10 +13649,14 @@ var SessionRunner = class {
13587
13649
  break;
13588
13650
  case "write-stdin":
13589
13651
  this.proc?.stdin?.write(effect.payload);
13652
+ this.recorder?.tapStdinWrite(effect.payload);
13590
13653
  break;
13591
- case "send-control-response-allow-with-input":
13592
- this.proc?.stdin?.write(encodeAllowWithInputControlResponse(effect.requestId, effect.updatedInput));
13654
+ case "send-control-response-allow-with-input": {
13655
+ const payload = encodeAllowWithInputControlResponse(effect.requestId, effect.updatedInput);
13656
+ this.proc?.stdin?.write(payload);
13657
+ this.recorder?.tapStdinWrite(payload);
13593
13658
  break;
13659
+ }
13594
13660
  case "persist-file":
13595
13661
  try {
13596
13662
  this.hooks.store.write(effect.file);
@@ -13620,6 +13686,13 @@ var SessionRunner = class {
13620
13686
  const proc = this.hooks.spawnOverride ? this.hooks.spawnOverride(ctx) : this.hooks.adapter.spawn(ctx);
13621
13687
  this.proc = proc;
13622
13688
  this.stdoutBuf = "";
13689
+ this.recorder = startRecorder({
13690
+ proc,
13691
+ sessionId: this.state.file.sessionId,
13692
+ dataDir: this.hooks.dataDir,
13693
+ env: this.hooks.recordEnv ?? process.env,
13694
+ now: this.hooks.now
13695
+ });
13623
13696
  proc.stdout?.on("data", (chunk) => {
13624
13697
  const { newBuf, lines } = splitStdoutChunk(this.stdoutBuf, chunk);
13625
13698
  this.stdoutBuf = newBuf;
@@ -13634,6 +13707,7 @@ var SessionRunner = class {
13634
13707
  });
13635
13708
  proc.on("exit", (code) => {
13636
13709
  this.proc = null;
13710
+ this.recorder = null;
13637
13711
  this.rejectAllPending(new Error("session gone"));
13638
13712
  this.input({ kind: "proc-exit", code });
13639
13713
  });
@@ -13760,7 +13834,8 @@ var SessionManager = class {
13760
13834
  now: this.deps.now,
13761
13835
  bufferCap: this.deps.bufferCap,
13762
13836
  // adapter 自己持有模型表 + 兜底逻辑(contains / opus-1M / [1m] 等),manager 不再 cache 转发
13763
- resolveContextWindow: (tool, modelId) => this.deps.getAdapter(tool).resolveContextWindow(modelId)
13837
+ resolveContextWindow: (tool, modelId) => this.deps.getAdapter(tool).resolveContextWindow(modelId),
13838
+ dataDir: this.deps.dataDir
13764
13839
  });
13765
13840
  return runner;
13766
13841
  }
@@ -13791,7 +13866,7 @@ var SessionManager = class {
13791
13866
  // ---- 命令方法:均返回 { response, broadcast[] },由 dispatcher 聚合 ----
13792
13867
  create(args) {
13793
13868
  try {
13794
- const stat = import_node_fs4.default.statSync(args.cwd);
13869
+ const stat = import_node_fs5.default.statSync(args.cwd);
13795
13870
  if (!stat.isDirectory()) throw new Error("not dir");
13796
13871
  } catch {
13797
13872
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `cwd not a directory: ${args.cwd}`);
@@ -13832,6 +13907,49 @@ var SessionManager = class {
13832
13907
  this.deps.store.write(updated);
13833
13908
  return { response: updated, broadcast: [] };
13834
13909
  }
13910
+ // sidebar 拖拽完成 → 整体重写 pinned root 的 pinSortOrder(按 orderedIds 下标 0,1,2...)
13911
+ // orderedIds 必须正好覆盖所有当前 pinned root(既不能漏,也不能含 unpinned id)
13912
+ reorderPins(args) {
13913
+ const all = this.deps.store.list();
13914
+ const currentPinnedIds = new Set(
13915
+ all.filter((f) => typeof f.pinnedAt === "number").map((f) => f.sessionId)
13916
+ );
13917
+ const incoming = new Set(args.orderedIds);
13918
+ if (incoming.size !== args.orderedIds.length) {
13919
+ throw new Error("reorderPins: orderedIds contains duplicate ids");
13920
+ }
13921
+ if (currentPinnedIds.size !== incoming.size || [...currentPinnedIds].some((id) => !incoming.has(id))) {
13922
+ throw new Error(
13923
+ "reorderPins: orderedIds must cover exactly the current pinned root set"
13924
+ );
13925
+ }
13926
+ const broadcast = [];
13927
+ const updatedFiles = [];
13928
+ args.orderedIds.forEach((sessionId, index) => {
13929
+ const runner = this.runners.get(sessionId);
13930
+ if (runner) {
13931
+ const { value, broadcast: b } = this.withCollector(() => {
13932
+ runner.input({
13933
+ kind: "command",
13934
+ command: { kind: "update", patch: { pinSortOrder: index } }
13935
+ });
13936
+ return runner.getState().file;
13937
+ });
13938
+ updatedFiles.push(value);
13939
+ broadcast.push(...b);
13940
+ } else {
13941
+ const existing = this.getFile(sessionId);
13942
+ const updated = {
13943
+ ...existing,
13944
+ pinSortOrder: index,
13945
+ updatedAt: nowIso2(this.deps)
13946
+ };
13947
+ this.deps.store.write(updated);
13948
+ updatedFiles.push(updated);
13949
+ }
13950
+ });
13951
+ return { response: { sessions: updatedFiles }, broadcast };
13952
+ }
13835
13953
  list() {
13836
13954
  return { response: { sessions: this.deps.store.list() }, broadcast: [] };
13837
13955
  }
@@ -14237,14 +14355,14 @@ var SessionManager = class {
14237
14355
  // src/tools/claude.ts
14238
14356
  var import_node_child_process = require("child_process");
14239
14357
  var import_node_child_process2 = require("child_process");
14240
- var import_node_fs6 = __toESM(require("fs"), 1);
14358
+ var import_node_fs7 = __toESM(require("fs"), 1);
14241
14359
  var import_node_os3 = __toESM(require("os"), 1);
14242
- var import_node_path5 = __toESM(require("path"), 1);
14360
+ var import_node_path6 = __toESM(require("path"), 1);
14243
14361
 
14244
14362
  // src/tools/claude-history.ts
14245
- var import_node_fs5 = __toESM(require("fs"), 1);
14363
+ var import_node_fs6 = __toESM(require("fs"), 1);
14246
14364
  var import_node_os2 = __toESM(require("os"), 1);
14247
- var import_node_path4 = __toESM(require("path"), 1);
14365
+ var import_node_path5 = __toESM(require("path"), 1);
14248
14366
 
14249
14367
  // ../node_modules/.pnpm/diff@7.0.0/node_modules/diff/lib/index.mjs
14250
14368
  function Diff() {
@@ -14348,11 +14466,11 @@ Diff.prototype = {
14348
14466
  }
14349
14467
  }
14350
14468
  },
14351
- addToPath: function addToPath(path17, added, removed, oldPosInc, options) {
14352
- var last = path17.lastComponent;
14469
+ addToPath: function addToPath(path18, added, removed, oldPosInc, options) {
14470
+ var last = path18.lastComponent;
14353
14471
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
14354
14472
  return {
14355
- oldPos: path17.oldPos + oldPosInc,
14473
+ oldPos: path18.oldPos + oldPosInc,
14356
14474
  lastComponent: {
14357
14475
  count: last.count + 1,
14358
14476
  added,
@@ -14362,7 +14480,7 @@ Diff.prototype = {
14362
14480
  };
14363
14481
  } else {
14364
14482
  return {
14365
- oldPos: path17.oldPos + oldPosInc,
14483
+ oldPos: path18.oldPos + oldPosInc,
14366
14484
  lastComponent: {
14367
14485
  count: 1,
14368
14486
  added,
@@ -15021,7 +15139,7 @@ function hashDirToCwd(hash) {
15021
15139
  }
15022
15140
  function safeStatMtime(p) {
15023
15141
  try {
15024
- return import_node_fs5.default.statSync(p).mtimeMs;
15142
+ return import_node_fs6.default.statSync(p).mtimeMs;
15025
15143
  } catch {
15026
15144
  return 0;
15027
15145
  }
@@ -15029,7 +15147,7 @@ function safeStatMtime(p) {
15029
15147
  function readJsonlLines(file) {
15030
15148
  let raw;
15031
15149
  try {
15032
- raw = import_node_fs5.default.readFileSync(file, "utf8");
15150
+ raw = import_node_fs6.default.readFileSync(file, "utf8");
15033
15151
  } catch (err) {
15034
15152
  if (err.code === "ENOENT") return [];
15035
15153
  throw err;
@@ -15221,10 +15339,10 @@ function attachmentToHistoryMessage(o, ts) {
15221
15339
  const memories = raw.map((m) => {
15222
15340
  if (!m || typeof m !== "object") return null;
15223
15341
  const rec = m;
15224
- const path17 = typeof rec.path === "string" ? rec.path : null;
15342
+ const path18 = typeof rec.path === "string" ? rec.path : null;
15225
15343
  const content = typeof rec.content === "string" ? rec.content : null;
15226
- if (!path17 || content == null) return null;
15227
- const entry = { path: path17, content };
15344
+ if (!path18 || content == null) return null;
15345
+ const entry = { path: path18, content };
15228
15346
  if (typeof rec.mtimeMs === "number") entry.mtimeMs = rec.mtimeMs;
15229
15347
  return entry;
15230
15348
  }).filter((m) => m !== null);
@@ -15260,8 +15378,8 @@ function attachmentDeferredToolsText(a) {
15260
15378
  function readBackupContent(fileHistoryRoot, toolSessionId, backupFileName) {
15261
15379
  if (backupFileName === null) return null;
15262
15380
  try {
15263
- return import_node_fs5.default.readFileSync(
15264
- import_node_path4.default.join(fileHistoryRoot, toolSessionId, backupFileName),
15381
+ return import_node_fs6.default.readFileSync(
15382
+ import_node_path5.default.join(fileHistoryRoot, toolSessionId, backupFileName),
15265
15383
  "utf8"
15266
15384
  );
15267
15385
  } catch {
@@ -15270,7 +15388,7 @@ function readBackupContent(fileHistoryRoot, toolSessionId, backupFileName) {
15270
15388
  }
15271
15389
  function readCurrentContent(filePath) {
15272
15390
  try {
15273
- return import_node_fs5.default.readFileSync(filePath, "utf8");
15391
+ return import_node_fs6.default.readFileSync(filePath, "utf8");
15274
15392
  } catch (err) {
15275
15393
  if (err.code === "ENOENT") return null;
15276
15394
  return null;
@@ -15282,14 +15400,14 @@ var ClaudeHistoryReader = class {
15282
15400
  // 每次 user 提交前 trackEdit 拷一份,作为 rewind 回退目标
15283
15401
  fileHistoryRoot;
15284
15402
  constructor(opts = {}) {
15285
- const base = opts.baseDir ?? import_node_path4.default.join(import_node_os2.default.homedir(), ".claude");
15286
- this.projectsRoot = import_node_path4.default.join(base, "projects");
15287
- this.fileHistoryRoot = import_node_path4.default.join(base, "file-history");
15403
+ const base = opts.baseDir ?? import_node_path5.default.join(import_node_os2.default.homedir(), ".claude");
15404
+ this.projectsRoot = import_node_path5.default.join(base, "projects");
15405
+ this.fileHistoryRoot = import_node_path5.default.join(base, "file-history");
15288
15406
  }
15289
15407
  async listProjects() {
15290
15408
  let entries;
15291
15409
  try {
15292
- entries = import_node_fs5.default.readdirSync(this.projectsRoot, { withFileTypes: true });
15410
+ entries = import_node_fs6.default.readdirSync(this.projectsRoot, { withFileTypes: true });
15293
15411
  } catch (err) {
15294
15412
  if (err.code === "ENOENT") return [];
15295
15413
  throw err;
@@ -15297,9 +15415,9 @@ var ClaudeHistoryReader = class {
15297
15415
  const out = [];
15298
15416
  for (const ent of entries) {
15299
15417
  if (!ent.isDirectory()) continue;
15300
- const dir = import_node_path4.default.join(this.projectsRoot, ent.name);
15301
- const files = import_node_fs5.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
15302
- const updatedAtMs = files.reduce((m, f) => Math.max(m, safeStatMtime(import_node_path4.default.join(dir, f))), 0);
15418
+ const dir = import_node_path5.default.join(this.projectsRoot, ent.name);
15419
+ const files = import_node_fs6.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
15420
+ const updatedAtMs = files.reduce((m, f) => Math.max(m, safeStatMtime(import_node_path5.default.join(dir, f))), 0);
15303
15421
  out.push({
15304
15422
  projectPath: hashDirToCwd(ent.name),
15305
15423
  hashDir: ent.name,
@@ -15311,17 +15429,17 @@ var ClaudeHistoryReader = class {
15311
15429
  return out;
15312
15430
  }
15313
15431
  async listSessions(args) {
15314
- const dir = import_node_path4.default.join(this.projectsRoot, cwdToHashDir(args.projectPath));
15432
+ const dir = import_node_path5.default.join(this.projectsRoot, cwdToHashDir(args.projectPath));
15315
15433
  let files;
15316
15434
  try {
15317
- files = import_node_fs5.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
15435
+ files = import_node_fs6.default.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
15318
15436
  } catch (err) {
15319
15437
  if (err.code === "ENOENT") return [];
15320
15438
  throw err;
15321
15439
  }
15322
15440
  const out = [];
15323
15441
  for (const f of files) {
15324
- const full = import_node_path4.default.join(dir, f);
15442
+ const full = import_node_path5.default.join(dir, f);
15325
15443
  const toolSessionId = f.slice(0, -".jsonl".length);
15326
15444
  const lines = readJsonlLines(full);
15327
15445
  let summary = "";
@@ -15376,7 +15494,7 @@ var ClaudeHistoryReader = class {
15376
15494
  return out;
15377
15495
  }
15378
15496
  async read(args) {
15379
- const file = import_node_path4.default.join(
15497
+ const file = import_node_path5.default.join(
15380
15498
  this.projectsRoot,
15381
15499
  cwdToHashDir(args.cwd),
15382
15500
  `${args.toolSessionId}.jsonl`
@@ -15409,7 +15527,7 @@ var ClaudeHistoryReader = class {
15409
15527
  // 独立目录路径:<projectsRoot>/<cwdHash>/<toolSessionId>/subagents/*.jsonl
15410
15528
  // 返回 null 表示目录不存在(调用方回退旧实现);返回空数组表示目录存在但无 jsonl
15411
15529
  listSubagentsFromDirectory(cwd, toolSessionId) {
15412
- const dir = import_node_path4.default.join(
15530
+ const dir = import_node_path5.default.join(
15413
15531
  this.projectsRoot,
15414
15532
  cwdToHashDir(cwd),
15415
15533
  toolSessionId,
@@ -15417,7 +15535,7 @@ var ClaudeHistoryReader = class {
15417
15535
  );
15418
15536
  let entries;
15419
15537
  try {
15420
- entries = import_node_fs5.default.readdirSync(dir, { withFileTypes: true });
15538
+ entries = import_node_fs6.default.readdirSync(dir, { withFileTypes: true });
15421
15539
  } catch (err) {
15422
15540
  if (err.code === "ENOENT") return null;
15423
15541
  return null;
@@ -15427,7 +15545,7 @@ var ClaudeHistoryReader = class {
15427
15545
  if (!e.isFile()) continue;
15428
15546
  if (!e.name.startsWith("agent-") || !e.name.endsWith(".jsonl")) continue;
15429
15547
  const subagentId = e.name.slice("agent-".length, -".jsonl".length);
15430
- const filePath = import_node_path4.default.join(dir, e.name);
15548
+ const filePath = import_node_path5.default.join(dir, e.name);
15431
15549
  const lines = readJsonlLines(filePath);
15432
15550
  let firstText = "";
15433
15551
  let messageCount = 0;
@@ -15444,7 +15562,7 @@ var ClaudeHistoryReader = class {
15444
15562
  return out;
15445
15563
  }
15446
15564
  listSubagentsFromMainJsonl(cwd, toolSessionId) {
15447
- const file = import_node_path4.default.join(
15565
+ const file = import_node_path5.default.join(
15448
15566
  this.projectsRoot,
15449
15567
  cwdToHashDir(cwd),
15450
15568
  `${toolSessionId}.jsonl`
@@ -15479,7 +15597,7 @@ var ClaudeHistoryReader = class {
15479
15597
  }
15480
15598
  // 独立文件路径:agent-<subagentId>.jsonl;文件不存在返回 null 让调用方回退旧实现
15481
15599
  readSubagentFromFile(cwd, toolSessionId, subagentId) {
15482
- const file = import_node_path4.default.join(
15600
+ const file = import_node_path5.default.join(
15483
15601
  this.projectsRoot,
15484
15602
  cwdToHashDir(cwd),
15485
15603
  toolSessionId,
@@ -15488,7 +15606,7 @@ var ClaudeHistoryReader = class {
15488
15606
  );
15489
15607
  let exists = false;
15490
15608
  try {
15491
- exists = import_node_fs5.default.statSync(file).isFile();
15609
+ exists = import_node_fs6.default.statSync(file).isFile();
15492
15610
  } catch {
15493
15611
  return null;
15494
15612
  }
@@ -15507,7 +15625,7 @@ var ClaudeHistoryReader = class {
15507
15625
  * "那一刻每个 tracked 文件对应的 backup 文件名"
15508
15626
  */
15509
15627
  readFileHistorySnapshots(args) {
15510
- const file = import_node_path4.default.join(
15628
+ const file = import_node_path5.default.join(
15511
15629
  this.projectsRoot,
15512
15630
  cwdToHashDir(args.cwd),
15513
15631
  `${args.toolSessionId}.jsonl`
@@ -15552,7 +15670,7 @@ var ClaudeHistoryReader = class {
15552
15670
  for (const [anchorId, target] of snapshots) {
15553
15671
  let hasAny = false;
15554
15672
  for (const [rawPath, backup] of Object.entries(target)) {
15555
- const absPath = import_node_path4.default.isAbsolute(rawPath) ? rawPath : import_node_path4.default.join(args.cwd, rawPath);
15673
+ const absPath = import_node_path5.default.isAbsolute(rawPath) ? rawPath : import_node_path5.default.join(args.cwd, rawPath);
15556
15674
  const backupContent = readBackupContent(
15557
15675
  this.fileHistoryRoot,
15558
15676
  args.toolSessionId,
@@ -15592,7 +15710,7 @@ var ClaudeHistoryReader = class {
15592
15710
  let totalInsertions = 0;
15593
15711
  let totalDeletions = 0;
15594
15712
  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);
15713
+ const absPath = import_node_path5.default.isAbsolute(rawPath) ? rawPath : import_node_path5.default.join(args.cwd, rawPath);
15596
15714
  const backupContent = readBackupContent(
15597
15715
  this.fileHistoryRoot,
15598
15716
  args.toolSessionId,
@@ -15639,7 +15757,7 @@ var ClaudeHistoryReader = class {
15639
15757
  };
15640
15758
  }
15641
15759
  readSubagentFromMainJsonl(cwd, toolSessionId, subagentId) {
15642
- const file = import_node_path4.default.join(
15760
+ const file = import_node_path5.default.join(
15643
15761
  this.projectsRoot,
15644
15762
  cwdToHashDir(cwd),
15645
15763
  `${toolSessionId}.jsonl`
@@ -15661,27 +15779,27 @@ var ClaudeHistoryReader = class {
15661
15779
  // src/tools/claude.ts
15662
15780
  function macOSDesktopCandidates(home) {
15663
15781
  return [
15664
- import_node_path5.default.join(home, "Applications", "Claude.app", "Contents", "Resources", "app.asar.unpacked", "node_modules", "@anthropic-ai", "claude-code", "cli.js"),
15782
+ import_node_path6.default.join(home, "Applications", "Claude.app", "Contents", "Resources", "app.asar.unpacked", "node_modules", "@anthropic-ai", "claude-code", "cli.js"),
15665
15783
  "/Applications/Claude.app/Contents/Resources/app.asar.unpacked/node_modules/@anthropic-ai/claude-code/cli.js"
15666
15784
  ];
15667
15785
  }
15668
15786
  function probeViaWhich() {
15669
15787
  try {
15670
15788
  const out = (0, import_node_child_process2.execFileSync)("which", ["claude"], { encoding: "utf8" }).trim();
15671
- if (out && import_node_fs6.default.existsSync(out)) return out;
15789
+ if (out && import_node_fs7.default.existsSync(out)) return out;
15672
15790
  } catch {
15673
15791
  }
15674
15792
  return null;
15675
15793
  }
15676
15794
  async function probeClaude(env = process.env, home = import_node_os3.default.homedir()) {
15677
- if (env.CLAUDE_BIN && import_node_fs6.default.existsSync(env.CLAUDE_BIN)) {
15795
+ if (env.CLAUDE_BIN && import_node_fs7.default.existsSync(env.CLAUDE_BIN)) {
15678
15796
  return { available: true, path: env.CLAUDE_BIN };
15679
15797
  }
15680
15798
  const w = probeViaWhich();
15681
15799
  if (w) return { available: true, path: w };
15682
15800
  if (process.platform === "darwin") {
15683
15801
  for (const candidate of macOSDesktopCandidates(home)) {
15684
- if (import_node_fs6.default.existsSync(candidate)) {
15802
+ if (import_node_fs7.default.existsSync(candidate)) {
15685
15803
  return { available: true, path: candidate };
15686
15804
  }
15687
15805
  }
@@ -15992,10 +16110,10 @@ function parseAttachment(obj) {
15992
16110
  const memories = raw.map((m) => {
15993
16111
  if (!m || typeof m !== "object") return null;
15994
16112
  const rec = m;
15995
- const path17 = typeof rec.path === "string" ? rec.path : null;
16113
+ const path18 = typeof rec.path === "string" ? rec.path : null;
15996
16114
  const content = typeof rec.content === "string" ? rec.content : null;
15997
- if (!path17 || content == null) return null;
15998
- const out = { path: path17, content };
16115
+ if (!path18 || content == null) return null;
16116
+ const out = { path: path18, content };
15999
16117
  if (typeof rec.mtimeMs === "number") out.mtimeMs = rec.mtimeMs;
16000
16118
  return out;
16001
16119
  }).filter((m) => m !== null);
@@ -16223,22 +16341,22 @@ var ClaudeAdapter = class {
16223
16341
  };
16224
16342
 
16225
16343
  // src/workspace/browser.ts
16226
- var import_node_fs7 = __toESM(require("fs"), 1);
16344
+ var import_node_fs8 = __toESM(require("fs"), 1);
16227
16345
  var import_node_os4 = __toESM(require("os"), 1);
16228
- var import_node_path6 = __toESM(require("path"), 1);
16346
+ var import_node_path7 = __toESM(require("path"), 1);
16229
16347
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
16230
16348
  function resolveInsideCwd(cwd, subpath) {
16231
- const absCwd = import_node_path6.default.resolve(cwd);
16232
- const joined = import_node_path6.default.resolve(absCwd, subpath ?? ".");
16233
- const rel = import_node_path6.default.relative(absCwd, joined);
16234
- if (rel.startsWith("..") || import_node_path6.default.isAbsolute(rel)) {
16349
+ const absCwd = import_node_path7.default.resolve(cwd);
16350
+ const joined = import_node_path7.default.resolve(absCwd, subpath ?? ".");
16351
+ const rel = import_node_path7.default.relative(absCwd, joined);
16352
+ if (rel.startsWith("..") || import_node_path7.default.isAbsolute(rel)) {
16235
16353
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `path escapes cwd: ${subpath}`);
16236
16354
  }
16237
16355
  return joined;
16238
16356
  }
16239
16357
  function ensureCwd(cwd) {
16240
16358
  try {
16241
- const stat = import_node_fs7.default.statSync(cwd);
16359
+ const stat = import_node_fs8.default.statSync(cwd);
16242
16360
  if (!stat.isDirectory()) {
16243
16361
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `not a directory: ${cwd}`);
16244
16362
  }
@@ -16252,7 +16370,7 @@ var WorkspaceBrowser = class {
16252
16370
  const cwd = args.cwd && args.cwd.length > 0 ? args.cwd : import_node_os4.default.homedir();
16253
16371
  ensureCwd(cwd);
16254
16372
  const full = resolveInsideCwd(cwd, args.path);
16255
- const dirents = import_node_fs7.default.readdirSync(full, { withFileTypes: true });
16373
+ const dirents = import_node_fs8.default.readdirSync(full, { withFileTypes: true });
16256
16374
  const entries = [];
16257
16375
  for (const d of dirents) {
16258
16376
  if (!args.showHidden && d.name.startsWith(".")) continue;
@@ -16262,7 +16380,7 @@ var WorkspaceBrowser = class {
16262
16380
  mtime: ""
16263
16381
  };
16264
16382
  try {
16265
- const st = import_node_fs7.default.statSync(import_node_path6.default.join(full, d.name));
16383
+ const st = import_node_fs8.default.statSync(import_node_path7.default.join(full, d.name));
16266
16384
  entry.mtime = new Date(st.mtimeMs).toISOString();
16267
16385
  if (d.isFile()) entry.size = st.size;
16268
16386
  } catch {
@@ -16278,14 +16396,14 @@ var WorkspaceBrowser = class {
16278
16396
  read(args) {
16279
16397
  ensureCwd(args.cwd);
16280
16398
  const full = resolveInsideCwd(args.cwd, args.path);
16281
- const st = import_node_fs7.default.statSync(full);
16399
+ const st = import_node_fs8.default.statSync(full);
16282
16400
  if (!st.isFile()) {
16283
16401
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `not a file: ${args.path}`);
16284
16402
  }
16285
16403
  if (st.size > MAX_FILE_BYTES) {
16286
16404
  throw new ClawdError(ERROR_CODES.FILE_TOO_LARGE, `file > ${MAX_FILE_BYTES} bytes`);
16287
16405
  }
16288
- const buf = import_node_fs7.default.readFileSync(full);
16406
+ const buf = import_node_fs8.default.readFileSync(full);
16289
16407
  const isBinary = buf.includes(0);
16290
16408
  if (isBinary) {
16291
16409
  return {
@@ -16307,9 +16425,9 @@ var WorkspaceBrowser = class {
16307
16425
  };
16308
16426
 
16309
16427
  // src/skills/scanner.ts
16310
- var import_node_fs8 = __toESM(require("fs"), 1);
16428
+ var import_node_fs9 = __toESM(require("fs"), 1);
16311
16429
  var import_node_os5 = __toESM(require("os"), 1);
16312
- var import_node_path7 = __toESM(require("path"), 1);
16430
+ var import_node_path8 = __toESM(require("path"), 1);
16313
16431
  function parseFrontmatter(content) {
16314
16432
  if (!content.startsWith("---")) return { name: "", description: "" };
16315
16433
  const end = content.indexOf("---", 3);
@@ -16345,7 +16463,7 @@ function parseFrontmatter(content) {
16345
16463
  }
16346
16464
  function isDirLikeSync(p) {
16347
16465
  try {
16348
- return import_node_fs8.default.statSync(p).isDirectory();
16466
+ return import_node_fs9.default.statSync(p).isDirectory();
16349
16467
  } catch {
16350
16468
  return false;
16351
16469
  }
@@ -16353,19 +16471,19 @@ function isDirLikeSync(p) {
16353
16471
  function scanSkillDir(dir, source, seen, out, pluginName) {
16354
16472
  let entries;
16355
16473
  try {
16356
- entries = import_node_fs8.default.readdirSync(dir, { withFileTypes: true });
16474
+ entries = import_node_fs9.default.readdirSync(dir, { withFileTypes: true });
16357
16475
  } catch {
16358
16476
  return;
16359
16477
  }
16360
16478
  for (const ent of entries) {
16361
- const entryPath = import_node_path7.default.join(dir, ent.name);
16479
+ const entryPath = import_node_path8.default.join(dir, ent.name);
16362
16480
  if (!ent.isDirectory() && !(ent.isSymbolicLink() && isDirLikeSync(entryPath))) continue;
16363
16481
  let content;
16364
16482
  try {
16365
- content = import_node_fs8.default.readFileSync(import_node_path7.default.join(entryPath, "SKILL.md"), "utf8");
16483
+ content = import_node_fs9.default.readFileSync(import_node_path8.default.join(entryPath, "SKILL.md"), "utf8");
16366
16484
  } catch {
16367
16485
  try {
16368
- content = import_node_fs8.default.readFileSync(import_node_path7.default.join(entryPath, "skill.md"), "utf8");
16486
+ content = import_node_fs9.default.readFileSync(import_node_path8.default.join(entryPath, "skill.md"), "utf8");
16369
16487
  } catch {
16370
16488
  continue;
16371
16489
  }
@@ -16383,26 +16501,26 @@ function scanSkillDir(dir, source, seen, out, pluginName) {
16383
16501
  function scanCommandDir(dir, source, seen, out, pluginName) {
16384
16502
  let entries;
16385
16503
  try {
16386
- entries = import_node_fs8.default.readdirSync(dir, { withFileTypes: true });
16504
+ entries = import_node_fs9.default.readdirSync(dir, { withFileTypes: true });
16387
16505
  } catch {
16388
16506
  return;
16389
16507
  }
16390
16508
  for (const ent of entries) {
16391
- const entryPath = import_node_path7.default.join(dir, ent.name);
16509
+ const entryPath = import_node_path8.default.join(dir, ent.name);
16392
16510
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync(entryPath)) {
16393
16511
  const ns = ent.name;
16394
16512
  let subEntries;
16395
16513
  try {
16396
- subEntries = import_node_fs8.default.readdirSync(entryPath, { withFileTypes: true });
16514
+ subEntries = import_node_fs9.default.readdirSync(entryPath, { withFileTypes: true });
16397
16515
  } catch {
16398
16516
  continue;
16399
16517
  }
16400
16518
  for (const se of subEntries) {
16401
16519
  if (!se.name.endsWith(".md")) continue;
16402
- const sePath = import_node_path7.default.join(entryPath, se.name);
16520
+ const sePath = import_node_path8.default.join(entryPath, se.name);
16403
16521
  let content;
16404
16522
  try {
16405
- content = import_node_fs8.default.readFileSync(sePath, "utf8");
16523
+ content = import_node_fs9.default.readFileSync(sePath, "utf8");
16406
16524
  } catch {
16407
16525
  continue;
16408
16526
  }
@@ -16419,7 +16537,7 @@ function scanCommandDir(dir, source, seen, out, pluginName) {
16419
16537
  } else if (ent.name.endsWith(".md")) {
16420
16538
  let content;
16421
16539
  try {
16422
- content = import_node_fs8.default.readFileSync(entryPath, "utf8");
16540
+ content = import_node_fs9.default.readFileSync(entryPath, "utf8");
16423
16541
  } catch {
16424
16542
  continue;
16425
16543
  }
@@ -16435,10 +16553,10 @@ function scanCommandDir(dir, source, seen, out, pluginName) {
16435
16553
  }
16436
16554
  }
16437
16555
  function readInstalledPlugins(home) {
16438
- const file = import_node_path7.default.join(home, ".claude", "plugins", "installed_plugins.json");
16556
+ const file = import_node_path8.default.join(home, ".claude", "plugins", "installed_plugins.json");
16439
16557
  let raw;
16440
16558
  try {
16441
- raw = import_node_fs8.default.readFileSync(file, "utf8");
16559
+ raw = import_node_fs9.default.readFileSync(file, "utf8");
16442
16560
  } catch {
16443
16561
  return [];
16444
16562
  }
@@ -16476,14 +16594,14 @@ var SkillsScanner = class {
16476
16594
  list(args) {
16477
16595
  const seen = /* @__PURE__ */ new Set();
16478
16596
  const out = [];
16479
- scanSkillDir(import_node_path7.default.join(this.home, ".claude", "skills"), "global", seen, out);
16480
- scanCommandDir(import_node_path7.default.join(this.home, ".claude", "commands"), "global", seen, out);
16481
- scanSkillDir(import_node_path7.default.join(args.cwd, ".claude", "skills"), "project", seen, out);
16482
- scanCommandDir(import_node_path7.default.join(args.cwd, ".claude", "commands"), "project", seen, out);
16597
+ scanSkillDir(import_node_path8.default.join(this.home, ".claude", "skills"), "global", seen, out);
16598
+ scanCommandDir(import_node_path8.default.join(this.home, ".claude", "commands"), "global", seen, out);
16599
+ scanSkillDir(import_node_path8.default.join(args.cwd, ".claude", "skills"), "project", seen, out);
16600
+ scanCommandDir(import_node_path8.default.join(args.cwd, ".claude", "commands"), "project", seen, out);
16483
16601
  const plugins = [...readInstalledPlugins(this.home), ...this.extraPluginRoots];
16484
16602
  for (const { name, root } of plugins) {
16485
- scanSkillDir(import_node_path7.default.join(root, "skills"), "plugin", seen, out, name);
16486
- scanCommandDir(import_node_path7.default.join(root, "commands"), "plugin", seen, out, name);
16603
+ scanSkillDir(import_node_path8.default.join(root, "skills"), "plugin", seen, out, name);
16604
+ scanCommandDir(import_node_path8.default.join(root, "commands"), "plugin", seen, out, name);
16487
16605
  }
16488
16606
  out.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
16489
16607
  return out;
@@ -16491,9 +16609,9 @@ var SkillsScanner = class {
16491
16609
  };
16492
16610
 
16493
16611
  // src/observer/session-observer.ts
16494
- var import_node_fs9 = __toESM(require("fs"), 1);
16612
+ var import_node_fs10 = __toESM(require("fs"), 1);
16495
16613
  var import_node_os6 = __toESM(require("os"), 1);
16496
- var import_node_path8 = __toESM(require("path"), 1);
16614
+ var import_node_path9 = __toESM(require("path"), 1);
16497
16615
  var SessionObserver = class {
16498
16616
  constructor(opts) {
16499
16617
  this.opts = opts;
@@ -16504,14 +16622,14 @@ var SessionObserver = class {
16504
16622
  watches = /* @__PURE__ */ new Map();
16505
16623
  resolveJsonlPath(cwd, toolSessionId, override) {
16506
16624
  if (override) return override;
16507
- return import_node_path8.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
16625
+ return import_node_path9.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
16508
16626
  }
16509
16627
  start(args) {
16510
16628
  this.stop(args.sessionId);
16511
16629
  const filePath = this.resolveJsonlPath(args.cwd, args.toolSessionId, args.jsonlPath);
16512
16630
  let size = 0;
16513
16631
  try {
16514
- size = import_node_fs9.default.statSync(filePath).size;
16632
+ size = import_node_fs10.default.statSync(filePath).size;
16515
16633
  } catch {
16516
16634
  }
16517
16635
  const w = {
@@ -16524,10 +16642,10 @@ var SessionObserver = class {
16524
16642
  adapter: args.adapter
16525
16643
  };
16526
16644
  try {
16527
- import_node_fs9.default.mkdirSync(import_node_path8.default.dirname(filePath), { recursive: true });
16645
+ import_node_fs10.default.mkdirSync(import_node_path9.default.dirname(filePath), { recursive: true });
16528
16646
  } catch {
16529
16647
  }
16530
- w.watcher = import_node_fs9.default.watch(import_node_path8.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
16648
+ w.watcher = import_node_fs10.default.watch(import_node_path9.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
16531
16649
  if (!changedName || !filePath.endsWith(changedName)) return;
16532
16650
  this.poll(w);
16533
16651
  });
@@ -16542,7 +16660,7 @@ var SessionObserver = class {
16542
16660
  // reducer.shallowEqMeta diff 让重复 patch 静默吞掉;异常静默吞,不阻塞 watcher 启动
16543
16661
  hydrateMetaTail(w, maxLines = 200) {
16544
16662
  try {
16545
- const raw = import_node_fs9.default.readFileSync(w.filePath, "utf8");
16663
+ const raw = import_node_fs10.default.readFileSync(w.filePath, "utf8");
16546
16664
  if (!raw) return;
16547
16665
  const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
16548
16666
  if (allLines.length === 0) return;
@@ -16563,7 +16681,7 @@ var SessionObserver = class {
16563
16681
  poll(w) {
16564
16682
  let size = 0;
16565
16683
  try {
16566
- size = import_node_fs9.default.statSync(w.filePath).size;
16684
+ size = import_node_fs10.default.statSync(w.filePath).size;
16567
16685
  } catch {
16568
16686
  return;
16569
16687
  }
@@ -16572,11 +16690,11 @@ var SessionObserver = class {
16572
16690
  w.buf = "";
16573
16691
  }
16574
16692
  if (size === w.lastSize) return;
16575
- const fd = import_node_fs9.default.openSync(w.filePath, "r");
16693
+ const fd = import_node_fs10.default.openSync(w.filePath, "r");
16576
16694
  try {
16577
16695
  const len = size - w.lastSize;
16578
16696
  const buf = Buffer.alloc(len);
16579
- import_node_fs9.default.readSync(fd, buf, 0, len, w.lastSize);
16697
+ import_node_fs10.default.readSync(fd, buf, 0, len, w.lastSize);
16580
16698
  w.lastSize = size;
16581
16699
  w.buf += buf.toString("utf8");
16582
16700
  let newlineIndex;
@@ -16590,7 +16708,7 @@ var SessionObserver = class {
16590
16708
  this.maybeReportUserMessage(w.sessionId, line);
16591
16709
  }
16592
16710
  } finally {
16593
- import_node_fs9.default.closeSync(fd);
16711
+ import_node_fs10.default.closeSync(fd);
16594
16712
  }
16595
16713
  }
16596
16714
  // 解析 JSONL 单行:仅当是主链 user 文本行(非 sidechain / 非 sub-agent / message.role='user'
@@ -17005,10 +17123,10 @@ function isLocalhost(addr) {
17005
17123
  }
17006
17124
 
17007
17125
  // src/discovery/state-file.ts
17008
- var import_node_fs10 = __toESM(require("fs"), 1);
17009
- var import_node_path9 = __toESM(require("path"), 1);
17126
+ var import_node_fs11 = __toESM(require("fs"), 1);
17127
+ var import_node_path10 = __toESM(require("path"), 1);
17010
17128
  function defaultStateFilePath(dataDir) {
17011
- return import_node_path9.default.join(dataDir, "state.json");
17129
+ return import_node_path10.default.join(dataDir, "state.json");
17012
17130
  }
17013
17131
  function isPidAlive(pid) {
17014
17132
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -17030,7 +17148,7 @@ var StateFileManager = class {
17030
17148
  }
17031
17149
  read() {
17032
17150
  try {
17033
- const raw = import_node_fs10.default.readFileSync(this.file, "utf8");
17151
+ const raw = import_node_fs11.default.readFileSync(this.file, "utf8");
17034
17152
  const parsed = JSON.parse(raw);
17035
17153
  return parsed;
17036
17154
  } catch {
@@ -17044,34 +17162,34 @@ var StateFileManager = class {
17044
17162
  return { status: "stale", existing };
17045
17163
  }
17046
17164
  write(state) {
17047
- import_node_fs10.default.mkdirSync(import_node_path9.default.dirname(this.file), { recursive: true });
17165
+ import_node_fs11.default.mkdirSync(import_node_path10.default.dirname(this.file), { recursive: true });
17048
17166
  const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
17049
- import_node_fs10.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
17050
- import_node_fs10.default.renameSync(tmp, this.file);
17167
+ import_node_fs11.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
17168
+ import_node_fs11.default.renameSync(tmp, this.file);
17051
17169
  if (process.platform !== "win32") {
17052
17170
  try {
17053
- import_node_fs10.default.chmodSync(this.file, 384);
17171
+ import_node_fs11.default.chmodSync(this.file, 384);
17054
17172
  } catch {
17055
17173
  }
17056
17174
  }
17057
17175
  }
17058
17176
  delete() {
17059
17177
  try {
17060
- import_node_fs10.default.unlinkSync(this.file);
17178
+ import_node_fs11.default.unlinkSync(this.file);
17061
17179
  } catch {
17062
17180
  }
17063
17181
  }
17064
17182
  };
17065
17183
 
17066
17184
  // src/tunnel/tunnel-manager.ts
17067
- var import_node_fs13 = __toESM(require("fs"), 1);
17068
- var import_node_path12 = __toESM(require("path"), 1);
17185
+ var import_node_fs14 = __toESM(require("fs"), 1);
17186
+ var import_node_path13 = __toESM(require("path"), 1);
17069
17187
  var import_node_crypto3 = __toESM(require("crypto"), 1);
17070
17188
  var import_node_child_process4 = require("child_process");
17071
17189
 
17072
17190
  // src/tunnel/tunnel-store.ts
17073
- var import_node_fs11 = __toESM(require("fs"), 1);
17074
- var import_node_path10 = __toESM(require("path"), 1);
17191
+ var import_node_fs12 = __toESM(require("fs"), 1);
17192
+ var import_node_path11 = __toESM(require("path"), 1);
17075
17193
  var TunnelStore = class {
17076
17194
  constructor(filePath) {
17077
17195
  this.filePath = filePath;
@@ -17079,7 +17197,7 @@ var TunnelStore = class {
17079
17197
  filePath;
17080
17198
  async get() {
17081
17199
  try {
17082
- const raw = await import_node_fs11.default.promises.readFile(this.filePath, "utf8");
17200
+ const raw = await import_node_fs12.default.promises.readFile(this.filePath, "utf8");
17083
17201
  const obj = JSON.parse(raw);
17084
17202
  if (!isPersistedTunnel(obj)) return null;
17085
17203
  return obj;
@@ -17090,22 +17208,22 @@ var TunnelStore = class {
17090
17208
  }
17091
17209
  }
17092
17210
  async set(v) {
17093
- const dir = import_node_path10.default.dirname(this.filePath);
17094
- await import_node_fs11.default.promises.mkdir(dir, { recursive: true });
17211
+ const dir = import_node_path11.default.dirname(this.filePath);
17212
+ await import_node_fs12.default.promises.mkdir(dir, { recursive: true });
17095
17213
  const data = JSON.stringify(v, null, 2);
17096
17214
  const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
17097
- await import_node_fs11.default.promises.writeFile(tmp, data, { mode: 384 });
17215
+ await import_node_fs12.default.promises.writeFile(tmp, data, { mode: 384 });
17098
17216
  if (process.platform !== "win32") {
17099
17217
  try {
17100
- await import_node_fs11.default.promises.chmod(tmp, 384);
17218
+ await import_node_fs12.default.promises.chmod(tmp, 384);
17101
17219
  } catch {
17102
17220
  }
17103
17221
  }
17104
- await import_node_fs11.default.promises.rename(tmp, this.filePath);
17222
+ await import_node_fs12.default.promises.rename(tmp, this.filePath);
17105
17223
  }
17106
17224
  async clear() {
17107
17225
  try {
17108
- await import_node_fs11.default.promises.unlink(this.filePath);
17226
+ await import_node_fs12.default.promises.unlink(this.filePath);
17109
17227
  } catch (err) {
17110
17228
  const code = err?.code;
17111
17229
  if (code !== "ENOENT") throw err;
@@ -17200,9 +17318,9 @@ function escape(v) {
17200
17318
  }
17201
17319
 
17202
17320
  // src/tunnel/frpc-binary.ts
17203
- var import_node_fs12 = __toESM(require("fs"), 1);
17321
+ var import_node_fs13 = __toESM(require("fs"), 1);
17204
17322
  var import_node_os7 = __toESM(require("os"), 1);
17205
- var import_node_path11 = __toESM(require("path"), 1);
17323
+ var import_node_path12 = __toESM(require("path"), 1);
17206
17324
  var import_node_child_process3 = require("child_process");
17207
17325
  var import_node_stream = require("stream");
17208
17326
  var import_promises = require("stream/promises");
@@ -17234,20 +17352,20 @@ function frpcDownloadUrl(version2, p) {
17234
17352
  }
17235
17353
  async function ensureFrpcBinary(opts) {
17236
17354
  if (opts.override) {
17237
- if (!import_node_fs12.default.existsSync(opts.override)) {
17355
+ if (!import_node_fs13.default.existsSync(opts.override)) {
17238
17356
  throw new Error(`frpc binary not found at override path: ${opts.override}`);
17239
17357
  }
17240
17358
  return opts.override;
17241
17359
  }
17242
17360
  const version2 = opts.version ?? FRPC_VERSION;
17243
17361
  const platform = opts.platform ?? detectPlatform();
17244
- const binDir = import_node_path11.default.join(opts.dataDir, "bin");
17245
- import_node_fs12.default.mkdirSync(binDir, { recursive: true });
17362
+ const binDir = import_node_path12.default.join(opts.dataDir, "bin");
17363
+ import_node_fs13.default.mkdirSync(binDir, { recursive: true });
17246
17364
  cleanupStaleArtifacts(binDir);
17247
- const stableBin = import_node_path11.default.join(binDir, "frpc");
17248
- if (import_node_fs12.default.existsSync(stableBin)) return stableBin;
17365
+ const stableBin = import_node_path12.default.join(binDir, "frpc");
17366
+ if (import_node_fs13.default.existsSync(stableBin)) return stableBin;
17249
17367
  const partialBin = `${stableBin}.partial`;
17250
- const tarballPath = import_node_path11.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
17368
+ const tarballPath = import_node_path12.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
17251
17369
  try {
17252
17370
  const url = frpcDownloadUrl(version2, platform);
17253
17371
  await downloadToFile(url, tarballPath, opts.fetchImpl);
@@ -17256,8 +17374,8 @@ async function ensureFrpcBinary(opts) {
17256
17374
  } else {
17257
17375
  await extractFrpcFromTarball(tarballPath, binDir, version2, platform, partialBin);
17258
17376
  }
17259
- import_node_fs12.default.chmodSync(partialBin, 493);
17260
- import_node_fs12.default.renameSync(partialBin, stableBin);
17377
+ import_node_fs13.default.chmodSync(partialBin, 493);
17378
+ import_node_fs13.default.renameSync(partialBin, stableBin);
17261
17379
  } finally {
17262
17380
  safeUnlink(tarballPath);
17263
17381
  safeUnlink(partialBin);
@@ -17267,15 +17385,15 @@ async function ensureFrpcBinary(opts) {
17267
17385
  function cleanupStaleArtifacts(binDir) {
17268
17386
  let entries;
17269
17387
  try {
17270
- entries = import_node_fs12.default.readdirSync(binDir);
17388
+ entries = import_node_fs13.default.readdirSync(binDir);
17271
17389
  } catch {
17272
17390
  return;
17273
17391
  }
17274
17392
  for (const name of entries) {
17275
17393
  if (name.endsWith(".partial") || name.startsWith("extract-")) {
17276
- const full = import_node_path11.default.join(binDir, name);
17394
+ const full = import_node_path12.default.join(binDir, name);
17277
17395
  try {
17278
- import_node_fs12.default.rmSync(full, { recursive: true, force: true });
17396
+ import_node_fs13.default.rmSync(full, { recursive: true, force: true });
17279
17397
  } catch {
17280
17398
  }
17281
17399
  }
@@ -17283,7 +17401,7 @@ function cleanupStaleArtifacts(binDir) {
17283
17401
  }
17284
17402
  function safeUnlink(p) {
17285
17403
  try {
17286
- import_node_fs12.default.unlinkSync(p);
17404
+ import_node_fs13.default.unlinkSync(p);
17287
17405
  } catch {
17288
17406
  }
17289
17407
  }
@@ -17294,13 +17412,13 @@ async function downloadToFile(url, dest, fetchImpl) {
17294
17412
  if (!res.ok || !res.body) {
17295
17413
  throw new Error(`download failed: ${res.status} ${res.statusText}`);
17296
17414
  }
17297
- const out = import_node_fs12.default.createWriteStream(dest);
17415
+ const out = import_node_fs13.default.createWriteStream(dest);
17298
17416
  const nodeStream = import_node_stream.Readable.fromWeb(res.body);
17299
17417
  await (0, import_promises.pipeline)(nodeStream, out);
17300
17418
  }
17301
17419
  async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
17302
- const work = import_node_path11.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
17303
- import_node_fs12.default.mkdirSync(work, { recursive: true });
17420
+ const work = import_node_path12.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
17421
+ import_node_fs13.default.mkdirSync(work, { recursive: true });
17304
17422
  try {
17305
17423
  await new Promise((resolve, reject) => {
17306
17424
  const proc = (0, import_node_child_process3.spawn)("tar", ["xzf", tarball, "-C", work], { stdio: "pipe" });
@@ -17308,13 +17426,13 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
17308
17426
  proc.on("exit", (code) => code === 0 ? resolve() : reject(new Error(`tar exited ${code}`)));
17309
17427
  });
17310
17428
  const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
17311
- const src = import_node_path11.default.join(work, dirName, "frpc");
17312
- if (!import_node_fs12.default.existsSync(src)) {
17429
+ const src = import_node_path12.default.join(work, dirName, "frpc");
17430
+ if (!import_node_fs13.default.existsSync(src)) {
17313
17431
  throw new Error(`frpc not found inside tarball at ${src}`);
17314
17432
  }
17315
- import_node_fs12.default.copyFileSync(src, destBin);
17433
+ import_node_fs13.default.copyFileSync(src, destBin);
17316
17434
  } finally {
17317
- import_node_fs12.default.rmSync(work, { recursive: true, force: true });
17435
+ import_node_fs13.default.rmSync(work, { recursive: true, force: true });
17318
17436
  }
17319
17437
  }
17320
17438
 
@@ -17323,7 +17441,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
17323
17441
  var TunnelManager = class {
17324
17442
  constructor(deps) {
17325
17443
  this.deps = deps;
17326
- this.store = deps.store ?? new TunnelStore(import_node_path12.default.join(deps.dataDir, "tunnel.json"));
17444
+ this.store = deps.store ?? new TunnelStore(import_node_path13.default.join(deps.dataDir, "tunnel.json"));
17327
17445
  this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
17328
17446
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
17329
17447
  }
@@ -17440,7 +17558,7 @@ var TunnelManager = class {
17440
17558
  dataDir: this.deps.dataDir,
17441
17559
  override: this.deps.frpcBinaryOverride ?? void 0
17442
17560
  });
17443
- const tomlPath = import_node_path12.default.join(this.deps.dataDir, "frpc.toml");
17561
+ const tomlPath = import_node_path13.default.join(this.deps.dataDir, "frpc.toml");
17444
17562
  const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto3.default.randomBytes(3).toString("hex")}`;
17445
17563
  const toml = buildFrpcToml({
17446
17564
  serverAddr: t.frpsHost,
@@ -17451,12 +17569,12 @@ var TunnelManager = class {
17451
17569
  localPort,
17452
17570
  logLevel: "info"
17453
17571
  });
17454
- await import_node_fs13.default.promises.writeFile(tomlPath, toml, { mode: 384 });
17572
+ await import_node_fs14.default.promises.writeFile(tomlPath, toml, { mode: 384 });
17455
17573
  const proc = (this.deps.spawnImpl ?? import_node_child_process4.spawn)(frpcBin, ["-c", tomlPath], {
17456
17574
  stdio: ["ignore", "pipe", "pipe"]
17457
17575
  });
17458
- const logFilePath = import_node_path12.default.join(this.deps.dataDir, "frpc.log");
17459
- const logStream = import_node_fs13.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
17576
+ const logFilePath = import_node_path13.default.join(this.deps.dataDir, "frpc.log");
17577
+ const logStream = import_node_fs14.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
17460
17578
  logStream.on("error", () => {
17461
17579
  });
17462
17580
  const tee = (chunk) => {
@@ -17548,12 +17666,12 @@ function deriveStableDeviceKey(opts = {}) {
17548
17666
  }
17549
17667
 
17550
17668
  // src/auth-store.ts
17551
- var import_node_fs14 = __toESM(require("fs"), 1);
17552
- var import_node_path13 = __toESM(require("path"), 1);
17669
+ var import_node_fs15 = __toESM(require("fs"), 1);
17670
+ var import_node_path14 = __toESM(require("path"), 1);
17553
17671
  var import_node_crypto5 = __toESM(require("crypto"), 1);
17554
17672
  var AUTH_FILE_NAME = "auth.json";
17555
17673
  function authFilePath(dataDir) {
17556
- return import_node_path13.default.join(dataDir, AUTH_FILE_NAME);
17674
+ return import_node_path14.default.join(dataDir, AUTH_FILE_NAME);
17557
17675
  }
17558
17676
  function loadOrCreateAuthToken(opts) {
17559
17677
  const file = authFilePath(opts.dataDir);
@@ -17569,7 +17687,7 @@ function defaultGenerate() {
17569
17687
  }
17570
17688
  function readAuthFile(file) {
17571
17689
  try {
17572
- const raw = import_node_fs14.default.readFileSync(file, "utf8");
17690
+ const raw = import_node_fs15.default.readFileSync(file, "utf8");
17573
17691
  const parsed = JSON.parse(raw);
17574
17692
  if (typeof parsed?.token === "string" && parsed.token.length > 0) {
17575
17693
  return {
@@ -17585,20 +17703,20 @@ function readAuthFile(file) {
17585
17703
  }
17586
17704
  }
17587
17705
  function writeAuthFile(file, content) {
17588
- import_node_fs14.default.mkdirSync(import_node_path13.default.dirname(file), { recursive: true });
17589
- import_node_fs14.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
17706
+ import_node_fs15.default.mkdirSync(import_node_path14.default.dirname(file), { recursive: true });
17707
+ import_node_fs15.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
17590
17708
  try {
17591
- import_node_fs14.default.chmodSync(file, 384);
17709
+ import_node_fs15.default.chmodSync(file, 384);
17592
17710
  } catch {
17593
17711
  }
17594
17712
  }
17595
17713
 
17596
17714
  // src/session/fork.ts
17597
- var import_node_fs15 = __toESM(require("fs"), 1);
17715
+ var import_node_fs16 = __toESM(require("fs"), 1);
17598
17716
  var import_node_os9 = __toESM(require("os"), 1);
17599
- var import_node_path14 = __toESM(require("path"), 1);
17717
+ var import_node_path15 = __toESM(require("path"), 1);
17600
17718
  function readJsonlEntries(file) {
17601
- const raw = import_node_fs15.default.readFileSync(file, "utf8");
17719
+ const raw = import_node_fs16.default.readFileSync(file, "utf8");
17602
17720
  const out = [];
17603
17721
  for (const line of raw.split("\n")) {
17604
17722
  const t = line.trim();
@@ -17611,10 +17729,10 @@ function readJsonlEntries(file) {
17611
17729
  return out;
17612
17730
  }
17613
17731
  function forkSession(input) {
17614
- const baseDir = input.baseDir ?? import_node_path14.default.join(import_node_os9.default.homedir(), ".claude");
17615
- const projectDir = import_node_path14.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
17616
- const sourceFile = import_node_path14.default.join(projectDir, `${input.toolSessionId}.jsonl`);
17617
- if (!import_node_fs15.default.existsSync(sourceFile)) {
17732
+ const baseDir = input.baseDir ?? import_node_path15.default.join(import_node_os9.default.homedir(), ".claude");
17733
+ const projectDir = import_node_path15.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
17734
+ const sourceFile = import_node_path15.default.join(projectDir, `${input.toolSessionId}.jsonl`);
17735
+ if (!import_node_fs16.default.existsSync(sourceFile)) {
17618
17736
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
17619
17737
  }
17620
17738
  const entries = readJsonlEntries(sourceFile);
@@ -17644,9 +17762,9 @@ function forkSession(input) {
17644
17762
  }
17645
17763
  forkedLines.push(JSON.stringify(forked));
17646
17764
  }
17647
- const forkedFilePath = import_node_path14.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
17648
- import_node_fs15.default.mkdirSync(projectDir, { recursive: true });
17649
- import_node_fs15.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
17765
+ const forkedFilePath = import_node_path15.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
17766
+ import_node_fs16.default.mkdirSync(projectDir, { recursive: true });
17767
+ import_node_fs16.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
17650
17768
  return { forkedToolSessionId, forkedFilePath };
17651
17769
  }
17652
17770
 
@@ -17767,6 +17885,11 @@ function buildSessionHandlers(deps) {
17767
17885
  const { response, broadcast } = manager.pin(args);
17768
17886
  return { response: { type: "session:info", ...response }, broadcast };
17769
17887
  };
17888
+ const reorderPins = async (frame) => {
17889
+ const args = SessionReorderPinsArgs.parse(frame);
17890
+ const { response, broadcast } = manager.reorderPins(args);
17891
+ return { response: { type: "session:reorderPins", ...response }, broadcast };
17892
+ };
17770
17893
  const answerQuestion = async (frame) => {
17771
17894
  const args = AnswerQuestionArgs.parse(frame);
17772
17895
  const { response, broadcast } = manager.answerQuestion(args);
@@ -17792,6 +17915,7 @@ function buildSessionHandlers(deps) {
17792
17915
  "session:subscribe": subscribe,
17793
17916
  "session:unsubscribe": unsubscribe,
17794
17917
  "session:pin": pin,
17918
+ "session:reorderPins": reorderPins,
17795
17919
  "session:answerQuestion": answerQuestion
17796
17920
  };
17797
17921
  }
@@ -17902,9 +18026,9 @@ function buildWorkspaceHandlers(deps) {
17902
18026
 
17903
18027
  // src/workspace/git.ts
17904
18028
  var import_node_child_process5 = require("child_process");
17905
- var import_node_fs16 = __toESM(require("fs"), 1);
18029
+ var import_node_fs17 = __toESM(require("fs"), 1);
17906
18030
  var import_node_os10 = __toESM(require("os"), 1);
17907
- var import_node_path15 = __toESM(require("path"), 1);
18031
+ var import_node_path16 = __toESM(require("path"), 1);
17908
18032
  var import_node_util = require("util");
17909
18033
  var pexec = (0, import_node_util.promisify)(import_node_child_process5.execFile);
17910
18034
  function formatChildProcessError(err) {
@@ -17919,9 +18043,9 @@ function formatChildProcessError(err) {
17919
18043
  return e.message ?? "unknown error";
17920
18044
  }
17921
18045
  function normalizePath(p) {
17922
- const resolved = import_node_path15.default.resolve(p);
18046
+ const resolved = import_node_path16.default.resolve(p);
17923
18047
  try {
17924
- return import_node_fs16.default.realpathSync(resolved);
18048
+ return import_node_fs17.default.realpathSync(resolved);
17925
18049
  } catch {
17926
18050
  return resolved;
17927
18051
  }
@@ -18022,13 +18146,13 @@ function flattenToDirName(branch) {
18022
18146
  }
18023
18147
  function encodeClaudeProjectDir(absPath) {
18024
18148
  if (!absPath || typeof absPath !== "string") return "";
18025
- let canonical = import_node_path15.default.resolve(absPath);
18149
+ let canonical = import_node_path16.default.resolve(absPath);
18026
18150
  try {
18027
- canonical = import_node_fs16.default.realpathSync(canonical);
18151
+ canonical = import_node_fs17.default.realpathSync(canonical);
18028
18152
  } catch {
18029
18153
  try {
18030
- const parent = import_node_fs16.default.realpathSync(import_node_path15.default.dirname(canonical));
18031
- canonical = import_node_path15.default.join(parent, import_node_path15.default.basename(canonical));
18154
+ const parent = import_node_fs17.default.realpathSync(import_node_path16.default.dirname(canonical));
18155
+ canonical = import_node_path16.default.join(parent, import_node_path16.default.basename(canonical));
18032
18156
  } catch {
18033
18157
  }
18034
18158
  }
@@ -18052,11 +18176,11 @@ async function createWorktree(input) {
18052
18176
  if (!isGitRoot) {
18053
18177
  throw new Error(`\u76EE\u5F55 ${cwd} \u4E0D\u662F git repo \u6839`);
18054
18178
  }
18055
- const parent = import_node_path15.default.dirname(import_node_path15.default.resolve(cwd));
18056
- if (parent === "/" || parent === import_node_path15.default.resolve(cwd)) {
18179
+ const parent = import_node_path16.default.dirname(import_node_path16.default.resolve(cwd));
18180
+ if (parent === "/" || parent === import_node_path16.default.resolve(cwd)) {
18057
18181
  throw new Error("repo \u5728\u78C1\u76D8\u6839\u76EE\u5F55\uFF0C\u65E0\u6CD5\u5728\u540C\u7EA7\u521B\u5EFA worktree");
18058
18182
  }
18059
- const worktreeRoot = import_node_path15.default.join(parent, dirName);
18183
+ const worktreeRoot = import_node_path16.default.join(parent, dirName);
18060
18184
  try {
18061
18185
  await pexec("git", ["-C", cwd, "rev-parse", "--verify", `refs/heads/${baseBranch}`], {
18062
18186
  timeout: 3e3
@@ -18073,7 +18197,7 @@ async function createWorktree(input) {
18073
18197
  const msg = err.message;
18074
18198
  if (msg.startsWith("\u5206\u652F ")) throw err;
18075
18199
  }
18076
- if (import_node_fs16.default.existsSync(worktreeRoot)) {
18200
+ if (import_node_fs17.default.existsSync(worktreeRoot)) {
18077
18201
  throw new Error(`\u76EE\u5F55 ${worktreeRoot} \u5DF2\u5B58\u5728\uFF0C\u8BF7\u6362\u4E00\u4E2A label \u6216\u6E05\u7406\u540E\u91CD\u8BD5`);
18078
18202
  }
18079
18203
  try {
@@ -18101,8 +18225,8 @@ async function removeWorktree(input) {
18101
18225
  );
18102
18226
  const gitCommonDir = stdout.trim();
18103
18227
  if (!gitCommonDir) throw new Error("empty git-common-dir");
18104
- const absGitCommon = import_node_path15.default.isAbsolute(gitCommonDir) ? gitCommonDir : import_node_path15.default.resolve(worktreeRoot, gitCommonDir);
18105
- repoRoot = import_node_path15.default.dirname(absGitCommon);
18228
+ const absGitCommon = import_node_path16.default.isAbsolute(gitCommonDir) ? gitCommonDir : import_node_path16.default.resolve(worktreeRoot, gitCommonDir);
18229
+ repoRoot = import_node_path16.default.dirname(absGitCommon);
18106
18230
  } catch {
18107
18231
  repoRoot = null;
18108
18232
  }
@@ -18114,7 +18238,7 @@ async function removeWorktree(input) {
18114
18238
  } catch (err) {
18115
18239
  const stderr = err.stderr ?? "";
18116
18240
  const lower = stderr.toLowerCase();
18117
- const vanished = lower.includes("not a working tree") || lower.includes("is not a working tree") || !import_node_fs16.default.existsSync(worktreeRoot);
18241
+ const vanished = lower.includes("not a working tree") || lower.includes("is not a working tree") || !import_node_fs17.default.existsSync(worktreeRoot);
18118
18242
  if (!vanished) {
18119
18243
  throw new Error(`\u6E05\u7406 worktree \u5931\u8D25\uFF1A${formatChildProcessError(err)}`);
18120
18244
  }
@@ -18133,10 +18257,10 @@ async function removeWorktree(input) {
18133
18257
  try {
18134
18258
  const encoded = encodeClaudeProjectDir(worktreeRoot);
18135
18259
  if (encoded) {
18136
- const projectsRoot = import_node_path15.default.join(import_node_os10.default.homedir(), ".claude", "projects");
18137
- const target = import_node_path15.default.resolve(projectsRoot, encoded);
18138
- if (target.startsWith(projectsRoot + import_node_path15.default.sep) && target !== projectsRoot) {
18139
- import_node_fs16.default.rmSync(target, { recursive: true, force: true });
18260
+ const projectsRoot = import_node_path16.default.join(import_node_os10.default.homedir(), ".claude", "projects");
18261
+ const target = import_node_path16.default.resolve(projectsRoot, encoded);
18262
+ if (target.startsWith(projectsRoot + import_node_path16.default.sep) && target !== projectsRoot) {
18263
+ import_node_fs17.default.rmSync(target, { recursive: true, force: true });
18140
18264
  }
18141
18265
  }
18142
18266
  } catch {
@@ -18263,7 +18387,7 @@ function buildMethodHandlers(deps) {
18263
18387
  async function startDaemon(config) {
18264
18388
  const logger = createLogger({
18265
18389
  level: config.logLevel,
18266
- file: import_node_path16.default.join(config.dataDir, "clawd.log")
18390
+ file: import_node_path17.default.join(config.dataDir, "clawd.log")
18267
18391
  });
18268
18392
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
18269
18393
  const stateMgr = new StateFileManager({ dataDir: config.dataDir });
@@ -18303,6 +18427,7 @@ async function startDaemon(config) {
18303
18427
  logger,
18304
18428
  getAdapter,
18305
18429
  historyReader: history,
18430
+ dataDir: config.dataDir,
18306
18431
  broadcastFrame: (frame, target) => {
18307
18432
  if (target === "all") {
18308
18433
  transport?.broadcastAll(frame);
@@ -18425,8 +18550,8 @@ async function startDaemon(config) {
18425
18550
  const lines = [
18426
18551
  `Tunnel: ${r.url}`,
18427
18552
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
18428
- `Frpc config: ${import_node_path16.default.join(config.dataDir, "frpc.toml")}`,
18429
- `Frpc log: ${import_node_path16.default.join(config.dataDir, "frpc.log")}`
18553
+ `Frpc config: ${import_node_path17.default.join(config.dataDir, "frpc.toml")}`,
18554
+ `Frpc log: ${import_node_path17.default.join(config.dataDir, "frpc.log")}`
18430
18555
  ];
18431
18556
  const width = Math.max(...lines.map((l) => l.length));
18432
18557
  const bar = "\u2550".repeat(width + 4);
@@ -18439,8 +18564,8 @@ ${bar}
18439
18564
 
18440
18565
  `);
18441
18566
  try {
18442
- const connectPath = import_node_path16.default.join(config.dataDir, "connect.txt");
18443
- import_node_fs17.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
18567
+ const connectPath = import_node_path17.default.join(config.dataDir, "connect.txt");
18568
+ import_node_fs18.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
18444
18569
  } catch {
18445
18570
  }
18446
18571
  } catch (err) {