@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.
- package/CHANGELOG.md +44 -0
- package/dist/cli.js +130 -10
- 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(
|
|
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: "
|
|
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.
|
|
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://
|
|
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.
|
|
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")
|
|
39546
|
-
|
|
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(/\/$/, "")}
|
|
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.
|
|
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.",
|