@jacobbd/relay-ai 0.12.5 → 0.12.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/dist/{chunk-S7HYE2FI.js → chunk-5MEDBKPX.js} +10 -4
- package/dist/{chunk-S7HYE2FI.js.map → chunk-5MEDBKPX.js.map} +1 -1
- package/dist/{chunk-SJEJAHKL.js → chunk-6ULON7FU.js} +2 -2
- package/dist/{chunk-2LUDFIZX.js → chunk-VPDX24ZR.js} +13 -2
- package/dist/chunk-VPDX24ZR.js.map +1 -0
- package/dist/cli.js +130 -25
- package/dist/cli.js.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/{manual-models-WCK2QGLV.js → manual-models-EIKWUZEG.js} +3 -3
- package/dist/{ui-command-7AZ7SLTB.js → ui-command-U3L4LI2K.js} +4 -4
- package/package.json +1 -1
- package/dist/chunk-2LUDFIZX.js.map +0 -1
- /package/dist/{chunk-SJEJAHKL.js.map → chunk-6ULON7FU.js.map} +0 -0
- /package/dist/{manual-models-WCK2QGLV.js.map → manual-models-EIKWUZEG.js.map} +0 -0
- /package/dist/{ui-command-7AZ7SLTB.js.map → ui-command-U3L4LI2K.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
addManualModel,
|
|
4
4
|
removeManualModel
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-6ULON7FU.js";
|
|
6
6
|
import {
|
|
7
7
|
CODEX_APP_AUTO_COMPACT_RATIO,
|
|
8
8
|
CODEX_APP_PROVIDER_ID,
|
|
@@ -148,7 +148,7 @@ import {
|
|
|
148
148
|
waitForCodexAppQuit,
|
|
149
149
|
writeSecureLogLine,
|
|
150
150
|
zenRegistryStub
|
|
151
|
-
} from "./chunk-
|
|
151
|
+
} from "./chunk-5MEDBKPX.js";
|
|
152
152
|
import {
|
|
153
153
|
filterTemplates,
|
|
154
154
|
getTemplateById,
|
|
@@ -222,7 +222,7 @@ import {
|
|
|
222
222
|
thinkingProviderOptions,
|
|
223
223
|
upstreamHttpStatus,
|
|
224
224
|
validateCustomEndpointUrl
|
|
225
|
-
} from "./chunk-
|
|
225
|
+
} from "./chunk-VPDX24ZR.js";
|
|
226
226
|
import "./chunk-JIDIH7DS.js";
|
|
227
227
|
|
|
228
228
|
// src/cli.ts
|
|
@@ -237,6 +237,7 @@ import * as p2 from "@clack/prompts";
|
|
|
237
237
|
|
|
238
238
|
// src/opencode-serve.ts
|
|
239
239
|
import { execSync, spawn } from "child_process";
|
|
240
|
+
import { randomBytes } from "crypto";
|
|
240
241
|
import { existsSync } from "fs";
|
|
241
242
|
import { homedir } from "os";
|
|
242
243
|
import { join } from "path";
|
|
@@ -268,13 +269,99 @@ function findOpencodeBinary() {
|
|
|
268
269
|
}
|
|
269
270
|
return null;
|
|
270
271
|
}
|
|
272
|
+
function parseOpencodeServerBanner(output) {
|
|
273
|
+
const v1 = /opencode server listening on (http:\/\/127\.0\.0\.1:\d+)/.exec(output);
|
|
274
|
+
if (v1?.[1]) return { generation: "v1", url: v1[1] };
|
|
275
|
+
const v2 = /(?:^|\n)\s*server listening on (http:\/\/127\.0\.0\.1:\d+)/.exec(output);
|
|
276
|
+
if (v2?.[1]) return { generation: "v2", url: v2[1] };
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
function v2SdkPackage(packageName, baseUrl) {
|
|
280
|
+
if (packageName.startsWith("aisdk:")) return packageName.slice("aisdk:".length);
|
|
281
|
+
if (!packageName.startsWith("@opencode/ai/providers/")) return packageName;
|
|
282
|
+
const provider = packageName.slice("@opencode/ai/providers/".length);
|
|
283
|
+
const packages = {
|
|
284
|
+
anthropic: "@ai-sdk/anthropic",
|
|
285
|
+
"anthropic-compatible": "@ai-sdk/anthropic",
|
|
286
|
+
openai: "@ai-sdk/openai",
|
|
287
|
+
"openai-compatible": "@ai-sdk/openai-compatible",
|
|
288
|
+
google: "@ai-sdk/google",
|
|
289
|
+
"google-vertex": "@ai-sdk/google-vertex",
|
|
290
|
+
xai: "@ai-sdk/xai",
|
|
291
|
+
azure: "@ai-sdk/azure",
|
|
292
|
+
"amazon-bedrock": "@ai-sdk/amazon-bedrock",
|
|
293
|
+
openrouter: "@openrouter/ai-sdk-provider"
|
|
294
|
+
};
|
|
295
|
+
return packages[provider] ?? (baseUrl ? "@ai-sdk/openai-compatible" : "");
|
|
296
|
+
}
|
|
297
|
+
function normalizeV2Providers(models, integrations, providers, env = process.env) {
|
|
298
|
+
const providerById = /* @__PURE__ */ new Map();
|
|
299
|
+
for (const value of providers) {
|
|
300
|
+
if (!value || typeof value !== "object") continue;
|
|
301
|
+
const provider = value;
|
|
302
|
+
if (provider.id) providerById.set(provider.id, provider);
|
|
303
|
+
}
|
|
304
|
+
const v2Models = models.filter((value) => !!value && typeof value === "object");
|
|
305
|
+
const result = [];
|
|
306
|
+
for (const value of integrations) {
|
|
307
|
+
if (!value || typeof value !== "object") continue;
|
|
308
|
+
const integration = value;
|
|
309
|
+
if (!integration.id || !integration.connections?.length) continue;
|
|
310
|
+
const provider = providerById.get(integration.id);
|
|
311
|
+
const rawModels = {};
|
|
312
|
+
for (const model of v2Models) {
|
|
313
|
+
if (model.providerID !== integration.id || !model.id) continue;
|
|
314
|
+
if (model.enabled === false || model.status === "deprecated") continue;
|
|
315
|
+
const settings = { ...provider?.settings ?? {}, ...model.settings ?? {} };
|
|
316
|
+
const baseUrl = typeof settings["baseURL"] === "string" ? settings["baseURL"] : void 0;
|
|
317
|
+
const packageName = v2SdkPackage(model.package ?? provider?.package ?? "", baseUrl);
|
|
318
|
+
if (!packageName) continue;
|
|
319
|
+
const cost = model.cost?.[0];
|
|
320
|
+
rawModels[model.id] = {
|
|
321
|
+
id: model.id,
|
|
322
|
+
name: model.name,
|
|
323
|
+
family: model.family,
|
|
324
|
+
api: {
|
|
325
|
+
id: model.modelID ?? model.id,
|
|
326
|
+
npm: packageName,
|
|
327
|
+
url: baseUrl
|
|
328
|
+
},
|
|
329
|
+
cost: cost && typeof cost.input === "number" && typeof cost.output === "number" ? {
|
|
330
|
+
input: cost.input,
|
|
331
|
+
output: cost.output,
|
|
332
|
+
cache_read: cost.cache?.read,
|
|
333
|
+
cache_write: cost.cache?.write
|
|
334
|
+
} : void 0,
|
|
335
|
+
limit: model.limit
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
if (Object.keys(rawModels).length === 0) continue;
|
|
339
|
+
const envConnection = integration.connections.find((connection) => connection.type === "env" && connection.name);
|
|
340
|
+
result.push({
|
|
341
|
+
id: integration.id,
|
|
342
|
+
name: integration.name ?? integration.id,
|
|
343
|
+
key: envConnection?.name ? env[envConnection.name] : void 0,
|
|
344
|
+
configured: true,
|
|
345
|
+
models: rawModels
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
return result;
|
|
349
|
+
}
|
|
350
|
+
async function fetchV2Data(url, authHeader) {
|
|
351
|
+
const response = await fetch(url, { headers: { authorization: authHeader } });
|
|
352
|
+
if (!response.ok) return null;
|
|
353
|
+
const body = await response.json();
|
|
354
|
+
return body.data ?? null;
|
|
355
|
+
}
|
|
271
356
|
async function fetchRawOpencodeProviders() {
|
|
272
357
|
const binary = findOpencodeBinary();
|
|
273
358
|
if (!binary) return null;
|
|
274
359
|
return new Promise((resolve2) => {
|
|
275
360
|
let child = null;
|
|
276
361
|
let settled = false;
|
|
277
|
-
const TIMEOUT_MS =
|
|
362
|
+
const TIMEOUT_MS = 15e3;
|
|
363
|
+
const password3 = randomBytes(24).toString("hex");
|
|
364
|
+
const authHeader = `Basic ${Buffer.from(`opencode:${password3}`).toString("base64")}`;
|
|
278
365
|
const finish = (value) => {
|
|
279
366
|
if (settled) return;
|
|
280
367
|
settled = true;
|
|
@@ -289,31 +376,49 @@ async function fetchRawOpencodeProviders() {
|
|
|
289
376
|
finish(null);
|
|
290
377
|
}, TIMEOUT_MS);
|
|
291
378
|
try {
|
|
292
|
-
child = isWindows ? spawn("cmd.exe", ["/c", binary, "serve", "--port", "0"], {
|
|
379
|
+
child = isWindows ? spawn("cmd.exe", ["/c", binary, "serve", "--port", "0"], {
|
|
380
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
381
|
+
env: { ...process.env, OPENCODE_SERVER_PASSWORD: password3 }
|
|
382
|
+
}) : spawn(binary, ["serve", "--port", "0"], {
|
|
383
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
384
|
+
env: { ...process.env, OPENCODE_SERVER_PASSWORD: password3 }
|
|
385
|
+
});
|
|
293
386
|
} catch {
|
|
294
387
|
finish(null);
|
|
295
388
|
return;
|
|
296
389
|
}
|
|
297
|
-
const portRegex = /opencode server listening on http:\/\/127\.0\.0\.1:(\d+)/;
|
|
298
390
|
let portFound = false;
|
|
299
391
|
let stdoutBuf = "";
|
|
300
392
|
const onData = (chunk) => {
|
|
301
393
|
if (portFound) return;
|
|
302
394
|
stdoutBuf += chunk.toString();
|
|
303
|
-
const
|
|
304
|
-
if (!
|
|
395
|
+
const banner = parseOpencodeServerBanner(stdoutBuf);
|
|
396
|
+
if (!banner) return;
|
|
305
397
|
portFound = true;
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
398
|
+
if (banner.generation === "v1") {
|
|
399
|
+
fetch(`${banner.url}/config/providers`, { headers: { authorization: authHeader } }).then((res) => res.json()).then((data) => {
|
|
400
|
+
const raw = data.providers;
|
|
401
|
+
finish(Array.isArray(raw) ? raw : null);
|
|
402
|
+
}).catch(() => finish(null));
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
void (async () => {
|
|
406
|
+
try {
|
|
407
|
+
const integrations = await fetchV2Data(`${banner.url}/api/integration`, authHeader);
|
|
408
|
+
const models = await fetchV2Data(`${banner.url}/api/model`, authHeader);
|
|
409
|
+
if (!Array.isArray(integrations) || !Array.isArray(models)) {
|
|
410
|
+
finish(null);
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
const connectedIds = integrations.filter((value) => !!value && typeof value === "object").filter((integration) => !!integration.id && !!integration.connections?.length).map((integration) => integration.id);
|
|
414
|
+
const providerValues = await Promise.all(connectedIds.map(
|
|
415
|
+
(id) => fetchV2Data(`${banner.url}/api/provider/${encodeURIComponent(id)}`, authHeader)
|
|
416
|
+
));
|
|
417
|
+
finish(normalizeV2Providers(models, integrations, providerValues.filter((value) => value !== null)));
|
|
418
|
+
} catch {
|
|
310
419
|
finish(null);
|
|
311
|
-
return;
|
|
312
420
|
}
|
|
313
|
-
|
|
314
|
-
}).catch(() => {
|
|
315
|
-
finish(null);
|
|
316
|
-
});
|
|
421
|
+
})();
|
|
317
422
|
};
|
|
318
423
|
child.stdout?.on("data", onData);
|
|
319
424
|
child.stderr?.on("data", onData);
|
|
@@ -1415,7 +1520,7 @@ async function runProvidersImport() {
|
|
|
1415
1520
|
);
|
|
1416
1521
|
if (result.skipped.length > 0) {
|
|
1417
1522
|
for (const s of result.skipped) {
|
|
1418
|
-
const reason = s.reason === "user-skipped" ? "skipped by you" : s.reason === "conflict-kept" ? "kept your existing config" : s.reason === "oauth-no-token" ? "OAuth provider in OpenCode but not signed in \u2014 run relay-ai providers auth" : s.reason === "no-api-key" ? "
|
|
1523
|
+
const reason = s.reason === "user-skipped" ? "skipped by you" : s.reason === "conflict-kept" ? "kept your existing config" : s.reason === "oauth-no-token" ? "OAuth provider in OpenCode but not signed in \u2014 run relay-ai providers auth" : s.reason === "no-api-key" ? "API key is not available to import \u2014 use relay-ai providers add" : s.reason === "manual-only" ? "uses gcloud/AWS credentials \u2014 not importable via API key" : s.reason === "placeholder-key" ? "placeholder API key \u2014 provider not imported" : s.reason === "invalid-key" ? "API key failed verification \u2014 provider not imported" : s.reason === "credential-save-failed" ? "could not save credential \u2014 provider not imported" : s.reason;
|
|
1419
1524
|
p6.log.warn(`Skipped ${s.name} (${s.id}): ${reason}`);
|
|
1420
1525
|
}
|
|
1421
1526
|
}
|
|
@@ -3435,7 +3540,7 @@ function responsesRateLimitBody(modelId, message) {
|
|
|
3435
3540
|
}
|
|
3436
3541
|
|
|
3437
3542
|
// src/codex/routing.ts
|
|
3438
|
-
import { randomBytes } from "crypto";
|
|
3543
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
3439
3544
|
function classifyCodexDispatch(modelId, relayRoutes, nativeModelIds) {
|
|
3440
3545
|
if (nativeModelIds.has(modelId)) return { kind: "native", modelId };
|
|
3441
3546
|
const route = relayRoutes.find((candidate) => candidate.modelId === modelId);
|
|
@@ -3451,7 +3556,7 @@ function classifyCodexMixedDispatch(input) {
|
|
|
3451
3556
|
return dispatch;
|
|
3452
3557
|
}
|
|
3453
3558
|
function createMixedProxyCapability() {
|
|
3454
|
-
return
|
|
3559
|
+
return randomBytes2(32).toString("base64url");
|
|
3455
3560
|
}
|
|
3456
3561
|
function mixedProxyBaseUrl(port, capability) {
|
|
3457
3562
|
return `http://127.0.0.1:${port}/_relay-codex/${capability}`;
|
|
@@ -13877,7 +13982,7 @@ function buildHttpProxyChildEnv(baseEnv, proxyUrl, caCertPath) {
|
|
|
13877
13982
|
}
|
|
13878
13983
|
|
|
13879
13984
|
// src/http-proxy/ca.ts
|
|
13880
|
-
import { randomBytes as
|
|
13985
|
+
import { randomBytes as randomBytes3, randomUUID as randomUUID4 } from "crypto";
|
|
13881
13986
|
import {
|
|
13882
13987
|
chmodSync as chmodSync3,
|
|
13883
13988
|
existsSync as existsSync13,
|
|
@@ -13894,7 +13999,7 @@ var SESSION_ROOT = "http-proxy-sessions";
|
|
|
13894
13999
|
var OWNER_FILE = "owner.pid";
|
|
13895
14000
|
var MID_CREATION_GRACE_MS = 3e4;
|
|
13896
14001
|
function serialNumber() {
|
|
13897
|
-
const bytes =
|
|
14002
|
+
const bytes = randomBytes3(16);
|
|
13898
14003
|
bytes[0] &= 127;
|
|
13899
14004
|
return bytes.toString("hex");
|
|
13900
14005
|
}
|
|
@@ -14042,7 +14147,7 @@ ${additionalCa}
|
|
|
14042
14147
|
import * as http2 from "http";
|
|
14043
14148
|
import * as https from "https";
|
|
14044
14149
|
import * as net from "net";
|
|
14045
|
-
import { randomBytes as
|
|
14150
|
+
import { randomBytes as randomBytes4, timingSafeEqual } from "crypto";
|
|
14046
14151
|
import { URL as URL2 } from "url";
|
|
14047
14152
|
var ANTHROPIC_HOST = RELAY_SENTINEL_HOST;
|
|
14048
14153
|
var MAX_BODY_BYTES = 50 * 1024 * 1024;
|
|
@@ -14430,7 +14535,7 @@ async function startHttpProxy(options) {
|
|
|
14430
14535
|
});
|
|
14431
14536
|
mitmServer.on("tlsClientError", () => {
|
|
14432
14537
|
});
|
|
14433
|
-
const password3 =
|
|
14538
|
+
const password3 = randomBytes4(32).toString("base64url");
|
|
14434
14539
|
const expectedAuthorization = `Basic ${Buffer.from(`${PROXY_USERNAME}:${password3}`).toString("base64")}`;
|
|
14435
14540
|
const sockets = /* @__PURE__ */ new Set();
|
|
14436
14541
|
const proxyServer = http2.createServer((req, res) => {
|
|
@@ -16051,7 +16156,7 @@ Options:
|
|
|
16051
16156
|
--trace Write debug logs under ~/.relay-ai/logs/`);
|
|
16052
16157
|
return 0;
|
|
16053
16158
|
}
|
|
16054
|
-
const { runUiCommand } = await import("./ui-command-
|
|
16159
|
+
const { runUiCommand } = await import("./ui-command-U3L4LI2K.js");
|
|
16055
16160
|
return runUiCommand({ trace: parsed.trace, serverMode: parsed.uiServerMode });
|
|
16056
16161
|
}
|
|
16057
16162
|
if (parsed.command === "models") {
|