@jacobbd/relay-ai 0.4.6 → 0.4.8
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/README.md +22 -1
- package/dist/{chunk-YM6G7BHE.js → chunk-I3SSHXSP.js} +122 -9
- package/dist/chunk-I3SSHXSP.js.map +1 -0
- package/dist/cli.js +853 -88
- package/dist/cli.js.map +1 -1
- package/dist/ui/public/app.js +83 -10
- package/dist/ui/public/style.css +124 -1
- package/dist/{ui-command-2HNQX7U3.js → ui-command-P27KHEJG.js} +43 -7
- package/dist/ui-command-P27KHEJG.js.map +1 -0
- package/package.json +3 -1
- package/dist/chunk-YM6G7BHE.js.map +0 -1
- package/dist/ui-command-2HNQX7U3.js.map +0 -1
package/README.md
CHANGED
|
@@ -186,6 +186,7 @@ relay-ai ui
|
|
|
186
186
|
Opens a browser-based dashboard on a random local port. From the UI you can:
|
|
187
187
|
|
|
188
188
|
- **Launch any supported tool** — app cards for Claude Code CLI, Codex CLI, Gemini CLI, Antigravity CLI, Antigravity App, Antigravity IDE, Claude Code Desktop, and the ChatGPT Desktop app (Codex mode). Select a provider and model in the card, then click **Launch** — a native terminal opens with the selection pre-wired. No second picker in the terminal.
|
|
189
|
+
- **Keep Claude Code's Anthropic login** — on the Claude Code CLI card, check **Keep my Anthropic login and add Relay models** to keep your normal Claude models while adding the selected Relay model and compatible favorites. This option is not shown for Claude Desktop.
|
|
189
190
|
- **Manage General Favorites** — the sidebar shows your saved favorite models with a slot indicator (Slots used X/20). Favorites launch through all supported agents.
|
|
190
191
|
- **Manage Antigravity Favorites** — separate favorites panel for Antigravity sessions.
|
|
191
192
|
- **Manage providers** — add providers from templates, delete providers, and refresh model lists inline, all without leaving the browser.
|
|
@@ -201,6 +202,18 @@ relay-ai claude
|
|
|
201
202
|
|
|
202
203
|
First run: pick a provider from your registry (or complete the inline setup wizard). If you've added OpenCode Zen/Go, those appear alongside registry providers like Groq, Nvidia, or DeepSeek.
|
|
203
204
|
|
|
205
|
+
After you choose a compatible Relay model, the wizard asks whether you want to keep your normal Claude models available too. Choose **Yes** to use the Relay model alongside your existing Anthropic login, or **No** for the existing Relay-only launch.
|
|
206
|
+
|
|
207
|
+
To skip that question, use `--http-proxy`. A selected model does not need to be a favorite:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
relay-ai claude --http-proxy --provider moonshot --model kimi-k3
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Using `--http-proxy` without a provider/model keeps native Claude as the starting model and adds any compatible saved favorites. Relay models cannot be inserted into Claude Code's built-in model picker, so the terminal prints the exact `/model relay:<provider>:<model>` commands you can use to switch. The temporary local connection is password-protected, uses a per-session certificate, and closes automatically when Claude Code exits. Existing non-local network or corporate proxy settings are rejected with a clear error because proxy chaining is not supported yet.
|
|
214
|
+
|
|
215
|
+
> **Compatibility:** This mixed Anthropic + Relay mode currently supports Claude Code connected directly to Anthropic. It does not preserve Google Vertex AI configuration. Vertex users should continue using their normal Claude Code launch or Relay-only mode.
|
|
216
|
+
|
|
204
217
|
#### Favorite models and mid-session switching
|
|
205
218
|
|
|
206
219
|
Save the models you bounce between:
|
|
@@ -213,6 +226,8 @@ Add up to 20 favorites from Zen, Go, or any OpenCode-configured provider. When y
|
|
|
213
226
|
|
|
214
227
|
No favorites? Launch works like before: single model, no switch menu. `--dry-run` ignores saved favorites so you can preview a single-model launch.
|
|
215
228
|
|
|
229
|
+
That built-in favorites catalog applies to **Relay-only mode** (choose **No** when asked whether to keep your normal Claude models). In mixed Anthropic + Relay mode, compatible favorites are available through the printed `/model relay:<provider>:<model>` commands instead; Claude Code cannot add them to its built-in picker.
|
|
230
|
+
|
|
216
231
|
#### `relay-ai claude` options
|
|
217
232
|
|
|
218
233
|
| Flag | Description |
|
|
@@ -220,6 +235,7 @@ No favorites? Launch works like before: single model, no switch menu. `--dry-run
|
|
|
220
235
|
| `--dry-run` | Run the full wizard but preview the launch command instead of executing |
|
|
221
236
|
| `--setup` | Reminder to use `relay-ai providers` for provider setup |
|
|
222
237
|
| `--trace` | Write debug logs to `~/.relay-ai/logs/` and show errors on exit |
|
|
238
|
+
| `--http-proxy` | Keep the normal Anthropic login/models and add the selected model plus compatible favorites |
|
|
223
239
|
| `--help` | Show command help |
|
|
224
240
|
| `--version` | Show version |
|
|
225
241
|
|
|
@@ -227,6 +243,7 @@ No favorites? Launch works like before: single model, no switch menu. `--dry-run
|
|
|
227
243
|
relay-ai claude --dry-run
|
|
228
244
|
relay-ai claude --setup
|
|
229
245
|
relay-ai claude --trace
|
|
246
|
+
relay-ai claude --http-proxy --provider moonshot --model kimi-k3
|
|
230
247
|
```
|
|
231
248
|
|
|
232
249
|
Claude Code flags and session IDs pass through unchanged:
|
|
@@ -491,6 +508,8 @@ When you launch, relay-ai builds a clean child environment:
|
|
|
491
508
|
|
|
492
509
|
When Claude Code exits (normal exit, Ctrl+C, terminal close), your shell is unchanged. No cleanup step. No restore needed.
|
|
493
510
|
|
|
511
|
+
In `--http-proxy` mode, relay-ai does not replace your Anthropic credential. It removes stale third-party endpoint/cloud overrides from the child process, sets a password-protected loopback proxy plus a temporary CA bundle, and forwards native Anthropic requests—including their original auth and body—unchanged. Only the selected Relay model and compatible favorites are allowed to use registry provider credentials.
|
|
512
|
+
|
|
494
513
|
**Caveat: Claude Code persists the model.** relay-ai doesn't edit `~/.claude/settings.json`, but Claude Code saves the model you launched with (via `--model` and `ANTHROPIC_MODEL`). A later bare `claude` launch may still show that model, e.g. `anthropic-opencode-go__deepseek-v4-flash` from a prior relay-ai session. To get back to a first-party default, run `claude --model sonnet` (or your preferred Claude model), or remove the `"model"` key from `~/.claude/settings.json`. If you used the favorites switch menu, Claude Code may also cache the gateway catalog at `~/.claude/cache/gateway-models.json`. Delete that file if `/model` shows stale entries from a dead proxy.
|
|
495
514
|
|
|
496
515
|
### Model compatibility
|
|
@@ -570,7 +589,9 @@ The deprecated `OPENCODE_STARTER_HOME` env var still works as a fallback for `RE
|
|
|
570
589
|
|
|
571
590
|
## Contributing
|
|
572
591
|
|
|
573
|
-
Private beta right now. Issues and PRs welcome on GitHub.
|
|
592
|
+
Private beta right now. Issues and focused PRs are welcome on GitHub.
|
|
593
|
+
|
|
594
|
+
Please read [CONTRIBUTING.md](CONTRIBUTING.md) before starting. Submit one feature or fix per PR, and discuss large or security-sensitive changes with a maintainer first.
|
|
574
595
|
|
|
575
596
|
## Support
|
|
576
597
|
|
|
@@ -11,7 +11,7 @@ import { join } from "path";
|
|
|
11
11
|
// package.json
|
|
12
12
|
var package_default = {
|
|
13
13
|
name: "@jacobbd/relay-ai",
|
|
14
|
-
version: "0.4.
|
|
14
|
+
version: "0.4.8",
|
|
15
15
|
publishConfig: {
|
|
16
16
|
access: "public"
|
|
17
17
|
},
|
|
@@ -77,6 +77,7 @@ var package_default = {
|
|
|
77
77
|
ai: "^6.0.197",
|
|
78
78
|
"gitlab-ai-provider": "^6.8.0",
|
|
79
79
|
"ipaddr.js": "^2.4.0",
|
|
80
|
+
"node-forge": "^1.4.0",
|
|
80
81
|
open: "^11.0.0",
|
|
81
82
|
picocolors: "^1.1.1",
|
|
82
83
|
"smol-toml": "^1.6.1",
|
|
@@ -86,6 +87,7 @@ var package_default = {
|
|
|
86
87
|
},
|
|
87
88
|
devDependencies: {
|
|
88
89
|
"@types/node": "^22.0.0",
|
|
90
|
+
"@types/node-forge": "^1.3.14",
|
|
89
91
|
"@types/ws": "^8.18.1",
|
|
90
92
|
"@vitest/coverage-v8": "^2.1.9",
|
|
91
93
|
tsup: "^8.0.0",
|
|
@@ -1677,6 +1679,7 @@ function loadPreferences() {
|
|
|
1677
1679
|
lastGeminiModel: config.lastGeminiModel,
|
|
1678
1680
|
lastAntigravityProvider: config.lastAntigravityProvider,
|
|
1679
1681
|
lastAntigravityModel: config.lastAntigravityModel,
|
|
1682
|
+
lastClaudeTransparentMode: config.lastClaudeTransparentMode,
|
|
1680
1683
|
recentModelsByProvider: config.recentModelsByProvider,
|
|
1681
1684
|
favoriteModels: config.favoriteModels,
|
|
1682
1685
|
antigravityCliFavoriteModels: config.antigravityCliFavoriteModels,
|
|
@@ -1697,6 +1700,7 @@ function savePreferences(prefs) {
|
|
|
1697
1700
|
if (prefs.lastGeminiModel !== void 0) config.lastGeminiModel = prefs.lastGeminiModel;
|
|
1698
1701
|
if (prefs.lastAntigravityProvider !== void 0) config.lastAntigravityProvider = prefs.lastAntigravityProvider;
|
|
1699
1702
|
if (prefs.lastAntigravityModel !== void 0) config.lastAntigravityModel = prefs.lastAntigravityModel;
|
|
1703
|
+
if (prefs.lastClaudeTransparentMode !== void 0) config.lastClaudeTransparentMode = prefs.lastClaudeTransparentMode;
|
|
1700
1704
|
if (prefs.recentModelsByProvider !== void 0) config.recentModelsByProvider = prefs.recentModelsByProvider;
|
|
1701
1705
|
if (prefs.favoriteModels !== void 0) config.favoriteModels = prefs.favoriteModels;
|
|
1702
1706
|
if (prefs.antigravityCliFavoriteModels !== void 0) config.antigravityCliFavoriteModels = prefs.antigravityCliFavoriteModels;
|
|
@@ -1909,7 +1913,7 @@ function getInstalledClaudeVersion() {
|
|
|
1909
1913
|
return "2.1.183";
|
|
1910
1914
|
}
|
|
1911
1915
|
function buildClaudeArgs(model, extraArgs) {
|
|
1912
|
-
return ["--model", model, ...extraArgs];
|
|
1916
|
+
return model ? ["--model", model, ...extraArgs] : [...extraArgs];
|
|
1913
1917
|
}
|
|
1914
1918
|
function launchClaude(env, model, extraArgs) {
|
|
1915
1919
|
return new Promise((resolve) => {
|
|
@@ -1947,14 +1951,24 @@ function launchClaude(env, model, extraArgs) {
|
|
|
1947
1951
|
const forward = (signal) => {
|
|
1948
1952
|
child.kill(signal);
|
|
1949
1953
|
};
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1954
|
+
const onSigint = () => forward("SIGINT");
|
|
1955
|
+
const onSigterm = () => forward("SIGTERM");
|
|
1956
|
+
const onSighup = () => forward("SIGHUP");
|
|
1957
|
+
const cleanup = () => {
|
|
1953
1958
|
restore();
|
|
1959
|
+
process.off("SIGINT", onSigint);
|
|
1960
|
+
process.off("SIGTERM", onSigterm);
|
|
1961
|
+
process.off("SIGHUP", onSighup);
|
|
1962
|
+
};
|
|
1963
|
+
process.once("SIGINT", onSigint);
|
|
1964
|
+
process.once("SIGTERM", onSigterm);
|
|
1965
|
+
process.once("SIGHUP", onSighup);
|
|
1966
|
+
child.on("exit", (code) => {
|
|
1967
|
+
cleanup();
|
|
1954
1968
|
resolve(code ?? 0);
|
|
1955
1969
|
});
|
|
1956
1970
|
child.on("error", (err) => {
|
|
1957
|
-
|
|
1971
|
+
cleanup();
|
|
1958
1972
|
resolve(1);
|
|
1959
1973
|
});
|
|
1960
1974
|
});
|
|
@@ -5814,6 +5828,54 @@ function startProxy(completionsUrl, modelId, debug = false, contextWindow, sdk,
|
|
|
5814
5828
|
}], clientModelId, debug);
|
|
5815
5829
|
}
|
|
5816
5830
|
|
|
5831
|
+
// src/catalog.ts
|
|
5832
|
+
function localModelToRoute(lp, model) {
|
|
5833
|
+
if (model.modelFormat === "anthropic" && !model.baseUrl) return null;
|
|
5834
|
+
if (model.modelFormat === "openai" && !isSdkMigratedNpm(model.npm) && !model.completionsUrl) return null;
|
|
5835
|
+
const upstreamUrl = model.modelFormat === "cloud-code" ? model.baseUrl ?? ANTIGRAVITY_BASE_URLS[0] : model.modelFormat === "anthropic" ? model.baseUrl : model.completionsUrl;
|
|
5836
|
+
return {
|
|
5837
|
+
aliasId: claudeCodeClientModelId(aliasModelId(model.id, lp.id), model.contextWindow),
|
|
5838
|
+
realModelId: model.upstreamModelId,
|
|
5839
|
+
displayName: `${model.name || model.id} (${lp.name})`,
|
|
5840
|
+
upstreamUrl: upstreamUrl ?? "",
|
|
5841
|
+
apiKey: lp.apiKey,
|
|
5842
|
+
modelFormat: model.modelFormat,
|
|
5843
|
+
contextWindow: model.contextWindow,
|
|
5844
|
+
npm: model.npm,
|
|
5845
|
+
baseURL: model.apiBaseUrl,
|
|
5846
|
+
providerId: lp.id,
|
|
5847
|
+
authType: lp.authType,
|
|
5848
|
+
oauthAccountId: lp.oauthAccountId,
|
|
5849
|
+
providerData: lp.providerData,
|
|
5850
|
+
headers: lp.headers,
|
|
5851
|
+
supportedParameters: model.supportedParameters,
|
|
5852
|
+
reasoning: model.reasoning,
|
|
5853
|
+
interleavedReasoningField: model.interleavedReasoningField,
|
|
5854
|
+
useResponsesLite: model.useResponsesLite,
|
|
5855
|
+
preferWebSockets: model.preferWebSockets
|
|
5856
|
+
};
|
|
5857
|
+
}
|
|
5858
|
+
function makeRouteResolver(localProviders) {
|
|
5859
|
+
return (providerId, modelId) => {
|
|
5860
|
+
const provider = localProviders?.find((lp) => lp.id === providerId);
|
|
5861
|
+
const model = provider?.models.find((m) => m.id === modelId);
|
|
5862
|
+
return provider && model ? localModelToRoute(provider, model) ?? void 0 : void 0;
|
|
5863
|
+
};
|
|
5864
|
+
}
|
|
5865
|
+
function buildCatalogRoutes(startingRoute, favorites, resolveRoute, max = MAX_MODEL_CATALOG) {
|
|
5866
|
+
const droppedFavorites = [];
|
|
5867
|
+
const tail = favorites.map((fav) => {
|
|
5868
|
+
const route = resolveRoute(fav.providerId, fav.modelId);
|
|
5869
|
+
if (!route) droppedFavorites.push(fav);
|
|
5870
|
+
return route;
|
|
5871
|
+
}).filter((route) => route !== void 0);
|
|
5872
|
+
const routes = [
|
|
5873
|
+
startingRoute,
|
|
5874
|
+
...tail.filter((route) => route.aliasId !== startingRoute.aliasId)
|
|
5875
|
+
].slice(0, max);
|
|
5876
|
+
return { routes, droppedFavorites };
|
|
5877
|
+
}
|
|
5878
|
+
|
|
5817
5879
|
// src/data/model-incompatible.json
|
|
5818
5880
|
var model_incompatible_default = {
|
|
5819
5881
|
schema_version: "1",
|
|
@@ -8235,6 +8297,52 @@ function favoriteProviderDisplayName(provider) {
|
|
|
8235
8297
|
return provider.name;
|
|
8236
8298
|
}
|
|
8237
8299
|
|
|
8300
|
+
// src/http-proxy/routes.ts
|
|
8301
|
+
var HTTP_PROXY_MODEL_PREFIX = "relay:";
|
|
8302
|
+
function httpProxyModelId(providerId, modelId) {
|
|
8303
|
+
return `${HTTP_PROXY_MODEL_PREFIX}${providerId}:${modelId}`;
|
|
8304
|
+
}
|
|
8305
|
+
function supportsClaudeTransparentMode(model) {
|
|
8306
|
+
return model.modelFormat === "openai" && isSdkMigratedNpm(model.npm);
|
|
8307
|
+
}
|
|
8308
|
+
function buildHttpProxyRoutes(providers, favorites, selected, max = MAX_MODEL_CATALOG) {
|
|
8309
|
+
const routes = [];
|
|
8310
|
+
const unavailable = [];
|
|
8311
|
+
const unsupported = [];
|
|
8312
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8313
|
+
const requested = selected ? [selected, ...favorites] : favorites;
|
|
8314
|
+
for (const item of requested) {
|
|
8315
|
+
const requestKey = `${item.providerId}\0${item.modelId}`;
|
|
8316
|
+
if (seen.has(requestKey)) continue;
|
|
8317
|
+
seen.add(requestKey);
|
|
8318
|
+
if (routes.length >= max) break;
|
|
8319
|
+
const provider = providers.find((candidate) => candidate.id === item.providerId);
|
|
8320
|
+
const model = provider?.models.find((candidate) => candidate.id === item.modelId);
|
|
8321
|
+
if (!provider || !model) {
|
|
8322
|
+
unavailable.push(item);
|
|
8323
|
+
continue;
|
|
8324
|
+
}
|
|
8325
|
+
if (!supportsClaudeTransparentMode(model)) {
|
|
8326
|
+
unsupported.push(item);
|
|
8327
|
+
continue;
|
|
8328
|
+
}
|
|
8329
|
+
const route = localModelToRoute(provider, model);
|
|
8330
|
+
if (!route || !route.apiKey.trim()) {
|
|
8331
|
+
unavailable.push(item);
|
|
8332
|
+
continue;
|
|
8333
|
+
}
|
|
8334
|
+
routes.push({
|
|
8335
|
+
...route,
|
|
8336
|
+
aliasId: claudeCodeClientModelId(
|
|
8337
|
+
httpProxyModelId(provider.id, model.id),
|
|
8338
|
+
model.contextWindow
|
|
8339
|
+
),
|
|
8340
|
+
displayName: `${model.name || model.id} (${provider.name})`
|
|
8341
|
+
});
|
|
8342
|
+
}
|
|
8343
|
+
return { routes, unavailable, unsupported };
|
|
8344
|
+
}
|
|
8345
|
+
|
|
8238
8346
|
// src/opencode-serve.ts
|
|
8239
8347
|
import { execSync as execSync2, spawn as spawn2 } from "child_process";
|
|
8240
8348
|
import { existsSync as existsSync10 } from "fs";
|
|
@@ -10349,6 +10457,7 @@ async function launchOrRestartClaudeApp(prompt = "Restart Claude Desktop to appl
|
|
|
10349
10457
|
|
|
10350
10458
|
export {
|
|
10351
10459
|
BACKENDS,
|
|
10460
|
+
CONFLICTING_ENV_VARS,
|
|
10352
10461
|
MAX_MODEL_CATALOG,
|
|
10353
10462
|
VERTEX_ANTHROPIC_NPM,
|
|
10354
10463
|
VERSION,
|
|
@@ -10358,7 +10467,6 @@ export {
|
|
|
10358
10467
|
buildClaudeCodeBillingSystemLine,
|
|
10359
10468
|
selectBetaFlags,
|
|
10360
10469
|
injectClaudeIdentity,
|
|
10361
|
-
isSdkMigratedNpm,
|
|
10362
10470
|
maxToolsForNpm,
|
|
10363
10471
|
createLanguageModel,
|
|
10364
10472
|
getReasoningCapabilities,
|
|
@@ -10507,6 +10615,8 @@ export {
|
|
|
10507
10615
|
aliasModelId,
|
|
10508
10616
|
startProxyCatalog,
|
|
10509
10617
|
startProxy,
|
|
10618
|
+
makeRouteResolver,
|
|
10619
|
+
buildCatalogRoutes,
|
|
10510
10620
|
cachedModelToLocal,
|
|
10511
10621
|
fetchProviderCatalog,
|
|
10512
10622
|
providersForPicker,
|
|
@@ -10550,6 +10660,9 @@ export {
|
|
|
10550
10660
|
findClaudeApp,
|
|
10551
10661
|
isClaudeAppRunning,
|
|
10552
10662
|
quitClaudeAppGracefully,
|
|
10553
|
-
launchOrRestartClaudeApp
|
|
10663
|
+
launchOrRestartClaudeApp,
|
|
10664
|
+
httpProxyModelId,
|
|
10665
|
+
supportsClaudeTransparentMode,
|
|
10666
|
+
buildHttpProxyRoutes
|
|
10554
10667
|
};
|
|
10555
|
-
//# sourceMappingURL=chunk-
|
|
10668
|
+
//# sourceMappingURL=chunk-I3SSHXSP.js.map
|