@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.
- package/.markdown-link-check.json +7 -0
- package/AGENTS.md +2 -2
- package/CHANGELOG.md +8 -1
- package/README.md +39 -3
- package/dist/cli.js +178 -63
- package/package.json +1 -1
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.
|
|
153
|
+
## Release status (v0.2.6)
|
|
154
154
|
|
|
155
|
-
Current version is **v0.2.
|
|
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.
|
|
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
|
[](https://www.npmjs.com/package/@jacobbd/relay-ai)
|
|
11
|
-
[](
|
|
11
|
+
[](LICENSE)
|
|
12
12
|
[](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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
3366
|
-
const
|
|
3367
|
-
const
|
|
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) =>
|
|
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
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) -
|
|
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) -
|
|
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
|
|
8152
|
-
|
|
8205
|
+
const zenStub = addZenRegistryStub();
|
|
8206
|
+
const goStub = addGoRegistryStub();
|
|
8207
|
+
if (!zenStub.added && !goStub.added) {
|
|
8153
8208
|
spinner10.stop("");
|
|
8154
|
-
p10.log.warn(
|
|
8209
|
+
p10.log.warn("OpenCode Zen / Go is already configured.");
|
|
8155
8210
|
return 0;
|
|
8156
8211
|
}
|
|
8157
8212
|
const registry = loadRegistry();
|
|
8158
|
-
const
|
|
8213
|
+
const refreshResults = [
|
|
8214
|
+
await refreshProviderModels("zen", apiKey2, registry),
|
|
8215
|
+
await refreshProviderModels("go", apiKey2, registry)
|
|
8216
|
+
];
|
|
8159
8217
|
spinner10.stop("");
|
|
8160
|
-
|
|
8161
|
-
|
|
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
|
|
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
|
|
8337
|
-
const
|
|
8338
|
-
|
|
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 === "
|
|
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
|
|
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
|
|
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
|