@jacobbd/relay-ai 0.11.0 → 0.12.0

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.
@@ -50,7 +50,7 @@ import { join as join2 } from "path";
50
50
  // package.json
51
51
  var package_default = {
52
52
  name: "@jacobbd/relay-ai",
53
- version: "0.11.0",
53
+ version: "0.12.0",
54
54
  publishConfig: {
55
55
  access: "public"
56
56
  },
@@ -156,7 +156,7 @@ var package_default = {
156
156
 
157
157
  // src/constants.ts
158
158
  var CODEX_RESPONSES_LITE_WS_URL = "wss://chatgpt.com/backend-api/codex/responses";
159
- var CODEX_RESPONSES_LITE_VERSION = "0.144.1";
159
+ var CODEX_RESPONSES_LITE_VERSION = "0.153.4";
160
160
  var CODEX_RESPONSES_WEBSOCKETS_BETA = "responses_websockets=2026-02-06";
161
161
  var OPENCODE_CACHE_PATH = join2(homedir2(), ".cache", "opencode", "models.json");
162
162
  var CODEX_SUBAGENT_MODEL_CAP = 1;
@@ -1623,6 +1623,51 @@ function isValidProviderId(id) {
1623
1623
  return PROVIDER_ID_PATTERN.test(id);
1624
1624
  }
1625
1625
 
1626
+ // src/registry/provider-models.ts
1627
+ var MANUAL_MODEL_PACKAGES = /* @__PURE__ */ new Set([
1628
+ "@ai-sdk/openai-compatible",
1629
+ "@ai-sdk/openai",
1630
+ "@ai-sdk/anthropic",
1631
+ "@openrouter/ai-sdk-provider"
1632
+ ]);
1633
+ function supportsManualModels(provider) {
1634
+ return provider.authType !== "oauth" && !["zen", "go", "antigravity"].includes(provider.templateId) && MANUAL_MODEL_PACKAGES.has(provider.api.npm ?? "");
1635
+ }
1636
+ function getProviderModels(provider) {
1637
+ const models = new Map((provider.modelsCache?.models ?? []).map((model) => [model.id, model]));
1638
+ if (supportsManualModels(provider)) {
1639
+ for (const model of provider.manualModels ?? []) {
1640
+ models.set(model.id, { ...model, modelFormat: provider.api.npm === "@ai-sdk/anthropic" ? "anthropic" : "openai" });
1641
+ }
1642
+ }
1643
+ return [...models.values()];
1644
+ }
1645
+ function modelIdError(value) {
1646
+ if (typeof value !== "string" || !value.trim()) return "Model ID is required.";
1647
+ if (value.trim().length > 512 || /[\s\u0000-\u001f\u007f]/u.test(value.trim())) {
1648
+ return "Model ID must be at most 512 characters with no whitespace or control characters.";
1649
+ }
1650
+ }
1651
+ function contextWindowError(value) {
1652
+ if (value !== void 0 && (!Number.isSafeInteger(value) || Number(value) <= 0)) {
1653
+ return "Context size must be a positive whole number of tokens, or left blank.";
1654
+ }
1655
+ }
1656
+ function parseManualModel(raw) {
1657
+ if (!raw || typeof raw !== "object") return void 0;
1658
+ const m = raw;
1659
+ if (modelIdError(m.id) || typeof m.name !== "string" || !m.name.trim() || m.name.length > 200 || m.source !== "manual" || typeof m.validatedAt !== "string" || !Number.isFinite(Date.parse(m.validatedAt)) || contextWindowError(m.contextWindow) || m.modelFormat !== "openai" && m.modelFormat !== "anthropic") return void 0;
1660
+ return {
1661
+ id: m.id.trim(),
1662
+ name: m.name.trim(),
1663
+ upstreamModelId: m.id.trim(),
1664
+ modelFormat: m.modelFormat,
1665
+ source: "manual",
1666
+ validatedAt: m.validatedAt,
1667
+ ...m.contextWindow === void 0 ? {} : { contextWindow: m.contextWindow, contextWindowSource: "user" }
1668
+ };
1669
+ }
1670
+
1626
1671
  // src/registry/io.ts
1627
1672
  var DIR_MODE = 448;
1628
1673
  var FILE_MODE = 384;
@@ -1675,6 +1720,12 @@ function parseProvider(raw) {
1675
1720
  provider.authType = p.authType;
1676
1721
  }
1677
1722
  if (typeof p.refreshedAt === "string") provider.refreshedAt = p.refreshedAt;
1723
+ if (Array.isArray(p.manualModels)) {
1724
+ provider.manualModels = p.manualModels.flatMap((raw2) => {
1725
+ const model = parseManualModel(raw2);
1726
+ return model ? [model] : [];
1727
+ });
1728
+ }
1678
1729
  if (p.modelsCache && typeof p.modelsCache === "object") {
1679
1730
  const cache = p.modelsCache;
1680
1731
  if (typeof cache.fetchedAt === "string" && Array.isArray(cache.models)) {
@@ -1757,6 +1808,16 @@ var PROVIDER_TEMPLATES = [
1757
1808
  supported: true,
1758
1809
  subscriptionRisk: true
1759
1810
  },
1811
+ {
1812
+ id: "commandcode",
1813
+ name: "Command Code",
1814
+ authType: "api",
1815
+ npm: "@ai-sdk/openai-compatible",
1816
+ defaultBaseUrl: "https://api.commandcode.ai/provider/v1",
1817
+ signupUrl: "https://commandcode.ai/settings",
1818
+ modelSource: "commandcode",
1819
+ supported: true
1820
+ },
1760
1821
  {
1761
1822
  id: "groq",
1762
1823
  name: "Groq",
@@ -2454,7 +2515,7 @@ function listRelayModels(registryPath) {
2454
2515
  const descriptors = [];
2455
2516
  for (const provider of registry.providers) {
2456
2517
  if (!provider.enabled) continue;
2457
- for (const model of provider.modelsCache?.models ?? []) {
2518
+ for (const model of getProviderModels(provider)) {
2458
2519
  descriptors.push(toDescriptor(provider, model, favorites));
2459
2520
  }
2460
2521
  }
@@ -3824,7 +3885,7 @@ function findRoute(registry, providerId, modelId, routeId) {
3824
3885
  if (!provider.enabled) {
3825
3886
  throw new RelayCoreError("PROVIDER_DISABLED", `Provider "${provider.name}" is disabled \u2014 enable it in relay-ai ui.`, { providerId, routeId });
3826
3887
  }
3827
- const model = provider.modelsCache?.models.find((m) => m.id === modelId);
3888
+ const model = getProviderModels(provider).find((m) => m.id === modelId);
3828
3889
  if (!model) {
3829
3890
  throw new RelayCoreError("UNSUPPORTED_MODEL", `Provider "${provider.name}" has no cached model "${modelId}" \u2014 refresh its models in relay-ai ui.`, { providerId, routeId });
3830
3891
  }