@jacobbd/relay-ai 0.6.0 → 0.6.1
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 +1 -1
- package/dist/{chunk-MVBA7ABV.js → chunk-EJONCU3B.js} +4 -4
- package/dist/chunk-EJONCU3B.js.map +1 -0
- package/dist/{chunk-SV2Y6OCD.js → chunk-P4S42QJK.js} +120 -301
- package/dist/chunk-P4S42QJK.js.map +1 -0
- package/dist/cli.js +341 -221
- package/dist/cli.js.map +1 -1
- package/dist/{provider-templates-6XYKAZB5.js → provider-templates-BPGB5V2L.js} +2 -2
- package/dist/ui/public/app.js +44 -3
- package/dist/ui/public/style.css +16 -0
- package/dist/{ui-command-DJZIIIWC.js → ui-command-EQJIZRZZ.js} +27 -13
- package/dist/{ui-command-DJZIIIWC.js.map → ui-command-EQJIZRZZ.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-MVBA7ABV.js.map +0 -1
- package/dist/chunk-SV2Y6OCD.js.map +0 -1
- /package/dist/{provider-templates-6XYKAZB5.js.map → provider-templates-BPGB5V2L.js.map} +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
getTemplateById,
|
|
4
4
|
init_provider_templates
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-EJONCU3B.js";
|
|
6
6
|
|
|
7
7
|
// src/constants.ts
|
|
8
8
|
import { homedir } from "os";
|
|
@@ -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.6.
|
|
14
|
+
version: "0.6.1",
|
|
15
15
|
publishConfig: {
|
|
16
16
|
access: "public"
|
|
17
17
|
},
|
|
@@ -1784,6 +1784,10 @@ async function getSavedServerPassword() {
|
|
|
1784
1784
|
}
|
|
1785
1785
|
return null;
|
|
1786
1786
|
}
|
|
1787
|
+
function getEnvServerPassword() {
|
|
1788
|
+
const value = process.env["RELAY_AI_SERVER_PASSWORD"]?.trim();
|
|
1789
|
+
return value || null;
|
|
1790
|
+
}
|
|
1787
1791
|
async function setSavedServerPassword(password) {
|
|
1788
1792
|
const keyring = await getServerPasswordKeyring();
|
|
1789
1793
|
if (keyring) {
|
|
@@ -1867,15 +1871,15 @@ import { join as join4 } from "path";
|
|
|
1867
1871
|
import { execFileSync } from "child_process";
|
|
1868
1872
|
import { existsSync as existsSync2 } from "fs";
|
|
1869
1873
|
function findBinaryOnPath(name, fallbackPaths, options = {}) {
|
|
1870
|
-
const
|
|
1874
|
+
const isWindows2 = options.isWindows ?? process.platform === "win32";
|
|
1871
1875
|
const exists = options.exists ?? existsSync2;
|
|
1872
1876
|
const runWhich = options.runWhich ?? ((binary, win) => execFileSync(win ? "where.exe" : "which", [binary], {
|
|
1873
1877
|
encoding: "utf8",
|
|
1874
1878
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1875
1879
|
}));
|
|
1876
1880
|
try {
|
|
1877
|
-
const lines = runWhich(name,
|
|
1878
|
-
const path = (
|
|
1881
|
+
const lines = runWhich(name, isWindows2).trim().split("\n").map((line) => line.trim()).filter(Boolean);
|
|
1882
|
+
const path = (isWindows2 ? lines.find((line) => line.toLowerCase().endsWith(".cmd")) : null) ?? lines[0];
|
|
1879
1883
|
if (path && (!options.verifyWhichResult || exists(path))) return path;
|
|
1880
1884
|
} catch {
|
|
1881
1885
|
}
|
|
@@ -7377,7 +7381,7 @@ async function fetchTemplateModels(template, apiKey, baseUrlOverride, extraHeade
|
|
|
7377
7381
|
models: [],
|
|
7378
7382
|
baseUrl: "",
|
|
7379
7383
|
error: "This provider needs a base URL.",
|
|
7380
|
-
hint: "
|
|
7384
|
+
hint: template.urlPrompt ? "Enter the API base URL when adding this provider." : "This template is missing a default base URL \u2014 report a bug."
|
|
7381
7385
|
};
|
|
7382
7386
|
}
|
|
7383
7387
|
if (template.modelSource === "static-seed") {
|
|
@@ -8233,7 +8237,7 @@ async function refreshProviderModels(providerId, apiKey, registry = loadRegistry
|
|
|
8233
8237
|
}
|
|
8234
8238
|
const source = resolveModelSource(provider);
|
|
8235
8239
|
if (source === "manual-only") {
|
|
8236
|
-
const hint = provider.templateId === "google-vertex" || provider.id === "google-vertex" || provider.api.npm === "@ai-sdk/google-vertex" ? "Vertex uses gcloud credentials \u2014
|
|
8240
|
+
const hint = provider.templateId === "google-vertex" || provider.id === "google-vertex" || provider.api.npm === "@ai-sdk/google-vertex" ? "Vertex uses gcloud credentials \u2014 use relay-ai server --vertex, or refresh after configuring ADC." : "Manual-only provider \u2014 model list is not refreshed automatically.";
|
|
8237
8241
|
return {
|
|
8238
8242
|
id: provider.id,
|
|
8239
8243
|
name: provider.name,
|
|
@@ -8464,6 +8468,44 @@ async function ensureOpencodeCloudProviders(hasOpencodeKey) {
|
|
|
8464
8468
|
}
|
|
8465
8469
|
return { seeded, refreshed };
|
|
8466
8470
|
}
|
|
8471
|
+
async function addOpencodeCloudFromApiKey(apiKey) {
|
|
8472
|
+
const trimmed = apiKey.trim();
|
|
8473
|
+
if (!trimmed) {
|
|
8474
|
+
return { added: false, error: "API key cannot be empty." };
|
|
8475
|
+
}
|
|
8476
|
+
const saved = await saveToCredentialStore(trimmed);
|
|
8477
|
+
if (!saved) {
|
|
8478
|
+
return {
|
|
8479
|
+
added: false,
|
|
8480
|
+
error: "Could not save API key.",
|
|
8481
|
+
hint: "Ensure RELAY_AI_HOME is writable (Docker uses secrets.json when no OS keyring)."
|
|
8482
|
+
};
|
|
8483
|
+
}
|
|
8484
|
+
process.env["OPENCODE_API_KEY"] = trimmed;
|
|
8485
|
+
const zenStub = addZenRegistryStub();
|
|
8486
|
+
const goStub = addGoRegistryStub();
|
|
8487
|
+
if (!zenStub.added && !goStub.added) {
|
|
8488
|
+
return {
|
|
8489
|
+
added: false,
|
|
8490
|
+
error: "OpenCode Zen / Go is already configured.",
|
|
8491
|
+
hint: "Remove zen or go first, or use Refresh on the provider cards."
|
|
8492
|
+
};
|
|
8493
|
+
}
|
|
8494
|
+
const registry = loadRegistry();
|
|
8495
|
+
const refreshResults = [
|
|
8496
|
+
await refreshProviderModels("zen", trimmed, registry),
|
|
8497
|
+
await refreshProviderModels("go", trimmed, registry)
|
|
8498
|
+
];
|
|
8499
|
+
const modelCount = refreshResults.reduce((total, result) => total + (result.modelCount ?? 0), 0);
|
|
8500
|
+
const failed = refreshResults.filter((result) => !result.ok);
|
|
8501
|
+
return {
|
|
8502
|
+
added: true,
|
|
8503
|
+
modelCount,
|
|
8504
|
+
...failed.length > 0 ? {
|
|
8505
|
+
hint: `Providers added, but ${failed.length} catalog refresh${failed.length === 1 ? "" : "es"} failed \u2014 try Refresh on the provider card.`
|
|
8506
|
+
} : {}
|
|
8507
|
+
};
|
|
8508
|
+
}
|
|
8467
8509
|
function toggleProviderEnabled(id) {
|
|
8468
8510
|
const registry = loadRegistry();
|
|
8469
8511
|
const provider = registry.providers.find((p8) => p8.id === id);
|
|
@@ -9484,7 +9526,7 @@ async function getServerPasswordForQuickMode(mode, passwordOverride) {
|
|
|
9484
9526
|
if (mode === "local") return { password: null, wasSaved: false };
|
|
9485
9527
|
const trimmedOverride = passwordOverride?.trim();
|
|
9486
9528
|
if (trimmedOverride) return { password: trimmedOverride, wasSaved: false };
|
|
9487
|
-
const fromEnv =
|
|
9529
|
+
const fromEnv = getEnvServerPassword();
|
|
9488
9530
|
if (fromEnv) return { password: fromEnv, wasSaved: false };
|
|
9489
9531
|
const savedPassword = await getSavedServerPassword();
|
|
9490
9532
|
if (savedPassword) return { password: savedPassword, wasSaved: true };
|
|
@@ -9637,8 +9679,8 @@ async function resolveServerUpstreamApiKey() {
|
|
|
9637
9679
|
}));
|
|
9638
9680
|
if (apiKey) {
|
|
9639
9681
|
const isMac = process.platform === "darwin";
|
|
9640
|
-
const
|
|
9641
|
-
const storeName = isMac ? "macOS Keychain" :
|
|
9682
|
+
const isWindows2 = process.platform === "win32";
|
|
9683
|
+
const storeName = isMac ? "macOS Keychain" : isWindows2 ? "Windows Credential Manager" : "Secret Service";
|
|
9642
9684
|
p4.log.success(`Found key in ${storeName}`);
|
|
9643
9685
|
return apiKey;
|
|
9644
9686
|
}
|
|
@@ -9961,97 +10003,6 @@ function buildHttpProxyRoutes(providers, favorites, selected, max = MAX_MODEL_CA
|
|
|
9961
10003
|
return { routes, unavailable, unsupported };
|
|
9962
10004
|
}
|
|
9963
10005
|
|
|
9964
|
-
// src/opencode-serve.ts
|
|
9965
|
-
import { execSync as execSync2, spawn as spawn2 } from "child_process";
|
|
9966
|
-
import { existsSync as existsSync11 } from "fs";
|
|
9967
|
-
import { homedir as homedir7 } from "os";
|
|
9968
|
-
import { join as join11 } from "path";
|
|
9969
|
-
var isWindows2 = process.platform === "win32";
|
|
9970
|
-
var OPENCODE_FALLBACK_PATHS = isWindows2 ? [
|
|
9971
|
-
join11(process.env["APPDATA"] ?? homedir7(), "npm", "opencode.cmd"),
|
|
9972
|
-
join11(process.env["APPDATA"] ?? homedir7(), "npm", "opencode"),
|
|
9973
|
-
join11(homedir7(), "AppData", "Roaming", "npm", "opencode.cmd")
|
|
9974
|
-
] : [
|
|
9975
|
-
join11(homedir7(), ".opencode", "bin", "opencode"),
|
|
9976
|
-
join11(homedir7(), ".local", "bin", "opencode"),
|
|
9977
|
-
join11(homedir7(), ".npm", "bin", "opencode"),
|
|
9978
|
-
"/usr/local/bin/opencode",
|
|
9979
|
-
"/opt/homebrew/bin/opencode"
|
|
9980
|
-
];
|
|
9981
|
-
function findOpencodeBinary() {
|
|
9982
|
-
try {
|
|
9983
|
-
const result = execSync2(isWindows2 ? "where.exe opencode" : "which opencode", {
|
|
9984
|
-
encoding: "utf8",
|
|
9985
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
9986
|
-
});
|
|
9987
|
-
const lines = result.trim().split("\n").map((l) => l.trim()).filter(Boolean);
|
|
9988
|
-
const path = (isWindows2 ? lines.find((l) => l.toLowerCase().endsWith(".cmd")) : null) ?? lines[0];
|
|
9989
|
-
if (path) return path;
|
|
9990
|
-
} catch {
|
|
9991
|
-
}
|
|
9992
|
-
for (const path of OPENCODE_FALLBACK_PATHS) {
|
|
9993
|
-
if (existsSync11(path)) return path;
|
|
9994
|
-
}
|
|
9995
|
-
return null;
|
|
9996
|
-
}
|
|
9997
|
-
async function fetchRawOpencodeProviders() {
|
|
9998
|
-
const binary = findOpencodeBinary();
|
|
9999
|
-
if (!binary) return null;
|
|
10000
|
-
return new Promise((resolve) => {
|
|
10001
|
-
let child = null;
|
|
10002
|
-
let settled = false;
|
|
10003
|
-
const TIMEOUT_MS = 1e4;
|
|
10004
|
-
const finish = (value) => {
|
|
10005
|
-
if (settled) return;
|
|
10006
|
-
settled = true;
|
|
10007
|
-
clearTimeout(timer);
|
|
10008
|
-
try {
|
|
10009
|
-
child?.kill();
|
|
10010
|
-
} catch {
|
|
10011
|
-
}
|
|
10012
|
-
resolve(value);
|
|
10013
|
-
};
|
|
10014
|
-
const timer = setTimeout(() => {
|
|
10015
|
-
finish(null);
|
|
10016
|
-
}, TIMEOUT_MS);
|
|
10017
|
-
try {
|
|
10018
|
-
child = isWindows2 ? spawn2("cmd.exe", ["/c", binary, "serve", "--port", "0"], { stdio: ["pipe", "pipe", "pipe"] }) : spawn2(binary, ["serve", "--port", "0"], { stdio: ["pipe", "pipe", "pipe"] });
|
|
10019
|
-
} catch {
|
|
10020
|
-
finish(null);
|
|
10021
|
-
return;
|
|
10022
|
-
}
|
|
10023
|
-
const portRegex = /opencode server listening on http:\/\/127\.0\.0\.1:(\d+)/;
|
|
10024
|
-
let portFound = false;
|
|
10025
|
-
let stdoutBuf = "";
|
|
10026
|
-
const onData = (chunk) => {
|
|
10027
|
-
if (portFound) return;
|
|
10028
|
-
stdoutBuf += chunk.toString();
|
|
10029
|
-
const match = portRegex.exec(stdoutBuf);
|
|
10030
|
-
if (!match) return;
|
|
10031
|
-
portFound = true;
|
|
10032
|
-
const port = match[1];
|
|
10033
|
-
fetch(`http://127.0.0.1:${port}/config/providers`).then((res) => res.json()).then((data) => {
|
|
10034
|
-
const raw = data.providers;
|
|
10035
|
-
if (!Array.isArray(raw)) {
|
|
10036
|
-
finish(null);
|
|
10037
|
-
return;
|
|
10038
|
-
}
|
|
10039
|
-
finish(raw);
|
|
10040
|
-
}).catch(() => {
|
|
10041
|
-
finish(null);
|
|
10042
|
-
});
|
|
10043
|
-
};
|
|
10044
|
-
child.stdout?.on("data", onData);
|
|
10045
|
-
child.stderr?.on("data", onData);
|
|
10046
|
-
child.on("error", () => {
|
|
10047
|
-
finish(null);
|
|
10048
|
-
});
|
|
10049
|
-
child.on("exit", () => {
|
|
10050
|
-
if (!settled) finish(null);
|
|
10051
|
-
});
|
|
10052
|
-
});
|
|
10053
|
-
}
|
|
10054
|
-
|
|
10055
10006
|
// src/registry/add-template.ts
|
|
10056
10007
|
async function probeTemplatePackage(template) {
|
|
10057
10008
|
if (!template.supported) return template.unsupportedReason ?? "Provider is not supported yet.";
|
|
@@ -10075,14 +10026,17 @@ function filterAnonymousFreeModels(models, template) {
|
|
|
10075
10026
|
})));
|
|
10076
10027
|
}
|
|
10077
10028
|
async function addProviderFromTemplate(template, apiKey, opts) {
|
|
10078
|
-
const packageError = await probeTemplatePackage(template);
|
|
10079
|
-
if (packageError) {
|
|
10080
|
-
return { added: false, error: packageError };
|
|
10081
|
-
}
|
|
10082
10029
|
const trimmedKey = apiKey.trim();
|
|
10083
10030
|
if (!trimmedKey && !template.apiKeyOptional) {
|
|
10084
10031
|
return { added: false, error: "API key cannot be empty." };
|
|
10085
10032
|
}
|
|
10033
|
+
if (template.modelSource === "zen-go-api") {
|
|
10034
|
+
return addOpencodeCloudFromApiKey(trimmedKey);
|
|
10035
|
+
}
|
|
10036
|
+
const packageError = await probeTemplatePackage(template);
|
|
10037
|
+
if (packageError) {
|
|
10038
|
+
return { added: false, error: packageError };
|
|
10039
|
+
}
|
|
10086
10040
|
const registry = loadRegistry();
|
|
10087
10041
|
const existing = registry.providers.find((p8) => p8.id === template.id);
|
|
10088
10042
|
if (existing && !opts?.replaceExisting) {
|
|
@@ -10159,80 +10113,6 @@ import pc6 from "picocolors";
|
|
|
10159
10113
|
import * as p5 from "@clack/prompts";
|
|
10160
10114
|
import open3 from "open";
|
|
10161
10115
|
init_provider_templates();
|
|
10162
|
-
|
|
10163
|
-
// src/registry/auth-broker.ts
|
|
10164
|
-
import { spawn as spawn3 } from "child_process";
|
|
10165
|
-
async function runOpencodeAuthBroker(providerId, options = {}) {
|
|
10166
|
-
const binary = findOpencodeBinary();
|
|
10167
|
-
if (!binary) {
|
|
10168
|
-
throw new Error("OpenCode CLI not found. Install from https://opencode.ai or use native device-code auth.");
|
|
10169
|
-
}
|
|
10170
|
-
const args = ["auth", "login", "--provider", providerId];
|
|
10171
|
-
if (options.method) args.push("-m", options.method);
|
|
10172
|
-
const exitCode = await new Promise((resolve, reject) => {
|
|
10173
|
-
const child = spawn3(binary, args, { stdio: "inherit" });
|
|
10174
|
-
child.on("error", reject);
|
|
10175
|
-
child.on("exit", (code) => resolve(code ?? 1));
|
|
10176
|
-
});
|
|
10177
|
-
if (exitCode !== 0) {
|
|
10178
|
-
throw new Error(`OpenCode auth login failed (exit ${exitCode})`);
|
|
10179
|
-
}
|
|
10180
|
-
const authFile = readOpencodeAuthFile();
|
|
10181
|
-
const entry = authFile?.entries[providerId];
|
|
10182
|
-
if (!isOpencodeOAuth(entry)) {
|
|
10183
|
-
throw new Error(`No OAuth token found for "${providerId}" after OpenCode login`);
|
|
10184
|
-
}
|
|
10185
|
-
return entry;
|
|
10186
|
-
}
|
|
10187
|
-
|
|
10188
|
-
// src/registry/convert.ts
|
|
10189
|
-
function modelToCached(model) {
|
|
10190
|
-
return {
|
|
10191
|
-
id: model.id,
|
|
10192
|
-
name: model.name,
|
|
10193
|
-
upstreamModelId: model.upstreamModelId,
|
|
10194
|
-
family: model.family,
|
|
10195
|
-
brand: model.brand,
|
|
10196
|
-
contextWindow: model.contextWindow,
|
|
10197
|
-
cost: model.cost,
|
|
10198
|
-
isFree: model.isFree,
|
|
10199
|
-
freeStatus: model.freeStatus,
|
|
10200
|
-
modelFormat: model.modelFormat,
|
|
10201
|
-
npm: model.npm,
|
|
10202
|
-
apiUrl: model.apiBaseUrl,
|
|
10203
|
-
supportedParameters: model.supportedParameters,
|
|
10204
|
-
reasoning: model.reasoning,
|
|
10205
|
-
interleavedReasoningField: model.interleavedReasoningField,
|
|
10206
|
-
useResponsesLite: model.useResponsesLite,
|
|
10207
|
-
preferWebSockets: model.preferWebSockets
|
|
10208
|
-
};
|
|
10209
|
-
}
|
|
10210
|
-
function localProviderToRegistry(provider, opts) {
|
|
10211
|
-
if (!isValidProviderId(provider.id)) return null;
|
|
10212
|
-
if (provider.models.length === 0) return null;
|
|
10213
|
-
const first = provider.models[0];
|
|
10214
|
-
const apiUrl = (first.apiBaseUrl ?? first.baseUrl)?.trim();
|
|
10215
|
-
const authType = opts?.authType ?? "api";
|
|
10216
|
-
return {
|
|
10217
|
-
id: provider.id,
|
|
10218
|
-
templateId: opts?.templateId ?? provider.id,
|
|
10219
|
-
name: provider.name,
|
|
10220
|
-
enabled: true,
|
|
10221
|
-
authRef: opts?.authRef ?? `keyring:provider:${provider.id}`,
|
|
10222
|
-
authType,
|
|
10223
|
-
api: {
|
|
10224
|
-
npm: first.npm,
|
|
10225
|
-
...apiUrl ? { url: apiUrl } : {}
|
|
10226
|
-
},
|
|
10227
|
-
addedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10228
|
-
modelsCache: {
|
|
10229
|
-
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10230
|
-
models: provider.models.map(modelToCached)
|
|
10231
|
-
}
|
|
10232
|
-
};
|
|
10233
|
-
}
|
|
10234
|
-
|
|
10235
|
-
// src/registry/provider-auth.ts
|
|
10236
10116
|
var OPENAI_DISPLAY = "OpenAI ChatGPT Plus/Pro";
|
|
10237
10117
|
var PROVIDER_DISPLAY = {
|
|
10238
10118
|
xai: "xAI Grok (SuperGrok)",
|
|
@@ -10366,30 +10246,8 @@ async function upsertOAuthProvider(providerId, cred) {
|
|
|
10366
10246
|
const templateId = providerId.replace(/-oauth$/, "") || providerId;
|
|
10367
10247
|
const registry = loadRegistry();
|
|
10368
10248
|
const authRef = oauthAuthRef(registryId);
|
|
10369
|
-
const template = getTemplateById(templateId);
|
|
10249
|
+
const template = getTemplateById(templateId) ?? getTemplateById(registryId);
|
|
10370
10250
|
let entry = registry.providers.find((pr) => pr.id === registryId);
|
|
10371
|
-
if (!entry) {
|
|
10372
|
-
const raw = await fetchRawOpencodeProviders();
|
|
10373
|
-
if (raw) {
|
|
10374
|
-
const { providers } = buildImportProviderList(raw, { [providerId]: cred });
|
|
10375
|
-
const lp = providers.find((pr) => pr.id === registryId || pr.id === providerId);
|
|
10376
|
-
if (lp) {
|
|
10377
|
-
const converted = localProviderToRegistry(lp, { authType: "oauth", authRef });
|
|
10378
|
-
if (converted) {
|
|
10379
|
-
entry = {
|
|
10380
|
-
...converted,
|
|
10381
|
-
id: registryId,
|
|
10382
|
-
templateId,
|
|
10383
|
-
name: oauthDisplayName(registryId, converted.name),
|
|
10384
|
-
api: {
|
|
10385
|
-
...converted.api,
|
|
10386
|
-
...template?.headers ? { headers: { ...template.headers, ...converted.api.headers } } : {}
|
|
10387
|
-
}
|
|
10388
|
-
};
|
|
10389
|
-
}
|
|
10390
|
-
}
|
|
10391
|
-
}
|
|
10392
|
-
}
|
|
10393
10251
|
if (!entry) {
|
|
10394
10252
|
if (!template) {
|
|
10395
10253
|
throw new Error(`Provider "${providerId}" is not in your registry and has no template`);
|
|
@@ -10397,7 +10255,7 @@ async function upsertOAuthProvider(providerId, cred) {
|
|
|
10397
10255
|
const displayName = oauthDisplayName(registryId, template.name);
|
|
10398
10256
|
entry = {
|
|
10399
10257
|
id: registryId,
|
|
10400
|
-
templateId,
|
|
10258
|
+
templateId: template.id,
|
|
10401
10259
|
name: displayName,
|
|
10402
10260
|
enabled: true,
|
|
10403
10261
|
authRef,
|
|
@@ -10410,7 +10268,7 @@ async function upsertOAuthProvider(providerId, cred) {
|
|
|
10410
10268
|
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10411
10269
|
};
|
|
10412
10270
|
} else {
|
|
10413
|
-
entry = { ...entry, authType: "oauth", authRef, templateId };
|
|
10271
|
+
entry = { ...entry, authType: "oauth", authRef, templateId: entry.templateId ?? templateId };
|
|
10414
10272
|
}
|
|
10415
10273
|
const idx = registry.providers.findIndex((pr) => pr.id === registryId);
|
|
10416
10274
|
if (idx >= 0) registry.providers[idx] = entry;
|
|
@@ -10421,50 +10279,16 @@ async function upsertOAuthProvider(providerId, cred) {
|
|
|
10421
10279
|
async function authenticateProvider(providerId, options = {}) {
|
|
10422
10280
|
const registryId = toOAuthRegistryId(providerId);
|
|
10423
10281
|
if (!supportsNativeOAuth(providerId)) {
|
|
10424
|
-
if (findOpencodeBinary()) {
|
|
10425
|
-
const cred2 = await runOpencodeAuthBroker(providerId, { method: options.brokerMethod });
|
|
10426
|
-
let brokerDiagMsg = "";
|
|
10427
|
-
const saved2 = await saveProviderCredential(
|
|
10428
|
-
oauthAuthRef(registryId),
|
|
10429
|
-
oauthCredentialToKeychainJson(cred2),
|
|
10430
|
-
(msg) => {
|
|
10431
|
-
brokerDiagMsg = msg;
|
|
10432
|
-
}
|
|
10433
|
-
);
|
|
10434
|
-
if (!saved2) {
|
|
10435
|
-
p5.log.warn(`Could not save OAuth tokens \u2014 ${brokerDiagMsg || "session may not persist."}`);
|
|
10436
|
-
}
|
|
10437
|
-
const registryProvider2 = await upsertOAuthProvider(providerId, cred2);
|
|
10438
|
-
return { providerId: registryId, credential: cred2, registryProvider: registryProvider2 };
|
|
10439
|
-
}
|
|
10440
10282
|
throw new Error(
|
|
10441
|
-
`
|
|
10283
|
+
`OAuth for "${providerId}" is not built into relay-ai. Add an API-key provider with relay-ai providers add, or run relay-ai providers import if you already configured it in the OpenCode CLI.`
|
|
10442
10284
|
);
|
|
10443
10285
|
}
|
|
10444
|
-
|
|
10445
|
-
|
|
10446
|
-
|
|
10447
|
-
|
|
10448
|
-
}
|
|
10449
|
-
method = "native";
|
|
10450
|
-
}
|
|
10451
|
-
if (!method) {
|
|
10452
|
-
const hasOpencode = findOpencodeBinary() !== null;
|
|
10453
|
-
if (hasOpencode) {
|
|
10454
|
-
const choice = await p5.select({
|
|
10455
|
-
message: "How would you like to sign in?",
|
|
10456
|
-
options: [
|
|
10457
|
-
{ value: "native", label: "Device code (recommended)", hint: "Works on SSH/VPS \u2014 open URL on any device" },
|
|
10458
|
-
{ value: "broker", label: "Via OpenCode", hint: "Uses opencode auth login" }
|
|
10459
|
-
]
|
|
10460
|
-
});
|
|
10461
|
-
if (p5.isCancel(choice)) throw new Error("Cancelled");
|
|
10462
|
-
method = choice;
|
|
10463
|
-
} else {
|
|
10464
|
-
method = "native";
|
|
10465
|
-
}
|
|
10286
|
+
if (options.method === "broker") {
|
|
10287
|
+
throw new Error(
|
|
10288
|
+
"OpenCode auth broker is no longer used for providers. Use the built-in OAuth flow, or relay-ai providers import for OpenCode CLI configs."
|
|
10289
|
+
);
|
|
10466
10290
|
}
|
|
10467
|
-
const cred =
|
|
10291
|
+
const cred = isBrowserRedirectOAuth(providerId) ? await runNativeBrowserOAuth(providerId) : await runNativeDeviceCode(providerId);
|
|
10468
10292
|
let nativeDiagMsg = "";
|
|
10469
10293
|
const saved = await saveProviderCredential(
|
|
10470
10294
|
oauthAuthRef(registryId),
|
|
@@ -10492,25 +10316,23 @@ function providerAuthHelpText() {
|
|
|
10492
10316
|
|
|
10493
10317
|
${pc6.bold("Usage:")}
|
|
10494
10318
|
relay-ai providers auth <id>
|
|
10495
|
-
relay-ai providers auth xai-oauth
|
|
10496
|
-
relay-ai providers auth openai
|
|
10319
|
+
relay-ai providers auth xai-oauth
|
|
10320
|
+
relay-ai providers auth openai-oauth
|
|
10497
10321
|
relay-ai providers auth github-copilot
|
|
10498
10322
|
|
|
10499
|
-
${pc6.bold("Options:")}
|
|
10500
|
-
--native Use built-in OAuth flow
|
|
10501
|
-
--broker Delegate to OpenCode auth login
|
|
10502
|
-
|
|
10503
10323
|
${pc6.bold("Device code (works on SSH/VPS):")}
|
|
10504
10324
|
xai-oauth SuperGrok / X Premium (device code at x.ai/device)
|
|
10505
10325
|
openai-oauth ChatGPT Plus/Pro (device code at auth.openai.com/codex/device)
|
|
10506
|
-
github-copilot GitHub Copilot Free or paid (device code at github.com/login/device)
|
|
10326
|
+
github-copilot GitHub Copilot Free or paid (device code at github.com/login/device)
|
|
10327
|
+
|
|
10328
|
+
${pc6.dim("OpenCode CLI configs: use")} relay-ai providers import${pc6.dim(" (optional one-time migration).")}`;
|
|
10507
10329
|
}
|
|
10508
10330
|
|
|
10509
10331
|
// src/codex/app-launch.ts
|
|
10510
|
-
import { execSync as
|
|
10511
|
-
import { existsSync as
|
|
10512
|
-
import { homedir as
|
|
10513
|
-
import { join as
|
|
10332
|
+
import { execSync as execSync2, spawn as spawn2 } from "child_process";
|
|
10333
|
+
import { existsSync as existsSync11, readdirSync, statSync as statSync3 } from "fs";
|
|
10334
|
+
import { homedir as homedir7 } from "os";
|
|
10335
|
+
import { join as join11 } from "path";
|
|
10514
10336
|
import * as p6 from "@clack/prompts";
|
|
10515
10337
|
var CODEX_BUNDLE_ID = "com.openai.codex";
|
|
10516
10338
|
var DARWIN_APP_NAMES = ["ChatGPT", "Codex"];
|
|
@@ -10521,7 +10343,7 @@ function codexAppSupported() {
|
|
|
10521
10343
|
}
|
|
10522
10344
|
}
|
|
10523
10345
|
function run(cmd, encoding = "utf8") {
|
|
10524
|
-
return
|
|
10346
|
+
return execSync2(cmd, { encoding, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
10525
10347
|
}
|
|
10526
10348
|
function runPowerShell(script) {
|
|
10527
10349
|
return run(`powershell.exe -NoProfile -Command ${JSON.stringify(script)}`);
|
|
@@ -10529,33 +10351,33 @@ function runPowerShell(script) {
|
|
|
10529
10351
|
function darwinAppCandidates() {
|
|
10530
10352
|
return DARWIN_APP_NAMES.flatMap((name) => [
|
|
10531
10353
|
`/Applications/${name}.app`,
|
|
10532
|
-
|
|
10354
|
+
join11(homedir7(), "Applications", `${name}.app`)
|
|
10533
10355
|
]);
|
|
10534
10356
|
}
|
|
10535
10357
|
function winLocalAppData() {
|
|
10536
|
-
return process.env.LOCALAPPDATA ??
|
|
10358
|
+
return process.env.LOCALAPPDATA ?? join11(homedir7(), "AppData", "Local");
|
|
10537
10359
|
}
|
|
10538
10360
|
function winCodexExeCandidates() {
|
|
10539
10361
|
const local = winLocalAppData();
|
|
10540
10362
|
const bases = WIN_APP_NAMES.flatMap((name) => [
|
|
10541
|
-
|
|
10542
|
-
|
|
10543
|
-
|
|
10544
|
-
|
|
10545
|
-
|
|
10363
|
+
join11(local, "Programs", name),
|
|
10364
|
+
join11(local, "Programs", `OpenAI ${name}`),
|
|
10365
|
+
join11(local, name),
|
|
10366
|
+
join11(local, `OpenAI ${name}`),
|
|
10367
|
+
join11(local, "OpenAI", name)
|
|
10546
10368
|
]);
|
|
10547
|
-
bases.push(
|
|
10369
|
+
bases.push(join11(local, "openai-codex-electron"), join11(local, "openai-chatgpt-electron"));
|
|
10548
10370
|
const out = [];
|
|
10549
10371
|
for (const base of bases) {
|
|
10550
10372
|
for (const name of WIN_APP_NAMES) {
|
|
10551
|
-
out.push(
|
|
10373
|
+
out.push(join11(base, `${name}.exe`));
|
|
10552
10374
|
}
|
|
10553
10375
|
try {
|
|
10554
|
-
if (
|
|
10376
|
+
if (existsSync11(base)) {
|
|
10555
10377
|
for (const dir of readdirSync(base)) {
|
|
10556
10378
|
if (dir.startsWith("app-")) {
|
|
10557
10379
|
for (const name of WIN_APP_NAMES) {
|
|
10558
|
-
out.push(
|
|
10380
|
+
out.push(join11(base, dir, `${name}.exe`));
|
|
10559
10381
|
}
|
|
10560
10382
|
}
|
|
10561
10383
|
}
|
|
@@ -10569,7 +10391,7 @@ function mdfindCodexApp() {
|
|
|
10569
10391
|
try {
|
|
10570
10392
|
const out = run(`mdfind "kMDItemCFBundleIdentifier == '${CODEX_BUNDLE_ID}'"`);
|
|
10571
10393
|
const first = out.split("\n").map((l) => l.trim()).find(Boolean);
|
|
10572
|
-
return first &&
|
|
10394
|
+
return first && existsSync11(first) ? first : null;
|
|
10573
10395
|
} catch {
|
|
10574
10396
|
return null;
|
|
10575
10397
|
}
|
|
@@ -10577,14 +10399,14 @@ function mdfindCodexApp() {
|
|
|
10577
10399
|
function findCodexApp() {
|
|
10578
10400
|
if (process.platform === "darwin") {
|
|
10579
10401
|
for (const path of darwinAppCandidates()) {
|
|
10580
|
-
if (
|
|
10402
|
+
if (existsSync11(path)) return path;
|
|
10581
10403
|
}
|
|
10582
10404
|
return mdfindCodexApp();
|
|
10583
10405
|
}
|
|
10584
10406
|
if (process.platform === "win32") {
|
|
10585
10407
|
for (const path of winCodexExeCandidates()) {
|
|
10586
10408
|
try {
|
|
10587
|
-
if (
|
|
10409
|
+
if (existsSync11(path) && statSync3(path).isFile()) return path;
|
|
10588
10410
|
} catch {
|
|
10589
10411
|
}
|
|
10590
10412
|
}
|
|
@@ -10654,15 +10476,15 @@ async function waitForQuit(timeoutMs) {
|
|
|
10654
10476
|
function openCodexAppAt(path) {
|
|
10655
10477
|
if (process.platform === "darwin") {
|
|
10656
10478
|
if (path.endsWith(".app")) {
|
|
10657
|
-
|
|
10479
|
+
execSync2(`open ${JSON.stringify(path)}`, { stdio: "inherit" });
|
|
10658
10480
|
} else {
|
|
10659
|
-
|
|
10481
|
+
execSync2(`open -b ${CODEX_BUNDLE_ID}`, { stdio: "inherit" });
|
|
10660
10482
|
}
|
|
10661
10483
|
return;
|
|
10662
10484
|
}
|
|
10663
10485
|
if (process.platform === "win32") {
|
|
10664
10486
|
if (path.startsWith("shell:AppsFolder\\")) {
|
|
10665
|
-
|
|
10487
|
+
spawn2("cmd.exe", ["/c", "start", "", path], { stdio: "ignore", detached: true }).unref();
|
|
10666
10488
|
} else {
|
|
10667
10489
|
runPowerShell(`Start-Process -FilePath '${path.replace(/'/g, "''")}'`);
|
|
10668
10490
|
}
|
|
@@ -10679,9 +10501,9 @@ function openCodexApp() {
|
|
|
10679
10501
|
}
|
|
10680
10502
|
function darwinQuit() {
|
|
10681
10503
|
try {
|
|
10682
|
-
|
|
10504
|
+
execSync2(`osascript -e 'tell application "Codex" to quit'`, { stdio: "pipe" });
|
|
10683
10505
|
} catch {
|
|
10684
|
-
|
|
10506
|
+
execSync2(`osascript -e 'tell application id "${CODEX_BUNDLE_ID}" to quit'`, { stdio: "pipe" });
|
|
10685
10507
|
}
|
|
10686
10508
|
}
|
|
10687
10509
|
function winQuitGraceful() {
|
|
@@ -10729,10 +10551,10 @@ function codexAppInstallHint() {
|
|
|
10729
10551
|
}
|
|
10730
10552
|
|
|
10731
10553
|
// src/claude-desktop/app-launch.ts
|
|
10732
|
-
import { execSync as
|
|
10733
|
-
import { existsSync as
|
|
10734
|
-
import { homedir as
|
|
10735
|
-
import { join as
|
|
10554
|
+
import { execSync as execSync3, spawn as spawn3 } from "child_process";
|
|
10555
|
+
import { existsSync as existsSync12, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
|
|
10556
|
+
import { homedir as homedir8 } from "os";
|
|
10557
|
+
import { join as join12 } from "path";
|
|
10736
10558
|
import * as p7 from "@clack/prompts";
|
|
10737
10559
|
var CLAUDE_BUNDLE_ID = "com.anthropic.claudefordesktop";
|
|
10738
10560
|
function claudeAppSupported() {
|
|
@@ -10741,7 +10563,7 @@ function claudeAppSupported() {
|
|
|
10741
10563
|
}
|
|
10742
10564
|
}
|
|
10743
10565
|
function run2(cmd, encoding = "utf8") {
|
|
10744
|
-
return
|
|
10566
|
+
return execSync3(cmd, { encoding, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
10745
10567
|
}
|
|
10746
10568
|
function runPowerShell2(script) {
|
|
10747
10569
|
return run2(`powershell.exe -NoProfile -Command ${JSON.stringify(script)}`);
|
|
@@ -10749,26 +10571,26 @@ function runPowerShell2(script) {
|
|
|
10749
10571
|
function darwinAppCandidates2() {
|
|
10750
10572
|
return [
|
|
10751
10573
|
"/Applications/Claude.app",
|
|
10752
|
-
|
|
10574
|
+
join12(homedir8(), "Applications", "Claude.app")
|
|
10753
10575
|
];
|
|
10754
10576
|
}
|
|
10755
10577
|
function winLocalAppData2() {
|
|
10756
|
-
return process.env.LOCALAPPDATA ??
|
|
10578
|
+
return process.env.LOCALAPPDATA ?? join12(homedir8(), "AppData", "Local");
|
|
10757
10579
|
}
|
|
10758
10580
|
function winClaudeExeCandidates() {
|
|
10759
10581
|
const local = winLocalAppData2();
|
|
10760
10582
|
const bases = [
|
|
10761
|
-
|
|
10762
|
-
|
|
10583
|
+
join12(local, "Programs", "Claude"),
|
|
10584
|
+
join12(local, "Claude")
|
|
10763
10585
|
];
|
|
10764
10586
|
const out = [];
|
|
10765
10587
|
for (const base of bases) {
|
|
10766
|
-
out.push(
|
|
10588
|
+
out.push(join12(base, "Claude.exe"));
|
|
10767
10589
|
try {
|
|
10768
|
-
if (
|
|
10590
|
+
if (existsSync12(base)) {
|
|
10769
10591
|
for (const name of readdirSync2(base)) {
|
|
10770
10592
|
if (name.startsWith("app-")) {
|
|
10771
|
-
out.push(
|
|
10593
|
+
out.push(join12(base, name, "Claude.exe"));
|
|
10772
10594
|
}
|
|
10773
10595
|
}
|
|
10774
10596
|
}
|
|
@@ -10781,7 +10603,7 @@ function mdfindClaudeApp() {
|
|
|
10781
10603
|
try {
|
|
10782
10604
|
const out = run2(`mdfind "kMDItemCFBundleIdentifier == '${CLAUDE_BUNDLE_ID}'"`);
|
|
10783
10605
|
const first = out.split("\n").map((l) => l.trim()).find(Boolean);
|
|
10784
|
-
return first &&
|
|
10606
|
+
return first && existsSync12(first) ? first : null;
|
|
10785
10607
|
} catch {
|
|
10786
10608
|
return null;
|
|
10787
10609
|
}
|
|
@@ -10789,14 +10611,14 @@ function mdfindClaudeApp() {
|
|
|
10789
10611
|
function findClaudeApp() {
|
|
10790
10612
|
if (process.platform === "darwin") {
|
|
10791
10613
|
for (const path of darwinAppCandidates2()) {
|
|
10792
|
-
if (
|
|
10614
|
+
if (existsSync12(path)) return path;
|
|
10793
10615
|
}
|
|
10794
10616
|
return mdfindClaudeApp();
|
|
10795
10617
|
}
|
|
10796
10618
|
if (process.platform === "win32") {
|
|
10797
10619
|
for (const path of winClaudeExeCandidates()) {
|
|
10798
10620
|
try {
|
|
10799
|
-
if (
|
|
10621
|
+
if (existsSync12(path) && statSync4(path).isFile()) return path;
|
|
10800
10622
|
} catch {
|
|
10801
10623
|
}
|
|
10802
10624
|
}
|
|
@@ -10860,15 +10682,15 @@ async function waitForQuit2(timeoutMs) {
|
|
|
10860
10682
|
function openClaudeAppAt(path) {
|
|
10861
10683
|
if (process.platform === "darwin") {
|
|
10862
10684
|
if (path.endsWith(".app")) {
|
|
10863
|
-
|
|
10685
|
+
execSync3(`open ${JSON.stringify(path)}`, { stdio: "inherit" });
|
|
10864
10686
|
} else {
|
|
10865
|
-
|
|
10687
|
+
execSync3(`open -b ${CLAUDE_BUNDLE_ID}`, { stdio: "inherit" });
|
|
10866
10688
|
}
|
|
10867
10689
|
return;
|
|
10868
10690
|
}
|
|
10869
10691
|
if (process.platform === "win32") {
|
|
10870
10692
|
if (path.startsWith("shell:AppsFolder\\")) {
|
|
10871
|
-
|
|
10693
|
+
spawn3("cmd.exe", ["/c", "start", "", path], { stdio: "ignore", detached: true }).unref();
|
|
10872
10694
|
} else {
|
|
10873
10695
|
runPowerShell2(`Start-Process -FilePath '${path.replace(/'/g, "''")}'`);
|
|
10874
10696
|
}
|
|
@@ -10885,9 +10707,9 @@ function openClaudeApp() {
|
|
|
10885
10707
|
}
|
|
10886
10708
|
function darwinQuit2() {
|
|
10887
10709
|
try {
|
|
10888
|
-
|
|
10710
|
+
execSync3(`osascript -e 'tell application "Claude" to quit'`, { stdio: "pipe" });
|
|
10889
10711
|
} catch {
|
|
10890
|
-
|
|
10712
|
+
execSync3(`osascript -e 'tell application id "${CLAUDE_BUNDLE_ID}" to quit'`, { stdio: "pipe" });
|
|
10891
10713
|
}
|
|
10892
10714
|
}
|
|
10893
10715
|
function winQuitGraceful2() {
|
|
@@ -10991,6 +10813,7 @@ export {
|
|
|
10991
10813
|
recordLaunchFolder,
|
|
10992
10814
|
recordLaunchSelection,
|
|
10993
10815
|
getSavedServerPassword,
|
|
10816
|
+
getEnvServerPassword,
|
|
10994
10817
|
setSavedServerPassword,
|
|
10995
10818
|
getServerExposedProviders,
|
|
10996
10819
|
setServerExposedProviders,
|
|
@@ -11039,10 +10862,7 @@ export {
|
|
|
11039
10862
|
freeStatusLabel,
|
|
11040
10863
|
refreshModelsDevCacheAsync,
|
|
11041
10864
|
shouldHideModel,
|
|
11042
|
-
findOpencodeBinary,
|
|
11043
|
-
fetchRawOpencodeProviders,
|
|
11044
10865
|
zenRegistryStub,
|
|
11045
|
-
localProviderToRegistry,
|
|
11046
10866
|
isLikelyPlaceholderKey,
|
|
11047
10867
|
resolveRefreshCredential,
|
|
11048
10868
|
oauthAuthRef,
|
|
@@ -11106,9 +10926,8 @@ export {
|
|
|
11106
10926
|
refreshProviderModels,
|
|
11107
10927
|
refreshAllProviderModels,
|
|
11108
10928
|
removeProviderFromRegistry,
|
|
11109
|
-
addZenRegistryStub,
|
|
11110
|
-
addGoRegistryStub,
|
|
11111
10929
|
ensureOpencodeCloudProviders,
|
|
10930
|
+
addOpencodeCloudFromApiKey,
|
|
11112
10931
|
toggleProviderEnabled,
|
|
11113
10932
|
startServer,
|
|
11114
10933
|
filterServerModelsByProviders,
|
|
@@ -11143,4 +10962,4 @@ export {
|
|
|
11143
10962
|
supportsClaudeTransparentMode,
|
|
11144
10963
|
buildHttpProxyRoutes
|
|
11145
10964
|
};
|
|
11146
|
-
//# sourceMappingURL=chunk-
|
|
10965
|
+
//# sourceMappingURL=chunk-P4S42QJK.js.map
|