@remodex/rmx 1.0.2 → 1.0.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.
@@ -16,8 +16,8 @@
16
16
  } catch (e) {}
17
17
  })();
18
18
  </script>
19
- <script type="module" crossorigin src="/assets/index-CkETtt7P.js"></script>
20
- <link rel="stylesheet" crossorigin href="/assets/index-CZqebSPQ.css">
19
+ <script type="module" crossorigin src="/assets/index-D3MaPif3.js"></script>
20
+ <link rel="stylesheet" crossorigin href="/assets/index-1SDbgh2-.css">
21
21
  </head>
22
22
  <body>
23
23
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remodex/rmx",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Remodex universal provider proxy for OpenAI Codex & Claude Code — use any LLM with Codex CLI/App/SDK and Claude Code",
5
5
  "displayName": "Remodex",
6
6
  "type": "module",
@@ -13,10 +13,10 @@
13
13
  "./package.json": "./package.json"
14
14
  },
15
15
  "bin": {
16
- "rmx": "./bin/ocx.mjs",
17
- "remodex": "./bin/ocx.mjs",
18
- "opencodex": "./bin/ocx.mjs",
19
- "ocx": "./bin/ocx.mjs"
16
+ "rmx": "bin/ocx.mjs",
17
+ "remodex": "bin/ocx.mjs",
18
+ "opencodex": "bin/ocx.mjs",
19
+ "ocx": "bin/ocx.mjs"
20
20
  },
21
21
  "files": [
22
22
  "bin",
@@ -8,7 +8,23 @@ import { resolveCloudflared, type CloudflaredExecutable } from "./cloudflared";
8
8
  const QUICK_TUNNEL_URL = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/iu;
9
9
  const QUICK_URL_TIMEOUT_MS = 30_000;
10
10
  const VERIFY_TIMEOUT_MS = 5_000;
11
- const RETRY_DELAYS_MS = [1_000, 2_500, 5_000, 10_000, 30_000] as const;
11
+ const PROCESS_RETRY_DELAYS_MS = [1_000, 2_500, 5_000, 10_000, 30_000] as const;
12
+ // A freshly-created Quick Tunnel URL and a newly-written Named Tunnel DNS route
13
+ // can be visible in cloudflared output before Cloudflare's edge can serve it.
14
+ // Keep that expected propagation neutral in the UI for almost two minutes. If
15
+ // it still is not reachable, expose the failure but continue probing the same
16
+ // child and URL so late propagation can recover without generating a new URL.
17
+ const VERIFY_PROPAGATION_DELAYS_MS = [
18
+ 1_000,
19
+ 2_000,
20
+ 3_000,
21
+ 5_000,
22
+ 8_000,
23
+ 13_000,
24
+ 20_000,
25
+ 30_000,
26
+ 30_000,
27
+ ] as const;
12
28
 
13
29
  export type AndroidRemoteCloudflareFailure =
14
30
  | "cloudflared_unavailable"
@@ -175,8 +191,8 @@ async function verifiedPublicGateway(
175
191
  try {
176
192
  const signal = AbortSignal.timeout(VERIFY_TIMEOUT_MS);
177
193
  const [healthResponse, descriptorResponse] = await Promise.all([
178
- fetchImpl(new URL("/healthz", baseUrl), { signal }),
179
- fetchImpl(new URL("/.well-known/t3/environment", baseUrl), { signal }),
194
+ fetchImpl(new URL("/healthz", baseUrl), { signal, cache: "no-store" }),
195
+ fetchImpl(new URL("/.well-known/t3/environment", baseUrl), { signal, cache: "no-store" }),
180
196
  ]);
181
197
  if (!healthResponse.ok || !descriptorResponse.ok) return false;
182
198
  const health = await healthResponse.json() as Record<string, unknown>;
@@ -218,7 +234,9 @@ export class ManagedAndroidRemoteCloudflareTunnel implements AndroidRemoteCloudf
218
234
  }
219
235
  return {
220
236
  mode: settings.tunnelMode,
221
- ...(settings.namedTunnelHostname ? { namedHostname: settings.namedTunnelHostname } : {}),
237
+ ...(settings.tunnelMode === "named" && settings.namedTunnelHostname
238
+ ? { namedHostname: settings.namedTunnelHostname }
239
+ : {}),
222
240
  hasNamedTunnelToken: this.hasNamedTunnelToken,
223
241
  };
224
242
  }
@@ -322,7 +340,7 @@ export class ManagedAndroidRemoteCloudflareTunnel implements AndroidRemoteCloudf
322
340
  publicUrl: null,
323
341
  error: result,
324
342
  });
325
- const delay = RETRY_DELAYS_MS[Math.min(attempt, RETRY_DELAYS_MS.length - 1)]!;
343
+ const delay = PROCESS_RETRY_DELAYS_MS[Math.min(attempt, PROCESS_RETRY_DELAYS_MS.length - 1)]!;
326
344
  attempt += 1;
327
345
  await this.deps.sleep(delay);
328
346
  }
@@ -444,7 +462,7 @@ export class ManagedAndroidRemoteCloudflareTunnel implements AndroidRemoteCloudf
444
462
  return "tunnel_failed";
445
463
  }
446
464
  verificationFailures += 1;
447
- if (verificationFailures >= 5) {
465
+ if (verificationFailures > VERIFY_PROPAGATION_DELAYS_MS.length) {
448
466
  this.publish({
449
467
  mode,
450
468
  status: "error",
@@ -452,7 +470,9 @@ export class ManagedAndroidRemoteCloudflareTunnel implements AndroidRemoteCloudf
452
470
  error: "verification_failed",
453
471
  });
454
472
  }
455
- const delay = RETRY_DELAYS_MS[Math.min(verificationFailures - 1, RETRY_DELAYS_MS.length - 1)]!;
473
+ const delay = VERIFY_PROPAGATION_DELAYS_MS[
474
+ Math.min(verificationFailures - 1, VERIFY_PROPAGATION_DELAYS_MS.length - 1)
475
+ ]!;
456
476
  const wait = await Promise.race([
457
477
  this.deps.sleep(delay).then(() => "retry" as const),
458
478
  child.exited.then(() => "exit" as const),
@@ -473,7 +493,9 @@ export class DisabledAndroidRemoteCloudflareTunnel implements AndroidRemoteCloud
473
493
  async configuration(settings: AndroidRemoteSettings): Promise<AndroidRemoteCloudflareConfiguration> {
474
494
  return {
475
495
  mode: settings.tunnelMode,
476
- ...(settings.namedTunnelHostname ? { namedHostname: settings.namedTunnelHostname } : {}),
496
+ ...(settings.tunnelMode === "named" && settings.namedTunnelHostname
497
+ ? { namedHostname: settings.namedTunnelHostname }
498
+ : {}),
477
499
  hasNamedTunnelToken: false,
478
500
  };
479
501
  }
package/src/cli/help.ts CHANGED
@@ -20,6 +20,15 @@ function canonicalCommand(value: string): string {
20
20
  }
21
21
 
22
22
  const helpEntries: Record<string, HelpEntry> = {
23
+ onboard: {
24
+ usage: "rmx onboard [--verbose] [--json] [--no-open]",
25
+ summary: "Configure Codex, Android Remote, the background service, and a verified phone link.",
26
+ details: [
27
+ "Safe to rerun: existing providers and custom-domain tunnels are preserved.",
28
+ "Fresh users get Quick Tunnel; Windows also installs the status tray.",
29
+ "--verbose shows inner command diagnostics; --json implies --no-open.",
30
+ ],
31
+ },
23
32
  init: { usage: "rmx init", summary: "Interactive setup for providers and Codex config injection." },
24
33
  setup: { usage: "rmx setup", summary: "Interactive setup for providers and Codex config injection (alias of init)." },
25
34
  start: { usage: "rmx start [--port <port>]", summary: "Start the proxy server and sync models to Codex." },
@@ -104,10 +113,17 @@ const helpEntries: Record<string, HelpEntry> = {
104
113
  },
105
114
  login: { usage: "rmx login <provider>", summary: "OAuth or API-key login for a provider." },
106
115
  logout: { usage: "rmx logout <provider>", summary: "Remove a stored provider login." },
107
- gui: { usage: "rmx gui", summary: "Open the Remodex dashboard." },
116
+ gui: {
117
+ usage: "rmx gui [--update]",
118
+ summary: "Open the Remodex dashboard; --update opens the npm package updater.",
119
+ },
108
120
  update: {
109
121
  usage: "rmx update [--tag latest|preview]",
110
122
  summary: "Update Remodex. Preview installs stay on the preview tag unless overridden.",
123
+ details: [
124
+ "Automatic updates are enabled by default for global installs.",
125
+ "Use `rmx system update auto off` to disable them, or `rmx system update auto status` to inspect the scheduler.",
126
+ ],
111
127
  },
112
128
  provider: {
113
129
  usage: "rmx provider <list|add|edit|test|remove|show|set-default|selected|quota|presets|account-mode>",
@@ -194,13 +210,15 @@ const helpEntries: Record<string, HelpEntry> = {
194
210
  grok: { usage: "rmx grok <status|exclude|include|set|clear|apply> ...", summary: "Manage and apply the Grok Build model fence." },
195
211
  integration: { usage: "rmx integration <claude|grok|client> ...", summary: "Manage supported client integrations." },
196
212
  system: {
197
- usage: "rmx system <status|settings|startup|diagnostics|sync|update|desktop-update> ...",
213
+ usage: "rmx system <status|settings|startup|diagnostics|sync|update> ...",
198
214
  summary: "Manage headless runtime settings, startup, sync, diagnostics, and updates.",
199
215
  details: [
200
- "desktop-update check [--channel latest|preview] Check the native desktop release manifest.",
201
- "desktop-update run [--channel latest|preview] Download and verify a native desktop update.",
202
- "desktop-update status Show shared tray/dashboard update state.",
203
- "Headless commands never install a native package; installation requires the desktop shell.",
216
+ "update check [--channel latest|preview] Check the @remodex/rmx package on npm.",
217
+ "update run [--channel latest|preview] --yes Install the npm package update.",
218
+ "update auto on [--channel latest|preview] Enable the daily unattended package updater (enabled by default for global installs).",
219
+ "update auto off Disable unattended package updates.",
220
+ "update auto status Show scheduler, last result, and rollback information.",
221
+ "Changelogs come from the matching GitHub Release (v<version>).",
204
222
  ],
205
223
  },
206
224
  config: {
@@ -295,6 +313,8 @@ export function printUsage(): void {
295
313
  const canonical = `Remodex (rmx) — Universal provider proxy for Codex
296
314
 
297
315
  Usage:
316
+ rmx Install/update and start the background service
317
+ rmx onboard Complete first-time setup and open the phone pairing QR
298
318
  rmx setup Interactive setup (alias: init)
299
319
  rmx start [--port <port>] Start the proxy server (auto-syncs models to Codex)
300
320
  rmx stop Stop the proxy AND restore native Codex (plain codex works again)
@@ -340,6 +360,8 @@ Usage:
340
360
  rmx --version | -v Print version
341
361
 
342
362
  Examples:
363
+ rmx Install/update and start the background service
364
+ rmx onboard Set up Codex, the service, and Android Remote
343
365
  rmx init Set up provider and inject into Codex
344
366
  rmx start Start on default port (10100)
345
367
  rmx start --port 8080 Start on custom port
package/src/cli/index.ts CHANGED
@@ -59,15 +59,6 @@ import { maybeShowUpdatePrompt } from "../update/notify";
59
59
  import { syncModelsToCodex } from "../codex/sync";
60
60
  import { setIntegrationEnabled, shouldSyncCodexOnStart, shouldSyncGrokOnStart, syncCodexOnStartIfEnabled } from "../codex/desired-state";
61
61
  import { normalizeUpdateChannel, runGuiUpdateWorker } from "../update/job";
62
- import { currentVersion } from "../update/index";
63
- import {
64
- desktopUpdateInstallAction,
65
- readDesktopUpdateState,
66
- runAutomaticDesktopUpdateCheck,
67
- runDesktopUpdateWorker,
68
- startDesktopUpdateJob,
69
- type DesktopReleaseChannel,
70
- } from "../update/desktop-release";
71
62
  import { collectOrcaCodexHomeDiagnostic } from "../codex/home";
72
63
  import { removeOwnedConfigState } from "../lib/config-ownership";
73
64
  import { withProcessRuntimeProvenance } from "../lib/bun-runtime";
@@ -460,6 +451,10 @@ async function handleStart(options: { block?: boolean } = {}) {
460
451
  // to explain it. Name the failure and the one command that repairs it.
461
452
  console.error(`⚠️ ${grokSyncFailureMessage(err)}`);
462
453
  }
454
+ try {
455
+ const { ensureDefaultAutoUpdateScheduler } = await import("../update/auto-scheduler");
456
+ ensureDefaultAutoUpdateScheduler();
457
+ } catch { /* default updater provisioning must never block proxy startup */ }
463
458
  if (options.block ?? true) {
464
459
  setInterval(() => {}, 60_000);
465
460
  await new Promise<void>(() => {});
@@ -477,6 +472,12 @@ function detachedStartEnvironment(): NodeJS.ProcessEnv {
477
472
  async function handleEnsure(options: { existingIsSuccess?: boolean } = {}): Promise<boolean> {
478
473
  if (!currentExternalCodexModelProvider()) reconcileJournal();
479
474
  const config = loadConfig();
475
+ // Provision the daily updater for global installs during the first normal
476
+ // bootstrap. The helper skips service children and the updater worker itself.
477
+ try {
478
+ const { ensureDefaultAutoUpdateScheduler } = await import("../update/auto-scheduler");
479
+ ensureDefaultAutoUpdateScheduler();
480
+ } catch { /* automatic updates must never block proxy startup */ }
480
481
  if (!codexAutoStartEnabled(config)) {
481
482
  console.log("Codex autostart is disabled.");
482
483
  return false;
@@ -653,56 +654,6 @@ async function handleTrayProxyRestart(): Promise<void> {
653
654
  await handleProxyRestart(() => handleTrayProxyStart(false));
654
655
  }
655
656
 
656
- /**
657
- * Fixed desktop-shell action used by the tray. A first click checks and
658
- * downloads a verified artifact; once the tray shows "ready", the next click
659
- * hands it to the platform installer. No caller-selected command or path is
660
- * accepted here.
661
- */
662
- async function handleDesktopUpdateRequest(requestArgs: string[] = []): Promise<void> {
663
- let expectedReady: { id: string; channel: DesktopReleaseChannel } | undefined;
664
- if (requestArgs.length > 0) {
665
- const [mode, id, channel] = requestArgs;
666
- if (
667
- requestArgs.length !== 3
668
- || mode !== "--install-ready"
669
- || !id
670
- || !/^desktop-\d+-[a-z0-9]{1,32}$/.test(id)
671
- || (channel !== "latest" && channel !== "preview")
672
- ) {
673
- console.error("Invalid desktop update request.");
674
- process.exitCode = 64;
675
- return;
676
- }
677
- expectedReady = { id, channel };
678
- }
679
- const previous = readDesktopUpdateState();
680
- const install = expectedReady !== undefined || desktopUpdateInstallAction(previous);
681
- const channel: DesktopReleaseChannel = expectedReady?.channel
682
- ?? previous?.channel
683
- ?? (currentVersion().includes("-") ? "preview" : "latest");
684
- try {
685
- const job = startDesktopUpdateJob(channel, {
686
- install,
687
- ...(expectedReady ? { expectedReady } : {}),
688
- });
689
- console.log(
690
- install
691
- ? `Desktop update installation started (${job.latestVersion ?? "latest"}).`
692
- : "Desktop update check started.",
693
- );
694
- } catch (error) {
695
- console.error(error instanceof Error ? error.message : "Desktop update could not start.");
696
- process.exitCode = 1;
697
- }
698
- }
699
-
700
- async function handleAutomaticDesktopUpdateCheck(): Promise<void> {
701
- const channel: DesktopReleaseChannel = currentVersion().includes("-") ? "preview" : "latest";
702
- const result = await runAutomaticDesktopUpdateCheck(channel);
703
- console.log(JSON.stringify(result));
704
- }
705
-
706
657
  async function handleRestartStartWhenStopped(): Promise<boolean | "skipped"> {
707
658
  if (!codexAutoStartEnabled(loadConfig())) {
708
659
  console.log("Codex autostart is disabled; no proxy was started.");
@@ -872,6 +823,17 @@ async function handleUninstall() {
872
823
  }
873
824
  };
874
825
 
826
+ await runStep("automatic updater removed", async () => {
827
+ const {
828
+ disableAutoUpdates,
829
+ readAutoUpdateStatus,
830
+ } = await import("../update/auto-scheduler");
831
+ const status = readAutoUpdateStatus();
832
+ if (status.scheduler === "absent" && !status.state) return false;
833
+ disableAutoUpdates();
834
+ return true;
835
+ });
836
+
875
837
  await runStep("service stopped", () => stopServiceIfInstalled());
876
838
 
877
839
  await runStep("proxy stopped", async () => {
@@ -1059,6 +1021,11 @@ async function handleReady(args: ReadyArgs): Promise<never> {
1059
1021
  }
1060
1022
 
1061
1023
  switch (command) {
1024
+ case "onboard": {
1025
+ const { runOnboardCommand } = await import("./onboard");
1026
+ process.exitCode = await runOnboardCommand(args.slice(1));
1027
+ break;
1028
+ }
1062
1029
  case "init":
1063
1030
  case "setup": {
1064
1031
  const { runInit } = await import("./init");
@@ -1251,6 +1218,13 @@ switch (command) {
1251
1218
  break;
1252
1219
  }
1253
1220
  case "gui": {
1221
+ const guiArgs = args.slice(1);
1222
+ const openUpdate = guiArgs.length === 1 && guiArgs[0] === "--update";
1223
+ if (guiArgs.length > (openUpdate ? 1 : 0)) {
1224
+ console.error("Usage: rmx gui [--update]");
1225
+ process.exitCode = 64;
1226
+ break;
1227
+ }
1254
1228
  const cfg = await import("../config");
1255
1229
  const config = cfg.loadConfig();
1256
1230
  // Identity-checked liveness (not the pid file + a fixed sleep): finds a fallback-port
@@ -1274,15 +1248,23 @@ switch (command) {
1274
1248
  // Open the host the proxy actually binds — `localhost` only answers for
1275
1249
  // loopback/wildcard binds, not a concrete LAN/IPv6 hostname.
1276
1250
  const guiHost = probeHostname(live?.hostname ?? config.hostname);
1277
- const guiUrl = `http://${guiHost === "127.0.0.1" ? "localhost" : guiHost}:${live?.port ?? config.port}`;
1251
+ const guiUrl = `http://${guiHost === "127.0.0.1" ? "localhost" : guiHost}:${live?.port ?? config.port}${openUpdate ? "/#dashboard/update" : ""}`;
1278
1252
  console.log(`Opening ${guiUrl}`);
1279
1253
  const { openUrl } = await import("../lib/open-url");
1280
1254
  openUrl(guiUrl);
1281
1255
  break;
1282
1256
  }
1283
- case "service":
1257
+ case "service": {
1258
+ const serviceSubcommand = args[1] ?? "install";
1284
1259
  await serviceCommand(...args.slice(1));
1260
+ if (!["status", "stop", "uninstall", "remove"].includes(serviceSubcommand)) {
1261
+ try {
1262
+ const { ensureDefaultAutoUpdateScheduler } = await import("../update/auto-scheduler");
1263
+ ensureDefaultAutoUpdateScheduler();
1264
+ } catch { /* default updater provisioning must not block service commands */ }
1265
+ }
1285
1266
  break;
1267
+ }
1286
1268
  case "tray": {
1287
1269
  const { windowsTrayCommand } = await import("../tray/windows");
1288
1270
  await windowsTrayCommand(args.slice(1));
@@ -1335,19 +1317,12 @@ switch (command) {
1335
1317
  await refreshVersionCache(channel);
1336
1318
  break;
1337
1319
  }
1338
- case "__desktop-update-worker": {
1339
- const id = args[1];
1340
- const mode = args[2];
1341
- if (!id || !/^desktop-\d+-[a-z0-9]+$/.test(id) || (mode !== "download" && mode !== "install")) {
1342
- console.error("Invalid desktop update worker arguments.");
1343
- process.exitCode = 64;
1344
- break;
1345
- }
1346
- await runDesktopUpdateWorker(id, mode === "install");
1320
+ case "__auto-update": {
1321
+ const { runAutomaticUpdateWorker } = await import("../update/auto-scheduler");
1322
+ const result = await runAutomaticUpdateWorker();
1323
+ if (!result.ok) process.exitCode = 1;
1347
1324
  break;
1348
1325
  }
1349
- case "__desktop-update":
1350
- case "__desktop-auto-update-check":
1351
1326
  case "__tray-start":
1352
1327
  case "__tray-restart":
1353
1328
  case "__startup-health":
@@ -1356,8 +1331,6 @@ switch (command) {
1356
1331
  await dispatchInternalCliCommand(command as InternalCliCommand, {
1357
1332
  trayStart: async () => { await handleTrayProxyStart(); },
1358
1333
  trayRestart: handleTrayProxyRestart,
1359
- desktopUpdate: () => handleDesktopUpdateRequest(args.slice(1)),
1360
- desktopAutoUpdateCheck: handleAutomaticDesktopUpdateCheck,
1361
1334
  startupHealth: async () => {
1362
1335
  const { collectStartupHealth } = await import("../codex/autostart-health");
1363
1336
  console.log(JSON.stringify(collectStartupHealth(loadConfig())));
package/src/cli/init.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as readline from "node:readline";
2
2
  import { constants as fsConstants, copyFileSync, existsSync, readFileSync, unlinkSync } from "node:fs";
3
3
  import { injectCodexConfig } from "../codex/inject";
4
+ import { CODEX_CONFIG_PATH } from "../codex/paths";
4
5
  import { classifyOpenAiTierBackup, getConfigPath, getDefaultConfig, isValidProviderName, saveConfig } from "../config";
5
6
  import { enrichProviderFromCatalog } from "../oauth/key-providers";
6
7
  import { deriveInitProviders } from "../providers/derive";
@@ -189,13 +190,18 @@ export async function runInit(): Promise<void> {
189
190
  // preserved by renaming it out of the collision path (sol review 260722).
190
191
  cleanupOpenAiTierBackupAfterInit();
191
192
  console.log(`\n✅ Config saved to ${getConfigPath()}`);
193
+ console.log(` Codex config target: ${CODEX_CONFIG_PATH}`);
192
194
  if (oauthHint) console.log(`🔐 Authenticate this provider with: rmx login ${providerName}`);
193
195
 
194
196
  const injectAnswer = await prompt.ask("Inject into Codex config.toml? [Y/n]: ");
195
197
  if (injectAnswer.trim().toLowerCase() !== "n") {
196
198
  console.log("Fetching available models from provider...");
197
199
  const result = await injectCodexConfig(port, config);
198
- console.log(result.success ? `✅ ${result.message}` : `⚠️ ${result.message}`);
200
+ const routed = result.success && result.routingApplied !== false;
201
+ console.log(routed ? `✅ ${result.message}` : `⚠️ ${result.message}`);
202
+ if (!routed) {
203
+ console.log(` Remodex did not take ownership of plain Codex routing. Review the reason above, then run 'rmx sync'.`);
204
+ }
199
205
  }
200
206
 
201
207
  const shimAnswer = await prompt.ask("Install Codex autostart shim? [Y/n]: ");
@@ -2,8 +2,6 @@ export type InternalCliCommand =
2
2
  | "__tray-start"
3
3
  | "__tray-restart"
4
4
  | "__startup-health"
5
- | "__desktop-update"
6
- | "__desktop-auto-update-check"
7
5
  | "__desktop-restart-codex"
8
6
  | "__desktop-restart-client";
9
7
 
@@ -11,8 +9,6 @@ export interface InternalCliHandlers {
11
9
  trayStart: () => void | Promise<void>;
12
10
  trayRestart: () => void | Promise<void>;
13
11
  startupHealth: () => void | Promise<void>;
14
- desktopUpdate: () => void | Promise<void>;
15
- desktopAutoUpdateCheck: () => void | Promise<void>;
16
12
  desktopRestartCodex: () => void | Promise<void>;
17
13
  desktopRestartClient: () => void | Promise<void>;
18
14
  }
@@ -26,8 +22,6 @@ export async function dispatchInternalCliCommand(
26
22
  case "__tray-start": return void await handlers.trayStart();
27
23
  case "__tray-restart": return void await handlers.trayRestart();
28
24
  case "__startup-health": return void await handlers.startupHealth();
29
- case "__desktop-update": return void await handlers.desktopUpdate();
30
- case "__desktop-auto-update-check": return void await handlers.desktopAutoUpdateCheck();
31
25
  case "__desktop-restart-codex": return void await handlers.desktopRestartCodex();
32
26
  case "__desktop-restart-client": return void await handlers.desktopRestartClient();
33
27
  default: throw new Error(`Unsupported internal CLI command: ${String(command)}`);