switchboard-router 0.6.3 → 0.6.5

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 (145) hide show
  1. package/README.md +40 -0
  2. package/app/.next-cli-build/BUILD_ID +1 -1
  3. package/app/.next-cli-build/app-path-routes-manifest.json +13 -13
  4. package/app/.next-cli-build/build-manifest.json +2 -2
  5. package/app/.next-cli-build/prerender-manifest.json +3 -3
  6. package/app/.next-cli-build/server/app/(dashboard)/dashboard/agent-library/page_client-reference-manifest.js +1 -1
  7. package/app/.next-cli-build/server/app/(dashboard)/dashboard/basic-chat/page_client-reference-manifest.js +1 -1
  8. package/app/.next-cli-build/server/app/(dashboard)/dashboard/cli-tools/[toolId]/page_client-reference-manifest.js +1 -1
  9. package/app/.next-cli-build/server/app/(dashboard)/dashboard/cli-tools/page_client-reference-manifest.js +1 -1
  10. package/app/.next-cli-build/server/app/(dashboard)/dashboard/combos/page_client-reference-manifest.js +1 -1
  11. package/app/.next-cli-build/server/app/(dashboard)/dashboard/combos/routing/page_client-reference-manifest.js +1 -1
  12. package/app/.next-cli-build/server/app/(dashboard)/dashboard/console-log/page_client-reference-manifest.js +1 -1
  13. package/app/.next-cli-build/server/app/(dashboard)/dashboard/endpoint/page_client-reference-manifest.js +1 -1
  14. package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/[kind]/[id]/page_client-reference-manifest.js +1 -1
  15. package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/[kind]/page_client-reference-manifest.js +1 -1
  16. package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/combo/[id]/page_client-reference-manifest.js +1 -1
  17. package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/page_client-reference-manifest.js +1 -1
  18. package/app/.next-cli-build/server/app/(dashboard)/dashboard/media-providers/web/page_client-reference-manifest.js +1 -1
  19. package/app/.next-cli-build/server/app/(dashboard)/dashboard/mitm/page_client-reference-manifest.js +1 -1
  20. package/app/.next-cli-build/server/app/(dashboard)/dashboard/page_client-reference-manifest.js +1 -1
  21. package/app/.next-cli-build/server/app/(dashboard)/dashboard/profile/page_client-reference-manifest.js +1 -1
  22. package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/[id]/page_client-reference-manifest.js +1 -1
  23. package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/new/page_client-reference-manifest.js +1 -1
  24. package/app/.next-cli-build/server/app/(dashboard)/dashboard/providers/page_client-reference-manifest.js +1 -1
  25. package/app/.next-cli-build/server/app/(dashboard)/dashboard/quota/page_client-reference-manifest.js +1 -1
  26. package/app/.next-cli-build/server/app/(dashboard)/dashboard/skills/[id]/page_client-reference-manifest.js +1 -1
  27. package/app/.next-cli-build/server/app/(dashboard)/dashboard/skills/page_client-reference-manifest.js +1 -1
  28. package/app/.next-cli-build/server/app/(dashboard)/dashboard/token-saver/page_client-reference-manifest.js +1 -1
  29. package/app/.next-cli-build/server/app/(dashboard)/dashboard/translator/page_client-reference-manifest.js +1 -1
  30. package/app/.next-cli-build/server/app/(dashboard)/dashboard/usage/page_client-reference-manifest.js +1 -1
  31. package/app/.next-cli-build/server/app/_global-error/page_client-reference-manifest.js +1 -1
  32. package/app/.next-cli-build/server/app/_global-error.html +1 -1
  33. package/app/.next-cli-build/server/app/_global-error.rsc +1 -1
  34. package/app/.next-cli-build/server/app/_global-error.segments/_full.segment.rsc +1 -1
  35. package/app/.next-cli-build/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  36. package/app/.next-cli-build/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  37. package/app/.next-cli-build/server/app/_global-error.segments/_head.segment.rsc +1 -1
  38. package/app/.next-cli-build/server/app/_global-error.segments/_index.segment.rsc +1 -1
  39. package/app/.next-cli-build/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  40. package/app/.next-cli-build/server/app/_not-found/page_client-reference-manifest.js +1 -1
  41. package/app/.next-cli-build/server/app/_not-found.html +1 -1
  42. package/app/.next-cli-build/server/app/_not-found.rsc +3 -3
  43. package/app/.next-cli-build/server/app/_not-found.segments/_full.segment.rsc +3 -3
  44. package/app/.next-cli-build/server/app/_not-found.segments/_head.segment.rsc +1 -1
  45. package/app/.next-cli-build/server/app/_not-found.segments/_index.segment.rsc +3 -3
  46. package/app/.next-cli-build/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  47. package/app/.next-cli-build/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  48. package/app/.next-cli-build/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  49. package/app/.next-cli-build/server/app/api/cli-tools/aider-settings/route.js +2 -2
  50. package/app/.next-cli-build/server/app/api/cli-tools/all-statuses/route.js +4 -4
  51. package/app/.next-cli-build/server/app/api/cli-tools/antigravity-mitm/alias/route.js +1 -1
  52. package/app/.next-cli-build/server/app/api/cli-tools/claude-settings/route.js +2 -2
  53. package/app/.next-cli-build/server/app/api/cli-tools/cline-settings/route.js +2 -2
  54. package/app/.next-cli-build/server/app/api/cli-tools/codex-settings/route.js +2 -2
  55. package/app/.next-cli-build/server/app/api/cli-tools/cowork-mcp-registry/route.js +1 -1
  56. package/app/.next-cli-build/server/app/api/cli-tools/cowork-mcp-tools/route.js +1 -1
  57. package/app/.next-cli-build/server/app/api/cli-tools/cowork-settings/route.js +2 -2
  58. package/app/.next-cli-build/server/app/api/cli-tools/deepseek-tui-settings/route.js +1 -1
  59. package/app/.next-cli-build/server/app/api/cli-tools/droid-settings/route.js +2 -2
  60. package/app/.next-cli-build/server/app/api/cli-tools/gemini-cli-settings/route.js +2 -2
  61. package/app/.next-cli-build/server/app/api/cli-tools/grok-settings/route.js +3 -3
  62. package/app/.next-cli-build/server/app/api/cli-tools/hermes-settings/route.js +3 -3
  63. package/app/.next-cli-build/server/app/api/cli-tools/jcode-settings/route.js +2 -2
  64. package/app/.next-cli-build/server/app/api/cli-tools/kilo-settings/route.js +2 -2
  65. package/app/.next-cli-build/server/app/api/cli-tools/openclaw-settings/route.js +1 -1
  66. package/app/.next-cli-build/server/app/api/cli-tools/opencode-settings/route.js +1 -1
  67. package/app/.next-cli-build/server/app/api/cli-tools/pi-settings/route.js +2 -2
  68. package/app/.next-cli-build/server/app/api/version/route.js +1 -1
  69. package/app/.next-cli-build/server/app/api/version/shutdown/route.js +1 -1
  70. package/app/.next-cli-build/server/app/api/version/update/route.js +1 -1
  71. package/app/.next-cli-build/server/app/callback/page_client-reference-manifest.js +1 -1
  72. package/app/.next-cli-build/server/app/callback.html +1 -1
  73. package/app/.next-cli-build/server/app/callback.rsc +3 -3
  74. package/app/.next-cli-build/server/app/callback.segments/_full.segment.rsc +3 -3
  75. package/app/.next-cli-build/server/app/callback.segments/_head.segment.rsc +1 -1
  76. package/app/.next-cli-build/server/app/callback.segments/_index.segment.rsc +3 -3
  77. package/app/.next-cli-build/server/app/callback.segments/_tree.segment.rsc +1 -1
  78. package/app/.next-cli-build/server/app/callback.segments/callback/__PAGE__.segment.rsc +1 -1
  79. package/app/.next-cli-build/server/app/callback.segments/callback.segment.rsc +1 -1
  80. package/app/.next-cli-build/server/app/dashboard/settings/pricing/page_client-reference-manifest.js +1 -1
  81. package/app/.next-cli-build/server/app/dashboard/settings/pricing.html +1 -1
  82. package/app/.next-cli-build/server/app/dashboard/settings/pricing.rsc +3 -3
  83. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/_full.segment.rsc +3 -3
  84. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/_head.segment.rsc +1 -1
  85. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/_index.segment.rsc +3 -3
  86. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/_tree.segment.rsc +1 -1
  87. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/dashboard/settings/pricing/__PAGE__.segment.rsc +1 -1
  88. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/dashboard/settings/pricing.segment.rsc +1 -1
  89. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/dashboard/settings.segment.rsc +1 -1
  90. package/app/.next-cli-build/server/app/dashboard/settings/pricing.segments/dashboard.segment.rsc +1 -1
  91. package/app/.next-cli-build/server/app/index.html +1 -1
  92. package/app/.next-cli-build/server/app/index.rsc +3 -3
  93. package/app/.next-cli-build/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  94. package/app/.next-cli-build/server/app/index.segments/_full.segment.rsc +3 -3
  95. package/app/.next-cli-build/server/app/index.segments/_head.segment.rsc +1 -1
  96. package/app/.next-cli-build/server/app/index.segments/_index.segment.rsc +3 -3
  97. package/app/.next-cli-build/server/app/index.segments/_tree.segment.rsc +1 -1
  98. package/app/.next-cli-build/server/app/landing/page_client-reference-manifest.js +1 -1
  99. package/app/.next-cli-build/server/app/landing.html +1 -1
  100. package/app/.next-cli-build/server/app/landing.rsc +3 -3
  101. package/app/.next-cli-build/server/app/landing.segments/_full.segment.rsc +3 -3
  102. package/app/.next-cli-build/server/app/landing.segments/_head.segment.rsc +1 -1
  103. package/app/.next-cli-build/server/app/landing.segments/_index.segment.rsc +3 -3
  104. package/app/.next-cli-build/server/app/landing.segments/_tree.segment.rsc +1 -1
  105. package/app/.next-cli-build/server/app/landing.segments/landing/__PAGE__.segment.rsc +1 -1
  106. package/app/.next-cli-build/server/app/landing.segments/landing.segment.rsc +1 -1
  107. package/app/.next-cli-build/server/app/login/page_client-reference-manifest.js +1 -1
  108. package/app/.next-cli-build/server/app/login.html +1 -1
  109. package/app/.next-cli-build/server/app/login.rsc +3 -3
  110. package/app/.next-cli-build/server/app/login.segments/_full.segment.rsc +3 -3
  111. package/app/.next-cli-build/server/app/login.segments/_head.segment.rsc +1 -1
  112. package/app/.next-cli-build/server/app/login.segments/_index.segment.rsc +3 -3
  113. package/app/.next-cli-build/server/app/login.segments/_tree.segment.rsc +1 -1
  114. package/app/.next-cli-build/server/app/login.segments/login/__PAGE__.segment.rsc +1 -1
  115. package/app/.next-cli-build/server/app/login.segments/login.segment.rsc +1 -1
  116. package/app/.next-cli-build/server/app/page_client-reference-manifest.js +1 -1
  117. package/app/.next-cli-build/server/app-paths-manifest.json +13 -13
  118. package/app/.next-cli-build/server/chunks/194.js +1 -1
  119. package/app/.next-cli-build/server/chunks/4126.js +1 -1
  120. package/app/.next-cli-build/server/chunks/4746.js +1 -1
  121. package/app/.next-cli-build/server/chunks/869.js +1 -1
  122. package/app/.next-cli-build/server/chunks/8998.js +1 -1
  123. package/app/.next-cli-build/server/middleware-build-manifest.js +1 -1
  124. package/app/.next-cli-build/server/middleware.js +3 -3
  125. package/app/.next-cli-build/server/pages/404.html +1 -1
  126. package/app/.next-cli-build/server/pages/500.html +1 -1
  127. package/app/.next-cli-build/server/server-reference-manifest.js +1 -1
  128. package/app/.next-cli-build/server/server-reference-manifest.json +1 -1
  129. package/app/.next-cli-build/static/chunks/{1321-c8ffcb14dcf776aa.js → 1321-109f024dd6c8b8ae.js} +1 -1
  130. package/app/package.json +1 -1
  131. package/app/src/lib/updater/updater.js +67 -27
  132. package/cli.js +401 -320
  133. package/package.json +1 -1
  134. package/src/cli/cliOptions.js +139 -0
  135. package/src/cli/menus/settings.js +2 -2
  136. package/src/cli/processManager.js +186 -0
  137. package/src/cli/serverStatus.js +90 -0
  138. package/src/cli/terminalUI.js +12 -11
  139. package/src/cli/tray/autostart.js +35 -14
  140. package/src/cli/tray/tray.js +34 -32
  141. package/src/cli/tray/trayWin.js +23 -9
  142. package/src/cli/utils/input.js +10 -1
  143. package/src/shared/openBrowser.js +12 -7
  144. /package/app/.next-cli-build/static/{emM_vF1dDbzWCO6J4_WjU → 87MF74MeJauiwe6D6C7fg}/_buildManifest.js +0 -0
  145. /package/app/.next-cli-build/static/{emM_vF1dDbzWCO6J4_WjU → 87MF74MeJauiwe6D6C7fg}/_ssgManifest.js +0 -0
package/app/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "switchboard-app",
3
- "version": "0.6.3",
3
+ "version": "0.6.5",
4
4
  "description": "Switchboard — intelligent model routing dashboard",
5
5
  "private": true,
6
6
  "scripts": {
@@ -2,7 +2,7 @@
2
2
  // Spawns `npm i -g <pkg>@latest`, exposes progress via tiny HTTP server.
3
3
  // Survives after parent Next server exits (detached + unref by spawner).
4
4
 
5
- const { spawn } = require("child_process");
5
+ const { execFile, spawn } = require("child_process");
6
6
  const http = require("http");
7
7
  const net = require("net");
8
8
  const path = require("path");
@@ -20,6 +20,8 @@ const waitMaxMs = parseInt(process.env.UPDATER_WAIT_MAX_MS || "15000", 10);
20
20
  const waitCheckMs = parseInt(process.env.UPDATER_WAIT_CHECK_MS || "500", 10);
21
21
  const statusStartupTimeoutMs = parseInt(process.env.UPDATER_STARTUP_TIMEOUT_MS || "2000", 10);
22
22
  const appPort = parseInt(process.env.UPDATER_APP_PORT || "20128", 10);
23
+ const readyFile = process.env.UPDATER_READY_FILE || "";
24
+ const readyToken = process.env.UPDATER_READY_TOKEN || "";
23
25
  const allowedStatusOrigins = new Set([
24
26
  `http://127.0.0.1:${appPort}`,
25
27
  `http://localhost:${appPort}`,
@@ -70,6 +72,14 @@ function setPhase(phase) {
70
72
  persistStatus();
71
73
  }
72
74
 
75
+ function publishReady() {
76
+ if (!readyFile || !readyToken) return;
77
+ const tmp = `${readyFile}.tmp-${process.pid}`;
78
+ fs.mkdirSync(path.dirname(readyFile), { recursive: true });
79
+ fs.writeFileSync(tmp, readyToken, { mode: 0o600 });
80
+ fs.renameSync(tmp, readyFile);
81
+ }
82
+
73
83
  // HTTP server exposing status (browser polls this while Next server is dead)
74
84
  const server = http.createServer((req, res) => {
75
85
  const origin = req.headers.origin;
@@ -88,6 +98,7 @@ const server = http.createServer((req, res) => {
88
98
  });
89
99
 
90
100
  let statusServerReady = false;
101
+ let finalized = false;
91
102
  let statusServerFailed = false;
92
103
  let statusServerStartupTimer = null;
93
104
 
@@ -124,8 +135,14 @@ statusServerStartupTimer = setTimeout(() => {
124
135
 
125
136
  try {
126
137
  server.listen(port, "127.0.0.1", () => {
127
- statusServerReady = true;
128
138
  if (statusServerStartupTimer) clearTimeout(statusServerStartupTimer);
139
+ try {
140
+ publishReady();
141
+ } catch (error) {
142
+ failStatusServer(new Error(`could not publish readiness: ${error.message}`));
143
+ return;
144
+ }
145
+ statusServerReady = true;
129
146
  persistStatus();
130
147
  waitForAppExit().then(runInstall).catch((error) => {
131
148
  pushLog(`[updater] wait failed: ${error.message}`);
@@ -178,6 +195,7 @@ function sleep(ms) {
178
195
  }
179
196
 
180
197
  function runInstall() {
198
+ if (finalized) return;
181
199
  state.attempt += 1;
182
200
  setPhase("installing");
183
201
  pushLog(`[updater] attempt ${state.attempt}/${maxRetries} — npm i -g ${packageName} --prefer-online`);
@@ -201,41 +219,51 @@ function runInstall() {
201
219
  persistStatus();
202
220
  });
203
221
 
204
- child.on("error", (e) => {
205
- pushLog(`[updater] spawn error: ${e.message}`);
206
- finalize(false, null, e.message);
207
- });
208
-
209
- child.on("close", (code) => {
210
- pushLog(`[updater] npm exited with code ${code}`);
211
- if (code === 0) {
222
+ let attemptSettled = false;
223
+ const settleAttempt = (code, spawnError = null) => {
224
+ if (attemptSettled || finalized) return;
225
+ attemptSettled = true;
226
+ if (spawnError) pushLog(`[updater] spawn error: ${spawnError}`);
227
+ else pushLog(`[updater] npm exited with code ${code}`);
228
+ if (!spawnError && code === 0) {
212
229
  finalize(true, code, null);
213
230
  return;
214
231
  }
215
232
  if (state.attempt < maxRetries) {
216
233
  pushLog(`[updater] retrying in ${Math.round(retryDelayMs / 1000)}s...`);
217
- setTimeout(runInstall, retryDelayMs);
234
+ setTimeout(() => { if (!finalized) runInstall(); }, retryDelayMs);
218
235
  return;
219
236
  }
220
- finalize(false, code, `Install failed after ${maxRetries} attempts`);
237
+ finalize(false, code, spawnError || `Install failed after ${maxRetries} attempts`);
238
+ };
239
+
240
+ child.on("error", (e) => {
241
+ settleAttempt(null, e.message);
242
+ });
243
+
244
+ child.on("close", (code) => {
245
+ settleAttempt(code);
221
246
  });
222
247
  }
223
248
 
224
249
  function openBrowser(url) {
225
250
  const platform = process.platform;
226
251
  const launcher = platform === "darwin"
227
- ? { command: "open", args: [url] }
252
+ ? { command: "/usr/bin/open", args: [url] }
228
253
  : platform === "win32"
229
254
  ? { command: "rundll32.exe", args: ["url.dll,FileProtocolHandler", url] }
230
255
  : { command: "xdg-open", args: [url] };
231
- try {
232
- spawn(launcher.command, launcher.args, {
233
- shell: false,
234
- detached: true,
235
- stdio: "ignore",
236
- windowsHide: true,
237
- }).unref();
238
- } catch { /* ignore */ }
256
+ return new Promise((resolve) => {
257
+ try {
258
+ execFile(launcher.command, launcher.args, { windowsHide: true, timeout: 3000 }, (error) => {
259
+ if (error) pushLog(`[updater] browser launch failed: ${error.message}`);
260
+ resolve(!error);
261
+ });
262
+ } catch (error) {
263
+ pushLog(`[updater] browser launch failed: ${error.message}`);
264
+ resolve(false);
265
+ }
266
+ });
239
267
  }
240
268
 
241
269
  // Wait until app port is listening (server alive again), then open dashboard
@@ -244,8 +272,11 @@ async function waitForAppAndOpenBrowser() {
244
272
  while (Date.now() < deadline) {
245
273
  const busy = await isAppPortBusy();
246
274
  if (busy) {
247
- openBrowser(`http://localhost:${appPort}/dashboard`);
248
- pushLog(`[updater] app ready, opened dashboard`);
275
+ const dashboardUrl = `http://localhost:${appPort}/dashboard`;
276
+ const opened = await openBrowser(dashboardUrl);
277
+ pushLog(opened
278
+ ? `[updater] app ready, opened dashboard`
279
+ : `[updater] app ready; open dashboard manually: ${dashboardUrl}`);
249
280
  return;
250
281
  }
251
282
  await sleep(1000);
@@ -268,23 +299,32 @@ function relaunchApp() {
268
299
  shell: isWin,
269
300
  env: { ...process.env, UPDATER_RELAUNCH: "", UPDATER_RELAUNCH_CMD: "", UPDATER_RELAUNCH_ARGS: "" },
270
301
  });
302
+ child.once("error", (error) => {
303
+ pushLog(`[updater] relaunch failed: ${error.message}`);
304
+ });
305
+ child.once("spawn", () => {
306
+ pushLog(`[updater] relaunched: ${cmd} ${args.join(" ")} (pid=${child.pid})`);
307
+ // Wait for new app to come up, then auto-open browser so user sees the result
308
+ waitForAppAndOpenBrowser();
309
+ });
271
310
  child.unref();
272
- pushLog(`[updater] relaunched: ${cmd} ${args.join(" ")} (pid=${child.pid})`);
273
- // Wait for new app to come up, then auto-open browser so user sees the result
274
- waitForAppAndOpenBrowser();
275
311
  } catch (e) {
276
312
  pushLog(`[updater] relaunch failed: ${e.message}`);
277
313
  }
278
314
  }
279
315
 
280
316
  function finalize(success, exitCode, error) {
317
+ if (finalized) return;
318
+ finalized = true;
281
319
  state.done = true;
282
320
  state.success = success;
283
321
  state.exitCode = exitCode;
284
322
  state.error = error;
285
323
  state.finishedAt = Date.now();
286
324
  setPhase(success ? "done" : "error");
287
- if (success) relaunchApp();
325
+ // Restore service even when installation fails; the previously installed
326
+ // package is still the safest recovery target.
327
+ relaunchApp();
288
328
  // Linger so browser can poll final status, then exit & close the port
289
329
  setTimeout(() => {
290
330
  try { server.close(); } catch { /* ignore */ }