@clawos-dev/clawd 0.2.8 → 0.2.9

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 +180 -99
  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 path16 = req.path;
297
- _req.url = typeof path16 === "string" ? path16 : req.url ? req.url.path || req.url : void 0;
296
+ const path17 = req.path;
297
+ _req.url = typeof path17 === "string" ? path17 : 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(path16) {
462
+ function parsePath(path17) {
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 < path16.length; i++) {
469
- const char = path16[i];
468
+ for (let i = 0; i < path17.length; i++) {
469
+ const char = path17[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 path16 of paths) {
601
- const parts = parsePath(path16);
600
+ for (const path17 of paths) {
601
+ const parts = parsePath(path17);
602
602
  if (parts.includes("*")) {
603
- redactWildcardPath(obj, parts, censor, path16, remove);
603
+ redactWildcardPath(obj, parts, censor, path17, 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, path16) => {
689
- const fullPath = [...pathArray.slice(0, pathLength), ...path16];
688
+ const wrappedCensor = typeof censor === "function" ? (value, path17) => {
689
+ const fullPath = [...pathArray.slice(0, pathLength), ...path17];
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 path16 of pathsToClone) {
725
- const parts = parsePath(path16);
724
+ for (const path17 of pathsToClone) {
725
+ const parts = parsePath(path17);
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(path16) {
778
- if (typeof path16 !== "string") {
777
+ function validatePath(path17) {
778
+ if (typeof path17 !== "string") {
779
779
  throw new Error("Paths must be (non-empty) strings");
780
780
  }
781
- if (path16 === "") {
781
+ if (path17 === "") {
782
782
  throw new Error("Invalid redaction path ()");
783
783
  }
784
- if (path16.includes("..")) {
785
- throw new Error(`Invalid redaction path (${path16})`);
784
+ if (path17.includes("..")) {
785
+ throw new Error(`Invalid redaction path (${path17})`);
786
786
  }
787
- if (path16.includes(",")) {
788
- throw new Error(`Invalid redaction path (${path16})`);
787
+ if (path17.includes(",")) {
788
+ throw new Error(`Invalid redaction path (${path17})`);
789
789
  }
790
790
  let bracketCount = 0;
791
791
  let inQuotes = false;
792
792
  let quoteChar = "";
793
- for (let i = 0; i < path16.length; i++) {
794
- const char = path16[i];
793
+ for (let i = 0; i < path17.length; i++) {
794
+ const char = path17[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 (${path16})`);
808
+ throw new Error(`Invalid redaction path (${path17})`);
809
809
  }
810
810
  }
811
811
  }
812
812
  if (bracketCount !== 0) {
813
- throw new Error(`Invalid redaction path (${path16})`);
813
+ throw new Error(`Invalid redaction path (${path17})`);
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 path16 of paths) {
821
- validatePath(path16);
820
+ for (const path17 of paths) {
821
+ validatePath(path17);
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, path16) => {
990
- return censor(value, [k, ...path16]);
989
+ const wrappedCensor = typeof censor === "function" ? (value, path17) => {
990
+ return censor(value, [k, ...path17]);
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 fs17 = require("fs");
1208
+ var fs18 = require("fs");
1209
1209
  var EventEmitter = require("events");
1210
1210
  var inherits = require("util").inherits;
1211
- var path16 = require("path");
1211
+ var path17 = 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) fs17.mkdirSync(path16.dirname(file), { recursive: true });
1266
- const fd = fs17.openSync(file, flags, mode);
1265
+ if (sonic.mkdir) fs18.mkdirSync(path17.dirname(file), { recursive: true });
1266
+ const fd = fs18.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
- fs17.mkdir(path16.dirname(file), { recursive: true }, (err) => {
1273
+ fs18.mkdir(path17.dirname(file), { recursive: true }, (err) => {
1274
1274
  if (err) return fileOpened(err);
1275
- fs17.open(file, flags, mode, fileOpened);
1275
+ fs18.open(file, flags, mode, fileOpened);
1276
1276
  });
1277
1277
  } else {
1278
- fs17.open(file, flags, mode, fileOpened);
1278
+ fs18.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 = () => fs17.writeSync(this.fd, this._writingBuf);
1320
- fsWrite = () => fs17.write(this.fd, this._writingBuf, this.release);
1319
+ fsWriteSync = () => fs18.writeSync(this.fd, this._writingBuf);
1320
+ fsWrite = () => fs18.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 fs17.writeSync(this.fd, this._writingBuf);
1329
+ return fs18.writeSync(this.fd, this._writingBuf);
1330
1330
  }
1331
- return fs17.writeSync(this.fd, this._writingBuf, "utf8");
1331
+ return fs18.writeSync(this.fd, this._writingBuf, "utf8");
1332
1332
  };
1333
1333
  fsWrite = () => {
1334
1334
  if (Buffer.isBuffer(this._writingBuf)) {
1335
- return fs17.write(this.fd, this._writingBuf, this.release);
1335
+ return fs18.write(this.fd, this._writingBuf, this.release);
1336
1336
  }
1337
- return fs17.write(this.fd, this._writingBuf, "utf8", this.release);
1337
+ return fs18.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
- fs17.fsyncSync(this.fd);
1394
+ fs18.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
- fs17.fsync(this.fd, (err) => {
1508
+ fs18.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
- fs17.close(fd, (err) => {
1610
+ fs18.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) ? fs17.writeSync(this.fd, buf) : fs17.writeSync(this.fd, buf, "utf8");
1659
+ const n = Buffer.isBuffer(buf) ? fs18.writeSync(this.fd, buf) : fs18.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
- fs17.fsyncSync(this.fd);
1675
+ fs18.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 = fs17.writeSync(this.fd, buf);
1696
+ const n = fs18.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) ? fs17.writeSync(this.fd, this._writingBuf) : fs17.writeSync(this.fd, this._writingBuf, "utf8");
1724
+ const written = Buffer.isBuffer(this._writingBuf) ? fs18.writeSync(this.fd, this._writingBuf) : fs18.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
- fs17.write(this.fd, this._writingBuf, release);
1730
+ fs18.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 = fs17.writeSync(this.fd, this._writingBuf);
1739
+ const written = fs18.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
- fs17.write(this.fd, this._writingBuf, release);
1748
+ fs18.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
- fs17.fsync(sonic.fd, closeWrapped);
1764
+ fs18.fsync(sonic.fd, closeWrapped);
1765
1765
  } catch {
1766
1766
  }
1767
1767
  function closeWrapped() {
1768
1768
  if (sonic.fd !== 1 && sonic.fd !== 2) {
1769
- fs17.close(sonic.fd, done);
1769
+ fs18.close(sonic.fd, done);
1770
1770
  } else {
1771
1771
  done();
1772
1772
  }
@@ -4130,7 +4130,7 @@ var require_multistream = __commonJS({
4130
4130
  var require_pino = __commonJS({
4131
4131
  "../node_modules/.pnpm/pino@9.14.0/node_modules/pino/pino.js"(exports2, module2) {
4132
4132
  "use strict";
4133
- var os11 = require("os");
4133
+ var os12 = require("os");
4134
4134
  var stdSerializers = require_pino_std_serializers();
4135
4135
  var caller = require_caller();
4136
4136
  var redaction = require_redaction();
@@ -4177,7 +4177,7 @@ var require_pino = __commonJS({
4177
4177
  } = symbols;
4178
4178
  var { epochTime, nullTime } = time;
4179
4179
  var { pid } = process;
4180
- var hostname = os11.hostname();
4180
+ var hostname = os12.hostname();
4181
4181
  var defaultErrorSerializer = stdSerializers.err;
4182
4182
  var defaultOptions = {
4183
4183
  level: "info",
@@ -8075,8 +8075,8 @@ Env (advanced):
8075
8075
  `;
8076
8076
 
8077
8077
  // src/index.ts
8078
- var import_node_path15 = __toESM(require("path"), 1);
8079
- var import_node_fs16 = __toESM(require("fs"), 1);
8078
+ var import_node_path16 = __toESM(require("path"), 1);
8079
+ var import_node_fs17 = __toESM(require("fs"), 1);
8080
8080
 
8081
8081
  // src/logger.ts
8082
8082
  var import_node_fs2 = __toESM(require("fs"), 1);
@@ -8134,6 +8134,9 @@ var METHOD_NAMES = [
8134
8134
  "session:rewind-diff",
8135
8135
  // 打开 session 时一次性拿到"有真实 diff"的 user message id 集合,UI heuristic 据此过滤按钮显示
8136
8136
  "session:rewindable-message-ids",
8137
+ // 从指定 message uuid 处 fork 出新 transcript(截断 + 重写 sessionId/parentUuid + forkedFrom)
8138
+ // daemon 只生成新 jsonl 文件并返回新 toolSessionId;UI 自行决定是否走 session:create 拉起 resume
8139
+ "session:fork",
8137
8140
  "session:new",
8138
8141
  "session:resume",
8139
8142
  "session:observe",
@@ -8667,8 +8670,8 @@ function getErrorMap() {
8667
8670
 
8668
8671
  // ../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
8669
8672
  var makeIssue = (params) => {
8670
- const { data, path: path16, errorMaps, issueData } = params;
8671
- const fullPath = [...path16, ...issueData.path || []];
8673
+ const { data, path: path17, errorMaps, issueData } = params;
8674
+ const fullPath = [...path17, ...issueData.path || []];
8672
8675
  const fullIssue = {
8673
8676
  ...issueData,
8674
8677
  path: fullPath
@@ -8784,11 +8787,11 @@ var errorUtil;
8784
8787
 
8785
8788
  // ../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
8786
8789
  var ParseInputLazyPath = class {
8787
- constructor(parent, value, path16, key) {
8790
+ constructor(parent, value, path17, key) {
8788
8791
  this._cachedPath = [];
8789
8792
  this.parent = parent;
8790
8793
  this.data = value;
8791
- this._path = path16;
8794
+ this._path = path17;
8792
8795
  this._key = key;
8793
8796
  }
8794
8797
  get path() {
@@ -12310,6 +12313,9 @@ var SessionFileSchema = external_exports.object({
12310
12313
  // NewSessionDialog 勾选 worktree 创建的 session 才会持久化这两个字段
12311
12314
  worktreeRoot: external_exports.string().min(1).optional(),
12312
12315
  worktreeBranch: external_exports.string().min(1).optional(),
12316
+ // 由 session:fork 派生的 session 在 create 时记录源 transcript 的 toolSessionId;
12317
+ // sidebar 据此挂 fork 图标。非 fork session 该字段缺省
12318
+ forkedFromToolSessionId: external_exports.string().min(1).optional(),
12313
12319
  createdAt: external_exports.string().min(1),
12314
12320
  updatedAt: external_exports.string().min(1)
12315
12321
  });
@@ -12464,7 +12470,9 @@ var SessionCreateArgs = external_exports.object({
12464
12470
  effort: external_exports.string().optional(),
12465
12471
  // NewSessionDialog 勾选 worktree 时由 UI 传入,daemon 原样持久化,不做额外副作用
12466
12472
  worktreeRoot: external_exports.string().min(1).optional(),
12467
- worktreeBranch: external_exports.string().min(1).optional()
12473
+ worktreeBranch: external_exports.string().min(1).optional(),
12474
+ // session:fork 派生 session 时由 UI 传入,daemon 写到 SessionFile.forkedFromToolSessionId
12475
+ forkedFromToolSessionId: external_exports.string().min(1).optional()
12468
12476
  });
12469
12477
  var SessionIdArgs = external_exports.object({ sessionId: external_exports.string().min(1) });
12470
12478
  var SessionUpdateArgs = external_exports.object({
@@ -12526,6 +12534,15 @@ var SessionResumeArgs = external_exports.object({
12526
12534
  sessionId: external_exports.string().min(1),
12527
12535
  toolSessionId: external_exports.string().min(1)
12528
12536
  });
12537
+ var SessionForkArgs = external_exports.object({
12538
+ cwd: external_exports.string().min(1),
12539
+ toolSessionId: external_exports.string().min(1),
12540
+ messageUuid: external_exports.string().min(1)
12541
+ });
12542
+ var SessionForkResponseSchema = external_exports.object({
12543
+ forkedToolSessionId: external_exports.string().min(1),
12544
+ forkedFilePath: external_exports.string().min(1)
12545
+ });
12529
12546
  var SessionObserveArgs = external_exports.object({
12530
12547
  sessionId: external_exports.string().min(1),
12531
12548
  toolSessionId: external_exports.string().min(1),
@@ -13581,6 +13598,7 @@ var SessionManager = class {
13581
13598
  effort: args.effort,
13582
13599
  worktreeRoot: args.worktreeRoot,
13583
13600
  worktreeBranch: args.worktreeBranch,
13601
+ forkedFromToolSessionId: args.forkedFromToolSessionId,
13584
13602
  createdAt: iso,
13585
13603
  updatedAt: iso
13586
13604
  };
@@ -14046,11 +14064,11 @@ Diff.prototype = {
14046
14064
  }
14047
14065
  }
14048
14066
  },
14049
- addToPath: function addToPath(path16, added, removed, oldPosInc, options) {
14050
- var last = path16.lastComponent;
14067
+ addToPath: function addToPath(path17, added, removed, oldPosInc, options) {
14068
+ var last = path17.lastComponent;
14051
14069
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
14052
14070
  return {
14053
- oldPos: path16.oldPos + oldPosInc,
14071
+ oldPos: path17.oldPos + oldPosInc,
14054
14072
  lastComponent: {
14055
14073
  count: last.count + 1,
14056
14074
  added,
@@ -14060,7 +14078,7 @@ Diff.prototype = {
14060
14078
  };
14061
14079
  } else {
14062
14080
  return {
14063
- oldPos: path16.oldPos + oldPosInc,
14081
+ oldPos: path17.oldPos + oldPosInc,
14064
14082
  lastComponent: {
14065
14083
  count: 1,
14066
14084
  added,
@@ -14903,10 +14921,10 @@ function attachmentToHistoryMessage(o, ts) {
14903
14921
  const memories = raw.map((m) => {
14904
14922
  if (!m || typeof m !== "object") return null;
14905
14923
  const rec = m;
14906
- const path16 = typeof rec.path === "string" ? rec.path : null;
14924
+ const path17 = typeof rec.path === "string" ? rec.path : null;
14907
14925
  const content = typeof rec.content === "string" ? rec.content : null;
14908
- if (!path16 || content == null) return null;
14909
- const entry = { path: path16, content };
14926
+ if (!path17 || content == null) return null;
14927
+ const entry = { path: path17, content };
14910
14928
  if (typeof rec.mtimeMs === "number") entry.mtimeMs = rec.mtimeMs;
14911
14929
  return entry;
14912
14930
  }).filter((m) => m !== null);
@@ -15642,10 +15660,10 @@ function parseAttachment(obj) {
15642
15660
  const memories = raw.map((m) => {
15643
15661
  if (!m || typeof m !== "object") return null;
15644
15662
  const rec = m;
15645
- const path16 = typeof rec.path === "string" ? rec.path : null;
15663
+ const path17 = typeof rec.path === "string" ? rec.path : null;
15646
15664
  const content = typeof rec.content === "string" ? rec.content : null;
15647
- if (!path16 || content == null) return null;
15648
- const out = { path: path16, content };
15665
+ if (!path17 || content == null) return null;
15666
+ const out = { path: path17, content };
15649
15667
  if (typeof rec.mtimeMs === "number") out.mtimeMs = rec.mtimeMs;
15650
15668
  return out;
15651
15669
  }).filter((m) => m !== null);
@@ -17134,6 +17152,63 @@ function writeAuthFile(file, content) {
17134
17152
  }
17135
17153
  }
17136
17154
 
17155
+ // src/session/fork.ts
17156
+ var import_node_fs15 = __toESM(require("fs"), 1);
17157
+ var import_node_os9 = __toESM(require("os"), 1);
17158
+ var import_node_path14 = __toESM(require("path"), 1);
17159
+ function readJsonlEntries(file) {
17160
+ const raw = import_node_fs15.default.readFileSync(file, "utf8");
17161
+ const out = [];
17162
+ for (const line of raw.split("\n")) {
17163
+ const t = line.trim();
17164
+ if (!t) continue;
17165
+ try {
17166
+ out.push(JSON.parse(t));
17167
+ } catch {
17168
+ }
17169
+ }
17170
+ return out;
17171
+ }
17172
+ function forkSession(input) {
17173
+ const baseDir = input.baseDir ?? import_node_path14.default.join(import_node_os9.default.homedir(), ".claude");
17174
+ const projectDir = import_node_path14.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
17175
+ const sourceFile = import_node_path14.default.join(projectDir, `${input.toolSessionId}.jsonl`);
17176
+ if (!import_node_fs15.default.existsSync(sourceFile)) {
17177
+ throw new Error(`fork: source transcript not found: ${sourceFile}`);
17178
+ }
17179
+ const entries = readJsonlEntries(sourceFile);
17180
+ const mainEntries = entries.filter((e) => e.isSidechain !== true);
17181
+ const targetIdx = mainEntries.findIndex((e) => e.uuid === input.messageUuid);
17182
+ if (targetIdx === -1) {
17183
+ throw new Error(`fork: messageUuid not found in transcript: ${input.messageUuid}`);
17184
+ }
17185
+ const sliced = mainEntries.slice(0, targetIdx + 1);
17186
+ const forkedToolSessionId = v4_default();
17187
+ let parentUuid = null;
17188
+ const forkedLines = [];
17189
+ for (const entry of sliced) {
17190
+ const originalSessionId = entry.sessionId ?? input.toolSessionId;
17191
+ const originalUuid = entry.uuid;
17192
+ const forked = {
17193
+ ...entry,
17194
+ sessionId: forkedToolSessionId,
17195
+ parentUuid,
17196
+ isSidechain: false
17197
+ };
17198
+ if (originalUuid) {
17199
+ forked.forkedFrom = { sessionId: originalSessionId, messageUuid: originalUuid };
17200
+ }
17201
+ if (entry.type !== "progress" && originalUuid) {
17202
+ parentUuid = originalUuid;
17203
+ }
17204
+ forkedLines.push(JSON.stringify(forked));
17205
+ }
17206
+ const forkedFilePath = import_node_path14.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
17207
+ import_node_fs15.default.mkdirSync(projectDir, { recursive: true });
17208
+ import_node_fs15.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
17209
+ return { forkedToolSessionId, forkedFilePath };
17210
+ }
17211
+
17137
17212
  // src/handlers/session.ts
17138
17213
  function buildSessionHandlers(deps) {
17139
17214
  const { manager, observer, getAdapter: getAdapter2 } = deps;
@@ -17196,6 +17271,11 @@ function buildSessionHandlers(deps) {
17196
17271
  response: { type: "session:rewindable-message-ids", ...response }
17197
17272
  };
17198
17273
  };
17274
+ const fork = async (frame) => {
17275
+ const args = SessionForkArgs.parse(frame);
17276
+ const result = forkSession(args);
17277
+ return { response: { type: "session:fork", ...result } };
17278
+ };
17199
17279
  const newSession = async (frame) => {
17200
17280
  const args = SessionIdArgs.parse(frame);
17201
17281
  observer.stop(args.sessionId);
@@ -17258,6 +17338,7 @@ function buildSessionHandlers(deps) {
17258
17338
  "session:rewind": rewind,
17259
17339
  "session:rewind-diff": rewindDiff,
17260
17340
  "session:rewindable-message-ids": rewindableMessageIds,
17341
+ "session:fork": fork,
17261
17342
  "session:new": newSession,
17262
17343
  "session:resume": resume,
17263
17344
  "session:observe": observe,
@@ -17374,15 +17455,15 @@ function buildWorkspaceHandlers(deps) {
17374
17455
 
17375
17456
  // src/workspace/git.ts
17376
17457
  var import_node_child_process5 = require("child_process");
17377
- var import_node_fs15 = __toESM(require("fs"), 1);
17378
- var import_node_os9 = __toESM(require("os"), 1);
17379
- var import_node_path14 = __toESM(require("path"), 1);
17458
+ var import_node_fs16 = __toESM(require("fs"), 1);
17459
+ var import_node_os10 = __toESM(require("os"), 1);
17460
+ var import_node_path15 = __toESM(require("path"), 1);
17380
17461
  var import_node_util = require("util");
17381
17462
  var pexec = (0, import_node_util.promisify)(import_node_child_process5.execFile);
17382
17463
  function normalizePath(p) {
17383
- const resolved = import_node_path14.default.resolve(p);
17464
+ const resolved = import_node_path15.default.resolve(p);
17384
17465
  try {
17385
- return import_node_fs15.default.realpathSync(resolved);
17466
+ return import_node_fs16.default.realpathSync(resolved);
17386
17467
  } catch {
17387
17468
  return resolved;
17388
17469
  }
@@ -17469,7 +17550,7 @@ function sanitizeLabel(raw) {
17469
17550
  function computePrefix() {
17470
17551
  let username;
17471
17552
  try {
17472
- username = import_node_os9.default.userInfo().username;
17553
+ username = import_node_os10.default.userInfo().username;
17473
17554
  } catch {
17474
17555
  username = void 0;
17475
17556
  }
@@ -17483,13 +17564,13 @@ function flattenToDirName(branch) {
17483
17564
  }
17484
17565
  function encodeClaudeProjectDir(absPath) {
17485
17566
  if (!absPath || typeof absPath !== "string") return "";
17486
- let canonical = import_node_path14.default.resolve(absPath);
17567
+ let canonical = import_node_path15.default.resolve(absPath);
17487
17568
  try {
17488
- canonical = import_node_fs15.default.realpathSync(canonical);
17569
+ canonical = import_node_fs16.default.realpathSync(canonical);
17489
17570
  } catch {
17490
17571
  try {
17491
- const parent = import_node_fs15.default.realpathSync(import_node_path14.default.dirname(canonical));
17492
- canonical = import_node_path14.default.join(parent, import_node_path14.default.basename(canonical));
17572
+ const parent = import_node_fs16.default.realpathSync(import_node_path15.default.dirname(canonical));
17573
+ canonical = import_node_path15.default.join(parent, import_node_path15.default.basename(canonical));
17493
17574
  } catch {
17494
17575
  }
17495
17576
  }
@@ -17513,11 +17594,11 @@ async function createWorktree(input) {
17513
17594
  if (!isGitRoot) {
17514
17595
  throw new Error(`\u76EE\u5F55 ${cwd} \u4E0D\u662F git repo \u6839`);
17515
17596
  }
17516
- const parent = import_node_path14.default.dirname(import_node_path14.default.resolve(cwd));
17517
- if (parent === "/" || parent === import_node_path14.default.resolve(cwd)) {
17597
+ const parent = import_node_path15.default.dirname(import_node_path15.default.resolve(cwd));
17598
+ if (parent === "/" || parent === import_node_path15.default.resolve(cwd)) {
17518
17599
  throw new Error("repo \u5728\u78C1\u76D8\u6839\u76EE\u5F55\uFF0C\u65E0\u6CD5\u5728\u540C\u7EA7\u521B\u5EFA worktree");
17519
17600
  }
17520
- const worktreeRoot = import_node_path14.default.join(parent, dirName);
17601
+ const worktreeRoot = import_node_path15.default.join(parent, dirName);
17521
17602
  try {
17522
17603
  await pexec("git", ["-C", cwd, "rev-parse", "--verify", `refs/heads/${baseBranch}`], {
17523
17604
  timeout: 3e3
@@ -17534,7 +17615,7 @@ async function createWorktree(input) {
17534
17615
  const msg = err.message;
17535
17616
  if (msg.startsWith("\u5206\u652F ")) throw err;
17536
17617
  }
17537
- if (import_node_fs15.default.existsSync(worktreeRoot)) {
17618
+ if (import_node_fs16.default.existsSync(worktreeRoot)) {
17538
17619
  throw new Error(`\u76EE\u5F55 ${worktreeRoot} \u5DF2\u5B58\u5728\uFF0C\u8BF7\u6362\u4E00\u4E2A label \u6216\u6E05\u7406\u540E\u91CD\u8BD5`);
17539
17620
  }
17540
17621
  try {
@@ -17564,8 +17645,8 @@ async function removeWorktree(input) {
17564
17645
  );
17565
17646
  const gitCommonDir = stdout.trim();
17566
17647
  if (!gitCommonDir) throw new Error("empty git-common-dir");
17567
- const absGitCommon = import_node_path14.default.isAbsolute(gitCommonDir) ? gitCommonDir : import_node_path14.default.resolve(worktreeRoot, gitCommonDir);
17568
- repoRoot = import_node_path14.default.dirname(absGitCommon);
17648
+ const absGitCommon = import_node_path15.default.isAbsolute(gitCommonDir) ? gitCommonDir : import_node_path15.default.resolve(worktreeRoot, gitCommonDir);
17649
+ repoRoot = import_node_path15.default.dirname(absGitCommon);
17569
17650
  } catch {
17570
17651
  repoRoot = null;
17571
17652
  }
@@ -17577,7 +17658,7 @@ async function removeWorktree(input) {
17577
17658
  } catch (err) {
17578
17659
  const stderr = err.stderr || err.message;
17579
17660
  const lower = stderr.toLowerCase();
17580
- const vanished = lower.includes("not a working tree") || lower.includes("is not a working tree") || !import_node_fs15.default.existsSync(worktreeRoot);
17661
+ const vanished = lower.includes("not a working tree") || lower.includes("is not a working tree") || !import_node_fs16.default.existsSync(worktreeRoot);
17581
17662
  if (!vanished) {
17582
17663
  const firstLine = stderr.split("\n")[0].trim();
17583
17664
  throw new Error(`\u6E05\u7406 worktree \u5931\u8D25\uFF1A${firstLine}`);
@@ -17598,10 +17679,10 @@ async function removeWorktree(input) {
17598
17679
  try {
17599
17680
  const encoded = encodeClaudeProjectDir(worktreeRoot);
17600
17681
  if (encoded) {
17601
- const projectsRoot = import_node_path14.default.join(import_node_os9.default.homedir(), ".claude", "projects");
17602
- const target = import_node_path14.default.resolve(projectsRoot, encoded);
17603
- if (target.startsWith(projectsRoot + import_node_path14.default.sep) && target !== projectsRoot) {
17604
- import_node_fs15.default.rmSync(target, { recursive: true, force: true });
17682
+ const projectsRoot = import_node_path15.default.join(import_node_os10.default.homedir(), ".claude", "projects");
17683
+ const target = import_node_path15.default.resolve(projectsRoot, encoded);
17684
+ if (target.startsWith(projectsRoot + import_node_path15.default.sep) && target !== projectsRoot) {
17685
+ import_node_fs16.default.rmSync(target, { recursive: true, force: true });
17605
17686
  }
17606
17687
  }
17607
17688
  } catch {
@@ -17672,7 +17753,7 @@ function buildCapabilitiesHandlers(deps) {
17672
17753
  }
17673
17754
 
17674
17755
  // src/handlers/meta.ts
17675
- var import_node_os10 = __toESM(require("os"), 1);
17756
+ var import_node_os11 = __toESM(require("os"), 1);
17676
17757
 
17677
17758
  // src/version.ts
17678
17759
  var version = "0.2.6".length > 0 ? "0.2.6" : "dev";
@@ -17692,7 +17773,7 @@ function buildReadyFrame(deps) {
17692
17773
  return {
17693
17774
  version,
17694
17775
  protocolVersion: PROTOCOL_VERSION,
17695
- hostname: import_node_os10.default.hostname(),
17776
+ hostname: import_node_os11.default.hostname(),
17696
17777
  os: process.platform,
17697
17778
  tools,
17698
17779
  runningSessions: info.runningSessions
@@ -17728,7 +17809,7 @@ function buildMethodHandlers(deps) {
17728
17809
  async function startDaemon(config) {
17729
17810
  const logger = createLogger({
17730
17811
  level: config.logLevel,
17731
- file: import_node_path15.default.join(config.dataDir, "clawd.log")
17812
+ file: import_node_path16.default.join(config.dataDir, "clawd.log")
17732
17813
  });
17733
17814
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
17734
17815
  const stateMgr = new StateFileManager({ dataDir: config.dataDir });
@@ -17864,8 +17945,8 @@ async function startDaemon(config) {
17864
17945
  const lines = [
17865
17946
  `Tunnel: ${r.url}`,
17866
17947
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
17867
- `Frpc config: ${import_node_path15.default.join(config.dataDir, "frpc.toml")}`,
17868
- `Frpc log: ${import_node_path15.default.join(config.dataDir, "frpc.log")}`
17948
+ `Frpc config: ${import_node_path16.default.join(config.dataDir, "frpc.toml")}`,
17949
+ `Frpc log: ${import_node_path16.default.join(config.dataDir, "frpc.log")}`
17869
17950
  ];
17870
17951
  const width = Math.max(...lines.map((l) => l.length));
17871
17952
  const bar = "\u2550".repeat(width + 4);
@@ -17878,8 +17959,8 @@ ${bar}
17878
17959
 
17879
17960
  `);
17880
17961
  try {
17881
- const connectPath = import_node_path15.default.join(config.dataDir, "connect.txt");
17882
- import_node_fs16.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
17962
+ const connectPath = import_node_path16.default.join(config.dataDir, "connect.txt");
17963
+ import_node_fs17.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
17883
17964
  } catch {
17884
17965
  }
17885
17966
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawos-dev/clawd",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Standalone clawd daemon — Claude Code (and future Codex) session server over WebSocket",
5
5
  "type": "module",
6
6
  "license": "MIT",