@jacobbd/relay-ai 0.2.5 → 0.2.6

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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "ignorePatterns": [
3
+ {
4
+ "pattern": "^https://www\\.npmjs\\.com/package/"
5
+ }
6
+ ]
7
+ }
package/AGENTS.md CHANGED
@@ -150,9 +150,9 @@ In all cases `process.env['OPENCODE_API_KEY']` is set immediately so the key is
150
150
  - `--restore` globs `models-*.json` (CLI) and `app-models-*.json` (App); the new files are `models-favorites.json` and `app-models-favorites.json`.
151
151
  - Zen/Go favorites are skipped in Codex (use Claude or Desktop gateway).
152
152
 
153
- ## Release status (v0.2.5)
153
+ ## Release status (v0.2.6)
154
154
 
155
- Current version is **v0.2.5** — native provider registry, agent boot flags (`--provider` / `--model`), `relay-ai --ai`, Codex favorites catalog, Codex App restore handling, reasoning capability metadata, and OpenCode cloud provider setup in `relay-ai providers add`.
155
+ Current version is **v0.2.6** — official launch release with the native provider registry, complete Claude/Codex app help, unified OpenCode Zen / Go setup, duplicate-provider migration, stable post-import refreshes, agent boot flags (`--provider` / `--model`), `relay-ai --ai`, favorites catalogs, and reasoning capability metadata.
156
156
 
157
157
  **Known limitations (by design):**
158
158
  - Cost display in Codex is always inaccurate for non-Anthropic models.
package/CHANGELOG.md CHANGED
@@ -1,13 +1,20 @@
1
1
  # Changelog
2
2
 
3
- ## [0.2.5] - 2026-06-18 (Official Launch Release)
3
+ ## [0.2.6] - 2026-06-18 (Official Launch Release)
4
4
 
5
5
  ### Added
6
6
  - **Native provider registry** — Add, list, remove, refresh, and import providers with secure OS credential storage and templates for OpenRouter, Groq, Mistral, Together AI, Zen/Go, and SDK-backed custom endpoints.
7
7
  - **Claude Code and Codex launchers** — Launch registry models through `relay-ai`, including provider/model boot flags, local OpenCode provider discovery, recent models, search, pagination, and favorites catalogs for mid-session switching.
8
8
  - **Unified SDK gateway** — Route non-Anthropic providers through the Vercel AI SDK adapter while preserving Anthropic-compatible tool use, streaming, context windows, and model catalogs.
9
9
  - **Codex App integration** — Launch the desktop app with registry providers and restore Codex App model settings after interrupted sessions.
10
+ - **Claude Desktop integration** — Launch Claude Desktop in third-party provider mode with automatic configuration backup and restore.
10
11
  - **Reasoning capability metadata** — Resolve reasoning controls from provider metadata, including OpenRouter `supported_parameters`, so models such as GLM 5.2 receive compatible reasoning options instead of stale defaults.
11
12
  - **Foreground server gateway** — Run `relay-ai server` for Claude Desktop or LAN usage, with registry-backed routing, password protection, and optional Vertex AI support.
12
13
  - **First-run setup** — Configure providers from an inline wizard or import existing OpenCode provider settings.
14
+ - **Complete command help** — Document every top-level command and managed option, including `codex-app`, `claude-app`, Vertex, restore, config, trace, and agent-reference flags.
13
15
 
16
+ ### Fixed
17
+ - **Unified OpenCode cloud setup** — Configure OpenCode Zen / Go once with their shared API key while preserving separate internal routes for overlapping model IDs and distinct upstream endpoints.
18
+ - **Duplicate OpenCode providers** — Map OpenCode CLI IDs to Relay IDs, compare existing configurations during import, and automatically migrate or remove legacy `opencode` / `opencode-go` registry duplicates.
19
+ - **Stable model refreshes** — Refresh imported providers sequentially against one shared registry so provider caches cannot overwrite each other.
20
+ - **Accurate refresh reporting** — Treat imported catalogs as snapshots rather than live-refresh baselines, preventing misleading first-refresh model-count deltas.
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  > Relay any model into any coding agent — launch tools, switch providers, and run local API gateways.
9
9
 
10
10
  [![npm version](https://img.shields.io/npm/v/@jacobbd/relay-ai)](https://www.npmjs.com/package/@jacobbd/relay-ai)
11
- [![License](https://img.shields.io/npm/l/@jacobbd/relay-ai)](https://github.com/jacob-bd/relay-ai/blob/main/LICENSE)
11
+ [![License](https://img.shields.io/npm/l/@jacobbd/relay-ai)](LICENSE)
12
12
  [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-FFDD00?style=flat-square&logo=buy-me-a-coffee&logoColor=black)](https://buymeacoffee.com/jacobbd)
13
13
 
14
14
  **relay-ai** is an interactive CLI that launches AI coding tools and runs local API gateways on your machine. Currently, it supports **Claude Code**, **Claude Desktop (Cowork + Code)**, the **OpenAI Codex CLI**, and the **Codex desktop app (macOS + Windows)**.
@@ -30,7 +30,9 @@ Pick your backend:
30
30
  | `relay-ai models` | Manage favorite models for mid-session `/model` switching |
31
31
  | `relay-ai server` | Foreground API gateway (registry providers + optional Zen/Go) |
32
32
  | `relay-ai server --vertex` | Foreground Anthropic-compatible gateway to Claude on Vertex AI |
33
+ | `relay-ai claude-app` | Launch Claude Desktop app with registry providers ([guide](docs/CLAUDE_DESKTOP_SETUP.md)) |
33
34
  | `relay-ai codex` | Launch OpenAI Codex CLI with registry providers ([guide](docs/CODEX.md)) |
35
+ | `relay-ai codex-app` | Launch Codex desktop app with registry providers ([guide](docs/CODEX.md)) |
34
36
  | `relay-ai --ai` | Full agent reference for scripts and alef-agent ([guide](docs/AI-AGENTS.md)) |
35
37
 
36
38
  Bare `relay-ai` prints help and migration guidance. Use `relay-ai claude` for the wizard.
@@ -80,14 +82,48 @@ Bare `relay-ai` prints help and migration guidance. Use `relay-ai claude` for th
80
82
 
81
83
  ## Installation
82
84
 
85
+ To install the CLI globally:
86
+
83
87
  ```bash
84
- # Install globally
85
88
  npm install -g @jacobbd/relay-ai
89
+ ```
90
+
91
+ ### Upgrading
86
92
 
87
- # Upgrade to the latest version
93
+ To upgrade to the latest version:
94
+
95
+ ```bash
88
96
  npm update -g @jacobbd/relay-ai
89
97
  ```
90
98
 
99
+ ### Uninstallation
100
+
101
+ To uninstall the CLI globally:
102
+
103
+ ```bash
104
+ npm uninstall -g @jacobbd/relay-ai
105
+ ```
106
+
107
+ > [!NOTE]
108
+ > If you use a Node version manager like **NVM**, make sure you run the uninstall command using the active Node version that was used to install it (e.g., run `nvm use <version>` first).
109
+
110
+ To fully remove the tool and all its configuration data, you can delete the configuration directory (`.relay-ai`) on your operating system:
111
+
112
+ - **macOS / Linux**:
113
+ ```bash
114
+ rm -rf ~/.relay-ai
115
+ ```
116
+ - **Windows**:
117
+ - In Command Prompt:
118
+ ```cmd
119
+ rmdir /s /q "%USERPROFILE%\.relay-ai"
120
+ ```
121
+ - In PowerShell:
122
+ ```powershell
123
+ Remove-Item -Recurse -Force "$env:USERPROFILE\.relay-ai"
124
+ ```
125
+
126
+
91
127
  ## Setup
92
128
 
93
129
  ### Configure providers
package/dist/cli.js CHANGED
@@ -76,7 +76,7 @@ function classifyModelFormat(modelId, providerNpm) {
76
76
  if (lower.startsWith("gemini-")) return "unsupported";
77
77
  return "openai";
78
78
  }
79
- var VERSION = "0.2.5";
79
+ var VERSION = "0.2.6";
80
80
 
81
81
  // src/provider-factory.ts
82
82
  var RESPONSES_ONLY_PREFIXES = [
@@ -1210,6 +1210,32 @@ import { dirname } from "path";
1210
1210
  // src/registry/types.ts
1211
1211
  var REGISTRY_SCHEMA_VERSION = 1;
1212
1212
 
1213
+ // src/registry/migrate.ts
1214
+ var LEGACY_CLOUD_PROVIDER_IDS = [
1215
+ { legacyId: "opencode", id: "zen", name: "OpenCode Zen" },
1216
+ { legacyId: "opencode-go", id: "go", name: "OpenCode Go" }
1217
+ ];
1218
+ function migrateLegacyCloudProviders(registry) {
1219
+ let changed = false;
1220
+ for (const { legacyId, id, name } of LEGACY_CLOUD_PROVIDER_IDS) {
1221
+ const legacyIdx = registry.providers.findIndex((provider) => provider.id === legacyId);
1222
+ if (legacyIdx < 0) continue;
1223
+ if (registry.providers.some((provider) => provider.id === id)) {
1224
+ registry.providers.splice(legacyIdx, 1);
1225
+ } else {
1226
+ registry.providers[legacyIdx] = {
1227
+ ...registry.providers[legacyIdx],
1228
+ id,
1229
+ templateId: id,
1230
+ name,
1231
+ api: {}
1232
+ };
1233
+ }
1234
+ changed = true;
1235
+ }
1236
+ return changed;
1237
+ }
1238
+
1213
1239
  // src/registry/validate.ts
1214
1240
  var PROVIDER_ID_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
1215
1241
  function isValidProviderId(id) {
@@ -1315,7 +1341,14 @@ function loadRegistry(path = getProvidersPath()) {
1315
1341
  }
1316
1342
  try {
1317
1343
  const raw = JSON.parse(readFileSync(path, "utf8"));
1318
- return parseRegistry(raw);
1344
+ const registry = parseRegistry(raw);
1345
+ if (migrateLegacyCloudProviders(registry)) {
1346
+ try {
1347
+ saveRegistry(registry, path);
1348
+ } catch {
1349
+ }
1350
+ }
1351
+ return registry;
1319
1352
  } catch {
1320
1353
  return { schemaVersion: REGISTRY_SCHEMA_VERSION, providers: [] };
1321
1354
  }
@@ -3298,7 +3331,6 @@ function localProviderToRegistry(provider, opts) {
3298
3331
  ...apiUrl ? { url: apiUrl } : {}
3299
3332
  },
3300
3333
  addedAt: (/* @__PURE__ */ new Date()).toISOString(),
3301
- refreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
3302
3334
  modelsCache: {
3303
3335
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
3304
3336
  models: provider.models.map(modelToCached)
@@ -3360,11 +3392,25 @@ async function resolveRefreshCredential(provider, resolveKey) {
3360
3392
  function oauthAuthRef(providerId) {
3361
3393
  return `keyring:oauth:provider:${providerId}`;
3362
3394
  }
3395
+ function normalizeImportProviderIdentity(provider) {
3396
+ if (provider.id === "opencode") {
3397
+ return { ...provider, id: "zen", name: "OpenCode Zen" };
3398
+ }
3399
+ if (provider.id === "opencode-go") {
3400
+ return { ...provider, id: "go", name: "OpenCode Go" };
3401
+ }
3402
+ return provider;
3403
+ }
3363
3404
  function buildImportProviderList(raw, authEntries) {
3364
3405
  const oauthByProviderId = /* @__PURE__ */ new Map();
3365
- const apiProviders = normalizeProviders(raw);
3366
- const covered = new Set(apiProviders.map((p19) => p19.id));
3367
- const merged = [...apiProviders];
3406
+ const covered = /* @__PURE__ */ new Set();
3407
+ const merged = [];
3408
+ for (const provider of normalizeProviders(raw)) {
3409
+ const normalized = normalizeImportProviderIdentity(provider);
3410
+ if (covered.has(normalized.id)) continue;
3411
+ merged.push(normalized);
3412
+ covered.add(normalized.id);
3413
+ }
3368
3414
  for (const provider of raw) {
3369
3415
  if (provider.id === "opencode" || provider.id === "opencode-go") continue;
3370
3416
  if (covered.has(provider.id)) continue;
@@ -3973,6 +4019,15 @@ var PROVIDER_TEMPLATES = [
3973
4019
  supported: false,
3974
4020
  unsupportedReason: "Uses gcloud Application Default Credentials \u2014 not supported via API key import."
3975
4021
  },
4022
+ {
4023
+ id: "opencode-cloud",
4024
+ name: "OpenCode Zen / Go",
4025
+ authType: "api",
4026
+ npm: "@ai-sdk/openai-compatible",
4027
+ signupUrl: "https://opencode.ai/auth",
4028
+ modelSource: "zen-go-api",
4029
+ supported: true
4030
+ },
3976
4031
  {
3977
4032
  id: "zen",
3978
4033
  name: "OpenCode Zen",
@@ -3980,7 +4035,8 @@ var PROVIDER_TEMPLATES = [
3980
4035
  npm: "@ai-sdk/openai-compatible",
3981
4036
  signupUrl: "https://opencode.ai/auth",
3982
4037
  modelSource: "zen-go-api",
3983
- supported: true
4038
+ supported: true,
4039
+ addable: false
3984
4040
  },
3985
4041
  {
3986
4042
  id: "go",
@@ -3989,7 +4045,8 @@ var PROVIDER_TEMPLATES = [
3989
4045
  npm: "@ai-sdk/openai-compatible",
3990
4046
  signupUrl: "https://opencode.ai/auth",
3991
4047
  modelSource: "zen-go-api",
3992
- supported: true
4048
+ supported: true,
4049
+ addable: false
3993
4050
  },
3994
4051
  // OAuth-gated subscription providers — use relay-ai providers auth <id> to sign in
3995
4052
  {
@@ -4004,11 +4061,16 @@ var PROVIDER_TEMPLATES = [
4004
4061
  }
4005
4062
  ];
4006
4063
  function listSupportedTemplates() {
4007
- return PROVIDER_TEMPLATES.filter((t) => t.supported && t.authType === "api");
4064
+ return PROVIDER_TEMPLATES.filter((t) => t.supported && t.authType === "api" && t.addable !== false);
4008
4065
  }
4009
4066
  function listAddableTemplates(configuredIds = []) {
4010
4067
  const configured = new Set(configuredIds);
4011
- return listSupportedTemplates().filter((t) => !configured.has(t.id));
4068
+ return listSupportedTemplates().filter((t) => {
4069
+ if (t.id === "opencode-cloud") {
4070
+ return !configured.has("zen") && !configured.has("go");
4071
+ }
4072
+ return !configured.has(t.id);
4073
+ });
4012
4074
  }
4013
4075
  function getTemplateById(id) {
4014
4076
  return PROVIDER_TEMPLATES.find((t) => t.id === id);
@@ -4193,6 +4255,7 @@ async function importFromOpencode(options = {}) {
4193
4255
  const authEntries = authFile?.entries ?? {};
4194
4256
  const { providers: fetched, oauth } = buildImportProviderList(raw, authEntries);
4195
4257
  const registry = loadRegistry();
4258
+ migrateLegacyCloudProviders(registry);
4196
4259
  const imported = [];
4197
4260
  const skipped = [];
4198
4261
  const keysSkipped = [];
@@ -5924,8 +5987,8 @@ function countUsableZenGoModels(models) {
5924
5987
  }
5925
5988
  async function resolveProvidersForDisplay() {
5926
5989
  const reg = loadRegistry();
5927
- const registryIds = new Set(reg.providers.map((p19) => p19.id));
5928
5990
  const entries = [];
5991
+ const cloudProviders = reg.providers.filter((provider) => provider.id === "zen" || provider.id === "go");
5929
5992
  const opencodeKey = await readGlobalOpencodeCredential();
5930
5993
  let zenCount = 0;
5931
5994
  let goCount = 0;
@@ -5933,37 +5996,27 @@ async function resolveProvidersForDisplay() {
5933
5996
  const zenGo = await fetchZenGoModels(["zen", "go"]);
5934
5997
  zenCount = countUsableZenGoModels(zenGo.zenModels);
5935
5998
  goCount = countUsableZenGoModels(zenGo.goModels);
5936
- if (!registryIds.has("zen") && zenCount > 0) {
5937
- entries.push({
5938
- id: "zen",
5939
- name: "OpenCode Zen",
5940
- modelCount: zenCount,
5941
- enabled: true,
5942
- authLabel: "keychain (OpenCode API key)",
5943
- inRegistry: false,
5944
- cloudBuiltin: "zen"
5945
- });
5946
- }
5947
- if (!registryIds.has("go") && goCount > 0) {
5948
- entries.push({
5949
- id: "go",
5950
- name: "OpenCode Go",
5951
- modelCount: goCount,
5952
- enabled: true,
5953
- authLabel: "keychain (OpenCode API key)",
5954
- inRegistry: false,
5955
- cloudBuiltin: "go"
5956
- });
5957
- }
5999
+ }
6000
+ if (cloudProviders.length > 0 || zenCount + goCount > 0) {
6001
+ entries.push({
6002
+ id: "opencode-cloud",
6003
+ name: "OpenCode Zen / Go",
6004
+ modelCount: zenCount + goCount || cloudProviders.reduce(
6005
+ (total, provider) => total + (provider.modelsCache?.models.length ?? 0),
6006
+ 0
6007
+ ),
6008
+ enabled: cloudProviders.length === 0 || cloudProviders.some((provider) => provider.enabled),
6009
+ authLabel: cloudProviders[0] ? formatRegistryAuthLabel(cloudProviders[0]) : "keychain (OpenCode API key)",
6010
+ inRegistry: cloudProviders.length > 0,
6011
+ cloudBuiltin: "opencode"
6012
+ });
5958
6013
  }
5959
6014
  for (const provider of reg.providers) {
5960
- let modelCount = provider.modelsCache?.models.length ?? 0;
5961
- if (provider.id === "zen" && zenCount > 0) modelCount = zenCount;
5962
- if (provider.id === "go" && goCount > 0) modelCount = goCount;
6015
+ if (provider.id === "zen" || provider.id === "go") continue;
5963
6016
  entries.push({
5964
6017
  id: provider.id,
5965
6018
  name: provider.name,
5966
- modelCount,
6019
+ modelCount: provider.modelsCache?.models.length ?? 0,
5967
6020
  enabled: provider.enabled,
5968
6021
  authLabel: formatRegistryAuthLabel(provider),
5969
6022
  inRegistry: true
@@ -7609,7 +7662,7 @@ async function refreshProviderModels(providerId, apiKey, registry = loadRegistry
7609
7662
  name: provider.name,
7610
7663
  ok: true,
7611
7664
  modelCount: enriched.length,
7612
- previousModelCount
7665
+ previousModelCount: provider.refreshedAt ? previousModelCount : void 0
7613
7666
  };
7614
7667
  } catch (err) {
7615
7668
  return {
@@ -7970,13 +8023,14 @@ async function runProvidersImport() {
7970
8023
  if (result.imported.length > 0) {
7971
8024
  const refreshSpinner = p10.spinner();
7972
8025
  refreshSpinner.start("Fetching model capabilities from providers...");
7973
- await Promise.all(result.imported.map(async (provider) => {
8026
+ const registry2 = loadRegistry();
8027
+ for (const provider of result.imported) {
7974
8028
  const key = await resolveRefreshCredential(
7975
8029
  provider,
7976
8030
  async (pr) => resolveProviderCredential(pr.id, pr.authRef)
7977
8031
  );
7978
- await refreshProviderModels(provider.id, key);
7979
- }));
8032
+ await refreshProviderModels(provider.id, key, registry2);
8033
+ }
7980
8034
  refreshSpinner.stop("Model capabilities refreshed.");
7981
8035
  }
7982
8036
  return 0;
@@ -8021,8 +8075,8 @@ async function runProvidersRefreshModels(providerId) {
8021
8075
  p10.log.error(`${result.name}: ${result.reason ?? "Refresh failed."}`);
8022
8076
  return 1;
8023
8077
  }
8024
- const diff = (result.modelCount ?? 0) - (result.previousModelCount ?? 0);
8025
- const diffStr = diff > 0 ? ` (+${diff})` : diff < 0 ? ` (${diff})` : "";
8078
+ const diff = result.previousModelCount === void 0 ? 0 : (result.modelCount ?? 0) - result.previousModelCount;
8079
+ const diffStr = result.previousModelCount === void 0 ? "" : diff > 0 ? ` (+${diff})` : diff < 0 ? ` (${diff})` : "";
8026
8080
  p10.log.success(`${result.name}: ${result.modelCount} model${result.modelCount === 1 ? "" : "s"} updated${diffStr}.`);
8027
8081
  return 0;
8028
8082
  }
@@ -8036,8 +8090,8 @@ async function runProvidersRefreshModels(providerId) {
8036
8090
  if (ok.length > 0) {
8037
8091
  p10.log.success(`Updated ${ok.length} provider${ok.length === 1 ? "" : "s"}.`);
8038
8092
  for (const r of ok) {
8039
- const diff = (r.modelCount ?? 0) - (r.previousModelCount ?? 0);
8040
- const diffStr = diff > 0 ? ` (+${diff})` : diff < 0 ? ` (${diff})` : "";
8093
+ const diff = r.previousModelCount === void 0 ? 0 : (r.modelCount ?? 0) - r.previousModelCount;
8094
+ const diffStr = r.previousModelCount === void 0 ? "" : diff > 0 ? ` (+${diff})` : diff < 0 ? ` (${diff})` : "";
8041
8095
  p10.log.info(` ${r.name}: ${r.modelCount} model${r.modelCount === 1 ? "" : "s"}${diffStr}`);
8042
8096
  }
8043
8097
  }
@@ -8148,19 +8202,25 @@ async function runTemplateAddFlow() {
8148
8202
  await migrateGlobalOpencodeCredential();
8149
8203
  const spinner10 = p10.spinner();
8150
8204
  spinner10.start(`Adding ${template.name}...`);
8151
- const stub = template.id === "zen" ? addZenRegistryStub() : addGoRegistryStub();
8152
- if (!stub.added && stub.reason) {
8205
+ const zenStub = addZenRegistryStub();
8206
+ const goStub = addGoRegistryStub();
8207
+ if (!zenStub.added && !goStub.added) {
8153
8208
  spinner10.stop("");
8154
- p10.log.warn(stub.reason);
8209
+ p10.log.warn("OpenCode Zen / Go is already configured.");
8155
8210
  return 0;
8156
8211
  }
8157
8212
  const registry = loadRegistry();
8158
- const refreshResult = await refreshProviderModels(template.id, apiKey2, registry);
8213
+ const refreshResults = [
8214
+ await refreshProviderModels("zen", apiKey2, registry),
8215
+ await refreshProviderModels("go", apiKey2, registry)
8216
+ ];
8159
8217
  spinner10.stop("");
8160
- if (refreshResult.ok) {
8161
- p10.log.success(`Added ${template.name} \u2014 ${fmtCount(refreshResult.modelCount ?? 0, "model")} updated.`);
8218
+ const modelCount = refreshResults.reduce((total, result2) => total + (result2.modelCount ?? 0), 0);
8219
+ const failed = refreshResults.filter((result2) => !result2.ok);
8220
+ if (failed.length === 0) {
8221
+ p10.log.success(`Added ${template.name} \u2014 ${fmtCount(modelCount, "model")} updated.`);
8162
8222
  } else {
8163
- p10.log.warn(`Added ${template.name}, but model refresh failed: ${refreshResult.reason ?? "Unknown error"}`);
8223
+ p10.log.warn(`Added ${template.name}, but ${failed.length} catalog refresh${failed.length === 1 ? "" : "es"} failed.`);
8164
8224
  }
8165
8225
  return 0;
8166
8226
  }
@@ -8333,9 +8393,25 @@ async function runProvidersRemove(id, interactive = false) {
8333
8393
  }
8334
8394
  return 0;
8335
8395
  }
8336
- async function runCloudBuiltinDetail(id) {
8337
- const name = id === "zen" ? "OpenCode Zen" : "OpenCode Go";
8338
- printCloudProviderPanel(name);
8396
+ async function runOpenCodeCloudDetail() {
8397
+ const registry = loadRegistry();
8398
+ const routes = registry.providers.filter((provider) => provider.id === "zen" || provider.id === "go");
8399
+ printCloudProviderPanel("OpenCode Zen / Go");
8400
+ if (routes.length === 0) return "back";
8401
+ const choice = await p10.select({
8402
+ message: "Manage an OpenCode catalog",
8403
+ options: [
8404
+ ...routes.map((provider) => ({
8405
+ value: provider.id,
8406
+ label: provider.name,
8407
+ hint: `${provider.modelsCache?.models.length ?? 0} cached models`
8408
+ })),
8409
+ { value: "back", label: "Back", hint: "" }
8410
+ ]
8411
+ });
8412
+ if (!p10.isCancel(choice) && choice !== "back") {
8413
+ await runProviderDetail(String(choice));
8414
+ }
8339
8415
  return "back";
8340
8416
  }
8341
8417
  function providerHubChoiceValue(entry) {
@@ -8450,7 +8526,7 @@ async function runProvidersHub() {
8450
8526
  }
8451
8527
  if (typeof choice === "string" && choice.startsWith("cloud:")) {
8452
8528
  const id = choice.slice("cloud:".length);
8453
- if (id === "zen" || id === "go") await runCloudBuiltinDetail(id);
8529
+ if (id === "opencode") await runOpenCodeCloudDetail();
8454
8530
  continue;
8455
8531
  }
8456
8532
  if (typeof choice === "string" && choice.startsWith("provider:")) {
@@ -9895,13 +9971,21 @@ function codexHelpText() {
9895
9971
 
9896
9972
  ${pc13.bold("Usage:")}
9897
9973
  relay-ai codex [options] [codex-flags]
9974
+ relay-ai codex --vertex
9898
9975
  relay-ai codex --restore
9899
9976
  relay-ai codex --config
9977
+ relay-ai codex --help
9978
+ relay-ai codex --version
9900
9979
 
9901
9980
  ${pc13.bold("Options:")}
9902
9981
  --trace Write proxy debug logs to ~/.relay-ai/logs/ and show errors on exit
9903
9982
  --provider Boot provider id (skip wizard when paired with --model or non-interactive)
9904
9983
  --model Boot model id (skip wizard when paired with --provider or non-interactive)
9984
+ --vertex Use Claude models through Google Vertex AI
9985
+ --restore Remove interrupted-session overlay files
9986
+ --config Preview/write launch configuration without starting Codex
9987
+ --help Show this command help
9988
+ --version Show version
9905
9989
 
9906
9990
  ${pc13.bold("Description:")}
9907
9991
  Picks a provider and model from ~/.relay-ai/providers.json, writes a temporary
@@ -10947,9 +11031,19 @@ function codexAppHelpText() {
10947
11031
  return `${pc14.bold("relay-ai codex-app")} \u2014 launch Codex desktop app with your registry providers
10948
11032
 
10949
11033
  ${pc14.bold("Usage:")}
10950
- relay-ai codex-app
11034
+ relay-ai codex-app [options]
11035
+ relay-ai codex-app --vertex
10951
11036
  relay-ai codex-app --restore
10952
11037
  relay-ai codex-app --config
11038
+ relay-ai codex-app --help
11039
+ relay-ai codex-app --version
11040
+
11041
+ ${pc14.bold("Options:")}
11042
+ --vertex Use Claude models through Google Vertex AI
11043
+ --restore Restore Codex config after an interrupted app session
11044
+ --config Preview the generated Codex app configuration without launching
11045
+ --help Show this command help
11046
+ --version Show version
10953
11047
 
10954
11048
  ${pc14.bold("Description:")}
10955
11049
  Picks a provider and model from ~/.relay-ai/providers.json, patches ~/.codex/config.toml
@@ -10970,6 +11064,8 @@ ${pc14.bold("Preview (no writes):")}
10970
11064
 
10971
11065
  ${pc14.bold("Examples:")}
10972
11066
  relay-ai codex-app
11067
+ relay-ai codex-app --vertex
11068
+ relay-ai codex-app --config
10973
11069
  relay-ai codex-app --restore
10974
11070
 
10975
11071
  ${pc14.bold("Favorites:")}
@@ -11779,9 +11875,17 @@ function claudeAppHelpText() {
11779
11875
  return `${pc15.bold("relay-ai claude-app")} \u2014 launch Claude Desktop app in 3P mode with your registry providers
11780
11876
 
11781
11877
  ${pc15.bold("Usage:")}
11782
- relay-ai claude-app
11878
+ relay-ai claude-app [options]
11783
11879
  relay-ai claude-app --trace
11784
11880
  relay-ai claude-app --restore
11881
+ relay-ai claude-app --help
11882
+ relay-ai claude-app --version
11883
+
11884
+ ${pc15.bold("Options:")}
11885
+ --trace Write proxy debug logs to ~/.relay-ai/logs/
11886
+ --restore Restore Claude Desktop config after an interrupted app session
11887
+ --help Show this command help
11888
+ --version Show version
11785
11889
 
11786
11890
  ${pc15.bold("Description:")}
11787
11891
  Picks a provider and model from ~/.relay-ai/providers.json, patches Claude Desktop config
@@ -12663,28 +12767,37 @@ function parseArgs(args) {
12663
12767
  }
12664
12768
  function rootHelpText() {
12665
12769
  return `${pc16.bold("relay-ai")} v${VERSION}
12666
- Launch AI coding tools with OpenCode Zen, Go, or local providers (Groq, Mistral,
12770
+ Launch AI coding tools with OpenCode Zen / Go or local providers (Groq, Mistral,
12667
12771
  OpenAI, Gemini, Ollama, and more).
12668
12772
 
12669
12773
  ${pc16.bold("Usage:")}
12670
12774
  relay-ai claude [options] [claude-flags]
12775
+ relay-ai claude-app [options]
12776
+ relay-ai codex [options] [codex-flags]
12777
+ relay-ai codex-app [options]
12778
+ relay-ai server [options]
12671
12779
  relay-ai models
12780
+ relay-ai favorites
12672
12781
  relay-ai providers
12673
- relay-ai codex [codex-flags]
12674
- relay-ai codex-app
12675
- relay-ai server
12676
12782
  relay-ai --help
12677
12783
  relay-ai --version
12678
12784
  relay-ai --ai Full reference for AI agents (run this when unsure)
12679
12785
  relay-ai --ai --install Install or upgrade agent skill when version changed
12680
12786
  relay-ai --ai --install --force Reinstall skill even if already current
12681
12787
 
12788
+ ${pc16.bold("Root options:")}
12789
+ -h, --help Show this help
12790
+ -v, --version Show version
12791
+ --ai Print the full reference for AI agents
12792
+ --ai --install Install or upgrade the relay-ai agent skill
12793
+ --force Reinstall the agent skill when used with --ai --install
12794
+
12682
12795
  ${pc16.bold("Commands:")}
12683
12796
  claude Launch Claude Code \u2014 pick a provider from your registry
12684
12797
  models Manage favorite models for mid-session /model switching (max ${MAX_MODEL_CATALOG})
12685
12798
  favorites Alias for models
12686
12799
  providers Add, import, and manage your AI providers
12687
- server Run a foreground API gateway (Zen, Go, and local providers)
12800
+ server Run a foreground API gateway (OpenCode Zen / Go and local providers)
12688
12801
  codex Launch OpenAI Codex CLI with registry providers
12689
12802
  codex-app Launch Codex desktop app with registry providers (macOS + Windows)
12690
12803
  claude-app Launch Claude Desktop app with registry providers (macOS + Windows)
@@ -12696,8 +12809,10 @@ ${pc16.bold("Migration:")}
12696
12809
  ${pc16.bold("Examples:")}
12697
12810
  relay-ai claude
12698
12811
  relay-ai models
12812
+ relay-ai providers
12699
12813
  relay-ai codex
12700
12814
  relay-ai codex-app
12815
+ relay-ai claude-app
12701
12816
  relay-ai server
12702
12817
  relay-ai claude -c
12703
12818
  relay-ai claude --resume abc-123
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jacobbd/relay-ai",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },