@tonyclaw/llm-inspector 1.18.2 → 1.19.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 (42) hide show
  1. package/.output/cli.js +903 -139
  2. package/.output/nitro.json +1 -1
  3. package/.output/public/assets/{CompareDrawer-C-4ypEWs.js → CompareDrawer-DtERUdIt.js} +1 -1
  4. package/.output/public/assets/ProxyViewerContainer-DfxRK7Nt.js +101 -0
  5. package/.output/public/assets/{ReplayDialog-CyBKOgba.js → ReplayDialog-VMsGnJSI.js} +1 -1
  6. package/.output/public/assets/{RequestAnatomy-C0IrVQ3q.js → RequestAnatomy-Cx_vluvK.js} +1 -1
  7. package/.output/public/assets/{ResponseView-MogToC4i.js → ResponseView-5F8Ms5z4.js} +1 -1
  8. package/.output/public/assets/{StreamingChunkSequence-ClhUhT-s.js → StreamingChunkSequence-CKDCWfu9.js} +1 -1
  9. package/.output/public/assets/_sessionId-C-aKd1Ky.js +1 -0
  10. package/.output/public/assets/index-B8ttyigz.js +1 -0
  11. package/.output/public/assets/index-DeJyypsp.css +1 -0
  12. package/.output/public/assets/{json-viewer-BicGakI5.js → json-viewer-CztuZ9cT.js} +2 -2
  13. package/.output/public/assets/{main-Be2qqUUW.js → main-CR9IJlz1.js} +2 -2
  14. package/.output/server/_libs/lucide-react.mjs +93 -72
  15. package/.output/server/{_sessionId-DhKJIdQC.mjs → _sessionId-DvWQaDEm.mjs} +2 -2
  16. package/.output/server/_ssr/{CompareDrawer-BGUgukJ8.mjs → CompareDrawer-C5FsxSDS.mjs} +4 -4
  17. package/.output/server/_ssr/{ProxyViewerContainer--3K3o3Sm.mjs → ProxyViewerContainer-v0cvR8f5.mjs} +354 -343
  18. package/.output/server/_ssr/{ReplayDialog-Bo86xZI4.mjs → ReplayDialog-C3KOv9OW.mjs} +4 -4
  19. package/.output/server/_ssr/{RequestAnatomy-jRU5qgwB.mjs → RequestAnatomy-BYRe33eG.mjs} +3 -3
  20. package/.output/server/_ssr/{ResponseView-DdO_-79a.mjs → ResponseView-va7yQDeL.mjs} +4 -4
  21. package/.output/server/_ssr/{StreamingChunkSequence-BigLwhh4.mjs → StreamingChunkSequence-BJlI-gWl.mjs} +3 -3
  22. package/.output/server/_ssr/{index-BHG6vOnr.mjs → index-CS0fA2GT.mjs} +2 -2
  23. package/.output/server/_ssr/index.mjs +2 -2
  24. package/.output/server/_ssr/{json-viewer-B4c_WjXD.mjs → json-viewer-Dg8rqrxL.mjs} +9 -5
  25. package/.output/server/_ssr/{router-DVixpJO-.mjs → router-D_Boe9Bu.mjs} +3 -3
  26. package/.output/server/{_tanstack-start-manifest_v-BbvWUF4v.mjs → _tanstack-start-manifest_v-KFXyNRGC.mjs} +1 -1
  27. package/.output/server/index.mjs +65 -65
  28. package/package.json +2 -1
  29. package/src/cli/detect-tools.ts +146 -0
  30. package/src/cli/onboard.ts +229 -0
  31. package/src/cli/templates/command-onboard.ts +17 -0
  32. package/src/cli/templates/skill-onboard.ts +458 -0
  33. package/src/cli.ts +193 -163
  34. package/src/components/ProxyViewer.tsx +153 -142
  35. package/src/components/proxy-viewer/LogEntry.tsx +136 -157
  36. package/src/components/proxy-viewer/LogEntryHeader.tsx +147 -66
  37. package/src/components/proxy-viewer/useCopyFeedback.ts +36 -0
  38. package/src/components/ui/json-viewer.tsx +12 -0
  39. package/.output/public/assets/ProxyViewerContainer-WRenRpeh.js +0 -101
  40. package/.output/public/assets/_sessionId-BO47oA3Z.js +0 -1
  41. package/.output/public/assets/index-BRvz6-L6.css +0 -1
  42. package/.output/public/assets/index-Btw8ec7-.js +0 -1
package/src/cli.ts CHANGED
@@ -9,182 +9,212 @@ const __dirname = dirname(__filename);
9
9
 
10
10
  const DEFAULT_PORT = 25947;
11
11
 
12
- const envPort = process.env["PORT"];
13
- const portDefault = envPort !== undefined ? Number(envPort) : DEFAULT_PORT;
14
-
15
- // Simple argument parsing
16
- const args = process.argv.slice(2);
17
- let port = portDefault;
18
- let open = true;
19
- let configDir: string | undefined;
20
- let providersJson: string | undefined;
21
-
22
- for (let i = 0; i < args.length; i++) {
23
- const arg = args[i] ?? "";
24
- switch (arg) {
25
- case "--port":
26
- case "-p":
27
- port = Number(args[i + 1]);
28
- i++;
29
- break;
30
- case "--no-open":
31
- open = false;
32
- break;
33
- case "--open":
34
- open = true;
35
- break;
36
- case "--config-dir":
37
- configDir = args[i + 1];
38
- i++;
39
- break;
40
- case "--providers":
41
- providersJson = args[i + 1];
42
- i++;
43
- break;
44
- default:
45
- break;
46
- }
47
- }
48
-
49
12
  /**
50
- * Check if a port is in use and kill the process using it
13
+ * Subcommand router. The legacy one-liner UX (`llm-inspector` with no args,
14
+ * or `llm-inspector start`) starts the proxy. `llm-inspector onboard` runs
15
+ * the guided skill install. Unknown subcommands fall back to the legacy
16
+ * behavior so a stray positional arg doesn't break an old invocation.
51
17
  */
52
- function killProcessOnPort(targetPort: number): void {
53
- const platform = process.platform;
54
-
55
- try {
56
- let pids: number[] = [];
57
-
58
- if (platform === "win32") {
59
- // Windows: use netstat to find PID, then taskkill
60
- const output = execSync(`netstat -ano | findstr :${targetPort}`, {
61
- encoding: "utf8",
62
- timeout: 5000,
63
- });
64
- const lines = output.trim().split("\n");
65
- for (const line of lines) {
66
- const parts = line.trim().split(/\s+/);
67
- if (parts.length >= 5) {
68
- const localAddress = parts[1] ?? "";
69
- const pidStr = parts[4] ?? "";
70
- if (localAddress !== "" && pidStr !== "" && localAddress.includes(`:${targetPort}`)) {
71
- const pid = parseInt(pidStr, 10);
72
- if (!isNaN(pid) && pid > 0) {
73
- pids.push(pid);
18
+ const subcommand = process.argv[2];
19
+
20
+ if (subcommand === "onboard") {
21
+ const { runOnboard } = await import("./cli/onboard.js");
22
+ const code = await runOnboard(process.argv.slice(3));
23
+ process.exit(code);
24
+ }
25
+
26
+ runStart(process.argv.slice(2));
27
+
28
+ // -----------------------------------------------------------------------------
29
+ // Legacy `start` behavior — start the proxy on the configured port. Extracted
30
+ // into a function so the router above can keep the top-level flow readable.
31
+ // -----------------------------------------------------------------------------
32
+ function runStart(args: string[]): void {
33
+ const envPort = process.env["PORT"];
34
+ const portDefault = envPort !== undefined ? Number(envPort) : DEFAULT_PORT;
35
+
36
+ let port = portDefault;
37
+ let open = true;
38
+ let configDir: string | undefined;
39
+ let providersJson: string | undefined;
40
+
41
+ for (let i = 0; i < args.length; i++) {
42
+ const arg = args[i] ?? "";
43
+ switch (arg) {
44
+ case "--port":
45
+ case "-p":
46
+ port = Number(args[i + 1]);
47
+ i++;
48
+ break;
49
+ case "--no-open":
50
+ open = false;
51
+ break;
52
+ case "--open":
53
+ open = true;
54
+ break;
55
+ case "--config-dir":
56
+ configDir = args[i + 1];
57
+ i++;
58
+ break;
59
+ case "--providers":
60
+ providersJson = args[i + 1];
61
+ i++;
62
+ break;
63
+ default:
64
+ break;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Check if a port is in use and kill the process using it
70
+ */
71
+ function killProcessOnPort(targetPort: number): void {
72
+ const platform = process.platform;
73
+
74
+ try {
75
+ let pids: number[] = [];
76
+
77
+ if (platform === "win32") {
78
+ // Windows: use netstat to find PID, then taskkill
79
+ const output = execSync(`netstat -ano | findstr :${targetPort}`, {
80
+ encoding: "utf8",
81
+ timeout: 5000,
82
+ });
83
+ const lines = output.trim().split("\n");
84
+ for (const line of lines) {
85
+ const parts = line.trim().split(/\s+/);
86
+ if (parts.length >= 5) {
87
+ const localAddress = parts[1] ?? "";
88
+ const pidStr = parts[4] ?? "";
89
+ if (localAddress !== "" && pidStr !== "" && localAddress.includes(`:${targetPort}`)) {
90
+ const pid = parseInt(pidStr, 10);
91
+ if (!isNaN(pid) && pid > 0) {
92
+ pids.push(pid);
93
+ }
74
94
  }
75
95
  }
76
96
  }
77
- }
78
- // Remove duplicates
79
- pids = [...new Set(pids)];
80
-
81
- for (const pid of pids) {
82
- try {
83
- console.log(`Killing process ${pid} on port ${port}...`);
84
- execSync(`taskkill /PID ${pid} /F`, { encoding: "utf8", timeout: 5000 });
85
- } catch {
86
- // Process may have already exited
97
+ // Remove duplicates
98
+ pids = [...new Set(pids)];
99
+
100
+ for (const pid of pids) {
101
+ try {
102
+ console.log(`Killing process ${pid} on port ${port}...`);
103
+ execSync(`taskkill /PID ${pid} /F`, { encoding: "utf8", timeout: 5000 });
104
+ } catch {
105
+ // Process may have already exited
106
+ }
87
107
  }
88
- }
89
- } else {
90
- // Unix-like: use lsof
91
- const output = execSync(`lsof -ti:${targetPort}`, { encoding: "utf8", timeout: 5000 });
92
- const lines = output.trim().split("\n");
93
- for (const line of lines) {
94
- const pid = parseInt(line.trim(), 10);
95
- if (!isNaN(pid) && pid > 0) {
96
- pids.push(pid);
108
+ } else {
109
+ // Unix-like: use lsof
110
+ const output = execSync(`lsof -ti:${targetPort}`, { encoding: "utf8", timeout: 5000 });
111
+ const lines = output.trim().split("\n");
112
+ for (const line of lines) {
113
+ const pid = parseInt(line.trim(), 10);
114
+ if (!isNaN(pid) && pid > 0) {
115
+ pids.push(pid);
116
+ }
97
117
  }
98
- }
99
- // Remove duplicates
100
- pids = [...new Set(pids)];
101
-
102
- for (const pid of pids) {
103
- try {
104
- console.log(`Killing process ${pid} on port ${targetPort}...`);
105
- execSync(`kill -9 ${pid}`, { encoding: "utf8", timeout: 5000 });
106
- } catch {
107
- // Process may have already exited
118
+ // Remove duplicates
119
+ pids = [...new Set(pids)];
120
+
121
+ for (const pid of pids) {
122
+ try {
123
+ console.log(`Killing process ${pid} on port ${port}...`);
124
+ execSync(`kill -9 ${pid}`, { encoding: "utf8", timeout: 5000 });
125
+ } catch {
126
+ // Process may have already exited
127
+ }
108
128
  }
109
129
  }
130
+ } catch {
131
+ // No process found on port, which is fine
110
132
  }
111
- } catch {
112
- // No process found on port, which is fine
113
133
  }
114
- }
115
134
 
116
- process.env["PORT"] = String(port);
117
-
118
- // Kill any existing process on the port
119
- killProcessOnPort(port);
120
-
121
- const url = `http://localhost:${port}`;
122
-
123
- console.log(`Server running at ${url}`);
124
- console.log(` Proxy: ${url}/proxy`);
125
- console.log(``);
126
- console.log(`Route AI coding tools through the proxy:`);
127
- console.log(` Claude Code: ANTHROPIC_BASE_URL=${url}/proxy claude`);
128
- console.log(` OpenCode: LLM_BASE_URL=${url}/proxy opencode`);
129
- console.log(` MiMo Code: OPENAI_BASE_URL=${url}/proxy mimo`);
130
- console.log(` Direct HTTP: curl ${url}/proxy/v1/messages -d '{"model":"...","messages":[...]}'`);
131
- console.log(``);
132
- console.log(`Routing environment variables:`);
133
- console.log(` ROUTES JSON map of model prefix -> upstream URL`);
134
- console.log(` DEFAULT_UPSTREAM Fallback upstream for unmatched models`);
135
- console.log(
136
- ` Example: ROUTES='{"claude-":"https://api.anthropic.com","MiniMax":"https://api.minimaxi.com/anthropic"}'`,
137
- );
138
-
139
- const openBrowser = (targetUrl: string): void => {
140
- let command: string[] | undefined;
141
- // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
142
- switch (process.platform) {
143
- case "darwin":
144
- command = ["open", targetUrl];
145
- break;
146
- case "linux":
147
- command = ["xdg-open", targetUrl];
148
- break;
149
- case "win32":
150
- command = ["cmd", "/c", "start", targetUrl];
151
- break;
152
- default:
153
- // Unsupported platform - do nothing
154
- break;
135
+ process.env["PORT"] = String(port);
136
+
137
+ // Kill any existing process on the port
138
+ killProcessOnPort(port);
139
+
140
+ const url = `http://localhost:${port}`;
141
+
142
+ console.log(`Server running at ${url}`);
143
+ console.log(` Proxy: ${url}/proxy`);
144
+ console.log(``);
145
+ console.log(`Route AI coding tools through the proxy:`);
146
+ console.log(` Claude Code: ANTHROPIC_BASE_URL=${url}/proxy claude`);
147
+ console.log(` OpenCode: LLM_BASE_URL=${url}/proxy opencode`);
148
+ console.log(` MiMo Code: OPENAI_BASE_URL=${url}/proxy mimo`);
149
+ console.log(
150
+ ` Direct HTTP: curl ${url}/proxy/v1/messages -d '{"model":"...","messages":[...]}'`,
151
+ );
152
+ console.log(``);
153
+ console.log(`Routing environment variables:`);
154
+ console.log(` ROUTES JSON map of model prefix -> upstream URL`);
155
+ console.log(` DEFAULT_UPSTREAM Fallback upstream for unmatched models`);
156
+ console.log(
157
+ ` Example: ROUTES='{"claude-":"https://api.anthropic.com","MiniMax":"https://api.minimaxi.com/anthropic"}'`,
158
+ );
159
+
160
+ const openBrowser = (targetUrl: string): void => {
161
+ let command: string[] | undefined;
162
+ // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
163
+ switch (process.platform) {
164
+ case "darwin":
165
+ command = ["open", targetUrl];
166
+ break;
167
+ case "linux":
168
+ command = ["xdg-open", targetUrl];
169
+ break;
170
+ case "win32":
171
+ command = ["cmd", "/c", "start", targetUrl];
172
+ break;
173
+ default:
174
+ // Unsupported platform - do nothing
175
+ break;
176
+ }
177
+ if (command === undefined) return;
178
+ const [bin, ...cmdArgs] = command;
179
+ if (bin === undefined) return;
180
+ spawn(bin, cmdArgs, { stdio: "ignore", detached: true });
181
+ };
182
+
183
+ if (open) {
184
+ openBrowser(url);
155
185
  }
156
- if (command === undefined) return;
157
- const [bin, ...cmdArgs] = command;
158
- if (bin === undefined) return;
159
- spawn(bin, cmdArgs, { stdio: "ignore", detached: true });
160
- };
161
-
162
- if (open) {
163
- openBrowser(url);
164
- }
165
186
 
166
- // Compute server path
167
- const outputDir = __dirname;
168
- const serverPath = join(outputDir, "../.output/server/index.mjs");
169
-
170
- // Start server with node
171
- const serverEnv = { ...process.env };
172
- if (configDir !== undefined) {
173
- // Convert MSYS/Git Bash path like /c/Users/... to Windows absolute path
174
- let resolvedPath = join(configDir, "config.json");
175
- // Convert /c/... to C:\... format
176
- if (resolvedPath.startsWith("\\c\\")) {
177
- resolvedPath = "C:" + resolvedPath;
187
+ // Compute server path
188
+ const outputDir = __dirname;
189
+ const serverPath = join(outputDir, "../.output/server/index.mjs");
190
+
191
+ // Start server with node
192
+ const serverEnv = { ...process.env };
193
+ if (configDir !== undefined) {
194
+ // Normalize MSYS / Git Bash paths to Windows native form.
195
+ // On Windows, `path.join('/c/Users/foo', 'config.json')` becomes
196
+ // `\c\Users\foo\config.json` (leading slash converted to backslash).
197
+ // Child processes spawned by `spawn()` won't follow that style, so
198
+ // rewrite `\c\...` (or any `\x\...` drive) to `C:\...` before
199
+ // handing the path to the proxy server. No-op on already-native
200
+ // Windows paths and on non-Windows platforms.
201
+ let resolvedPath = join(configDir, "config.json");
202
+ const msysMatch = /^\\([a-z])\\(.*)$/i.exec(resolvedPath);
203
+ if (msysMatch !== null) {
204
+ const drive = (msysMatch[1] ?? "").toUpperCase();
205
+ const rest = msysMatch[2] ?? "";
206
+ resolvedPath = `${drive}:\\${rest}`;
207
+ }
208
+ serverEnv["LLM_INSPECTOR_CONFIG_PATH"] = resolvedPath;
178
209
  }
179
- serverEnv["LLM_INSPECTOR_CONFIG_PATH"] = resolvedPath;
180
- }
181
- if (providersJson !== undefined) {
182
- serverEnv["LLM_INSPECTOR_PROVIDERS_JSON"] = providersJson;
183
- }
184
- const serverProcess = spawn(process.execPath, [serverPath], {
185
- stdio: ["ignore", "inherit", "inherit"],
186
- detached: true,
187
- env: serverEnv,
188
- });
210
+ if (providersJson !== undefined) {
211
+ serverEnv["LLM_INSPECTOR_PROVIDERS_JSON"] = providersJson;
212
+ }
213
+ const serverProcess = spawn(process.execPath, [serverPath], {
214
+ stdio: ["ignore", "inherit", "inherit"],
215
+ detached: true,
216
+ env: serverEnv,
217
+ });
189
218
 
190
- serverProcess.unref();
219
+ serverProcess.unref();
220
+ }