@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:
|
|
454
|
-
const fullPath = [...
|
|
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,
|
|
765
|
+
constructor(parent, value, path94, key) {
|
|
766
766
|
this._cachedPath = [];
|
|
767
767
|
this.parent = parent;
|
|
768
768
|
this.data = value;
|
|
769
|
-
this._path =
|
|
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
|
|
9827
|
-
_req.url = typeof
|
|
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(
|
|
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 <
|
|
9999
|
-
const char =
|
|
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
|
|
10131
|
-
const parts = parsePath(
|
|
10130
|
+
for (const path94 of paths) {
|
|
10131
|
+
const parts = parsePath(path94);
|
|
10132
10132
|
if (parts.includes("*")) {
|
|
10133
|
-
redactWildcardPath(obj, parts, censor,
|
|
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,
|
|
10219
|
-
const fullPath = [...pathArray.slice(0, pathLength), ...
|
|
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
|
|
10255
|
-
const parts = parsePath(
|
|
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(
|
|
10308
|
-
if (typeof
|
|
10307
|
+
function validatePath(path94) {
|
|
10308
|
+
if (typeof path94 !== "string") {
|
|
10309
10309
|
throw new Error("Paths must be (non-empty) strings");
|
|
10310
10310
|
}
|
|
10311
|
-
if (
|
|
10311
|
+
if (path94 === "") {
|
|
10312
10312
|
throw new Error("Invalid redaction path ()");
|
|
10313
10313
|
}
|
|
10314
|
-
if (
|
|
10315
|
-
throw new Error(`Invalid redaction path (${
|
|
10314
|
+
if (path94.includes("..")) {
|
|
10315
|
+
throw new Error(`Invalid redaction path (${path94})`);
|
|
10316
10316
|
}
|
|
10317
|
-
if (
|
|
10318
|
-
throw new Error(`Invalid redaction path (${
|
|
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 <
|
|
10324
|
-
const char =
|
|
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 (${
|
|
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 (${
|
|
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
|
|
10351
|
-
validatePath(
|
|
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,
|
|
10520
|
-
return censor(value, [k2, ...
|
|
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
|
|
10738
|
+
var fs83 = require("fs");
|
|
10739
10739
|
var EventEmitter3 = require("events");
|
|
10740
10740
|
var inherits = require("util").inherits;
|
|
10741
|
-
var
|
|
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)
|
|
10796
|
-
const fd =
|
|
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
|
-
|
|
10803
|
+
fs83.mkdir(path94.dirname(file), { recursive: true }, (err) => {
|
|
10804
10804
|
if (err) return fileOpened(err);
|
|
10805
|
-
|
|
10805
|
+
fs83.open(file, flags, mode, fileOpened);
|
|
10806
10806
|
});
|
|
10807
10807
|
} else {
|
|
10808
|
-
|
|
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 = () =>
|
|
10850
|
-
fsWrite = () =>
|
|
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
|
|
10859
|
+
return fs83.writeSync(this.fd, this._writingBuf);
|
|
10860
10860
|
}
|
|
10861
|
-
return
|
|
10861
|
+
return fs83.writeSync(this.fd, this._writingBuf, "utf8");
|
|
10862
10862
|
};
|
|
10863
10863
|
fsWrite = () => {
|
|
10864
10864
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
10865
|
-
return
|
|
10865
|
+
return fs83.write(this.fd, this._writingBuf, this.release);
|
|
10866
10866
|
}
|
|
10867
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) ?
|
|
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
|
-
|
|
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 =
|
|
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) ?
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
14435
|
-
var last =
|
|
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:
|
|
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:
|
|
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
|
|
15030
|
+
const path94 = typeof rec4.path === "string" ? rec4.path : null;
|
|
15031
15031
|
const content = typeof rec4.content === "string" ? rec4.content : null;
|
|
15032
|
-
if (!
|
|
15033
|
-
const entry = { path:
|
|
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
|
|
15899
|
+
const path94 = typeof rec4.path === "string" ? rec4.path : null;
|
|
15900
15900
|
const content = typeof rec4.content === "string" ? rec4.content : null;
|
|
15901
|
-
if (!
|
|
15902
|
-
const out = { path:
|
|
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(
|
|
36877
|
-
var parts =
|
|
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(
|
|
42731
|
-
if (
|
|
42732
|
-
|
|
42730
|
+
var parentFolder = function(path94) {
|
|
42731
|
+
if (path94.slice(-1) === "/") {
|
|
42732
|
+
path94 = path94.substring(0, path94.length - 1);
|
|
42733
42733
|
}
|
|
42734
|
-
var lastSlash =
|
|
42735
|
-
return lastSlash > 0 ?
|
|
42734
|
+
var lastSlash = path94.lastIndexOf("/");
|
|
42735
|
+
return lastSlash > 0 ? path94.substring(0, lastSlash) : "";
|
|
42736
42736
|
};
|
|
42737
|
-
var forceTrailingSlash = function(
|
|
42738
|
-
if (
|
|
42739
|
-
|
|
42737
|
+
var forceTrailingSlash = function(path94) {
|
|
42738
|
+
if (path94.slice(-1) !== "/") {
|
|
42739
|
+
path94 += "/";
|
|
42740
42740
|
}
|
|
42741
|
-
return
|
|
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 =
|
|
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
|
-
|
|
43758
|
-
stream =
|
|
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
|
|
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
|
-
|
|
43788
|
-
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
44002
|
+
import_node_fs56 = require("fs");
|
|
44003
44003
|
import_node_os27 = __toESM(require("os"), 1);
|
|
44004
|
-
|
|
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
|
|
44280
|
-
var
|
|
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,
|
|
51428
|
-
items.push({ type: "skill", name, path:
|
|
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
|
|
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
|
-
...
|
|
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,
|
|
60870
|
+
async function centralRequest(opts, path94, init) {
|
|
60869
60871
|
const f = opts.fetchImpl ?? globalThis.fetch;
|
|
60870
|
-
const url = `${opts.api.replace(/\/+$/, "")}${
|
|
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
|
|
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.
|
|
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
|
|
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,
|
|
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/
|
|
64995
|
-
var
|
|
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
|
|
65340
|
-
var
|
|
65341
|
-
var
|
|
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
|
|
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
|
|
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
|
|
64931
|
+
const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
|
|
64932
|
+
let project;
|
|
64933
|
+
let projectDir;
|
|
65488
64934
|
try {
|
|
65489
|
-
const
|
|
65490
|
-
|
|
65491
|
-
|
|
65492
|
-
|
|
65493
|
-
|
|
65494
|
-
|
|
65495
|
-
|
|
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
|
-
|
|
65500
|
-
|
|
65501
|
-
|
|
65502
|
-
|
|
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
|
|
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 &&
|
|
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,
|
|
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
|
|
65703
|
-
|
|
65704
|
-
|
|
65705
|
-
|
|
65706
|
-
|
|
65707
|
-
|
|
65708
|
-
|
|
65709
|
-
|
|
65710
|
-
|
|
65711
|
-
|
|
65712
|
-
|
|
65713
|
-
|
|
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
|
-
|
|
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
|
|
65761
|
-
if (
|
|
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:
|
|
65765
|
-
stage: job.stage
|
|
65198
|
+
jobId: inFlightJobId
|
|
65766
65199
|
});
|
|
65767
|
-
|
|
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
|
|
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(
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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:
|
|
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
|
|
66225
|
+
var import_node_child_process18 = require("child_process");
|
|
66792
66226
|
init_claude();
|
|
66793
66227
|
|
|
66794
66228
|
// src/tools/shell-path.ts
|
|
66795
|
-
var
|
|
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,
|
|
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
|
|
66807
|
-
if (
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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 =
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
67591
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
67725
|
+
file: import_node_path75.default.join(logDir, "screen-idle-probe.log")
|
|
67604
67726
|
});
|
|
67605
67727
|
const CAP_TARGETS = [
|
|
67606
|
-
|
|
67607
|
-
|
|
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(
|
|
67730
|
-
const usersRoot =
|
|
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:
|
|
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:
|
|
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:
|
|
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 :
|
|
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
|
-
|
|
67925
|
+
import_node_path75.default.join(here, "dispatch", "mcp-server.cjs"),
|
|
67804
67926
|
// 生产 dist/index → dist/dispatch/mcp-server.cjs
|
|
67805
|
-
|
|
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) =>
|
|
67930
|
+
const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs54.default.existsSync(p2));
|
|
67809
67931
|
if (dispatchServerScriptPath) {
|
|
67810
|
-
const dispatchLogPath =
|
|
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
|
-
|
|
67831
|
-
|
|
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) =>
|
|
67955
|
+
const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs54.default.existsSync(p2));
|
|
67834
67956
|
if (ticketServerScriptPath) {
|
|
67835
|
-
const ticketLogPath =
|
|
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
|
-
|
|
67858
|
-
|
|
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) =>
|
|
67982
|
+
const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs54.default.existsSync(p2));
|
|
67861
67983
|
if (shiftServerScriptPath) {
|
|
67862
|
-
const shiftLogPath =
|
|
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
|
-
|
|
67884
|
-
|
|
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) =>
|
|
68008
|
+
const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs54.default.existsSync(p2));
|
|
67887
68009
|
if (inboxServerScriptPath) {
|
|
67888
|
-
const inboxLogPath =
|
|
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
|
-
|
|
67910
|
-
|
|
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) =>
|
|
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
|
-
|
|
67933
|
-
|
|
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) =>
|
|
68057
|
+
const rpcToolServerScriptPath = rpcToolServerCandidates.find((p2) => import_node_fs54.default.existsSync(p2));
|
|
67936
68058
|
if (rpcToolServerScriptPath) {
|
|
67937
|
-
const rpcToolLogPath =
|
|
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:
|
|
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:
|
|
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 =
|
|
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 =
|
|
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:
|
|
68216
|
+
file: import_node_path75.default.join(config.dataDir, "projects.json"),
|
|
68068
68217
|
clawdDir: config.dataDir,
|
|
68069
|
-
codexStateFile:
|
|
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 =
|
|
68278
|
+
const codexIsolatedHome = import_node_path75.default.join(config.dataDir, "codex-home");
|
|
68130
68279
|
try {
|
|
68131
|
-
|
|
68280
|
+
import_node_fs54.default.mkdirSync(codexIsolatedHome, { recursive: true });
|
|
68132
68281
|
for (const f of ["auth.json", "config.toml"]) {
|
|
68133
|
-
const link =
|
|
68134
|
-
const target =
|
|
68135
|
-
if (!
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
68691
|
+
appBuilderPersonaRoot: import_node_path75.default.join(config.dataDir, "personas", "persona-app-builder"),
|
|
68540
68692
|
// 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
|
|
68541
|
-
deployKitRoot:
|
|
68693
|
+
deployKitRoot: import_node_path75.default.join(config.dataDir, "deploy-kit"),
|
|
68542
68694
|
// scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
|
|
68543
|
-
resolvePersonaRoot: (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:
|
|
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:
|
|
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${
|
|
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(
|
|
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) =>
|
|
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: ${
|
|
69036
|
-
`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 =
|
|
69050
|
-
|
|
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 =
|
|
69291
|
+
const dir = import_node_path75.default.join(dataDir, "persons");
|
|
69140
69292
|
try {
|
|
69141
|
-
|
|
69293
|
+
import_node_fs54.default.rmSync(dir, { recursive: true, force: true });
|
|
69142
69294
|
} catch {
|
|
69143
69295
|
}
|
|
69144
69296
|
}
|