@corenel/sidecar 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/LICENSE +93 -0
  2. package/dist/cli.js +608 -359
  3. package/package.json +5 -5
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 code = json.substr(i2 + 2, 4);
1188
- switch (code) {
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 (code.substr(0, 2) === "00")
1215
- str8 += "\\x" + code.substr(2);
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, code, message) {
3714
+ constructor(name, pos, code2, message) {
3715
3715
  super();
3716
3716
  this.name = name;
3717
- this.code = 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, code, message) {
3724
- super("YAMLParseError", pos, code, message);
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, code, message) {
3729
- super("YAMLWarning", pos, code, message);
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, code, msg) => onError(offset + rel, code, msg);
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 code = ok ? parseInt(cc, 16) : NaN;
4830
+ const code2 = ok ? parseInt(cc, 16) : NaN;
4831
4831
  try {
4832
- return String.fromCodePoint(code);
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, code, message, warning) => {
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, code, message));
5157
+ this.warnings.push(new errors.YAMLWarning(pos, code2, message));
5158
5158
  else
5159
- this.errors.push(new errors.YAMLParseError(pos, code, message));
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, code, message) => {
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, code, message);
5324
+ onError(offset, code2, message);
5325
5325
  else
5326
- throw new errors.YAMLParseError([offset, offset + 1], code, message);
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 fs13 = this.flowScalar(this.type);
6907
+ const fs15 = this.flowScalar(this.type);
6908
6908
  if (atNextItem || it2.value) {
6909
- map.items.push({ start, key: fs13, sep: [] });
6909
+ map.items.push({ start, key: fs15, sep: [] });
6910
6910
  this.onKeyLine = true;
6911
6911
  } else if (it2.sep) {
6912
- this.stack.push(fs13);
6912
+ this.stack.push(fs15);
6913
6913
  } else {
6914
- Object.assign(it2, { key: fs13, sep: [] });
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 fs13 = this.flowScalar(this.type);
7042
+ const fs15 = this.flowScalar(this.type);
7043
7043
  if (!it2 || it2.value)
7044
- fc.items.push({ start: [], key: fs13, sep: [] });
7044
+ fc.items.push({ start: [], key: fs15, sep: [] });
7045
7045
  else if (it2.sep)
7046
- this.stack.push(fs13);
7046
+ this.stack.push(fs15);
7047
7047
  else
7048
- Object.assign(it2, { key: fs13, sep: [] });
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(code) {
12252
- this.codebuf.push(code);
12251
+ _proto._emit = function _emit(code2) {
12252
+ this.codebuf.push(code2);
12253
12253
  };
12254
- _proto._emitLine = function _emitLine(code) {
12255
- this._emit(code + "\n");
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 join14(arr, del2, attr) {
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 = join14;
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 fs13 = __require("fs");
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(fs13.existsSync);
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 && fs13.existsSync(p2)) {
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: fs13.readFileSync(fullpath, "utf-8"),
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: fs13.readFileSync(fullpath, "utf-8"),
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 fs13 = __require("fs");
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 = fs13.existsSync(input) && fs13.statSync(input);
14616
+ var pathStats = fs15.existsSync(input) && fs15.statSync(input);
14617
14617
  var precompiled = [];
14618
14618
  var templates = [];
14619
14619
  function addTemplates(dir) {
14620
- fs13.readdirSync(dir).forEach(function(file) {
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 = fs13.statSync(filepath);
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(fs13.readFileSync(input, "utf-8"), opts.name || input, env));
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(fs13.readFileSync(templates[i2], "utf-8"), name, env));
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(code) {
18036
- return code === 301 || code === 302 || code === 303 || code === 307 || code === 308;
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 fs7 } from "fs";
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 fs7.stat(path);
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 fs7.stat(__classPrivateFieldGet5(this, _FileFromPath_path, "f"));
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 join13 } from "node:path";
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/token.ts
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 join(homedir(), stateDirName());
22580
+ return join2(homedir(), stateDirName());
22581
+ }
22582
+ function sidecarSubdir(stateDir = corenelDir()) {
22583
+ return join2(stateDir, "sidecar");
22517
22584
  }
22518
22585
  function tokenPath() {
22519
- return join(corenelDir(), "daemon.token");
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 fs.readFile(path, "utf8")).trim();
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 fs.mkdir(corenelDir(), { recursive: true });
22599
+ await fs2.mkdir(sidecarSubdir(), { recursive: true });
22533
22600
  const token = randomBytes(32).toString("hex");
22534
- await fs.writeFile(path, token + "\n", { mode: 384 });
22601
+ await fs2.writeFile(path, token + "\n", { mode: 384 });
22535
22602
  try {
22536
- await fs.chmod(path, 384);
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 fs13 = await deps.files();
23146
- if (!fs13) return "";
23212
+ const fs15 = await deps.files();
23213
+ if (!fs15) return "";
23147
23214
  try {
23148
- return await fs13.read(path);
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 fs2 } from "node:fs";
23243
+ import { promises as fs3 } from "node:fs";
23177
23244
  import { homedir as homedir2 } from "node:os";
23178
- import { join as join2 } from "node:path";
23245
+ import { join as join3 } from "node:path";
23179
23246
  var TOKEN_ENV = "CORENEL_TOKEN";
23180
23247
  function authDir(stateDir) {
23181
- return stateDir ?? join2(homedir2(), stateDirName());
23248
+ return stateDir ?? join3(homedir2(), stateDirName());
23182
23249
  }
23183
23250
  function credentialPath(stateDir) {
23184
- return join2(authDir(stateDir), "auth.json");
23251
+ return join3(authDir(stateDir), "auth.json");
23185
23252
  }
23186
23253
  async function saveCredential(cred, stateDir) {
23187
23254
  const path = credentialPath(stateDir);
23188
- await fs2.mkdir(authDir(stateDir), { recursive: true });
23189
- await fs2.writeFile(path, JSON.stringify(cred, null, 2) + "\n", { mode: 384 });
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 fs2.chmod(path, 384);
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 fs2.readFile(credentialPath(stateDir), "utf8");
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 fs2.rm(credentialPath(stateDir), { force: true });
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();
@@ -23265,7 +23332,7 @@ function makeControlPlane(deps) {
23265
23332
  }
23266
23333
 
23267
23334
  // src/tools.ts
23268
- import { promises as fs3 } from "node:fs";
23335
+ import { promises as fs4 } from "node:fs";
23269
23336
  import { spawn } from "node:child_process";
23270
23337
  import { resolve, relative, isAbsolute, sep, dirname } from "node:path";
23271
23338
  function withinRoot(root, abs) {
@@ -23282,11 +23349,11 @@ function confine(roots, rootArg, p2) {
23282
23349
  return abs;
23283
23350
  }
23284
23351
  async function assertRealWithinRoot(roots, abs) {
23285
- const realRoots = await Promise.all(roots.map((r2) => fs3.realpath(r2)));
23352
+ const realRoots = await Promise.all(roots.map((r2) => fs4.realpath(r2)));
23286
23353
  let probe = abs;
23287
23354
  for (; ; ) {
23288
23355
  try {
23289
- const real = await fs3.realpath(probe);
23356
+ const real = await fs4.realpath(probe);
23290
23357
  if (!realRoots.some((rr2) => withinRoot(rr2, real))) throw new Error(`path escapes the sidecar roots via symlink: ${abs}`);
23291
23358
  return;
23292
23359
  } catch (e2) {
@@ -23314,11 +23381,11 @@ function nodeFsTools(roots) {
23314
23381
  async run(args) {
23315
23382
  const abs = confine(roots, args.root, String(args.path ?? ""));
23316
23383
  await assertRealWithinRoot(roots, abs);
23317
- const st2 = await fs3.stat(abs);
23384
+ const st2 = await fs4.stat(abs);
23318
23385
  if (st2.size > MAX_READ_BYTES) {
23319
23386
  throw new Error(`file too large to read (${st2.size} bytes; max ${MAX_READ_BYTES})`);
23320
23387
  }
23321
- return await fs3.readFile(abs, "utf8");
23388
+ return await fs4.readFile(abs, "utf8");
23322
23389
  }
23323
23390
  },
23324
23391
  {
@@ -23338,9 +23405,9 @@ function nodeFsTools(roots) {
23338
23405
  mutates: true,
23339
23406
  async run(args) {
23340
23407
  const abs = confine(roots, args.root, String(args.path ?? ""));
23341
- await fs3.mkdir(dirname(abs), { recursive: true });
23408
+ await fs4.mkdir(dirname(abs), { recursive: true });
23342
23409
  await assertRealWithinRoot(roots, abs);
23343
- await fs3.writeFile(abs, String(args.content ?? ""), "utf8");
23410
+ await fs4.writeFile(abs, String(args.content ?? ""), "utf8");
23344
23411
  return `wrote ${args.path}`;
23345
23412
  }
23346
23413
  },
@@ -23357,11 +23424,11 @@ function nodeFsTools(roots) {
23357
23424
  async run(args) {
23358
23425
  const abs = confine(roots, args.root, String(args.path ?? ""));
23359
23426
  await assertRealWithinRoot(roots, abs);
23360
- const st2 = await fs3.stat(abs);
23427
+ const st2 = await fs4.stat(abs);
23361
23428
  if (st2.size > MAX_READ_BYTES) {
23362
23429
  throw new Error(`file too large to read (${st2.size} bytes; max ${MAX_READ_BYTES})`);
23363
23430
  }
23364
- return (await fs3.readFile(abs)).toString("base64");
23431
+ return (await fs4.readFile(abs)).toString("base64");
23365
23432
  }
23366
23433
  },
23367
23434
  {
@@ -23376,7 +23443,7 @@ function nodeFsTools(roots) {
23376
23443
  async run(args) {
23377
23444
  const abs = confine(roots, args.root, String(args.path ?? "."));
23378
23445
  await assertRealWithinRoot(roots, abs);
23379
- const entries = await fs3.readdir(abs, { withFileTypes: true });
23446
+ const entries = await fs4.readdir(abs, { withFileTypes: true });
23380
23447
  return entries.map((e2) => e2.isDirectory() ? `${e2.name}/` : e2.name).join("\n");
23381
23448
  }
23382
23449
  },
@@ -23395,7 +23462,7 @@ function nodeFsTools(roots) {
23395
23462
  const abs = confine(roots, args.root, String(args.path ?? ""));
23396
23463
  await assertRealWithinRoot(roots, abs);
23397
23464
  if (roots.some((r2) => resolve(r2) === abs)) throw new Error("refusing to delete a sidecar root itself");
23398
- await fs3.rm(abs, { recursive: true, force: true });
23465
+ await fs4.rm(abs, { recursive: true, force: true });
23399
23466
  return `deleted ${args.path}`;
23400
23467
  }
23401
23468
  }
@@ -23458,9 +23525,9 @@ function runShell(command, cwd, timeout) {
23458
23525
  clearTimeout(timer);
23459
23526
  resolveOut(`Error: failed to start command: ${e2 instanceof Error ? e2.message : String(e2)}`);
23460
23527
  });
23461
- child.on("close", (code, signal) => {
23528
+ child.on("close", (code2, signal) => {
23462
23529
  clearTimeout(timer);
23463
- const status = timedOut ? `timed out after ${timeout}ms (killed)` : `exit ${code ?? "null"}${signal ? ` (signal ${signal})` : ""}`;
23530
+ const status = timedOut ? `timed out after ${timeout}ms (killed)` : `exit ${code2 ?? "null"}${signal ? ` (signal ${signal})` : ""}`;
23464
23531
  const body = out.trim() || "(no output)";
23465
23532
  resolveOut(`${status}
23466
23533
  ${truncated ? "[output truncated]\n" : ""}${body}`);
@@ -23534,20 +23601,20 @@ function createNodeProcs(opts) {
23534
23601
  opts.log?.(`proc ${info.procId} spawn: ${line}`);
23535
23602
  child.stdout.on("data", (b2) => push2(entry, "out", b2.toString("utf8")));
23536
23603
  child.stderr.on("data", (b2) => push2(entry, "err", b2.toString("utf8")));
23537
- const settle = (code, signal, failure) => {
23604
+ const settle = (code2, signal, failure) => {
23538
23605
  if (!entry.info.running) return;
23539
23606
  if (failure) push2(entry, "err", failure);
23540
23607
  entry.info.running = false;
23541
23608
  entry.info.endedAt = Date.now();
23542
- entry.info.exitCode = code;
23609
+ entry.info.exitCode = code2;
23543
23610
  if (signal) entry.info.signal = signal;
23544
23611
  entry.child = null;
23545
- opts.log?.(`proc ${entry.info.procId} exit ${code ?? "null"}${signal ? ` (${signal})` : ""}`);
23612
+ opts.log?.(`proc ${entry.info.procId} exit ${code2 ?? "null"}${signal ? ` (${signal})` : ""}`);
23546
23613
  emit({ procId: entry.info.procId, kind: "exit", info: { ...entry.info } });
23547
23614
  evictExited();
23548
23615
  };
23549
23616
  child.on("error", (err) => settle(null, null, `failed to start: ${err instanceof Error ? err.message : String(err)}`));
23550
- child.on("close", (code, signal) => settle(code, signal));
23617
+ child.on("close", (code2, signal) => settle(code2, signal));
23551
23618
  return { ...info };
23552
23619
  },
23553
23620
  list() {
@@ -23623,22 +23690,22 @@ function isNotFound(e2) {
23623
23690
  const msg = String(e2?.message ?? e2);
23624
23691
  return name === "NotFoundError" || /not\s*found|no such|enoent/i.test(msg);
23625
23692
  }
23626
- function fileServicePackageStore(fs13) {
23627
- const remove = (path) => fs13.remove(path).catch((e2) => {
23693
+ function fileServicePackageStore(fs15) {
23694
+ const remove = (path) => fs15.remove(path).catch((e2) => {
23628
23695
  if (!isNotFound(e2)) throw e2;
23629
23696
  });
23630
23697
  return {
23631
- writeFile: (path, content) => fs13.write(path, content),
23698
+ writeFile: (path, content) => fs15.write(path, content),
23632
23699
  // Present only when the backend supports binary — core feature-detects it,
23633
23700
  // and advertising a method that throws is worse than not advertising it.
23634
- ...fs13.writeBytes ? { writeBytes: (path, bytes) => fs13.writeBytes(path, bytes) } : {},
23701
+ ...fs15.writeBytes ? { writeBytes: (path, bytes) => fs15.writeBytes(path, bytes) } : {},
23635
23702
  removeDir: remove,
23636
23703
  removeFile: remove,
23637
- readFile: (path) => fs13.read(path).catch(() => null),
23638
- ...fs13.readBytes ? { readBytes: (path) => fs13.readBytes(path) } : {},
23704
+ readFile: (path) => fs15.read(path).catch(() => null),
23705
+ ...fs15.readBytes ? { readBytes: (path) => fs15.readBytes(path) } : {},
23639
23706
  readdir: async (path) => {
23640
23707
  try {
23641
- const nodes = fs13.listDir ? await fs13.listDir(path) : await fs13.list(path);
23708
+ const nodes = fs15.listDir ? await fs15.listDir(path) : await fs15.list(path);
23642
23709
  return nodes.map((n2) => n2.name);
23643
23710
  } catch {
23644
23711
  return [];
@@ -23740,9 +23807,9 @@ var SessionPackages = class {
23740
23807
  return !!this.deps.installer;
23741
23808
  }
23742
23809
  async filesOrThrow(sessionId) {
23743
- const fs13 = await this.deps.filesFor(sessionId);
23744
- if (!fs13) throw new Error(`no such session: ${sessionId}`);
23745
- return fs13;
23810
+ const fs15 = await this.deps.filesFor(sessionId);
23811
+ if (!fs15) throw new Error(`no such session: ${sessionId}`);
23812
+ return fs15;
23746
23813
  }
23747
23814
  installerOrThrow() {
23748
23815
  const i2 = this.deps.installer;
@@ -23750,13 +23817,13 @@ var SessionPackages = class {
23750
23817
  return i2;
23751
23818
  }
23752
23819
  async readLock(sessionId) {
23753
- const fs13 = await this.filesOrThrow(sessionId);
23754
- const text = await fs13.read(LOCKFILE_PATH).catch(() => null);
23820
+ const fs15 = await this.filesOrThrow(sessionId);
23821
+ const text = await fs15.read(LOCKFILE_PATH).catch(() => null);
23755
23822
  return (text != null ? parseLock(text) : null) ?? emptyLock();
23756
23823
  }
23757
23824
  async writeLock(sessionId, lock) {
23758
- const fs13 = await this.filesOrThrow(sessionId);
23759
- await fs13.write(LOCKFILE_PATH, serializeLock(lock));
23825
+ const fs15 = await this.filesOrThrow(sessionId);
23826
+ await fs15.write(LOCKFILE_PATH, serializeLock(lock));
23760
23827
  }
23761
23828
  /**
23762
23829
  * Install one package into the session and pin what resolved.
@@ -23767,11 +23834,11 @@ var SessionPackages = class {
23767
23834
  */
23768
23835
  async install(sessionId, ref, type) {
23769
23836
  const installer = this.installerOrThrow();
23770
- const fs13 = await this.filesOrThrow(sessionId);
23837
+ const fs15 = await this.filesOrThrow(sessionId);
23771
23838
  const installed = await installer.install({
23772
23839
  ref,
23773
23840
  root: "",
23774
- store: fileServicePackageStore(fs13),
23841
+ store: fileServicePackageStore(fs15),
23775
23842
  ...type ? { type } : {}
23776
23843
  });
23777
23844
  try {
@@ -23789,8 +23856,8 @@ var SessionPackages = class {
23789
23856
  }
23790
23857
  async uninstall(sessionId, ref) {
23791
23858
  const installer = this.installerOrThrow();
23792
- const fs13 = await this.filesOrThrow(sessionId);
23793
- await installer.uninstall({ ref, root: "", store: fileServicePackageStore(fs13) });
23859
+ const fs15 = await this.filesOrThrow(sessionId);
23860
+ await installer.uninstall({ ref, root: "", store: fileServicePackageStore(fs15) });
23794
23861
  const name = ref.replace(/@[^@/]*$/, "") || ref;
23795
23862
  try {
23796
23863
  await this.writeLock(sessionId, withoutPackage(await this.readLock(sessionId), name));
@@ -23807,9 +23874,9 @@ var SessionPackages = class {
23807
23874
  */
23808
23875
  async installFromLock(sessionId) {
23809
23876
  const installer = this.installerOrThrow();
23810
- const fs13 = await this.filesOrThrow(sessionId);
23877
+ const fs15 = await this.filesOrThrow(sessionId);
23811
23878
  const lock = await this.readLock(sessionId);
23812
- const store = fileServicePackageStore(fs13);
23879
+ const store = fileServicePackageStore(fs15);
23813
23880
  const done = [];
23814
23881
  for (const pin of lock.packages) {
23815
23882
  done.push(await installer.install({ ref: `${pin.name}@${pin.version}`, root: "", store }));
@@ -24068,8 +24135,8 @@ var SessionManager = class {
24068
24135
  import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve3 } from "node:path";
24069
24136
 
24070
24137
  // ../tools-node/fileService.ts
24071
- import { promises as fs4 } from "node:fs";
24072
- import { join as join3, resolve as resolve2, relative as relative2, dirname as dirname2, sep as sep2, basename, isAbsolute as isAbsolute2 } from "node:path";
24138
+ import { promises as fs5 } from "node:fs";
24139
+ import { join as join4, resolve as resolve2, relative as relative2, dirname as dirname2, sep as sep2, basename, isAbsolute as isAbsolute2 } from "node:path";
24073
24140
  var SKIP = /* @__PURE__ */ new Set([
24074
24141
  "node_modules",
24075
24142
  ".git",
@@ -24092,7 +24159,7 @@ var COVERAGE_REPORT_MARKERS = /* @__PURE__ */ new Set([
24092
24159
  ]);
24093
24160
  async function looksLikeGeneratedCoverage(absDir) {
24094
24161
  try {
24095
- const names = await fs4.readdir(absDir);
24162
+ const names = await fs5.readdir(absDir);
24096
24163
  return names.some((n2) => COVERAGE_REPORT_MARKERS.has(n2));
24097
24164
  } catch {
24098
24165
  return false;
@@ -24135,11 +24202,11 @@ var NodeFileService = class {
24135
24202
  * root pointing outside it. Resolve the real path (or nearest existing ancestor,
24136
24203
  * for a not-yet-created write target) and assert it stays within the real root. */
24137
24204
  async assertReal(a2) {
24138
- const realRoot = await fs4.realpath(this.root);
24205
+ const realRoot = await fs5.realpath(this.root);
24139
24206
  let probe = a2;
24140
24207
  for (; ; ) {
24141
24208
  try {
24142
- const real = await fs4.realpath(probe);
24209
+ const real = await fs5.realpath(probe);
24143
24210
  const rel = relative2(realRoot, real);
24144
24211
  if (escapesRoot(rel)) {
24145
24212
  throw new Error(`path escapes the workspace root via symlink: ${a2}`);
@@ -24163,21 +24230,21 @@ var NodeFileService = class {
24163
24230
  const walk = async (absDir) => {
24164
24231
  let entries;
24165
24232
  try {
24166
- entries = await fs4.readdir(absDir, { withFileTypes: true });
24233
+ entries = await fs5.readdir(absDir, { withFileTypes: true });
24167
24234
  } catch {
24168
24235
  return [];
24169
24236
  }
24170
24237
  const out = [];
24171
24238
  for (const e2 of entries.sort((a2, b2) => a2.name.localeCompare(b2.name))) {
24172
24239
  if (SKIP.has(e2.name)) continue;
24173
- const absChild = join3(absDir, e2.name);
24240
+ const absChild = join4(absDir, e2.name);
24174
24241
  const path = normalizeFilePath(relative2(this.root, absChild));
24175
24242
  if (e2.isDirectory() && SKIP_PATHS.has(path)) continue;
24176
24243
  if (e2.isDirectory() && e2.name === "coverage" && await looksLikeGeneratedCoverage(absChild)) continue;
24177
24244
  if (e2.isDirectory()) {
24178
24245
  out.push({ name: e2.name, path, kind: "directory", children: await walk(absChild) });
24179
24246
  } else {
24180
- const st2 = await fs4.stat(absChild).catch(() => null);
24247
+ const st2 = await fs5.stat(absChild).catch(() => null);
24181
24248
  out.push({ name: e2.name, path, kind: "file", size: st2?.size, ext: extOf(e2.name) });
24182
24249
  }
24183
24250
  }
@@ -24186,11 +24253,11 @@ var NodeFileService = class {
24186
24253
  return walk(this.abs(dir));
24187
24254
  }
24188
24255
  async read(path) {
24189
- return fs4.readFile(await this.realAbs(path), "utf8");
24256
+ return fs5.readFile(await this.realAbs(path), "utf8");
24190
24257
  }
24191
24258
  async stat(path) {
24192
24259
  try {
24193
- const st2 = await fs4.stat(this.abs(path));
24260
+ const st2 = await fs5.stat(this.abs(path));
24194
24261
  return { path: normalizeFilePath(path), kind: st2.isDirectory() ? "directory" : "file", size: st2.size, ext: extOf(path) };
24195
24262
  } catch {
24196
24263
  return null;
@@ -24198,38 +24265,38 @@ var NodeFileService = class {
24198
24265
  }
24199
24266
  async write(path, content) {
24200
24267
  const a2 = this.abs(path);
24201
- await fs4.mkdir(dirname2(a2), { recursive: true });
24268
+ await fs5.mkdir(dirname2(a2), { recursive: true });
24202
24269
  await this.assertReal(a2);
24203
- await fs4.writeFile(a2, content, "utf8");
24270
+ await fs5.writeFile(a2, content, "utf8");
24204
24271
  }
24205
24272
  async writeBytes(path, bytes) {
24206
24273
  const a2 = this.abs(path);
24207
- await fs4.mkdir(dirname2(a2), { recursive: true });
24274
+ await fs5.mkdir(dirname2(a2), { recursive: true });
24208
24275
  await this.assertReal(a2);
24209
- await fs4.writeFile(a2, bytes);
24276
+ await fs5.writeFile(a2, bytes);
24210
24277
  }
24211
24278
  async create(path, content = "") {
24212
24279
  const a2 = this.abs(path);
24213
- await fs4.mkdir(dirname2(a2), { recursive: true });
24280
+ await fs5.mkdir(dirname2(a2), { recursive: true });
24214
24281
  await this.assertReal(a2);
24215
24282
  try {
24216
- await fs4.writeFile(a2, content, { flag: "wx" });
24283
+ await fs5.writeFile(a2, content, { flag: "wx" });
24217
24284
  } catch (e2) {
24218
24285
  if (e2.code === "EEXIST") throw new Error(`already exists: ${path}`);
24219
24286
  throw e2;
24220
24287
  }
24221
24288
  }
24222
24289
  async remove(path) {
24223
- await fs4.rm(await this.realAbs(path), { recursive: true, force: true });
24290
+ await fs5.rm(await this.realAbs(path), { recursive: true, force: true });
24224
24291
  }
24225
24292
  async rename(from, to) {
24226
24293
  const t2 = this.abs(to);
24227
- await fs4.mkdir(dirname2(t2), { recursive: true });
24294
+ await fs5.mkdir(dirname2(t2), { recursive: true });
24228
24295
  await this.assertReal(t2);
24229
- await fs4.rename(await this.realAbs(from), t2);
24296
+ await fs5.rename(await this.realAbs(from), t2);
24230
24297
  }
24231
24298
  async mkdir(path) {
24232
- await fs4.mkdir(this.abs(path), { recursive: true });
24299
+ await fs5.mkdir(this.abs(path), { recursive: true });
24233
24300
  }
24234
24301
  async search(query) {
24235
24302
  const q2 = query.toLowerCase();
@@ -24429,13 +24496,13 @@ function makeMsbStreamer(bin, wrap) {
24429
24496
  const done = new Promise((r2) => {
24430
24497
  resolveDone = r2;
24431
24498
  });
24432
- const settle = (code) => {
24499
+ const settle = (code2) => {
24433
24500
  if (settled) return;
24434
24501
  settled = true;
24435
24502
  for (const l2 of out.flush()) onLine(l2, "out");
24436
24503
  for (const l2 of err.flush()) onLine(l2, "err");
24437
24504
  clearTimeout(timer);
24438
- resolveDone({ code, stdout: "", stderr: stderrText });
24505
+ resolveDone({ code: code2, stdout: "", stderr: stderrText });
24439
24506
  };
24440
24507
  const timer = opts?.timeoutMs === void 0 ? void 0 : setTimeout(() => {
24441
24508
  child.kill("SIGKILL");
@@ -24455,7 +24522,7 @@ function makeMsbStreamer(bin, wrap) {
24455
24522
  ${e2 instanceof Error ? e2.message : String(e2)}`;
24456
24523
  settle(-1);
24457
24524
  });
24458
- child.on("close", (code) => settle(code ?? -1));
24525
+ child.on("close", (code2) => settle(code2 ?? -1));
24459
24526
  return {
24460
24527
  stop: () => {
24461
24528
  if (!settled) child.kill("SIGTERM");
@@ -24498,9 +24565,9 @@ msb timed out after ${opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS2}ms` });
24498
24565
  settle({ code: -1, stdout, stderr: `${stderr}
24499
24566
  ${e2 instanceof Error ? e2.message : String(e2)}` });
24500
24567
  });
24501
- child.on("close", (code) => {
24568
+ child.on("close", (code2) => {
24502
24569
  clearTimeout(timer);
24503
- settle({ code: code ?? -1, stdout, stderr });
24570
+ settle({ code: code2 ?? -1, stdout, stderr });
24504
24571
  });
24505
24572
  });
24506
24573
  }
@@ -24675,9 +24742,9 @@ function makeMicrovmBackend(deps) {
24675
24742
  if (!live.length) return;
24676
24743
  const r2 = await deps.run(listArgs());
24677
24744
  if (r2.code !== 0) return;
24678
- const present = new Set(parseSandboxList(r2.stdout).map((row) => row.name));
24745
+ const present2 = new Set(parseSandboxList(r2.stdout).map((row) => row.name));
24679
24746
  for (const s2 of live) {
24680
- if (present.has(sandboxName(s2.id))) continue;
24747
+ if (present2.has(sandboxName(s2.id))) continue;
24681
24748
  if (at2 - s2.createdAt < RECONCILE_MS) continue;
24682
24749
  sessions.delete(s2.id);
24683
24750
  deps.log?.(`session ${s2.id} ended outside this daemon (sandbox ${sandboxName(s2.id)} is gone) \u2014 dropped`);
@@ -25148,11 +25215,11 @@ function makeGuestRunner(deps) {
25148
25215
  onEvent(e2);
25149
25216
  }
25150
25217
  };
25151
- const finish = (code, stderr) => {
25218
+ const finish = (code2, stderr) => {
25152
25219
  if (skipped) deps.log?.(`session ${sessionId}: ${skipped} unparseable line(s) from the guest were skipped`);
25153
- if (code !== 0) {
25220
+ if (code2 !== 0) {
25154
25221
  const why = stderr.trim().split(NEWLINE).slice(-3).join("; ");
25155
- return { text: finalText(seen), stopReason: `error: guest exit ${code}${why ? ` \u2014 ${why}` : ""}` };
25222
+ return { text: finalText(seen), stopReason: `error: guest exit ${code2}${why ? ` \u2014 ${why}` : ""}` };
25156
25223
  }
25157
25224
  return { text: finalText(seen), stopReason: "done" };
25158
25225
  };
@@ -25291,18 +25358,21 @@ function makeSessionsPort(deps) {
25291
25358
  }
25292
25359
 
25293
25360
  // ../tools-node/auth.ts
25294
- import { promises as fs5 } from "node:fs";
25361
+ import { promises as fs6 } from "node:fs";
25295
25362
  import { homedir as homedir3 } from "node:os";
25296
- import { join as join4 } from "node:path";
25297
- function corenelDir2() {
25298
- return join4(homedir3(), stateDirName());
25363
+ import { join as join5 } from "node:path";
25364
+ function corenelDir2(base = homedir3()) {
25365
+ return join5(base, stateDirName());
25299
25366
  }
25300
- function authTokenPath() {
25301
- return join4(corenelDir2(), "token");
25367
+ function sidecarDir(base = homedir3()) {
25368
+ return join5(corenelDir2(base), "sidecar");
25369
+ }
25370
+ function authTokenPath(base = homedir3()) {
25371
+ return join5(corenelDir2(base), "token");
25302
25372
  }
25303
25373
  async function readAuthToken() {
25304
25374
  try {
25305
- const t2 = (await fs5.readFile(authTokenPath(), "utf8")).trim();
25375
+ const t2 = (await fs6.readFile(authTokenPath(), "utf8")).trim();
25306
25376
  return t2 || null;
25307
25377
  } catch {
25308
25378
  return null;
@@ -27067,12 +27137,12 @@ var TemplateProcessingStage = class {
27067
27137
  */
27068
27138
  async loadPackageResource(context, packagePath, resourcePath, prefix) {
27069
27139
  try {
27070
- const fs13 = context.fileSystem;
27140
+ const fs15 = context.fileSystem;
27071
27141
  const possibleExtensions = ["", ".prmd", ".md", ".txt"];
27072
27142
  for (const ext of possibleExtensions) {
27073
27143
  const filePath = resolvePackageFile(packagePath, resourcePath + ext);
27074
- if (await fs13.exists(filePath)) {
27075
- const contentData = await fs13.readFile(filePath);
27144
+ if (await fs15.exists(filePath)) {
27145
+ const contentData = await fs15.readFile(filePath);
27076
27146
  if (isPrompdFile(filePath)) {
27077
27147
  const parser2 = new PrompdParser();
27078
27148
  try {
@@ -27106,16 +27176,16 @@ var TemplateProcessingStage = class {
27106
27176
  return content;
27107
27177
  }
27108
27178
  try {
27109
- const fs13 = context.fileSystem;
27179
+ const fs15 = context.fileSystem;
27110
27180
  const parser2 = new PrompdParser();
27111
27181
  let parentFile = null;
27112
- if (isPrompdFile(parentPath) && await fs13.exists(parentPath)) {
27182
+ if (isPrompdFile(parentPath) && await fs15.exists(parentPath)) {
27113
27183
  parentFile = parentPath;
27114
- } else if (await fs13.exists(parentPath) && await fs13.isDirectory(parentPath)) {
27115
- const files = (await fs13.readdir(parentPath)).filter((f2) => isPrompdFile(f2));
27184
+ } else if (await fs15.exists(parentPath) && await fs15.isDirectory(parentPath)) {
27185
+ const files = (await fs15.readdir(parentPath)).filter((f2) => isPrompdFile(f2));
27116
27186
  if (files.length > 0) {
27117
27187
  const mainFile = files.find((f2) => f2 === "main.prmd" || f2 === "main.md");
27118
- parentFile = fs13.join(parentPath, mainFile || files[0]);
27188
+ parentFile = fs15.join(parentPath, mainFile || files[0]);
27119
27189
  }
27120
27190
  }
27121
27191
  if (!parentFile) {
@@ -27129,14 +27199,14 @@ var TemplateProcessingStage = class {
27129
27199
  });
27130
27200
  return content;
27131
27201
  }
27132
- const parentFileContent = await fs13.readFile(parentFile);
27202
+ const parentFileContent = await fs15.readFile(parentFile);
27133
27203
  const parentData = parser2.parseContent(parentFileContent);
27134
- await this.validateParentFileReferences(context, parentData.metadata, fs13, parentFile);
27204
+ await this.validateParentFileReferences(context, parentData.metadata, fs15, parentFile);
27135
27205
  if (context.hasErrors()) {
27136
27206
  return content;
27137
27207
  }
27138
27208
  if (parentData.content) {
27139
- const parentDir = fs13.dirname(parentFile);
27209
+ const parentDir = fs15.dirname(parentFile);
27140
27210
  parentData.content = this.resolveIncludePaths(parentData.content, parentDir);
27141
27211
  }
27142
27212
  const overrides = context.metadata?.override || {};
@@ -27687,10 +27757,10 @@ ${content}` : parentData.content;
27687
27757
  * Called during inheritance processing because AssetExtractionStage only runs on
27688
27758
  * the child file's metadata — the parent is only parsed, never fully compiled.
27689
27759
  */
27690
- async validateParentFileReferences(context, metadata, fs13, parentFile) {
27760
+ async validateParentFileReferences(context, metadata, fs15, parentFile) {
27691
27761
  if (!metadata) return;
27692
27762
  const metadataAsRecord = metadata;
27693
- const parentDir = fs13.dirname(parentFile);
27763
+ const parentDir = fs15.dirname(parentFile);
27694
27764
  const fileFields = ["system", "task", "user", "assistant", "response", "output", "context"];
27695
27765
  for (const field of fileFields) {
27696
27766
  const fieldValue = metadataAsRecord[field];
@@ -27698,8 +27768,8 @@ ${content}` : parentData.content;
27698
27768
  const refs = Array.isArray(fieldValue) ? fieldValue.filter((v2) => typeof v2 === "string") : typeof fieldValue === "string" ? [fieldValue] : [];
27699
27769
  for (const ref of refs) {
27700
27770
  if (!ref.startsWith("./") && !ref.startsWith("../")) continue;
27701
- const resolvedPath = fs13.resolve(parentDir, ref);
27702
- const exists = await Promise.resolve(fs13.exists(resolvedPath));
27771
+ const resolvedPath = fs15.resolve(parentDir, ref);
27772
+ const exists = await Promise.resolve(fs15.exists(resolvedPath));
27703
27773
  if (!exists) {
27704
27774
  const location = context.findLocation(/inherits:/);
27705
27775
  context.addDiagnostic({
@@ -28076,6 +28146,90 @@ var PrompdCompiler = class {
28076
28146
  var parser = new PrompdParser();
28077
28147
  var compiler = new PrompdCompiler();
28078
28148
 
28149
+ // ../tools-node/migrateHome.ts
28150
+ import { promises as fs7 } from "node:fs";
28151
+ import { homedir as homedir4 } from "node:os";
28152
+ import { join as join6, dirname as dirname3 } from "node:path";
28153
+ var SIDECAR_FILES = [
28154
+ "daemon.id",
28155
+ "daemon.token",
28156
+ "hosts.json",
28157
+ "sidecar.crt",
28158
+ "sidecar.key",
28159
+ "sidecar.sans.json"
28160
+ ];
28161
+ var SIDECAR_DIRS = ["hosts"];
28162
+ async function isDir(p2) {
28163
+ return fs7.stat(p2).then((s2) => s2.isDirectory()).catch(() => false);
28164
+ }
28165
+ async function present(p2) {
28166
+ return fs7.stat(p2).then(() => true).catch(() => false);
28167
+ }
28168
+ function code(e2) {
28169
+ return e2?.code;
28170
+ }
28171
+ async function moveEntry(from, to, report) {
28172
+ if (!await present(from)) return;
28173
+ if (await present(to)) {
28174
+ report.skipped.push({ path: to, reason: "destination already exists" });
28175
+ return;
28176
+ }
28177
+ await fs7.mkdir(dirname3(to), { recursive: true });
28178
+ try {
28179
+ await fs7.rename(from, to);
28180
+ report.moved.push(to);
28181
+ } catch (e2) {
28182
+ const c2 = code(e2);
28183
+ if (c2 === "EEXIST" || c2 === "ENOTEMPTY") {
28184
+ report.skipped.push({ path: to, reason: "moved concurrently" });
28185
+ return;
28186
+ }
28187
+ if (c2 === "ENOENT") {
28188
+ report.skipped.push({ path: to, reason: "moved concurrently" });
28189
+ return;
28190
+ }
28191
+ if (c2 === "EXDEV") {
28192
+ await fs7.cp(from, to, { recursive: true });
28193
+ await fs7.rm(from, { recursive: true, force: true });
28194
+ report.moved.push(to);
28195
+ return;
28196
+ }
28197
+ throw e2;
28198
+ }
28199
+ }
28200
+ async function mergeDir(from, to, report) {
28201
+ const entries = await fs7.readdir(from, { withFileTypes: true }).catch(() => []);
28202
+ for (const e2 of entries) {
28203
+ const src = join6(from, e2.name);
28204
+ const dst = join6(to, e2.name);
28205
+ if (e2.isDirectory() && await isDir(dst)) await mergeDir(src, dst, report);
28206
+ else await moveEntry(src, dst, report);
28207
+ }
28208
+ await fs7.rmdir(from).catch(() => {
28209
+ });
28210
+ }
28211
+ async function migrateHomeLayout(base = homedir4()) {
28212
+ const report = { moved: [], skipped: [] };
28213
+ const home = corenelDir2(base);
28214
+ if (!await present(home)) return report;
28215
+ const config = join6(home, "config");
28216
+ if (await isDir(config)) await mergeDir(config, home, report);
28217
+ const side = sidecarDir(base);
28218
+ for (const name of SIDECAR_FILES) {
28219
+ await moveEntry(join6(home, name), join6(side, name), report);
28220
+ }
28221
+ for (const name of SIDECAR_DIRS) {
28222
+ const src = join6(home, name);
28223
+ if (!await isDir(src)) continue;
28224
+ const dst = join6(side, name);
28225
+ if (await isDir(dst)) await mergeDir(src, dst, report);
28226
+ else await moveEntry(src, dst, report);
28227
+ }
28228
+ if (report.moved.length) await fs7.chmod(home, 448).catch(() => {
28229
+ });
28230
+ return report;
28231
+ }
28232
+
28079
28233
  // src/session/wiring.ts
28080
28234
  async function probeMsb(run) {
28081
28235
  const r2 = await run(["--version"], { timeoutMs: 3e4 });
@@ -28144,10 +28298,120 @@ async function wireSessions(opts) {
28144
28298
  return { manager, backend, port: makeSessionsPort({ manager, backend, guestEnv, ...portExtras(opts) }) };
28145
28299
  }
28146
28300
 
28301
+ // src/runner/owners.ts
28302
+ import { readdir } from "node:fs/promises";
28303
+ import { join as join8 } from "node:path";
28304
+
28305
+ // src/hostsDir.ts
28306
+ import { join as join7 } from "node:path";
28307
+ var HOSTS_SEG = "hosts";
28308
+ var MAX_SYNC_PATH_LEN = 400;
28309
+ var MAX_SYNC_SEGMENTS = 24;
28310
+ var CONTROL_CHAR_RE = new RegExp(
28311
+ "[\\u0000-\\u001f\\u007f-\\u009f]"
28312
+ );
28313
+ var DIR_NAME_RE = /^[a-z0-9_-]{1,32}$/;
28314
+ function assertSyncPath(path) {
28315
+ if (!path) throw new Error("sync path is empty");
28316
+ if (path.length > MAX_SYNC_PATH_LEN) throw new Error(`sync path too long (> ${MAX_SYNC_PATH_LEN})`);
28317
+ if (CONTROL_CHAR_RE.test(path)) throw new Error("sync path contains control characters");
28318
+ if (path.includes(":")) throw new Error("sync path contains a colon");
28319
+ const n2 = normalizeFilePath(path);
28320
+ if (!n2) throw new Error("sync path normalizes to empty (targets the root)");
28321
+ const segments = n2.split("/");
28322
+ if (segments.length > MAX_SYNC_SEGMENTS) throw new Error(`sync path has too many segments (> ${MAX_SYNC_SEGMENTS})`);
28323
+ if (segments.some((s2) => s2 === "." || s2 === "..")) {
28324
+ throw new Error('sync path contains a "." or ".." segment');
28325
+ }
28326
+ }
28327
+ function hostsRoot(stateDir) {
28328
+ return join7(stateDir, "sidecar", HOSTS_SEG);
28329
+ }
28330
+ function hostsDirFor(stateDir, dirName2) {
28331
+ if (!DIR_NAME_RE.test(dirName2)) throw new Error(`invalid host dir name: ${dirName2}`);
28332
+ return join7(hostsRoot(stateDir), dirName2);
28333
+ }
28334
+ var RAW = /* @__PURE__ */ new WeakMap();
28335
+ function hostFilesFor(stateDir, dirName2) {
28336
+ const raw = new NodeFileService(hostsDirFor(stateDir, dirName2));
28337
+ const wrapper = {
28338
+ kind: raw.kind,
28339
+ label: raw.label,
28340
+ isLocalDisk: raw.isLocalDisk,
28341
+ writable: raw.writable,
28342
+ list: (dir) => raw.list(dir),
28343
+ read: (path) => raw.read(path),
28344
+ stat: (path) => raw.stat(path),
28345
+ search: (query) => raw.search(query),
28346
+ onChange: (_cb) => raw.onChange()
28347
+ };
28348
+ RAW.set(wrapper, raw);
28349
+ return wrapper;
28350
+ }
28351
+ async function syncWrite(files, path, content) {
28352
+ assertSyncPath(path);
28353
+ const raw = RAW.get(files);
28354
+ if (!raw) throw new Error("syncWrite: not a hostFilesFor() result");
28355
+ await raw.write(path, content);
28356
+ }
28357
+ async function syncRemove(files, path) {
28358
+ assertSyncPath(path);
28359
+ const raw = RAW.get(files);
28360
+ if (!raw) throw new Error("syncRemove: not a hostFilesFor() result");
28361
+ await raw.remove(path);
28362
+ }
28363
+
28364
+ // src/runner/owners.ts
28365
+ var WORKSPACES_SEG = "workspaces";
28366
+ var WORKSPACE_ID_RE = /^[a-z0-9_-]{1,32}$/;
28367
+ async function workspaceRootsFor(dirName2, hostDir) {
28368
+ let entries;
28369
+ try {
28370
+ entries = await readdir(join8(hostDir, WORKSPACES_SEG), { withFileTypes: true });
28371
+ } catch {
28372
+ return [];
28373
+ }
28374
+ const out = [];
28375
+ for (const entry of entries) {
28376
+ if (!entry.isDirectory()) continue;
28377
+ if (!WORKSPACE_ID_RE.test(entry.name)) continue;
28378
+ out.push({
28379
+ owner: `${dirName2}/ws/${entry.name}`,
28380
+ files: new NodeFileService(join8(hostDir, WORKSPACES_SEG, entry.name)),
28381
+ root: "crew"
28382
+ });
28383
+ }
28384
+ return out;
28385
+ }
28386
+ async function listOwnerRoots(stateDir) {
28387
+ const roots = [
28388
+ { owner: "", files: new NodeFileService(stateDir), root: "crew" }
28389
+ ];
28390
+ let entries;
28391
+ try {
28392
+ entries = await readdir(hostsRoot(stateDir), { withFileTypes: true });
28393
+ } catch {
28394
+ return roots;
28395
+ }
28396
+ for (const entry of entries) {
28397
+ if (!entry.isDirectory()) continue;
28398
+ const dirName2 = entry.name;
28399
+ let hostDir;
28400
+ try {
28401
+ hostDir = hostsDirFor(stateDir, dirName2);
28402
+ } catch {
28403
+ continue;
28404
+ }
28405
+ roots.push({ owner: dirName2, files: new NodeFileService(hostDir), root: "crew" });
28406
+ roots.push(...await workspaceRootsFor(dirName2, hostDir));
28407
+ }
28408
+ return roots;
28409
+ }
28410
+
28147
28411
  // src/cert.ts
28148
- import { promises as fs6 } from "node:fs";
28412
+ import { promises as fs8 } from "node:fs";
28149
28413
  import { networkInterfaces } from "node:os";
28150
- import { join as join5 } from "node:path";
28414
+ import { join as join9 } from "node:path";
28151
28415
  import selfsigned from "selfsigned";
28152
28416
  function lanIPv4s() {
28153
28417
  const out = [];
@@ -28158,18 +28422,18 @@ function lanIPv4s() {
28158
28422
  }
28159
28423
  return [...new Set(out)];
28160
28424
  }
28161
- var certPath = () => join5(corenelDir(), "sidecar.crt");
28162
- var keyPath = () => join5(corenelDir(), "sidecar.key");
28163
- var sansPath = () => join5(corenelDir(), "sidecar.sans.json");
28425
+ var certPath = () => join9(sidecarSubdir(), "sidecar.crt");
28426
+ var keyPath = () => join9(sidecarSubdir(), "sidecar.key");
28427
+ var sansPath = () => join9(sidecarSubdir(), "sidecar.sans.json");
28164
28428
  function isIp(s2) {
28165
28429
  return /^\d{1,3}(?:\.\d{1,3}){3}$/.test(s2) || s2.includes(":");
28166
28430
  }
28167
28431
  async function loadOrCreateSelfSigned(extraHosts = []) {
28168
28432
  const sans = [...new Set(["localhost", "127.0.0.1", "::1", ...lanIPv4s(), ...extraHosts].filter(Boolean))];
28169
28433
  try {
28170
- const have = JSON.parse(await fs6.readFile(sansPath(), "utf8"));
28434
+ const have = JSON.parse(await fs8.readFile(sansPath(), "utf8"));
28171
28435
  if (Array.isArray(have) && sans.every((s2) => have.includes(s2))) {
28172
- const [cert, key] = await Promise.all([fs6.readFile(certPath(), "utf8"), fs6.readFile(keyPath(), "utf8")]);
28436
+ const [cert, key] = await Promise.all([fs8.readFile(certPath(), "utf8"), fs8.readFile(keyPath(), "utf8")]);
28173
28437
  return { cert, key, sans: have };
28174
28438
  }
28175
28439
  } catch {
@@ -28181,10 +28445,10 @@ async function loadOrCreateSelfSigned(extraHosts = []) {
28181
28445
  algorithm: "sha256",
28182
28446
  extensions: [{ name: "subjectAltName", altNames }]
28183
28447
  });
28184
- await fs6.mkdir(corenelDir(), { recursive: true });
28185
- await fs6.writeFile(certPath(), pems.cert, { mode: 384 });
28186
- await fs6.writeFile(keyPath(), pems.private, { mode: 384 });
28187
- await fs6.writeFile(sansPath(), JSON.stringify(sans));
28448
+ await fs8.mkdir(sidecarSubdir(), { recursive: true });
28449
+ await fs8.writeFile(certPath(), pems.cert, { mode: 384 });
28450
+ await fs8.writeFile(keyPath(), pems.private, { mode: 384 });
28451
+ await fs8.writeFile(sansPath(), JSON.stringify(sans));
28188
28452
  return { cert: pems.cert, key: pems.private, sans };
28189
28453
  }
28190
28454
 
@@ -28289,8 +28553,8 @@ async function startWith(bin, port, name) {
28289
28553
  child.stdout?.on("data", onData);
28290
28554
  child.stderr?.on("data", onData);
28291
28555
  child.on("error", (e2) => finish(() => reject(e2)));
28292
- child.on("exit", (code) => finish(() => reject(new Error(
28293
- `${bin} exited (code ${code}) before a share URL appeared \u2014 is the environment enabled? Run \`${bin} enable <token>\` once.
28556
+ child.on("exit", (code2) => finish(() => reject(new Error(
28557
+ `${bin} exited (code ${code2}) before a share URL appeared \u2014 is the environment enabled? Run \`${bin} enable <token>\` once.
28294
28558
  ${tail(out)}`
28295
28559
  ))));
28296
28560
  const timer = setTimeout(() => finish(() => {
@@ -28427,13 +28691,13 @@ function startWith2(bin, mode, log, onDown) {
28427
28691
  child.stdout?.on("data", onData);
28428
28692
  child.stderr?.on("data", onData);
28429
28693
  child.on("error", (e2) => finish(() => reject(e2)));
28430
- child.on("exit", (code) => {
28694
+ child.on("exit", (code2) => {
28431
28695
  if (settled) {
28432
- log?.(`cloudflared exited (code ${code}) \u2014 the tunnel is down`);
28433
- fireDown(`cloudflared exited (code ${code})`);
28696
+ log?.(`cloudflared exited (code ${code2}) \u2014 the tunnel is down`);
28697
+ fireDown(`cloudflared exited (code ${code2})`);
28434
28698
  return;
28435
28699
  }
28436
- finish(() => reject(new Error(`${bin} exited (code ${code}) before the tunnel came up.
28700
+ finish(() => reject(new Error(`${bin} exited (code ${code2}) before the tunnel came up.
28437
28701
  ${tail2(out)}`)));
28438
28702
  });
28439
28703
  const timer = setTimeout(() => finish(() => {
@@ -28682,6 +28946,14 @@ function parseCanCall(meta) {
28682
28946
  if (!Array.isArray(raw)) return [];
28683
28947
  return raw.filter((x2) => typeof x2 === "string" && isValidAgentName(x2));
28684
28948
  }
28949
+ function parseWorkRoots(raw) {
28950
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
28951
+ const out = {};
28952
+ for (const [id, dir] of Object.entries(raw)) {
28953
+ if (typeof dir === "string" && dir) out[id] = dir;
28954
+ }
28955
+ return Object.keys(out).length ? out : void 0;
28956
+ }
28685
28957
  function parseSettings(text) {
28686
28958
  try {
28687
28959
  const raw = JSON.parse(text);
@@ -28689,7 +28961,13 @@ function parseSettings(text) {
28689
28961
  const o2 = raw;
28690
28962
  return {
28691
28963
  model: typeof o2.model === "string" ? o2.model : void 0,
28692
- temperature: typeof o2.temperature === "number" ? o2.temperature : void 0
28964
+ temperature: typeof o2.temperature === "number" ? o2.temperature : void 0,
28965
+ /* Carried through as-is. Validation belongs to the daemon, which is the
28966
+ * only side that knows its own approved roots -- checking here would be
28967
+ * checking against the wrong list, on the wrong machine. Non-string
28968
+ * values are dropped per entry rather than failing the whole file: one
28969
+ * bad key must not cost an agent every other machine's setting. */
28970
+ workRoots: parseWorkRoots(o2.workRoots)
28693
28971
  };
28694
28972
  }
28695
28973
  } catch {
@@ -30075,113 +30353,6 @@ function nodeCrewLocation(stateDir, scope, workspaceRoot) {
30075
30353
  return { files: new NodeFileService(wsRoot), root: `${stateDirName()}/crew` };
30076
30354
  }
30077
30355
 
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
30356
  // ../../node_modules/.pnpm/openai@4.104.0_ws@8.21.0/node_modules/openai/internal/qs/formats.mjs
30186
30357
  var default_format = "RFC3986";
30187
30358
  var formatters = {
@@ -36933,10 +37104,10 @@ function statusOf(message) {
36933
37104
  function classifyStreamError(message, status) {
36934
37105
  if (/\baborted\b|AbortError/i.test(message)) return "fatal";
36935
37106
  if (/\b(timed out|request timeout|gateway timeout|deadline exceeded)\b/i.test(message)) return "fatal";
36936
- const code = status ?? statusOf(message);
36937
- if (code != null) {
36938
- if (FATAL_STATUS.has(code)) return "fatal";
36939
- if (TRANSIENT_STATUS.has(code)) return "transient";
37107
+ const code2 = status ?? statusOf(message);
37108
+ if (code2 != null) {
37109
+ if (FATAL_STATUS.has(code2)) return "fatal";
37110
+ if (TRANSIENT_STATUS.has(code2)) return "transient";
36940
37111
  }
36941
37112
  if (/\b(invalid[\s_-]?api[\s_-]?key|incorrect api key|unauthorized|forbidden|quota exceeded|insufficient)\b/i.test(message)) return "fatal";
36942
37113
  return "transient";
@@ -38446,7 +38617,7 @@ var NodeRunnerService = class {
38446
38617
  // src/runner/core.ts
38447
38618
  import { watch } from "node:fs";
38448
38619
  import { readdir as readdir2, stat } from "node:fs/promises";
38449
- import { join as join8, relative as relative5, sep as sep3 } from "node:path";
38620
+ import { join as join10, relative as relative5, sep as sep3 } from "node:path";
38450
38621
 
38451
38622
  // src/runner/triggerConfig.ts
38452
38623
  function inputFor(trigger, fallback) {
@@ -38926,6 +39097,7 @@ function startDaemonCore(opts) {
38926
39097
  w2.on("error", (err) => log(`daemon core: watch error on "${dir}" -- ${err.message}`));
38927
39098
  watchers.push(w2);
38928
39099
  } catch (err) {
39100
+ if (err?.code === "ENOENT") continue;
38929
39101
  log(`daemon core: could not watch "${dir}" -- ${err instanceof Error ? err.message : String(err)}`);
38930
39102
  }
38931
39103
  }
@@ -38955,9 +39127,9 @@ function startDaemonCore(opts) {
38955
39127
  for (const entry of entries) {
38956
39128
  if (entry.isDirectory()) {
38957
39129
  if (isExcludedSegment(entry.name, dotDir)) continue;
38958
- await walkForChanges(root, join8(dir, entry.name), since);
39130
+ await walkForChanges(root, join10(dir, entry.name), since);
38959
39131
  } else {
38960
- const abs = join8(dir, entry.name);
39132
+ const abs = join10(dir, entry.name);
38961
39133
  let mtimeMs;
38962
39134
  try {
38963
39135
  mtimeMs = (await stat(abs)).mtimeMs;
@@ -39235,26 +39407,26 @@ function makeRunHub(opts = {}) {
39235
39407
  }
39236
39408
 
39237
39409
  // src/identity.ts
39238
- import { promises as fs8 } from "node:fs";
39410
+ import { promises as fs10 } from "node:fs";
39239
39411
  import { randomBytes as randomBytes2 } from "node:crypto";
39240
39412
  import { hostname } from "node:os";
39241
- import { join as join9 } from "node:path";
39242
- function idPath(dir = corenelDir()) {
39243
- return join9(dir, "daemon.id");
39413
+ import { join as join11 } from "node:path";
39414
+ function idPath(dir = sidecarSubdir()) {
39415
+ return join11(dir, "daemon.id");
39244
39416
  }
39245
- async function loadOrCreateSidecarId(dir = corenelDir()) {
39417
+ async function loadOrCreateSidecarId(dir = sidecarSubdir()) {
39246
39418
  const path = idPath(dir);
39247
39419
  try {
39248
- const existing = (await fs8.readFile(path, "utf8")).trim();
39420
+ const existing = (await fs10.readFile(path, "utf8")).trim();
39249
39421
  if (existing) return existing;
39250
39422
  } catch (err) {
39251
39423
  if (!isEnoent(err)) throw err;
39252
39424
  }
39253
- await fs8.mkdir(dir, { recursive: true });
39425
+ await fs10.mkdir(dir, { recursive: true });
39254
39426
  const id = randomBytes2(16).toString("hex");
39255
- await fs8.writeFile(path, id + "\n", { mode: 384 });
39427
+ await fs10.writeFile(path, id + "\n", { mode: 384 });
39256
39428
  try {
39257
- await fs8.chmod(path, 384);
39429
+ await fs10.chmod(path, 384);
39258
39430
  } catch {
39259
39431
  }
39260
39432
  return id;
@@ -39267,9 +39439,9 @@ function sidecarName(opts) {
39267
39439
  }
39268
39440
 
39269
39441
  // src/hosts.ts
39270
- import { promises as fs9 } from "node:fs";
39442
+ import { promises as fs11 } from "node:fs";
39271
39443
  import { randomBytes as randomBytes3 } from "node:crypto";
39272
- import { join as join10 } from "node:path";
39444
+ import { join as join12 } from "node:path";
39273
39445
  var MAX_DIR = 32;
39274
39446
  var MAX_FIELD_LEN = 200;
39275
39447
  var MAX_HOSTS = 200;
@@ -39303,7 +39475,7 @@ function sanitizeDirName(raw) {
39303
39475
  return RESERVED_WINDOWS_NAMES.has(safe) ? `${safe}-host` : safe;
39304
39476
  }
39305
39477
  function hostsPath(dir) {
39306
- return join10(dir, "hosts.json");
39478
+ return join12(dir, "hosts.json");
39307
39479
  }
39308
39480
  function emptyManifest() {
39309
39481
  return /* @__PURE__ */ Object.create(null);
@@ -39324,7 +39496,7 @@ function repairHostRecord(value, fallbackLastSeen) {
39324
39496
  async function read(dir, fallbackLastSeen) {
39325
39497
  let raw;
39326
39498
  try {
39327
- raw = await fs9.readFile(hostsPath(dir), "utf8");
39499
+ raw = await fs11.readFile(hostsPath(dir), "utf8");
39328
39500
  } catch {
39329
39501
  return emptyManifest();
39330
39502
  }
@@ -39343,14 +39515,14 @@ async function read(dir, fallbackLastSeen) {
39343
39515
  return manifest;
39344
39516
  }
39345
39517
  async function write(dir, manifest) {
39346
- await fs9.mkdir(dir, { recursive: true });
39518
+ await fs11.mkdir(dir, { recursive: true });
39347
39519
  const target = hostsPath(dir);
39348
- const tmp = join10(dir, `.hosts.json.tmp-${randomBytes3(8).toString("hex")}`);
39520
+ const tmp = join12(dir, `.hosts.json.tmp-${randomBytes3(8).toString("hex")}`);
39349
39521
  try {
39350
- await fs9.writeFile(tmp, JSON.stringify(manifest, null, 2));
39351
- await fs9.rename(tmp, target);
39522
+ await fs11.writeFile(tmp, JSON.stringify(manifest, null, 2));
39523
+ await fs11.rename(tmp, target);
39352
39524
  } catch (err) {
39353
- await fs9.rm(tmp, { force: true }).catch(() => void 0);
39525
+ await fs11.rm(tmp, { force: true }).catch(() => void 0);
39354
39526
  throw err;
39355
39527
  }
39356
39528
  }
@@ -39498,8 +39670,8 @@ function makeHostSync(opts) {
39498
39670
  }
39499
39671
 
39500
39672
  // src/hostsCli.ts
39501
- import { resolve as resolve5, join as join11 } from "node:path";
39502
- import { promises as fs10 } from "node:fs";
39673
+ import { resolve as resolve5, join as join13 } from "node:path";
39674
+ import { promises as fs12 } from "node:fs";
39503
39675
  async function listHosts(manifest) {
39504
39676
  const m2 = await manifest.list();
39505
39677
  return Object.entries(m2).map(([clientId, r2]) => ({
@@ -39522,7 +39694,7 @@ async function forgetHost(stateDir, manifest, dirName2) {
39522
39694
  return { ok: false, reason: e2 instanceof Error ? e2.message : String(e2) };
39523
39695
  }
39524
39696
  try {
39525
- await fs10.rm(dir, { recursive: true, force: true });
39697
+ await fs12.rm(dir, { recursive: true, force: true });
39526
39698
  } catch (e2) {
39527
39699
  return {
39528
39700
  ok: false,
@@ -39533,10 +39705,10 @@ async function forgetHost(stateDir, manifest, dirName2) {
39533
39705
  return { ok: true };
39534
39706
  }
39535
39707
  async function checkHostsDrift(stateDir, manifest, log) {
39536
- const hostsRoot = join11(stateDir, HOSTS_SEG);
39708
+ const hostsRoot2 = join13(stateDir, HOSTS_SEG);
39537
39709
  let entries;
39538
39710
  try {
39539
- entries = (await fs10.readdir(hostsRoot, { withFileTypes: true })).filter((d2) => d2.isDirectory()).map((d2) => d2.name);
39711
+ entries = (await fs12.readdir(hostsRoot2, { withFileTypes: true })).filter((d2) => d2.isDirectory()).map((d2) => d2.name);
39540
39712
  } catch {
39541
39713
  return [];
39542
39714
  }
@@ -40025,7 +40197,7 @@ var FLAGS = [
40025
40197
  { 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
40198
  { flag: "--daemon", help: "Run crew agents on this machine unattended: cron, file-change and run-complete triggers." },
40027
40199
  { 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)." },
40200
+ { 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
40201
  { flag: "--default-model", arg: "<id>", help: "Model for crew agents that do not name one." },
40030
40202
  { flag: "--name", arg: "<label>", help: "Friendly name advertised to clients (defaults to the hostname)." },
40031
40203
  { flag: "--max-chain-depth", arg: "<n>", help: "Cap how far run-complete triggers may chain before suppressing (default 8)." },
@@ -40075,6 +40247,11 @@ function helpText() {
40075
40247
  ` ${self} --daemon --default-model gpt-4.1-mini`,
40076
40248
  ` ${self} --root ~/work --acp claude-code --acp-policy read-only`,
40077
40249
  "",
40250
+ " A second sidecar on the same machine needs its own state dir \u2014 without one",
40251
+ " both read the same daemon.id and advertise the same identity, so a paired",
40252
+ " browser sees one sidecar where there are two:",
40253
+ ` ${self} --root ~/other --port 4859 --state-dir ~/.corenel-other`,
40254
+ "",
40078
40255
  "Reachable both ways: `corenel sidecar \u2026` through the CLI, and",
40079
40256
  "`corenel-sidecar \u2026` standalone, for machines that run only this.",
40080
40257
  "",
@@ -40274,7 +40451,7 @@ function assertDirectory(path, flag2) {
40274
40451
  }
40275
40452
 
40276
40453
  // src/roots.ts
40277
- import { promises as fs11 } from "node:fs";
40454
+ import { promises as fs13 } from "node:fs";
40278
40455
  import { resolve as resolve8 } from "node:path";
40279
40456
  var DISABLED = "adding working directories is not enabled (no --allow-add-under)";
40280
40457
  function createRootsController(opts) {
@@ -40288,8 +40465,8 @@ function createRootsController(opts) {
40288
40465
  if (!areas.some((a2) => withinRoot(a2, abs))) {
40289
40466
  throw new Error(`path is outside every allowed area: ${abs}`);
40290
40467
  }
40291
- const real = await fs11.realpath(abs);
40292
- const realAreas = await Promise.all(areas.map((a2) => fs11.realpath(a2)));
40468
+ const real = await fs13.realpath(abs);
40469
+ const realAreas = await Promise.all(areas.map((a2) => fs13.realpath(a2)));
40293
40470
  if (!realAreas.some((a2) => withinRoot(a2, real))) {
40294
40471
  throw new Error(`path escapes the allowed area via symlink: ${abs}`);
40295
40472
  }
@@ -40314,12 +40491,12 @@ function createRootsController(opts) {
40314
40491
  if (!cloneAreas.some((a2) => withinRoot(a2, abs))) {
40315
40492
  throw new Error(`checkout is outside every --clone-under directory: ${abs}`);
40316
40493
  }
40317
- const real = await fs11.realpath(abs);
40318
- const realAreas = await Promise.all(cloneAreas.map((a2) => fs11.realpath(a2)));
40494
+ const real = await fs13.realpath(abs);
40495
+ const realAreas = await Promise.all(cloneAreas.map((a2) => fs13.realpath(a2)));
40319
40496
  if (!realAreas.some((a2) => withinRoot(a2, real))) {
40320
40497
  throw new Error(`checkout escapes its --clone-under directory via symlink: ${abs}`);
40321
40498
  }
40322
- const st2 = await fs11.stat(abs);
40499
+ const st2 = await fs13.stat(abs);
40323
40500
  if (!st2.isDirectory()) throw new Error(`not a directory: ${abs}`);
40324
40501
  roots.push(abs);
40325
40502
  announce();
@@ -40329,14 +40506,14 @@ function createRootsController(opts) {
40329
40506
  if (path === void 0) return [...areas];
40330
40507
  const abs = resolve8(path);
40331
40508
  await assertInArea(abs);
40332
- const entries = await fs11.readdir(abs, { withFileTypes: true });
40509
+ const entries = await fs13.readdir(abs, { withFileTypes: true });
40333
40510
  return entries.filter((e2) => e2.isDirectory()).map((e2) => e2.name);
40334
40511
  },
40335
40512
  async add(path) {
40336
40513
  const abs = resolve8(path);
40337
40514
  if (roots.some((r2) => resolve8(r2) === abs)) return;
40338
40515
  await assertInArea(abs);
40339
- const st2 = await fs11.stat(abs);
40516
+ const st2 = await fs13.stat(abs);
40340
40517
  if (!st2.isDirectory()) throw new Error(`not a directory: ${abs}`);
40341
40518
  roots.push(abs);
40342
40519
  announce();
@@ -40352,8 +40529,8 @@ function createRootsController(opts) {
40352
40529
 
40353
40530
  // src/session/gitSource.ts
40354
40531
  import { spawn as spawn6 } from "node:child_process";
40355
- import { promises as fs12 } from "node:fs";
40356
- import { join as join12, resolve as resolve9 } from "node:path";
40532
+ import { promises as fs14 } from "node:fs";
40533
+ import { join as join14, resolve as resolve9 } from "node:path";
40357
40534
  var DEFAULT_TIMEOUT_MS3 = 10 * 6e4;
40358
40535
  var ALLOWED_SCHEMES = ["https://", "ssh://", "git+ssh://"];
40359
40536
  var SCP_LIKE = /^[A-Za-z0-9_.-]+@[A-Za-z0-9_.-]+:[A-Za-z0-9_./~-]+$/;
@@ -40426,9 +40603,9 @@ git timed out after ${opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS3}ms` });
40426
40603
  settle({ code: -1, stdout, stderr: `${stderr}
40427
40604
  ${e2 instanceof Error ? e2.message : String(e2)}` });
40428
40605
  });
40429
- child.on("close", (code) => {
40606
+ child.on("close", (code2) => {
40430
40607
  clearTimeout(timer);
40431
- settle({ code: code ?? -1, stdout, stderr });
40608
+ settle({ code: code2 ?? -1, stdout, stderr });
40432
40609
  });
40433
40610
  });
40434
40611
  }
@@ -40445,14 +40622,14 @@ function createGitSource(deps) {
40445
40622
  if (!areas.some((a2) => withinRoot(a2, abs))) {
40446
40623
  throw new Error(`clone destination is outside every allowed area: ${abs}`);
40447
40624
  }
40448
- const realAreas = await Promise.all(areas.map((a2) => fs12.realpath(a2)));
40625
+ const realAreas = await Promise.all(areas.map((a2) => fs14.realpath(a2)));
40449
40626
  if (!realAreas.some((a2) => withinRoot(a2, abs))) {
40450
40627
  throw new Error(`clone destination escapes the allowed area via symlink: ${abs}`);
40451
40628
  }
40452
40629
  }
40453
40630
  async function existing(dir, url) {
40454
40631
  try {
40455
- const entries = await fs12.readdir(dir);
40632
+ const entries = await fs14.readdir(dir);
40456
40633
  if (entries.length === 0) return "absent";
40457
40634
  } catch {
40458
40635
  return "absent";
@@ -40470,7 +40647,7 @@ function createGitSource(deps) {
40470
40647
  if (!areas.length) throw new Error(DISABLED2);
40471
40648
  assertCloneUrl(spec.url);
40472
40649
  if (spec.ref !== void 0) assertRef(spec.ref);
40473
- const dir = join12(areas[0], checkoutName(spec.url));
40650
+ const dir = join14(areas[0], checkoutName(spec.url));
40474
40651
  await assertInArea(dir);
40475
40652
  const state = await existing(dir, spec.url);
40476
40653
  if (state === "occupied") {
@@ -40627,8 +40804,8 @@ var JsonRpcPeer = class {
40627
40804
  this.respondError(id, shape.code, shape.message);
40628
40805
  }
40629
40806
  }
40630
- respondError(id, code, message) {
40631
- this.write({ jsonrpc: "2.0", id, error: { code, message } });
40807
+ respondError(id, code2, message) {
40808
+ this.write({ jsonrpc: "2.0", id, error: { code: code2, message } });
40632
40809
  }
40633
40810
  write(value) {
40634
40811
  if (this.closed) return;
@@ -40668,6 +40845,10 @@ var JsonRpcPeer = class {
40668
40845
 
40669
40846
  // src/acp/client.ts
40670
40847
  var ACP_PROTOCOL_VERSION = 1;
40848
+ function acceptsHttpMcp(agentCapabilities) {
40849
+ const mcp = agentCapabilities.mcpCapabilities;
40850
+ return !!(mcp && typeof mcp === "object" && mcp.http === true);
40851
+ }
40671
40852
  function rec2(v2) {
40672
40853
  return typeof v2 === "object" && v2 !== null && !Array.isArray(v2) ? v2 : {};
40673
40854
  }
@@ -40682,6 +40863,10 @@ var AcpClient = class {
40682
40863
  currentMode = null;
40683
40864
  /** Whether the agent said it can reattach to an existing session. */
40684
40865
  canLoad = false;
40866
+ /* Whether the agent accepts HTTP MCP servers. Recorded at initialize, read at
40867
+ * session/new -- the schema defaults it to FALSE, so an agent that says
40868
+ * nothing gets nothing. */
40869
+ httpMcp = false;
40685
40870
  disposed = false;
40686
40871
  constructor(opts) {
40687
40872
  this.opts = opts;
@@ -40692,8 +40877,8 @@ var AcpClient = class {
40692
40877
  ...opts.log ? { log: opts.log } : {}
40693
40878
  });
40694
40879
  opts.transport.onData((chunk) => this.peer.push(chunk));
40695
- opts.transport.onExit((code) => {
40696
- this.peer.close(`agent exited (code ${code ?? "null"})`);
40880
+ opts.transport.onExit((code2) => {
40881
+ this.peer.close(`agent exited (code ${code2 ?? "null"})`);
40697
40882
  });
40698
40883
  }
40699
40884
  log(m2) {
@@ -40737,6 +40922,7 @@ var AcpClient = class {
40737
40922
  }));
40738
40923
  const caps = rec2(raw.agentCapabilities);
40739
40924
  this.canLoad = caps.loadSession === true;
40925
+ this.httpMcp = acceptsHttpMcp(caps);
40740
40926
  const methods = Array.isArray(raw.authMethods) ? raw.authMethods : [];
40741
40927
  const version = typeof raw.protocolVersion === "number" ? raw.protocolVersion : ACP_PROTOCOL_VERSION;
40742
40928
  if (version !== ACP_PROTOCOL_VERSION) {
@@ -40749,9 +40935,16 @@ var AcpClient = class {
40749
40935
  authMethods: methods
40750
40936
  };
40751
40937
  }
40752
- /** `cwd` MUST be absolute per the ACP spec; the caller passes a confined root. */
40753
- async newSession(cwd) {
40754
- const raw = rec2(await this.peer.request("session/new", { cwd, mcpServers: [] }));
40938
+ /**
40939
+ * `cwd` MUST be absolute per the ACP spec; the caller passes a confined root.
40940
+ *
40941
+ * `mcpServers` is filtered by what the agent said it can take: this used to
40942
+ * be a hardcoded `[]`, so a user's configured MCP servers never reached the
40943
+ * agent at all and MCP was configurable but inert.
40944
+ */
40945
+ async newSession(cwd, mcpServers = []) {
40946
+ const servers = this.allowedMcp(mcpServers);
40947
+ const raw = rec2(await this.peer.request("session/new", { cwd, mcpServers: servers }));
40755
40948
  const id = str7(raw.sessionId);
40756
40949
  if (!id) throw new Error("acp: agent returned no sessionId");
40757
40950
  this.sessionId = id;
@@ -40764,6 +40957,22 @@ var AcpClient = class {
40764
40957
  this.currentMode = str7(modes.currentModeId) || null;
40765
40958
  return id;
40766
40959
  }
40960
+ /**
40961
+ * The MCP servers this agent will actually accept.
40962
+ *
40963
+ * Silently empty rather than throwing: an agent without the capability is a
40964
+ * normal thing to be pointed at, and refusing to open a session over it would
40965
+ * turn "no MCP here" into "no agent here". Logged once so the absence is
40966
+ * visible rather than mysterious.
40967
+ */
40968
+ allowedMcp(servers) {
40969
+ if (!servers.length) return [];
40970
+ if (!this.httpMcp) {
40971
+ this.log(`acp: agent does not accept HTTP MCP servers \u2014 ${servers.length} not passed`);
40972
+ return [];
40973
+ }
40974
+ return servers;
40975
+ }
40767
40976
  /** True when `session/load` is worth attempting. */
40768
40977
  supportsLoad() {
40769
40978
  return this.canLoad;
@@ -40775,9 +40984,9 @@ var AcpClient = class {
40775
40984
  * this resolves, which is what lets a caller rebuild a transcript it lost —
40776
40985
  * so the update handler must be wired before calling, not after.
40777
40986
  */
40778
- async loadSession(sessionId, cwd) {
40987
+ async loadSession(sessionId, cwd, mcpServers = []) {
40779
40988
  if (!this.canLoad) throw new Error("agent cannot load an existing session");
40780
- await this.peer.request("session/load", { sessionId, cwd, mcpServers: [] });
40989
+ await this.peer.request("session/load", { sessionId, cwd, mcpServers: this.allowedMcp(mcpServers) });
40781
40990
  this.sessionId = sessionId;
40782
40991
  }
40783
40992
  availableModes() {
@@ -40872,7 +41081,7 @@ function spawnAcpTransport(target, cwd, log, env) {
40872
41081
  if (!killed && child.stdin.writable) child.stdin.write(line);
40873
41082
  },
40874
41083
  onData: (cb) => child.stdout.on("data", (d2) => cb(d2)),
40875
- onExit: (cb) => child.on("exit", (code) => cb(code)),
41084
+ onExit: (cb) => child.on("exit", (code2) => cb(code2)),
40876
41085
  kill: () => {
40877
41086
  if (killed) return;
40878
41087
  killed = true;
@@ -41171,7 +41380,7 @@ function makeAcpService(deps) {
41171
41380
  return {
41172
41381
  targets: () => targetIds(),
41173
41382
  async start(targetId, onUpdate, opts) {
41174
- const { root, modeId, resume } = opts ?? {};
41383
+ const { root, modeId, resume, mcpServers } = opts ?? {};
41175
41384
  const target = resolveTarget(targetId);
41176
41385
  if (!target) {
41177
41386
  throw new Error(`unknown ACP target "${targetId}" (known: ${targetIds().join(", ")})`);
@@ -41205,7 +41414,7 @@ function makeAcpService(deps) {
41205
41414
  let resumed = false;
41206
41415
  if (resume && c2.supportsLoad()) {
41207
41416
  try {
41208
- await c2.loadSession(resume, cwd);
41417
+ await c2.loadSession(resume, cwd, mcpServers ?? []);
41209
41418
  sessionId = resume;
41210
41419
  resumed = true;
41211
41420
  log(`acp: reattached to session ${resume}`);
@@ -41215,7 +41424,7 @@ function makeAcpService(deps) {
41215
41424
  } else if (resume) {
41216
41425
  log(`acp: ${target.label} cannot reattach to a session \u2014 starting a new one`);
41217
41426
  }
41218
- if (!sessionId) sessionId = await c2.newSession(cwd);
41427
+ if (!sessionId) sessionId = await c2.newSession(cwd, mcpServers ?? []);
41219
41428
  if (!resumed && modeId && c2.availableModes().some((m2) => m2.id === modeId)) {
41220
41429
  await c2.setMode(modeId);
41221
41430
  log(`acp: mode set to ${modeId} (requested by the client)`);
@@ -41557,7 +41766,7 @@ function awaitCallback(pkce, port, timeoutMs) {
41557
41766
  return;
41558
41767
  }
41559
41768
  const err = url.searchParams.get("error");
41560
- const code = url.searchParams.get("code");
41769
+ const code2 = url.searchParams.get("code");
41561
41770
  const state = url.searchParams.get("state");
41562
41771
  if (!stateMatches(pkce.state, state)) {
41563
41772
  res.writeHead(400, { "content-type": "text/html" }).end(PAGE_FAIL);
@@ -41569,13 +41778,13 @@ function awaitCallback(pkce, port, timeoutMs) {
41569
41778
  rejectResult(new Error(`authorization failed: ${err}`));
41570
41779
  return;
41571
41780
  }
41572
- if (!code) {
41781
+ if (!code2) {
41573
41782
  res.writeHead(400, { "content-type": "text/html" }).end(PAGE_FAIL);
41574
41783
  rejectResult(new Error("callback carried no authorization code"));
41575
41784
  return;
41576
41785
  }
41577
41786
  res.writeHead(200, { "content-type": "text/html" }).end(PAGE_OK);
41578
- resolveResult({ code });
41787
+ resolveResult({ code: code2 });
41579
41788
  });
41580
41789
  const timer = setTimeout(() => {
41581
41790
  rejectResult(new Error("timed out waiting for the browser callback"));
@@ -41615,11 +41824,11 @@ async function login(deps) {
41615
41824
  log("Opening your browser to sign in...");
41616
41825
  log(url);
41617
41826
  await deps.openBrowser(url);
41618
- const { code } = await result;
41827
+ const { code: code2 } = await result;
41619
41828
  const tokens = await deps.exchange({
41620
41829
  issuer: deps.config.issuer,
41621
41830
  clientId: deps.config.clientId,
41622
- code,
41831
+ code: code2,
41623
41832
  verifier: pkce.verifier,
41624
41833
  redirectUri
41625
41834
  });
@@ -41755,8 +41964,8 @@ async function main() {
41755
41964
  if (rawArgv[0] === "login" || rawArgv[0] === "logout" || rawArgv[0] === "auth") {
41756
41965
  const sub = rawArgv[0];
41757
41966
  const parsed = parseArgs(rawArgv.slice(1));
41758
- const code = sub === "login" ? await runLogin(parsed.stateDir) : sub === "logout" ? await runLogout(parsed.stateDir) : await runAuthStatus(parsed.stateDir);
41759
- process.exit(code);
41967
+ const code2 = sub === "login" ? await runLogin(parsed.stateDir) : sub === "logout" ? await runLogout(parsed.stateDir) : await runAuthStatus(parsed.stateDir);
41968
+ process.exit(code2);
41760
41969
  }
41761
41970
  if (rawArgv[0] === "acp-agent") {
41762
41971
  await runAcpAgent(rawArgv.slice(1));
@@ -41767,9 +41976,23 @@ async function main() {
41767
41976
  validateArgs(args);
41768
41977
  const rootsController = createRootsController({ roots: args.roots, areas: args.addUnder, cloneAreas: args.cloneUnder });
41769
41978
  const token = await loadOrCreateToken();
41770
- const sidecarId = await loadOrCreateSidecarId(args.stateDir);
41979
+ try {
41980
+ const note = (line) => {
41981
+ process.stdout.write(` ${line}
41982
+ `);
41983
+ };
41984
+ const migrated = await migrateHomeLayout();
41985
+ if (migrated.moved.length) {
41986
+ note(`layout: moved ${migrated.moved.length} item(s) into the current ~/${stateDirName()} layout`);
41987
+ }
41988
+ for (const left of migrated.skipped) note(`layout: left ${left.path} alone (${left.reason})`);
41989
+ } catch (e2) {
41990
+ process.stdout.write(` layout: migration skipped (${e2 instanceof Error ? e2.message : String(e2)})
41991
+ `);
41992
+ }
41993
+ const sidecarId = await loadOrCreateSidecarId(sidecarSubdir(args.stateDir));
41771
41994
  const friendlyName = sidecarName({ name: args.name });
41772
- const hosts = makeHostsManifest(args.stateDir);
41995
+ const hosts = makeHostsManifest(sidecarSubdir(args.stateDir));
41773
41996
  if (args.tlsCert && !args.tlsKey || !args.tlsCert && args.tlsKey) {
41774
41997
  throw new Error("TLS needs both --tls-cert and --tls-key");
41775
41998
  }
@@ -41795,7 +42018,9 @@ async function main() {
41795
42018
  process.stdout.write(` [${(/* @__PURE__ */ new Date()).toTimeString().slice(0, 8)}] ${line}
41796
42019
  `);
41797
42020
  };
41798
- await checkHostsDrift(args.stateDir, hosts, slog);
42021
+ await assertPortFree(probeAddress(args.host), args.port);
42022
+ await assertStateDirFree(sidecarSubdir(args.stateDir));
42023
+ await checkHostsDrift(sidecarSubdir(args.stateDir), hosts, slog);
41799
42024
  const hostSync = makeHostSync({ stateDir: args.stateDir, manifest: hosts, log: slog });
41800
42025
  const attendance = makeAttendanceRegistry({ log: slog });
41801
42026
  const runs = makeRunHub({ log: slog });
@@ -41807,6 +42032,25 @@ async function main() {
41807
42032
  roots: args.roots,
41808
42033
  allowShell: args.allowShell,
41809
42034
  defaultModel: args.defaultModel,
42035
+ /* Which approved root this agent works in.
42036
+ *
42037
+ * MEMBERSHIP, never a prefix test. A prefix check would accept
42038
+ * `<approved>/../elsewhere`, and the operator's `--root` list is the
42039
+ * whole security boundary here: an agent definition can be PUSHED from a
42040
+ * browser, so `settings.workRoot` is a request from a client the daemon
42041
+ * does not trust -- exactly like the clientId that resolveIdentity
42042
+ * refuses to take at face value.
42043
+ *
42044
+ * An unrecognised root falls back to the primary rather than refusing to
42045
+ * run. Removing a --root should not silently convert an agent into a
42046
+ * dead one; it runs where it always did, and the browser is what reports
42047
+ * the mismatch. */
42048
+ workRootFor: (def) => {
42049
+ const want = def.settings.workRoots?.[sidecarId];
42050
+ if (want && args.roots.includes(want)) return want;
42051
+ if (want) slog(`crew: "${def.name}" asks for root "${want}", which is not approved \u2014 using ${args.roots[0]}`);
42052
+ return args.roots[0];
42053
+ },
41810
42054
  onScheduleSelf: () => core?.wake(),
41811
42055
  // The mirror's cue. Late-bound like `core` below: the runner is built
41812
42056
  // before the control plane, and a session cannot be written before both
@@ -41829,9 +42073,11 @@ async function main() {
41829
42073
  if (args.daemon && runner) {
41830
42074
  const flipped = await runner.reconcilePendingOnBoot();
41831
42075
  if (flipped > 0) slog(`daemon: marked ${flipped} stale pending permission record(s) dead (daemon restarted)`);
42076
+ const hostCrewDirs = (await listOwnerRoots(args.stateDir)).filter((r2) => r2.owner !== "").map((r2) => join15(hostsDirFor(args.stateDir, r2.owner), "crew"));
41832
42077
  const watchDirs = [
41833
- join13(args.stateDir, "crew"),
41834
- ...args.roots.map((r2) => join13(r2, stateDirName(), "crew"))
42078
+ join15(args.stateDir, "crew"),
42079
+ ...args.roots.map((r2) => join15(r2, stateDirName(), "crew")),
42080
+ ...hostCrewDirs
41835
42081
  ];
41836
42082
  core = startDaemonCore({
41837
42083
  // Task 12: the stall watchdog scans the SAME runner's in-flight runs
@@ -41936,7 +42182,6 @@ async function main() {
41936
42182
  controlPlane = null;
41937
42183
  }
41938
42184
  }
41939
- await assertPortFree(probeAddress(args.host), args.port);
41940
42185
  const procs = args.allowShell ? createNodeProcs({ roots: args.roots, log: slog }) : void 0;
41941
42186
  const sessions = runner ? await wireSessions({
41942
42187
  isolation: args.isolation,
@@ -42020,6 +42265,9 @@ async function main() {
42020
42265
  },
42021
42266
  hostSync
42022
42267
  });
42268
+ await writePidFile(sidecarSubdir(args.stateDir), args.port).catch((e2) => {
42269
+ slog(`state dir: could not record this daemon's pid (${e2 instanceof Error ? e2.message : String(e2)})`);
42270
+ });
42023
42271
  let tunnelSup = null;
42024
42272
  let tunnelWss = null;
42025
42273
  if (tunnelProvider) {
@@ -42081,6 +42329,7 @@ async function main() {
42081
42329
  openBrowser2(target);
42082
42330
  }
42083
42331
  const shutdown = () => {
42332
+ void clearPidFile(sidecarSubdir(args.stateDir));
42084
42333
  procs?.shutdown();
42085
42334
  const alive = sessions?.backend.list() ?? [];
42086
42335
  if (alive.length) {
@@ -42097,8 +42346,8 @@ async function main() {
42097
42346
  process.on("SIGTERM", shutdown);
42098
42347
  }
42099
42348
  main().catch((e2) => {
42100
- const code = e2?.code;
42101
- const msg = code === "EADDRINUSE" ? `port already in use \u2014 another sidecar may be running. Stop it, or pass --port <n>.` : e2 instanceof Error ? e2.message : String(e2);
42349
+ const code2 = e2?.code;
42350
+ 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
42351
  process.stderr.write(`corenel-sidecar: ${msg}
42103
42352
  `);
42104
42353
  process.exit(1);