@corenel/sidecar 0.4.0 → 0.4.2
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/LICENSE +93 -0
- package/dist/cli.js +683 -362
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -1184,8 +1184,8 @@ var require_stringifyString = __commonJS({
|
|
|
1184
1184
|
case "u":
|
|
1185
1185
|
{
|
|
1186
1186
|
str8 += json.slice(start, i2);
|
|
1187
|
-
const
|
|
1188
|
-
switch (
|
|
1187
|
+
const code2 = json.substr(i2 + 2, 4);
|
|
1188
|
+
switch (code2) {
|
|
1189
1189
|
case "0000":
|
|
1190
1190
|
str8 += "\\0";
|
|
1191
1191
|
break;
|
|
@@ -1211,8 +1211,8 @@ var require_stringifyString = __commonJS({
|
|
|
1211
1211
|
str8 += "\\P";
|
|
1212
1212
|
break;
|
|
1213
1213
|
default:
|
|
1214
|
-
if (
|
|
1215
|
-
str8 += "\\x" +
|
|
1214
|
+
if (code2.substr(0, 2) === "00")
|
|
1215
|
+
str8 += "\\x" + code2.substr(2);
|
|
1216
1216
|
else
|
|
1217
1217
|
str8 += json.substr(i2, 6);
|
|
1218
1218
|
}
|
|
@@ -3711,22 +3711,22 @@ var require_errors = __commonJS({
|
|
|
3711
3711
|
"../../node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js"(exports) {
|
|
3712
3712
|
"use strict";
|
|
3713
3713
|
var YAMLError = class extends Error {
|
|
3714
|
-
constructor(name, pos,
|
|
3714
|
+
constructor(name, pos, code2, message) {
|
|
3715
3715
|
super();
|
|
3716
3716
|
this.name = name;
|
|
3717
|
-
this.code =
|
|
3717
|
+
this.code = code2;
|
|
3718
3718
|
this.message = message;
|
|
3719
3719
|
this.pos = pos;
|
|
3720
3720
|
}
|
|
3721
3721
|
};
|
|
3722
3722
|
var YAMLParseError = class extends YAMLError {
|
|
3723
|
-
constructor(pos,
|
|
3724
|
-
super("YAMLParseError", pos,
|
|
3723
|
+
constructor(pos, code2, message) {
|
|
3724
|
+
super("YAMLParseError", pos, code2, message);
|
|
3725
3725
|
}
|
|
3726
3726
|
};
|
|
3727
3727
|
var YAMLWarning = class extends YAMLError {
|
|
3728
|
-
constructor(pos,
|
|
3729
|
-
super("YAMLWarning", pos,
|
|
3728
|
+
constructor(pos, code2, message) {
|
|
3729
|
+
super("YAMLWarning", pos, code2, message);
|
|
3730
3730
|
}
|
|
3731
3731
|
};
|
|
3732
3732
|
var prettifyError = (src, lc) => (error) => {
|
|
@@ -4632,7 +4632,7 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
4632
4632
|
const { offset, type, source, end } = scalar;
|
|
4633
4633
|
let _type;
|
|
4634
4634
|
let value;
|
|
4635
|
-
const _onError = (rel,
|
|
4635
|
+
const _onError = (rel, code2, msg) => onError(offset + rel, code2, msg);
|
|
4636
4636
|
switch (type) {
|
|
4637
4637
|
case "scalar":
|
|
4638
4638
|
_type = Scalar.Scalar.PLAIN;
|
|
@@ -4827,9 +4827,9 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
4827
4827
|
function parseCharCode(source, offset, length, onError) {
|
|
4828
4828
|
const cc = source.substr(offset, length);
|
|
4829
4829
|
const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc);
|
|
4830
|
-
const
|
|
4830
|
+
const code2 = ok ? parseInt(cc, 16) : NaN;
|
|
4831
4831
|
try {
|
|
4832
|
-
return String.fromCodePoint(
|
|
4832
|
+
return String.fromCodePoint(code2);
|
|
4833
4833
|
} catch {
|
|
4834
4834
|
const raw = source.substr(offset - 2, length + 2);
|
|
4835
4835
|
onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`);
|
|
@@ -5151,12 +5151,12 @@ var require_composer = __commonJS({
|
|
|
5151
5151
|
this.prelude = [];
|
|
5152
5152
|
this.errors = [];
|
|
5153
5153
|
this.warnings = [];
|
|
5154
|
-
this.onError = (source,
|
|
5154
|
+
this.onError = (source, code2, message, warning) => {
|
|
5155
5155
|
const pos = getErrorPos(source);
|
|
5156
5156
|
if (warning)
|
|
5157
|
-
this.warnings.push(new errors.YAMLWarning(pos,
|
|
5157
|
+
this.warnings.push(new errors.YAMLWarning(pos, code2, message));
|
|
5158
5158
|
else
|
|
5159
|
-
this.errors.push(new errors.YAMLParseError(pos,
|
|
5159
|
+
this.errors.push(new errors.YAMLParseError(pos, code2, message));
|
|
5160
5160
|
};
|
|
5161
5161
|
this.directives = new directives.Directives({ version: options.version || "1.2" });
|
|
5162
5162
|
this.options = options;
|
|
@@ -5318,12 +5318,12 @@ var require_cst_scalar = __commonJS({
|
|
|
5318
5318
|
var stringifyString = require_stringifyString();
|
|
5319
5319
|
function resolveAsScalar(token, strict = true, onError) {
|
|
5320
5320
|
if (token) {
|
|
5321
|
-
const _onError = (pos,
|
|
5321
|
+
const _onError = (pos, code2, message) => {
|
|
5322
5322
|
const offset = typeof pos === "number" ? pos : Array.isArray(pos) ? pos[0] : pos.offset;
|
|
5323
5323
|
if (onError)
|
|
5324
|
-
onError(offset,
|
|
5324
|
+
onError(offset, code2, message);
|
|
5325
5325
|
else
|
|
5326
|
-
throw new errors.YAMLParseError([offset, offset + 1],
|
|
5326
|
+
throw new errors.YAMLParseError([offset, offset + 1], code2, message);
|
|
5327
5327
|
};
|
|
5328
5328
|
switch (token.type) {
|
|
5329
5329
|
case "scalar":
|
|
@@ -6904,14 +6904,14 @@ var require_parser = __commonJS({
|
|
|
6904
6904
|
case "scalar":
|
|
6905
6905
|
case "single-quoted-scalar":
|
|
6906
6906
|
case "double-quoted-scalar": {
|
|
6907
|
-
const
|
|
6907
|
+
const fs15 = this.flowScalar(this.type);
|
|
6908
6908
|
if (atNextItem || it2.value) {
|
|
6909
|
-
map.items.push({ start, key:
|
|
6909
|
+
map.items.push({ start, key: fs15, sep: [] });
|
|
6910
6910
|
this.onKeyLine = true;
|
|
6911
6911
|
} else if (it2.sep) {
|
|
6912
|
-
this.stack.push(
|
|
6912
|
+
this.stack.push(fs15);
|
|
6913
6913
|
} else {
|
|
6914
|
-
Object.assign(it2, { key:
|
|
6914
|
+
Object.assign(it2, { key: fs15, sep: [] });
|
|
6915
6915
|
this.onKeyLine = true;
|
|
6916
6916
|
}
|
|
6917
6917
|
return;
|
|
@@ -7039,13 +7039,13 @@ var require_parser = __commonJS({
|
|
|
7039
7039
|
case "scalar":
|
|
7040
7040
|
case "single-quoted-scalar":
|
|
7041
7041
|
case "double-quoted-scalar": {
|
|
7042
|
-
const
|
|
7042
|
+
const fs15 = this.flowScalar(this.type);
|
|
7043
7043
|
if (!it2 || it2.value)
|
|
7044
|
-
fc.items.push({ start: [], key:
|
|
7044
|
+
fc.items.push({ start: [], key: fs15, sep: [] });
|
|
7045
7045
|
else if (it2.sep)
|
|
7046
|
-
this.stack.push(
|
|
7046
|
+
this.stack.push(fs15);
|
|
7047
7047
|
else
|
|
7048
|
-
Object.assign(it2, { key:
|
|
7048
|
+
Object.assign(it2, { key: fs15, sep: [] });
|
|
7049
7049
|
return;
|
|
7050
7050
|
}
|
|
7051
7051
|
case "flow-map-end":
|
|
@@ -12248,11 +12248,11 @@ var require_compiler = __commonJS({
|
|
|
12248
12248
|
_proto._popBuffer = function _popBuffer() {
|
|
12249
12249
|
this.buffer = this.bufferStack.pop();
|
|
12250
12250
|
};
|
|
12251
|
-
_proto._emit = function _emit(
|
|
12252
|
-
this.codebuf.push(
|
|
12251
|
+
_proto._emit = function _emit(code2) {
|
|
12252
|
+
this.codebuf.push(code2);
|
|
12253
12253
|
};
|
|
12254
|
-
_proto._emitLine = function _emitLine(
|
|
12255
|
-
this._emit(
|
|
12254
|
+
_proto._emitLine = function _emitLine(code2) {
|
|
12255
|
+
this._emit(code2 + "\n");
|
|
12256
12256
|
};
|
|
12257
12257
|
_proto._emitLines = function _emitLines() {
|
|
12258
12258
|
var _this = this;
|
|
@@ -13268,7 +13268,7 @@ var require_filters = __commonJS({
|
|
|
13268
13268
|
return r2.copySafeness(str8, res);
|
|
13269
13269
|
}
|
|
13270
13270
|
_exports.indent = indent;
|
|
13271
|
-
function
|
|
13271
|
+
function join16(arr, del2, attr) {
|
|
13272
13272
|
del2 = del2 || "";
|
|
13273
13273
|
if (attr) {
|
|
13274
13274
|
arr = lib.map(arr, function(v2) {
|
|
@@ -13277,7 +13277,7 @@ var require_filters = __commonJS({
|
|
|
13277
13277
|
}
|
|
13278
13278
|
return arr.join(del2);
|
|
13279
13279
|
}
|
|
13280
|
-
_exports.join =
|
|
13280
|
+
_exports.join = join16;
|
|
13281
13281
|
function last(arr) {
|
|
13282
13282
|
return arr[arr.length - 1];
|
|
13283
13283
|
}
|
|
@@ -13711,7 +13711,7 @@ var require_node_loaders = __commonJS({
|
|
|
13711
13711
|
};
|
|
13712
13712
|
return _setPrototypeOf(o2, p2);
|
|
13713
13713
|
}
|
|
13714
|
-
var
|
|
13714
|
+
var fs15 = __require("fs");
|
|
13715
13715
|
var path = __require("path");
|
|
13716
13716
|
var Loader2 = require_loader();
|
|
13717
13717
|
var _require = require_precompiled_loader();
|
|
@@ -13740,7 +13740,7 @@ var require_node_loaders = __commonJS({
|
|
|
13740
13740
|
} catch (e2) {
|
|
13741
13741
|
throw new Error("watch requires chokidar to be installed");
|
|
13742
13742
|
}
|
|
13743
|
-
var paths = _this.searchPaths.filter(
|
|
13743
|
+
var paths = _this.searchPaths.filter(fs15.existsSync);
|
|
13744
13744
|
var watcher = chokidar.watch(paths);
|
|
13745
13745
|
watcher.on("all", function(event, fullname) {
|
|
13746
13746
|
fullname = path.resolve(fullname);
|
|
@@ -13761,7 +13761,7 @@ var require_node_loaders = __commonJS({
|
|
|
13761
13761
|
for (var i2 = 0; i2 < paths.length; i2++) {
|
|
13762
13762
|
var basePath = path.resolve(paths[i2]);
|
|
13763
13763
|
var p2 = path.resolve(paths[i2], name);
|
|
13764
|
-
if (p2.indexOf(basePath) === 0 &&
|
|
13764
|
+
if (p2.indexOf(basePath) === 0 && fs15.existsSync(p2)) {
|
|
13765
13765
|
fullpath = p2;
|
|
13766
13766
|
break;
|
|
13767
13767
|
}
|
|
@@ -13771,7 +13771,7 @@ var require_node_loaders = __commonJS({
|
|
|
13771
13771
|
}
|
|
13772
13772
|
this.pathsToNames[fullpath] = name;
|
|
13773
13773
|
var source = {
|
|
13774
|
-
src:
|
|
13774
|
+
src: fs15.readFileSync(fullpath, "utf-8"),
|
|
13775
13775
|
path: fullpath,
|
|
13776
13776
|
noCache: this.noCache
|
|
13777
13777
|
};
|
|
@@ -13823,7 +13823,7 @@ var require_node_loaders = __commonJS({
|
|
|
13823
13823
|
}
|
|
13824
13824
|
this.pathsToNames[fullpath] = name;
|
|
13825
13825
|
var source = {
|
|
13826
|
-
src:
|
|
13826
|
+
src: fs15.readFileSync(fullpath, "utf-8"),
|
|
13827
13827
|
path: fullpath,
|
|
13828
13828
|
noCache: this.noCache
|
|
13829
13829
|
};
|
|
@@ -14580,7 +14580,7 @@ var require_precompile_global = __commonJS({
|
|
|
14580
14580
|
var require_precompile = __commonJS({
|
|
14581
14581
|
"../../node_modules/.pnpm/nunjucks@3.2.4/node_modules/nunjucks/src/precompile.js"(exports, module) {
|
|
14582
14582
|
"use strict";
|
|
14583
|
-
var
|
|
14583
|
+
var fs15 = __require("fs");
|
|
14584
14584
|
var path = __require("path");
|
|
14585
14585
|
var _require = require_lib();
|
|
14586
14586
|
var _prettifyError = _require._prettifyError;
|
|
@@ -14613,14 +14613,14 @@ var require_precompile = __commonJS({
|
|
|
14613
14613
|
if (opts.isString) {
|
|
14614
14614
|
return precompileString(input, opts);
|
|
14615
14615
|
}
|
|
14616
|
-
var pathStats =
|
|
14616
|
+
var pathStats = fs15.existsSync(input) && fs15.statSync(input);
|
|
14617
14617
|
var precompiled = [];
|
|
14618
14618
|
var templates = [];
|
|
14619
14619
|
function addTemplates(dir) {
|
|
14620
|
-
|
|
14620
|
+
fs15.readdirSync(dir).forEach(function(file) {
|
|
14621
14621
|
var filepath = path.join(dir, file);
|
|
14622
14622
|
var subpath = filepath.substr(path.join(input, "/").length);
|
|
14623
|
-
var stat2 =
|
|
14623
|
+
var stat2 = fs15.statSync(filepath);
|
|
14624
14624
|
if (stat2 && stat2.isDirectory()) {
|
|
14625
14625
|
subpath += "/";
|
|
14626
14626
|
if (!match(subpath, opts.exclude)) {
|
|
@@ -14632,13 +14632,13 @@ var require_precompile = __commonJS({
|
|
|
14632
14632
|
});
|
|
14633
14633
|
}
|
|
14634
14634
|
if (pathStats.isFile()) {
|
|
14635
|
-
precompiled.push(_precompile(
|
|
14635
|
+
precompiled.push(_precompile(fs15.readFileSync(input, "utf-8"), opts.name || input, env));
|
|
14636
14636
|
} else if (pathStats.isDirectory()) {
|
|
14637
14637
|
addTemplates(input);
|
|
14638
14638
|
for (var i2 = 0; i2 < templates.length; i2++) {
|
|
14639
14639
|
var name = templates[i2].replace(path.join(input, "/"), "");
|
|
14640
14640
|
try {
|
|
14641
|
-
precompiled.push(_precompile(
|
|
14641
|
+
precompiled.push(_precompile(fs15.readFileSync(templates[i2], "utf-8"), name, env));
|
|
14642
14642
|
} catch (e2) {
|
|
14643
14643
|
if (opts.force) {
|
|
14644
14644
|
console.error(e2);
|
|
@@ -18032,8 +18032,8 @@ var require_lib3 = __commonJS({
|
|
|
18032
18032
|
stream.end();
|
|
18033
18033
|
}
|
|
18034
18034
|
}
|
|
18035
|
-
fetch3.isRedirect = function(
|
|
18036
|
-
return
|
|
18035
|
+
fetch3.isRedirect = function(code2) {
|
|
18036
|
+
return code2 === 301 || code2 === 302 || code2 === 303 || code2 === 307 || code2 === 308;
|
|
18037
18037
|
};
|
|
18038
18038
|
fetch3.Promise = global.Promise;
|
|
18039
18039
|
module.exports = exports = fetch3;
|
|
@@ -21357,7 +21357,7 @@ __export(fileFromPath_exports, {
|
|
|
21357
21357
|
fileFromPathSync: () => fileFromPathSync,
|
|
21358
21358
|
isFile: () => isFile
|
|
21359
21359
|
});
|
|
21360
|
-
import { statSync, createReadStream, promises as
|
|
21360
|
+
import { statSync, createReadStream, promises as fs9 } from "fs";
|
|
21361
21361
|
import { basename as basename2 } from "path";
|
|
21362
21362
|
function createFileFromPath(path, { mtimeMs, size }, filenameOrOptions, options = {}) {
|
|
21363
21363
|
let filename;
|
|
@@ -21380,7 +21380,7 @@ function fileFromPathSync(path, filenameOrOptions, options = {}) {
|
|
|
21380
21380
|
return createFileFromPath(path, stats, filenameOrOptions, options);
|
|
21381
21381
|
}
|
|
21382
21382
|
async function fileFromPath2(path, filenameOrOptions, options) {
|
|
21383
|
-
const stats = await
|
|
21383
|
+
const stats = await fs9.stat(path);
|
|
21384
21384
|
return createFileFromPath(path, stats, filenameOrOptions, options);
|
|
21385
21385
|
}
|
|
21386
21386
|
var import_node_domexception, __classPrivateFieldSet4, __classPrivateFieldGet5, _FileFromPath_path, _FileFromPath_start, MESSAGE, FileFromPath;
|
|
@@ -21421,7 +21421,7 @@ var init_fileFromPath = __esm({
|
|
|
21421
21421
|
});
|
|
21422
21422
|
}
|
|
21423
21423
|
async *stream() {
|
|
21424
|
-
const { mtimeMs } = await
|
|
21424
|
+
const { mtimeMs } = await fs9.stat(__classPrivateFieldGet5(this, _FileFromPath_path, "f"));
|
|
21425
21425
|
if (mtimeMs > this.lastModified) {
|
|
21426
21426
|
throw new import_node_domexception.default(MESSAGE, "NotReadableError");
|
|
21427
21427
|
}
|
|
@@ -21440,7 +21440,7 @@ var init_fileFromPath = __esm({
|
|
|
21440
21440
|
});
|
|
21441
21441
|
|
|
21442
21442
|
// src/cli.ts
|
|
21443
|
-
import { join as
|
|
21443
|
+
import { join as join15 } from "node:path";
|
|
21444
21444
|
import { readFileSync } from "node:fs";
|
|
21445
21445
|
import { spawn as spawn9 } from "node:child_process";
|
|
21446
21446
|
|
|
@@ -22041,7 +22041,11 @@ var SidecarServer = class {
|
|
|
22041
22041
|
}, {
|
|
22042
22042
|
...m2.root ? { root: m2.root } : {},
|
|
22043
22043
|
...m2.modeId ? { modeId: m2.modeId } : {},
|
|
22044
|
-
...m2.resume ? { resume: m2.resume } : {}
|
|
22044
|
+
...m2.resume ? { resume: m2.resume } : {},
|
|
22045
|
+
/* Passed through untouched. The daemon does not decide WHICH servers a
|
|
22046
|
+
* user has -- the client does -- and the ACP client drops them if the
|
|
22047
|
+
* agent never advertised mcpCapabilities.http. */
|
|
22048
|
+
...m2.mcpServers?.length ? { mcpServers: m2.mcpServers } : {}
|
|
22045
22049
|
});
|
|
22046
22050
|
this.ch.send({ t: "acp-started", id: m2.id, ...started });
|
|
22047
22051
|
} catch (err) {
|
|
@@ -22507,16 +22511,79 @@ keeps serving your browser, so the restart would look successful and change noth
|
|
|
22507
22511
|
}
|
|
22508
22512
|
}
|
|
22509
22513
|
|
|
22510
|
-
// src/
|
|
22514
|
+
// src/pidFile.ts
|
|
22511
22515
|
import { promises as fs } from "node:fs";
|
|
22516
|
+
import { join } from "node:path";
|
|
22517
|
+
function pidPath(dir) {
|
|
22518
|
+
return join(dir, "daemon.pid");
|
|
22519
|
+
}
|
|
22520
|
+
function isAlive(pid, kill = process.kill) {
|
|
22521
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
22522
|
+
try {
|
|
22523
|
+
kill(pid, 0);
|
|
22524
|
+
return true;
|
|
22525
|
+
} catch (e2) {
|
|
22526
|
+
return e2?.code === "EPERM";
|
|
22527
|
+
}
|
|
22528
|
+
}
|
|
22529
|
+
async function readPidFile(dir) {
|
|
22530
|
+
let raw;
|
|
22531
|
+
try {
|
|
22532
|
+
raw = await fs.readFile(pidPath(dir), "utf8");
|
|
22533
|
+
} catch {
|
|
22534
|
+
return null;
|
|
22535
|
+
}
|
|
22536
|
+
try {
|
|
22537
|
+
const o2 = JSON.parse(raw);
|
|
22538
|
+
if (!o2 || typeof o2 !== "object") return null;
|
|
22539
|
+
const r2 = o2;
|
|
22540
|
+
if (typeof r2.pid !== "number" || typeof r2.port !== "number") return null;
|
|
22541
|
+
return { pid: r2.pid, port: r2.port, startedAt: typeof r2.startedAt === "number" ? r2.startedAt : 0 };
|
|
22542
|
+
} catch {
|
|
22543
|
+
return null;
|
|
22544
|
+
}
|
|
22545
|
+
}
|
|
22546
|
+
async function assertStateDirFree(dir, now2 = Date.now()) {
|
|
22547
|
+
const rec5 = await readPidFile(dir);
|
|
22548
|
+
if (!rec5 || !isAlive(rec5.pid)) return;
|
|
22549
|
+
const mins = rec5.startedAt ? Math.max(0, Math.round((now2 - rec5.startedAt) / 6e4)) : null;
|
|
22550
|
+
const age = mins === null ? "" : ` (started ${mins}m ago)`;
|
|
22551
|
+
throw new Error(
|
|
22552
|
+
`another sidecar is already using this state dir \u2014 pid ${rec5.pid} on port ${rec5.port}${age}.
|
|
22553
|
+
Two daemons sharing a state dir share their IDENTITY: they read the same daemon.id and
|
|
22554
|
+
advertise the same sidecarId, so a paired browser sees one sidecar, one sync baseline and
|
|
22555
|
+
one working folder for what are really several machines' worth of state.
|
|
22556
|
+
Run this one with its own --state-dir <dir>, or stop the other first.`
|
|
22557
|
+
);
|
|
22558
|
+
}
|
|
22559
|
+
async function writePidFile(dir, port, pid = process.pid, now2 = Date.now()) {
|
|
22560
|
+
await fs.mkdir(dir, { recursive: true });
|
|
22561
|
+
const rec5 = { pid, port, startedAt: now2 };
|
|
22562
|
+
await fs.writeFile(pidPath(dir), `${JSON.stringify(rec5)}
|
|
22563
|
+
`, { mode: 384 });
|
|
22564
|
+
}
|
|
22565
|
+
async function clearPidFile(dir, pid = process.pid) {
|
|
22566
|
+
const rec5 = await readPidFile(dir);
|
|
22567
|
+
if (!rec5 || rec5.pid !== pid) return;
|
|
22568
|
+
try {
|
|
22569
|
+
await fs.rm(pidPath(dir), { force: true });
|
|
22570
|
+
} catch {
|
|
22571
|
+
}
|
|
22572
|
+
}
|
|
22573
|
+
|
|
22574
|
+
// src/token.ts
|
|
22575
|
+
import { promises as fs2 } from "node:fs";
|
|
22512
22576
|
import { randomBytes } from "node:crypto";
|
|
22513
22577
|
import { homedir } from "node:os";
|
|
22514
|
-
import { join } from "node:path";
|
|
22578
|
+
import { join as join2 } from "node:path";
|
|
22515
22579
|
function corenelDir() {
|
|
22516
|
-
return
|
|
22580
|
+
return join2(homedir(), stateDirName());
|
|
22581
|
+
}
|
|
22582
|
+
function sidecarSubdir(stateDir = corenelDir()) {
|
|
22583
|
+
return join2(stateDir, "sidecar");
|
|
22517
22584
|
}
|
|
22518
22585
|
function tokenPath() {
|
|
22519
|
-
return
|
|
22586
|
+
return join2(sidecarSubdir(), "daemon.token");
|
|
22520
22587
|
}
|
|
22521
22588
|
function isEnoent(err) {
|
|
22522
22589
|
return typeof err === "object" && err !== null && err.code === "ENOENT";
|
|
@@ -22524,16 +22591,16 @@ function isEnoent(err) {
|
|
|
22524
22591
|
async function loadOrCreateToken() {
|
|
22525
22592
|
const path = tokenPath();
|
|
22526
22593
|
try {
|
|
22527
|
-
const existing = (await
|
|
22594
|
+
const existing = (await fs2.readFile(path, "utf8")).trim();
|
|
22528
22595
|
if (existing) return existing;
|
|
22529
22596
|
} catch (err) {
|
|
22530
22597
|
if (!isEnoent(err)) throw err;
|
|
22531
22598
|
}
|
|
22532
|
-
await
|
|
22599
|
+
await fs2.mkdir(sidecarSubdir(), { recursive: true });
|
|
22533
22600
|
const token = randomBytes(32).toString("hex");
|
|
22534
|
-
await
|
|
22601
|
+
await fs2.writeFile(path, token + "\n", { mode: 384 });
|
|
22535
22602
|
try {
|
|
22536
|
-
await
|
|
22603
|
+
await fs2.chmod(path, 384);
|
|
22537
22604
|
} catch {
|
|
22538
22605
|
}
|
|
22539
22606
|
return token;
|
|
@@ -23142,10 +23209,10 @@ function makeFileMirrorSource(deps) {
|
|
|
23142
23209
|
};
|
|
23143
23210
|
const readText = async (path) => {
|
|
23144
23211
|
if (!path) return "";
|
|
23145
|
-
const
|
|
23146
|
-
if (!
|
|
23212
|
+
const fs15 = await deps.files();
|
|
23213
|
+
if (!fs15) return "";
|
|
23147
23214
|
try {
|
|
23148
|
-
return await
|
|
23215
|
+
return await fs15.read(path);
|
|
23149
23216
|
} catch {
|
|
23150
23217
|
return "";
|
|
23151
23218
|
}
|
|
@@ -23173,28 +23240,28 @@ function makeFileMirrorSource(deps) {
|
|
|
23173
23240
|
}
|
|
23174
23241
|
|
|
23175
23242
|
// src/auth/store.ts
|
|
23176
|
-
import { promises as
|
|
23243
|
+
import { promises as fs3 } from "node:fs";
|
|
23177
23244
|
import { homedir as homedir2 } from "node:os";
|
|
23178
|
-
import { join as
|
|
23245
|
+
import { join as join3 } from "node:path";
|
|
23179
23246
|
var TOKEN_ENV = "CORENEL_TOKEN";
|
|
23180
23247
|
function authDir(stateDir) {
|
|
23181
|
-
return stateDir ??
|
|
23248
|
+
return stateDir ?? join3(homedir2(), stateDirName());
|
|
23182
23249
|
}
|
|
23183
23250
|
function credentialPath(stateDir) {
|
|
23184
|
-
return
|
|
23251
|
+
return join3(authDir(stateDir), "auth.json");
|
|
23185
23252
|
}
|
|
23186
23253
|
async function saveCredential(cred, stateDir) {
|
|
23187
23254
|
const path = credentialPath(stateDir);
|
|
23188
|
-
await
|
|
23189
|
-
await
|
|
23255
|
+
await fs3.mkdir(authDir(stateDir), { recursive: true });
|
|
23256
|
+
await fs3.writeFile(path, JSON.stringify(cred, null, 2) + "\n", { mode: 384 });
|
|
23190
23257
|
try {
|
|
23191
|
-
await
|
|
23258
|
+
await fs3.chmod(path, 384);
|
|
23192
23259
|
} catch {
|
|
23193
23260
|
}
|
|
23194
23261
|
}
|
|
23195
23262
|
async function loadCredential(stateDir) {
|
|
23196
23263
|
try {
|
|
23197
|
-
const raw = await
|
|
23264
|
+
const raw = await fs3.readFile(credentialPath(stateDir), "utf8");
|
|
23198
23265
|
const parsed = JSON.parse(raw);
|
|
23199
23266
|
if (typeof parsed !== "object" || parsed === null) return null;
|
|
23200
23267
|
const c2 = parsed;
|
|
@@ -23213,7 +23280,7 @@ async function loadCredential(stateDir) {
|
|
|
23213
23280
|
}
|
|
23214
23281
|
}
|
|
23215
23282
|
async function clearCredential(stateDir) {
|
|
23216
|
-
await
|
|
23283
|
+
await fs3.rm(credentialPath(stateDir), { force: true });
|
|
23217
23284
|
}
|
|
23218
23285
|
async function resolveCredential(env = process.env, stateDir, now2 = Date.now) {
|
|
23219
23286
|
const fromEnv = env[TOKEN_ENV]?.trim();
|
|
@@ -23225,6 +23292,15 @@ async function resolveCredential(env = process.env, stateDir, now2 = Date.now) {
|
|
|
23225
23292
|
}
|
|
23226
23293
|
|
|
23227
23294
|
// src/controlPlane.ts
|
|
23295
|
+
function describeBackend(url, mirrors, cred) {
|
|
23296
|
+
if (!mirrors) return { url, mirrors: false };
|
|
23297
|
+
return {
|
|
23298
|
+
url,
|
|
23299
|
+
mirrors: true,
|
|
23300
|
+
signedIn: !!cred.token,
|
|
23301
|
+
...cred.expired ? { expired: true } : {}
|
|
23302
|
+
};
|
|
23303
|
+
}
|
|
23228
23304
|
function makeControlPlane(deps) {
|
|
23229
23305
|
if (!deps || !deps.baseUrl) return null;
|
|
23230
23306
|
const log = deps.log ?? (() => void 0);
|
|
@@ -23265,7 +23341,7 @@ function makeControlPlane(deps) {
|
|
|
23265
23341
|
}
|
|
23266
23342
|
|
|
23267
23343
|
// src/tools.ts
|
|
23268
|
-
import { promises as
|
|
23344
|
+
import { promises as fs4 } from "node:fs";
|
|
23269
23345
|
import { spawn } from "node:child_process";
|
|
23270
23346
|
import { resolve, relative, isAbsolute, sep, dirname } from "node:path";
|
|
23271
23347
|
function withinRoot(root, abs) {
|
|
@@ -23282,11 +23358,11 @@ function confine(roots, rootArg, p2) {
|
|
|
23282
23358
|
return abs;
|
|
23283
23359
|
}
|
|
23284
23360
|
async function assertRealWithinRoot(roots, abs) {
|
|
23285
|
-
const realRoots = await Promise.all(roots.map((r2) =>
|
|
23361
|
+
const realRoots = await Promise.all(roots.map((r2) => fs4.realpath(r2)));
|
|
23286
23362
|
let probe = abs;
|
|
23287
23363
|
for (; ; ) {
|
|
23288
23364
|
try {
|
|
23289
|
-
const real = await
|
|
23365
|
+
const real = await fs4.realpath(probe);
|
|
23290
23366
|
if (!realRoots.some((rr2) => withinRoot(rr2, real))) throw new Error(`path escapes the sidecar roots via symlink: ${abs}`);
|
|
23291
23367
|
return;
|
|
23292
23368
|
} catch (e2) {
|
|
@@ -23314,11 +23390,11 @@ function nodeFsTools(roots) {
|
|
|
23314
23390
|
async run(args) {
|
|
23315
23391
|
const abs = confine(roots, args.root, String(args.path ?? ""));
|
|
23316
23392
|
await assertRealWithinRoot(roots, abs);
|
|
23317
|
-
const st2 = await
|
|
23393
|
+
const st2 = await fs4.stat(abs);
|
|
23318
23394
|
if (st2.size > MAX_READ_BYTES) {
|
|
23319
23395
|
throw new Error(`file too large to read (${st2.size} bytes; max ${MAX_READ_BYTES})`);
|
|
23320
23396
|
}
|
|
23321
|
-
return await
|
|
23397
|
+
return await fs4.readFile(abs, "utf8");
|
|
23322
23398
|
}
|
|
23323
23399
|
},
|
|
23324
23400
|
{
|
|
@@ -23338,9 +23414,9 @@ function nodeFsTools(roots) {
|
|
|
23338
23414
|
mutates: true,
|
|
23339
23415
|
async run(args) {
|
|
23340
23416
|
const abs = confine(roots, args.root, String(args.path ?? ""));
|
|
23341
|
-
await
|
|
23417
|
+
await fs4.mkdir(dirname(abs), { recursive: true });
|
|
23342
23418
|
await assertRealWithinRoot(roots, abs);
|
|
23343
|
-
await
|
|
23419
|
+
await fs4.writeFile(abs, String(args.content ?? ""), "utf8");
|
|
23344
23420
|
return `wrote ${args.path}`;
|
|
23345
23421
|
}
|
|
23346
23422
|
},
|
|
@@ -23357,11 +23433,11 @@ function nodeFsTools(roots) {
|
|
|
23357
23433
|
async run(args) {
|
|
23358
23434
|
const abs = confine(roots, args.root, String(args.path ?? ""));
|
|
23359
23435
|
await assertRealWithinRoot(roots, abs);
|
|
23360
|
-
const st2 = await
|
|
23436
|
+
const st2 = await fs4.stat(abs);
|
|
23361
23437
|
if (st2.size > MAX_READ_BYTES) {
|
|
23362
23438
|
throw new Error(`file too large to read (${st2.size} bytes; max ${MAX_READ_BYTES})`);
|
|
23363
23439
|
}
|
|
23364
|
-
return (await
|
|
23440
|
+
return (await fs4.readFile(abs)).toString("base64");
|
|
23365
23441
|
}
|
|
23366
23442
|
},
|
|
23367
23443
|
{
|
|
@@ -23376,7 +23452,7 @@ function nodeFsTools(roots) {
|
|
|
23376
23452
|
async run(args) {
|
|
23377
23453
|
const abs = confine(roots, args.root, String(args.path ?? "."));
|
|
23378
23454
|
await assertRealWithinRoot(roots, abs);
|
|
23379
|
-
const entries = await
|
|
23455
|
+
const entries = await fs4.readdir(abs, { withFileTypes: true });
|
|
23380
23456
|
return entries.map((e2) => e2.isDirectory() ? `${e2.name}/` : e2.name).join("\n");
|
|
23381
23457
|
}
|
|
23382
23458
|
},
|
|
@@ -23395,7 +23471,7 @@ function nodeFsTools(roots) {
|
|
|
23395
23471
|
const abs = confine(roots, args.root, String(args.path ?? ""));
|
|
23396
23472
|
await assertRealWithinRoot(roots, abs);
|
|
23397
23473
|
if (roots.some((r2) => resolve(r2) === abs)) throw new Error("refusing to delete a sidecar root itself");
|
|
23398
|
-
await
|
|
23474
|
+
await fs4.rm(abs, { recursive: true, force: true });
|
|
23399
23475
|
return `deleted ${args.path}`;
|
|
23400
23476
|
}
|
|
23401
23477
|
}
|
|
@@ -23458,9 +23534,9 @@ function runShell(command, cwd, timeout) {
|
|
|
23458
23534
|
clearTimeout(timer);
|
|
23459
23535
|
resolveOut(`Error: failed to start command: ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
23460
23536
|
});
|
|
23461
|
-
child.on("close", (
|
|
23537
|
+
child.on("close", (code2, signal) => {
|
|
23462
23538
|
clearTimeout(timer);
|
|
23463
|
-
const status = timedOut ? `timed out after ${timeout}ms (killed)` : `exit ${
|
|
23539
|
+
const status = timedOut ? `timed out after ${timeout}ms (killed)` : `exit ${code2 ?? "null"}${signal ? ` (signal ${signal})` : ""}`;
|
|
23464
23540
|
const body = out.trim() || "(no output)";
|
|
23465
23541
|
resolveOut(`${status}
|
|
23466
23542
|
${truncated ? "[output truncated]\n" : ""}${body}`);
|
|
@@ -23534,20 +23610,20 @@ function createNodeProcs(opts) {
|
|
|
23534
23610
|
opts.log?.(`proc ${info.procId} spawn: ${line}`);
|
|
23535
23611
|
child.stdout.on("data", (b2) => push2(entry, "out", b2.toString("utf8")));
|
|
23536
23612
|
child.stderr.on("data", (b2) => push2(entry, "err", b2.toString("utf8")));
|
|
23537
|
-
const settle = (
|
|
23613
|
+
const settle = (code2, signal, failure) => {
|
|
23538
23614
|
if (!entry.info.running) return;
|
|
23539
23615
|
if (failure) push2(entry, "err", failure);
|
|
23540
23616
|
entry.info.running = false;
|
|
23541
23617
|
entry.info.endedAt = Date.now();
|
|
23542
|
-
entry.info.exitCode =
|
|
23618
|
+
entry.info.exitCode = code2;
|
|
23543
23619
|
if (signal) entry.info.signal = signal;
|
|
23544
23620
|
entry.child = null;
|
|
23545
|
-
opts.log?.(`proc ${entry.info.procId} exit ${
|
|
23621
|
+
opts.log?.(`proc ${entry.info.procId} exit ${code2 ?? "null"}${signal ? ` (${signal})` : ""}`);
|
|
23546
23622
|
emit({ procId: entry.info.procId, kind: "exit", info: { ...entry.info } });
|
|
23547
23623
|
evictExited();
|
|
23548
23624
|
};
|
|
23549
23625
|
child.on("error", (err) => settle(null, null, `failed to start: ${err instanceof Error ? err.message : String(err)}`));
|
|
23550
|
-
child.on("close", (
|
|
23626
|
+
child.on("close", (code2, signal) => settle(code2, signal));
|
|
23551
23627
|
return { ...info };
|
|
23552
23628
|
},
|
|
23553
23629
|
list() {
|
|
@@ -23623,22 +23699,22 @@ function isNotFound(e2) {
|
|
|
23623
23699
|
const msg = String(e2?.message ?? e2);
|
|
23624
23700
|
return name === "NotFoundError" || /not\s*found|no such|enoent/i.test(msg);
|
|
23625
23701
|
}
|
|
23626
|
-
function fileServicePackageStore(
|
|
23627
|
-
const remove = (path) =>
|
|
23702
|
+
function fileServicePackageStore(fs15) {
|
|
23703
|
+
const remove = (path) => fs15.remove(path).catch((e2) => {
|
|
23628
23704
|
if (!isNotFound(e2)) throw e2;
|
|
23629
23705
|
});
|
|
23630
23706
|
return {
|
|
23631
|
-
writeFile: (path, content) =>
|
|
23707
|
+
writeFile: (path, content) => fs15.write(path, content),
|
|
23632
23708
|
// Present only when the backend supports binary — core feature-detects it,
|
|
23633
23709
|
// and advertising a method that throws is worse than not advertising it.
|
|
23634
|
-
...
|
|
23710
|
+
...fs15.writeBytes ? { writeBytes: (path, bytes) => fs15.writeBytes(path, bytes) } : {},
|
|
23635
23711
|
removeDir: remove,
|
|
23636
23712
|
removeFile: remove,
|
|
23637
|
-
readFile: (path) =>
|
|
23638
|
-
...
|
|
23713
|
+
readFile: (path) => fs15.read(path).catch(() => null),
|
|
23714
|
+
...fs15.readBytes ? { readBytes: (path) => fs15.readBytes(path) } : {},
|
|
23639
23715
|
readdir: async (path) => {
|
|
23640
23716
|
try {
|
|
23641
|
-
const nodes =
|
|
23717
|
+
const nodes = fs15.listDir ? await fs15.listDir(path) : await fs15.list(path);
|
|
23642
23718
|
return nodes.map((n2) => n2.name);
|
|
23643
23719
|
} catch {
|
|
23644
23720
|
return [];
|
|
@@ -23740,9 +23816,9 @@ var SessionPackages = class {
|
|
|
23740
23816
|
return !!this.deps.installer;
|
|
23741
23817
|
}
|
|
23742
23818
|
async filesOrThrow(sessionId) {
|
|
23743
|
-
const
|
|
23744
|
-
if (!
|
|
23745
|
-
return
|
|
23819
|
+
const fs15 = await this.deps.filesFor(sessionId);
|
|
23820
|
+
if (!fs15) throw new Error(`no such session: ${sessionId}`);
|
|
23821
|
+
return fs15;
|
|
23746
23822
|
}
|
|
23747
23823
|
installerOrThrow() {
|
|
23748
23824
|
const i2 = this.deps.installer;
|
|
@@ -23750,13 +23826,13 @@ var SessionPackages = class {
|
|
|
23750
23826
|
return i2;
|
|
23751
23827
|
}
|
|
23752
23828
|
async readLock(sessionId) {
|
|
23753
|
-
const
|
|
23754
|
-
const text = await
|
|
23829
|
+
const fs15 = await this.filesOrThrow(sessionId);
|
|
23830
|
+
const text = await fs15.read(LOCKFILE_PATH).catch(() => null);
|
|
23755
23831
|
return (text != null ? parseLock(text) : null) ?? emptyLock();
|
|
23756
23832
|
}
|
|
23757
23833
|
async writeLock(sessionId, lock) {
|
|
23758
|
-
const
|
|
23759
|
-
await
|
|
23834
|
+
const fs15 = await this.filesOrThrow(sessionId);
|
|
23835
|
+
await fs15.write(LOCKFILE_PATH, serializeLock(lock));
|
|
23760
23836
|
}
|
|
23761
23837
|
/**
|
|
23762
23838
|
* Install one package into the session and pin what resolved.
|
|
@@ -23767,11 +23843,11 @@ var SessionPackages = class {
|
|
|
23767
23843
|
*/
|
|
23768
23844
|
async install(sessionId, ref, type) {
|
|
23769
23845
|
const installer = this.installerOrThrow();
|
|
23770
|
-
const
|
|
23846
|
+
const fs15 = await this.filesOrThrow(sessionId);
|
|
23771
23847
|
const installed = await installer.install({
|
|
23772
23848
|
ref,
|
|
23773
23849
|
root: "",
|
|
23774
|
-
store: fileServicePackageStore(
|
|
23850
|
+
store: fileServicePackageStore(fs15),
|
|
23775
23851
|
...type ? { type } : {}
|
|
23776
23852
|
});
|
|
23777
23853
|
try {
|
|
@@ -23789,8 +23865,8 @@ var SessionPackages = class {
|
|
|
23789
23865
|
}
|
|
23790
23866
|
async uninstall(sessionId, ref) {
|
|
23791
23867
|
const installer = this.installerOrThrow();
|
|
23792
|
-
const
|
|
23793
|
-
await installer.uninstall({ ref, root: "", store: fileServicePackageStore(
|
|
23868
|
+
const fs15 = await this.filesOrThrow(sessionId);
|
|
23869
|
+
await installer.uninstall({ ref, root: "", store: fileServicePackageStore(fs15) });
|
|
23794
23870
|
const name = ref.replace(/@[^@/]*$/, "") || ref;
|
|
23795
23871
|
try {
|
|
23796
23872
|
await this.writeLock(sessionId, withoutPackage(await this.readLock(sessionId), name));
|
|
@@ -23807,9 +23883,9 @@ var SessionPackages = class {
|
|
|
23807
23883
|
*/
|
|
23808
23884
|
async installFromLock(sessionId) {
|
|
23809
23885
|
const installer = this.installerOrThrow();
|
|
23810
|
-
const
|
|
23886
|
+
const fs15 = await this.filesOrThrow(sessionId);
|
|
23811
23887
|
const lock = await this.readLock(sessionId);
|
|
23812
|
-
const store = fileServicePackageStore(
|
|
23888
|
+
const store = fileServicePackageStore(fs15);
|
|
23813
23889
|
const done = [];
|
|
23814
23890
|
for (const pin of lock.packages) {
|
|
23815
23891
|
done.push(await installer.install({ ref: `${pin.name}@${pin.version}`, root: "", store }));
|
|
@@ -24068,8 +24144,8 @@ var SessionManager = class {
|
|
|
24068
24144
|
import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve3 } from "node:path";
|
|
24069
24145
|
|
|
24070
24146
|
// ../tools-node/fileService.ts
|
|
24071
|
-
import { promises as
|
|
24072
|
-
import { join as
|
|
24147
|
+
import { promises as fs5 } from "node:fs";
|
|
24148
|
+
import { join as join4, resolve as resolve2, relative as relative2, dirname as dirname2, sep as sep2, basename, isAbsolute as isAbsolute2 } from "node:path";
|
|
24073
24149
|
var SKIP = /* @__PURE__ */ new Set([
|
|
24074
24150
|
"node_modules",
|
|
24075
24151
|
".git",
|
|
@@ -24092,7 +24168,7 @@ var COVERAGE_REPORT_MARKERS = /* @__PURE__ */ new Set([
|
|
|
24092
24168
|
]);
|
|
24093
24169
|
async function looksLikeGeneratedCoverage(absDir) {
|
|
24094
24170
|
try {
|
|
24095
|
-
const names = await
|
|
24171
|
+
const names = await fs5.readdir(absDir);
|
|
24096
24172
|
return names.some((n2) => COVERAGE_REPORT_MARKERS.has(n2));
|
|
24097
24173
|
} catch {
|
|
24098
24174
|
return false;
|
|
@@ -24135,11 +24211,11 @@ var NodeFileService = class {
|
|
|
24135
24211
|
* root pointing outside it. Resolve the real path (or nearest existing ancestor,
|
|
24136
24212
|
* for a not-yet-created write target) and assert it stays within the real root. */
|
|
24137
24213
|
async assertReal(a2) {
|
|
24138
|
-
const realRoot = await
|
|
24214
|
+
const realRoot = await fs5.realpath(this.root);
|
|
24139
24215
|
let probe = a2;
|
|
24140
24216
|
for (; ; ) {
|
|
24141
24217
|
try {
|
|
24142
|
-
const real = await
|
|
24218
|
+
const real = await fs5.realpath(probe);
|
|
24143
24219
|
const rel = relative2(realRoot, real);
|
|
24144
24220
|
if (escapesRoot(rel)) {
|
|
24145
24221
|
throw new Error(`path escapes the workspace root via symlink: ${a2}`);
|
|
@@ -24163,21 +24239,21 @@ var NodeFileService = class {
|
|
|
24163
24239
|
const walk = async (absDir) => {
|
|
24164
24240
|
let entries;
|
|
24165
24241
|
try {
|
|
24166
|
-
entries = await
|
|
24242
|
+
entries = await fs5.readdir(absDir, { withFileTypes: true });
|
|
24167
24243
|
} catch {
|
|
24168
24244
|
return [];
|
|
24169
24245
|
}
|
|
24170
24246
|
const out = [];
|
|
24171
24247
|
for (const e2 of entries.sort((a2, b2) => a2.name.localeCompare(b2.name))) {
|
|
24172
24248
|
if (SKIP.has(e2.name)) continue;
|
|
24173
|
-
const absChild =
|
|
24249
|
+
const absChild = join4(absDir, e2.name);
|
|
24174
24250
|
const path = normalizeFilePath(relative2(this.root, absChild));
|
|
24175
24251
|
if (e2.isDirectory() && SKIP_PATHS.has(path)) continue;
|
|
24176
24252
|
if (e2.isDirectory() && e2.name === "coverage" && await looksLikeGeneratedCoverage(absChild)) continue;
|
|
24177
24253
|
if (e2.isDirectory()) {
|
|
24178
24254
|
out.push({ name: e2.name, path, kind: "directory", children: await walk(absChild) });
|
|
24179
24255
|
} else {
|
|
24180
|
-
const st2 = await
|
|
24256
|
+
const st2 = await fs5.stat(absChild).catch(() => null);
|
|
24181
24257
|
out.push({ name: e2.name, path, kind: "file", size: st2?.size, ext: extOf(e2.name) });
|
|
24182
24258
|
}
|
|
24183
24259
|
}
|
|
@@ -24186,11 +24262,11 @@ var NodeFileService = class {
|
|
|
24186
24262
|
return walk(this.abs(dir));
|
|
24187
24263
|
}
|
|
24188
24264
|
async read(path) {
|
|
24189
|
-
return
|
|
24265
|
+
return fs5.readFile(await this.realAbs(path), "utf8");
|
|
24190
24266
|
}
|
|
24191
24267
|
async stat(path) {
|
|
24192
24268
|
try {
|
|
24193
|
-
const st2 = await
|
|
24269
|
+
const st2 = await fs5.stat(this.abs(path));
|
|
24194
24270
|
return { path: normalizeFilePath(path), kind: st2.isDirectory() ? "directory" : "file", size: st2.size, ext: extOf(path) };
|
|
24195
24271
|
} catch {
|
|
24196
24272
|
return null;
|
|
@@ -24198,38 +24274,38 @@ var NodeFileService = class {
|
|
|
24198
24274
|
}
|
|
24199
24275
|
async write(path, content) {
|
|
24200
24276
|
const a2 = this.abs(path);
|
|
24201
|
-
await
|
|
24277
|
+
await fs5.mkdir(dirname2(a2), { recursive: true });
|
|
24202
24278
|
await this.assertReal(a2);
|
|
24203
|
-
await
|
|
24279
|
+
await fs5.writeFile(a2, content, "utf8");
|
|
24204
24280
|
}
|
|
24205
24281
|
async writeBytes(path, bytes) {
|
|
24206
24282
|
const a2 = this.abs(path);
|
|
24207
|
-
await
|
|
24283
|
+
await fs5.mkdir(dirname2(a2), { recursive: true });
|
|
24208
24284
|
await this.assertReal(a2);
|
|
24209
|
-
await
|
|
24285
|
+
await fs5.writeFile(a2, bytes);
|
|
24210
24286
|
}
|
|
24211
24287
|
async create(path, content = "") {
|
|
24212
24288
|
const a2 = this.abs(path);
|
|
24213
|
-
await
|
|
24289
|
+
await fs5.mkdir(dirname2(a2), { recursive: true });
|
|
24214
24290
|
await this.assertReal(a2);
|
|
24215
24291
|
try {
|
|
24216
|
-
await
|
|
24292
|
+
await fs5.writeFile(a2, content, { flag: "wx" });
|
|
24217
24293
|
} catch (e2) {
|
|
24218
24294
|
if (e2.code === "EEXIST") throw new Error(`already exists: ${path}`);
|
|
24219
24295
|
throw e2;
|
|
24220
24296
|
}
|
|
24221
24297
|
}
|
|
24222
24298
|
async remove(path) {
|
|
24223
|
-
await
|
|
24299
|
+
await fs5.rm(await this.realAbs(path), { recursive: true, force: true });
|
|
24224
24300
|
}
|
|
24225
24301
|
async rename(from, to) {
|
|
24226
24302
|
const t2 = this.abs(to);
|
|
24227
|
-
await
|
|
24303
|
+
await fs5.mkdir(dirname2(t2), { recursive: true });
|
|
24228
24304
|
await this.assertReal(t2);
|
|
24229
|
-
await
|
|
24305
|
+
await fs5.rename(await this.realAbs(from), t2);
|
|
24230
24306
|
}
|
|
24231
24307
|
async mkdir(path) {
|
|
24232
|
-
await
|
|
24308
|
+
await fs5.mkdir(this.abs(path), { recursive: true });
|
|
24233
24309
|
}
|
|
24234
24310
|
async search(query) {
|
|
24235
24311
|
const q2 = query.toLowerCase();
|
|
@@ -24429,13 +24505,13 @@ function makeMsbStreamer(bin, wrap) {
|
|
|
24429
24505
|
const done = new Promise((r2) => {
|
|
24430
24506
|
resolveDone = r2;
|
|
24431
24507
|
});
|
|
24432
|
-
const settle = (
|
|
24508
|
+
const settle = (code2) => {
|
|
24433
24509
|
if (settled) return;
|
|
24434
24510
|
settled = true;
|
|
24435
24511
|
for (const l2 of out.flush()) onLine(l2, "out");
|
|
24436
24512
|
for (const l2 of err.flush()) onLine(l2, "err");
|
|
24437
24513
|
clearTimeout(timer);
|
|
24438
|
-
resolveDone({ code, stdout: "", stderr: stderrText });
|
|
24514
|
+
resolveDone({ code: code2, stdout: "", stderr: stderrText });
|
|
24439
24515
|
};
|
|
24440
24516
|
const timer = opts?.timeoutMs === void 0 ? void 0 : setTimeout(() => {
|
|
24441
24517
|
child.kill("SIGKILL");
|
|
@@ -24455,7 +24531,7 @@ function makeMsbStreamer(bin, wrap) {
|
|
|
24455
24531
|
${e2 instanceof Error ? e2.message : String(e2)}`;
|
|
24456
24532
|
settle(-1);
|
|
24457
24533
|
});
|
|
24458
|
-
child.on("close", (
|
|
24534
|
+
child.on("close", (code2) => settle(code2 ?? -1));
|
|
24459
24535
|
return {
|
|
24460
24536
|
stop: () => {
|
|
24461
24537
|
if (!settled) child.kill("SIGTERM");
|
|
@@ -24498,9 +24574,9 @@ msb timed out after ${opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS2}ms` });
|
|
|
24498
24574
|
settle({ code: -1, stdout, stderr: `${stderr}
|
|
24499
24575
|
${e2 instanceof Error ? e2.message : String(e2)}` });
|
|
24500
24576
|
});
|
|
24501
|
-
child.on("close", (
|
|
24577
|
+
child.on("close", (code2) => {
|
|
24502
24578
|
clearTimeout(timer);
|
|
24503
|
-
settle({ code:
|
|
24579
|
+
settle({ code: code2 ?? -1, stdout, stderr });
|
|
24504
24580
|
});
|
|
24505
24581
|
});
|
|
24506
24582
|
}
|
|
@@ -24675,9 +24751,9 @@ function makeMicrovmBackend(deps) {
|
|
|
24675
24751
|
if (!live.length) return;
|
|
24676
24752
|
const r2 = await deps.run(listArgs());
|
|
24677
24753
|
if (r2.code !== 0) return;
|
|
24678
|
-
const
|
|
24754
|
+
const present2 = new Set(parseSandboxList(r2.stdout).map((row) => row.name));
|
|
24679
24755
|
for (const s2 of live) {
|
|
24680
|
-
if (
|
|
24756
|
+
if (present2.has(sandboxName(s2.id))) continue;
|
|
24681
24757
|
if (at2 - s2.createdAt < RECONCILE_MS) continue;
|
|
24682
24758
|
sessions.delete(s2.id);
|
|
24683
24759
|
deps.log?.(`session ${s2.id} ended outside this daemon (sandbox ${sandboxName(s2.id)} is gone) \u2014 dropped`);
|
|
@@ -25148,11 +25224,11 @@ function makeGuestRunner(deps) {
|
|
|
25148
25224
|
onEvent(e2);
|
|
25149
25225
|
}
|
|
25150
25226
|
};
|
|
25151
|
-
const finish = (
|
|
25227
|
+
const finish = (code2, stderr) => {
|
|
25152
25228
|
if (skipped) deps.log?.(`session ${sessionId}: ${skipped} unparseable line(s) from the guest were skipped`);
|
|
25153
|
-
if (
|
|
25229
|
+
if (code2 !== 0) {
|
|
25154
25230
|
const why = stderr.trim().split(NEWLINE).slice(-3).join("; ");
|
|
25155
|
-
return { text: finalText(seen), stopReason: `error: guest exit ${
|
|
25231
|
+
return { text: finalText(seen), stopReason: `error: guest exit ${code2}${why ? ` \u2014 ${why}` : ""}` };
|
|
25156
25232
|
}
|
|
25157
25233
|
return { text: finalText(seen), stopReason: "done" };
|
|
25158
25234
|
};
|
|
@@ -25291,18 +25367,21 @@ function makeSessionsPort(deps) {
|
|
|
25291
25367
|
}
|
|
25292
25368
|
|
|
25293
25369
|
// ../tools-node/auth.ts
|
|
25294
|
-
import { promises as
|
|
25370
|
+
import { promises as fs6 } from "node:fs";
|
|
25295
25371
|
import { homedir as homedir3 } from "node:os";
|
|
25296
|
-
import { join as
|
|
25297
|
-
function corenelDir2() {
|
|
25298
|
-
return
|
|
25372
|
+
import { join as join5 } from "node:path";
|
|
25373
|
+
function corenelDir2(base = homedir3()) {
|
|
25374
|
+
return join5(base, stateDirName());
|
|
25375
|
+
}
|
|
25376
|
+
function sidecarDir(base = homedir3()) {
|
|
25377
|
+
return join5(corenelDir2(base), "sidecar");
|
|
25299
25378
|
}
|
|
25300
|
-
function authTokenPath() {
|
|
25301
|
-
return
|
|
25379
|
+
function authTokenPath(base = homedir3()) {
|
|
25380
|
+
return join5(corenelDir2(base), "token");
|
|
25302
25381
|
}
|
|
25303
25382
|
async function readAuthToken() {
|
|
25304
25383
|
try {
|
|
25305
|
-
const t2 = (await
|
|
25384
|
+
const t2 = (await fs6.readFile(authTokenPath(), "utf8")).trim();
|
|
25306
25385
|
return t2 || null;
|
|
25307
25386
|
} catch {
|
|
25308
25387
|
return null;
|
|
@@ -27067,12 +27146,12 @@ var TemplateProcessingStage = class {
|
|
|
27067
27146
|
*/
|
|
27068
27147
|
async loadPackageResource(context, packagePath, resourcePath, prefix) {
|
|
27069
27148
|
try {
|
|
27070
|
-
const
|
|
27149
|
+
const fs15 = context.fileSystem;
|
|
27071
27150
|
const possibleExtensions = ["", ".prmd", ".md", ".txt"];
|
|
27072
27151
|
for (const ext of possibleExtensions) {
|
|
27073
27152
|
const filePath = resolvePackageFile(packagePath, resourcePath + ext);
|
|
27074
|
-
if (await
|
|
27075
|
-
const contentData = await
|
|
27153
|
+
if (await fs15.exists(filePath)) {
|
|
27154
|
+
const contentData = await fs15.readFile(filePath);
|
|
27076
27155
|
if (isPrompdFile(filePath)) {
|
|
27077
27156
|
const parser2 = new PrompdParser();
|
|
27078
27157
|
try {
|
|
@@ -27106,16 +27185,16 @@ var TemplateProcessingStage = class {
|
|
|
27106
27185
|
return content;
|
|
27107
27186
|
}
|
|
27108
27187
|
try {
|
|
27109
|
-
const
|
|
27188
|
+
const fs15 = context.fileSystem;
|
|
27110
27189
|
const parser2 = new PrompdParser();
|
|
27111
27190
|
let parentFile = null;
|
|
27112
|
-
if (isPrompdFile(parentPath) && await
|
|
27191
|
+
if (isPrompdFile(parentPath) && await fs15.exists(parentPath)) {
|
|
27113
27192
|
parentFile = parentPath;
|
|
27114
|
-
} else if (await
|
|
27115
|
-
const files = (await
|
|
27193
|
+
} else if (await fs15.exists(parentPath) && await fs15.isDirectory(parentPath)) {
|
|
27194
|
+
const files = (await fs15.readdir(parentPath)).filter((f2) => isPrompdFile(f2));
|
|
27116
27195
|
if (files.length > 0) {
|
|
27117
27196
|
const mainFile = files.find((f2) => f2 === "main.prmd" || f2 === "main.md");
|
|
27118
|
-
parentFile =
|
|
27197
|
+
parentFile = fs15.join(parentPath, mainFile || files[0]);
|
|
27119
27198
|
}
|
|
27120
27199
|
}
|
|
27121
27200
|
if (!parentFile) {
|
|
@@ -27129,14 +27208,14 @@ var TemplateProcessingStage = class {
|
|
|
27129
27208
|
});
|
|
27130
27209
|
return content;
|
|
27131
27210
|
}
|
|
27132
|
-
const parentFileContent = await
|
|
27211
|
+
const parentFileContent = await fs15.readFile(parentFile);
|
|
27133
27212
|
const parentData = parser2.parseContent(parentFileContent);
|
|
27134
|
-
await this.validateParentFileReferences(context, parentData.metadata,
|
|
27213
|
+
await this.validateParentFileReferences(context, parentData.metadata, fs15, parentFile);
|
|
27135
27214
|
if (context.hasErrors()) {
|
|
27136
27215
|
return content;
|
|
27137
27216
|
}
|
|
27138
27217
|
if (parentData.content) {
|
|
27139
|
-
const parentDir =
|
|
27218
|
+
const parentDir = fs15.dirname(parentFile);
|
|
27140
27219
|
parentData.content = this.resolveIncludePaths(parentData.content, parentDir);
|
|
27141
27220
|
}
|
|
27142
27221
|
const overrides = context.metadata?.override || {};
|
|
@@ -27687,10 +27766,10 @@ ${content}` : parentData.content;
|
|
|
27687
27766
|
* Called during inheritance processing because AssetExtractionStage only runs on
|
|
27688
27767
|
* the child file's metadata — the parent is only parsed, never fully compiled.
|
|
27689
27768
|
*/
|
|
27690
|
-
async validateParentFileReferences(context, metadata,
|
|
27769
|
+
async validateParentFileReferences(context, metadata, fs15, parentFile) {
|
|
27691
27770
|
if (!metadata) return;
|
|
27692
27771
|
const metadataAsRecord = metadata;
|
|
27693
|
-
const parentDir =
|
|
27772
|
+
const parentDir = fs15.dirname(parentFile);
|
|
27694
27773
|
const fileFields = ["system", "task", "user", "assistant", "response", "output", "context"];
|
|
27695
27774
|
for (const field of fileFields) {
|
|
27696
27775
|
const fieldValue = metadataAsRecord[field];
|
|
@@ -27698,8 +27777,8 @@ ${content}` : parentData.content;
|
|
|
27698
27777
|
const refs = Array.isArray(fieldValue) ? fieldValue.filter((v2) => typeof v2 === "string") : typeof fieldValue === "string" ? [fieldValue] : [];
|
|
27699
27778
|
for (const ref of refs) {
|
|
27700
27779
|
if (!ref.startsWith("./") && !ref.startsWith("../")) continue;
|
|
27701
|
-
const resolvedPath =
|
|
27702
|
-
const exists = await Promise.resolve(
|
|
27780
|
+
const resolvedPath = fs15.resolve(parentDir, ref);
|
|
27781
|
+
const exists = await Promise.resolve(fs15.exists(resolvedPath));
|
|
27703
27782
|
if (!exists) {
|
|
27704
27783
|
const location = context.findLocation(/inherits:/);
|
|
27705
27784
|
context.addDiagnostic({
|
|
@@ -28076,6 +28155,90 @@ var PrompdCompiler = class {
|
|
|
28076
28155
|
var parser = new PrompdParser();
|
|
28077
28156
|
var compiler = new PrompdCompiler();
|
|
28078
28157
|
|
|
28158
|
+
// ../tools-node/migrateHome.ts
|
|
28159
|
+
import { promises as fs7 } from "node:fs";
|
|
28160
|
+
import { homedir as homedir4 } from "node:os";
|
|
28161
|
+
import { join as join6, dirname as dirname3 } from "node:path";
|
|
28162
|
+
var SIDECAR_FILES = [
|
|
28163
|
+
"daemon.id",
|
|
28164
|
+
"daemon.token",
|
|
28165
|
+
"hosts.json",
|
|
28166
|
+
"sidecar.crt",
|
|
28167
|
+
"sidecar.key",
|
|
28168
|
+
"sidecar.sans.json"
|
|
28169
|
+
];
|
|
28170
|
+
var SIDECAR_DIRS = ["hosts"];
|
|
28171
|
+
async function isDir(p2) {
|
|
28172
|
+
return fs7.stat(p2).then((s2) => s2.isDirectory()).catch(() => false);
|
|
28173
|
+
}
|
|
28174
|
+
async function present(p2) {
|
|
28175
|
+
return fs7.stat(p2).then(() => true).catch(() => false);
|
|
28176
|
+
}
|
|
28177
|
+
function code(e2) {
|
|
28178
|
+
return e2?.code;
|
|
28179
|
+
}
|
|
28180
|
+
async function moveEntry(from, to, report) {
|
|
28181
|
+
if (!await present(from)) return;
|
|
28182
|
+
if (await present(to)) {
|
|
28183
|
+
report.skipped.push({ path: to, reason: "destination already exists" });
|
|
28184
|
+
return;
|
|
28185
|
+
}
|
|
28186
|
+
await fs7.mkdir(dirname3(to), { recursive: true });
|
|
28187
|
+
try {
|
|
28188
|
+
await fs7.rename(from, to);
|
|
28189
|
+
report.moved.push(to);
|
|
28190
|
+
} catch (e2) {
|
|
28191
|
+
const c2 = code(e2);
|
|
28192
|
+
if (c2 === "EEXIST" || c2 === "ENOTEMPTY") {
|
|
28193
|
+
report.skipped.push({ path: to, reason: "moved concurrently" });
|
|
28194
|
+
return;
|
|
28195
|
+
}
|
|
28196
|
+
if (c2 === "ENOENT") {
|
|
28197
|
+
report.skipped.push({ path: to, reason: "moved concurrently" });
|
|
28198
|
+
return;
|
|
28199
|
+
}
|
|
28200
|
+
if (c2 === "EXDEV") {
|
|
28201
|
+
await fs7.cp(from, to, { recursive: true });
|
|
28202
|
+
await fs7.rm(from, { recursive: true, force: true });
|
|
28203
|
+
report.moved.push(to);
|
|
28204
|
+
return;
|
|
28205
|
+
}
|
|
28206
|
+
throw e2;
|
|
28207
|
+
}
|
|
28208
|
+
}
|
|
28209
|
+
async function mergeDir(from, to, report) {
|
|
28210
|
+
const entries = await fs7.readdir(from, { withFileTypes: true }).catch(() => []);
|
|
28211
|
+
for (const e2 of entries) {
|
|
28212
|
+
const src = join6(from, e2.name);
|
|
28213
|
+
const dst = join6(to, e2.name);
|
|
28214
|
+
if (e2.isDirectory() && await isDir(dst)) await mergeDir(src, dst, report);
|
|
28215
|
+
else await moveEntry(src, dst, report);
|
|
28216
|
+
}
|
|
28217
|
+
await fs7.rmdir(from).catch(() => {
|
|
28218
|
+
});
|
|
28219
|
+
}
|
|
28220
|
+
async function migrateHomeLayout(base = homedir4()) {
|
|
28221
|
+
const report = { moved: [], skipped: [] };
|
|
28222
|
+
const home = corenelDir2(base);
|
|
28223
|
+
if (!await present(home)) return report;
|
|
28224
|
+
const config = join6(home, "config");
|
|
28225
|
+
if (await isDir(config)) await mergeDir(config, home, report);
|
|
28226
|
+
const side = sidecarDir(base);
|
|
28227
|
+
for (const name of SIDECAR_FILES) {
|
|
28228
|
+
await moveEntry(join6(home, name), join6(side, name), report);
|
|
28229
|
+
}
|
|
28230
|
+
for (const name of SIDECAR_DIRS) {
|
|
28231
|
+
const src = join6(home, name);
|
|
28232
|
+
if (!await isDir(src)) continue;
|
|
28233
|
+
const dst = join6(side, name);
|
|
28234
|
+
if (await isDir(dst)) await mergeDir(src, dst, report);
|
|
28235
|
+
else await moveEntry(src, dst, report);
|
|
28236
|
+
}
|
|
28237
|
+
if (report.moved.length) await fs7.chmod(home, 448).catch(() => {
|
|
28238
|
+
});
|
|
28239
|
+
return report;
|
|
28240
|
+
}
|
|
28241
|
+
|
|
28079
28242
|
// src/session/wiring.ts
|
|
28080
28243
|
async function probeMsb(run) {
|
|
28081
28244
|
const r2 = await run(["--version"], { timeoutMs: 3e4 });
|
|
@@ -28144,10 +28307,175 @@ async function wireSessions(opts) {
|
|
|
28144
28307
|
return { manager, backend, port: makeSessionsPort({ manager, backend, guestEnv, ...portExtras(opts) }) };
|
|
28145
28308
|
}
|
|
28146
28309
|
|
|
28310
|
+
// src/runner/owners.ts
|
|
28311
|
+
import { readdir } from "node:fs/promises";
|
|
28312
|
+
import { join as join8 } from "node:path";
|
|
28313
|
+
|
|
28314
|
+
// src/hostsDir.ts
|
|
28315
|
+
import { join as join7 } from "node:path";
|
|
28316
|
+
var HOSTS_SEG = "hosts";
|
|
28317
|
+
var MAX_SYNC_PATH_LEN = 400;
|
|
28318
|
+
var MAX_SYNC_SEGMENTS = 24;
|
|
28319
|
+
var CONTROL_CHAR_RE = new RegExp(
|
|
28320
|
+
"[\\u0000-\\u001f\\u007f-\\u009f]"
|
|
28321
|
+
);
|
|
28322
|
+
var DIR_NAME_RE = /^[a-z0-9_-]{1,32}$/;
|
|
28323
|
+
function assertSyncPath(path) {
|
|
28324
|
+
if (!path) throw new Error("sync path is empty");
|
|
28325
|
+
if (path.length > MAX_SYNC_PATH_LEN) throw new Error(`sync path too long (> ${MAX_SYNC_PATH_LEN})`);
|
|
28326
|
+
if (CONTROL_CHAR_RE.test(path)) throw new Error("sync path contains control characters");
|
|
28327
|
+
if (path.includes(":")) throw new Error("sync path contains a colon");
|
|
28328
|
+
const n2 = normalizeFilePath(path);
|
|
28329
|
+
if (!n2) throw new Error("sync path normalizes to empty (targets the root)");
|
|
28330
|
+
const segments = n2.split("/");
|
|
28331
|
+
if (segments.length > MAX_SYNC_SEGMENTS) throw new Error(`sync path has too many segments (> ${MAX_SYNC_SEGMENTS})`);
|
|
28332
|
+
if (segments.some((s2) => s2 === "." || s2 === "..")) {
|
|
28333
|
+
throw new Error('sync path contains a "." or ".." segment');
|
|
28334
|
+
}
|
|
28335
|
+
}
|
|
28336
|
+
function hostsRoot(stateDir) {
|
|
28337
|
+
return join7(stateDir, "sidecar", HOSTS_SEG);
|
|
28338
|
+
}
|
|
28339
|
+
function hostsDirFor(stateDir, dirName2) {
|
|
28340
|
+
if (!DIR_NAME_RE.test(dirName2)) throw new Error(`invalid host dir name: ${dirName2}`);
|
|
28341
|
+
return join7(hostsRoot(stateDir), dirName2);
|
|
28342
|
+
}
|
|
28343
|
+
var RAW = /* @__PURE__ */ new WeakMap();
|
|
28344
|
+
function hostFilesFor(stateDir, dirName2) {
|
|
28345
|
+
const raw = new NodeFileService(hostsDirFor(stateDir, dirName2));
|
|
28346
|
+
const wrapper = {
|
|
28347
|
+
kind: raw.kind,
|
|
28348
|
+
label: raw.label,
|
|
28349
|
+
isLocalDisk: raw.isLocalDisk,
|
|
28350
|
+
writable: raw.writable,
|
|
28351
|
+
list: (dir) => raw.list(dir),
|
|
28352
|
+
read: (path) => raw.read(path),
|
|
28353
|
+
stat: (path) => raw.stat(path),
|
|
28354
|
+
search: (query) => raw.search(query),
|
|
28355
|
+
onChange: (_cb) => raw.onChange()
|
|
28356
|
+
};
|
|
28357
|
+
RAW.set(wrapper, raw);
|
|
28358
|
+
return wrapper;
|
|
28359
|
+
}
|
|
28360
|
+
async function syncWrite(files, path, content) {
|
|
28361
|
+
assertSyncPath(path);
|
|
28362
|
+
const raw = RAW.get(files);
|
|
28363
|
+
if (!raw) throw new Error("syncWrite: not a hostFilesFor() result");
|
|
28364
|
+
await raw.write(path, content);
|
|
28365
|
+
}
|
|
28366
|
+
async function syncRemove(files, path) {
|
|
28367
|
+
assertSyncPath(path);
|
|
28368
|
+
const raw = RAW.get(files);
|
|
28369
|
+
if (!raw) throw new Error("syncRemove: not a hostFilesFor() result");
|
|
28370
|
+
await raw.remove(path);
|
|
28371
|
+
}
|
|
28372
|
+
|
|
28373
|
+
// src/runner/owners.ts
|
|
28374
|
+
var WORKSPACES_SEG = "workspaces";
|
|
28375
|
+
var WORKSPACE_ID_RE = /^[a-z0-9_-]{1,32}$/;
|
|
28376
|
+
async function workspaceRootsFor(dirName2, hostDir) {
|
|
28377
|
+
let entries;
|
|
28378
|
+
try {
|
|
28379
|
+
entries = await readdir(join8(hostDir, WORKSPACES_SEG), { withFileTypes: true });
|
|
28380
|
+
} catch {
|
|
28381
|
+
return [];
|
|
28382
|
+
}
|
|
28383
|
+
const out = [];
|
|
28384
|
+
for (const entry of entries) {
|
|
28385
|
+
if (!entry.isDirectory()) continue;
|
|
28386
|
+
if (!WORKSPACE_ID_RE.test(entry.name)) continue;
|
|
28387
|
+
out.push({
|
|
28388
|
+
owner: `${dirName2}/ws/${entry.name}`,
|
|
28389
|
+
files: new NodeFileService(join8(hostDir, WORKSPACES_SEG, entry.name)),
|
|
28390
|
+
root: "crew"
|
|
28391
|
+
});
|
|
28392
|
+
}
|
|
28393
|
+
return out;
|
|
28394
|
+
}
|
|
28395
|
+
async function listOwnerRoots(stateDir) {
|
|
28396
|
+
const roots = [
|
|
28397
|
+
{ owner: "", files: new NodeFileService(stateDir), root: "crew" }
|
|
28398
|
+
];
|
|
28399
|
+
let entries;
|
|
28400
|
+
try {
|
|
28401
|
+
entries = await readdir(hostsRoot(stateDir), { withFileTypes: true });
|
|
28402
|
+
} catch {
|
|
28403
|
+
return roots;
|
|
28404
|
+
}
|
|
28405
|
+
for (const entry of entries) {
|
|
28406
|
+
if (!entry.isDirectory()) continue;
|
|
28407
|
+
const dirName2 = entry.name;
|
|
28408
|
+
let hostDir;
|
|
28409
|
+
try {
|
|
28410
|
+
hostDir = hostsDirFor(stateDir, dirName2);
|
|
28411
|
+
} catch {
|
|
28412
|
+
continue;
|
|
28413
|
+
}
|
|
28414
|
+
roots.push({ owner: dirName2, files: new NodeFileService(hostDir), root: "crew" });
|
|
28415
|
+
roots.push(...await workspaceRootsFor(dirName2, hostDir));
|
|
28416
|
+
}
|
|
28417
|
+
return roots;
|
|
28418
|
+
}
|
|
28419
|
+
|
|
28420
|
+
// src/crewDeployment.ts
|
|
28421
|
+
import { readFile } from "node:fs/promises";
|
|
28422
|
+
import { join as join9 } from "node:path";
|
|
28423
|
+
|
|
28424
|
+
// ../crew/deployment.ts
|
|
28425
|
+
var DEPLOYMENT_FILE = "deployment.json";
|
|
28426
|
+
function emptyDeployment() {
|
|
28427
|
+
return { agents: /* @__PURE__ */ Object.create(null) };
|
|
28428
|
+
}
|
|
28429
|
+
function deploymentKey(name, wsId) {
|
|
28430
|
+
return wsId ? `workspaces/${wsId}/crew/${name}` : `crew/${name}`;
|
|
28431
|
+
}
|
|
28432
|
+
function parseDeployment(text) {
|
|
28433
|
+
const out = emptyDeployment();
|
|
28434
|
+
let raw;
|
|
28435
|
+
try {
|
|
28436
|
+
raw = JSON.parse(text);
|
|
28437
|
+
} catch {
|
|
28438
|
+
return out;
|
|
28439
|
+
}
|
|
28440
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return out;
|
|
28441
|
+
const agents = raw.agents;
|
|
28442
|
+
if (!agents || typeof agents !== "object" || Array.isArray(agents)) return out;
|
|
28443
|
+
for (const [key, value] of Object.entries(agents)) {
|
|
28444
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
28445
|
+
const root = value.root;
|
|
28446
|
+
if (typeof root === "string" && root) out.agents[key] = { root };
|
|
28447
|
+
}
|
|
28448
|
+
return out;
|
|
28449
|
+
}
|
|
28450
|
+
function deploymentRootFor(d2, key) {
|
|
28451
|
+
if (!Object.prototype.hasOwnProperty.call(d2.agents, key)) return void 0;
|
|
28452
|
+
return d2.agents[key].root;
|
|
28453
|
+
}
|
|
28454
|
+
|
|
28455
|
+
// src/crewDeployment.ts
|
|
28456
|
+
function splitOwner(owner) {
|
|
28457
|
+
if (owner === "") return void 0;
|
|
28458
|
+
const parts = owner.split("/");
|
|
28459
|
+
if (parts.length === 1) return { dirName: parts[0] };
|
|
28460
|
+
if (parts.length === 3 && parts[1] === "ws") return { dirName: parts[0], wsId: parts[2] };
|
|
28461
|
+
return void 0;
|
|
28462
|
+
}
|
|
28463
|
+
async function deployedRoot(stateDir, owner, agent) {
|
|
28464
|
+
const parts = splitOwner(owner);
|
|
28465
|
+
if (!parts) return void 0;
|
|
28466
|
+
let text;
|
|
28467
|
+
try {
|
|
28468
|
+
text = await readFile(join9(hostsDirFor(stateDir, parts.dirName), DEPLOYMENT_FILE), "utf8");
|
|
28469
|
+
} catch {
|
|
28470
|
+
return void 0;
|
|
28471
|
+
}
|
|
28472
|
+
return deploymentRootFor(parseDeployment(text), deploymentKey(agent, parts.wsId));
|
|
28473
|
+
}
|
|
28474
|
+
|
|
28147
28475
|
// src/cert.ts
|
|
28148
|
-
import { promises as
|
|
28476
|
+
import { promises as fs8 } from "node:fs";
|
|
28149
28477
|
import { networkInterfaces } from "node:os";
|
|
28150
|
-
import { join as
|
|
28478
|
+
import { join as join10 } from "node:path";
|
|
28151
28479
|
import selfsigned from "selfsigned";
|
|
28152
28480
|
function lanIPv4s() {
|
|
28153
28481
|
const out = [];
|
|
@@ -28158,18 +28486,18 @@ function lanIPv4s() {
|
|
|
28158
28486
|
}
|
|
28159
28487
|
return [...new Set(out)];
|
|
28160
28488
|
}
|
|
28161
|
-
var certPath = () =>
|
|
28162
|
-
var keyPath = () =>
|
|
28163
|
-
var sansPath = () =>
|
|
28489
|
+
var certPath = () => join10(sidecarSubdir(), "sidecar.crt");
|
|
28490
|
+
var keyPath = () => join10(sidecarSubdir(), "sidecar.key");
|
|
28491
|
+
var sansPath = () => join10(sidecarSubdir(), "sidecar.sans.json");
|
|
28164
28492
|
function isIp(s2) {
|
|
28165
28493
|
return /^\d{1,3}(?:\.\d{1,3}){3}$/.test(s2) || s2.includes(":");
|
|
28166
28494
|
}
|
|
28167
28495
|
async function loadOrCreateSelfSigned(extraHosts = []) {
|
|
28168
28496
|
const sans = [...new Set(["localhost", "127.0.0.1", "::1", ...lanIPv4s(), ...extraHosts].filter(Boolean))];
|
|
28169
28497
|
try {
|
|
28170
|
-
const have = JSON.parse(await
|
|
28498
|
+
const have = JSON.parse(await fs8.readFile(sansPath(), "utf8"));
|
|
28171
28499
|
if (Array.isArray(have) && sans.every((s2) => have.includes(s2))) {
|
|
28172
|
-
const [cert, key] = await Promise.all([
|
|
28500
|
+
const [cert, key] = await Promise.all([fs8.readFile(certPath(), "utf8"), fs8.readFile(keyPath(), "utf8")]);
|
|
28173
28501
|
return { cert, key, sans: have };
|
|
28174
28502
|
}
|
|
28175
28503
|
} catch {
|
|
@@ -28181,10 +28509,10 @@ async function loadOrCreateSelfSigned(extraHosts = []) {
|
|
|
28181
28509
|
algorithm: "sha256",
|
|
28182
28510
|
extensions: [{ name: "subjectAltName", altNames }]
|
|
28183
28511
|
});
|
|
28184
|
-
await
|
|
28185
|
-
await
|
|
28186
|
-
await
|
|
28187
|
-
await
|
|
28512
|
+
await fs8.mkdir(sidecarSubdir(), { recursive: true });
|
|
28513
|
+
await fs8.writeFile(certPath(), pems.cert, { mode: 384 });
|
|
28514
|
+
await fs8.writeFile(keyPath(), pems.private, { mode: 384 });
|
|
28515
|
+
await fs8.writeFile(sansPath(), JSON.stringify(sans));
|
|
28188
28516
|
return { cert: pems.cert, key: pems.private, sans };
|
|
28189
28517
|
}
|
|
28190
28518
|
|
|
@@ -28289,8 +28617,8 @@ async function startWith(bin, port, name) {
|
|
|
28289
28617
|
child.stdout?.on("data", onData);
|
|
28290
28618
|
child.stderr?.on("data", onData);
|
|
28291
28619
|
child.on("error", (e2) => finish(() => reject(e2)));
|
|
28292
|
-
child.on("exit", (
|
|
28293
|
-
`${bin} exited (code ${
|
|
28620
|
+
child.on("exit", (code2) => finish(() => reject(new Error(
|
|
28621
|
+
`${bin} exited (code ${code2}) before a share URL appeared \u2014 is the environment enabled? Run \`${bin} enable <token>\` once.
|
|
28294
28622
|
${tail(out)}`
|
|
28295
28623
|
))));
|
|
28296
28624
|
const timer = setTimeout(() => finish(() => {
|
|
@@ -28427,13 +28755,13 @@ function startWith2(bin, mode, log, onDown) {
|
|
|
28427
28755
|
child.stdout?.on("data", onData);
|
|
28428
28756
|
child.stderr?.on("data", onData);
|
|
28429
28757
|
child.on("error", (e2) => finish(() => reject(e2)));
|
|
28430
|
-
child.on("exit", (
|
|
28758
|
+
child.on("exit", (code2) => {
|
|
28431
28759
|
if (settled) {
|
|
28432
|
-
log?.(`cloudflared exited (code ${
|
|
28433
|
-
fireDown(`cloudflared exited (code ${
|
|
28760
|
+
log?.(`cloudflared exited (code ${code2}) \u2014 the tunnel is down`);
|
|
28761
|
+
fireDown(`cloudflared exited (code ${code2})`);
|
|
28434
28762
|
return;
|
|
28435
28763
|
}
|
|
28436
|
-
finish(() => reject(new Error(`${bin} exited (code ${
|
|
28764
|
+
finish(() => reject(new Error(`${bin} exited (code ${code2}) before the tunnel came up.
|
|
28437
28765
|
${tail2(out)}`)));
|
|
28438
28766
|
});
|
|
28439
28767
|
const timer = setTimeout(() => finish(() => {
|
|
@@ -28682,6 +29010,14 @@ function parseCanCall(meta) {
|
|
|
28682
29010
|
if (!Array.isArray(raw)) return [];
|
|
28683
29011
|
return raw.filter((x2) => typeof x2 === "string" && isValidAgentName(x2));
|
|
28684
29012
|
}
|
|
29013
|
+
function parseWorkRoots(raw) {
|
|
29014
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
29015
|
+
const out = {};
|
|
29016
|
+
for (const [id, dir] of Object.entries(raw)) {
|
|
29017
|
+
if (typeof dir === "string" && dir) out[id] = dir;
|
|
29018
|
+
}
|
|
29019
|
+
return Object.keys(out).length ? out : void 0;
|
|
29020
|
+
}
|
|
28685
29021
|
function parseSettings(text) {
|
|
28686
29022
|
try {
|
|
28687
29023
|
const raw = JSON.parse(text);
|
|
@@ -28689,7 +29025,13 @@ function parseSettings(text) {
|
|
|
28689
29025
|
const o2 = raw;
|
|
28690
29026
|
return {
|
|
28691
29027
|
model: typeof o2.model === "string" ? o2.model : void 0,
|
|
28692
|
-
temperature: typeof o2.temperature === "number" ? o2.temperature : void 0
|
|
29028
|
+
temperature: typeof o2.temperature === "number" ? o2.temperature : void 0,
|
|
29029
|
+
/* Carried through as-is. Validation belongs to the daemon, which is the
|
|
29030
|
+
* only side that knows its own approved roots -- checking here would be
|
|
29031
|
+
* checking against the wrong list, on the wrong machine. Non-string
|
|
29032
|
+
* values are dropped per entry rather than failing the whole file: one
|
|
29033
|
+
* bad key must not cost an agent every other machine's setting. */
|
|
29034
|
+
workRoots: parseWorkRoots(o2.workRoots)
|
|
28693
29035
|
};
|
|
28694
29036
|
}
|
|
28695
29037
|
} catch {
|
|
@@ -30075,113 +30417,6 @@ function nodeCrewLocation(stateDir, scope, workspaceRoot) {
|
|
|
30075
30417
|
return { files: new NodeFileService(wsRoot), root: `${stateDirName()}/crew` };
|
|
30076
30418
|
}
|
|
30077
30419
|
|
|
30078
|
-
// src/runner/owners.ts
|
|
30079
|
-
import { readdir } from "node:fs/promises";
|
|
30080
|
-
import { join as join7 } from "node:path";
|
|
30081
|
-
|
|
30082
|
-
// src/hostsDir.ts
|
|
30083
|
-
import { join as join6 } from "node:path";
|
|
30084
|
-
var HOSTS_SEG = "hosts";
|
|
30085
|
-
var MAX_SYNC_PATH_LEN = 400;
|
|
30086
|
-
var MAX_SYNC_SEGMENTS = 24;
|
|
30087
|
-
var CONTROL_CHAR_RE = new RegExp(
|
|
30088
|
-
"[\\u0000-\\u001f\\u007f-\\u009f]"
|
|
30089
|
-
);
|
|
30090
|
-
var DIR_NAME_RE = /^[a-z0-9_-]{1,32}$/;
|
|
30091
|
-
function assertSyncPath(path) {
|
|
30092
|
-
if (!path) throw new Error("sync path is empty");
|
|
30093
|
-
if (path.length > MAX_SYNC_PATH_LEN) throw new Error(`sync path too long (> ${MAX_SYNC_PATH_LEN})`);
|
|
30094
|
-
if (CONTROL_CHAR_RE.test(path)) throw new Error("sync path contains control characters");
|
|
30095
|
-
if (path.includes(":")) throw new Error("sync path contains a colon");
|
|
30096
|
-
const n2 = normalizeFilePath(path);
|
|
30097
|
-
if (!n2) throw new Error("sync path normalizes to empty (targets the root)");
|
|
30098
|
-
const segments = n2.split("/");
|
|
30099
|
-
if (segments.length > MAX_SYNC_SEGMENTS) throw new Error(`sync path has too many segments (> ${MAX_SYNC_SEGMENTS})`);
|
|
30100
|
-
if (segments.some((s2) => s2 === "." || s2 === "..")) {
|
|
30101
|
-
throw new Error('sync path contains a "." or ".." segment');
|
|
30102
|
-
}
|
|
30103
|
-
}
|
|
30104
|
-
function hostsDirFor(stateDir, dirName2) {
|
|
30105
|
-
if (!DIR_NAME_RE.test(dirName2)) throw new Error(`invalid host dir name: ${dirName2}`);
|
|
30106
|
-
return join6(stateDir, HOSTS_SEG, dirName2);
|
|
30107
|
-
}
|
|
30108
|
-
var RAW = /* @__PURE__ */ new WeakMap();
|
|
30109
|
-
function hostFilesFor(stateDir, dirName2) {
|
|
30110
|
-
const raw = new NodeFileService(hostsDirFor(stateDir, dirName2));
|
|
30111
|
-
const wrapper = {
|
|
30112
|
-
kind: raw.kind,
|
|
30113
|
-
label: raw.label,
|
|
30114
|
-
isLocalDisk: raw.isLocalDisk,
|
|
30115
|
-
writable: raw.writable,
|
|
30116
|
-
list: (dir) => raw.list(dir),
|
|
30117
|
-
read: (path) => raw.read(path),
|
|
30118
|
-
stat: (path) => raw.stat(path),
|
|
30119
|
-
search: (query) => raw.search(query),
|
|
30120
|
-
onChange: (_cb) => raw.onChange()
|
|
30121
|
-
};
|
|
30122
|
-
RAW.set(wrapper, raw);
|
|
30123
|
-
return wrapper;
|
|
30124
|
-
}
|
|
30125
|
-
async function syncWrite(files, path, content) {
|
|
30126
|
-
assertSyncPath(path);
|
|
30127
|
-
const raw = RAW.get(files);
|
|
30128
|
-
if (!raw) throw new Error("syncWrite: not a hostFilesFor() result");
|
|
30129
|
-
await raw.write(path, content);
|
|
30130
|
-
}
|
|
30131
|
-
async function syncRemove(files, path) {
|
|
30132
|
-
assertSyncPath(path);
|
|
30133
|
-
const raw = RAW.get(files);
|
|
30134
|
-
if (!raw) throw new Error("syncRemove: not a hostFilesFor() result");
|
|
30135
|
-
await raw.remove(path);
|
|
30136
|
-
}
|
|
30137
|
-
|
|
30138
|
-
// src/runner/owners.ts
|
|
30139
|
-
var WORKSPACES_SEG = "workspaces";
|
|
30140
|
-
var WORKSPACE_ID_RE = /^[a-z0-9_-]{1,32}$/;
|
|
30141
|
-
async function workspaceRootsFor(dirName2, hostDir) {
|
|
30142
|
-
let entries;
|
|
30143
|
-
try {
|
|
30144
|
-
entries = await readdir(join7(hostDir, WORKSPACES_SEG), { withFileTypes: true });
|
|
30145
|
-
} catch {
|
|
30146
|
-
return [];
|
|
30147
|
-
}
|
|
30148
|
-
const out = [];
|
|
30149
|
-
for (const entry of entries) {
|
|
30150
|
-
if (!entry.isDirectory()) continue;
|
|
30151
|
-
if (!WORKSPACE_ID_RE.test(entry.name)) continue;
|
|
30152
|
-
out.push({
|
|
30153
|
-
owner: `${dirName2}/ws/${entry.name}`,
|
|
30154
|
-
files: new NodeFileService(join7(hostDir, WORKSPACES_SEG, entry.name)),
|
|
30155
|
-
root: "crew"
|
|
30156
|
-
});
|
|
30157
|
-
}
|
|
30158
|
-
return out;
|
|
30159
|
-
}
|
|
30160
|
-
async function listOwnerRoots(stateDir) {
|
|
30161
|
-
const roots = [
|
|
30162
|
-
{ owner: "", files: new NodeFileService(stateDir), root: "crew" }
|
|
30163
|
-
];
|
|
30164
|
-
let entries;
|
|
30165
|
-
try {
|
|
30166
|
-
entries = await readdir(join7(stateDir, HOSTS_SEG), { withFileTypes: true });
|
|
30167
|
-
} catch {
|
|
30168
|
-
return roots;
|
|
30169
|
-
}
|
|
30170
|
-
for (const entry of entries) {
|
|
30171
|
-
if (!entry.isDirectory()) continue;
|
|
30172
|
-
const dirName2 = entry.name;
|
|
30173
|
-
let hostDir;
|
|
30174
|
-
try {
|
|
30175
|
-
hostDir = hostsDirFor(stateDir, dirName2);
|
|
30176
|
-
} catch {
|
|
30177
|
-
continue;
|
|
30178
|
-
}
|
|
30179
|
-
roots.push({ owner: dirName2, files: new NodeFileService(hostDir), root: "crew" });
|
|
30180
|
-
roots.push(...await workspaceRootsFor(dirName2, hostDir));
|
|
30181
|
-
}
|
|
30182
|
-
return roots;
|
|
30183
|
-
}
|
|
30184
|
-
|
|
30185
30420
|
// ../../node_modules/.pnpm/openai@4.104.0_ws@8.21.0/node_modules/openai/internal/qs/formats.mjs
|
|
30186
30421
|
var default_format = "RFC3986";
|
|
30187
30422
|
var formatters = {
|
|
@@ -36933,10 +37168,10 @@ function statusOf(message) {
|
|
|
36933
37168
|
function classifyStreamError(message, status) {
|
|
36934
37169
|
if (/\baborted\b|AbortError/i.test(message)) return "fatal";
|
|
36935
37170
|
if (/\b(timed out|request timeout|gateway timeout|deadline exceeded)\b/i.test(message)) return "fatal";
|
|
36936
|
-
const
|
|
36937
|
-
if (
|
|
36938
|
-
if (FATAL_STATUS.has(
|
|
36939
|
-
if (TRANSIENT_STATUS.has(
|
|
37171
|
+
const code2 = status ?? statusOf(message);
|
|
37172
|
+
if (code2 != null) {
|
|
37173
|
+
if (FATAL_STATUS.has(code2)) return "fatal";
|
|
37174
|
+
if (TRANSIENT_STATUS.has(code2)) return "transient";
|
|
36940
37175
|
}
|
|
36941
37176
|
if (/\b(invalid[\s_-]?api[\s_-]?key|incorrect api key|unauthorized|forbidden|quota exceeded|insufficient)\b/i.test(message)) return "fatal";
|
|
36942
37177
|
return "transient";
|
|
@@ -38334,7 +38569,7 @@ var NodeRunnerService = class {
|
|
|
38334
38569
|
fromWorkdir ? `crew agent not found: ${agent} (looked in ${fromWorkdir} and the owner's own crew)` : `crew agent not found: ${agent}`
|
|
38335
38570
|
);
|
|
38336
38571
|
}
|
|
38337
|
-
const workDir = this.opts.workRootFor?.(def) ?? this.opts.roots[0];
|
|
38572
|
+
const workDir = await this.opts.workRootFor?.(def, owner) ?? this.opts.roots[0];
|
|
38338
38573
|
if (!workDir) throw new Error("daemon has no --root workspace to run an agent in");
|
|
38339
38574
|
const workFiles = new NodeFileService(workDir);
|
|
38340
38575
|
const deps = this.opts.deps ?? {
|
|
@@ -38446,7 +38681,7 @@ var NodeRunnerService = class {
|
|
|
38446
38681
|
// src/runner/core.ts
|
|
38447
38682
|
import { watch } from "node:fs";
|
|
38448
38683
|
import { readdir as readdir2, stat } from "node:fs/promises";
|
|
38449
|
-
import { join as
|
|
38684
|
+
import { join as join11, relative as relative5, sep as sep3 } from "node:path";
|
|
38450
38685
|
|
|
38451
38686
|
// src/runner/triggerConfig.ts
|
|
38452
38687
|
function inputFor(trigger, fallback) {
|
|
@@ -38926,6 +39161,7 @@ function startDaemonCore(opts) {
|
|
|
38926
39161
|
w2.on("error", (err) => log(`daemon core: watch error on "${dir}" -- ${err.message}`));
|
|
38927
39162
|
watchers.push(w2);
|
|
38928
39163
|
} catch (err) {
|
|
39164
|
+
if (err?.code === "ENOENT") continue;
|
|
38929
39165
|
log(`daemon core: could not watch "${dir}" -- ${err instanceof Error ? err.message : String(err)}`);
|
|
38930
39166
|
}
|
|
38931
39167
|
}
|
|
@@ -38955,9 +39191,9 @@ function startDaemonCore(opts) {
|
|
|
38955
39191
|
for (const entry of entries) {
|
|
38956
39192
|
if (entry.isDirectory()) {
|
|
38957
39193
|
if (isExcludedSegment(entry.name, dotDir)) continue;
|
|
38958
|
-
await walkForChanges(root,
|
|
39194
|
+
await walkForChanges(root, join11(dir, entry.name), since);
|
|
38959
39195
|
} else {
|
|
38960
|
-
const abs =
|
|
39196
|
+
const abs = join11(dir, entry.name);
|
|
38961
39197
|
let mtimeMs;
|
|
38962
39198
|
try {
|
|
38963
39199
|
mtimeMs = (await stat(abs)).mtimeMs;
|
|
@@ -39235,26 +39471,26 @@ function makeRunHub(opts = {}) {
|
|
|
39235
39471
|
}
|
|
39236
39472
|
|
|
39237
39473
|
// src/identity.ts
|
|
39238
|
-
import { promises as
|
|
39474
|
+
import { promises as fs10 } from "node:fs";
|
|
39239
39475
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
39240
39476
|
import { hostname } from "node:os";
|
|
39241
|
-
import { join as
|
|
39242
|
-
function idPath(dir =
|
|
39243
|
-
return
|
|
39477
|
+
import { join as join12 } from "node:path";
|
|
39478
|
+
function idPath(dir = sidecarSubdir()) {
|
|
39479
|
+
return join12(dir, "daemon.id");
|
|
39244
39480
|
}
|
|
39245
|
-
async function loadOrCreateSidecarId(dir =
|
|
39481
|
+
async function loadOrCreateSidecarId(dir = sidecarSubdir()) {
|
|
39246
39482
|
const path = idPath(dir);
|
|
39247
39483
|
try {
|
|
39248
|
-
const existing = (await
|
|
39484
|
+
const existing = (await fs10.readFile(path, "utf8")).trim();
|
|
39249
39485
|
if (existing) return existing;
|
|
39250
39486
|
} catch (err) {
|
|
39251
39487
|
if (!isEnoent(err)) throw err;
|
|
39252
39488
|
}
|
|
39253
|
-
await
|
|
39489
|
+
await fs10.mkdir(dir, { recursive: true });
|
|
39254
39490
|
const id = randomBytes2(16).toString("hex");
|
|
39255
|
-
await
|
|
39491
|
+
await fs10.writeFile(path, id + "\n", { mode: 384 });
|
|
39256
39492
|
try {
|
|
39257
|
-
await
|
|
39493
|
+
await fs10.chmod(path, 384);
|
|
39258
39494
|
} catch {
|
|
39259
39495
|
}
|
|
39260
39496
|
return id;
|
|
@@ -39267,9 +39503,9 @@ function sidecarName(opts) {
|
|
|
39267
39503
|
}
|
|
39268
39504
|
|
|
39269
39505
|
// src/hosts.ts
|
|
39270
|
-
import { promises as
|
|
39506
|
+
import { promises as fs11 } from "node:fs";
|
|
39271
39507
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
39272
|
-
import { join as
|
|
39508
|
+
import { join as join13, dirname as dirname4 } from "node:path";
|
|
39273
39509
|
var MAX_DIR = 32;
|
|
39274
39510
|
var MAX_FIELD_LEN = 200;
|
|
39275
39511
|
var MAX_HOSTS = 200;
|
|
@@ -39302,8 +39538,8 @@ function sanitizeDirName(raw) {
|
|
|
39302
39538
|
const safe = collapsed || "host";
|
|
39303
39539
|
return RESERVED_WINDOWS_NAMES.has(safe) ? `${safe}-host` : safe;
|
|
39304
39540
|
}
|
|
39305
|
-
function hostsPath(
|
|
39306
|
-
return
|
|
39541
|
+
function hostsPath(stateDir) {
|
|
39542
|
+
return join13(stateDir, "sidecar", "hosts.json");
|
|
39307
39543
|
}
|
|
39308
39544
|
function emptyManifest() {
|
|
39309
39545
|
return /* @__PURE__ */ Object.create(null);
|
|
@@ -39324,7 +39560,7 @@ function repairHostRecord(value, fallbackLastSeen) {
|
|
|
39324
39560
|
async function read(dir, fallbackLastSeen) {
|
|
39325
39561
|
let raw;
|
|
39326
39562
|
try {
|
|
39327
|
-
raw = await
|
|
39563
|
+
raw = await fs11.readFile(hostsPath(dir), "utf8");
|
|
39328
39564
|
} catch {
|
|
39329
39565
|
return emptyManifest();
|
|
39330
39566
|
}
|
|
@@ -39342,15 +39578,16 @@ async function read(dir, fallbackLastSeen) {
|
|
|
39342
39578
|
}
|
|
39343
39579
|
return manifest;
|
|
39344
39580
|
}
|
|
39345
|
-
async function write(
|
|
39346
|
-
|
|
39347
|
-
|
|
39348
|
-
const
|
|
39581
|
+
async function write(stateDir, manifest) {
|
|
39582
|
+
const dir = dirname4(hostsPath(stateDir));
|
|
39583
|
+
await fs11.mkdir(dir, { recursive: true });
|
|
39584
|
+
const target = hostsPath(stateDir);
|
|
39585
|
+
const tmp = join13(dir, `.hosts.json.tmp-${randomBytes3(8).toString("hex")}`);
|
|
39349
39586
|
try {
|
|
39350
|
-
await
|
|
39351
|
-
await
|
|
39587
|
+
await fs11.writeFile(tmp, JSON.stringify(manifest, null, 2));
|
|
39588
|
+
await fs11.rename(tmp, target);
|
|
39352
39589
|
} catch (err) {
|
|
39353
|
-
await
|
|
39590
|
+
await fs11.rm(tmp, { force: true }).catch(() => void 0);
|
|
39354
39591
|
throw err;
|
|
39355
39592
|
}
|
|
39356
39593
|
}
|
|
@@ -39498,8 +39735,8 @@ function makeHostSync(opts) {
|
|
|
39498
39735
|
}
|
|
39499
39736
|
|
|
39500
39737
|
// src/hostsCli.ts
|
|
39501
|
-
import { resolve as resolve5
|
|
39502
|
-
import { promises as
|
|
39738
|
+
import { resolve as resolve5 } from "node:path";
|
|
39739
|
+
import { promises as fs12 } from "node:fs";
|
|
39503
39740
|
async function listHosts(manifest) {
|
|
39504
39741
|
const m2 = await manifest.list();
|
|
39505
39742
|
return Object.entries(m2).map(([clientId, r2]) => ({
|
|
@@ -39522,7 +39759,7 @@ async function forgetHost(stateDir, manifest, dirName2) {
|
|
|
39522
39759
|
return { ok: false, reason: e2 instanceof Error ? e2.message : String(e2) };
|
|
39523
39760
|
}
|
|
39524
39761
|
try {
|
|
39525
|
-
await
|
|
39762
|
+
await fs12.rm(dir, { recursive: true, force: true });
|
|
39526
39763
|
} catch (e2) {
|
|
39527
39764
|
return {
|
|
39528
39765
|
ok: false,
|
|
@@ -39533,10 +39770,10 @@ async function forgetHost(stateDir, manifest, dirName2) {
|
|
|
39533
39770
|
return { ok: true };
|
|
39534
39771
|
}
|
|
39535
39772
|
async function checkHostsDrift(stateDir, manifest, log) {
|
|
39536
|
-
const
|
|
39773
|
+
const root = hostsRoot(stateDir);
|
|
39537
39774
|
let entries;
|
|
39538
39775
|
try {
|
|
39539
|
-
entries = (await
|
|
39776
|
+
entries = (await fs12.readdir(root, { withFileTypes: true })).filter((d2) => d2.isDirectory()).map((d2) => d2.name);
|
|
39540
39777
|
} catch {
|
|
39541
39778
|
return [];
|
|
39542
39779
|
}
|
|
@@ -40025,7 +40262,7 @@ var FLAGS = [
|
|
|
40025
40262
|
{ flag: "--app-url", arg: "<url>", help: "App to open and pair with, path included (default https://corenel.ai; prompd pairs at https://prompd.app/studio)." },
|
|
40026
40263
|
{ flag: "--daemon", help: "Run crew agents on this machine unattended: cron, file-change and run-complete triggers." },
|
|
40027
40264
|
{ flag: "--endpoint", arg: "<url>", help: "Gateway the daemon calls for model runs (default https://api.corenel.ai/api). A bare origin gets /api appended." },
|
|
40028
|
-
{ flag: "--state-dir", arg: "<dir>", help: "Where crew agents, sessions and tokens live (defaults to the Corenel dot-dir)." },
|
|
40265
|
+
{ flag: "--state-dir", arg: "<dir>", help: "Where crew agents, sessions and tokens live (defaults to the Corenel dot-dir). REQUIRED for a second sidecar on this machine: two sharing a state dir share their identity." },
|
|
40029
40266
|
{ flag: "--default-model", arg: "<id>", help: "Model for crew agents that do not name one." },
|
|
40030
40267
|
{ flag: "--name", arg: "<label>", help: "Friendly name advertised to clients (defaults to the hostname)." },
|
|
40031
40268
|
{ flag: "--max-chain-depth", arg: "<n>", help: "Cap how far run-complete triggers may chain before suppressing (default 8)." },
|
|
@@ -40075,6 +40312,11 @@ function helpText() {
|
|
|
40075
40312
|
` ${self} --daemon --default-model gpt-4.1-mini`,
|
|
40076
40313
|
` ${self} --root ~/work --acp claude-code --acp-policy read-only`,
|
|
40077
40314
|
"",
|
|
40315
|
+
" A second sidecar on the same machine needs its own state dir \u2014 without one",
|
|
40316
|
+
" both read the same daemon.id and advertise the same identity, so a paired",
|
|
40317
|
+
" browser sees one sidecar where there are two:",
|
|
40318
|
+
` ${self} --root ~/other --port 4859 --state-dir ~/.corenel-other`,
|
|
40319
|
+
"",
|
|
40078
40320
|
"Reachable both ways: `corenel sidecar \u2026` through the CLI, and",
|
|
40079
40321
|
"`corenel-sidecar \u2026` standalone, for machines that run only this.",
|
|
40080
40322
|
"",
|
|
@@ -40274,7 +40516,7 @@ function assertDirectory(path, flag2) {
|
|
|
40274
40516
|
}
|
|
40275
40517
|
|
|
40276
40518
|
// src/roots.ts
|
|
40277
|
-
import { promises as
|
|
40519
|
+
import { promises as fs13 } from "node:fs";
|
|
40278
40520
|
import { resolve as resolve8 } from "node:path";
|
|
40279
40521
|
var DISABLED = "adding working directories is not enabled (no --allow-add-under)";
|
|
40280
40522
|
function createRootsController(opts) {
|
|
@@ -40288,8 +40530,8 @@ function createRootsController(opts) {
|
|
|
40288
40530
|
if (!areas.some((a2) => withinRoot(a2, abs))) {
|
|
40289
40531
|
throw new Error(`path is outside every allowed area: ${abs}`);
|
|
40290
40532
|
}
|
|
40291
|
-
const real = await
|
|
40292
|
-
const realAreas = await Promise.all(areas.map((a2) =>
|
|
40533
|
+
const real = await fs13.realpath(abs);
|
|
40534
|
+
const realAreas = await Promise.all(areas.map((a2) => fs13.realpath(a2)));
|
|
40293
40535
|
if (!realAreas.some((a2) => withinRoot(a2, real))) {
|
|
40294
40536
|
throw new Error(`path escapes the allowed area via symlink: ${abs}`);
|
|
40295
40537
|
}
|
|
@@ -40314,12 +40556,12 @@ function createRootsController(opts) {
|
|
|
40314
40556
|
if (!cloneAreas.some((a2) => withinRoot(a2, abs))) {
|
|
40315
40557
|
throw new Error(`checkout is outside every --clone-under directory: ${abs}`);
|
|
40316
40558
|
}
|
|
40317
|
-
const real = await
|
|
40318
|
-
const realAreas = await Promise.all(cloneAreas.map((a2) =>
|
|
40559
|
+
const real = await fs13.realpath(abs);
|
|
40560
|
+
const realAreas = await Promise.all(cloneAreas.map((a2) => fs13.realpath(a2)));
|
|
40319
40561
|
if (!realAreas.some((a2) => withinRoot(a2, real))) {
|
|
40320
40562
|
throw new Error(`checkout escapes its --clone-under directory via symlink: ${abs}`);
|
|
40321
40563
|
}
|
|
40322
|
-
const st2 = await
|
|
40564
|
+
const st2 = await fs13.stat(abs);
|
|
40323
40565
|
if (!st2.isDirectory()) throw new Error(`not a directory: ${abs}`);
|
|
40324
40566
|
roots.push(abs);
|
|
40325
40567
|
announce();
|
|
@@ -40329,14 +40571,14 @@ function createRootsController(opts) {
|
|
|
40329
40571
|
if (path === void 0) return [...areas];
|
|
40330
40572
|
const abs = resolve8(path);
|
|
40331
40573
|
await assertInArea(abs);
|
|
40332
|
-
const entries = await
|
|
40574
|
+
const entries = await fs13.readdir(abs, { withFileTypes: true });
|
|
40333
40575
|
return entries.filter((e2) => e2.isDirectory()).map((e2) => e2.name);
|
|
40334
40576
|
},
|
|
40335
40577
|
async add(path) {
|
|
40336
40578
|
const abs = resolve8(path);
|
|
40337
40579
|
if (roots.some((r2) => resolve8(r2) === abs)) return;
|
|
40338
40580
|
await assertInArea(abs);
|
|
40339
|
-
const st2 = await
|
|
40581
|
+
const st2 = await fs13.stat(abs);
|
|
40340
40582
|
if (!st2.isDirectory()) throw new Error(`not a directory: ${abs}`);
|
|
40341
40583
|
roots.push(abs);
|
|
40342
40584
|
announce();
|
|
@@ -40352,8 +40594,8 @@ function createRootsController(opts) {
|
|
|
40352
40594
|
|
|
40353
40595
|
// src/session/gitSource.ts
|
|
40354
40596
|
import { spawn as spawn6 } from "node:child_process";
|
|
40355
|
-
import { promises as
|
|
40356
|
-
import { join as
|
|
40597
|
+
import { promises as fs14 } from "node:fs";
|
|
40598
|
+
import { join as join14, resolve as resolve9 } from "node:path";
|
|
40357
40599
|
var DEFAULT_TIMEOUT_MS3 = 10 * 6e4;
|
|
40358
40600
|
var ALLOWED_SCHEMES = ["https://", "ssh://", "git+ssh://"];
|
|
40359
40601
|
var SCP_LIKE = /^[A-Za-z0-9_.-]+@[A-Za-z0-9_.-]+:[A-Za-z0-9_./~-]+$/;
|
|
@@ -40426,9 +40668,9 @@ git timed out after ${opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS3}ms` });
|
|
|
40426
40668
|
settle({ code: -1, stdout, stderr: `${stderr}
|
|
40427
40669
|
${e2 instanceof Error ? e2.message : String(e2)}` });
|
|
40428
40670
|
});
|
|
40429
|
-
child.on("close", (
|
|
40671
|
+
child.on("close", (code2) => {
|
|
40430
40672
|
clearTimeout(timer);
|
|
40431
|
-
settle({ code:
|
|
40673
|
+
settle({ code: code2 ?? -1, stdout, stderr });
|
|
40432
40674
|
});
|
|
40433
40675
|
});
|
|
40434
40676
|
}
|
|
@@ -40445,14 +40687,14 @@ function createGitSource(deps) {
|
|
|
40445
40687
|
if (!areas.some((a2) => withinRoot(a2, abs))) {
|
|
40446
40688
|
throw new Error(`clone destination is outside every allowed area: ${abs}`);
|
|
40447
40689
|
}
|
|
40448
|
-
const realAreas = await Promise.all(areas.map((a2) =>
|
|
40690
|
+
const realAreas = await Promise.all(areas.map((a2) => fs14.realpath(a2)));
|
|
40449
40691
|
if (!realAreas.some((a2) => withinRoot(a2, abs))) {
|
|
40450
40692
|
throw new Error(`clone destination escapes the allowed area via symlink: ${abs}`);
|
|
40451
40693
|
}
|
|
40452
40694
|
}
|
|
40453
40695
|
async function existing(dir, url) {
|
|
40454
40696
|
try {
|
|
40455
|
-
const entries = await
|
|
40697
|
+
const entries = await fs14.readdir(dir);
|
|
40456
40698
|
if (entries.length === 0) return "absent";
|
|
40457
40699
|
} catch {
|
|
40458
40700
|
return "absent";
|
|
@@ -40470,7 +40712,7 @@ function createGitSource(deps) {
|
|
|
40470
40712
|
if (!areas.length) throw new Error(DISABLED2);
|
|
40471
40713
|
assertCloneUrl(spec.url);
|
|
40472
40714
|
if (spec.ref !== void 0) assertRef(spec.ref);
|
|
40473
|
-
const dir =
|
|
40715
|
+
const dir = join14(areas[0], checkoutName(spec.url));
|
|
40474
40716
|
await assertInArea(dir);
|
|
40475
40717
|
const state = await existing(dir, spec.url);
|
|
40476
40718
|
if (state === "occupied") {
|
|
@@ -40627,8 +40869,8 @@ var JsonRpcPeer = class {
|
|
|
40627
40869
|
this.respondError(id, shape.code, shape.message);
|
|
40628
40870
|
}
|
|
40629
40871
|
}
|
|
40630
|
-
respondError(id,
|
|
40631
|
-
this.write({ jsonrpc: "2.0", id, error: { code, message } });
|
|
40872
|
+
respondError(id, code2, message) {
|
|
40873
|
+
this.write({ jsonrpc: "2.0", id, error: { code: code2, message } });
|
|
40632
40874
|
}
|
|
40633
40875
|
write(value) {
|
|
40634
40876
|
if (this.closed) return;
|
|
@@ -40668,6 +40910,10 @@ var JsonRpcPeer = class {
|
|
|
40668
40910
|
|
|
40669
40911
|
// src/acp/client.ts
|
|
40670
40912
|
var ACP_PROTOCOL_VERSION = 1;
|
|
40913
|
+
function acceptsHttpMcp(agentCapabilities) {
|
|
40914
|
+
const mcp = agentCapabilities.mcpCapabilities;
|
|
40915
|
+
return !!(mcp && typeof mcp === "object" && mcp.http === true);
|
|
40916
|
+
}
|
|
40671
40917
|
function rec2(v2) {
|
|
40672
40918
|
return typeof v2 === "object" && v2 !== null && !Array.isArray(v2) ? v2 : {};
|
|
40673
40919
|
}
|
|
@@ -40682,6 +40928,10 @@ var AcpClient = class {
|
|
|
40682
40928
|
currentMode = null;
|
|
40683
40929
|
/** Whether the agent said it can reattach to an existing session. */
|
|
40684
40930
|
canLoad = false;
|
|
40931
|
+
/* Whether the agent accepts HTTP MCP servers. Recorded at initialize, read at
|
|
40932
|
+
* session/new -- the schema defaults it to FALSE, so an agent that says
|
|
40933
|
+
* nothing gets nothing. */
|
|
40934
|
+
httpMcp = false;
|
|
40685
40935
|
disposed = false;
|
|
40686
40936
|
constructor(opts) {
|
|
40687
40937
|
this.opts = opts;
|
|
@@ -40692,8 +40942,8 @@ var AcpClient = class {
|
|
|
40692
40942
|
...opts.log ? { log: opts.log } : {}
|
|
40693
40943
|
});
|
|
40694
40944
|
opts.transport.onData((chunk) => this.peer.push(chunk));
|
|
40695
|
-
opts.transport.onExit((
|
|
40696
|
-
this.peer.close(`agent exited (code ${
|
|
40945
|
+
opts.transport.onExit((code2) => {
|
|
40946
|
+
this.peer.close(`agent exited (code ${code2 ?? "null"})`);
|
|
40697
40947
|
});
|
|
40698
40948
|
}
|
|
40699
40949
|
log(m2) {
|
|
@@ -40737,6 +40987,7 @@ var AcpClient = class {
|
|
|
40737
40987
|
}));
|
|
40738
40988
|
const caps = rec2(raw.agentCapabilities);
|
|
40739
40989
|
this.canLoad = caps.loadSession === true;
|
|
40990
|
+
this.httpMcp = acceptsHttpMcp(caps);
|
|
40740
40991
|
const methods = Array.isArray(raw.authMethods) ? raw.authMethods : [];
|
|
40741
40992
|
const version = typeof raw.protocolVersion === "number" ? raw.protocolVersion : ACP_PROTOCOL_VERSION;
|
|
40742
40993
|
if (version !== ACP_PROTOCOL_VERSION) {
|
|
@@ -40749,9 +41000,16 @@ var AcpClient = class {
|
|
|
40749
41000
|
authMethods: methods
|
|
40750
41001
|
};
|
|
40751
41002
|
}
|
|
40752
|
-
/**
|
|
40753
|
-
|
|
40754
|
-
|
|
41003
|
+
/**
|
|
41004
|
+
* `cwd` MUST be absolute per the ACP spec; the caller passes a confined root.
|
|
41005
|
+
*
|
|
41006
|
+
* `mcpServers` is filtered by what the agent said it can take: this used to
|
|
41007
|
+
* be a hardcoded `[]`, so a user's configured MCP servers never reached the
|
|
41008
|
+
* agent at all and MCP was configurable but inert.
|
|
41009
|
+
*/
|
|
41010
|
+
async newSession(cwd, mcpServers = []) {
|
|
41011
|
+
const servers = this.allowedMcp(mcpServers);
|
|
41012
|
+
const raw = rec2(await this.peer.request("session/new", { cwd, mcpServers: servers }));
|
|
40755
41013
|
const id = str7(raw.sessionId);
|
|
40756
41014
|
if (!id) throw new Error("acp: agent returned no sessionId");
|
|
40757
41015
|
this.sessionId = id;
|
|
@@ -40764,6 +41022,22 @@ var AcpClient = class {
|
|
|
40764
41022
|
this.currentMode = str7(modes.currentModeId) || null;
|
|
40765
41023
|
return id;
|
|
40766
41024
|
}
|
|
41025
|
+
/**
|
|
41026
|
+
* The MCP servers this agent will actually accept.
|
|
41027
|
+
*
|
|
41028
|
+
* Silently empty rather than throwing: an agent without the capability is a
|
|
41029
|
+
* normal thing to be pointed at, and refusing to open a session over it would
|
|
41030
|
+
* turn "no MCP here" into "no agent here". Logged once so the absence is
|
|
41031
|
+
* visible rather than mysterious.
|
|
41032
|
+
*/
|
|
41033
|
+
allowedMcp(servers) {
|
|
41034
|
+
if (!servers.length) return [];
|
|
41035
|
+
if (!this.httpMcp) {
|
|
41036
|
+
this.log(`acp: agent does not accept HTTP MCP servers \u2014 ${servers.length} not passed`);
|
|
41037
|
+
return [];
|
|
41038
|
+
}
|
|
41039
|
+
return servers;
|
|
41040
|
+
}
|
|
40767
41041
|
/** True when `session/load` is worth attempting. */
|
|
40768
41042
|
supportsLoad() {
|
|
40769
41043
|
return this.canLoad;
|
|
@@ -40775,9 +41049,9 @@ var AcpClient = class {
|
|
|
40775
41049
|
* this resolves, which is what lets a caller rebuild a transcript it lost —
|
|
40776
41050
|
* so the update handler must be wired before calling, not after.
|
|
40777
41051
|
*/
|
|
40778
|
-
async loadSession(sessionId, cwd) {
|
|
41052
|
+
async loadSession(sessionId, cwd, mcpServers = []) {
|
|
40779
41053
|
if (!this.canLoad) throw new Error("agent cannot load an existing session");
|
|
40780
|
-
await this.peer.request("session/load", { sessionId, cwd, mcpServers:
|
|
41054
|
+
await this.peer.request("session/load", { sessionId, cwd, mcpServers: this.allowedMcp(mcpServers) });
|
|
40781
41055
|
this.sessionId = sessionId;
|
|
40782
41056
|
}
|
|
40783
41057
|
availableModes() {
|
|
@@ -40872,7 +41146,7 @@ function spawnAcpTransport(target, cwd, log, env) {
|
|
|
40872
41146
|
if (!killed && child.stdin.writable) child.stdin.write(line);
|
|
40873
41147
|
},
|
|
40874
41148
|
onData: (cb) => child.stdout.on("data", (d2) => cb(d2)),
|
|
40875
|
-
onExit: (cb) => child.on("exit", (
|
|
41149
|
+
onExit: (cb) => child.on("exit", (code2) => cb(code2)),
|
|
40876
41150
|
kill: () => {
|
|
40877
41151
|
if (killed) return;
|
|
40878
41152
|
killed = true;
|
|
@@ -41171,7 +41445,7 @@ function makeAcpService(deps) {
|
|
|
41171
41445
|
return {
|
|
41172
41446
|
targets: () => targetIds(),
|
|
41173
41447
|
async start(targetId, onUpdate, opts) {
|
|
41174
|
-
const { root, modeId, resume } = opts ?? {};
|
|
41448
|
+
const { root, modeId, resume, mcpServers } = opts ?? {};
|
|
41175
41449
|
const target = resolveTarget(targetId);
|
|
41176
41450
|
if (!target) {
|
|
41177
41451
|
throw new Error(`unknown ACP target "${targetId}" (known: ${targetIds().join(", ")})`);
|
|
@@ -41205,7 +41479,7 @@ function makeAcpService(deps) {
|
|
|
41205
41479
|
let resumed = false;
|
|
41206
41480
|
if (resume && c2.supportsLoad()) {
|
|
41207
41481
|
try {
|
|
41208
|
-
await c2.loadSession(resume, cwd);
|
|
41482
|
+
await c2.loadSession(resume, cwd, mcpServers ?? []);
|
|
41209
41483
|
sessionId = resume;
|
|
41210
41484
|
resumed = true;
|
|
41211
41485
|
log(`acp: reattached to session ${resume}`);
|
|
@@ -41215,7 +41489,7 @@ function makeAcpService(deps) {
|
|
|
41215
41489
|
} else if (resume) {
|
|
41216
41490
|
log(`acp: ${target.label} cannot reattach to a session \u2014 starting a new one`);
|
|
41217
41491
|
}
|
|
41218
|
-
if (!sessionId) sessionId = await c2.newSession(cwd);
|
|
41492
|
+
if (!sessionId) sessionId = await c2.newSession(cwd, mcpServers ?? []);
|
|
41219
41493
|
if (!resumed && modeId && c2.availableModes().some((m2) => m2.id === modeId)) {
|
|
41220
41494
|
await c2.setMode(modeId);
|
|
41221
41495
|
log(`acp: mode set to ${modeId} (requested by the client)`);
|
|
@@ -41557,7 +41831,7 @@ function awaitCallback(pkce, port, timeoutMs) {
|
|
|
41557
41831
|
return;
|
|
41558
41832
|
}
|
|
41559
41833
|
const err = url.searchParams.get("error");
|
|
41560
|
-
const
|
|
41834
|
+
const code2 = url.searchParams.get("code");
|
|
41561
41835
|
const state = url.searchParams.get("state");
|
|
41562
41836
|
if (!stateMatches(pkce.state, state)) {
|
|
41563
41837
|
res.writeHead(400, { "content-type": "text/html" }).end(PAGE_FAIL);
|
|
@@ -41569,13 +41843,13 @@ function awaitCallback(pkce, port, timeoutMs) {
|
|
|
41569
41843
|
rejectResult(new Error(`authorization failed: ${err}`));
|
|
41570
41844
|
return;
|
|
41571
41845
|
}
|
|
41572
|
-
if (!
|
|
41846
|
+
if (!code2) {
|
|
41573
41847
|
res.writeHead(400, { "content-type": "text/html" }).end(PAGE_FAIL);
|
|
41574
41848
|
rejectResult(new Error("callback carried no authorization code"));
|
|
41575
41849
|
return;
|
|
41576
41850
|
}
|
|
41577
41851
|
res.writeHead(200, { "content-type": "text/html" }).end(PAGE_OK);
|
|
41578
|
-
resolveResult({ code });
|
|
41852
|
+
resolveResult({ code: code2 });
|
|
41579
41853
|
});
|
|
41580
41854
|
const timer = setTimeout(() => {
|
|
41581
41855
|
rejectResult(new Error("timed out waiting for the browser callback"));
|
|
@@ -41615,11 +41889,11 @@ async function login(deps) {
|
|
|
41615
41889
|
log("Opening your browser to sign in...");
|
|
41616
41890
|
log(url);
|
|
41617
41891
|
await deps.openBrowser(url);
|
|
41618
|
-
const { code } = await result;
|
|
41892
|
+
const { code: code2 } = await result;
|
|
41619
41893
|
const tokens = await deps.exchange({
|
|
41620
41894
|
issuer: deps.config.issuer,
|
|
41621
41895
|
clientId: deps.config.clientId,
|
|
41622
|
-
code,
|
|
41896
|
+
code: code2,
|
|
41623
41897
|
verifier: pkce.verifier,
|
|
41624
41898
|
redirectUri
|
|
41625
41899
|
});
|
|
@@ -41755,8 +42029,8 @@ async function main() {
|
|
|
41755
42029
|
if (rawArgv[0] === "login" || rawArgv[0] === "logout" || rawArgv[0] === "auth") {
|
|
41756
42030
|
const sub = rawArgv[0];
|
|
41757
42031
|
const parsed = parseArgs(rawArgv.slice(1));
|
|
41758
|
-
const
|
|
41759
|
-
process.exit(
|
|
42032
|
+
const code2 = sub === "login" ? await runLogin(parsed.stateDir) : sub === "logout" ? await runLogout(parsed.stateDir) : await runAuthStatus(parsed.stateDir);
|
|
42033
|
+
process.exit(code2);
|
|
41760
42034
|
}
|
|
41761
42035
|
if (rawArgv[0] === "acp-agent") {
|
|
41762
42036
|
await runAcpAgent(rawArgv.slice(1));
|
|
@@ -41767,7 +42041,21 @@ async function main() {
|
|
|
41767
42041
|
validateArgs(args);
|
|
41768
42042
|
const rootsController = createRootsController({ roots: args.roots, areas: args.addUnder, cloneAreas: args.cloneUnder });
|
|
41769
42043
|
const token = await loadOrCreateToken();
|
|
41770
|
-
|
|
42044
|
+
try {
|
|
42045
|
+
const note = (line) => {
|
|
42046
|
+
process.stdout.write(` ${line}
|
|
42047
|
+
`);
|
|
42048
|
+
};
|
|
42049
|
+
const migrated = await migrateHomeLayout();
|
|
42050
|
+
if (migrated.moved.length) {
|
|
42051
|
+
note(`layout: moved ${migrated.moved.length} item(s) into the current ~/${stateDirName()} layout`);
|
|
42052
|
+
}
|
|
42053
|
+
for (const left of migrated.skipped) note(`layout: left ${left.path} alone (${left.reason})`);
|
|
42054
|
+
} catch (e2) {
|
|
42055
|
+
process.stdout.write(` layout: migration skipped (${e2 instanceof Error ? e2.message : String(e2)})
|
|
42056
|
+
`);
|
|
42057
|
+
}
|
|
42058
|
+
const sidecarId = await loadOrCreateSidecarId(sidecarSubdir(args.stateDir));
|
|
41771
42059
|
const friendlyName = sidecarName({ name: args.name });
|
|
41772
42060
|
const hosts = makeHostsManifest(args.stateDir);
|
|
41773
42061
|
if (args.tlsCert && !args.tlsKey || !args.tlsCert && args.tlsKey) {
|
|
@@ -41795,6 +42083,8 @@ async function main() {
|
|
|
41795
42083
|
process.stdout.write(` [${(/* @__PURE__ */ new Date()).toTimeString().slice(0, 8)}] ${line}
|
|
41796
42084
|
`);
|
|
41797
42085
|
};
|
|
42086
|
+
await assertPortFree(probeAddress(args.host), args.port);
|
|
42087
|
+
await assertStateDirFree(sidecarSubdir(args.stateDir));
|
|
41798
42088
|
await checkHostsDrift(args.stateDir, hosts, slog);
|
|
41799
42089
|
const hostSync = makeHostSync({ stateDir: args.stateDir, manifest: hosts, log: slog });
|
|
41800
42090
|
const attendance = makeAttendanceRegistry({ log: slog });
|
|
@@ -41807,6 +42097,25 @@ async function main() {
|
|
|
41807
42097
|
roots: args.roots,
|
|
41808
42098
|
allowShell: args.allowShell,
|
|
41809
42099
|
defaultModel: args.defaultModel,
|
|
42100
|
+
/* Which approved root this agent works in.
|
|
42101
|
+
*
|
|
42102
|
+
* MEMBERSHIP, never a prefix test. A prefix check would accept
|
|
42103
|
+
* `<approved>/../elsewhere`, and the operator's `--root` list is the
|
|
42104
|
+
* whole security boundary here: an agent definition can be PUSHED from a
|
|
42105
|
+
* browser, so `settings.workRoot` is a request from a client the daemon
|
|
42106
|
+
* does not trust -- exactly like the clientId that resolveIdentity
|
|
42107
|
+
* refuses to take at face value.
|
|
42108
|
+
*
|
|
42109
|
+
* An unrecognised root falls back to the primary rather than refusing to
|
|
42110
|
+
* run. Removing a --root should not silently convert an agent into a
|
|
42111
|
+
* dead one; it runs where it always did, and the browser is what reports
|
|
42112
|
+
* the mismatch. */
|
|
42113
|
+
workRootFor: async (def, owner) => {
|
|
42114
|
+
const want = await deployedRoot(args.stateDir, owner, def.name) ?? def.settings.workRoots?.[sidecarId];
|
|
42115
|
+
if (want && args.roots.includes(want)) return want;
|
|
42116
|
+
if (want) slog(`crew: "${def.name}" asks for root "${want}", which is not approved \u2014 using ${args.roots[0]}`);
|
|
42117
|
+
return args.roots[0];
|
|
42118
|
+
},
|
|
41810
42119
|
onScheduleSelf: () => core?.wake(),
|
|
41811
42120
|
// The mirror's cue. Late-bound like `core` below: the runner is built
|
|
41812
42121
|
// before the control plane, and a session cannot be written before both
|
|
@@ -41829,9 +42138,11 @@ async function main() {
|
|
|
41829
42138
|
if (args.daemon && runner) {
|
|
41830
42139
|
const flipped = await runner.reconcilePendingOnBoot();
|
|
41831
42140
|
if (flipped > 0) slog(`daemon: marked ${flipped} stale pending permission record(s) dead (daemon restarted)`);
|
|
42141
|
+
const hostCrewDirs = (await listOwnerRoots(args.stateDir)).filter((r2) => r2.owner !== "").map((r2) => join15(hostsDirFor(args.stateDir, r2.owner), "crew"));
|
|
41832
42142
|
const watchDirs = [
|
|
41833
|
-
|
|
41834
|
-
...args.roots.map((r2) =>
|
|
42143
|
+
join15(args.stateDir, "crew"),
|
|
42144
|
+
...args.roots.map((r2) => join15(r2, stateDirName(), "crew")),
|
|
42145
|
+
...hostCrewDirs
|
|
41835
42146
|
];
|
|
41836
42147
|
core = startDaemonCore({
|
|
41837
42148
|
// Task 12: the stall watchdog scans the SAME runner's in-flight runs
|
|
@@ -41922,21 +42233,25 @@ async function main() {
|
|
|
41922
42233
|
...args.stateDir ? { stateDir: args.stateDir } : {},
|
|
41923
42234
|
log: slog
|
|
41924
42235
|
});
|
|
42236
|
+
let backendInfo;
|
|
41925
42237
|
if (controlPlane) {
|
|
41926
42238
|
const caps = await controlPlane.backend.probe();
|
|
41927
42239
|
if (caps.includes("sessions")) {
|
|
41928
42240
|
slog(`backend: ${args.endpoint} mirrors sessions (as "${friendlyName || sidecarId}")`);
|
|
41929
42241
|
const cred = await resolveCredential(process.env, args.stateDir);
|
|
42242
|
+
backendInfo = describeBackend(args.endpoint, true, cred);
|
|
41930
42243
|
if (!cred.token) {
|
|
41931
42244
|
slog("backend: NOT SIGNED IN \u2014 pushes will be refused until `corenel login` (or CORENEL_TOKEN)");
|
|
42245
|
+
} else if (cred.expired) {
|
|
42246
|
+
slog("backend: SIGN-IN EXPIRED \u2014 pushes will be refused until `corenel login`");
|
|
41932
42247
|
}
|
|
41933
42248
|
} else {
|
|
41934
42249
|
slog(`backend: ${args.endpoint} holds no sessions \u2014 working local-only`);
|
|
42250
|
+
backendInfo = describeBackend(args.endpoint, false, { token: null });
|
|
41935
42251
|
controlPlane.dispose();
|
|
41936
42252
|
controlPlane = null;
|
|
41937
42253
|
}
|
|
41938
42254
|
}
|
|
41939
|
-
await assertPortFree(probeAddress(args.host), args.port);
|
|
41940
42255
|
const procs = args.allowShell ? createNodeProcs({ roots: args.roots, log: slog }) : void 0;
|
|
41941
42256
|
const sessions = runner ? await wireSessions({
|
|
41942
42257
|
isolation: args.isolation,
|
|
@@ -42000,7 +42315,9 @@ async function main() {
|
|
|
42000
42315
|
billing: acpAuth.billing,
|
|
42001
42316
|
notes: [...acpAuth.notes]
|
|
42002
42317
|
}
|
|
42003
|
-
} : {}
|
|
42318
|
+
} : {},
|
|
42319
|
+
// Absent when this daemon has no backend at all — see backendInfo above.
|
|
42320
|
+
...backendInfo ? { backend: backendInfo } : {}
|
|
42004
42321
|
},
|
|
42005
42322
|
// The mutable root set. Present only so a client may add a working directory
|
|
42006
42323
|
// mid-session; with no --allow-add-under its areas are empty and every add is
|
|
@@ -42020,6 +42337,9 @@ async function main() {
|
|
|
42020
42337
|
},
|
|
42021
42338
|
hostSync
|
|
42022
42339
|
});
|
|
42340
|
+
await writePidFile(sidecarSubdir(args.stateDir), args.port).catch((e2) => {
|
|
42341
|
+
slog(`state dir: could not record this daemon's pid (${e2 instanceof Error ? e2.message : String(e2)})`);
|
|
42342
|
+
});
|
|
42023
42343
|
let tunnelSup = null;
|
|
42024
42344
|
let tunnelWss = null;
|
|
42025
42345
|
if (tunnelProvider) {
|
|
@@ -42081,6 +42401,7 @@ async function main() {
|
|
|
42081
42401
|
openBrowser2(target);
|
|
42082
42402
|
}
|
|
42083
42403
|
const shutdown = () => {
|
|
42404
|
+
void clearPidFile(sidecarSubdir(args.stateDir));
|
|
42084
42405
|
procs?.shutdown();
|
|
42085
42406
|
const alive = sessions?.backend.list() ?? [];
|
|
42086
42407
|
if (alive.length) {
|
|
@@ -42097,8 +42418,8 @@ async function main() {
|
|
|
42097
42418
|
process.on("SIGTERM", shutdown);
|
|
42098
42419
|
}
|
|
42099
42420
|
main().catch((e2) => {
|
|
42100
|
-
const
|
|
42101
|
-
const msg =
|
|
42421
|
+
const code2 = e2?.code;
|
|
42422
|
+
const msg = code2 === "EADDRINUSE" ? `port already in use \u2014 another sidecar may be running. Stop it, or pass --port <n>.` : e2 instanceof Error ? e2.message : String(e2);
|
|
42102
42423
|
process.stderr.write(`corenel-sidecar: ${msg}
|
|
42103
42424
|
`);
|
|
42104
42425
|
process.exit(1);
|