@clawos-dev/clawd 0.2.453 → 0.2.454

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -450,8 +450,8 @@ var init_parseUtil = __esm({
450
450
  init_errors();
451
451
  init_en();
452
452
  makeIssue = (params) => {
453
- const { data, path: path93, errorMaps, issueData } = params;
454
- const fullPath = [...path93, ...issueData.path || []];
453
+ const { data, path: path94, errorMaps, issueData } = params;
454
+ const fullPath = [...path94, ...issueData.path || []];
455
455
  const fullIssue = {
456
456
  ...issueData,
457
457
  path: fullPath
@@ -762,11 +762,11 @@ var init_types = __esm({
762
762
  init_parseUtil();
763
763
  init_util();
764
764
  ParseInputLazyPath = class {
765
- constructor(parent, value, path93, key) {
765
+ constructor(parent, value, path94, key) {
766
766
  this._cachedPath = [];
767
767
  this.parent = parent;
768
768
  this.data = value;
769
- this._path = path93;
769
+ this._path = path94;
770
770
  this._key = key;
771
771
  }
772
772
  get path() {
@@ -9823,8 +9823,8 @@ var require_req = __commonJS({
9823
9823
  if (req.originalUrl) {
9824
9824
  _req.url = req.originalUrl;
9825
9825
  } else {
9826
- const path93 = req.path;
9827
- _req.url = typeof path93 === "string" ? path93 : req.url ? req.url.path || req.url : void 0;
9826
+ const path94 = req.path;
9827
+ _req.url = typeof path94 === "string" ? path94 : req.url ? req.url.path || req.url : void 0;
9828
9828
  }
9829
9829
  if (req.query) {
9830
9830
  _req.query = req.query;
@@ -9989,14 +9989,14 @@ var require_redact = __commonJS({
9989
9989
  }
9990
9990
  return obj;
9991
9991
  }
9992
- function parsePath(path93) {
9992
+ function parsePath(path94) {
9993
9993
  const parts = [];
9994
9994
  let current = "";
9995
9995
  let inBrackets = false;
9996
9996
  let inQuotes = false;
9997
9997
  let quoteChar = "";
9998
- for (let i = 0; i < path93.length; i++) {
9999
- const char = path93[i];
9998
+ for (let i = 0; i < path94.length; i++) {
9999
+ const char = path94[i];
10000
10000
  if (!inBrackets && char === ".") {
10001
10001
  if (current) {
10002
10002
  parts.push(current);
@@ -10127,10 +10127,10 @@ var require_redact = __commonJS({
10127
10127
  return current;
10128
10128
  }
10129
10129
  function redactPaths(obj, paths, censor, remove = false) {
10130
- for (const path93 of paths) {
10131
- const parts = parsePath(path93);
10130
+ for (const path94 of paths) {
10131
+ const parts = parsePath(path94);
10132
10132
  if (parts.includes("*")) {
10133
- redactWildcardPath(obj, parts, censor, path93, remove);
10133
+ redactWildcardPath(obj, parts, censor, path94, remove);
10134
10134
  } else {
10135
10135
  if (remove) {
10136
10136
  removeKey(obj, parts);
@@ -10215,8 +10215,8 @@ var require_redact = __commonJS({
10215
10215
  }
10216
10216
  } else {
10217
10217
  if (afterWildcard.includes("*")) {
10218
- const wrappedCensor = typeof censor === "function" ? (value, path93) => {
10219
- const fullPath = [...pathArray.slice(0, pathLength), ...path93];
10218
+ const wrappedCensor = typeof censor === "function" ? (value, path94) => {
10219
+ const fullPath = [...pathArray.slice(0, pathLength), ...path94];
10220
10220
  return censor(value, fullPath);
10221
10221
  } : censor;
10222
10222
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -10251,8 +10251,8 @@ var require_redact = __commonJS({
10251
10251
  return null;
10252
10252
  }
10253
10253
  const pathStructure = /* @__PURE__ */ new Map();
10254
- for (const path93 of pathsToClone) {
10255
- const parts = parsePath(path93);
10254
+ for (const path94 of pathsToClone) {
10255
+ const parts = parsePath(path94);
10256
10256
  let current = pathStructure;
10257
10257
  for (let i = 0; i < parts.length; i++) {
10258
10258
  const part = parts[i];
@@ -10304,24 +10304,24 @@ var require_redact = __commonJS({
10304
10304
  }
10305
10305
  return cloneSelectively(obj, pathStructure);
10306
10306
  }
10307
- function validatePath(path93) {
10308
- if (typeof path93 !== "string") {
10307
+ function validatePath(path94) {
10308
+ if (typeof path94 !== "string") {
10309
10309
  throw new Error("Paths must be (non-empty) strings");
10310
10310
  }
10311
- if (path93 === "") {
10311
+ if (path94 === "") {
10312
10312
  throw new Error("Invalid redaction path ()");
10313
10313
  }
10314
- if (path93.includes("..")) {
10315
- throw new Error(`Invalid redaction path (${path93})`);
10314
+ if (path94.includes("..")) {
10315
+ throw new Error(`Invalid redaction path (${path94})`);
10316
10316
  }
10317
- if (path93.includes(",")) {
10318
- throw new Error(`Invalid redaction path (${path93})`);
10317
+ if (path94.includes(",")) {
10318
+ throw new Error(`Invalid redaction path (${path94})`);
10319
10319
  }
10320
10320
  let bracketCount = 0;
10321
10321
  let inQuotes = false;
10322
10322
  let quoteChar = "";
10323
- for (let i = 0; i < path93.length; i++) {
10324
- const char = path93[i];
10323
+ for (let i = 0; i < path94.length; i++) {
10324
+ const char = path94[i];
10325
10325
  if ((char === '"' || char === "'") && bracketCount > 0) {
10326
10326
  if (!inQuotes) {
10327
10327
  inQuotes = true;
@@ -10335,20 +10335,20 @@ var require_redact = __commonJS({
10335
10335
  } else if (char === "]" && !inQuotes) {
10336
10336
  bracketCount--;
10337
10337
  if (bracketCount < 0) {
10338
- throw new Error(`Invalid redaction path (${path93})`);
10338
+ throw new Error(`Invalid redaction path (${path94})`);
10339
10339
  }
10340
10340
  }
10341
10341
  }
10342
10342
  if (bracketCount !== 0) {
10343
- throw new Error(`Invalid redaction path (${path93})`);
10343
+ throw new Error(`Invalid redaction path (${path94})`);
10344
10344
  }
10345
10345
  }
10346
10346
  function validatePaths(paths) {
10347
10347
  if (!Array.isArray(paths)) {
10348
10348
  throw new TypeError("paths must be an array");
10349
10349
  }
10350
- for (const path93 of paths) {
10351
- validatePath(path93);
10350
+ for (const path94 of paths) {
10351
+ validatePath(path94);
10352
10352
  }
10353
10353
  }
10354
10354
  function slowRedact(options = {}) {
@@ -10516,8 +10516,8 @@ var require_redaction = __commonJS({
10516
10516
  if (shape[k2] === null) {
10517
10517
  o[k2] = (value) => topCensor(value, [k2]);
10518
10518
  } else {
10519
- const wrappedCensor = typeof censor === "function" ? (value, path93) => {
10520
- return censor(value, [k2, ...path93]);
10519
+ const wrappedCensor = typeof censor === "function" ? (value, path94) => {
10520
+ return censor(value, [k2, ...path94]);
10521
10521
  } : censor;
10522
10522
  o[k2] = Redact({
10523
10523
  paths: shape[k2],
@@ -10735,10 +10735,10 @@ var require_atomic_sleep = __commonJS({
10735
10735
  var require_sonic_boom = __commonJS({
10736
10736
  "../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
10737
10737
  "use strict";
10738
- var fs82 = require("fs");
10738
+ var fs83 = require("fs");
10739
10739
  var EventEmitter3 = require("events");
10740
10740
  var inherits = require("util").inherits;
10741
- var path93 = require("path");
10741
+ var path94 = require("path");
10742
10742
  var sleep2 = require_atomic_sleep();
10743
10743
  var assert = require("assert");
10744
10744
  var BUSY_WRITE_TIMEOUT = 100;
@@ -10792,20 +10792,20 @@ var require_sonic_boom = __commonJS({
10792
10792
  const mode = sonic.mode;
10793
10793
  if (sonic.sync) {
10794
10794
  try {
10795
- if (sonic.mkdir) fs82.mkdirSync(path93.dirname(file), { recursive: true });
10796
- const fd = fs82.openSync(file, flags, mode);
10795
+ if (sonic.mkdir) fs83.mkdirSync(path94.dirname(file), { recursive: true });
10796
+ const fd = fs83.openSync(file, flags, mode);
10797
10797
  fileOpened(null, fd);
10798
10798
  } catch (err) {
10799
10799
  fileOpened(err);
10800
10800
  throw err;
10801
10801
  }
10802
10802
  } else if (sonic.mkdir) {
10803
- fs82.mkdir(path93.dirname(file), { recursive: true }, (err) => {
10803
+ fs83.mkdir(path94.dirname(file), { recursive: true }, (err) => {
10804
10804
  if (err) return fileOpened(err);
10805
- fs82.open(file, flags, mode, fileOpened);
10805
+ fs83.open(file, flags, mode, fileOpened);
10806
10806
  });
10807
10807
  } else {
10808
- fs82.open(file, flags, mode, fileOpened);
10808
+ fs83.open(file, flags, mode, fileOpened);
10809
10809
  }
10810
10810
  }
10811
10811
  function SonicBoom(opts) {
@@ -10846,8 +10846,8 @@ var require_sonic_boom = __commonJS({
10846
10846
  this.flush = flushBuffer;
10847
10847
  this.flushSync = flushBufferSync;
10848
10848
  this._actualWrite = actualWriteBuffer;
10849
- fsWriteSync = () => fs82.writeSync(this.fd, this._writingBuf);
10850
- fsWrite = () => fs82.write(this.fd, this._writingBuf, this.release);
10849
+ fsWriteSync = () => fs83.writeSync(this.fd, this._writingBuf);
10850
+ fsWrite = () => fs83.write(this.fd, this._writingBuf, this.release);
10851
10851
  } else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
10852
10852
  this._writingBuf = "";
10853
10853
  this.write = write;
@@ -10856,15 +10856,15 @@ var require_sonic_boom = __commonJS({
10856
10856
  this._actualWrite = actualWrite;
10857
10857
  fsWriteSync = () => {
10858
10858
  if (Buffer.isBuffer(this._writingBuf)) {
10859
- return fs82.writeSync(this.fd, this._writingBuf);
10859
+ return fs83.writeSync(this.fd, this._writingBuf);
10860
10860
  }
10861
- return fs82.writeSync(this.fd, this._writingBuf, "utf8");
10861
+ return fs83.writeSync(this.fd, this._writingBuf, "utf8");
10862
10862
  };
10863
10863
  fsWrite = () => {
10864
10864
  if (Buffer.isBuffer(this._writingBuf)) {
10865
- return fs82.write(this.fd, this._writingBuf, this.release);
10865
+ return fs83.write(this.fd, this._writingBuf, this.release);
10866
10866
  }
10867
- return fs82.write(this.fd, this._writingBuf, "utf8", this.release);
10867
+ return fs83.write(this.fd, this._writingBuf, "utf8", this.release);
10868
10868
  };
10869
10869
  } else {
10870
10870
  throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
@@ -10921,7 +10921,7 @@ var require_sonic_boom = __commonJS({
10921
10921
  }
10922
10922
  }
10923
10923
  if (this._fsync) {
10924
- fs82.fsyncSync(this.fd);
10924
+ fs83.fsyncSync(this.fd);
10925
10925
  }
10926
10926
  const len = this._len;
10927
10927
  if (this._reopening) {
@@ -11035,7 +11035,7 @@ var require_sonic_boom = __commonJS({
11035
11035
  const onDrain = () => {
11036
11036
  if (!this._fsync) {
11037
11037
  try {
11038
- fs82.fsync(this.fd, (err) => {
11038
+ fs83.fsync(this.fd, (err) => {
11039
11039
  this._flushPending = false;
11040
11040
  cb(err);
11041
11041
  });
@@ -11137,7 +11137,7 @@ var require_sonic_boom = __commonJS({
11137
11137
  const fd = this.fd;
11138
11138
  this.once("ready", () => {
11139
11139
  if (fd !== this.fd) {
11140
- fs82.close(fd, (err) => {
11140
+ fs83.close(fd, (err) => {
11141
11141
  if (err) {
11142
11142
  return this.emit("error", err);
11143
11143
  }
@@ -11186,7 +11186,7 @@ var require_sonic_boom = __commonJS({
11186
11186
  buf = this._bufs[0];
11187
11187
  }
11188
11188
  try {
11189
- const n = Buffer.isBuffer(buf) ? fs82.writeSync(this.fd, buf) : fs82.writeSync(this.fd, buf, "utf8");
11189
+ const n = Buffer.isBuffer(buf) ? fs83.writeSync(this.fd, buf) : fs83.writeSync(this.fd, buf, "utf8");
11190
11190
  const releasedBufObj = releaseWritingBuf(buf, this._len, n);
11191
11191
  buf = releasedBufObj.writingBuf;
11192
11192
  this._len = releasedBufObj.len;
@@ -11202,7 +11202,7 @@ var require_sonic_boom = __commonJS({
11202
11202
  }
11203
11203
  }
11204
11204
  try {
11205
- fs82.fsyncSync(this.fd);
11205
+ fs83.fsyncSync(this.fd);
11206
11206
  } catch {
11207
11207
  }
11208
11208
  }
@@ -11223,7 +11223,7 @@ var require_sonic_boom = __commonJS({
11223
11223
  buf = mergeBuf(this._bufs[0], this._lens[0]);
11224
11224
  }
11225
11225
  try {
11226
- const n = fs82.writeSync(this.fd, buf);
11226
+ const n = fs83.writeSync(this.fd, buf);
11227
11227
  buf = buf.subarray(n);
11228
11228
  this._len = Math.max(this._len - n, 0);
11229
11229
  if (buf.length <= 0) {
@@ -11251,13 +11251,13 @@ var require_sonic_boom = __commonJS({
11251
11251
  this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
11252
11252
  if (this.sync) {
11253
11253
  try {
11254
- const written = Buffer.isBuffer(this._writingBuf) ? fs82.writeSync(this.fd, this._writingBuf) : fs82.writeSync(this.fd, this._writingBuf, "utf8");
11254
+ const written = Buffer.isBuffer(this._writingBuf) ? fs83.writeSync(this.fd, this._writingBuf) : fs83.writeSync(this.fd, this._writingBuf, "utf8");
11255
11255
  release(null, written);
11256
11256
  } catch (err) {
11257
11257
  release(err);
11258
11258
  }
11259
11259
  } else {
11260
- fs82.write(this.fd, this._writingBuf, release);
11260
+ fs83.write(this.fd, this._writingBuf, release);
11261
11261
  }
11262
11262
  }
11263
11263
  function actualWriteBuffer() {
@@ -11266,7 +11266,7 @@ var require_sonic_boom = __commonJS({
11266
11266
  this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
11267
11267
  if (this.sync) {
11268
11268
  try {
11269
- const written = fs82.writeSync(this.fd, this._writingBuf);
11269
+ const written = fs83.writeSync(this.fd, this._writingBuf);
11270
11270
  release(null, written);
11271
11271
  } catch (err) {
11272
11272
  release(err);
@@ -11275,7 +11275,7 @@ var require_sonic_boom = __commonJS({
11275
11275
  if (kCopyBuffer) {
11276
11276
  this._writingBuf = Buffer.from(this._writingBuf);
11277
11277
  }
11278
- fs82.write(this.fd, this._writingBuf, release);
11278
+ fs83.write(this.fd, this._writingBuf, release);
11279
11279
  }
11280
11280
  }
11281
11281
  function actualClose(sonic) {
@@ -11291,12 +11291,12 @@ var require_sonic_boom = __commonJS({
11291
11291
  sonic._lens = [];
11292
11292
  assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
11293
11293
  try {
11294
- fs82.fsync(sonic.fd, closeWrapped);
11294
+ fs83.fsync(sonic.fd, closeWrapped);
11295
11295
  } catch {
11296
11296
  }
11297
11297
  function closeWrapped() {
11298
11298
  if (sonic.fd !== 1 && sonic.fd !== 2) {
11299
- fs82.close(sonic.fd, done);
11299
+ fs83.close(sonic.fd, done);
11300
11300
  } else {
11301
11301
  done();
11302
11302
  }
@@ -14431,11 +14431,11 @@ var init_lib = __esm({
14431
14431
  }
14432
14432
  }
14433
14433
  },
14434
- addToPath: function addToPath(path93, added, removed, oldPosInc, options) {
14435
- var last = path93.lastComponent;
14434
+ addToPath: function addToPath(path94, added, removed, oldPosInc, options) {
14435
+ var last = path94.lastComponent;
14436
14436
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
14437
14437
  return {
14438
- oldPos: path93.oldPos + oldPosInc,
14438
+ oldPos: path94.oldPos + oldPosInc,
14439
14439
  lastComponent: {
14440
14440
  count: last.count + 1,
14441
14441
  added,
@@ -14445,7 +14445,7 @@ var init_lib = __esm({
14445
14445
  };
14446
14446
  } else {
14447
14447
  return {
14448
- oldPos: path93.oldPos + oldPosInc,
14448
+ oldPos: path94.oldPos + oldPosInc,
14449
14449
  lastComponent: {
14450
14450
  count: 1,
14451
14451
  added,
@@ -15027,10 +15027,10 @@ function attachmentToHistoryMessage(o, ts) {
15027
15027
  const memories = raw.map((m2) => {
15028
15028
  if (!m2 || typeof m2 !== "object") return null;
15029
15029
  const rec4 = m2;
15030
- const path93 = typeof rec4.path === "string" ? rec4.path : null;
15030
+ const path94 = typeof rec4.path === "string" ? rec4.path : null;
15031
15031
  const content = typeof rec4.content === "string" ? rec4.content : null;
15032
- if (!path93 || content == null) return null;
15033
- const entry = { path: path93, content };
15032
+ if (!path94 || content == null) return null;
15033
+ const entry = { path: path94, content };
15034
15034
  if (typeof rec4.mtimeMs === "number") entry.mtimeMs = rec4.mtimeMs;
15035
15035
  return entry;
15036
15036
  }).filter((m2) => m2 !== null);
@@ -15896,10 +15896,10 @@ function parseAttachment(obj) {
15896
15896
  const memories = raw.map((m2) => {
15897
15897
  if (!m2 || typeof m2 !== "object") return null;
15898
15898
  const rec4 = m2;
15899
- const path93 = typeof rec4.path === "string" ? rec4.path : null;
15899
+ const path94 = typeof rec4.path === "string" ? rec4.path : null;
15900
15900
  const content = typeof rec4.content === "string" ? rec4.content : null;
15901
- if (!path93 || content == null) return null;
15902
- const out = { path: path93, content };
15901
+ if (!path94 || content == null) return null;
15902
+ const out = { path: path94, content };
15903
15903
  if (typeof rec4.mtimeMs === "number") out.mtimeMs = rec4.mtimeMs;
15904
15904
  return out;
15905
15905
  }).filter((m2) => m2 !== null);
@@ -36873,8 +36873,8 @@ var require_utils = __commonJS({
36873
36873
  var result = transform[inputType][outputType](input);
36874
36874
  return result;
36875
36875
  };
36876
- exports2.resolve = function(path93) {
36877
- var parts = path93.split("/");
36876
+ exports2.resolve = function(path94) {
36877
+ var parts = path94.split("/");
36878
36878
  var result = [];
36879
36879
  for (var index = 0; index < parts.length; index++) {
36880
36880
  var part = parts[index];
@@ -42727,18 +42727,18 @@ var require_object = __commonJS({
42727
42727
  var object = new ZipObject(name, zipObjectContent, o);
42728
42728
  this.files[name] = object;
42729
42729
  };
42730
- var parentFolder = function(path93) {
42731
- if (path93.slice(-1) === "/") {
42732
- path93 = path93.substring(0, path93.length - 1);
42730
+ var parentFolder = function(path94) {
42731
+ if (path94.slice(-1) === "/") {
42732
+ path94 = path94.substring(0, path94.length - 1);
42733
42733
  }
42734
- var lastSlash = path93.lastIndexOf("/");
42735
- return lastSlash > 0 ? path93.substring(0, lastSlash) : "";
42734
+ var lastSlash = path94.lastIndexOf("/");
42735
+ return lastSlash > 0 ? path94.substring(0, lastSlash) : "";
42736
42736
  };
42737
- var forceTrailingSlash = function(path93) {
42738
- if (path93.slice(-1) !== "/") {
42739
- path93 += "/";
42737
+ var forceTrailingSlash = function(path94) {
42738
+ if (path94.slice(-1) !== "/") {
42739
+ path94 += "/";
42740
42740
  }
42741
- return path93;
42741
+ return path94;
42742
42742
  };
42743
42743
  var folderAdd = function(name, createFolders) {
42744
42744
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
@@ -43740,7 +43740,7 @@ var require_lib3 = __commonJS({
43740
43740
  // src/run-case/recorder.ts
43741
43741
  function startRunCaseRecorder(opts) {
43742
43742
  const now = opts.now ?? Date.now;
43743
- const dir = import_node_path75.default.dirname(opts.recordPath);
43743
+ const dir = import_node_path76.default.dirname(opts.recordPath);
43744
43744
  let stream = null;
43745
43745
  let closing = false;
43746
43746
  let closedSettled = false;
@@ -43754,8 +43754,8 @@ function startRunCaseRecorder(opts) {
43754
43754
  });
43755
43755
  const ensureStream = () => {
43756
43756
  if (stream) return stream;
43757
- import_node_fs54.default.mkdirSync(dir, { recursive: true });
43758
- stream = import_node_fs54.default.createWriteStream(opts.recordPath, { flags: "a" });
43757
+ import_node_fs55.default.mkdirSync(dir, { recursive: true });
43758
+ stream = import_node_fs55.default.createWriteStream(opts.recordPath, { flags: "a" });
43759
43759
  stream.on("close", () => closedResolve());
43760
43760
  return stream;
43761
43761
  };
@@ -43780,12 +43780,12 @@ function startRunCaseRecorder(opts) {
43780
43780
  };
43781
43781
  return { tap, close, closed };
43782
43782
  }
43783
- var import_node_fs54, import_node_path75;
43783
+ var import_node_fs55, import_node_path76;
43784
43784
  var init_recorder = __esm({
43785
43785
  "src/run-case/recorder.ts"() {
43786
43786
  "use strict";
43787
- import_node_fs54 = __toESM(require("fs"), 1);
43788
- import_node_path75 = __toESM(require("path"), 1);
43787
+ import_node_fs55 = __toESM(require("fs"), 1);
43788
+ import_node_path76 = __toESM(require("path"), 1);
43789
43789
  }
43790
43790
  });
43791
43791
 
@@ -43828,7 +43828,7 @@ var init_wire = __esm({
43828
43828
  // src/run-case/controller.ts
43829
43829
  async function runController(opts) {
43830
43830
  const now = opts.now ?? Date.now;
43831
- const cwd = opts.cwd ?? (0, import_node_fs55.mkdtempSync)(import_node_path76.default.join(import_node_os27.default.tmpdir(), "clawd-runcase-"));
43831
+ const cwd = opts.cwd ?? (0, import_node_fs56.mkdtempSync)(import_node_path77.default.join(import_node_os27.default.tmpdir(), "clawd-runcase-"));
43832
43832
  const ownsCwd = opts.cwd === void 0;
43833
43833
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
43834
43834
  const spawnCtx = { cwd };
@@ -43989,19 +43989,19 @@ async function runController(opts) {
43989
43989
  if (sigintHandler) process.off("SIGINT", sigintHandler);
43990
43990
  if (ownsCwd) {
43991
43991
  try {
43992
- (0, import_node_fs55.rmSync)(cwd, { recursive: true, force: true });
43992
+ (0, import_node_fs56.rmSync)(cwd, { recursive: true, force: true });
43993
43993
  } catch {
43994
43994
  }
43995
43995
  }
43996
43996
  return exitCode ?? 0;
43997
43997
  }
43998
- var import_node_fs55, import_node_os27, import_node_path76;
43998
+ var import_node_fs56, import_node_os27, import_node_path77;
43999
43999
  var init_controller = __esm({
44000
44000
  "src/run-case/controller.ts"() {
44001
44001
  "use strict";
44002
- import_node_fs55 = require("fs");
44002
+ import_node_fs56 = require("fs");
44003
44003
  import_node_os27 = __toESM(require("os"), 1);
44004
- import_node_path76 = __toESM(require("path"), 1);
44004
+ import_node_path77 = __toESM(require("path"), 1);
44005
44005
  init_claude();
44006
44006
  init_stdout_splitter();
44007
44007
  init_permission_stdio();
@@ -44276,8 +44276,8 @@ Env (advanced):
44276
44276
  `;
44277
44277
 
44278
44278
  // src/index.ts
44279
- var import_node_path74 = __toESM(require("path"), 1);
44280
- var import_node_fs53 = __toESM(require("fs"), 1);
44279
+ var import_node_path75 = __toESM(require("path"), 1);
44280
+ var import_node_fs54 = __toESM(require("fs"), 1);
44281
44281
  var import_node_os26 = __toESM(require("os"), 1);
44282
44282
  var import_node_crypto22 = require("crypto");
44283
44283
  var import_node_url4 = require("url");
@@ -45582,7 +45582,9 @@ function buildSpawnContext(state, deps) {
45582
45582
  if (dispatchId) env.CLAWD_DISPATCH_ID = dispatchId;
45583
45583
  const personaId2 = file.ownerPersonaId;
45584
45584
  if (personaId2) env.CLAWD_PERSONA_ID = personaId2;
45585
- const mcpConfigs = deps.getMcpConfigs?.() ?? [];
45585
+ const mcpConfigs = (deps.getMcpConfigs?.() ?? []).filter(
45586
+ (c) => !c.personas || personaId2 != null && c.personas.includes(personaId2)
45587
+ );
45586
45588
  const ctx = {
45587
45589
  cwd: file.cwd,
45588
45590
  toolSessionId: file.toolSessionId,
@@ -51424,8 +51426,8 @@ function turnStartInput(text) {
51424
51426
  const items = [];
51425
51427
  let leftover = text;
51426
51428
  for (const m2 of text.matchAll(SKILL_RE)) {
51427
- const [marker, name, path93] = m2;
51428
- items.push({ type: "skill", name, path: path93 });
51429
+ const [marker, name, path94] = m2;
51430
+ items.push({ type: "skill", name, path: path94 });
51429
51431
  leftover = leftover.replace(marker, "");
51430
51432
  }
51431
51433
  for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
@@ -56537,13 +56539,13 @@ function mapSkillsListResponse(res) {
56537
56539
  const r = s ?? {};
56538
56540
  const name = str3(r.name);
56539
56541
  if (!name) continue;
56540
- const path93 = str3(r.path);
56542
+ const path94 = str3(r.path);
56541
56543
  const description = str3(r.description);
56542
56544
  const isPlugin = name.includes(":");
56543
56545
  out.push({
56544
56546
  name,
56545
56547
  source: isPlugin ? "plugin" : "project",
56546
- ...path93 ? { path: path93 } : {},
56548
+ ...path94 ? { path: path94 } : {},
56547
56549
  ...description ? { description } : {},
56548
56550
  ...isPlugin ? { plugin: name.split(":")[0] } : {}
56549
56551
  });
@@ -60865,9 +60867,9 @@ var CentralClientError = class extends Error {
60865
60867
  code;
60866
60868
  cause;
60867
60869
  };
60868
- async function centralRequest(opts, path93, init) {
60870
+ async function centralRequest(opts, path94, init) {
60869
60871
  const f = opts.fetchImpl ?? globalThis.fetch;
60870
- const url = `${opts.api.replace(/\/+$/, "")}${path93}`;
60872
+ const url = `${opts.api.replace(/\/+$/, "")}${path94}`;
60871
60873
  const ctrl = new AbortController();
60872
60874
  const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
60873
60875
  let res;
@@ -61348,7 +61350,7 @@ var ServerKeyStore = class {
61348
61350
  init_protocol();
61349
61351
 
61350
61352
  // src/handlers/index.ts
61351
- var import_node_path70 = __toESM(require("path"), 1);
61353
+ var import_node_path68 = __toESM(require("path"), 1);
61352
61354
 
61353
61355
  // src/handlers/session.ts
61354
61356
  init_protocol();
@@ -63689,7 +63691,7 @@ function computeMethodAccess(args) {
63689
63691
  }
63690
63692
 
63691
63693
  // src/version.ts
63692
- var version = "0.2.453".length > 0 ? "0.2.453" : "dev";
63694
+ var version = "0.2.454".length > 0 ? "0.2.454" : "dev";
63693
63695
 
63694
63696
  // src/cli-probe/probe.ts
63695
63697
  var fs68 = __toESM(require("fs"), 1);
@@ -64717,218 +64719,8 @@ function buildExtensionHandlers(deps) {
64717
64719
  };
64718
64720
  }
64719
64721
 
64720
- // src/app-builder/project-store.ts
64721
- var import_node_fs50 = require("fs");
64722
- var import_node_child_process15 = require("child_process");
64723
- var import_node_path65 = require("path");
64724
- init_protocol();
64725
- var PROJECTS_DIR = "projects";
64726
- var META_FILE = ".clawd-project.json";
64727
- var DEFAULT_TEMPLATE = "nestjs-react";
64728
- var SCAFFOLD_TIMEOUT_MS = 3e4;
64729
- var ProjectStore = class {
64730
- /** @param root projects 父目录,例如 `~/.clawd/users/<userId>/`(v2 per-user) */
64731
- constructor(root) {
64732
- this.root = root;
64733
- }
64734
- root;
64735
- /** projects/<name>/.clawd-project.json 路径 */
64736
- metaPath(name) {
64737
- return (0, import_node_path65.join)(this.projectsRoot(), name, META_FILE);
64738
- }
64739
- /** projects/<name>/ 目录路径(cwd 用) */
64740
- projectDir(name) {
64741
- return (0, import_node_path65.join)(this.projectsRoot(), name);
64742
- }
64743
- projectsRoot() {
64744
- return (0, import_node_path65.join)(this.root, PROJECTS_DIR);
64745
- }
64746
- async list() {
64747
- let entries;
64748
- try {
64749
- entries = await import_node_fs50.promises.readdir(this.projectsRoot());
64750
- } catch (err) {
64751
- if (err.code === "ENOENT") return [];
64752
- throw err;
64753
- }
64754
- const out = [];
64755
- for (const name of entries) {
64756
- try {
64757
- const raw = await import_node_fs50.promises.readFile(this.metaPath(name), "utf8");
64758
- const json = JSON.parse(raw);
64759
- let migrated = false;
64760
- if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
64761
- json.devCommand = DEFAULT_DEV_COMMAND;
64762
- migrated = true;
64763
- }
64764
- const parsed = ProjectMetadataSchema.safeParse(json);
64765
- if (parsed.success) {
64766
- out.push(parsed.data);
64767
- if (migrated) {
64768
- void import_node_fs50.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
64769
- });
64770
- }
64771
- }
64772
- } catch {
64773
- }
64774
- }
64775
- return out.sort((a, b2) => a.name.localeCompare(b2.name));
64776
- }
64777
- /**
64778
- * @param reservedPorts 其它 user 已占端口(v2 per-user:端口必须全局唯一,所有 user 的
64779
- * dev server 同机跑;per-user store 各自只 list 自己,靠 handler 收集全局端口传进来,
64780
- * 否则不同 user 的首个项目都从 PROJECT_PORT_MIN 起 → 全撞 6173)。
64781
- */
64782
- async create(name, reservedPorts) {
64783
- const existing = await this.list();
64784
- if (existing.some((p2) => p2.name === name)) {
64785
- throw new Error(`project "${name}" already exists / \u5DF2\u5B58\u5728`);
64786
- }
64787
- const usedPorts = new Set(existing.map((p2) => p2.port));
64788
- if (reservedPorts) for (const p2 of reservedPorts) usedPorts.add(p2);
64789
- let port = -1;
64790
- for (let p2 = PROJECT_PORT_MIN; p2 <= PROJECT_PORT_MAX; p2++) {
64791
- if (!usedPorts.has(p2)) {
64792
- port = p2;
64793
- break;
64794
- }
64795
- }
64796
- if (port < 0) {
64797
- throw new Error(
64798
- `port pool exhausted / \u7AEF\u53E3\u6C60\u5DF2\u6EE1\uFF08${PROJECT_PORT_MIN}-${PROJECT_PORT_MAX}\uFF09\uFF0C\u5148\u5220\u4E00\u4E2A project \u91CA\u653E\u7AEF\u53E3`
64799
- );
64800
- }
64801
- const meta = {
64802
- name,
64803
- port,
64804
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
64805
- devCommand: DEFAULT_DEV_COMMAND
64806
- };
64807
- const validated = ProjectMetadataSchema.safeParse(meta);
64808
- if (!validated.success) {
64809
- throw new Error(`invalid name "${name}": ${validated.error.message}`);
64810
- }
64811
- const dir = this.projectDir(name);
64812
- await import_node_fs50.promises.mkdir(dir, { recursive: true });
64813
- await import_node_fs50.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
64814
- return meta;
64815
- }
64816
- /**
64817
- * 同步内联 scaffold:spawn `extension-kit/scripts/new-extension.sh <name>` 等 exit。
64818
- * 必须在 `create()` 之后调(脚本期望 projects/<name>/.clawd-project.json 已就位 = picker 模式)。
64819
- * 失败抛错;上层(handler)应当 catch 后回滚(删 dir + 不 mutate session)。
64820
- *
64821
- * spec 2026-06-02 §5.6.3:scaffold 对 assistant 不可见,避免 assistant 被诱导去"读 example
64822
- * 代码再参考"。assistant 进 project 时目录已是完整模板,直接 pnpm install 即可。
64823
- */
64824
- async scaffold(name, templateSrcDir, scaffoldScriptPath) {
64825
- const destDir = this.projectDir(name);
64826
- return await new Promise((resolve9, reject) => {
64827
- const child = (0, import_node_child_process15.spawn)("bash", [scaffoldScriptPath, name, templateSrcDir, destDir], {
64828
- env: { ...process.env, PATH: process.env.PATH ?? "" },
64829
- stdio: ["ignore", "pipe", "pipe"]
64830
- });
64831
- let stdout = "";
64832
- let stderr = "";
64833
- child.stdout.on("data", (d) => {
64834
- stdout += d.toString("utf8");
64835
- });
64836
- child.stderr.on("data", (d) => {
64837
- stderr += d.toString("utf8");
64838
- });
64839
- const timer = setTimeout(() => {
64840
- child.kill("SIGKILL");
64841
- reject(new Error(`scaffold timeout (>${SCAFFOLD_TIMEOUT_MS}ms): ${scaffoldScriptPath} ${name}`));
64842
- }, SCAFFOLD_TIMEOUT_MS);
64843
- child.on("error", (err) => {
64844
- clearTimeout(timer);
64845
- reject(new Error(`scaffold spawn failed: ${err.message}`));
64846
- });
64847
- child.on("exit", (code, signal) => {
64848
- clearTimeout(timer);
64849
- if (code === 0) resolve9({ stdout, stderr });
64850
- else reject(new Error(`scaffold exit ${code ?? signal}: ${stderr.slice(0, 500)}`));
64851
- });
64852
- });
64853
- }
64854
- async delete(name) {
64855
- const dir = this.projectDir(name);
64856
- await import_node_fs50.promises.rm(dir, { recursive: true, force: true });
64857
- }
64858
- async updatePort(name, newPort) {
64859
- if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
64860
- throw new Error(
64861
- `port range invalid: must be ${PROJECT_PORT_MIN}-${PROJECT_PORT_MAX}, got ${newPort}`
64862
- );
64863
- }
64864
- const list = await this.list();
64865
- const target = list.find((p2) => p2.name === name);
64866
- if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
64867
- const conflict = list.find((p2) => p2.name !== name && p2.port === newPort);
64868
- if (conflict) {
64869
- throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
64870
- }
64871
- const updated = { ...target, port: newPort };
64872
- await import_node_fs50.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
64873
- return updated;
64874
- }
64875
- /**
64876
- * 单栏 refactor (spec 2026-06-02 §5.6.1):发布上线后写公网 URL 到 .clawd-project.json.prodUrl。
64877
- * UI 通过 listProjects 读到 prodUrl 后切预览栏到线上 URL,取代旧 prod marker 机制。
64878
- * 重复调用幂等(覆盖旧 URL)。url 由 ProjectMetadataSchema 校验 url() 格式。
64879
- */
64880
- async setProdUrl(name, url) {
64881
- const list = await this.list();
64882
- const target = list.find((p2) => p2.name === name);
64883
- if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
64884
- const updated = { ...target, prodUrl: url };
64885
- const validated = ProjectMetadataSchema.safeParse(updated);
64886
- if (!validated.success) {
64887
- throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
64888
- }
64889
- await import_node_fs50.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
64890
- return validated.data;
64891
- }
64892
- /**
64893
- * 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design §5.1.4):
64894
- * 写 publishJob 字段到 .clawd-project.json。仅承载 in-flight 状态 ——
64895
- * - daemon 收到第一个 ::stage::build marker 时写入
64896
- * - 每次 stage 切换更新 stage 字段(jobId / startedAt 不变)
64897
- * - 成功 / 失败 / dismiss 时调 clearPublishJob 清掉
64898
- * - 只有 daemon 崩才会留残骸
64899
- * 写入时保留所有其他字段(prodUrl 等不受影响)。
64900
- */
64901
- async setPublishJob(name, job) {
64902
- const list = await this.list();
64903
- const target = list.find((p2) => p2.name === name);
64904
- if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
64905
- const updated = { ...target, publishJob: job };
64906
- const validated = ProjectMetadataSchema.safeParse(updated);
64907
- if (!validated.success) {
64908
- throw new Error(`invalid publishJob: ${validated.error.message}`);
64909
- }
64910
- await import_node_fs50.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
64911
- return validated.data;
64912
- }
64913
- /** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
64914
- async clearPublishJob(name) {
64915
- const list = await this.list();
64916
- const target = list.find((p2) => p2.name === name);
64917
- if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
64918
- if (!target.publishJob) return target;
64919
- const { publishJob: _drop, ...rest } = target;
64920
- void _drop;
64921
- const validated = ProjectMetadataSchema.safeParse(rest);
64922
- if (!validated.success) {
64923
- throw new Error(`failed to clear publishJob: ${validated.error.message}`);
64924
- }
64925
- await import_node_fs50.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
64926
- return validated.data;
64927
- }
64928
- };
64929
-
64930
64722
  // src/app-builder/kill-port.ts
64931
- var import_node_child_process16 = require("child_process");
64723
+ var import_node_child_process15 = require("child_process");
64932
64724
  async function killPortOccupants(port, ownedPids, logger) {
64933
64725
  let pids;
64934
64726
  try {
@@ -64970,7 +64762,7 @@ async function killPortOccupants(port, ownedPids, logger) {
64970
64762
  }
64971
64763
  function listPidsOnPort(port) {
64972
64764
  return new Promise((resolve9, reject) => {
64973
- (0, import_node_child_process16.execFile)(
64765
+ (0, import_node_child_process15.execFile)(
64974
64766
  "lsof",
64975
64767
  ["-ti", `:${port}`],
64976
64768
  { timeout: 3e3 },
@@ -64991,361 +64783,15 @@ function listPidsOnPort(port) {
64991
64783
  });
64992
64784
  }
64993
64785
 
64994
- // src/app-builder/publish-registry.ts
64995
- var import_node_crypto20 = require("crypto");
64996
- var PublishJobRegistry = class {
64997
- jobs = /* @__PURE__ */ new Map();
64998
- has(name) {
64999
- return this.jobs.has(name);
65000
- }
65001
- get(name) {
65002
- return this.jobs.get(name);
65003
- }
65004
- /**
65005
- * args.jobId 仅用于测试注入;生产代码不传,registry 自己生成。
65006
- * args.now 仅用于测试注入;生产代码不传,用 Date.now()。
65007
- */
65008
- create(args) {
65009
- if (this.jobs.has(args.name)) {
65010
- throw new Error(`already publishing: ${args.name}`);
65011
- }
65012
- const jobId = args.jobId ?? `job-${(0, import_node_crypto20.randomUUID)()}`;
65013
- this.jobs.set(args.name, {
65014
- jobId,
65015
- name: args.name,
65016
- stage: "build",
65017
- sessionId: args.sessionId,
65018
- childProc: args.childProc,
65019
- dismissed: false,
65020
- sawAnyStage: false,
65021
- startedAt: args.now ?? Date.now()
65022
- });
65023
- return jobId;
65024
- }
65025
- updateStage(name, stage) {
65026
- const job = this.jobs.get(name);
65027
- if (!job) return;
65028
- job.stage = stage;
65029
- job.sawAnyStage = true;
65030
- }
65031
- markDismissed(name) {
65032
- const job = this.jobs.get(name);
65033
- if (!job) return;
65034
- job.dismissed = true;
65035
- }
65036
- remove(name) {
65037
- const job = this.jobs.get(name);
65038
- if (!job) return void 0;
65039
- this.jobs.delete(name);
65040
- return { sessionId: job.sessionId };
65041
- }
65042
- };
65043
-
65044
- // src/app-builder/publish-job-runner.ts
65045
- var import_node_child_process17 = require("child_process");
65046
- var import_node_fs51 = require("fs");
65047
- var import_node_path66 = require("path");
65048
-
65049
- // src/app-builder/publish-stage-parser.ts
65050
- var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
65051
- var PROD_URL_RE = /^\s*::prod-url::(\S+)\s*$/;
65052
- var URL_RE = /\bhttps?:\/\/[^\s]+/;
65053
- function parsePublishLine(line) {
65054
- const stageMatch = STAGE_RE.exec(line);
65055
- if (stageMatch) return { kind: "stage", stage: stageMatch[1] };
65056
- const urlMatch = PROD_URL_RE.exec(line);
65057
- if (urlMatch) return { kind: "prod-url", url: urlMatch[1] };
65058
- return null;
65059
- }
65060
- function extractProdUrl(text) {
65061
- const m2 = URL_RE.exec(text);
65062
- return m2 ? m2[0] : null;
65063
- }
65064
- function tailStderrLines(buf, n) {
65065
- if (!buf) return "";
65066
- const lines = buf.split("\n");
65067
- if (lines.length <= n) return buf;
65068
- return lines.slice(lines.length - n).join("\n");
65069
- }
65070
-
65071
- // src/app-builder/publish-job-runner.ts
65072
- async function startPublishJob(deps, args) {
65073
- const { registry: registry2, projectDir } = deps;
65074
- const spawn13 = deps.spawnImpl ?? import_node_child_process17.spawn;
65075
- if (registry2.has(args.name)) {
65076
- return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
65077
- }
65078
- const projDir = projectDir(args.name);
65079
- const logPath = (0, import_node_path66.join)(projDir, ".publish.log");
65080
- let logStream = null;
65081
- try {
65082
- logStream = (0, import_node_fs51.createWriteStream)(logPath, { flags: "w" });
65083
- } catch {
65084
- logStream = null;
65085
- }
65086
- const child = spawn13("bash", [args.scriptPath, projDir, args.personaRoot ?? ""], {
65087
- cwd: projDir,
65088
- env: process.env,
65089
- stdio: ["ignore", "pipe", "pipe"]
65090
- });
65091
- const jobId = registry2.create({
65092
- name: args.name,
65093
- sessionId: args.sessionId,
65094
- childProc: child
65095
- });
65096
- deps.logger?.info("app-builder.publish.spawned", {
65097
- name: args.name,
65098
- jobId,
65099
- pid: child.pid ?? null,
65100
- scriptPath: args.scriptPath,
65101
- logPath
65102
- });
65103
- child.on("error", (err) => {
65104
- deps.logger?.warn("app-builder.publish.spawn-error", {
65105
- name: args.name,
65106
- jobId,
65107
- error: err instanceof Error ? err.message : String(err)
65108
- });
65109
- void handleExit(deps, {
65110
- name: args.name,
65111
- jobId,
65112
- code: null,
65113
- signal: null,
65114
- urlCandidate: null,
65115
- stderrTail: err instanceof Error ? err.message : String(err),
65116
- logPath
65117
- });
65118
- });
65119
- let urlFromMarker = null;
65120
- let urlFallback = null;
65121
- let stdoutBuf = "";
65122
- child.stdout?.on("data", (chunk) => {
65123
- if (logStream) logStream.write(chunk);
65124
- stdoutBuf += chunk.toString();
65125
- let nlIdx;
65126
- while ((nlIdx = stdoutBuf.indexOf("\n")) >= 0) {
65127
- const line = stdoutBuf.slice(0, nlIdx);
65128
- stdoutBuf = stdoutBuf.slice(nlIdx + 1);
65129
- handleStdoutLine(deps, args.name, jobId, line);
65130
- const parsedForUrl = parsePublishLine(line);
65131
- if (parsedForUrl?.kind === "prod-url") urlFromMarker = parsedForUrl.url;
65132
- const url = extractProdUrl(line);
65133
- if (url && !urlFallback) urlFallback = url;
65134
- }
65135
- });
65136
- let stderrBuf = "";
65137
- child.stderr?.on("data", (chunk) => {
65138
- if (logStream) logStream.write(chunk);
65139
- stderrBuf += chunk.toString();
65140
- });
65141
- child.on("exit", (code, signal) => {
65142
- if (logStream) logStream.end();
65143
- const urlCandidate = urlFromMarker ?? urlFallback;
65144
- deps.logger?.info("app-builder.publish.child-exit", {
65145
- name: args.name,
65146
- jobId,
65147
- code,
65148
- signal,
65149
- hadUrl: !!urlCandidate,
65150
- urlSource: urlFromMarker ? "marker" : urlFallback ? "fallback" : "none",
65151
- stderrBytes: stderrBuf.length
65152
- });
65153
- void handleExit(deps, {
65154
- name: args.name,
65155
- jobId,
65156
- code,
65157
- signal,
65158
- urlCandidate,
65159
- stderrTail: tailStderrLines(stderrBuf, 20),
65160
- logPath
65161
- });
65162
- });
65163
- return { jobId, status: "started" };
65164
- }
65165
- function handleStdoutLine(deps, name, jobId, line) {
65166
- const parsed = parsePublishLine(line);
65167
- if (parsed?.kind !== "stage") return;
65168
- advanceStage(deps, name, jobId, parsed.stage);
65169
- }
65170
- function advanceStage(deps, name, jobId, next) {
65171
- const job = deps.registry.get(name);
65172
- if (!job) return;
65173
- const prev = job.stage;
65174
- const wasFirstStage = !job.sawAnyStage;
65175
- deps.logger?.info("app-builder.publish.stage-advance", {
65176
- name,
65177
- jobId,
65178
- prev: wasFirstStage ? null : prev,
65179
- next,
65180
- firstStage: wasFirstStage
65181
- });
65182
- if (wasFirstStage) {
65183
- deps.registry.updateStage(name, next);
65184
- void deps.setPublishJob(name, {
65185
- jobId,
65186
- stage: next,
65187
- startedAt: job.startedAt
65188
- });
65189
- deps.broadcast({
65190
- type: "appBuilder:publish-progress",
65191
- name,
65192
- jobId,
65193
- stage: next,
65194
- status: "started"
65195
- });
65196
- return;
65197
- }
65198
- if (prev === next) {
65199
- return;
65200
- }
65201
- deps.broadcast({
65202
- type: "appBuilder:publish-progress",
65203
- name,
65204
- jobId,
65205
- stage: prev,
65206
- status: "completed"
65207
- });
65208
- deps.registry.updateStage(name, next);
65209
- void deps.setPublishJob(name, {
65210
- jobId,
65211
- stage: next,
65212
- startedAt: job.startedAt
65213
- });
65214
- deps.broadcast({
65215
- type: "appBuilder:publish-progress",
65216
- name,
65217
- jobId,
65218
- stage: next,
65219
- status: "started"
65220
- });
65221
- }
65222
- async function handleExit(deps, args) {
65223
- const job = deps.registry.get(args.name);
65224
- if (args.signal === "SIGTERM" && job?.dismissed) {
65225
- try {
65226
- await deps.clearPublishJob(args.name);
65227
- } catch (err) {
65228
- console.error("[publish] clearPublishJob failed (dismissed branch)", err);
65229
- }
65230
- deps.registry.remove(args.name);
65231
- return;
65232
- }
65233
- if (args.code === 0 && args.urlCandidate) {
65234
- if (job?.sawAnyStage) {
65235
- deps.broadcast({
65236
- type: "appBuilder:publish-progress",
65237
- name: args.name,
65238
- jobId: args.jobId,
65239
- stage: job.stage,
65240
- status: "completed"
65241
- });
65242
- }
65243
- try {
65244
- await deps.setProdUrl(args.name, args.urlCandidate);
65245
- } catch (err) {
65246
- console.error("[publish] setProdUrl failed; falling through to failure branch", err);
65247
- await emitFailure(deps, args, "\u8C03 setProdUrl \u5931\u8D25\uFF1A" + (err instanceof Error ? err.message : String(err)));
65248
- return;
65249
- }
65250
- try {
65251
- await deps.clearPublishJob(args.name);
65252
- } catch (err) {
65253
- console.error("[publish] clearPublishJob failed (success branch)", err);
65254
- }
65255
- deps.registry.remove(args.name);
65256
- return;
65257
- }
65258
- await emitFailure(deps, args, buildErrorSummary(args));
65259
- }
65260
- async function emitFailure(deps, args, errorSummary) {
65261
- const job = deps.registry.get(args.name);
65262
- const failStage = job?.sawAnyStage ? job.stage : "unknown";
65263
- deps.logger?.warn("app-builder.publish.failed", {
65264
- name: args.name,
65265
- jobId: args.jobId,
65266
- stage: failStage,
65267
- errorSummary: errorSummary.slice(0, 500)
65268
- });
65269
- try {
65270
- await deps.clearPublishJob(args.name);
65271
- } catch (err) {
65272
- deps.logger?.warn("app-builder.publish.clear-field-failed", {
65273
- name: args.name,
65274
- jobId: args.jobId,
65275
- error: err instanceof Error ? err.message : String(err)
65276
- });
65277
- }
65278
- const removed = deps.registry.remove(args.name);
65279
- deps.broadcast({
65280
- type: "appBuilder:publish-failed",
65281
- name: args.name,
65282
- jobId: args.jobId,
65283
- stage: failStage,
65284
- errorSummary
65285
- });
65286
- if (removed) {
65287
- const msg = formatFailureMessage(failStage, errorSummary, args.logPath);
65288
- try {
65289
- await deps.injectUserText(removed.sessionId, msg);
65290
- deps.logger?.info("app-builder.publish.failure-injected-to-chat", {
65291
- name: args.name,
65292
- jobId: args.jobId,
65293
- sessionId: removed.sessionId
65294
- });
65295
- } catch (err) {
65296
- deps.logger?.warn("app-builder.publish.inject-user-text-failed", {
65297
- name: args.name,
65298
- jobId: args.jobId,
65299
- sessionId: removed.sessionId,
65300
- error: err instanceof Error ? err.message : String(err)
65301
- });
65302
- }
65303
- } else {
65304
- deps.logger?.warn("app-builder.publish.no-registry-entry-on-failure", {
65305
- name: args.name,
65306
- jobId: args.jobId
65307
- });
65308
- }
65309
- }
65310
- function buildErrorSummary(args) {
65311
- if (args.signal) {
65312
- return `publish.sh \u88AB\u4E2D\u65AD\uFF08signal: ${args.signal}\uFF09`;
65313
- }
65314
- if (args.code === 0 && !args.urlCandidate) {
65315
- return "publish.sh \u6210\u529F\u9000\u51FA\u4F46\u672A\u8F93\u51FA\u516C\u7F51 URL";
65316
- }
65317
- return args.stderrTail || `publish.sh \u5F02\u5E38\u9000\u51FA\uFF08exit code: ${args.code}\uFF09`;
65318
- }
65319
- function formatFailureMessage(stage, errorSummary, logPath) {
65320
- return `\u53D1\u5E03\u5931\u8D25\uFF1A[${stage}] \u9636\u6BB5
65321
- ${errorSummary}
65322
-
65323
- \u5B8C\u6574\u65E5\u5FD7\uFF1A${logPath}
65324
- \u8BF7\u4F60\u63A5\u7BA1\u5B8C\u6210\u672C\u6B21\u53D1\u5E03\uFF1A\u5148\u6839\u636E\u65E5\u5FD7\u4FEE\u590D\u95EE\u9898\uFF0C\u7136\u540E\u91CD\u8DD1 publish.sh \u76F4\u5230\u4E0A\u7EBF\u6210\u529F\uFF0C\u6700\u540E\u7528 appBuilder.setProdUrl \u5199\u5165\u516C\u7F51 URL\u3002`;
65325
- }
65326
- async function recoverInterruptedJobs(deps) {
65327
- const projects = await deps.listAllProjects();
65328
- for (const p2 of projects) {
65329
- if (p2.publishJob) {
65330
- deps.log(
65331
- `[publish] found interrupted job ${p2.publishJob.jobId} for ${p2.name}, stage=${p2.publishJob.stage}`
65332
- );
65333
- }
65334
- }
65335
- }
64786
+ // src/app-builder/ports.ts
64787
+ var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
65336
64788
 
65337
64789
  // src/handlers/app-builder.ts
65338
64790
  init_protocol();
65339
- var import_node_path67 = require("path");
65340
- var import_node_fs52 = require("fs");
65341
- var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
64791
+ var import_node_path65 = require("path");
64792
+ var import_node_fs50 = require("fs");
64793
+ var DEFAULT_TEMPLATE = "nestjs-react";
65342
64794
  var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
65343
- async function recoverInterruptedPublishJobs(store, logger) {
65344
- await recoverInterruptedJobs({
65345
- listAllProjects: async () => store.list(),
65346
- log: (msg) => logger?.info(msg)
65347
- });
65348
- }
65349
64795
  function buildAppBuilderHandlers(deps) {
65350
64796
  const { store, devServerLifecycle, sessionManager, getTunnelUrl, broadcastProjectUpdated } = deps;
65351
64797
  function resolveStore(opts) {
@@ -65372,11 +64818,9 @@ function buildAppBuilderHandlers(deps) {
65372
64818
  }
65373
64819
  }
65374
64820
  const stages = /* @__PURE__ */ new Map();
65375
- const publishRegistry = new PublishJobRegistry();
65376
64821
  function withPublishJobStatus(meta) {
65377
64822
  if (!meta.publishJob) return void 0;
65378
- const job = publishRegistry.get(meta.name);
65379
- const inFlight = !!job && job.jobId === meta.publishJob.jobId;
64823
+ const inFlight = deps.publishService.inFlightJobId(meta.name) === meta.publishJob.jobId;
65380
64824
  return {
65381
64825
  ...meta.publishJob,
65382
64826
  status: inFlight ? "in-flight" : "interrupted"
@@ -65418,7 +64862,7 @@ function buildAppBuilderHandlers(deps) {
65418
64862
  async function listAllUsersProjects() {
65419
64863
  if (!deps.usersRoot || !deps.getStore) return [];
65420
64864
  const getStore = deps.getStore;
65421
- const userIds = await import_node_fs52.promises.readdir(deps.usersRoot).catch(() => []);
64865
+ const userIds = await import_node_fs50.promises.readdir(deps.usersRoot).catch(() => []);
65422
64866
  const perUser = await Promise.all(
65423
64867
  userIds.map((uid) => getStore(uid).list().catch(() => []))
65424
64868
  );
@@ -65484,26 +64928,26 @@ function buildAppBuilderHandlers(deps) {
65484
64928
  const userStore = resolveStore({ ctx, sessionId: f.sessionId });
65485
64929
  deps.logger?.info("app-builder.create-project.start", { name: f.name, sessionId: f.sessionId });
65486
64930
  const reservedPorts = new Set((await listAllUsersProjects()).map((p2) => p2.port));
65487
- const project = await userStore.create(f.name, reservedPorts);
64931
+ const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
64932
+ let project;
64933
+ let projectDir;
65488
64934
  try {
65489
- const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
65490
- const templateSrcDir = (0, import_node_path67.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
65491
- const scaffoldScript = (0, import_node_path67.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
65492
- const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
65493
- deps.logger?.info("app-builder.scaffold.done", {
65494
- name: project.name,
65495
- stdout: scaffoldResult.stdout.slice(0, 200)
64935
+ const created = await deps.createProjectOnDisk({
64936
+ store: userStore,
64937
+ name: f.name,
64938
+ templateSrcDir: (0, import_node_path65.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE),
64939
+ scaffoldScript: (0, import_node_path65.join)(deps.deployKitRoot, "scripts", "new-extension.sh"),
64940
+ reservedPorts,
64941
+ ...deps.logger ? { logger: deps.logger } : {}
65496
64942
  });
64943
+ project = created.project;
64944
+ projectDir = created.projectDir;
65497
64945
  } catch (err) {
65498
64946
  const message = err instanceof Error ? err.message : String(err);
65499
- deps.logger?.warn("app-builder.scaffold.failed", { name: project.name, error: message });
65500
- await userStore.delete(project.name).catch((delErr) => {
65501
- deps.logger?.warn("app-builder.scaffold.rollback-failed", {
65502
- name: project.name,
65503
- error: delErr instanceof Error ? delErr.message : String(delErr)
65504
- });
65505
- });
65506
- throw new ClawdError(ERROR_CODES.INTERNAL, `scaffold failed: ${message}`);
64947
+ if (message.startsWith("scaffold failed")) {
64948
+ throw new ClawdError(ERROR_CODES.INTERNAL, message);
64949
+ }
64950
+ throw err;
65507
64951
  }
65508
64952
  const { broadcast } = sessionManager.update({
65509
64953
  sessionId: f.sessionId,
@@ -65515,7 +64959,7 @@ function buildAppBuilderHandlers(deps) {
65515
64959
  port: project.port,
65516
64960
  sessionId: f.sessionId
65517
64961
  });
65518
- return { response: { project, projectDir: userStore.projectDir(project.name) }, broadcast };
64962
+ return { response: { project, projectDir }, broadcast };
65519
64963
  };
65520
64964
  const deleteProject = async (frame, _client, ctx) => {
65521
64965
  const { name } = parseArgs2(DeleteProjectArgsSchema, frame, "deleteProject");
@@ -65683,7 +65127,7 @@ function buildAppBuilderHandlers(deps) {
65683
65127
  `publish: project "${args.name}" has no bound session (orphan); cannot inject failure to chat`
65684
65128
  );
65685
65129
  }
65686
- if (project.publishJob && !publishRegistry.has(args.name)) {
65130
+ if (project.publishJob && deps.publishService.inFlightJobId(args.name) === null) {
65687
65131
  deps.logger?.info("app-builder.publish.clear-interrupted", {
65688
65132
  name: args.name,
65689
65133
  previousJobId: project.publishJob.jobId,
@@ -65692,28 +65136,25 @@ function buildAppBuilderHandlers(deps) {
65692
65136
  await userStore.clearPublishJob(args.name);
65693
65137
  }
65694
65138
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
65695
- const scriptPath = (0, import_node_path67.join)(deps.deployKitRoot, "scripts", "publish.sh");
65139
+ const scriptPath = (0, import_node_path65.join)(deps.deployKitRoot, "scripts", "publish.sh");
65696
65140
  deps.logger?.info("app-builder.publish.start", {
65697
65141
  name: args.name,
65698
65142
  sessionId: boundSession.sessionId,
65699
65143
  scriptPath,
65700
65144
  personaRoot
65701
65145
  });
65702
- const result = await startPublishJob(
65703
- {
65704
- registry: publishRegistry,
65705
- setPublishJob: async (name, job) => {
65706
- await userStore.setPublishJob(name, job);
65707
- const current = getStageOf(name);
65708
- await setStageAndBroadcast(name, current.stage, current.reason, userStore);
65709
- },
65710
- clearPublishJob: async (name) => {
65711
- await userStore.clearPublishJob(name);
65712
- const current = getStageOf(name);
65713
- await setStageAndBroadcast(name, current.stage, current.reason, userStore);
65714
- },
65715
- setProdUrl: async (name, url) => {
65716
- await userStore.setProdUrl(name, url);
65146
+ const result = await deps.publishService.start({
65147
+ name: args.name,
65148
+ sessionId: boundSession.sessionId,
65149
+ scriptPath,
65150
+ personaRoot,
65151
+ store: userStore,
65152
+ hooks: {
65153
+ // 元数据每次写盘(publishJob 起落 / prodUrl 回填)后重推一帧:UI 渲染 PublishProgress
65154
+ // 三段 checklist 依赖 project.publishJob.status==='in-flight',而 publishJob 字段只有
65155
+ // 通过 project-updated 帧才能到达 UI(publish-progress 帧 UI 端只用来设 currentStage,
65156
+ // 不更新 currentProject)。prodUrl 同理,靠这条链路让 PreviewPane 切线上预览。
65157
+ onProjectChanged: async (name) => {
65717
65158
  const current = getStageOf(name);
65718
65159
  await setStageAndBroadcast(name, current.stage, current.reason, userStore);
65719
65160
  },
@@ -65732,17 +65173,10 @@ function buildAppBuilderHandlers(deps) {
65732
65173
  for (const entry of broadcast) {
65733
65174
  deps.broadcastSessionFrame(entry.frame, entry.target);
65734
65175
  }
65735
- },
65736
- projectDir: (name) => userStore.projectDir(name),
65737
- logger: deps.logger
65176
+ }
65738
65177
  },
65739
- {
65740
- name: args.name,
65741
- sessionId: boundSession.sessionId,
65742
- scriptPath,
65743
- personaRoot
65744
- }
65745
- );
65178
+ ...deps.logger ? { logger: deps.logger } : {}
65179
+ });
65746
65180
  return { response: { ...result } };
65747
65181
  };
65748
65182
  const dismissPublishJob = async (frame, _client, ctx) => {
@@ -65757,15 +65191,13 @@ function buildAppBuilderHandlers(deps) {
65757
65191
  `dismissPublishJob: project "${args.name}" not found`
65758
65192
  );
65759
65193
  }
65760
- const job = publishRegistry.get(args.name);
65761
- if (job) {
65194
+ const inFlightJobId = deps.publishService.inFlightJobId(args.name);
65195
+ if (inFlightJobId) {
65762
65196
  deps.logger?.info("app-builder.publish.dismiss.in-flight", {
65763
65197
  name: args.name,
65764
- jobId: job.jobId,
65765
- stage: job.stage
65198
+ jobId: inFlightJobId
65766
65199
  });
65767
- publishRegistry.markDismissed(args.name);
65768
- job.childProc.kill("SIGTERM");
65200
+ deps.publishService.dismiss(args.name);
65769
65201
  } else if (project.publishJob) {
65770
65202
  deps.logger?.info("app-builder.publish.dismiss.interrupted", {
65771
65203
  name: args.name,
@@ -66451,7 +65883,7 @@ function buildVisitorHandlers(deps) {
66451
65883
 
66452
65884
  // src/extension/registry.ts
66453
65885
  var import_promises16 = __toESM(require("fs/promises"), 1);
66454
- var import_node_path68 = __toESM(require("path"), 1);
65886
+ var import_node_path66 = __toESM(require("path"), 1);
66455
65887
  async function loadAll(root) {
66456
65888
  let entries;
66457
65889
  try {
@@ -66464,13 +65896,13 @@ async function loadAll(root) {
66464
65896
  for (const ent of entries) {
66465
65897
  if (!ent.isDirectory()) continue;
66466
65898
  if (ent.name.startsWith(".")) continue;
66467
- records.push(await loadOne(import_node_path68.default.join(root, ent.name), ent.name));
65899
+ records.push(await loadOne(import_node_path66.default.join(root, ent.name), ent.name));
66468
65900
  }
66469
65901
  records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
66470
65902
  return records;
66471
65903
  }
66472
65904
  async function loadOne(dir, dirName) {
66473
- const manifestPath = import_node_path68.default.join(dir, "manifest.json");
65905
+ const manifestPath = import_node_path66.default.join(dir, "manifest.json");
66474
65906
  let raw;
66475
65907
  try {
66476
65908
  raw = await import_promises16.default.readFile(manifestPath, "utf8");
@@ -66515,7 +65947,7 @@ async function loadOne(dir, dirName) {
66515
65947
 
66516
65948
  // src/extension/uninstall.ts
66517
65949
  var import_promises17 = __toESM(require("fs/promises"), 1);
66518
- var import_node_path69 = __toESM(require("path"), 1);
65950
+ var import_node_path67 = __toESM(require("path"), 1);
66519
65951
  var UninstallError = class extends Error {
66520
65952
  constructor(code, message) {
66521
65953
  super(message);
@@ -66524,7 +65956,7 @@ var UninstallError = class extends Error {
66524
65956
  code;
66525
65957
  };
66526
65958
  async function uninstall(deps) {
66527
- const dir = import_node_path69.default.join(deps.root, deps.extId);
65959
+ const dir = import_node_path67.default.join(deps.root, deps.extId);
66528
65960
  try {
66529
65961
  await import_promises17.default.access(dir);
66530
65962
  } catch {
@@ -66535,13 +65967,13 @@ async function uninstall(deps) {
66535
65967
  }
66536
65968
 
66537
65969
  // src/handlers/index.ts
66538
- var import_node_crypto21 = require("crypto");
65970
+ var import_node_crypto20 = require("crypto");
66539
65971
 
66540
65972
  // src/handlers/peer-exec.ts
66541
65973
  init_protocol();
66542
65974
 
66543
65975
  // src/peer-ops/run-command.ts
66544
- var import_node_child_process18 = require("child_process");
65976
+ var import_node_child_process16 = require("child_process");
66545
65977
  var import_node_os24 = __toESM(require("os"), 1);
66546
65978
  var DEFAULT_TIMEOUT_MS5 = 3e4;
66547
65979
  var MAX_TIMEOUT_MS = 3e5;
@@ -66549,7 +65981,7 @@ var MAX_OUTPUT_BYTES = 1048576;
66549
65981
  function runCommand(command, opts = {}) {
66550
65982
  const timeoutMs = Math.min(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS5, MAX_TIMEOUT_MS);
66551
65983
  return new Promise((resolve9) => {
66552
- const child = (0, import_node_child_process18.spawn)("bash", ["-c", command], { cwd: import_node_os24.default.homedir() });
65984
+ const child = (0, import_node_child_process16.spawn)("bash", ["-c", command], { cwd: import_node_os24.default.homedir() });
66553
65985
  const out = [];
66554
65986
  const err = [];
66555
65987
  let outBytes = 0;
@@ -66691,7 +66123,7 @@ function buildMethodHandlers(deps) {
66691
66123
  const c = deps.contactStore.get(deviceId);
66692
66124
  return c ? { deviceId: c.deviceId, remoteUrl: c.remoteUrl, connectToken: c.connectToken } : null;
66693
66125
  },
66694
- genId: () => (0, import_node_crypto21.randomUUID)(),
66126
+ genId: () => (0, import_node_crypto20.randomUUID)(),
66695
66127
  now: () => Date.now(),
66696
66128
  forwardInboxPostToPeer,
66697
66129
  logger: deps.logger
@@ -66734,6 +66166,8 @@ function buildMethodHandlers(deps) {
66734
66166
  ...buildAppBuilderHandlers({
66735
66167
  store: deps.appBuilderStore,
66736
66168
  getStore: deps.getProjectStore,
66169
+ publishService: deps.publishService,
66170
+ createProjectOnDisk: deps.createProjectOnDisk,
66737
66171
  usersRoot: deps.usersRoot,
66738
66172
  devServerLifecycle: deps.devServerLifecycle,
66739
66173
  sessionManager: deps.manager,
@@ -66757,7 +66191,7 @@ function buildMethodHandlers(deps) {
66757
66191
  broadcast: deps.broadcastToOwners
66758
66192
  }),
66759
66193
  ...buildMasterHandlers({
66760
- authorityTablePath: import_node_path70.default.join(deps.masterDataDir, "master-authority.json")
66194
+ authorityTablePath: import_node_path68.default.join(deps.masterDataDir, "master-authority.json")
66761
66195
  }),
66762
66196
  ...buildTopicHandlers({
66763
66197
  topicStore: deps.topicStore,
@@ -66788,23 +66222,23 @@ function buildMethodHandlers(deps) {
66788
66222
  init_protocol();
66789
66223
 
66790
66224
  // src/tools/version-probe.ts
66791
- var import_node_child_process20 = require("child_process");
66225
+ var import_node_child_process18 = require("child_process");
66792
66226
  init_claude();
66793
66227
 
66794
66228
  // src/tools/shell-path.ts
66795
- var import_node_child_process19 = require("child_process");
66229
+ var import_node_child_process17 = require("child_process");
66796
66230
  var import_node_os25 = __toESM(require("os"), 1);
66797
66231
  function refreshPathFromLoginShell() {
66798
66232
  if (process.platform === "win32") return;
66799
66233
  const shell = process.env.SHELL ?? import_node_os25.default.userInfo().shell ?? "/bin/zsh";
66800
66234
  try {
66801
- const out = (0, import_node_child_process19.execFileSync)(shell, ["-ilc", 'command echo -n "$PATH"'], {
66235
+ const out = (0, import_node_child_process17.execFileSync)(shell, ["-ilc", 'command echo -n "$PATH"'], {
66802
66236
  encoding: "utf8",
66803
66237
  timeout: 3e3,
66804
66238
  stdio: ["ignore", "pipe", "ignore"]
66805
66239
  });
66806
- const path93 = out.trim();
66807
- if (path93) process.env.PATH = path93;
66240
+ const path94 = out.trim();
66241
+ if (path94) process.env.PATH = path94;
66808
66242
  } catch {
66809
66243
  }
66810
66244
  }
@@ -66815,7 +66249,7 @@ function parseVersionOutput(out) {
66815
66249
  }
66816
66250
  function execVersion(bin) {
66817
66251
  return new Promise((resolve9, reject) => {
66818
- (0, import_node_child_process20.execFile)(bin, ["--version"], { timeout: 5e3 }, (err, stdout) => {
66252
+ (0, import_node_child_process18.execFile)(bin, ["--version"], { timeout: 5e3 }, (err, stdout) => {
66819
66253
  if (err) reject(err);
66820
66254
  else resolve9(String(stdout));
66821
66255
  });
@@ -66927,8 +66361,242 @@ function buildOnboardingHandlers(deps) {
66927
66361
  return { "onboarding:complete": complete, "tools:probe": toolsProbe, "onboarding:iosInterest": iosInterest };
66928
66362
  }
66929
66363
 
66364
+ // src/app-builder-plugin/project-store.ts
66365
+ var import_node_fs51 = require("fs");
66366
+ var import_node_child_process19 = require("child_process");
66367
+ var import_node_path69 = require("path");
66368
+ init_protocol();
66369
+ var PROJECTS_DIR = "projects";
66370
+ var META_FILE = ".clawd-project.json";
66371
+ var SCAFFOLD_TIMEOUT_MS = 3e4;
66372
+ var metaWriteQueues = /* @__PURE__ */ new Map();
66373
+ function serializeMetaWrite(metaPath, op) {
66374
+ const prev = metaWriteQueues.get(metaPath) ?? Promise.resolve();
66375
+ const next = prev.then(op, op);
66376
+ const settled = next.then(
66377
+ () => void 0,
66378
+ () => void 0
66379
+ );
66380
+ metaWriteQueues.set(metaPath, settled);
66381
+ void settled.then(() => {
66382
+ if (metaWriteQueues.get(metaPath) === settled) metaWriteQueues.delete(metaPath);
66383
+ });
66384
+ return next;
66385
+ }
66386
+ var ProjectStore = class {
66387
+ /** @param root projects 父目录,例如 `~/.clawd/users/<userId>/`(v2 per-user) */
66388
+ constructor(root) {
66389
+ this.root = root;
66390
+ }
66391
+ root;
66392
+ serializeWrite(name, op) {
66393
+ return serializeMetaWrite(this.metaPath(name), op);
66394
+ }
66395
+ /** projects/<name>/.clawd-project.json 路径 */
66396
+ metaPath(name) {
66397
+ return (0, import_node_path69.join)(this.projectsRoot(), name, META_FILE);
66398
+ }
66399
+ /** projects/<name>/ 目录路径(cwd 用) */
66400
+ projectDir(name) {
66401
+ return (0, import_node_path69.join)(this.projectsRoot(), name);
66402
+ }
66403
+ projectsRoot() {
66404
+ return (0, import_node_path69.join)(this.root, PROJECTS_DIR);
66405
+ }
66406
+ async list() {
66407
+ let entries;
66408
+ try {
66409
+ entries = await import_node_fs51.promises.readdir(this.projectsRoot());
66410
+ } catch (err) {
66411
+ if (err.code === "ENOENT") return [];
66412
+ throw err;
66413
+ }
66414
+ const out = [];
66415
+ for (const name of entries) {
66416
+ try {
66417
+ const raw = await import_node_fs51.promises.readFile(this.metaPath(name), "utf8");
66418
+ const json = JSON.parse(raw);
66419
+ let migrated = false;
66420
+ if (typeof json.devCommand !== "string" || json.devCommand.length === 0) {
66421
+ json.devCommand = DEFAULT_DEV_COMMAND;
66422
+ migrated = true;
66423
+ }
66424
+ const parsed = ProjectMetadataSchema.safeParse(json);
66425
+ if (parsed.success) {
66426
+ out.push(parsed.data);
66427
+ if (migrated) {
66428
+ void import_node_fs51.promises.writeFile(this.metaPath(name), JSON.stringify(parsed.data, null, 2) + "\n", "utf8").catch(() => {
66429
+ });
66430
+ }
66431
+ }
66432
+ } catch {
66433
+ }
66434
+ }
66435
+ return out.sort((a, b2) => a.name.localeCompare(b2.name));
66436
+ }
66437
+ /**
66438
+ * @param reservedPorts 其它 user 已占端口(v2 per-user:端口必须全局唯一,所有 user 的
66439
+ * dev server 同机跑;per-user store 各自只 list 自己,靠 handler 收集全局端口传进来,
66440
+ * 否则不同 user 的首个项目都从 PROJECT_PORT_MIN 起 → 全撞 6173)。
66441
+ */
66442
+ async create(name, reservedPorts) {
66443
+ const existing = await this.list();
66444
+ if (existing.some((p2) => p2.name === name)) {
66445
+ throw new Error(`project "${name}" already exists / \u5DF2\u5B58\u5728`);
66446
+ }
66447
+ const usedPorts = new Set(existing.map((p2) => p2.port));
66448
+ if (reservedPorts) for (const p2 of reservedPorts) usedPorts.add(p2);
66449
+ let port = -1;
66450
+ for (let p2 = PROJECT_PORT_MIN; p2 <= PROJECT_PORT_MAX; p2++) {
66451
+ if (!usedPorts.has(p2)) {
66452
+ port = p2;
66453
+ break;
66454
+ }
66455
+ }
66456
+ if (port < 0) {
66457
+ throw new Error(
66458
+ `port pool exhausted / \u7AEF\u53E3\u6C60\u5DF2\u6EE1\uFF08${PROJECT_PORT_MIN}-${PROJECT_PORT_MAX}\uFF09\uFF0C\u5148\u5220\u4E00\u4E2A project \u91CA\u653E\u7AEF\u53E3`
66459
+ );
66460
+ }
66461
+ const meta = {
66462
+ name,
66463
+ port,
66464
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
66465
+ devCommand: DEFAULT_DEV_COMMAND
66466
+ };
66467
+ const validated = ProjectMetadataSchema.safeParse(meta);
66468
+ if (!validated.success) {
66469
+ throw new Error(`invalid name "${name}": ${validated.error.message}`);
66470
+ }
66471
+ const dir = this.projectDir(name);
66472
+ await import_node_fs51.promises.mkdir(dir, { recursive: true });
66473
+ await import_node_fs51.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
66474
+ return meta;
66475
+ }
66476
+ /**
66477
+ * 同步内联 scaffold:spawn `extension-kit/scripts/new-extension.sh <name>` 等 exit。
66478
+ * 必须在 `create()` 之后调(脚本期望 projects/<name>/.clawd-project.json 已就位 = picker 模式)。
66479
+ * 失败抛错;上层(handler)应当 catch 后回滚(删 dir + 不 mutate session)。
66480
+ *
66481
+ * spec 2026-06-02 §5.6.3:scaffold 对 assistant 不可见,避免 assistant 被诱导去"读 example
66482
+ * 代码再参考"。assistant 进 project 时目录已是完整模板,直接 pnpm install 即可。
66483
+ */
66484
+ async scaffold(name, templateSrcDir, scaffoldScriptPath) {
66485
+ const destDir = this.projectDir(name);
66486
+ return await new Promise((resolve9, reject) => {
66487
+ const child = (0, import_node_child_process19.spawn)("bash", [scaffoldScriptPath, name, templateSrcDir, destDir], {
66488
+ env: { ...process.env, PATH: process.env.PATH ?? "" },
66489
+ stdio: ["ignore", "pipe", "pipe"]
66490
+ });
66491
+ let stdout = "";
66492
+ let stderr = "";
66493
+ child.stdout.on("data", (d) => {
66494
+ stdout += d.toString("utf8");
66495
+ });
66496
+ child.stderr.on("data", (d) => {
66497
+ stderr += d.toString("utf8");
66498
+ });
66499
+ const timer = setTimeout(() => {
66500
+ child.kill("SIGKILL");
66501
+ reject(new Error(`scaffold timeout (>${SCAFFOLD_TIMEOUT_MS}ms): ${scaffoldScriptPath} ${name}`));
66502
+ }, SCAFFOLD_TIMEOUT_MS);
66503
+ child.on("error", (err) => {
66504
+ clearTimeout(timer);
66505
+ reject(new Error(`scaffold spawn failed: ${err.message}`));
66506
+ });
66507
+ child.on("exit", (code, signal) => {
66508
+ clearTimeout(timer);
66509
+ if (code === 0) resolve9({ stdout, stderr });
66510
+ else reject(new Error(`scaffold exit ${code ?? signal}: ${stderr.slice(0, 500)}`));
66511
+ });
66512
+ });
66513
+ }
66514
+ async delete(name) {
66515
+ const dir = this.projectDir(name);
66516
+ await import_node_fs51.promises.rm(dir, { recursive: true, force: true });
66517
+ }
66518
+ async updatePort(name, newPort) {
66519
+ if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
66520
+ throw new Error(
66521
+ `port range invalid: must be ${PROJECT_PORT_MIN}-${PROJECT_PORT_MAX}, got ${newPort}`
66522
+ );
66523
+ }
66524
+ return await this.serializeWrite(name, async () => {
66525
+ const list = await this.list();
66526
+ const target = list.find((p2) => p2.name === name);
66527
+ if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
66528
+ const conflict = list.find((p2) => p2.name !== name && p2.port === newPort);
66529
+ if (conflict) {
66530
+ throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
66531
+ }
66532
+ const updated = { ...target, port: newPort };
66533
+ await import_node_fs51.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
66534
+ return updated;
66535
+ });
66536
+ }
66537
+ /**
66538
+ * 单栏 refactor (spec 2026-06-02 §5.6.1):发布上线后写公网 URL 到 .clawd-project.json.prodUrl。
66539
+ * UI 通过 listProjects 读到 prodUrl 后切预览栏到线上 URL,取代旧 prod marker 机制。
66540
+ * 重复调用幂等(覆盖旧 URL)。url 由 ProjectMetadataSchema 校验 url() 格式。
66541
+ */
66542
+ async setProdUrl(name, url) {
66543
+ return await this.serializeWrite(name, async () => {
66544
+ const list = await this.list();
66545
+ const target = list.find((p2) => p2.name === name);
66546
+ if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
66547
+ const updated = { ...target, prodUrl: url };
66548
+ const validated = ProjectMetadataSchema.safeParse(updated);
66549
+ if (!validated.success) {
66550
+ throw new Error(`invalid prodUrl "${url}": ${validated.error.message}`);
66551
+ }
66552
+ await import_node_fs51.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
66553
+ return validated.data;
66554
+ });
66555
+ }
66556
+ /**
66557
+ * 发布上线脚手架化 (spec 2026-06-03-app-builder-publish-scaffold-design §5.1.4):
66558
+ * 写 publishJob 字段到 .clawd-project.json。仅承载 in-flight 状态 ——
66559
+ * - daemon 收到第一个 ::stage::build marker 时写入
66560
+ * - 每次 stage 切换更新 stage 字段(jobId / startedAt 不变)
66561
+ * - 成功 / 失败 / dismiss 时调 clearPublishJob 清掉
66562
+ * - 只有 daemon 崩才会留残骸
66563
+ * 写入时保留所有其他字段(prodUrl 等不受影响)。
66564
+ */
66565
+ async setPublishJob(name, job) {
66566
+ return await this.serializeWrite(name, async () => {
66567
+ const list = await this.list();
66568
+ const target = list.find((p2) => p2.name === name);
66569
+ if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
66570
+ const updated = { ...target, publishJob: job };
66571
+ const validated = ProjectMetadataSchema.safeParse(updated);
66572
+ if (!validated.success) {
66573
+ throw new Error(`invalid publishJob: ${validated.error.message}`);
66574
+ }
66575
+ await import_node_fs51.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
66576
+ return validated.data;
66577
+ });
66578
+ }
66579
+ /** 清掉 .clawd-project.json.publishJob 字段。其他字段保持原样。 */
66580
+ async clearPublishJob(name) {
66581
+ return await this.serializeWrite(name, async () => {
66582
+ const list = await this.list();
66583
+ const target = list.find((p2) => p2.name === name);
66584
+ if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
66585
+ if (!target.publishJob) return target;
66586
+ const { publishJob: _drop, ...rest } = target;
66587
+ void _drop;
66588
+ const validated = ProjectMetadataSchema.safeParse(rest);
66589
+ if (!validated.success) {
66590
+ throw new Error(`failed to clear publishJob: ${validated.error.message}`);
66591
+ }
66592
+ await import_node_fs51.promises.writeFile(this.metaPath(name), JSON.stringify(validated.data, null, 2) + "\n", "utf8");
66593
+ return validated.data;
66594
+ });
66595
+ }
66596
+ };
66597
+
66930
66598
  // src/app-builder/dev-server-supervisor.ts
66931
- var import_node_child_process21 = require("child_process");
66599
+ var import_node_child_process20 = require("child_process");
66932
66600
  var import_node_events2 = require("events");
66933
66601
  var DEFAULT_READY_PATTERN = /Local:\s+https?:\/\/|Nest application successfully started|server listening on/i;
66934
66602
  var DevServerSupervisor = class extends import_node_events2.EventEmitter {
@@ -66965,7 +66633,7 @@ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
66965
66633
  tunnelHost: args.tunnelHost,
66966
66634
  devCommand: cmd
66967
66635
  });
66968
- const child = (0, import_node_child_process21.spawn)("sh", ["-c", cmd], {
66636
+ const child = (0, import_node_child_process20.spawn)("sh", ["-c", cmd], {
66969
66637
  cwd: args.cwd,
66970
66638
  env,
66971
66639
  stdio: "pipe",
@@ -67181,6 +66849,460 @@ var DevServerSupervisor = class extends import_node_events2.EventEmitter {
67181
66849
  }
67182
66850
  };
67183
66851
 
66852
+ // src/app-builder-plugin/publish-registry.ts
66853
+ var import_node_crypto21 = require("crypto");
66854
+ var PublishJobRegistry = class {
66855
+ jobs = /* @__PURE__ */ new Map();
66856
+ has(name) {
66857
+ return this.jobs.has(name);
66858
+ }
66859
+ get(name) {
66860
+ return this.jobs.get(name);
66861
+ }
66862
+ /**
66863
+ * args.jobId 仅用于测试注入;生产代码不传,registry 自己生成。
66864
+ * args.now 仅用于测试注入;生产代码不传,用 Date.now()。
66865
+ */
66866
+ create(args) {
66867
+ if (this.jobs.has(args.name)) {
66868
+ throw new Error(`already publishing: ${args.name}`);
66869
+ }
66870
+ const jobId = args.jobId ?? `job-${(0, import_node_crypto21.randomUUID)()}`;
66871
+ this.jobs.set(args.name, {
66872
+ jobId,
66873
+ name: args.name,
66874
+ stage: "build",
66875
+ sessionId: args.sessionId,
66876
+ childProc: args.childProc,
66877
+ dismissed: false,
66878
+ sawAnyStage: false,
66879
+ startedAt: args.now ?? Date.now()
66880
+ });
66881
+ return jobId;
66882
+ }
66883
+ updateStage(name, stage) {
66884
+ const job = this.jobs.get(name);
66885
+ if (!job) return;
66886
+ job.stage = stage;
66887
+ job.sawAnyStage = true;
66888
+ }
66889
+ markDismissed(name) {
66890
+ const job = this.jobs.get(name);
66891
+ if (!job) return;
66892
+ job.dismissed = true;
66893
+ }
66894
+ remove(name) {
66895
+ const job = this.jobs.get(name);
66896
+ if (!job) return void 0;
66897
+ this.jobs.delete(name);
66898
+ return { sessionId: job.sessionId };
66899
+ }
66900
+ };
66901
+
66902
+ // src/app-builder-plugin/publish-job-runner.ts
66903
+ var import_node_child_process21 = require("child_process");
66904
+ var import_node_fs52 = require("fs");
66905
+ var import_node_path70 = require("path");
66906
+
66907
+ // src/app-builder-plugin/publish-stage-parser.ts
66908
+ var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
66909
+ var PROD_URL_RE = /^\s*::prod-url::(\S+)\s*$/;
66910
+ var URL_RE = /\bhttps?:\/\/[^\s]+/;
66911
+ function parsePublishLine(line) {
66912
+ const stageMatch = STAGE_RE.exec(line);
66913
+ if (stageMatch) return { kind: "stage", stage: stageMatch[1] };
66914
+ const urlMatch = PROD_URL_RE.exec(line);
66915
+ if (urlMatch) return { kind: "prod-url", url: urlMatch[1] };
66916
+ return null;
66917
+ }
66918
+ function extractProdUrl(text) {
66919
+ const m2 = URL_RE.exec(text);
66920
+ return m2 ? m2[0] : null;
66921
+ }
66922
+ function tailStderrLines(buf, n) {
66923
+ if (!buf) return "";
66924
+ const lines = buf.split("\n");
66925
+ if (lines.length <= n) return buf;
66926
+ return lines.slice(lines.length - n).join("\n");
66927
+ }
66928
+
66929
+ // src/app-builder-plugin/publish-job-runner.ts
66930
+ async function startPublishJob(deps, args) {
66931
+ const { registry: registry2, projectDir } = deps;
66932
+ const spawn13 = deps.spawnImpl ?? import_node_child_process21.spawn;
66933
+ if (registry2.has(args.name)) {
66934
+ return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
66935
+ }
66936
+ const projDir = projectDir(args.name);
66937
+ const logPath = (0, import_node_path70.join)(projDir, ".publish.log");
66938
+ let logStream = null;
66939
+ try {
66940
+ logStream = (0, import_node_fs52.createWriteStream)(logPath, { flags: "w" });
66941
+ } catch {
66942
+ logStream = null;
66943
+ }
66944
+ const child = spawn13("bash", [args.scriptPath, projDir, args.personaRoot ?? ""], {
66945
+ cwd: projDir,
66946
+ env: process.env,
66947
+ stdio: ["ignore", "pipe", "pipe"]
66948
+ });
66949
+ const jobId = registry2.create({
66950
+ name: args.name,
66951
+ sessionId: args.sessionId,
66952
+ childProc: child
66953
+ });
66954
+ deps.logger?.info("app-builder.publish.spawned", {
66955
+ name: args.name,
66956
+ jobId,
66957
+ pid: child.pid ?? null,
66958
+ scriptPath: args.scriptPath,
66959
+ logPath
66960
+ });
66961
+ child.on("error", (err) => {
66962
+ deps.logger?.warn("app-builder.publish.spawn-error", {
66963
+ name: args.name,
66964
+ jobId,
66965
+ error: err instanceof Error ? err.message : String(err)
66966
+ });
66967
+ void handleExit(deps, {
66968
+ name: args.name,
66969
+ jobId,
66970
+ code: null,
66971
+ signal: null,
66972
+ urlCandidate: null,
66973
+ stderrTail: err instanceof Error ? err.message : String(err),
66974
+ logPath
66975
+ });
66976
+ });
66977
+ let urlFromMarker = null;
66978
+ let urlFallback = null;
66979
+ let stdoutBuf = "";
66980
+ child.stdout?.on("data", (chunk) => {
66981
+ if (logStream) logStream.write(chunk);
66982
+ stdoutBuf += chunk.toString();
66983
+ let nlIdx;
66984
+ while ((nlIdx = stdoutBuf.indexOf("\n")) >= 0) {
66985
+ const line = stdoutBuf.slice(0, nlIdx);
66986
+ stdoutBuf = stdoutBuf.slice(nlIdx + 1);
66987
+ handleStdoutLine(deps, args.name, jobId, line);
66988
+ const parsedForUrl = parsePublishLine(line);
66989
+ if (parsedForUrl?.kind === "prod-url") urlFromMarker = parsedForUrl.url;
66990
+ const url = extractProdUrl(line);
66991
+ if (url && !urlFallback) urlFallback = url;
66992
+ }
66993
+ });
66994
+ let stderrBuf = "";
66995
+ child.stderr?.on("data", (chunk) => {
66996
+ if (logStream) logStream.write(chunk);
66997
+ stderrBuf += chunk.toString();
66998
+ });
66999
+ child.on("exit", (code, signal) => {
67000
+ if (logStream) logStream.end();
67001
+ const urlCandidate = urlFromMarker ?? urlFallback;
67002
+ deps.logger?.info("app-builder.publish.child-exit", {
67003
+ name: args.name,
67004
+ jobId,
67005
+ code,
67006
+ signal,
67007
+ hadUrl: !!urlCandidate,
67008
+ urlSource: urlFromMarker ? "marker" : urlFallback ? "fallback" : "none",
67009
+ stderrBytes: stderrBuf.length
67010
+ });
67011
+ void handleExit(deps, {
67012
+ name: args.name,
67013
+ jobId,
67014
+ code,
67015
+ signal,
67016
+ urlCandidate,
67017
+ stderrTail: tailStderrLines(stderrBuf, 20),
67018
+ logPath
67019
+ });
67020
+ });
67021
+ return { jobId, status: "started" };
67022
+ }
67023
+ function handleStdoutLine(deps, name, jobId, line) {
67024
+ const parsed = parsePublishLine(line);
67025
+ if (parsed?.kind !== "stage") return;
67026
+ advanceStage(deps, name, jobId, parsed.stage);
67027
+ }
67028
+ function advanceStage(deps, name, jobId, next) {
67029
+ const job = deps.registry.get(name);
67030
+ if (!job) return;
67031
+ const prev = job.stage;
67032
+ const wasFirstStage = !job.sawAnyStage;
67033
+ deps.logger?.info("app-builder.publish.stage-advance", {
67034
+ name,
67035
+ jobId,
67036
+ prev: wasFirstStage ? null : prev,
67037
+ next,
67038
+ firstStage: wasFirstStage
67039
+ });
67040
+ if (wasFirstStage) {
67041
+ deps.registry.updateStage(name, next);
67042
+ void deps.setPublishJob(name, {
67043
+ jobId,
67044
+ stage: next,
67045
+ startedAt: job.startedAt
67046
+ });
67047
+ deps.broadcast({
67048
+ type: "appBuilder:publish-progress",
67049
+ name,
67050
+ jobId,
67051
+ stage: next,
67052
+ status: "started"
67053
+ });
67054
+ return;
67055
+ }
67056
+ if (prev === next) {
67057
+ return;
67058
+ }
67059
+ deps.broadcast({
67060
+ type: "appBuilder:publish-progress",
67061
+ name,
67062
+ jobId,
67063
+ stage: prev,
67064
+ status: "completed"
67065
+ });
67066
+ deps.registry.updateStage(name, next);
67067
+ void deps.setPublishJob(name, {
67068
+ jobId,
67069
+ stage: next,
67070
+ startedAt: job.startedAt
67071
+ });
67072
+ deps.broadcast({
67073
+ type: "appBuilder:publish-progress",
67074
+ name,
67075
+ jobId,
67076
+ stage: next,
67077
+ status: "started"
67078
+ });
67079
+ }
67080
+ async function handleExit(deps, args) {
67081
+ const job = deps.registry.get(args.name);
67082
+ if (args.signal === "SIGTERM" && job?.dismissed) {
67083
+ try {
67084
+ await deps.clearPublishJob(args.name);
67085
+ } catch (err) {
67086
+ console.error("[publish] clearPublishJob failed (dismissed branch)", err);
67087
+ }
67088
+ deps.registry.remove(args.name);
67089
+ deps.onSettled?.({ kind: "dismissed" });
67090
+ return;
67091
+ }
67092
+ if (args.code === 0 && args.urlCandidate) {
67093
+ if (job?.sawAnyStage) {
67094
+ deps.broadcast({
67095
+ type: "appBuilder:publish-progress",
67096
+ name: args.name,
67097
+ jobId: args.jobId,
67098
+ stage: job.stage,
67099
+ status: "completed"
67100
+ });
67101
+ }
67102
+ try {
67103
+ await deps.setProdUrl(args.name, args.urlCandidate);
67104
+ } catch (err) {
67105
+ console.error("[publish] setProdUrl failed; falling through to failure branch", err);
67106
+ await emitFailure(deps, args, "\u8C03 setProdUrl \u5931\u8D25\uFF1A" + (err instanceof Error ? err.message : String(err)));
67107
+ return;
67108
+ }
67109
+ try {
67110
+ await deps.clearPublishJob(args.name);
67111
+ } catch (err) {
67112
+ console.error("[publish] clearPublishJob failed (success branch)", err);
67113
+ }
67114
+ deps.registry.remove(args.name);
67115
+ deps.onSettled?.({ kind: "published", url: args.urlCandidate });
67116
+ return;
67117
+ }
67118
+ await emitFailure(deps, args, buildErrorSummary(args));
67119
+ }
67120
+ async function emitFailure(deps, args, errorSummary) {
67121
+ const job = deps.registry.get(args.name);
67122
+ const failStage = job?.sawAnyStage ? job.stage : "unknown";
67123
+ deps.logger?.warn("app-builder.publish.failed", {
67124
+ name: args.name,
67125
+ jobId: args.jobId,
67126
+ stage: failStage,
67127
+ errorSummary: errorSummary.slice(0, 500)
67128
+ });
67129
+ try {
67130
+ await deps.clearPublishJob(args.name);
67131
+ } catch (err) {
67132
+ deps.logger?.warn("app-builder.publish.clear-field-failed", {
67133
+ name: args.name,
67134
+ jobId: args.jobId,
67135
+ error: err instanceof Error ? err.message : String(err)
67136
+ });
67137
+ }
67138
+ const removed = deps.registry.remove(args.name);
67139
+ deps.broadcast({
67140
+ type: "appBuilder:publish-failed",
67141
+ name: args.name,
67142
+ jobId: args.jobId,
67143
+ stage: failStage,
67144
+ errorSummary
67145
+ });
67146
+ if (removed) {
67147
+ const msg = formatFailureMessage(failStage, errorSummary, args.logPath);
67148
+ try {
67149
+ await deps.injectUserText(removed.sessionId, msg);
67150
+ deps.logger?.info("app-builder.publish.failure-injected-to-chat", {
67151
+ name: args.name,
67152
+ jobId: args.jobId,
67153
+ sessionId: removed.sessionId
67154
+ });
67155
+ } catch (err) {
67156
+ deps.logger?.warn("app-builder.publish.inject-user-text-failed", {
67157
+ name: args.name,
67158
+ jobId: args.jobId,
67159
+ sessionId: removed.sessionId,
67160
+ error: err instanceof Error ? err.message : String(err)
67161
+ });
67162
+ }
67163
+ } else {
67164
+ deps.logger?.warn("app-builder.publish.no-registry-entry-on-failure", {
67165
+ name: args.name,
67166
+ jobId: args.jobId
67167
+ });
67168
+ }
67169
+ deps.onSettled?.({ kind: "failed", stage: failStage, errorSummary });
67170
+ }
67171
+ function buildErrorSummary(args) {
67172
+ if (args.signal) {
67173
+ return `publish.sh \u88AB\u4E2D\u65AD\uFF08signal: ${args.signal}\uFF09`;
67174
+ }
67175
+ if (args.code === 0 && !args.urlCandidate) {
67176
+ return "publish.sh \u6210\u529F\u9000\u51FA\u4F46\u672A\u8F93\u51FA\u516C\u7F51 URL";
67177
+ }
67178
+ return args.stderrTail || `publish.sh \u5F02\u5E38\u9000\u51FA\uFF08exit code: ${args.code}\uFF09`;
67179
+ }
67180
+ function formatFailureMessage(stage, errorSummary, logPath) {
67181
+ return `\u53D1\u5E03\u5931\u8D25\uFF1A[${stage}] \u9636\u6BB5
67182
+ ${errorSummary}
67183
+
67184
+ \u5B8C\u6574\u65E5\u5FD7\uFF1A${logPath}
67185
+ \u8BF7\u4F60\u63A5\u7BA1\u5B8C\u6210\u672C\u6B21\u53D1\u5E03\uFF1A\u5148\u6839\u636E\u65E5\u5FD7\u4FEE\u590D\u95EE\u9898\uFF0C\u7136\u540E\u91CD\u8DD1 publish.sh \u76F4\u5230\u4E0A\u7EBF\u6210\u529F\uFF0C\u6700\u540E\u7528 appBuilder.setProdUrl \u5199\u5165\u516C\u7F51 URL\u3002`;
67186
+ }
67187
+ async function recoverInterruptedJobs(deps) {
67188
+ const projects = await deps.listAllProjects();
67189
+ for (const p2 of projects) {
67190
+ if (p2.publishJob) {
67191
+ deps.log(
67192
+ `[publish] found interrupted job ${p2.publishJob.jobId} for ${p2.name}, stage=${p2.publishJob.stage}`
67193
+ );
67194
+ }
67195
+ }
67196
+ }
67197
+
67198
+ // src/app-builder-plugin/core.ts
67199
+ function createPublishService() {
67200
+ const registry2 = new PublishJobRegistry();
67201
+ return {
67202
+ inFlightJobId(name) {
67203
+ return registry2.get(name)?.jobId ?? null;
67204
+ },
67205
+ async start(args) {
67206
+ const { store, hooks, logger } = args;
67207
+ const changed = async (name) => {
67208
+ await hooks?.onProjectChanged?.(name);
67209
+ };
67210
+ return await startPublishJob(
67211
+ {
67212
+ registry: registry2,
67213
+ setPublishJob: async (name, job) => {
67214
+ await store.setPublishJob(name, job);
67215
+ await changed(name);
67216
+ },
67217
+ clearPublishJob: async (name) => {
67218
+ await store.clearPublishJob(name);
67219
+ await changed(name);
67220
+ },
67221
+ setProdUrl: async (name, url) => {
67222
+ await store.setProdUrl(name, url);
67223
+ await changed(name);
67224
+ },
67225
+ broadcast: (frame) => hooks?.broadcast?.(frame),
67226
+ injectUserText: async (sessionId, text) => {
67227
+ await hooks?.injectUserText?.(sessionId, text);
67228
+ },
67229
+ projectDir: (name) => store.projectDir(name),
67230
+ ...hooks?.onSettled ? { onSettled: hooks.onSettled } : {},
67231
+ ...logger ? { logger } : {}
67232
+ },
67233
+ {
67234
+ name: args.name,
67235
+ sessionId: args.sessionId,
67236
+ scriptPath: args.scriptPath,
67237
+ personaRoot: args.personaRoot
67238
+ }
67239
+ );
67240
+ },
67241
+ dismiss(name) {
67242
+ const job = registry2.get(name);
67243
+ if (!job) return false;
67244
+ registry2.markDismissed(name);
67245
+ job.childProc.kill("SIGTERM");
67246
+ return true;
67247
+ }
67248
+ };
67249
+ }
67250
+ async function createProjectOnDisk(args) {
67251
+ const { store, name, templateSrcDir, scaffoldScript, logger } = args;
67252
+ const project = await store.create(name, args.reservedPorts);
67253
+ try {
67254
+ const result = await store.scaffold(name, templateSrcDir, scaffoldScript);
67255
+ logger?.info("app-builder.scaffold.done", { name, stdout: result.stdout.slice(0, 200) });
67256
+ } catch (err) {
67257
+ const message = err instanceof Error ? err.message : String(err);
67258
+ logger?.warn("app-builder.scaffold.failed", { name, error: message });
67259
+ await store.delete(name).catch((delErr) => {
67260
+ logger?.warn("app-builder.scaffold.rollback-failed", {
67261
+ name,
67262
+ error: delErr instanceof Error ? delErr.message : String(delErr)
67263
+ });
67264
+ });
67265
+ throw new Error(`scaffold failed: ${message}`);
67266
+ }
67267
+ return { project, projectDir: store.projectDir(name) };
67268
+ }
67269
+ async function recoverInterruptedPublishJobs(store, logger) {
67270
+ await recoverInterruptedJobs({
67271
+ listAllProjects: async () => store.list(),
67272
+ log: (msg) => logger?.info(msg)
67273
+ });
67274
+ }
67275
+
67276
+ // src/app-builder-plugin/mcp-config.ts
67277
+ var import_node_fs53 = __toESM(require("fs"), 1);
67278
+ var import_node_path71 = __toESM(require("path"), 1);
67279
+ function appBuilderMcpConfigPath(dataDir) {
67280
+ return import_node_path71.default.join(dataDir, "app-builder.mcp.json");
67281
+ }
67282
+ function writeAppBuilderMcpConfig(args) {
67283
+ const cfgPath = appBuilderMcpConfigPath(args.dataDir);
67284
+ const cfg = {
67285
+ _comment: "daemon \u542F\u52A8\u81EA\u52A8\u751F\u6210\uFF1Bagent spawn \u901A\u8FC7 --mcp-config \u6CE8\u5165\u3002app-builder-plugin/mcp-server.cjs \u662F clawd-app-builder \u63D2\u4EF6\uFF08app-builder persona \u79C1\u6709\u7684\u540E\u7AEF\uFF1Aproject \u76EE\u5F55/\u5143\u6570\u636E + \u53D1\u5E03\u6D41\u6C34\u7EBF\uFF09\u3002\u6709 CLAWD_DAEMON_URL \u65F6\u8F6C\u8C03 appBuilder:* RPC\uFF08\u7AEF\u53E3\u6C60 / \u4F1A\u8BDD\u7ED1\u5B9A / UI \u5E7F\u64AD\u5F52 daemon\uFF09\uFF0C\u6CA1\u6709\u65F6\u7528\u540C\u4E00\u4EFD\u5185\u6838\u81EA\u5DF1\u5E72\uFF08\u4E91\u4E0A\u5BB9\u5668\uFF09\u3002\u975E app-builder \u7CFB persona \u7684\u4F1A\u8BDD\u91CC\u672C server \u4E0D\u6CE8\u518C\u4EFB\u4F55 tool\u3002",
67286
+ mcpServers: {
67287
+ "clawd-app-builder": {
67288
+ type: "stdio",
67289
+ command: "node",
67290
+ args: [args.serverScriptPath],
67291
+ env: {
67292
+ CLAWD_DAEMON_TOKEN: args.daemonToken,
67293
+ CLAWD_DAEMON_URL: args.daemonUrl,
67294
+ CLAWD_APP_BUILDER_SCAFFOLD_SCRIPT: import_node_path71.default.join(args.deployKitScriptsDir, "new-extension.sh"),
67295
+ CLAWD_APP_BUILDER_PUBLISH_SCRIPT: import_node_path71.default.join(args.deployKitScriptsDir, "publish.sh"),
67296
+ ...args.logPath ? { CLAWD_APP_BUILDER_LOG: args.logPath } : {}
67297
+ }
67298
+ }
67299
+ }
67300
+ };
67301
+ import_node_fs53.default.mkdirSync(args.dataDir, { recursive: true });
67302
+ import_node_fs53.default.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2), "utf8");
67303
+ return cfgPath;
67304
+ }
67305
+
67184
67306
  // src/handlers/dispatch-rpc.ts
67185
67307
  init_zod();
67186
67308
  init_protocol();
@@ -67218,7 +67340,7 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
67218
67340
 
67219
67341
  // src/extension/runtime.ts
67220
67342
  var import_node_child_process22 = require("child_process");
67221
- var import_node_path71 = __toESM(require("path"), 1);
67343
+ var import_node_path72 = __toESM(require("path"), 1);
67222
67344
  var import_promises18 = require("timers/promises");
67223
67345
 
67224
67346
  // src/extension/port-allocator.ts
@@ -67319,7 +67441,7 @@ var Runtime = class {
67319
67441
  /\$CLAWOS_EXT_PORT/g,
67320
67442
  String(port)
67321
67443
  );
67322
- const dir = import_node_path71.default.join(this.root, extId);
67444
+ const dir = import_node_path72.default.join(this.root, extId);
67323
67445
  const env = {
67324
67446
  ...process.env,
67325
67447
  CLAWOS_EXT_PORT: String(port),
@@ -67431,7 +67553,7 @@ ${handle.stderrTail}`
67431
67553
 
67432
67554
  // src/extension/published-channels.ts
67433
67555
  var import_promises19 = __toESM(require("fs/promises"), 1);
67434
- var import_node_path72 = __toESM(require("path"), 1);
67556
+ var import_node_path73 = __toESM(require("path"), 1);
67435
67557
  init_zod();
67436
67558
  var PublishedChannelsError = class extends Error {
67437
67559
  constructor(code, message) {
@@ -67530,7 +67652,7 @@ var PublishedChannelStore = class {
67530
67652
  )
67531
67653
  };
67532
67654
  const tmp = `${this.filePath}.tmp`;
67533
- await import_promises19.default.mkdir(import_node_path72.default.dirname(this.filePath), { recursive: true });
67655
+ await import_promises19.default.mkdir(import_node_path73.default.dirname(this.filePath), { recursive: true });
67534
67656
  await import_promises19.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
67535
67657
  await import_promises19.default.rename(tmp, this.filePath);
67536
67658
  }
@@ -67538,7 +67660,7 @@ var PublishedChannelStore = class {
67538
67660
 
67539
67661
  // src/extension/bundle-cache.ts
67540
67662
  var import_promises20 = __toESM(require("fs/promises"), 1);
67541
- var import_node_path73 = __toESM(require("path"), 1);
67663
+ var import_node_path74 = __toESM(require("path"), 1);
67542
67664
  var BundleCache = class {
67543
67665
  constructor(rootDir) {
67544
67666
  this.rootDir = rootDir;
@@ -67547,14 +67669,14 @@ var BundleCache = class {
67547
67669
  /** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
67548
67670
  async write(snapshotHash, buffer) {
67549
67671
  await import_promises20.default.mkdir(this.rootDir, { recursive: true });
67550
- const file = import_node_path73.default.join(this.rootDir, `${snapshotHash}.zip`);
67672
+ const file = import_node_path74.default.join(this.rootDir, `${snapshotHash}.zip`);
67551
67673
  const tmp = `${file}.tmp`;
67552
67674
  await import_promises20.default.writeFile(tmp, buffer, { mode: 384 });
67553
67675
  await import_promises20.default.rename(tmp, file);
67554
67676
  }
67555
67677
  /** Returns the bundle bytes, or null when the file doesn't exist. */
67556
67678
  async read(snapshotHash) {
67557
- const file = import_node_path73.default.join(this.rootDir, `${snapshotHash}.zip`);
67679
+ const file = import_node_path74.default.join(this.rootDir, `${snapshotHash}.zip`);
67558
67680
  try {
67559
67681
  return await import_promises20.default.readFile(file);
67560
67682
  } catch (e) {
@@ -67564,7 +67686,7 @@ var BundleCache = class {
67564
67686
  }
67565
67687
  /** Idempotent — missing file is not an error. */
67566
67688
  async delete(snapshotHash) {
67567
- const file = import_node_path73.default.join(this.rootDir, `${snapshotHash}.zip`);
67689
+ const file = import_node_path74.default.join(this.rootDir, `${snapshotHash}.zip`);
67568
67690
  await import_promises20.default.rm(file, { force: true });
67569
67691
  }
67570
67692
  };
@@ -67587,24 +67709,24 @@ async function startDaemon(config) {
67587
67709
  sampling: logShippingCfg.sampling,
67588
67710
  homeDir: import_node_os26.default.homedir()
67589
67711
  });
67590
- const logDir = import_node_path74.default.join(config.dataDir, "log");
67591
- import_node_fs53.default.mkdirSync(logDir, { recursive: true });
67712
+ const logDir = import_node_path75.default.join(config.dataDir, "log");
67713
+ import_node_fs54.default.mkdirSync(logDir, { recursive: true });
67592
67714
  const logger = createLogger({
67593
67715
  level: config.logLevel,
67594
- file: import_node_path74.default.join(logDir, "clawd.log"),
67716
+ file: import_node_path75.default.join(logDir, "clawd.log"),
67595
67717
  logClient
67596
67718
  });
67597
67719
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
67598
67720
  const screenIdleProbeLogger = createFileOnlyLogger({
67599
- file: import_node_path74.default.join(logDir, "screen-idle-probe.log"),
67721
+ file: import_node_path75.default.join(logDir, "screen-idle-probe.log"),
67600
67722
  level: "debug"
67601
67723
  });
67602
67724
  logger.info("screen-idle probe logger enabled", {
67603
- file: import_node_path74.default.join(logDir, "screen-idle-probe.log")
67725
+ file: import_node_path75.default.join(logDir, "screen-idle-probe.log")
67604
67726
  });
67605
67727
  const CAP_TARGETS = [
67606
- import_node_path74.default.join(logDir, "clawd.log"),
67607
- import_node_path74.default.join(logDir, "screen-idle-probe.log")
67728
+ import_node_path75.default.join(logDir, "clawd.log"),
67729
+ import_node_path75.default.join(logDir, "screen-idle-probe.log")
67608
67730
  ];
67609
67731
  const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
67610
67732
  const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
@@ -67726,8 +67848,8 @@ async function startDaemon(config) {
67726
67848
  const agents = new AgentsScanner();
67727
67849
  const history = new ClaudeHistoryReader();
67728
67850
  let transport = null;
67729
- const personaStore = new PersonaStore(import_node_path74.default.join(config.dataDir, "personas"));
67730
- const usersRoot = import_node_path74.default.join(config.dataDir, "users");
67851
+ const personaStore = new PersonaStore(import_node_path75.default.join(config.dataDir, "personas"));
67852
+ const usersRoot = import_node_path75.default.join(config.dataDir, "users");
67731
67853
  const defaultsRoot = findDefaultsRoot(logger);
67732
67854
  if (defaultsRoot) {
67733
67855
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -67751,7 +67873,7 @@ async function startDaemon(config) {
67751
67873
  retirePersonaBuilder({ store: personaStore, logger });
67752
67874
  const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
67753
67875
  const dispatchStore = createDispatchStore({
67754
- filePath: import_node_path74.default.join(config.dataDir, "dispatch.json"),
67876
+ filePath: import_node_path75.default.join(config.dataDir, "dispatch.json"),
67755
67877
  now: () => Date.now(),
67756
67878
  onFlushError: (err) => logger.error("dispatch store flush failed", {
67757
67879
  reason: err instanceof Error ? err.message : String(err)
@@ -67760,7 +67882,7 @@ async function startDaemon(config) {
67760
67882
  await dispatchStore.load();
67761
67883
  dispatchStore.markInterruptedAsFailed((/* @__PURE__ */ new Date()).toISOString());
67762
67884
  const channelStore = createChannelStore({
67763
- filePath: import_node_path74.default.join(config.dataDir, "channels.json"),
67885
+ filePath: import_node_path75.default.join(config.dataDir, "channels.json"),
67764
67886
  now: () => Date.now(),
67765
67887
  genId: () => `ch-${(0, import_node_crypto22.randomUUID)().slice(0, 8)}`,
67766
67888
  onFlushError: (err) => logger.error("channel store flush failed", {
@@ -67769,7 +67891,7 @@ async function startDaemon(config) {
67769
67891
  });
67770
67892
  await channelStore.load();
67771
67893
  const topicStore = createTopicStore({
67772
- filePath: import_node_path74.default.join(config.dataDir, "topics.json"),
67894
+ filePath: import_node_path75.default.join(config.dataDir, "topics.json"),
67773
67895
  now: () => Date.now(),
67774
67896
  genId: () => `tp-${(0, import_node_crypto22.randomUUID)().slice(0, 8)}`,
67775
67897
  onFlushError: (err) => logger.error("topic store flush failed", {
@@ -67796,18 +67918,18 @@ async function startDaemon(config) {
67796
67918
  });
67797
67919
  }
67798
67920
  });
67799
- const here = typeof __dirname === "string" ? __dirname : import_node_path74.default.dirname((0, import_node_url4.fileURLToPath)(import_meta7.url));
67921
+ const here = typeof __dirname === "string" ? __dirname : import_node_path75.default.dirname((0, import_node_url4.fileURLToPath)(import_meta7.url));
67800
67922
  const mcpConfigs = [];
67801
67923
  const mcpDaemonUrl = `http://127.0.0.1:${config.port}`;
67802
67924
  const dispatchServerCandidates = [
67803
- import_node_path74.default.join(here, "dispatch", "mcp-server.cjs"),
67925
+ import_node_path75.default.join(here, "dispatch", "mcp-server.cjs"),
67804
67926
  // 生产 dist/index → dist/dispatch/mcp-server.cjs
67805
- import_node_path74.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
67927
+ import_node_path75.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
67806
67928
  // dev tsx src/index → ../dist/dispatch/mcp-server.cjs
67807
67929
  ];
67808
- const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs53.default.existsSync(p2));
67930
+ const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs54.default.existsSync(p2));
67809
67931
  if (dispatchServerScriptPath) {
67810
- const dispatchLogPath = import_node_path74.default.join(logDir, "dispatch-mcp-server.log");
67932
+ const dispatchLogPath = import_node_path75.default.join(logDir, "dispatch-mcp-server.log");
67811
67933
  const dispatchCfgPath = writeDispatchMcpConfig({
67812
67934
  dataDir: config.dataDir,
67813
67935
  serverScriptPath: dispatchServerScriptPath,
@@ -67827,12 +67949,12 @@ async function startDaemon(config) {
67827
67949
  });
67828
67950
  }
67829
67951
  const ticketServerCandidates = [
67830
- import_node_path74.default.join(here, "ticket", "mcp-server.cjs"),
67831
- import_node_path74.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
67952
+ import_node_path75.default.join(here, "ticket", "mcp-server.cjs"),
67953
+ import_node_path75.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
67832
67954
  ];
67833
- const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs53.default.existsSync(p2));
67955
+ const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs54.default.existsSync(p2));
67834
67956
  if (ticketServerScriptPath) {
67835
- const ticketLogPath = import_node_path74.default.join(logDir, "ticket-mcp-server.log");
67957
+ const ticketLogPath = import_node_path75.default.join(logDir, "ticket-mcp-server.log");
67836
67958
  const ticketCfgPath = writeTicketMcpConfig({
67837
67959
  dataDir: config.dataDir,
67838
67960
  serverScriptPath: ticketServerScriptPath,
@@ -67854,12 +67976,12 @@ async function startDaemon(config) {
67854
67976
  });
67855
67977
  }
67856
67978
  const shiftServerCandidates = [
67857
- import_node_path74.default.join(here, "shift", "mcp-server.cjs"),
67858
- import_node_path74.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
67979
+ import_node_path75.default.join(here, "shift", "mcp-server.cjs"),
67980
+ import_node_path75.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
67859
67981
  ];
67860
- const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs53.default.existsSync(p2));
67982
+ const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs54.default.existsSync(p2));
67861
67983
  if (shiftServerScriptPath) {
67862
- const shiftLogPath = import_node_path74.default.join(logDir, "shift-mcp-server.log");
67984
+ const shiftLogPath = import_node_path75.default.join(logDir, "shift-mcp-server.log");
67863
67985
  const shiftCfgPath = await writeShiftMcpConfig({
67864
67986
  dataDir: config.dataDir,
67865
67987
  serverScriptPath: shiftServerScriptPath,
@@ -67880,12 +68002,12 @@ async function startDaemon(config) {
67880
68002
  );
67881
68003
  }
67882
68004
  const inboxServerCandidates = [
67883
- import_node_path74.default.join(here, "inbox", "mcp-server.cjs"),
67884
- import_node_path74.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
68005
+ import_node_path75.default.join(here, "inbox", "mcp-server.cjs"),
68006
+ import_node_path75.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
67885
68007
  ];
67886
- const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs53.default.existsSync(p2));
68008
+ const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs54.default.existsSync(p2));
67887
68009
  if (inboxServerScriptPath) {
67888
- const inboxLogPath = import_node_path74.default.join(logDir, "inbox-mcp-server.log");
68010
+ const inboxLogPath = import_node_path75.default.join(logDir, "inbox-mcp-server.log");
67889
68011
  const inboxCfgPath = await writeInboxMcpConfig({
67890
68012
  dataDir: config.dataDir,
67891
68013
  serverScriptPath: inboxServerScriptPath,
@@ -67906,10 +68028,10 @@ async function startDaemon(config) {
67906
68028
  );
67907
68029
  }
67908
68030
  const peerOpsServerCandidates = [
67909
- import_node_path74.default.join(here, "peer-ops", "mcp-server.cjs"),
67910
- import_node_path74.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
68031
+ import_node_path75.default.join(here, "peer-ops", "mcp-server.cjs"),
68032
+ import_node_path75.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
67911
68033
  ];
67912
- const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs53.default.existsSync(p2));
68034
+ const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs54.default.existsSync(p2));
67913
68035
  if (peerOpsServerScriptPath) {
67914
68036
  const peerOpsCfgPath = writePeerOpsMcpConfig({
67915
68037
  dataDir: config.dataDir,
@@ -67929,12 +68051,12 @@ async function startDaemon(config) {
67929
68051
  );
67930
68052
  }
67931
68053
  const rpcToolServerCandidates = [
67932
- import_node_path74.default.join(here, "rpc-tool", "mcp-server.cjs"),
67933
- import_node_path74.default.join(here, "..", "dist", "rpc-tool", "mcp-server.cjs")
68054
+ import_node_path75.default.join(here, "rpc-tool", "mcp-server.cjs"),
68055
+ import_node_path75.default.join(here, "..", "dist", "rpc-tool", "mcp-server.cjs")
67934
68056
  ];
67935
- const rpcToolServerScriptPath = rpcToolServerCandidates.find((p2) => import_node_fs53.default.existsSync(p2));
68057
+ const rpcToolServerScriptPath = rpcToolServerCandidates.find((p2) => import_node_fs54.default.existsSync(p2));
67936
68058
  if (rpcToolServerScriptPath) {
67937
- const rpcToolLogPath = import_node_path74.default.join(logDir, "rpc-tool-mcp-server.log");
68059
+ const rpcToolLogPath = import_node_path75.default.join(logDir, "rpc-tool-mcp-server.log");
67938
68060
  const rpcToolCfgPath = writeRpcToolMcpConfig({
67939
68061
  dataDir: config.dataDir,
67940
68062
  serverScriptPath: rpcToolServerScriptPath,
@@ -67954,8 +68076,35 @@ async function startDaemon(config) {
67954
68076
  { tried: rpcToolServerCandidates }
67955
68077
  );
67956
68078
  }
68079
+ const appBuilderServerCandidates = [
68080
+ import_node_path75.default.join(here, "app-builder-plugin", "mcp-server.cjs"),
68081
+ import_node_path75.default.join(here, "..", "dist", "app-builder-plugin", "mcp-server.cjs")
68082
+ ];
68083
+ const appBuilderServerScriptPath = appBuilderServerCandidates.find((p2) => import_node_fs54.default.existsSync(p2));
68084
+ if (appBuilderServerScriptPath) {
68085
+ const appBuilderLogPath = import_node_path75.default.join(logDir, "app-builder-mcp-server.log");
68086
+ const appBuilderCfgPath = writeAppBuilderMcpConfig({
68087
+ dataDir: config.dataDir,
68088
+ serverScriptPath: appBuilderServerScriptPath,
68089
+ daemonToken: resolvedAuthToken,
68090
+ daemonUrl: mcpDaemonUrl,
68091
+ deployKitScriptsDir: import_node_path75.default.join(config.dataDir, "deploy-kit", "scripts"),
68092
+ logPath: appBuilderLogPath
68093
+ });
68094
+ mcpConfigs.push({ id: "app-builder", path: appBuilderCfgPath, personas: APP_BUILDER_PERSONAS });
68095
+ logger.info("app-builder.mcp.json written", {
68096
+ path: appBuilderCfgPath,
68097
+ server: appBuilderServerScriptPath,
68098
+ serverLog: appBuilderLogPath
68099
+ });
68100
+ } else {
68101
+ logger.warn(
68102
+ "app-builder-plugin/mcp-server.cjs not found; clawd-app-builder tools disabled (dev tsx \u6A21\u5F0F\u4E0B\u9700\u5148\u8DD1\u8FC7 build)",
68103
+ { tried: appBuilderServerCandidates }
68104
+ );
68105
+ }
67957
68106
  const shiftStore = createShiftStore({
67958
- filePath: import_node_path74.default.join(config.dataDir, "shift.json"),
68107
+ filePath: import_node_path75.default.join(config.dataDir, "shift.json"),
67959
68108
  ownerIdProvider: () => ownerPrincipalId,
67960
68109
  now: () => Date.now()
67961
68110
  });
@@ -67977,7 +68126,7 @@ async function startDaemon(config) {
67977
68126
  getAdapter,
67978
68127
  historyReader: history,
67979
68128
  dataDir: config.dataDir,
67980
- personaRoot: import_node_path74.default.join(config.dataDir, "personas"),
68129
+ personaRoot: import_node_path75.default.join(config.dataDir, "personas"),
67981
68130
  // dispatch / shift 自动化 spawn 按 persona 绑定 tool 起 B session(codex persona 用 codex)。
67982
68131
  // personaRegistry 声明在 manager 之后(同作用域 const,调用时机在启动完成后,无 TDZ 风险)。
67983
68132
  getPersonaDefaults: (personaId2) => personaRegistry.get(personaId2),
@@ -68031,10 +68180,10 @@ async function startDaemon(config) {
68031
68180
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
68032
68181
  attachmentGroup: {
68033
68182
  onFileEdit: (input) => {
68034
- const absPath = import_node_path74.default.isAbsolute(input.relPath) ? input.relPath : import_node_path74.default.join(input.cwd, input.relPath);
68183
+ const absPath = import_node_path75.default.isAbsolute(input.relPath) ? input.relPath : import_node_path75.default.join(input.cwd, input.relPath);
68035
68184
  let size = 0;
68036
68185
  try {
68037
- size = import_node_fs53.default.statSync(absPath).size;
68186
+ size = import_node_fs54.default.statSync(absPath).size;
68038
68187
  } catch (err) {
68039
68188
  logger.warn("attachment.onFileEdit stat failed", {
68040
68189
  sessionId: input.sessionId,
@@ -68064,9 +68213,9 @@ async function startDaemon(config) {
68064
68213
  logger.info("projects.seed-dropped", { dropped: droppedSeed.dropped, backup: droppedSeed.backup });
68065
68214
  }
68066
68215
  const projectsStore = new ProjectsStore({
68067
- file: import_node_path74.default.join(config.dataDir, "projects.json"),
68216
+ file: import_node_path75.default.join(config.dataDir, "projects.json"),
68068
68217
  clawdDir: config.dataDir,
68069
- codexStateFile: import_node_path74.default.join(import_node_os26.default.homedir(), ".codex", ".codex-global-state.json"),
68218
+ codexStateFile: import_node_path75.default.join(import_node_os26.default.homedir(), ".codex", ".codex-global-state.json"),
68070
68219
  logger
68071
68220
  });
68072
68221
  const observer = new SessionObserver({
@@ -68126,13 +68275,13 @@ async function startDaemon(config) {
68126
68275
  screenIdleProbeLogger
68127
68276
  }) : new ClaudeAdapter({ logger, historyReader: new ClaudeHistoryReader() });
68128
68277
  registerAdapter("claude", claudeAdapter);
68129
- const codexIsolatedHome = import_node_path74.default.join(config.dataDir, "codex-home");
68278
+ const codexIsolatedHome = import_node_path75.default.join(config.dataDir, "codex-home");
68130
68279
  try {
68131
- import_node_fs53.default.mkdirSync(codexIsolatedHome, { recursive: true });
68280
+ import_node_fs54.default.mkdirSync(codexIsolatedHome, { recursive: true });
68132
68281
  for (const f of ["auth.json", "config.toml"]) {
68133
- const link = import_node_path74.default.join(codexIsolatedHome, f);
68134
- const target = import_node_path74.default.join(import_node_os26.default.homedir(), ".codex", f);
68135
- if (!import_node_fs53.default.existsSync(link) && import_node_fs53.default.existsSync(target)) import_node_fs53.default.symlinkSync(target, link);
68282
+ const link = import_node_path75.default.join(codexIsolatedHome, f);
68283
+ const target = import_node_path75.default.join(import_node_os26.default.homedir(), ".codex", f);
68284
+ if (!import_node_fs54.default.existsSync(link) && import_node_fs54.default.existsSync(target)) import_node_fs54.default.symlinkSync(target, link);
68136
68285
  }
68137
68286
  } catch (err) {
68138
68287
  logger.warn("codex isolated home setup failed", { err: String(err) });
@@ -68145,11 +68294,11 @@ async function startDaemon(config) {
68145
68294
  // projectPaths 每次调用现取 —— 用户新建 project 后立即生效,不用重启 daemon
68146
68295
  codexHomeFor: (cwd) => resolveCodexHome({
68147
68296
  cwd,
68148
- personaRoot: import_node_path74.default.join(config.dataDir, "personas"),
68297
+ personaRoot: import_node_path75.default.join(config.dataDir, "personas"),
68149
68298
  // 必须用 listMerged(Codex registry ∪ 叠加层):只用本地 list() 的话,用户在
68150
68299
  // Codex 里建的 project 不在表里,在它下面开的会话会被误判成隔离 → Codex 侧看不到
68151
68300
  projectPaths: projectsStore.listMerged().map((x) => x.path),
68152
- sharedHome: import_node_path74.default.join(import_node_os26.default.homedir(), ".codex"),
68301
+ sharedHome: import_node_path75.default.join(import_node_os26.default.homedir(), ".codex"),
68153
68302
  isolatedHome: codexIsolatedHome
68154
68303
  })
68155
68304
  })
@@ -68331,6 +68480,7 @@ async function startDaemon(config) {
68331
68480
  error: err instanceof Error ? err.message : String(err)
68332
68481
  });
68333
68482
  });
68483
+ const appBuilderPublishService = createPublishService();
68334
68484
  const devServerSupervisor = new DevServerSupervisor();
68335
68485
  devServerSupervisor.setLogger(logger);
68336
68486
  const appBuilderPortRange = [];
@@ -68433,11 +68583,11 @@ async function startDaemon(config) {
68433
68583
  // 'persona/<pid>/owner',default 走 'default'。
68434
68584
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
68435
68585
  // guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
68436
- personaRoot: import_node_path74.default.join(config.dataDir, "personas"),
68586
+ personaRoot: import_node_path75.default.join(config.dataDir, "personas"),
68437
68587
  usersRoot
68438
68588
  },
68439
68589
  // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
68440
- personaRoot: import_node_path74.default.join(config.dataDir, "personas"),
68590
+ personaRoot: import_node_path75.default.join(config.dataDir, "personas"),
68441
68591
  // v2 多人 persona 隔离:handler 派生 guest user-dir 放行
68442
68592
  usersRoot,
68443
68593
  // v2 Phase 6: whoami handler 装在 owner principal.displayName + persona 解析
@@ -68518,6 +68668,8 @@ async function startDaemon(config) {
68518
68668
  // app-builder
68519
68669
  appBuilderStore,
68520
68670
  getProjectStore,
68671
+ publishService: appBuilderPublishService,
68672
+ createProjectOnDisk,
68521
68673
  devServerLifecycle: devServerSupervisor,
68522
68674
  // 单栏 refactor §5.6.2 stage fix:appBuilder:project-updated 帧通过 wsServer broadcast。
68523
68675
  // 推给所有 owner 连接(picker 可能没订阅特定 session,全局 broadcast 最稳)。
@@ -68536,11 +68688,11 @@ async function startDaemon(config) {
68536
68688
  // 发布上线脚手架化 (spec 2026-06-03 §5.2):
68537
68689
  // appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
68538
68690
  // dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
68539
- appBuilderPersonaRoot: import_node_path74.default.join(config.dataDir, "personas", "persona-app-builder"),
68691
+ appBuilderPersonaRoot: import_node_path75.default.join(config.dataDir, "personas", "persona-app-builder"),
68540
68692
  // 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
68541
- deployKitRoot: import_node_path74.default.join(config.dataDir, "deploy-kit"),
68693
+ deployKitRoot: import_node_path75.default.join(config.dataDir, "deploy-kit"),
68542
68694
  // scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
68543
- resolvePersonaRoot: (personaId2) => import_node_path74.default.join(config.dataDir, "personas", personaId2),
68695
+ resolvePersonaRoot: (personaId2) => import_node_path75.default.join(config.dataDir, "personas", personaId2),
68544
68696
  // 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
68545
68697
  // 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
68546
68698
  // 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
@@ -68653,12 +68805,12 @@ async function startDaemon(config) {
68653
68805
  });
68654
68806
  Object.assign(handlers, shiftHandlers);
68655
68807
  const deployReceipts = createReceiptStore({
68656
- filePath: import_node_path74.default.join(config.dataDir, "deploy.json"),
68808
+ filePath: import_node_path75.default.join(config.dataDir, "deploy.json"),
68657
68809
  now: () => Date.now(),
68658
68810
  onFlushError: (err) => logger.error("deploy receipts flush failed", { error: String(err) })
68659
68811
  });
68660
68812
  const deploySecrets = createSecretStore({
68661
- filePath: import_node_path74.default.join(config.dataDir, "deploy-secrets.json"),
68813
+ filePath: import_node_path75.default.join(config.dataDir, "deploy-secrets.json"),
68662
68814
  now: () => Date.now(),
68663
68815
  onFlushError: (err) => logger.error("deploy secrets flush failed", { error: String(err) })
68664
68816
  });
@@ -68672,7 +68824,7 @@ async function startDaemon(config) {
68672
68824
  hasPersona: (personaId2) => personaStore.has(personaId2),
68673
68825
  startScan: (deployId, personaId2) => {
68674
68826
  const assetLines = SCANNABLE_ASSETS.map(
68675
- (name) => ` - ${name}\uFF1A${import_node_path74.default.join(config.dataDir, name)}`
68827
+ (name) => ` - ${name}\uFF1A${import_node_path75.default.join(config.dataDir, name)}`
68676
68828
  ).join("\n");
68677
68829
  const prompt = [
68678
68830
  `\u626B\u63CF persona\u300C${personaId2}\u300D\u7684\u90E8\u7F72\u73AF\u5883\uFF0CdeployId=${deployId}\u3002`,
@@ -68713,7 +68865,7 @@ async function startDaemon(config) {
68713
68865
  });
68714
68866
  shiftScheduler.start();
68715
68867
  logger.info("shift scheduler started", { shiftCount: shiftStore.list().length });
68716
- const larkBotStore = createLarkBotBindingStore(import_node_path74.default.join(config.dataDir, "personas"));
68868
+ const larkBotStore = createLarkBotBindingStore(import_node_path75.default.join(config.dataDir, "personas"));
68717
68869
  const larkBotCloud = createLarkBotCloudClient({
68718
68870
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
68719
68871
  apiKey: resolveClawdTicketsApiKey(),
@@ -68727,7 +68879,7 @@ async function startDaemon(config) {
68727
68879
  store: larkBotStore,
68728
68880
  fetchResource: (appId, messageId, fileKey, maxBytes, type) => larkBotCloud.fetchResource(appId, messageId, fileKey, maxBytes, type),
68729
68881
  // 图片落 guest userWorkDir 下(capId = lark-<chat_id>):CC 经 --add-dir + allowRead carve 可 Read
68730
- mediaRootFor: (chatId) => import_node_path74.default.join(deriveUserWorkDir(`lark-${chatId}`, usersRoot), "lark-media"),
68882
+ mediaRootFor: (chatId) => import_node_path75.default.join(deriveUserWorkDir(`lark-${chatId}`, usersRoot), "lark-media"),
68731
68883
  isShuttingDown: () => larkDraining,
68732
68884
  logger: {
68733
68885
  warn: (msg) => logger.warn(msg),
@@ -69032,8 +69184,8 @@ async function startDaemon(config) {
69032
69184
  const lines = [
69033
69185
  `Tunnel: ${r.url}`,
69034
69186
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
69035
- `Frpc config: ${import_node_path74.default.join(config.dataDir, "frpc.toml")}`,
69036
- `Frpc log: ${import_node_path74.default.join(logDir, "frpc.log")}`
69187
+ `Frpc config: ${import_node_path75.default.join(config.dataDir, "frpc.toml")}`,
69188
+ `Frpc log: ${import_node_path75.default.join(logDir, "frpc.log")}`
69037
69189
  ];
69038
69190
  const width2 = Math.max(...lines.map((l) => l.length));
69039
69191
  const bar = "\u2550".repeat(width2 + 4);
@@ -69046,8 +69198,8 @@ ${bar}
69046
69198
 
69047
69199
  `);
69048
69200
  try {
69049
- const connectPath = import_node_path74.default.join(config.dataDir, "connect.txt");
69050
- import_node_fs53.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
69201
+ const connectPath = import_node_path75.default.join(config.dataDir, "connect.txt");
69202
+ import_node_fs54.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
69051
69203
  } catch {
69052
69204
  }
69053
69205
  } catch (err) {
@@ -69136,9 +69288,9 @@ ${bar}
69136
69288
  };
69137
69289
  }
69138
69290
  function migrateDropPersonsDir(dataDir) {
69139
- const dir = import_node_path74.default.join(dataDir, "persons");
69291
+ const dir = import_node_path75.default.join(dataDir, "persons");
69140
69292
  try {
69141
- import_node_fs53.default.rmSync(dir, { recursive: true, force: true });
69293
+ import_node_fs54.default.rmSync(dir, { recursive: true, force: true });
69142
69294
  } catch {
69143
69295
  }
69144
69296
  }