@corenel/sidecar 0.1.3 → 0.1.5
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/README.md +2 -2
- package/dist/cli.js +154 -24
- package/package.json +48 -41
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ match the site you're connecting from (the app fills it in for you).
|
|
|
25
25
|
| `--allow-origin <url>` | _(none)_ | Browser origin allowed to connect. Required for a web page. Repeatable. |
|
|
26
26
|
| `--port <n>` | `4858` | Port to listen on. |
|
|
27
27
|
| `--root <dir>` | cwd | Root the file tools are confined to. |
|
|
28
|
-
| `--allow-shell` | off | Enable the `
|
|
28
|
+
| `--allow-shell` | off | Enable the `sidecar_shell_exec` tool (RCE on your machine — opt-in). |
|
|
29
29
|
| `--host <addr>` | `127.0.0.1` | Bind address. Non-loopback is reachable off-box; keep the token secret. |
|
|
30
30
|
| `--tls-cert <f>` `--tls-key <f>` | _(none)_ | Serve `wss://` (needed for a remote sidecar from an https page). |
|
|
31
31
|
|
|
@@ -35,6 +35,6 @@ match the site you're connecting from (the app fills it in for you).
|
|
|
35
35
|
allowed Origin.
|
|
36
36
|
- The HTTP proxy (for local LLMs) only reaches **loopback** hosts — it can't be used as
|
|
37
37
|
an SSRF pivot to internal services.
|
|
38
|
-
- `
|
|
38
|
+
- `sidecar_shell_exec` is off unless you pass `--allow-shell`.
|
|
39
39
|
|
|
40
40
|
Licensed under Elastic-2.0.
|
package/dist/cli.js
CHANGED
|
@@ -10,6 +10,13 @@ import { spawn as spawn4 } from "node:child_process";
|
|
|
10
10
|
import { WebSocketServer } from "ws";
|
|
11
11
|
import { createServer as createHttpsServer } from "node:https";
|
|
12
12
|
|
|
13
|
+
// ../protocol/stateDir.ts
|
|
14
|
+
var DEFAULT_STATE_DIR = ".corenel";
|
|
15
|
+
var dirName = DEFAULT_STATE_DIR;
|
|
16
|
+
function stateDirName() {
|
|
17
|
+
return dirName;
|
|
18
|
+
}
|
|
19
|
+
|
|
13
20
|
// ../protocol/sidecar.ts
|
|
14
21
|
var SIDECAR_PROTOCOL = 1;
|
|
15
22
|
function isLoopbackHost(host) {
|
|
@@ -39,6 +46,10 @@ var SidecarServer = class {
|
|
|
39
46
|
authed = false;
|
|
40
47
|
hostSeq = 0;
|
|
41
48
|
hostPending = /* @__PURE__ */ new Map();
|
|
49
|
+
/** The connected host's dot-dir name, from `hello.stateDir` (informational —
|
|
50
|
+
* doesn't change any sidecar behavior yet; reserved for future workspace-state
|
|
51
|
+
* features that need to know the host's dir name). */
|
|
52
|
+
hostStateDir = ".corenel";
|
|
42
53
|
constructor(opts) {
|
|
43
54
|
this.opts = opts;
|
|
44
55
|
this.ch = opts.channel;
|
|
@@ -88,7 +99,8 @@ var SidecarServer = class {
|
|
|
88
99
|
return;
|
|
89
100
|
}
|
|
90
101
|
this.authed = true;
|
|
91
|
-
this.
|
|
102
|
+
this.hostStateDir = m.stateDir ?? ".corenel";
|
|
103
|
+
this.opts.log?.(`paired \u2014 ${this.tools.size} ${this.tools.size === 1 ? "tool" : "tools"} advertised (host dir: ${this.hostStateDir})`);
|
|
92
104
|
const tools = [...this.tools.values()].map(({ run: _run, ...decl }) => decl);
|
|
93
105
|
this.ch.send({ t: "welcome", tools, features: ["http-proxy"], ...this.opts.info ? { info: this.opts.info } : {} });
|
|
94
106
|
}
|
|
@@ -349,7 +361,7 @@ var MAX_READ_BYTES = 16 * 1024 * 1024;
|
|
|
349
361
|
function nodeFsTools(roots) {
|
|
350
362
|
return [
|
|
351
363
|
{
|
|
352
|
-
name: "
|
|
364
|
+
name: "sidecar_fs_read_file",
|
|
353
365
|
description: "Read a UTF-8 text file from the sidecar machine, relative to a pre-approved root.",
|
|
354
366
|
parameters: {
|
|
355
367
|
type: "object",
|
|
@@ -368,7 +380,7 @@ function nodeFsTools(roots) {
|
|
|
368
380
|
}
|
|
369
381
|
},
|
|
370
382
|
{
|
|
371
|
-
name: "
|
|
383
|
+
name: "sidecar_fs_write_file",
|
|
372
384
|
description: "Write a UTF-8 text file on the sidecar machine, relative to a pre-approved root.",
|
|
373
385
|
parameters: {
|
|
374
386
|
type: "object",
|
|
@@ -390,7 +402,7 @@ function nodeFsTools(roots) {
|
|
|
390
402
|
}
|
|
391
403
|
},
|
|
392
404
|
{
|
|
393
|
-
name: "
|
|
405
|
+
name: "sidecar_fs_read_bytes",
|
|
394
406
|
description: "Read a file from the sidecar machine as base64 (for previewing media/binary the text read would mangle), relative to a pre-approved root.",
|
|
395
407
|
parameters: {
|
|
396
408
|
type: "object",
|
|
@@ -409,7 +421,7 @@ function nodeFsTools(roots) {
|
|
|
409
421
|
}
|
|
410
422
|
},
|
|
411
423
|
{
|
|
412
|
-
name: "
|
|
424
|
+
name: "sidecar_fs_list_dir",
|
|
413
425
|
description: "List the entries of a directory on the sidecar machine, relative to a pre-approved root.",
|
|
414
426
|
parameters: {
|
|
415
427
|
type: "object",
|
|
@@ -430,7 +442,7 @@ var MAX_TIMEOUT_MS = 3e5;
|
|
|
430
442
|
var MAX_OUTPUT = 1e5;
|
|
431
443
|
function nodeShellTool(roots) {
|
|
432
444
|
return {
|
|
433
|
-
name: "
|
|
445
|
+
name: "sidecar_shell_exec",
|
|
434
446
|
description: "Run a shell command on the sidecar machine (commands a browser cannot spawn). Runs in a pre-approved root by default; returns the exit status plus combined stdout/stderr.",
|
|
435
447
|
parameters: {
|
|
436
448
|
type: "object",
|
|
@@ -497,7 +509,7 @@ import { randomBytes } from "node:crypto";
|
|
|
497
509
|
import { homedir } from "node:os";
|
|
498
510
|
import { join } from "node:path";
|
|
499
511
|
function corenelDir() {
|
|
500
|
-
return join(homedir(),
|
|
512
|
+
return join(homedir(), stateDirName());
|
|
501
513
|
}
|
|
502
514
|
function tokenPath() {
|
|
503
515
|
return join(corenelDir(), "daemon.token");
|
|
@@ -701,10 +713,20 @@ function extractTunnelUrl(s) {
|
|
|
701
713
|
const m = s.match(/https:\/\/[a-z0-9-]+\.trycloudflare\.com/i);
|
|
702
714
|
return m ? m[0] : null;
|
|
703
715
|
}
|
|
716
|
+
function isRegisteredLine(s) {
|
|
717
|
+
return /Registered tunnel connection/.test(s);
|
|
718
|
+
}
|
|
719
|
+
function isTunnelGoneLine(s) {
|
|
720
|
+
return /Unauthorized: Tunnel not found/i.test(s);
|
|
721
|
+
}
|
|
722
|
+
var GONE_LINE_LIMIT = 3;
|
|
704
723
|
var START_TIMEOUT_MS2 = 25e3;
|
|
705
724
|
function tunnelArgs(port, host = "localhost") {
|
|
706
725
|
return ["tunnel", "--no-autoupdate", "--protocol", "http2", "--url", `http://${host}:${port}`];
|
|
707
726
|
}
|
|
727
|
+
function namedTunnelArgs(name, port, host = "localhost") {
|
|
728
|
+
return ["tunnel", "--no-autoupdate", "--protocol", "http2", "run", "--url", `http://${host}:${port}`, name];
|
|
729
|
+
}
|
|
708
730
|
var tail2 = (s, n = 6) => s.trim().split("\n").slice(-n).join("\n");
|
|
709
731
|
function cloudflaredBins() {
|
|
710
732
|
const env = process.env.CLOUDFLARED_BIN?.trim();
|
|
@@ -735,24 +757,34 @@ function stopChild2(child) {
|
|
|
735
757
|
}
|
|
736
758
|
});
|
|
737
759
|
}
|
|
738
|
-
function startWith2(bin,
|
|
760
|
+
function startWith2(bin, mode, log, onDown) {
|
|
739
761
|
return new Promise((resolveHandle, reject) => {
|
|
740
|
-
const child = spawn3(bin,
|
|
762
|
+
const child = spawn3(bin, mode.argv, { stdio: ["ignore", "pipe", "pipe"] });
|
|
741
763
|
let out = "";
|
|
742
764
|
let settled = false;
|
|
743
765
|
let tailBuf = "";
|
|
766
|
+
let goneLines = 0;
|
|
767
|
+
let downFired = false;
|
|
744
768
|
const finish = (fn) => {
|
|
745
769
|
if (settled) return;
|
|
746
770
|
settled = true;
|
|
747
771
|
clearTimeout(timer);
|
|
748
772
|
fn();
|
|
749
773
|
};
|
|
774
|
+
const fireDown = (reason) => {
|
|
775
|
+
if (downFired) return;
|
|
776
|
+
downFired = true;
|
|
777
|
+
onDown?.(reason);
|
|
778
|
+
};
|
|
750
779
|
const onData = (b) => {
|
|
751
780
|
const text = b.toString();
|
|
752
781
|
if (!settled) {
|
|
753
782
|
out += text;
|
|
754
|
-
const url =
|
|
755
|
-
if (url) finish(() => resolveHandle({ url, stop: () =>
|
|
783
|
+
const url = mode.ready(out);
|
|
784
|
+
if (url) finish(() => resolveHandle({ url, stop: () => {
|
|
785
|
+
downFired = true;
|
|
786
|
+
return stopChild2(child);
|
|
787
|
+
} }));
|
|
756
788
|
return;
|
|
757
789
|
}
|
|
758
790
|
tailBuf += text;
|
|
@@ -760,7 +792,20 @@ function startWith2(bin, port, host, log) {
|
|
|
760
792
|
while ((nl = tailBuf.indexOf("\n")) >= 0) {
|
|
761
793
|
const line = tailBuf.slice(0, nl).replace(/\r$/, "").trim();
|
|
762
794
|
tailBuf = tailBuf.slice(nl + 1);
|
|
763
|
-
if (line)
|
|
795
|
+
if (!line) continue;
|
|
796
|
+
log?.(`cloudflared: ${line}`);
|
|
797
|
+
if (isRegisteredLine(line)) {
|
|
798
|
+
goneLines = 0;
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
801
|
+
if (isTunnelGoneLine(line)) {
|
|
802
|
+
goneLines++;
|
|
803
|
+
if (goneLines >= GONE_LINE_LIMIT && !downFired) {
|
|
804
|
+
log?.(`cloudflared: tunnel deregistered by the edge (${goneLines}x "Tunnel not found") \u2014 it can never re-register; replacing it`);
|
|
805
|
+
void stopChild2(child);
|
|
806
|
+
fireDown("quick tunnel deregistered by the edge");
|
|
807
|
+
}
|
|
808
|
+
}
|
|
764
809
|
}
|
|
765
810
|
};
|
|
766
811
|
child.stdout?.on("data", onData);
|
|
@@ -769,14 +814,15 @@ function startWith2(bin, port, host, log) {
|
|
|
769
814
|
child.on("exit", (code) => {
|
|
770
815
|
if (settled) {
|
|
771
816
|
log?.(`cloudflared exited (code ${code}) \u2014 the tunnel is down`);
|
|
817
|
+
fireDown(`cloudflared exited (code ${code})`);
|
|
772
818
|
return;
|
|
773
819
|
}
|
|
774
|
-
finish(() => reject(new Error(`${bin} exited (code ${code}) before
|
|
820
|
+
finish(() => reject(new Error(`${bin} exited (code ${code}) before the tunnel came up.
|
|
775
821
|
${tail2(out)}`)));
|
|
776
822
|
});
|
|
777
823
|
const timer = setTimeout(() => finish(() => {
|
|
778
824
|
void stopChild2(child);
|
|
779
|
-
reject(new Error(`${bin}
|
|
825
|
+
reject(new Error(`${bin} did not come up within ${START_TIMEOUT_MS2 / 1e3}s.
|
|
780
826
|
${tail2(out)}`));
|
|
781
827
|
}), START_TIMEOUT_MS2);
|
|
782
828
|
});
|
|
@@ -784,8 +830,21 @@ ${tail2(out)}`));
|
|
|
784
830
|
var cloudflaredProvider = {
|
|
785
831
|
id: "cloudflared",
|
|
786
832
|
aliases: ["cloudflare"],
|
|
787
|
-
description: "Cloudflare
|
|
788
|
-
start({ port, host, log }) {
|
|
833
|
+
description: "Cloudflare tunnel (needs the `cloudflared` CLI installed \u2014 https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/). Default: anonymous quick tunnel (no account, ephemeral URL). With --tunnel-name + --tunnel-hostname: your own NAMED tunnel (stable URL, auto-reconnects).",
|
|
834
|
+
start({ port, host, name, hostname, log, onDown }) {
|
|
835
|
+
const origin = host || "localhost";
|
|
836
|
+
let mode;
|
|
837
|
+
if (name) {
|
|
838
|
+
if (!hostname) {
|
|
839
|
+
return Promise.reject(new Error(
|
|
840
|
+
`a named cloudflared tunnel needs --tunnel-hostname <public-host> \u2014 the DNS route you created with \`cloudflared tunnel route dns ${name} <public-host>\` (one-time setup: \`cloudflared tunnel login\` then \`cloudflared tunnel create ${name}\`).`
|
|
841
|
+
));
|
|
842
|
+
}
|
|
843
|
+
const url = `https://${hostname.replace(/^https?:\/\//, "")}`;
|
|
844
|
+
mode = { argv: namedTunnelArgs(name, port, origin), ready: (s) => isRegisteredLine(s) ? url : null };
|
|
845
|
+
} else {
|
|
846
|
+
mode = { argv: tunnelArgs(port, origin), ready: extractTunnelUrl };
|
|
847
|
+
}
|
|
789
848
|
const bins = cloudflaredBins();
|
|
790
849
|
const tryAt = (i) => {
|
|
791
850
|
if (i >= bins.length) {
|
|
@@ -793,7 +852,7 @@ var cloudflaredProvider = {
|
|
|
793
852
|
`cloudflared not found (tried: ${bins.join(", ")}). Install the cloudflared CLI (https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/), or set CLOUDFLARED_BIN to the binary name/path.`
|
|
794
853
|
));
|
|
795
854
|
}
|
|
796
|
-
return startWith2(bins[i],
|
|
855
|
+
return startWith2(bins[i], mode, log, onDown).catch((e) => {
|
|
797
856
|
if (e?.code === "ENOENT") return tryAt(i + 1);
|
|
798
857
|
throw e;
|
|
799
858
|
});
|
|
@@ -806,6 +865,66 @@ var cloudflaredProvider = {
|
|
|
806
865
|
registerTunnel(cloudflaredProvider);
|
|
807
866
|
registerTunnel(zrokProvider);
|
|
808
867
|
|
|
868
|
+
// src/tunnel/supervisor.ts
|
|
869
|
+
var DEFAULT_BACKOFF = [2e3, 5e3, 1e4, 3e4];
|
|
870
|
+
async function superviseTunnel(opts) {
|
|
871
|
+
const backoff = opts.backoffMs ?? DEFAULT_BACKOFF;
|
|
872
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
873
|
+
const log = opts.start.log;
|
|
874
|
+
let stopped = false;
|
|
875
|
+
let restarting = false;
|
|
876
|
+
let current = null;
|
|
877
|
+
let incarnation = 0;
|
|
878
|
+
const startOnce = () => {
|
|
879
|
+
const mine = ++incarnation;
|
|
880
|
+
return opts.provider.start({
|
|
881
|
+
...opts.start,
|
|
882
|
+
onDown: (reason) => {
|
|
883
|
+
if (mine === incarnation) void restart(reason);
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
};
|
|
887
|
+
async function restart(reason) {
|
|
888
|
+
if (stopped || restarting) return;
|
|
889
|
+
restarting = true;
|
|
890
|
+
current = null;
|
|
891
|
+
log?.(`tunnel down (${reason}) \u2014 restarting`);
|
|
892
|
+
try {
|
|
893
|
+
for (let attempt = 0; !stopped; attempt++) {
|
|
894
|
+
await sleep(backoff[Math.min(attempt, backoff.length - 1)]);
|
|
895
|
+
if (stopped) return;
|
|
896
|
+
try {
|
|
897
|
+
const h = await startOnce();
|
|
898
|
+
if (stopped) {
|
|
899
|
+
void h.stop().catch(() => {
|
|
900
|
+
});
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
current = h;
|
|
904
|
+
opts.onUp(h.url, true);
|
|
905
|
+
return;
|
|
906
|
+
} catch (e) {
|
|
907
|
+
log?.(`tunnel restart failed (${e instanceof Error ? e.message : String(e)}) \u2014 retrying`);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
} finally {
|
|
911
|
+
restarting = false;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
current = await startOnce();
|
|
915
|
+
opts.onUp(current.url, false);
|
|
916
|
+
return {
|
|
917
|
+
stop: async () => {
|
|
918
|
+
stopped = true;
|
|
919
|
+
incarnation++;
|
|
920
|
+
const c = current;
|
|
921
|
+
current = null;
|
|
922
|
+
if (c) await c.stop().catch(() => {
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
|
|
809
928
|
// src/cli.ts
|
|
810
929
|
function parseArgs(argv) {
|
|
811
930
|
const out = { port: 4858, host: "127.0.0.1", roots: [], allowOrigins: [], allowShell: false, allowLan: false, selfSigned: false, open: false, appUrl: "https://prompd.app" };
|
|
@@ -822,9 +941,10 @@ function parseArgs(argv) {
|
|
|
822
941
|
else if (a === "--self-signed") out.selfSigned = true;
|
|
823
942
|
else if (a === "--tunnel") out.tunnel = argv[++i];
|
|
824
943
|
else if (a === "--tunnel-name") out.tunnelName = argv[++i];
|
|
944
|
+
else if (a === "--tunnel-hostname") out.tunnelHostname = argv[++i];
|
|
825
945
|
else if (a === "--open") out.open = true;
|
|
826
946
|
else if (a === "--app-url") out.appUrl = argv[++i];
|
|
827
|
-
else if (a === "--sidecar" || a === "start") {
|
|
947
|
+
else if (a === "--sidecar" || a === "start" || a === "--") {
|
|
828
948
|
} else throw new Error(`unknown argument: ${a}
|
|
829
949
|
valid flags: ${KNOWN_FLAGS.join(" ")}`);
|
|
830
950
|
}
|
|
@@ -842,6 +962,7 @@ var KNOWN_FLAGS = [
|
|
|
842
962
|
"--self-signed",
|
|
843
963
|
"--tunnel",
|
|
844
964
|
"--tunnel-name",
|
|
965
|
+
"--tunnel-hostname",
|
|
845
966
|
"--open",
|
|
846
967
|
"--app-url"
|
|
847
968
|
];
|
|
@@ -908,13 +1029,22 @@ async function main() {
|
|
|
908
1029
|
log: slog,
|
|
909
1030
|
allowLan: args.allowLan
|
|
910
1031
|
});
|
|
911
|
-
let
|
|
1032
|
+
let tunnelSup = null;
|
|
912
1033
|
let tunnelWss = null;
|
|
913
1034
|
if (tunnelProvider) {
|
|
914
|
-
process.stdout.write(`starting ${tunnelProvider.id} tunnel${args.tunnelName ? ` (
|
|
1035
|
+
process.stdout.write(`starting ${tunnelProvider.id} tunnel${args.tunnelName ? ` (name "${args.tunnelName}")` : ""}\u2026
|
|
915
1036
|
`);
|
|
916
|
-
|
|
917
|
-
|
|
1037
|
+
tunnelSup = await superviseTunnel({
|
|
1038
|
+
provider: tunnelProvider,
|
|
1039
|
+
start: { port: handle.port, host: handle.host, name: args.tunnelName, hostname: args.tunnelHostname, log: slog },
|
|
1040
|
+
onUp: (url, restarted) => {
|
|
1041
|
+
const wss = url.replace(/^http/, "ws");
|
|
1042
|
+
const changed = tunnelWss !== null && wss !== tunnelWss;
|
|
1043
|
+
tunnelWss = wss;
|
|
1044
|
+
if (!restarted) return;
|
|
1045
|
+
slog(changed ? `tunnel restored at a NEW URL: ${wss} \u2014 re-pair the app with this URL (the token is unchanged)` : "tunnel restored \u2014 same URL, existing pairings reconnect automatically");
|
|
1046
|
+
}
|
|
1047
|
+
});
|
|
918
1048
|
}
|
|
919
1049
|
const scheme = handle.secure ? "wss" : "ws";
|
|
920
1050
|
const exposed = handle.host !== "127.0.0.1" && handle.host !== "localhost";
|
|
@@ -924,7 +1054,7 @@ async function main() {
|
|
|
924
1054
|
[
|
|
925
1055
|
`corenel sidecar listening on ${scheme}://${handle.host}:${handle.port}`,
|
|
926
1056
|
args.roots.length === 1 ? ` root: ${args.roots[0]}` : ` roots: ${args.roots.join("\n ")} (${args.roots.length} approved; pick the active one in the app)`,
|
|
927
|
-
` tools:
|
|
1057
|
+
` tools: sidecar_fs_read/write/list${args.allowShell ? " + sidecar_shell_exec" : " (shell off; pass --allow-shell to enable)"}`,
|
|
928
1058
|
` token: ${token} (also at ${tokenPath()})`,
|
|
929
1059
|
tunnelWss ? ` connect at: ${tunnelWss} (public ${args.tunnel} tunnel${args.tunnelName ? ", stable" : ""} \u2014 pair from any device with this URL + the token)` : ` connect at: ${connectUrls.join(" ")}`,
|
|
930
1060
|
` origins: ${allowedOrigins.join(", ")}${args.allowLan ? " + any private-LAN origin (--allow-lan)" : args.allowOrigins.length ? "" : " (canonical defaults; pass --allow-origin to add dev/self-hosted)"}`,
|
|
@@ -943,7 +1073,7 @@ async function main() {
|
|
|
943
1073
|
}
|
|
944
1074
|
const shutdown = () => {
|
|
945
1075
|
const closeServer = () => handle.close().then(() => process.exit(0));
|
|
946
|
-
if (
|
|
1076
|
+
if (tunnelSup) void tunnelSup.stop().catch(() => {
|
|
947
1077
|
}).then(closeServer);
|
|
948
1078
|
else void closeServer();
|
|
949
1079
|
};
|
package/package.json
CHANGED
|
@@ -1,41 +1,48 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@corenel/sidecar",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"license": "Elastic-2.0",
|
|
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.",
|
|
7
|
-
"keywords": [
|
|
8
|
-
|
|
9
|
-
"corenel
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
],
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
},
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
},
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@corenel/sidecar",
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "Elastic-2.0",
|
|
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.",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"prompd",
|
|
9
|
+
"corenel",
|
|
10
|
+
"sidecar",
|
|
11
|
+
"ollama",
|
|
12
|
+
"local-llm",
|
|
13
|
+
"cors"
|
|
14
|
+
],
|
|
15
|
+
"bin": {
|
|
16
|
+
"corenel-sidecar": "dist/cli.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "node build.mjs",
|
|
30
|
+
"prepublishOnly": "node build.mjs",
|
|
31
|
+
"start": "tsx src/cli.ts",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"verify": "tsx src/verify.ts"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"selfsigned": "^2.4.1",
|
|
37
|
+
"ws": "^8.18.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@corenel/protocol": "workspace:*",
|
|
41
|
+
"@corenel/harness": "workspace:*",
|
|
42
|
+
"@types/node": "^24.13.1",
|
|
43
|
+
"@types/ws": "^8.5.13",
|
|
44
|
+
"esbuild": "^0.21.5",
|
|
45
|
+
"tsx": "^4.19.2",
|
|
46
|
+
"typescript": "^5.9.3"
|
|
47
|
+
}
|
|
48
|
+
}
|