@corenel/sidecar 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +96 -24
  2. package/package.json +4 -4
package/dist/cli.js CHANGED
@@ -23292,6 +23292,15 @@ async function resolveCredential(env = process.env, stateDir, now2 = Date.now) {
23292
23292
  }
23293
23293
 
23294
23294
  // src/controlPlane.ts
23295
+ function describeBackend(url, mirrors, cred) {
23296
+ if (!mirrors) return { url, mirrors: false };
23297
+ return {
23298
+ url,
23299
+ mirrors: true,
23300
+ signedIn: !!cred.token,
23301
+ ...cred.expired ? { expired: true } : {}
23302
+ };
23303
+ }
23295
23304
  function makeControlPlane(deps) {
23296
23305
  if (!deps || !deps.baseUrl) return null;
23297
23306
  const log = deps.log ?? (() => void 0);
@@ -28408,10 +28417,65 @@ async function listOwnerRoots(stateDir) {
28408
28417
  return roots;
28409
28418
  }
28410
28419
 
28420
+ // src/crewDeployment.ts
28421
+ import { readFile } from "node:fs/promises";
28422
+ import { join as join9 } from "node:path";
28423
+
28424
+ // ../crew/deployment.ts
28425
+ var DEPLOYMENT_FILE = "deployment.json";
28426
+ function emptyDeployment() {
28427
+ return { agents: /* @__PURE__ */ Object.create(null) };
28428
+ }
28429
+ function deploymentKey(name, wsId) {
28430
+ return wsId ? `workspaces/${wsId}/crew/${name}` : `crew/${name}`;
28431
+ }
28432
+ function parseDeployment(text) {
28433
+ const out = emptyDeployment();
28434
+ let raw;
28435
+ try {
28436
+ raw = JSON.parse(text);
28437
+ } catch {
28438
+ return out;
28439
+ }
28440
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return out;
28441
+ const agents = raw.agents;
28442
+ if (!agents || typeof agents !== "object" || Array.isArray(agents)) return out;
28443
+ for (const [key, value] of Object.entries(agents)) {
28444
+ if (!value || typeof value !== "object" || Array.isArray(value)) continue;
28445
+ const root = value.root;
28446
+ if (typeof root === "string" && root) out.agents[key] = { root };
28447
+ }
28448
+ return out;
28449
+ }
28450
+ function deploymentRootFor(d2, key) {
28451
+ if (!Object.prototype.hasOwnProperty.call(d2.agents, key)) return void 0;
28452
+ return d2.agents[key].root;
28453
+ }
28454
+
28455
+ // src/crewDeployment.ts
28456
+ function splitOwner(owner) {
28457
+ if (owner === "") return void 0;
28458
+ const parts = owner.split("/");
28459
+ if (parts.length === 1) return { dirName: parts[0] };
28460
+ if (parts.length === 3 && parts[1] === "ws") return { dirName: parts[0], wsId: parts[2] };
28461
+ return void 0;
28462
+ }
28463
+ async function deployedRoot(stateDir, owner, agent) {
28464
+ const parts = splitOwner(owner);
28465
+ if (!parts) return void 0;
28466
+ let text;
28467
+ try {
28468
+ text = await readFile(join9(hostsDirFor(stateDir, parts.dirName), DEPLOYMENT_FILE), "utf8");
28469
+ } catch {
28470
+ return void 0;
28471
+ }
28472
+ return deploymentRootFor(parseDeployment(text), deploymentKey(agent, parts.wsId));
28473
+ }
28474
+
28411
28475
  // src/cert.ts
28412
28476
  import { promises as fs8 } from "node:fs";
28413
28477
  import { networkInterfaces } from "node:os";
28414
- import { join as join9 } from "node:path";
28478
+ import { join as join10 } from "node:path";
28415
28479
  import selfsigned from "selfsigned";
28416
28480
  function lanIPv4s() {
28417
28481
  const out = [];
@@ -28422,9 +28486,9 @@ function lanIPv4s() {
28422
28486
  }
28423
28487
  return [...new Set(out)];
28424
28488
  }
28425
- var certPath = () => join9(sidecarSubdir(), "sidecar.crt");
28426
- var keyPath = () => join9(sidecarSubdir(), "sidecar.key");
28427
- var sansPath = () => join9(sidecarSubdir(), "sidecar.sans.json");
28489
+ var certPath = () => join10(sidecarSubdir(), "sidecar.crt");
28490
+ var keyPath = () => join10(sidecarSubdir(), "sidecar.key");
28491
+ var sansPath = () => join10(sidecarSubdir(), "sidecar.sans.json");
28428
28492
  function isIp(s2) {
28429
28493
  return /^\d{1,3}(?:\.\d{1,3}){3}$/.test(s2) || s2.includes(":");
28430
28494
  }
@@ -38505,7 +38569,7 @@ var NodeRunnerService = class {
38505
38569
  fromWorkdir ? `crew agent not found: ${agent} (looked in ${fromWorkdir} and the owner's own crew)` : `crew agent not found: ${agent}`
38506
38570
  );
38507
38571
  }
38508
- const workDir = this.opts.workRootFor?.(def) ?? this.opts.roots[0];
38572
+ const workDir = await this.opts.workRootFor?.(def, owner) ?? this.opts.roots[0];
38509
38573
  if (!workDir) throw new Error("daemon has no --root workspace to run an agent in");
38510
38574
  const workFiles = new NodeFileService(workDir);
38511
38575
  const deps = this.opts.deps ?? {
@@ -38617,7 +38681,7 @@ var NodeRunnerService = class {
38617
38681
  // src/runner/core.ts
38618
38682
  import { watch } from "node:fs";
38619
38683
  import { readdir as readdir2, stat } from "node:fs/promises";
38620
- import { join as join10, relative as relative5, sep as sep3 } from "node:path";
38684
+ import { join as join11, relative as relative5, sep as sep3 } from "node:path";
38621
38685
 
38622
38686
  // src/runner/triggerConfig.ts
38623
38687
  function inputFor(trigger, fallback) {
@@ -39127,9 +39191,9 @@ function startDaemonCore(opts) {
39127
39191
  for (const entry of entries) {
39128
39192
  if (entry.isDirectory()) {
39129
39193
  if (isExcludedSegment(entry.name, dotDir)) continue;
39130
- await walkForChanges(root, join10(dir, entry.name), since);
39194
+ await walkForChanges(root, join11(dir, entry.name), since);
39131
39195
  } else {
39132
- const abs = join10(dir, entry.name);
39196
+ const abs = join11(dir, entry.name);
39133
39197
  let mtimeMs;
39134
39198
  try {
39135
39199
  mtimeMs = (await stat(abs)).mtimeMs;
@@ -39410,9 +39474,9 @@ function makeRunHub(opts = {}) {
39410
39474
  import { promises as fs10 } from "node:fs";
39411
39475
  import { randomBytes as randomBytes2 } from "node:crypto";
39412
39476
  import { hostname } from "node:os";
39413
- import { join as join11 } from "node:path";
39477
+ import { join as join12 } from "node:path";
39414
39478
  function idPath(dir = sidecarSubdir()) {
39415
- return join11(dir, "daemon.id");
39479
+ return join12(dir, "daemon.id");
39416
39480
  }
39417
39481
  async function loadOrCreateSidecarId(dir = sidecarSubdir()) {
39418
39482
  const path = idPath(dir);
@@ -39441,7 +39505,7 @@ function sidecarName(opts) {
39441
39505
  // src/hosts.ts
39442
39506
  import { promises as fs11 } from "node:fs";
39443
39507
  import { randomBytes as randomBytes3 } from "node:crypto";
39444
- import { join as join12 } from "node:path";
39508
+ import { join as join13, dirname as dirname4 } from "node:path";
39445
39509
  var MAX_DIR = 32;
39446
39510
  var MAX_FIELD_LEN = 200;
39447
39511
  var MAX_HOSTS = 200;
@@ -39474,8 +39538,8 @@ function sanitizeDirName(raw) {
39474
39538
  const safe = collapsed || "host";
39475
39539
  return RESERVED_WINDOWS_NAMES.has(safe) ? `${safe}-host` : safe;
39476
39540
  }
39477
- function hostsPath(dir) {
39478
- return join12(dir, "hosts.json");
39541
+ function hostsPath(stateDir) {
39542
+ return join13(stateDir, "sidecar", "hosts.json");
39479
39543
  }
39480
39544
  function emptyManifest() {
39481
39545
  return /* @__PURE__ */ Object.create(null);
@@ -39514,10 +39578,11 @@ async function read(dir, fallbackLastSeen) {
39514
39578
  }
39515
39579
  return manifest;
39516
39580
  }
39517
- async function write(dir, manifest) {
39581
+ async function write(stateDir, manifest) {
39582
+ const dir = dirname4(hostsPath(stateDir));
39518
39583
  await fs11.mkdir(dir, { recursive: true });
39519
- const target = hostsPath(dir);
39520
- const tmp = join12(dir, `.hosts.json.tmp-${randomBytes3(8).toString("hex")}`);
39584
+ const target = hostsPath(stateDir);
39585
+ const tmp = join13(dir, `.hosts.json.tmp-${randomBytes3(8).toString("hex")}`);
39521
39586
  try {
39522
39587
  await fs11.writeFile(tmp, JSON.stringify(manifest, null, 2));
39523
39588
  await fs11.rename(tmp, target);
@@ -39670,7 +39735,7 @@ function makeHostSync(opts) {
39670
39735
  }
39671
39736
 
39672
39737
  // src/hostsCli.ts
39673
- import { resolve as resolve5, join as join13 } from "node:path";
39738
+ import { resolve as resolve5 } from "node:path";
39674
39739
  import { promises as fs12 } from "node:fs";
39675
39740
  async function listHosts(manifest) {
39676
39741
  const m2 = await manifest.list();
@@ -39705,10 +39770,10 @@ async function forgetHost(stateDir, manifest, dirName2) {
39705
39770
  return { ok: true };
39706
39771
  }
39707
39772
  async function checkHostsDrift(stateDir, manifest, log) {
39708
- const hostsRoot2 = join13(stateDir, HOSTS_SEG);
39773
+ const root = hostsRoot(stateDir);
39709
39774
  let entries;
39710
39775
  try {
39711
- entries = (await fs12.readdir(hostsRoot2, { withFileTypes: true })).filter((d2) => d2.isDirectory()).map((d2) => d2.name);
39776
+ entries = (await fs12.readdir(root, { withFileTypes: true })).filter((d2) => d2.isDirectory()).map((d2) => d2.name);
39712
39777
  } catch {
39713
39778
  return [];
39714
39779
  }
@@ -41992,7 +42057,7 @@ async function main() {
41992
42057
  }
41993
42058
  const sidecarId = await loadOrCreateSidecarId(sidecarSubdir(args.stateDir));
41994
42059
  const friendlyName = sidecarName({ name: args.name });
41995
- const hosts = makeHostsManifest(sidecarSubdir(args.stateDir));
42060
+ const hosts = makeHostsManifest(args.stateDir);
41996
42061
  if (args.tlsCert && !args.tlsKey || !args.tlsCert && args.tlsKey) {
41997
42062
  throw new Error("TLS needs both --tls-cert and --tls-key");
41998
42063
  }
@@ -42020,7 +42085,7 @@ async function main() {
42020
42085
  };
42021
42086
  await assertPortFree(probeAddress(args.host), args.port);
42022
42087
  await assertStateDirFree(sidecarSubdir(args.stateDir));
42023
- await checkHostsDrift(sidecarSubdir(args.stateDir), hosts, slog);
42088
+ await checkHostsDrift(args.stateDir, hosts, slog);
42024
42089
  const hostSync = makeHostSync({ stateDir: args.stateDir, manifest: hosts, log: slog });
42025
42090
  const attendance = makeAttendanceRegistry({ log: slog });
42026
42091
  const runs = makeRunHub({ log: slog });
@@ -42045,8 +42110,8 @@ async function main() {
42045
42110
  * run. Removing a --root should not silently convert an agent into a
42046
42111
  * dead one; it runs where it always did, and the browser is what reports
42047
42112
  * the mismatch. */
42048
- workRootFor: (def) => {
42049
- const want = def.settings.workRoots?.[sidecarId];
42113
+ workRootFor: async (def, owner) => {
42114
+ const want = await deployedRoot(args.stateDir, owner, def.name) ?? def.settings.workRoots?.[sidecarId];
42050
42115
  if (want && args.roots.includes(want)) return want;
42051
42116
  if (want) slog(`crew: "${def.name}" asks for root "${want}", which is not approved \u2014 using ${args.roots[0]}`);
42052
42117
  return args.roots[0];
@@ -42168,16 +42233,21 @@ async function main() {
42168
42233
  ...args.stateDir ? { stateDir: args.stateDir } : {},
42169
42234
  log: slog
42170
42235
  });
42236
+ let backendInfo;
42171
42237
  if (controlPlane) {
42172
42238
  const caps = await controlPlane.backend.probe();
42173
42239
  if (caps.includes("sessions")) {
42174
42240
  slog(`backend: ${args.endpoint} mirrors sessions (as "${friendlyName || sidecarId}")`);
42175
42241
  const cred = await resolveCredential(process.env, args.stateDir);
42242
+ backendInfo = describeBackend(args.endpoint, true, cred);
42176
42243
  if (!cred.token) {
42177
42244
  slog("backend: NOT SIGNED IN \u2014 pushes will be refused until `corenel login` (or CORENEL_TOKEN)");
42245
+ } else if (cred.expired) {
42246
+ slog("backend: SIGN-IN EXPIRED \u2014 pushes will be refused until `corenel login`");
42178
42247
  }
42179
42248
  } else {
42180
42249
  slog(`backend: ${args.endpoint} holds no sessions \u2014 working local-only`);
42250
+ backendInfo = describeBackend(args.endpoint, false, { token: null });
42181
42251
  controlPlane.dispose();
42182
42252
  controlPlane = null;
42183
42253
  }
@@ -42245,7 +42315,9 @@ async function main() {
42245
42315
  billing: acpAuth.billing,
42246
42316
  notes: [...acpAuth.notes]
42247
42317
  }
42248
- } : {}
42318
+ } : {},
42319
+ // Absent when this daemon has no backend at all — see backendInfo above.
42320
+ ...backendInfo ? { backend: backendInfo } : {}
42249
42321
  },
42250
42322
  // The mutable root set. Present only so a client may add a working directory
42251
42323
  // mid-session; with no --allow-add-under its areas are empty and every add is
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@corenel/sidecar",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "type": "module",
5
5
  "license": "Elastic-2.0",
6
6
  "description": "Prompd sidecar — a local capability server. Run it next to Prompd (prompd.app) so the browser app can reach your machine: local LLMs (Ollama/LM Studio/vLLM) without CORS, your filesystem, and (opt-in) shell.",
@@ -36,10 +36,10 @@
36
36
  "esbuild": "^0.21.5",
37
37
  "tsx": "^4.19.2",
38
38
  "typescript": "^5.9.3",
39
- "@corenel/harness": "0.4.1",
39
+ "@corenel/protocol": "0.4.2",
40
40
  "@corenel/tools-node": "0.2.2",
41
- "@corenel/protocol": "0.4.1",
42
- "@corenel/crew": "0.0.0"
41
+ "@corenel/crew": "0.0.0",
42
+ "@corenel/harness": "0.4.1"
43
43
  },
44
44
  "scripts": {
45
45
  "prebuild": "pnpm --filter @corenel/protocol build && pnpm --filter @corenel/harness build",