@khanglvm/llm-router 2.0.0-beta.1 → 2.0.0

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 (39) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +163 -426
  3. package/package.json +3 -3
  4. package/src/cli/router-module.js +2773 -2587
  5. package/src/cli-entry.js +32 -103
  6. package/src/node/activity-log.js +119 -0
  7. package/src/node/coding-tool-config.js +85 -11
  8. package/src/node/config-workflows.js +51 -12
  9. package/src/node/instance-state.js +1 -1
  10. package/src/node/litellm-context-catalog.js +184 -0
  11. package/src/node/local-server.js +23 -3
  12. package/src/node/port-reclaim.js +2 -2
  13. package/src/node/start-command.js +22 -22
  14. package/src/node/startup-manager.js +3 -3
  15. package/src/node/web-command.js +1 -1
  16. package/src/node/web-console-assets.js +1 -1
  17. package/src/node/web-console-client.js +34 -29
  18. package/src/node/web-console-server.js +420 -38
  19. package/src/node/web-console-styles.generated.js +1 -1
  20. package/src/node/web-console-ui/buffered-text-input.js +133 -0
  21. package/src/node/web-console-ui/config-editor-utils.js +57 -4
  22. package/src/node/web-console-ui/dropdown-placement.js +153 -0
  23. package/src/node/web-console-ui/select-search-utils.js +6 -0
  24. package/src/node/web-console-ui/transient-integer-input-utils.js +12 -0
  25. package/src/runtime/balancer.js +78 -1
  26. package/src/runtime/codex-request-transformer.js +16 -7
  27. package/src/runtime/config.js +448 -12
  28. package/src/runtime/handler/amp-response.js +5 -3
  29. package/src/runtime/handler/amp-web-search.js +2232 -0
  30. package/src/runtime/handler/fallback.js +30 -2
  31. package/src/runtime/handler/provider-call.js +353 -36
  32. package/src/runtime/handler/provider-translation.js +14 -0
  33. package/src/runtime/handler/request.js +128 -2
  34. package/src/runtime/handler/route-debug.js +36 -0
  35. package/src/runtime/handler.js +210 -20
  36. package/src/runtime/subscription-provider.js +1 -1
  37. package/src/shared/coding-tool-bindings.js +49 -0
  38. package/src/shared/local-router-defaults.js +62 -0
  39. package/src/translator/request/claude-to-openai.js +43 -0
@@ -0,0 +1,184 @@
1
+ export const LITELLM_CONTEXT_CATALOG_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
2
+ export const LITELLM_CONTEXT_CACHE_TTL_MS = 10 * 60 * 1000;
3
+
4
+ function normalizeModelLookupName(value) {
5
+ return String(value || "").trim().toLowerCase();
6
+ }
7
+
8
+ function tokenizeModelLookupName(value) {
9
+ return normalizeModelLookupName(value)
10
+ .split(/[^a-z0-9]+/g)
11
+ .filter(Boolean);
12
+ }
13
+
14
+ function buildCanonicalModelLookupVariants(value, provider = "") {
15
+ const normalizedValue = normalizeModelLookupName(value);
16
+ const normalizedProvider = normalizeModelLookupName(provider);
17
+ const variants = new Set();
18
+
19
+ if (!normalizedValue) return variants;
20
+ variants.add(normalizedValue);
21
+
22
+ if (!normalizedProvider) return variants;
23
+ for (const separator of ["/", ":"]) {
24
+ const prefix = `${normalizedProvider}${separator}`;
25
+ if (!normalizedValue.startsWith(prefix)) continue;
26
+ const strippedValue = normalizedValue.slice(prefix.length).trim();
27
+ if (strippedValue) variants.add(strippedValue);
28
+ }
29
+
30
+ return variants;
31
+ }
32
+
33
+ function isCanonicalExactModelNameMatch(query, candidate, provider = "") {
34
+ const queryVariants = buildCanonicalModelLookupVariants(query, provider);
35
+ const candidateVariants = buildCanonicalModelLookupVariants(candidate, provider);
36
+
37
+ if (queryVariants.size === 0 || candidateVariants.size === 0) return false;
38
+ for (const variant of queryVariants) {
39
+ if (candidateVariants.has(variant)) return true;
40
+ }
41
+ return false;
42
+ }
43
+
44
+ function scoreLooseModelNameMatch(query, candidate) {
45
+ const normalizedQuery = normalizeModelLookupName(query);
46
+ const normalizedCandidate = normalizeModelLookupName(candidate);
47
+ if (!normalizedQuery || !normalizedCandidate) return 0;
48
+ if (normalizedQuery === normalizedCandidate) return 1000;
49
+ if (normalizedCandidate.includes(normalizedQuery)) return 600 - (normalizedCandidate.length - normalizedQuery.length);
50
+ if (normalizedQuery.includes(normalizedCandidate)) return 500 - (normalizedQuery.length - normalizedCandidate.length);
51
+
52
+ const queryTokens = tokenizeModelLookupName(normalizedQuery);
53
+ const candidateTokens = tokenizeModelLookupName(normalizedCandidate);
54
+ if (queryTokens.length === 0 || candidateTokens.length === 0) return 0;
55
+
56
+ let score = 0;
57
+ for (const token of queryTokens) {
58
+ if (candidateTokens.includes(token)) {
59
+ score += token.length * 10;
60
+ continue;
61
+ }
62
+ const partialMatch = candidateTokens.find((candidateToken) => candidateToken.includes(token) || token.includes(candidateToken));
63
+ if (partialMatch) score += Math.min(token.length, partialMatch.length) * 4;
64
+ }
65
+ return score;
66
+ }
67
+
68
+ function extractLiteLlmContextWindow(entry) {
69
+ const maxInputTokens = Number(entry?.max_input_tokens);
70
+ if (Number.isFinite(maxInputTokens) && maxInputTokens > 0) return Math.floor(maxInputTokens);
71
+ const maxTokens = Number(entry?.max_tokens);
72
+ if (Number.isFinite(maxTokens) && maxTokens > 0) return Math.floor(maxTokens);
73
+ return null;
74
+ }
75
+
76
+ function createLiteLlmLookupResult(modelName, entry = {}) {
77
+ return {
78
+ model: String(modelName || "").trim(),
79
+ contextWindow: extractLiteLlmContextWindow(entry),
80
+ provider: String(entry?.litellm_provider || "").trim(),
81
+ mode: String(entry?.mode || "").trim()
82
+ };
83
+ }
84
+
85
+ function extractMedianContextWindow(exactMatch, suggestions = []) {
86
+ const exactContextWindow = Number(exactMatch?.contextWindow);
87
+ if (Number.isFinite(exactContextWindow) && exactContextWindow > 0) {
88
+ return Math.floor(exactContextWindow);
89
+ }
90
+
91
+ const contextWindows = (Array.isArray(suggestions) ? suggestions : [])
92
+ .map((entry) => Number(entry?.contextWindow))
93
+ .filter((value) => Number.isFinite(value) && value > 0)
94
+ .sort((left, right) => left - right);
95
+
96
+ if (contextWindows.length === 0) return null;
97
+ return contextWindows[Math.floor((contextWindows.length - 1) / 2)];
98
+ }
99
+
100
+ export function createLiteLlmContextLookupHelper({
101
+ fetchImpl = fetch,
102
+ catalogUrl = LITELLM_CONTEXT_CATALOG_URL,
103
+ cacheTtlMs = LITELLM_CONTEXT_CACHE_TTL_MS
104
+ } = {}) {
105
+ let cachedCatalog = null;
106
+ let cachedAt = 0;
107
+ let inFlightPromise = null;
108
+
109
+ async function loadCatalog() {
110
+ const now = Date.now();
111
+ if (cachedCatalog && (now - cachedAt) < cacheTtlMs) {
112
+ return cachedCatalog;
113
+ }
114
+ if (inFlightPromise) return inFlightPromise;
115
+
116
+ inFlightPromise = (async () => {
117
+ const response = await fetchImpl(catalogUrl);
118
+ if (!response.ok) {
119
+ throw new Error(`LiteLLM context catalog request failed with status ${response.status}.`);
120
+ }
121
+ const payload = await response.json();
122
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
123
+ throw new Error("LiteLLM context catalog returned an invalid payload.");
124
+ }
125
+ cachedCatalog = payload;
126
+ cachedAt = Date.now();
127
+ return cachedCatalog;
128
+ })();
129
+
130
+ try {
131
+ return await inFlightPromise;
132
+ } finally {
133
+ inFlightPromise = null;
134
+ }
135
+ }
136
+
137
+ return async function lookupLiteLlmContextWindow({ models = [], limit = 8 } = {}) {
138
+ const catalog = await loadCatalog();
139
+ const catalogEntries = Object.entries(catalog);
140
+ const resolvedLimit = Math.max(1, Math.min(Number(limit) || 8, 20));
141
+
142
+ return (Array.isArray(models) ? models : [])
143
+ .map((model) => String(model || "").trim())
144
+ .filter(Boolean)
145
+ .map((modelName) => {
146
+ const rankedMatches = catalogEntries
147
+ .map(([candidateName, entry]) => ({
148
+ result: createLiteLlmLookupResult(candidateName, entry),
149
+ score: scoreLooseModelNameMatch(modelName, candidateName)
150
+ }))
151
+ .filter((entry) => entry.score > 0 && Number.isFinite(entry.result.contextWindow))
152
+ .sort((left, right) => {
153
+ if (right.score !== left.score) return right.score - left.score;
154
+ return left.result.model.localeCompare(right.result.model);
155
+ });
156
+
157
+ const exactMatch = rankedMatches.find((entry) => isCanonicalExactModelNameMatch(
158
+ modelName,
159
+ entry.result.model,
160
+ entry.result.provider
161
+ ))?.result || null;
162
+
163
+ const suggestions = rankedMatches
164
+ .filter((entry) => {
165
+ if (!exactMatch) return true;
166
+ return !(
167
+ entry.result.model === exactMatch.model
168
+ && entry.result.provider === exactMatch.provider
169
+ && entry.result.mode === exactMatch.mode
170
+ && entry.result.contextWindow === exactMatch.contextWindow
171
+ );
172
+ })
173
+ .slice(0, resolvedLimit)
174
+ .map((entry) => entry.result);
175
+
176
+ return {
177
+ query: modelName,
178
+ exactMatch,
179
+ suggestions,
180
+ medianContextWindow: extractMedianContextWindow(exactMatch, suggestions)
181
+ };
182
+ });
183
+ };
184
+ }
@@ -9,6 +9,8 @@ import { Readable } from "node:stream";
9
9
  import { createFetchHandler } from "../runtime/handler.js";
10
10
  import { readConfigFile, getDefaultConfigPath } from "./config-store.js";
11
11
  import { FIXED_LOCAL_ROUTER_HOST, FIXED_LOCAL_ROUTER_PORT } from "./local-server-settings.js";
12
+ import { readActivityLogSettings } from "../shared/local-router-defaults.js";
13
+ import { appendActivityLogEntry, resolveActivityLogPath } from "./activity-log.js";
12
14
 
13
15
  const DEFAULT_CONFIG_RELOAD_DEBOUNCE_MS = 300;
14
16
  const MAX_CONFIG_RELOAD_DEBOUNCE_MS = 5000;
@@ -237,6 +239,7 @@ export async function startLocalRouteServer({
237
239
  port = FIXED_LOCAL_ROUTER_PORT,
238
240
  host = FIXED_LOCAL_ROUTER_HOST,
239
241
  configPath = getDefaultConfigPath(),
242
+ activityLogPath = "",
240
243
  watchConfig = true,
241
244
  configReloadDebounceMs = process.env.LLM_ROUTER_CONFIG_RELOAD_DEBOUNCE_MS,
242
245
  validateConfig,
@@ -245,20 +248,37 @@ export async function startLocalRouteServer({
245
248
  requireAuth = false
246
249
  } = {}) {
247
250
  const reloadDebounceMs = resolveReloadDebounceMs(configReloadDebounceMs);
251
+ const resolvedActivityLogPath = resolveActivityLogPath(configPath, activityLogPath);
252
+ let activityLogEnabled = true;
248
253
  const configStore = createLiveConfigStore({
249
254
  configPath,
250
255
  watchConfig,
251
256
  reloadDebounceMs,
252
257
  validateConfig,
253
- onReload: onConfigReload,
258
+ onReload: (nextConfig, reason) => {
259
+ activityLogEnabled = readActivityLogSettings(nextConfig).enabled;
260
+ if (typeof onConfigReload === "function") {
261
+ onConfigReload(nextConfig, reason);
262
+ }
263
+ },
254
264
  onReloadError: onConfigReloadError
255
265
  });
256
- await configStore.getConfig();
266
+ const initialConfig = await configStore.getConfig();
267
+ activityLogEnabled = readActivityLogSettings(initialConfig).enabled;
257
268
 
258
269
  const fetchHandler = createFetchHandler({
259
270
  ignoreAuth: !requireAuth,
260
271
  getConfig: () => configStore.getConfig(),
261
- defaultStateStoreBackend: "file"
272
+ defaultStateStoreBackend: "file",
273
+ onActivityLog: (entry) => {
274
+ if (!activityLogEnabled) return;
275
+ void appendActivityLogEntry(resolvedActivityLogPath, {
276
+ ...entry,
277
+ source: entry?.source || "runtime"
278
+ }).catch((error) => {
279
+ console.warn(`[llm-router] Failed writing activity log: ${formatError(error)}`);
280
+ });
281
+ }
262
282
  });
263
283
 
264
284
  const fallbackHost = formatHostForUrl(host, port);
@@ -130,7 +130,7 @@ export async function stopStartupManagedListener({ port, line, error }, deps = {
130
130
 
131
131
  if (!shouldStopStartup) return { ok: true, attempted: false };
132
132
 
133
- line(`Detected startup-managed llm-router on port ${port}. Stopping startup service before reclaim.`);
133
+ line(`Detected a startup-managed LLM Router instance on port ${port}. Stopping the startup service before reclaim.`);
134
134
  try {
135
135
  await stopStartupFn();
136
136
  await clearRuntimeStateFn();
@@ -140,7 +140,7 @@ export async function stopStartupManagedListener({ port, line, error }, deps = {
140
140
  return {
141
141
  ok: false,
142
142
  attempted: true,
143
- errorMessage: `Port ${port} is occupied by a startup-managed llm-router service and could not be stopped automatically. Stop it with 'llm-router stop' or 'llm-router config --operation=startup-uninstall' and retry.`
143
+ errorMessage: `Port ${port} is occupied by a startup-managed LLM Router service and could not be stopped automatically. Stop it with 'llr stop' or 'llr config --operation=startup-uninstall' and retry.`
144
144
  };
145
145
  }
146
146
  }
@@ -286,11 +286,11 @@ async function handoffToStartupManagedWithLatest({
286
286
  if (!stopped?.ok) {
287
287
  return {
288
288
  ok: false,
289
- errorMessage: stopped?.reason || `Failed to stop existing llm-router pid ${activeRuntime.pid}.`
289
+ errorMessage: stopped?.reason || `Failed to stop existing LLM Router pid ${activeRuntime.pid}.`
290
290
  };
291
291
  }
292
292
  await clearRuntimeStateFn({ pid: activeRuntime.pid });
293
- line(`Stopped manual llm-router on http://${activeRuntime.host}:${activeRuntime.port} so the startup service can own the router.`);
293
+ line(`Stopped manual LLM Router on http://${activeRuntime.host}:${activeRuntime.port} so the startup service can own the router.`);
294
294
  }
295
295
 
296
296
  const reclaimed = await reclaimPortFn({ port: startArgs.port, line, error });
@@ -319,7 +319,7 @@ async function handoffToStartupManagedWithLatest({
319
319
  if (!runtime) {
320
320
  return {
321
321
  ok: false,
322
- errorMessage: `Startup-managed llm-router did not become ready on http://${startArgs.host}:${startArgs.port}.`
322
+ errorMessage: `Startup-managed LLM Router did not become ready on http://${startArgs.host}:${startArgs.port}.`
323
323
  };
324
324
  }
325
325
  return {
@@ -333,7 +333,7 @@ async function handoffToStartupManagedWithLatest({
333
333
  error(`Failed restarting startup-managed service: ${message}`);
334
334
  return {
335
335
  ok: false,
336
- errorMessage: `Failed to restart startup-managed llm-router with latest installed version: ${message}`
336
+ errorMessage: `Failed to restart the startup-managed LLM Router instance with the latest installed version: ${message}`
337
337
  };
338
338
  }
339
339
  }
@@ -394,7 +394,7 @@ export async function runStartCommand(options = {}) {
394
394
  exitCode: 2,
395
395
  errorMessage: [
396
396
  `Config file not found: ${configPath}`,
397
- "Run 'llm-router config' to create provider config or 'llm-router -h' for help."
397
+ "Run 'llr config' to create provider config or 'llr -h' for help."
398
398
  ].join("\n")
399
399
  };
400
400
  }
@@ -439,7 +439,7 @@ export async function runStartCommand(options = {}) {
439
439
  exitCode: 2,
440
440
  errorMessage: [
441
441
  `No providers configured in ${configPath}`,
442
- "Run 'llm-router config' to add a provider or 'llm-router -h' for help."
442
+ "Run 'llr config' to add a provider or 'llr -h' for help."
443
443
  ].join("\n")
444
444
  };
445
445
  }
@@ -450,7 +450,7 @@ export async function runStartCommand(options = {}) {
450
450
  exitCode: 2,
451
451
  errorMessage: [
452
452
  `Local auth requires masterKey in ${configPath}.`,
453
- "Run 'llm-router config --operation=set-master-key --master-key=...' or start without --require-auth."
453
+ "Run 'llr config --operation=set-master-key --master-key=...' or start without --require-auth."
454
454
  ].join("\n")
455
455
  };
456
456
  }
@@ -494,7 +494,7 @@ export async function runStartCommand(options = {}) {
494
494
  ok: true,
495
495
  exitCode: 0,
496
496
  data: [
497
- `Startup-managed llm-router is active on http://${handoff.runtime.host}:${handoff.runtime.port}.`,
497
+ `Startup-managed LLM Router is active on http://${handoff.runtime.host}:${handoff.runtime.port}.`,
498
498
  `manager=${handoff.detail?.manager || startup.manager || "unknown"}`,
499
499
  `service=${handoff.detail?.serviceId || startup.serviceId || "unknown"}`
500
500
  ].join("\n")
@@ -538,7 +538,7 @@ export async function runStartCommand(options = {}) {
538
538
  startArgs: buildStartArgs({ configPath, ...nextLocalServer })
539
539
  });
540
540
  if (!launch.ok) {
541
- error(`Failed to relaunch llm-router after config runtime change: ${launch.error instanceof Error ? launch.error.message : String(launch.error)}`);
541
+ error(`Failed to relaunch LLM Router after the config runtime change: ${launch.error instanceof Error ? launch.error.message : String(launch.error)}`);
542
542
  process.exit(1);
543
543
  return;
544
544
  }
@@ -562,7 +562,7 @@ export async function runStartCommand(options = {}) {
562
562
  return {
563
563
  ok: false,
564
564
  exitCode: 1,
565
- errorMessage: `Another llm-router instance is already running at http://${activeRuntime.host}:${activeRuntime.port}. Stop it before starting a new one.`
565
+ errorMessage: `Another LLM Router instance is already running at http://${activeRuntime.host}:${activeRuntime.port}. Stop it before starting a new one.`
566
566
  };
567
567
  }
568
568
 
@@ -574,7 +574,7 @@ export async function runStartCommand(options = {}) {
574
574
  return {
575
575
  ok: false,
576
576
  exitCode: 1,
577
- errorMessage: `Failed to start llm-router on http://${host}:${port}: ${startError instanceof Error ? startError.message : String(startError)}`
577
+ errorMessage: `Failed to start LLM Router on http://${host}:${port}: ${startError instanceof Error ? startError.message : String(startError)}`
578
578
  };
579
579
  }
580
580
 
@@ -622,7 +622,7 @@ export async function runStartCommand(options = {}) {
622
622
  ok: true,
623
623
  exitCode: 0,
624
624
  data: [
625
- "Restarted startup-managed llm-router instance with latest installed version.",
625
+ "Restarted the startup-managed LLM Router instance with the latest installed version.",
626
626
  `manager=${restarted.detail?.manager || "unknown"}`,
627
627
  `service=${restarted.detail?.serviceId || "unknown"}`
628
628
  ].join("\n")
@@ -633,7 +633,7 @@ export async function runStartCommand(options = {}) {
633
633
  return {
634
634
  ok: true,
635
635
  exitCode: 0,
636
- data: `Startup-managed llm-router is still running on port ${port}. Exiting without changes.`
636
+ data: `Startup-managed LLM Router is still running on port ${port}. Exiting without changes.`
637
637
  };
638
638
  }
639
639
 
@@ -647,7 +647,7 @@ export async function runStartCommand(options = {}) {
647
647
  };
648
648
  }
649
649
 
650
- line("Startup-managed instance stopped. Starting llm-router in this terminal...");
650
+ line("Startup-managed instance stopped. Starting LLM Router in this terminal...");
651
651
  const takeoverStart = await attemptServerStartAfterStartupStop(buildLocalServerOptions, { startLocalRouteServer: startLocalRouteServerFn });
652
652
  if (takeoverStart.ok) {
653
653
  server = takeoverStart.server;
@@ -656,7 +656,7 @@ export async function runStartCommand(options = {}) {
656
656
  return {
657
657
  ok: false,
658
658
  exitCode: 1,
659
- errorMessage: `Failed to start llm-router on http://${host}:${port}: ${takeoverStart.error instanceof Error ? takeoverStart.error.message : String(takeoverStart.error)}`
659
+ errorMessage: `Failed to start LLM Router on http://${host}:${port}: ${takeoverStart.error instanceof Error ? takeoverStart.error.message : String(takeoverStart.error)}`
660
660
  };
661
661
  }
662
662
  }
@@ -682,7 +682,7 @@ export async function runStartCommand(options = {}) {
682
682
  return {
683
683
  ok: false,
684
684
  exitCode: 1,
685
- errorMessage: `Failed to start llm-router after reclaiming port ${port}: ${retryError instanceof Error ? retryError.message : String(retryError)}`
685
+ errorMessage: `Failed to start LLM Router after reclaiming port ${port}: ${retryError instanceof Error ? retryError.message : String(retryError)}`
686
686
  };
687
687
  }
688
688
  }
@@ -763,7 +763,7 @@ export async function runStartCommand(options = {}) {
763
763
  binaryState = nextState;
764
764
 
765
765
  if (managedByStartup) {
766
- line(`Detected llm-router update (${from} -> ${to}). Exiting for startup manager to relaunch latest version.`);
766
+ line(`Detected LLM Router update (${from} -> ${to}). Exiting for the startup manager to relaunch the latest version.`);
767
767
  void shutdown().then(() => {
768
768
  process.exit(0);
769
769
  });
@@ -774,7 +774,7 @@ export async function runStartCommand(options = {}) {
774
774
  if (!cliPath) {
775
775
  if (!binaryNoticeSent) {
776
776
  binaryNoticeSent = true;
777
- line(`Detected llm-router update (${from} -> ${to}). Restart this process to run the new version.`);
777
+ line(`Detected LLM Router update (${from} -> ${to}). Restart this process to run the new version.`);
778
778
  }
779
779
  return;
780
780
  }
@@ -782,22 +782,22 @@ export async function runStartCommand(options = {}) {
782
782
  binaryRelaunching = true;
783
783
  void (async () => {
784
784
  try {
785
- line(`Detected llm-router update (${from} -> ${to}). Relaunching latest version...`);
785
+ line(`Detected LLM Router update (${from} -> ${to}). Relaunching the latest version...`);
786
786
  await shutdown();
787
787
  const launch = await spawnReplacementCli({
788
788
  cliPath,
789
789
  startArgs: buildStartArgs({ configPath, host, port, watchConfig, watchBinary, requireAuth })
790
790
  });
791
791
  if (!launch.ok) {
792
- error(`Failed to relaunch updated llm-router: ${launch.error instanceof Error ? launch.error.message : String(launch.error)}`);
792
+ error(`Failed to relaunch the updated LLM Router process: ${launch.error instanceof Error ? launch.error.message : String(launch.error)}`);
793
793
  process.exit(1);
794
794
  return;
795
795
  }
796
796
 
797
- line(`Started updated llm-router process (pid ${launch.pid || "unknown"}).`);
797
+ line(`Started the updated LLM Router process (pid ${launch.pid || "unknown"}).`);
798
798
  process.exit(0);
799
799
  } catch (relaunchError) {
800
- error(`Failed during llm-router auto-relaunch: ${relaunchError instanceof Error ? relaunchError.message : String(relaunchError)}`);
800
+ error(`Failed during LLM Router auto-relaunch: ${relaunchError instanceof Error ? relaunchError.message : String(relaunchError)}`);
801
801
  process.exit(1);
802
802
  }
803
803
  })();
@@ -1,5 +1,5 @@
1
1
  /**
2
- * OS startup integration for llm-router.
2
+ * OS startup integration for LLM Router.
3
3
  * Supports macOS LaunchAgent and Linux systemd --user service.
4
4
  */
5
5
 
@@ -58,14 +58,14 @@ export function resolveStartupCliEntryPath({
58
58
  }
59
59
 
60
60
  const nodeBinDir = path.dirname(execPath);
61
- for (const binName of ["llm-router", "llm-router-route"]) {
61
+ for (const binName of ["llr", "llm-router", "llm-router-route"]) {
62
62
  const candidate = path.join(nodeBinDir, binName);
63
63
  if (exists(candidate)) return candidate;
64
64
  }
65
65
 
66
66
  if (envCliPath) return envCliPath;
67
67
  if (argvCliPath) return path.resolve(argvCliPath);
68
- throw new Error("Unable to resolve llm-router CLI entry path.");
68
+ throw new Error("Unable to resolve the LLM Router CLI entry path.");
69
69
  }
70
70
 
71
71
  function makeExecArgs({ configPath }) {
@@ -67,7 +67,7 @@ export async function runWebCommand(options = {}) {
67
67
  return {
68
68
  ok: false,
69
69
  exitCode: 1,
70
- errorMessage: `Failed to start llm-router web console: ${startError instanceof Error ? startError.message : String(startError)}`
70
+ errorMessage: `Failed to start the LLM Router web console: ${startError instanceof Error ? startError.message : String(startError)}`
71
71
  };
72
72
  }
73
73
 
@@ -3,7 +3,7 @@ import { WEB_CONSOLE_CSS } from "./web-console-styles.generated.js";
3
3
  export { WEB_CONSOLE_CSS };
4
4
 
5
5
  export function renderWebConsoleHtml({
6
- title = "LLM Router Web",
6
+ title = "LLM Router Web Console",
7
7
  headHtml = "",
8
8
  bodyHtml = ""
9
9
  } = {}) {