@corenel/sidecar 0.3.0 → 0.3.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/CHANGELOG.md +44 -0
  2. package/dist/cli.js +130 -10
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.1
4
+
5
+ This package is Corenel's, and now says so in its defaults. 0.3.0 pointed its
6
+ gateway, and the app it opens, at the sibling product.
7
+
8
+ The direction is the fix: prompd.app is a CONSUMER of these APIs, so it names
9
+ its own origin (`--app-url https://prompd.app/studio`, `--endpoint <its own>`)
10
+ and corenel.ai is what you get without asking.
11
+
12
+ ### Added
13
+ - `sessions` subcommand: `corenel-sidecar sessions [list | show <id>]`, also
14
+ reachable as `corenel sessions`. Read-only, and read from the SANDBOXES rather
15
+ than from a daemon -- a subcommand runs in a new process and cannot see a
16
+ running daemon's SessionManager, so anything derived from it would be a guess.
17
+ Asking msb gets the same answer the daemon gets when it adopts at boot, and it
18
+ works with nothing running.
19
+
20
+ ### Changed
21
+ - `--app-url` defaults to `https://corenel.ai`, and the open URL no longer has
22
+ `/studio` appended to it. That is a prompd route; corenel serves its app at
23
+ `/`, so the old shape would have opened a page that does not exist and lost
24
+ the pairing fragment on the redirect. `--app-url` is now the app URL in full,
25
+ path included -- prompd pairs at `https://prompd.app/studio`.
26
+
27
+ ### Fixed
28
+ - The daemon called `https://api.prompd.app` for every model run -- the sibling
29
+ product's gateway, with a token minted against this one. The default endpoint
30
+ is now `https://api.corenel.ai/api`.
31
+ - That default was also missing the `/api` prefix the gateway is mounted behind.
32
+ `setGatewayBase` appends `/v1`, so a bare origin composed
33
+ `https://host/v1/chat/completions` and 404'd -- surfacing as "the run failed",
34
+ nowhere near the flag responsible. A bare origin now has the prefix added, and
35
+ any endpoint carrying a path is left exactly as given.
36
+ - `--allow-lan` did not do what it says. It widened the CORS origin allowlist
37
+ while the socket stayed bound to `127.0.0.1`, so nothing on the network could
38
+ connect and the banner still printed a loopback URL. It now binds all
39
+ interfaces and serves `wss://` with the self-signed cert (which already
40
+ covered the LAN IPs for exactly this). Both halves defer to anything
41
+ explicit: `--host` keeps its address, a supplied `--tls-cert` is not replaced.
42
+
43
+ This is the only way LAN pairing can work from the product: the app is served
44
+ over https, and a browser on an https page refuses a `ws://` connection to a
45
+ LAN address.
46
+
3
47
  ## 0.3.0
4
48
 
5
49
  The isolation release. 0.2.0 could expose a machine's files and shell to a
package/dist/cli.js CHANGED
@@ -36619,7 +36619,7 @@ var openai_default = OpenAI;
36619
36619
  var enabled = true;
36620
36620
  function setAgentLogging(on) {
36621
36621
  enabled = on;
36622
- console.log(`[agent] logging ${on ? "enabled" : "disabled"}`);
36622
+ if (on) console.log("[agent] logging enabled");
36623
36623
  }
36624
36624
  function alog(...args) {
36625
36625
  if (enabled) console.debug("[agent]", ...args);
@@ -36738,8 +36738,18 @@ function createChatClient(auth, opts = {}) {
36738
36738
  }
36739
36739
 
36740
36740
  // src/runner/gatewayClient.ts
36741
+ var API_PREFIX = "/api";
36742
+ function normalizeEndpoint(endpoint) {
36743
+ const trimmed = endpoint.replace(/\/+$/, "");
36744
+ try {
36745
+ const u2 = new URL(trimmed);
36746
+ return u2.pathname === "" || u2.pathname === "/" ? `${u2.origin}${API_PREFIX}` : trimmed;
36747
+ } catch {
36748
+ return trimmed;
36749
+ }
36750
+ }
36741
36751
  function nodeGatewayClient(endpoint) {
36742
- setGatewayBase(endpoint);
36752
+ setGatewayBase(normalizeEndpoint(endpoint));
36743
36753
  return createChatClient(nodeAuthToken(), { origin: "daemon" });
36744
36754
  }
36745
36755
 
@@ -39364,6 +39374,97 @@ async function runHostsCli(argv) {
39364
39374
  }
39365
39375
  }
39366
39376
 
39377
+ // src/sessionsCli.ts
39378
+ async function listSessions(run) {
39379
+ const listed = await run(listArgs()).catch(() => null);
39380
+ if (!listed || listed.code !== 0) return [];
39381
+ const rows = [];
39382
+ for (const box of parseSandboxList(listed.stdout)) {
39383
+ const inspected = await run(inspectArgs(box.name)).catch(() => null);
39384
+ const labels = inspected && inspected.code === 0 ? parseSandboxLabels(inspected.stdout) : {};
39385
+ const id = labels[LABEL_SESSION];
39386
+ if (!id) continue;
39387
+ const url = labels[LABEL_REPO_URL];
39388
+ const ref = labels[LABEL_REPO_REF];
39389
+ rows.push({
39390
+ id,
39391
+ sandbox: box.name,
39392
+ status: box.status || "unknown",
39393
+ workdir: labels[LABEL_WORKDIR] ?? "",
39394
+ owner: labels[LABEL_OWNER] ?? "",
39395
+ created: labels[LABEL_CREATED] ?? (box.createdAt ? new Date(box.createdAt).toISOString() : ""),
39396
+ ...url ? { repo: ref ? `${url}#${ref}` : url } : {}
39397
+ });
39398
+ }
39399
+ return rows;
39400
+ }
39401
+ function formatSessions(rows) {
39402
+ if (!rows.length) {
39403
+ return [
39404
+ "no sessions on this machine.",
39405
+ "",
39406
+ "Sessions are created by a daemon running with --isolation microvm.",
39407
+ "Without it, runs execute in the daemon process and leave nothing to list."
39408
+ ].join("\n");
39409
+ }
39410
+ const w2 = (pick2, head) => Math.max(head.length, ...rows.map((r2) => pick2(r2).length));
39411
+ const idW = w2((r2) => r2.id, "session");
39412
+ const stW = w2((r2) => r2.status, "status");
39413
+ const wdW = w2((r2) => r2.workdir, "workdir");
39414
+ const line = (id, st2, wd, cr2) => `${id.padEnd(idW)} ${st2.padEnd(stW)} ${wd.padEnd(wdW)} ${cr2}`;
39415
+ return [
39416
+ line("session", "status", "workdir", "created"),
39417
+ ...rows.map((r2) => line(r2.id, r2.status, r2.workdir, r2.created))
39418
+ ].join("\n");
39419
+ }
39420
+ function formatSession(r2) {
39421
+ return [
39422
+ `session ${r2.id}`,
39423
+ `sandbox ${r2.sandbox}`,
39424
+ `status ${r2.status}`,
39425
+ `workdir ${r2.workdir || "(none)"}`,
39426
+ `owner ${r2.owner || "(none)"}`,
39427
+ `created ${r2.created || "(unknown)"}`,
39428
+ ...r2.repo ? [`repo ${r2.repo}`] : []
39429
+ ].join("\n");
39430
+ }
39431
+ async function runSessionsCli(argv, deps) {
39432
+ const out = deps.out ?? ((s2) => process.stdout.write(s2));
39433
+ const err = deps.err ?? ((s2) => process.stderr.write(s2));
39434
+ const run = deps.run ?? makeMsbRunner(deps.msbBin, deps.wsl ? wslMsb(deps.msbBin) : void 0);
39435
+ const [verb, ...rest] = argv;
39436
+ if (verb === void 0 || verb === "list") {
39437
+ if (rest.length) {
39438
+ err(`corenel sessions list: unexpected argument "${rest[0]}"
39439
+ `);
39440
+ return 1;
39441
+ }
39442
+ out(`${formatSessions(await listSessions(run))}
39443
+ `);
39444
+ return 0;
39445
+ }
39446
+ if (verb === "show") {
39447
+ const id = rest[0];
39448
+ if (!id) {
39449
+ err("corenel sessions show: missing <id>\n");
39450
+ return 1;
39451
+ }
39452
+ const found = (await listSessions(run)).find((r2) => r2.id === id || r2.sandbox === id);
39453
+ if (!found) {
39454
+ err(`corenel sessions: no session "${id}" on this machine
39455
+ `);
39456
+ return 1;
39457
+ }
39458
+ out(`${formatSession(found)}
39459
+ `);
39460
+ return 0;
39461
+ }
39462
+ err(`corenel sessions: unknown subcommand "${verb}"
39463
+ usage: corenel sessions [list | show <id>]
39464
+ `);
39465
+ return 1;
39466
+ }
39467
+
39367
39468
  // src/args.ts
39368
39469
  import { statSync as statSync2 } from "node:fs";
39369
39470
  import { resolve as resolve6 } from "node:path";
@@ -39376,7 +39477,7 @@ var FLAGS = [
39376
39477
  { flag: "--allow-add-under", arg: "<dir>", help: "Let the client add working directories beneath <dir> during a session. Repeatable. Grants directory-name enumeration over the subtree. Added roots last until the sidecar exits." },
39377
39478
  { flag: "--allow-origin", arg: "<url>", help: "Allow a browser origin to connect. Repeatable." },
39378
39479
  { flag: "--allow-shell", help: "Enable the shell tool. Off by default." },
39379
- { flag: "--allow-lan", help: "Accept connections from the local network, not just this machine." },
39480
+ { flag: "--allow-lan", help: "Serve the local network, not just this machine: binds all interfaces and serves wss with a self-signed cert. --host / --tls-cert override each half." },
39380
39481
  { flag: "--tls-cert", arg: "<file>", help: "TLS certificate for wss://." },
39381
39482
  { flag: "--tls-key", arg: "<file>", help: "TLS private key for wss://." },
39382
39483
  { flag: "--self-signed", help: "Generate a self-signed certificate instead of supplying one." },
@@ -39384,9 +39485,9 @@ var FLAGS = [
39384
39485
  { flag: "--tunnel-name", arg: "<name>", help: "Use a named cloudflared tunnel." },
39385
39486
  { flag: "--tunnel-hostname", arg: "<host>", help: "Hostname for the named tunnel." },
39386
39487
  { flag: "--open", help: "Open the app in a browser once the sidecar is listening." },
39387
- { flag: "--app-url", arg: "<url>", help: "App to open and pair with (default https://prompd.app)." },
39488
+ { 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)." },
39388
39489
  { flag: "--daemon", help: "Run crew agents on this machine unattended: cron, file-change and run-complete triggers." },
39389
- { flag: "--endpoint", arg: "<url>", help: "Gateway the daemon calls for model runs (default https://api.prompd.app)." },
39490
+ { flag: "--endpoint", arg: "<url>", help: "Gateway the daemon calls for model runs (default https://api.corenel.ai/api). A bare origin gets /api appended." },
39390
39491
  { flag: "--state-dir", arg: "<dir>", help: "Where crew agents, sessions and tokens live (defaults to the Corenel dot-dir)." },
39391
39492
  { flag: "--default-model", arg: "<id>", help: "Model for crew agents that do not name one." },
39392
39493
  { flag: "--name", arg: "<label>", help: "Friendly name advertised to clients (defaults to the hostname)." },
@@ -39442,6 +39543,7 @@ function helpText() {
39442
39543
  "Subcommands:",
39443
39544
  " hosts list Show clients that have paired with this sidecar.",
39444
39545
  " hosts forget Remove a paired client and its synced files.",
39546
+ " sessions List sandbox sessions on this machine (also `corenel sessions`).",
39445
39547
  " acp-agent Speak ACP over stdio, for an editor to spawn (Zed, JetBrains).",
39446
39548
  " login Sign in to Corenel in your browser.",
39447
39549
  " logout Forget the stored sign-in (paired devices are unaffected).",
@@ -39527,14 +39629,18 @@ function parseArgs(argv) {
39527
39629
  guestImage: "corenel-guest:dev",
39528
39630
  selfSigned: false,
39529
39631
  open: false,
39530
- appUrl: "https://prompd.app",
39632
+ appUrl: "https://corenel.ai",
39633
+ // The gateway this package's own product talks to, and it carries the /api
39634
+ // prefix the gateway actually lives behind -- setGatewayBase appends /v1,
39635
+ // so a bare origin composes .../v1/chat/completions, which is a 404.
39531
39636
  daemon: false,
39532
- endpoint: "https://api.prompd.app",
39637
+ endpoint: "https://api.corenel.ai/api",
39533
39638
  stateDir: corenelDir2(),
39534
39639
  maxChainDepth: 8,
39535
39640
  addUnder: [],
39536
39641
  cloneUnder: []
39537
39642
  };
39643
+ let hostSet = false;
39538
39644
  for (let i2 = 0; i2 < argv.length; i2++) {
39539
39645
  const a2 = argv[i2];
39540
39646
  if (isHelpFlag(a2)) {
@@ -39542,8 +39648,10 @@ function parseArgs(argv) {
39542
39648
  process.exit(0);
39543
39649
  }
39544
39650
  if (a2 === "--port") out.port = Number(argv[++i2]);
39545
- else if (a2 === "--host") out.host = argv[++i2];
39546
- else if (a2 === "--root") out.roots.push(resolve6(argv[++i2]));
39651
+ else if (a2 === "--host") {
39652
+ out.host = argv[++i2];
39653
+ hostSet = true;
39654
+ } else if (a2 === "--root") out.roots.push(resolve6(argv[++i2]));
39547
39655
  else if (a2 === "--allow-add-under") out.addUnder.push(resolve6(argv[++i2]));
39548
39656
  else if (a2 === "--clone-under") out.cloneUnder.push(resolve6(argv[++i2]));
39549
39657
  else if (a2 === "--allow-origin") out.allowOrigins.push(argv[++i2]);
@@ -39580,6 +39688,10 @@ valid flags: ${KNOWN_FLAGS.join(" ")}`);
39580
39688
  }
39581
39689
  out.addUnder = [...new Set(out.addUnder)];
39582
39690
  out.cloneUnder = [...new Set(out.cloneUnder)];
39691
+ if (out.allowLan) {
39692
+ if (!hostSet) out.host = "0.0.0.0";
39693
+ if (!out.tlsCert && !out.tlsKey) out.selfSigned = true;
39694
+ }
39583
39695
  return out;
39584
39696
  }
39585
39697
  function validateArgs(args) {
@@ -41086,6 +41198,14 @@ async function main() {
41086
41198
  await runHostsCli(rawArgv.slice(1));
41087
41199
  return;
41088
41200
  }
41201
+ if (rawArgv[0] === "sessions") {
41202
+ const rest = rawArgv.slice(1);
41203
+ const at2 = rest.indexOf("--msb");
41204
+ const msbBin = at2 >= 0 && rest[at2 + 1] ? rest[at2 + 1] : process.env.MSB_BIN ?? "msb";
41205
+ const verbs = rest.filter((a2, i2) => !a2.startsWith("--") && !(i2 > 0 && rest[i2 - 1].startsWith("--")));
41206
+ process.exitCode = await runSessionsCli(verbs, { msbBin, wsl: process.platform === "win32" });
41207
+ return;
41208
+ }
41089
41209
  if (rawArgv[0] === "login" || rawArgv[0] === "logout" || rawArgv[0] === "auth") {
41090
41210
  const sub = rawArgv[0];
41091
41211
  const parsed = parseArgs(rawArgv.slice(1));
@@ -41408,7 +41528,7 @@ async function main() {
41408
41528
  );
41409
41529
  if (args.open) {
41410
41530
  const loopback = `${handle.secure ? "wss" : "ws"}://127.0.0.1:${handle.port}`;
41411
- const target = `${args.appUrl.replace(/\/$/, "")}/studio#sidecar=${encodeURIComponent(loopback)}&token=${encodeURIComponent(token)}`;
41531
+ const target = `${args.appUrl.replace(/\/$/, "")}#sidecar=${encodeURIComponent(loopback)}&token=${encodeURIComponent(token)}`;
41412
41532
  process.stdout.write(` opening ${args.appUrl} (auto-pairing)\u2026
41413
41533
  `);
41414
41534
  openBrowser2(target);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@corenel/sidecar",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
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.",