@tonyclaw/llm-inspector 1.6.1 → 1.6.3
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/.output/cli.js +143 -0
- package/.output/nitro.json +1 -1
- package/.output/server/index.mjs +19 -19
- package/package.json +7 -6
- package/src/cli.ts +146 -36
package/.output/cli.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { spawn, execSync } from "node:child_process";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
var __dirname = dirname(__filename);
|
|
10
|
+
var DEFAULT_PORT = 25947;
|
|
11
|
+
var findBun = () => {
|
|
12
|
+
try {
|
|
13
|
+
const cmd = process.platform === "win32" ? "where bun" : "which bun";
|
|
14
|
+
const output = execSync(cmd, { encoding: "utf8", timeout: 5e3 }).trim();
|
|
15
|
+
if (!output) return null;
|
|
16
|
+
const firstLine = output.split("\n")[0] ?? "";
|
|
17
|
+
const firstPath = firstLine.trim();
|
|
18
|
+
if (!firstPath) return null;
|
|
19
|
+
if (process.platform === "win32" && firstPath.endsWith(".cmd")) {
|
|
20
|
+
const dir = join(firstPath, "..");
|
|
21
|
+
const actualPath = join(dir, "node_modules", "bun", "bin", "bun.exe");
|
|
22
|
+
if (existsSync(actualPath)) {
|
|
23
|
+
return actualPath;
|
|
24
|
+
}
|
|
25
|
+
const exePath = join(dir, "bun.exe");
|
|
26
|
+
if (existsSync(exePath)) {
|
|
27
|
+
return exePath;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (existsSync(firstPath)) {
|
|
31
|
+
return firstPath;
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
const pathEnv = process.env.PATH ?? "";
|
|
36
|
+
const pathDirs = pathEnv.split(process.platform === "win32" ? ";" : ":");
|
|
37
|
+
for (const dir of pathDirs) {
|
|
38
|
+
const bunPath2 = join(dir, process.platform === "win32" ? "bun.exe" : "bun");
|
|
39
|
+
if (existsSync(bunPath2)) {
|
|
40
|
+
if (process.platform === "win32" && !bunPath2.endsWith(".exe")) {
|
|
41
|
+
const actualPath = join(dir, "node_modules", "bun", "bin", "bun.exe");
|
|
42
|
+
if (existsSync(actualPath)) {
|
|
43
|
+
return actualPath;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return bunPath2;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (process.platform === "win32") {
|
|
50
|
+
const localAppData = process.env.LOCALAPPDATA ?? "";
|
|
51
|
+
const appData = process.env.APPDATA ?? "";
|
|
52
|
+
const userProfile = process.env.USERPROFILE ?? "";
|
|
53
|
+
const commonPaths = [
|
|
54
|
+
join(localAppData, "bun", "bin", "bun.exe"),
|
|
55
|
+
join(appData, "bun", "bin", "bun.exe"),
|
|
56
|
+
join(userProfile, "AppData", "Local", "bun", "bin", "bun.exe"),
|
|
57
|
+
join(userProfile, "AppData", "Roaming", "npm", "node_modules", "bun", "bin", "bun.exe"),
|
|
58
|
+
join(userProfile, "AppData", "Roaming", "npm", "bun.exe"),
|
|
59
|
+
join(userProfile, "AppData", "Roaming", "npm", "bun")
|
|
60
|
+
];
|
|
61
|
+
for (const bunPath2 of commonPaths) {
|
|
62
|
+
if (existsSync(bunPath2)) {
|
|
63
|
+
return bunPath2;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
};
|
|
69
|
+
var envPort = process.env["PORT"];
|
|
70
|
+
var portDefault = envPort !== void 0 ? Number(envPort) : DEFAULT_PORT;
|
|
71
|
+
var args = process.argv.slice(2);
|
|
72
|
+
var port = portDefault;
|
|
73
|
+
var open = true;
|
|
74
|
+
for (let i = 0; i < args.length; i++) {
|
|
75
|
+
const arg = args[i] ?? "";
|
|
76
|
+
switch (arg) {
|
|
77
|
+
case "--port":
|
|
78
|
+
case "-p":
|
|
79
|
+
port = Number(args[i + 1]);
|
|
80
|
+
i++;
|
|
81
|
+
break;
|
|
82
|
+
case "--no-open":
|
|
83
|
+
open = false;
|
|
84
|
+
break;
|
|
85
|
+
case "--open":
|
|
86
|
+
open = true;
|
|
87
|
+
break;
|
|
88
|
+
default:
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
process.env["PORT"] = String(port);
|
|
93
|
+
var url = `http://localhost:${port}`;
|
|
94
|
+
console.log(`Server running at ${url}`);
|
|
95
|
+
console.log(` Proxy: ${url}/proxy`);
|
|
96
|
+
console.log(``);
|
|
97
|
+
console.log(`Route AI coding tools through the proxy:`);
|
|
98
|
+
console.log(` Claude Code: ANTHROPIC_BASE_URL=${url}/proxy claude`);
|
|
99
|
+
console.log(` OpenCode: LLM_BASE_URL=${url}/proxy opencode`);
|
|
100
|
+
console.log(` Direct HTTP: curl ${url}/proxy/v1/messages -d '{"model":"...","messages":[...]}'`);
|
|
101
|
+
console.log(``);
|
|
102
|
+
console.log(`Routing environment variables:`);
|
|
103
|
+
console.log(` ROUTES JSON map of model prefix -> upstream URL`);
|
|
104
|
+
console.log(` DEFAULT_UPSTREAM Fallback upstream for unmatched models`);
|
|
105
|
+
console.log(
|
|
106
|
+
` Example: ROUTES='{"claude-":"https://api.anthropic.com","MiniMax":"https://api.minimaxi.com/anthropic"}'`
|
|
107
|
+
);
|
|
108
|
+
var openBrowser = (targetUrl) => {
|
|
109
|
+
let command;
|
|
110
|
+
switch (process.platform) {
|
|
111
|
+
case "darwin":
|
|
112
|
+
command = ["open", targetUrl];
|
|
113
|
+
break;
|
|
114
|
+
case "linux":
|
|
115
|
+
command = ["xdg-open", targetUrl];
|
|
116
|
+
break;
|
|
117
|
+
case "win32":
|
|
118
|
+
command = ["cmd", "/c", "start", targetUrl];
|
|
119
|
+
break;
|
|
120
|
+
default:
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
if (command === void 0) return;
|
|
124
|
+
const [bin, ...cmdArgs] = command;
|
|
125
|
+
if (bin === void 0) return;
|
|
126
|
+
spawn(bin, cmdArgs, { stdio: "ignore", detached: true });
|
|
127
|
+
};
|
|
128
|
+
if (open) {
|
|
129
|
+
openBrowser(url);
|
|
130
|
+
}
|
|
131
|
+
var bunPath = findBun();
|
|
132
|
+
if (bunPath === null) {
|
|
133
|
+
console.error("\nError: bun is not installed or not in PATH.");
|
|
134
|
+
console.error("Please install bun from https://bun.sh");
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
var outputDir = __dirname;
|
|
138
|
+
var serverPath = join(outputDir, "../.output/server/index.mjs");
|
|
139
|
+
var serverProcess = spawn(bunPath, [serverPath], {
|
|
140
|
+
stdio: ["ignore", "inherit", "inherit"],
|
|
141
|
+
detached: true
|
|
142
|
+
});
|
|
143
|
+
serverProcess.unref();
|
package/.output/nitro.json
CHANGED
package/.output/server/index.mjs
CHANGED
|
@@ -93,52 +93,52 @@ const headers = ((m) => function headersRouteRule(event) {
|
|
|
93
93
|
}
|
|
94
94
|
});
|
|
95
95
|
const assets = {
|
|
96
|
+
"/assets/index-B3RwBPLW.css": {
|
|
97
|
+
"type": "text/css; charset=utf-8",
|
|
98
|
+
"etag": '"10c74-aXacU4DRFVsUwcC5jHnjoPRSlTA"',
|
|
99
|
+
"mtime": "2026-06-03T08:06:11.052Z",
|
|
100
|
+
"size": 68724,
|
|
101
|
+
"path": "../public/assets/index-B3RwBPLW.css"
|
|
102
|
+
},
|
|
103
|
+
"/assets/alibaba-TTwafVwX.svg": {
|
|
104
|
+
"type": "image/svg+xml",
|
|
105
|
+
"etag": '"171b-6dyV5K8QjiaY35sN9qNprh9zDIs"',
|
|
106
|
+
"mtime": "2026-06-03T08:06:11.050Z",
|
|
107
|
+
"size": 5915,
|
|
108
|
+
"path": "../public/assets/alibaba-TTwafVwX.svg"
|
|
109
|
+
},
|
|
96
110
|
"/assets/minimax-BPMzvuL-.jpeg": {
|
|
97
111
|
"type": "image/jpeg",
|
|
98
112
|
"etag": '"1b06-IwivU89ko5UTMUM1/t7hn4sQK9A"',
|
|
99
|
-
"mtime": "2026-06-
|
|
113
|
+
"mtime": "2026-06-03T08:06:11.050Z",
|
|
100
114
|
"size": 6918,
|
|
101
115
|
"path": "../public/assets/minimax-BPMzvuL-.jpeg"
|
|
102
116
|
},
|
|
103
117
|
"/assets/zhipuai-BPNAnxo-.svg": {
|
|
104
118
|
"type": "image/svg+xml",
|
|
105
119
|
"etag": '"2bf8-hNaLCTi89nOFCsIIfWpP/jrfo0s"',
|
|
106
|
-
"mtime": "2026-06-
|
|
120
|
+
"mtime": "2026-06-03T08:06:11.050Z",
|
|
107
121
|
"size": 11256,
|
|
108
122
|
"path": "../public/assets/zhipuai-BPNAnxo-.svg"
|
|
109
123
|
},
|
|
110
|
-
"/assets/alibaba-TTwafVwX.svg": {
|
|
111
|
-
"type": "image/svg+xml",
|
|
112
|
-
"etag": '"171b-6dyV5K8QjiaY35sN9qNprh9zDIs"',
|
|
113
|
-
"mtime": "2026-06-03T06:15:56.951Z",
|
|
114
|
-
"size": 5915,
|
|
115
|
-
"path": "../public/assets/alibaba-TTwafVwX.svg"
|
|
116
|
-
},
|
|
117
|
-
"/assets/index-B3RwBPLW.css": {
|
|
118
|
-
"type": "text/css; charset=utf-8",
|
|
119
|
-
"etag": '"10c74-aXacU4DRFVsUwcC5jHnjoPRSlTA"',
|
|
120
|
-
"mtime": "2026-06-03T06:15:56.954Z",
|
|
121
|
-
"size": 68724,
|
|
122
|
-
"path": "../public/assets/index-B3RwBPLW.css"
|
|
123
|
-
},
|
|
124
124
|
"/assets/main-Cp8AM0Pa.js": {
|
|
125
125
|
"type": "text/javascript; charset=utf-8",
|
|
126
126
|
"etag": '"4db57-FpqlPRLq9OHoaAFCL2NIXtZbW5c"',
|
|
127
|
-
"mtime": "2026-06-
|
|
127
|
+
"mtime": "2026-06-03T08:06:11.052Z",
|
|
128
128
|
"size": 318295,
|
|
129
129
|
"path": "../public/assets/main-Cp8AM0Pa.js"
|
|
130
130
|
},
|
|
131
131
|
"/assets/qwen-CONDcHqt.png": {
|
|
132
132
|
"type": "image/png",
|
|
133
133
|
"etag": '"572c3-cdJAPaHdOvFCGzuaQjagdgOu6XE"',
|
|
134
|
-
"mtime": "2026-06-
|
|
134
|
+
"mtime": "2026-06-03T08:06:11.052Z",
|
|
135
135
|
"size": 357059,
|
|
136
136
|
"path": "../public/assets/qwen-CONDcHqt.png"
|
|
137
137
|
},
|
|
138
138
|
"/assets/index-s4lwsWvq.js": {
|
|
139
139
|
"type": "text/javascript; charset=utf-8",
|
|
140
140
|
"etag": '"828c8-LEW/XL92J2/5lU4VKALlH7aVpaA"',
|
|
141
|
-
"mtime": "2026-06-
|
|
141
|
+
"mtime": "2026-06-03T08:06:11.052Z",
|
|
142
142
|
"size": 534728,
|
|
143
143
|
"path": "../public/assets/index-s4lwsWvq.js"
|
|
144
144
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tonyclaw/llm-inspector",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "LLM API proxy inspector — captures and displays requests/responses from AI coding tools in a web UI",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"ai-coding-tools"
|
|
19
19
|
],
|
|
20
20
|
"bin": {
|
|
21
|
-
"llm-inspector": "
|
|
21
|
+
"llm-inspector": ".output/cli.js"
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
24
|
"src",
|
|
@@ -31,9 +31,10 @@
|
|
|
31
31
|
],
|
|
32
32
|
"scripts": {
|
|
33
33
|
"dev": "vite dev",
|
|
34
|
-
"start": "
|
|
35
|
-
"build": "vite build",
|
|
36
|
-
"
|
|
34
|
+
"start": "node .output/cli.js",
|
|
35
|
+
"build": "vite build && bun build:cli",
|
|
36
|
+
"build:cli": "npx esbuild src/cli.ts --bundle --platform=node --target=node18 --format=esm --outfile=.output/cli.js",
|
|
37
|
+
"prepublishOnly": "npm run build",
|
|
37
38
|
"typecheck": "tsc --noEmit",
|
|
38
39
|
"lint": "eslint .",
|
|
39
40
|
"format": "biome format --write .",
|
|
@@ -52,7 +53,6 @@
|
|
|
52
53
|
"@tanstack/react-start": "^1.161.0",
|
|
53
54
|
"@tanstack/react-virtual": "^3.13.26",
|
|
54
55
|
"class-variance-authority": "^0.7.1",
|
|
55
|
-
"cleye": "^2.2.1",
|
|
56
56
|
"clsx": "^2.1.1",
|
|
57
57
|
"conf": "^15.1.0",
|
|
58
58
|
"jszip": "^3.10.1",
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"@typescript-eslint/eslint-plugin": "^8.55.0",
|
|
77
77
|
"@typescript-eslint/parser": "^8.55.0",
|
|
78
78
|
"@vitejs/plugin-react": "^5.1.4",
|
|
79
|
+
"esbuild": "^0.25.0",
|
|
79
80
|
"eslint": "^9.32.0",
|
|
80
81
|
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
81
82
|
"eslint-plugin-functional": "^9.0.2",
|
package/src/cli.ts
CHANGED
|
@@ -1,31 +1,119 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
2
|
-
import {
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn, execSync } from "node:child_process";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
3
9
|
|
|
4
10
|
const DEFAULT_PORT = 25947;
|
|
5
11
|
|
|
12
|
+
// Find bun executable
|
|
13
|
+
const findBun = (): string | null => {
|
|
14
|
+
// Try running `where bun` (Windows) or `which bun` (Unix) to find in PATH
|
|
15
|
+
try {
|
|
16
|
+
const cmd = process.platform === "win32" ? "where bun" : "which bun";
|
|
17
|
+
const output = execSync(cmd, { encoding: "utf8", timeout: 5000 }).trim();
|
|
18
|
+
if (!output) return null;
|
|
19
|
+
|
|
20
|
+
// Take first line and handle Windows .cmd shim
|
|
21
|
+
const firstLine = output.split("\n")[0] ?? "";
|
|
22
|
+
const firstPath = firstLine.trim();
|
|
23
|
+
if (!firstPath) return null;
|
|
24
|
+
|
|
25
|
+
if (process.platform === "win32" && firstPath.endsWith(".cmd")) {
|
|
26
|
+
// npm shim - find actual exe
|
|
27
|
+
const dir = join(firstPath, "..");
|
|
28
|
+
const actualPath = join(dir, "node_modules", "bun", "bin", "bun.exe");
|
|
29
|
+
if (existsSync(actualPath)) {
|
|
30
|
+
return actualPath;
|
|
31
|
+
}
|
|
32
|
+
// Also try .exe directly in same directory
|
|
33
|
+
const exePath = join(dir, "bun.exe");
|
|
34
|
+
if (existsSync(exePath)) {
|
|
35
|
+
return exePath;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (existsSync(firstPath)) {
|
|
39
|
+
return firstPath;
|
|
40
|
+
}
|
|
41
|
+
} catch {
|
|
42
|
+
// Command failed, continue with other methods
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Check PATH directories directly
|
|
46
|
+
const pathEnv = process.env.PATH ?? "";
|
|
47
|
+
const pathDirs = pathEnv.split(process.platform === "win32" ? ";" : ":");
|
|
48
|
+
|
|
49
|
+
for (const dir of pathDirs) {
|
|
50
|
+
const bunPath = join(dir, process.platform === "win32" ? "bun.exe" : "bun");
|
|
51
|
+
if (existsSync(bunPath)) {
|
|
52
|
+
// On Windows, npm shim is a script, not the actual exe
|
|
53
|
+
if (process.platform === "win32" && !bunPath.endsWith(".exe")) {
|
|
54
|
+
const actualPath = join(dir, "node_modules", "bun", "bin", "bun.exe");
|
|
55
|
+
if (existsSync(actualPath)) {
|
|
56
|
+
return actualPath;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return bunPath;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Common Windows paths
|
|
64
|
+
if (process.platform === "win32") {
|
|
65
|
+
const localAppData = process.env.LOCALAPPDATA ?? "";
|
|
66
|
+
const appData = process.env.APPDATA ?? "";
|
|
67
|
+
const userProfile = process.env.USERPROFILE ?? "";
|
|
68
|
+
|
|
69
|
+
const commonPaths = [
|
|
70
|
+
join(localAppData, "bun", "bin", "bun.exe"),
|
|
71
|
+
join(appData, "bun", "bin", "bun.exe"),
|
|
72
|
+
join(userProfile, "AppData", "Local", "bun", "bin", "bun.exe"),
|
|
73
|
+
join(userProfile, "AppData", "Roaming", "npm", "node_modules", "bun", "bin", "bun.exe"),
|
|
74
|
+
join(userProfile, "AppData", "Roaming", "npm", "bun.exe"),
|
|
75
|
+
join(userProfile, "AppData", "Roaming", "npm", "bun"),
|
|
76
|
+
];
|
|
77
|
+
for (const bunPath of commonPaths) {
|
|
78
|
+
if (existsSync(bunPath)) {
|
|
79
|
+
return bunPath;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return null;
|
|
85
|
+
};
|
|
86
|
+
|
|
6
87
|
const envPort = process.env["PORT"];
|
|
7
88
|
const portDefault = envPort !== undefined ? Number(envPort) : DEFAULT_PORT;
|
|
8
89
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
type: Number,
|
|
14
|
-
alias: "p",
|
|
15
|
-
default: portDefault,
|
|
16
|
-
description: "Port to listen on (env: PORT)",
|
|
17
|
-
},
|
|
18
|
-
open: {
|
|
19
|
-
type: Boolean,
|
|
20
|
-
default: true,
|
|
21
|
-
description: "Open the browser on start (use --no-open to disable)",
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
});
|
|
90
|
+
// Simple argument parsing
|
|
91
|
+
const args = process.argv.slice(2);
|
|
92
|
+
let port = portDefault;
|
|
93
|
+
let open = true;
|
|
25
94
|
|
|
26
|
-
|
|
95
|
+
for (let i = 0; i < args.length; i++) {
|
|
96
|
+
const arg = args[i] ?? "";
|
|
97
|
+
switch (arg) {
|
|
98
|
+
case "--port":
|
|
99
|
+
case "-p":
|
|
100
|
+
port = Number(args[i + 1]);
|
|
101
|
+
i++;
|
|
102
|
+
break;
|
|
103
|
+
case "--no-open":
|
|
104
|
+
open = false;
|
|
105
|
+
break;
|
|
106
|
+
case "--open":
|
|
107
|
+
open = true;
|
|
108
|
+
break;
|
|
109
|
+
default:
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
27
113
|
|
|
28
|
-
|
|
114
|
+
process.env["PORT"] = String(port);
|
|
115
|
+
|
|
116
|
+
const url = `http://localhost:${port}`;
|
|
29
117
|
|
|
30
118
|
console.log(`Server running at ${url}`);
|
|
31
119
|
console.log(` Proxy: ${url}/proxy`);
|
|
@@ -43,26 +131,48 @@ console.log(
|
|
|
43
131
|
);
|
|
44
132
|
|
|
45
133
|
const openBrowser = (targetUrl: string): void => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
134
|
+
let command: string[] | undefined;
|
|
135
|
+
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
|
|
136
|
+
switch (process.platform) {
|
|
137
|
+
case "darwin":
|
|
138
|
+
command = ["open", targetUrl];
|
|
139
|
+
break;
|
|
140
|
+
case "linux":
|
|
141
|
+
command = ["xdg-open", targetUrl];
|
|
142
|
+
break;
|
|
143
|
+
case "win32":
|
|
144
|
+
command = ["cmd", "/c", "start", targetUrl];
|
|
145
|
+
break;
|
|
146
|
+
default:
|
|
147
|
+
// Unsupported platform - do nothing
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
52
150
|
if (command === undefined) return;
|
|
53
|
-
const [bin, ...
|
|
151
|
+
const [bin, ...cmdArgs] = command;
|
|
54
152
|
if (bin === undefined) return;
|
|
55
|
-
|
|
153
|
+
spawn(bin, cmdArgs, { stdio: "ignore", detached: true });
|
|
56
154
|
};
|
|
57
155
|
|
|
58
|
-
if (
|
|
156
|
+
if (open) {
|
|
59
157
|
openBrowser(url);
|
|
60
158
|
}
|
|
61
159
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
160
|
+
// Find bun and start server
|
|
161
|
+
const bunPath = findBun();
|
|
162
|
+
if (bunPath === null) {
|
|
163
|
+
console.error("\nError: bun is not installed or not in PATH.");
|
|
164
|
+
console.error("Please install bun from https://bun.sh");
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Compute server path
|
|
169
|
+
const outputDir = __dirname;
|
|
170
|
+
const serverPath = join(outputDir, "../.output/server/index.mjs");
|
|
171
|
+
|
|
172
|
+
// Start server with bun
|
|
173
|
+
const serverProcess = spawn(bunPath, [serverPath], {
|
|
174
|
+
stdio: ["ignore", "inherit", "inherit"],
|
|
175
|
+
detached: true,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
serverProcess.unref();
|