@khanglvm/llm-router 2.2.3 → 2.2.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanglvm/llm-router",
3
- "version": "2.2.3",
3
+ "version": "2.2.4",
4
4
  "description": "LLM Router: single gateway endpoint for multi-provider LLMs with unified OpenAI+Anthropic format and seamless fallback",
5
5
  "keywords": [
6
6
  "llm-router",
package/src/cli-entry.js CHANGED
@@ -9,6 +9,7 @@ import { FIXED_LOCAL_ROUTER_HOST, FIXED_LOCAL_ROUTER_PORT } from "./node/local-s
9
9
  import { resolveListenPort } from "./node/listen-port.js";
10
10
  import { runStartCommand } from "./node/start-command.js";
11
11
  import { runWebCommand } from "./node/web-command.js";
12
+ import { runUpgradeCommand } from "./node/upgrade-command.js";
12
13
 
13
14
  function parseSimpleArgs(argv) {
14
15
  const positional = [];
@@ -198,6 +199,14 @@ export async function runCli(argv = process.argv.slice(2), isTTY = undefined, ov
198
199
  return runWebFastPath(configArgs.args, runWebCommandImpl);
199
200
  }
200
201
 
202
+ if ((first === "upgrade" || first === "update") && !parsed.wantsHelp) {
203
+ const result = await runUpgradeCommand({
204
+ onLine: (msg) => console.log(msg),
205
+ onError: (msg) => console.error(msg)
206
+ });
207
+ return result.exitCode ?? (result.ok ? 0 : 1);
208
+ }
209
+
201
210
  if (firstIsSetup && !parsed.wantsHelp) {
202
211
  const setupArgs = argv.slice(1);
203
212
  const parsedSetup = parseSimpleArgs(setupArgs);
@@ -0,0 +1,128 @@
1
+ import { execSync } from "node:child_process";
2
+ import { readFileSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import {
6
+ getActiveRuntimeState,
7
+ stopProcessByPid,
8
+ clearRuntimeState,
9
+ spawnDetachedStart
10
+ } from "./instance-state.js";
11
+
12
+ const PKG_NAME = "@khanglvm/llm-router";
13
+
14
+ function readInstalledVersion() {
15
+ try {
16
+ const dir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
17
+ const pkg = JSON.parse(readFileSync(path.join(dir, "package.json"), "utf8"));
18
+ return pkg.version || "unknown";
19
+ } catch {
20
+ return "unknown";
21
+ }
22
+ }
23
+
24
+ function fetchLatestVersion() {
25
+ try {
26
+ return execSync(`npm view ${PKG_NAME} version`, { encoding: "utf8" }).trim();
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+
32
+ function detectPackageManager() {
33
+ try {
34
+ const npmGlobalRoot = execSync("npm root -g", { encoding: "utf8" }).trim();
35
+ const entryReal = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
36
+ if (entryReal.startsWith(npmGlobalRoot)) return "npm";
37
+ } catch { /* ignore */ }
38
+
39
+ try {
40
+ const out = execSync("pnpm list -g --json 2>/dev/null", { encoding: "utf8" });
41
+ if (out.includes(PKG_NAME)) return "pnpm";
42
+ } catch { /* ignore */ }
43
+
44
+ return "npm";
45
+ }
46
+
47
+ export async function runUpgradeCommand({ onLine, onError } = {}) {
48
+ const line = typeof onLine === "function" ? onLine : (msg) => console.log(msg);
49
+ const error = typeof onError === "function" ? onError : (msg) => console.error(msg);
50
+
51
+ const currentVersion = readInstalledVersion();
52
+ line(`Current version: ${currentVersion}`);
53
+
54
+ // Check latest
55
+ line("Checking for updates...");
56
+ const latestVersion = fetchLatestVersion();
57
+ if (!latestVersion) {
58
+ error("Could not fetch latest version from npm registry.");
59
+ return { ok: false, exitCode: 1 };
60
+ }
61
+
62
+ if (latestVersion === currentVersion) {
63
+ line(`Already on the latest version (${currentVersion}).`);
64
+ return { ok: true, exitCode: 0 };
65
+ }
66
+
67
+ line(`New version available: ${currentVersion} → ${latestVersion}`);
68
+
69
+ // Stop running instance
70
+ let wasRunning = false;
71
+ let savedState = null;
72
+ try {
73
+ const runtime = await getActiveRuntimeState();
74
+ if (runtime) {
75
+ wasRunning = true;
76
+ savedState = { ...runtime };
77
+ line(`Stopping running server (pid ${runtime.pid})...`);
78
+ const stopResult = await stopProcessByPid(runtime.pid);
79
+ if (stopResult.ok) {
80
+ await clearRuntimeState({ pid: runtime.pid });
81
+ line("Server stopped.");
82
+ } else {
83
+ error(`Warning: could not stop server cleanly — ${stopResult.reason || "unknown"}`);
84
+ }
85
+ }
86
+ } catch {
87
+ // instance-state not available, skip
88
+ }
89
+
90
+ // Install latest
91
+ const pm = detectPackageManager();
92
+ const installCmd = pm === "pnpm"
93
+ ? `pnpm add -g ${PKG_NAME}@latest`
94
+ : `npm install -g ${PKG_NAME}@latest`;
95
+
96
+ line(`Upgrading via: ${installCmd}`);
97
+ try {
98
+ execSync(installCmd, { stdio: "inherit" });
99
+ } catch {
100
+ error("Upgrade failed. You may need to run with sudo or fix npm permissions.");
101
+ return { ok: false, exitCode: 1 };
102
+ }
103
+
104
+ const newVersion = fetchLatestVersion() || latestVersion;
105
+ line(`Upgraded to ${newVersion}.`);
106
+
107
+ // Restart server if it was running
108
+ if (wasRunning && savedState) {
109
+ line("Restarting server...");
110
+ try {
111
+ spawnDetachedStart({
112
+ cliPath: savedState.cliPath || "",
113
+ configPath: savedState.configPath || "",
114
+ host: savedState.host || "127.0.0.1",
115
+ port: savedState.port || 18080,
116
+ watchConfig: savedState.watchConfig ?? true,
117
+ watchBinary: savedState.watchBinary ?? true,
118
+ requireAuth: savedState.requireAuth ?? false,
119
+ });
120
+ line("Server restarted.");
121
+ } catch (err) {
122
+ error(`Could not restart server: ${err instanceof Error ? err.message : String(err)}`);
123
+ line("Start manually with: llr start");
124
+ }
125
+ }
126
+
127
+ return { ok: true, exitCode: 0 };
128
+ }