Skill Tracer
Skills › gstack › browse › find-security-sidecar.ts

find-security-sidecar.ts

78 lines · browse/src/find-security-sidecar.ts at 3f79d81

1/**
2 * find-security-sidecar — resolve the Node entry that runs the L4 ML
3 * classifier sidecar.
4 *
5 * The sidecar can't be bundled into the compiled browse binary because
6 * onnxruntime-node fails to dlopen from Bun's compile extract dir. It runs
7 * as a separate Node subprocess instead. This module resolves the right
8 * path + interpreter on each platform:
9 *
10 * 1. Prefer node on PATH + a bundled JS entry at
11 * browse/dist/security-sidecar.js (built by package.json's
12 * build:security-sidecar script).
13 * 2. Dev fallback: node + browse/src/security-sidecar-entry.ts via tsx
14 * (only available in the source checkout, not the compiled install).
15 * 3. If Node is missing or no entry resolves, return null. The /pty-inject-scan
16 * endpoint then responds with l4 { available: false } and the extension
17 * degrades to WARN+confirm (D7).
18 */
20import { existsSync } from "fs";
21import { join, dirname } from "path";
22import { execFileSync } from "child_process";
24export interface SidecarLocation {
25 node: string;
26 entry: string;
27 /** "compiled" if running from browse/dist/, "dev" if running from src */
28 mode: "compiled" | "dev";
31function nodeOnPath(): string | null {
32 try {
33 execFileSync("node", ["--version"], { stdio: "ignore", timeout: 2000, windowsHide: true });
34 return "node";
35 } catch {
36 return null;
37 }
40function browseRoot(): string {
41 // When running compiled, __dirname (via import.meta.dir) points at the
42 // Bun extract temp. Walk up until we find a directory containing
43 // browse/dist/ or browse/src/.
44 let candidate = dirname(import.meta.path || "");
45 for (let i = 0; i < 6; i += 1) {
46 if (existsSync(join(candidate, "browse", "dist", "security-sidecar.js"))) {
47 return candidate;
48 }
49 if (existsSync(join(candidate, "src", "security-sidecar-entry.ts"))) {
50 return candidate;
51 }
52 const next = dirname(candidate);
53 if (next === candidate) break;
54 candidate = next;
55 }
56 return process.cwd();
59export function findSecuritySidecar(): SidecarLocation | null {
60 const node = nodeOnPath();
61 if (!node) return null;
63 const root = browseRoot();
65 const compiled = join(root, "browse", "dist", "security-sidecar.js");
66 if (existsSync(compiled)) {
67 return { node, entry: compiled, mode: "compiled" };
68 }
70 // Dev fallback. Compiled installs won't have src/ on disk so this only
71 // resolves when running from the source checkout.
72 const devEntry = join(root, "src", "security-sidecar-entry.ts");
73 if (existsSync(devEntry)) {
74 return { node, entry: devEntry, mode: "dev" };
75 }
77 return null;