@jacobbd/relay-ai 0.9.2 → 0.9.3
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-Q2FTCICO.js → chunk-NYKVDBQC.js} +2 -2
- package/dist/{chunk-Q2FTCICO.js.map → chunk-NYKVDBQC.js.map} +1 -1
- package/dist/{chunk-GQCFLSEM.js → chunk-PVGAE7HA.js} +506 -185
- package/dist/chunk-PVGAE7HA.js.map +1 -0
- package/dist/cli.js +148 -9
- package/dist/cli.js.map +1 -1
- package/dist/core/index.d.ts +32 -4
- package/dist/core/index.js +617 -109
- package/dist/core/index.js.map +1 -1
- package/dist/{provider-templates-XKNRKAQU.js → provider-templates-CGWE66TD.js} +2 -2
- package/dist/ui/public/app.js +190 -1
- package/dist/{ui-command-SDMYDYT6.js → ui-command-JQPEZAMN.js} +59 -7
- package/dist/ui-command-JQPEZAMN.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-GQCFLSEM.js.map +0 -1
- package/dist/ui-command-SDMYDYT6.js.map +0 -1
- /package/dist/{provider-templates-XKNRKAQU.js.map → provider-templates-CGWE66TD.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
confirmLaunchMessage,
|
|
49
49
|
createGatewayModelCatalog,
|
|
50
50
|
createLanguageModel,
|
|
51
|
+
customEndpointKind,
|
|
51
52
|
deepMergeProviderOptions,
|
|
52
53
|
detectConflicts,
|
|
53
54
|
effectiveProviderBaseUrl,
|
|
@@ -193,11 +194,12 @@ import {
|
|
|
193
194
|
thinkingProviderOptions,
|
|
194
195
|
toggleProviderEnabled,
|
|
195
196
|
translateRequest,
|
|
197
|
+
updateCustomEndpointProvider,
|
|
196
198
|
upstreamHttpStatus,
|
|
197
199
|
validateCustomEndpointUrl,
|
|
198
200
|
writeSecureLogLine,
|
|
199
201
|
zenRegistryStub
|
|
200
|
-
} from "./chunk-
|
|
202
|
+
} from "./chunk-PVGAE7HA.js";
|
|
201
203
|
import {
|
|
202
204
|
filterTemplates,
|
|
203
205
|
getTemplateById,
|
|
@@ -205,7 +207,7 @@ import {
|
|
|
205
207
|
listAddableTemplates,
|
|
206
208
|
listSupportedTemplates,
|
|
207
209
|
listVisibleOAuthTemplates
|
|
208
|
-
} from "./chunk-
|
|
210
|
+
} from "./chunk-NYKVDBQC.js";
|
|
209
211
|
|
|
210
212
|
// src/cli.ts
|
|
211
213
|
import pc12 from "picocolors";
|
|
@@ -1748,17 +1750,31 @@ async function runCustomEndpointAddFlow() {
|
|
|
1748
1750
|
if (name) headers[name] = value;
|
|
1749
1751
|
}
|
|
1750
1752
|
}
|
|
1751
|
-
const
|
|
1752
|
-
spinner9.start("Testing connection...");
|
|
1753
|
-
const result = await addCustomEndpointProvider({
|
|
1753
|
+
const addInput = {
|
|
1754
1754
|
displayName: String(displayName).trim(),
|
|
1755
1755
|
baseUrl: String(baseUrl).trim(),
|
|
1756
1756
|
apiKey: String(apiKey ?? "").trim(),
|
|
1757
1757
|
kind: kindChoice,
|
|
1758
1758
|
allowInsecureLocal: allowInsecureHttp,
|
|
1759
1759
|
headers: Object.keys(headers).length > 0 ? headers : void 0
|
|
1760
|
-
}
|
|
1760
|
+
};
|
|
1761
|
+
const spinner9 = p5.spinner();
|
|
1762
|
+
spinner9.start("Testing connection...");
|
|
1763
|
+
let result = await addCustomEndpointProvider(addInput);
|
|
1761
1764
|
spinner9.stop("");
|
|
1765
|
+
if (!result.added && result.duplicateOf) {
|
|
1766
|
+
const addAnyway = await p5.confirm({
|
|
1767
|
+
message: `You already have a backend with the same URL, key and headers (${result.duplicateOf}). Add another?`,
|
|
1768
|
+
initialValue: false
|
|
1769
|
+
});
|
|
1770
|
+
if (p5.isCancel(addAnyway) || !addAnyway) {
|
|
1771
|
+
p5.cancel("Cancelled.");
|
|
1772
|
+
return 0;
|
|
1773
|
+
}
|
|
1774
|
+
spinner9.start("Testing connection...");
|
|
1775
|
+
result = await addCustomEndpointProvider({ ...addInput, confirmDuplicate: true });
|
|
1776
|
+
spinner9.stop("");
|
|
1777
|
+
}
|
|
1762
1778
|
if (!result.added) {
|
|
1763
1779
|
showProviderAddFailure(result.error, result.hint, "Could not add custom provider.");
|
|
1764
1780
|
return 1;
|
|
@@ -1766,6 +1782,118 @@ async function runCustomEndpointAddFlow() {
|
|
|
1766
1782
|
logConnected(result.provider?.name ?? "Provider", result.modelCount ?? 0);
|
|
1767
1783
|
return 0;
|
|
1768
1784
|
}
|
|
1785
|
+
async function runCustomEndpointEditFlow(provider) {
|
|
1786
|
+
const currentHeaders = provider.api.headers ?? {};
|
|
1787
|
+
const headerSummary = Object.keys(currentHeaders).length > 0 ? Object.entries(currentHeaders).map(([k, v]) => `${k}: ${v}`).join(", ") : "none";
|
|
1788
|
+
p5.log.info(`Base URL: ${provider.api.url ?? "(none)"}
|
|
1789
|
+
Headers: ${headerSummary}`);
|
|
1790
|
+
const displayName = await p5.text({
|
|
1791
|
+
message: "Display name:",
|
|
1792
|
+
initialValue: provider.name,
|
|
1793
|
+
validate: (v) => v.trim() ? void 0 : "Name is required"
|
|
1794
|
+
});
|
|
1795
|
+
if (p5.isCancel(displayName)) {
|
|
1796
|
+
p5.cancel("Cancelled.");
|
|
1797
|
+
return 0;
|
|
1798
|
+
}
|
|
1799
|
+
const baseUrl = await p5.text({
|
|
1800
|
+
message: "Base URL:",
|
|
1801
|
+
initialValue: provider.api.url ?? "",
|
|
1802
|
+
validate: (v) => v.trim() ? void 0 : "URL is required"
|
|
1803
|
+
});
|
|
1804
|
+
if (p5.isCancel(baseUrl)) {
|
|
1805
|
+
p5.cancel("Cancelled.");
|
|
1806
|
+
return 0;
|
|
1807
|
+
}
|
|
1808
|
+
const usesHttp = /^http:\/\//i.test(String(baseUrl).trim());
|
|
1809
|
+
let allowInsecureHttp = false;
|
|
1810
|
+
if (usesHttp) {
|
|
1811
|
+
p5.log.warn("HTTP is not encrypted. Only use it for a trusted local or LAN server, like Ollama on your own network.");
|
|
1812
|
+
const allowLocal = await p5.confirm({
|
|
1813
|
+
message: "Allow insecure HTTP for this local/LAN server?",
|
|
1814
|
+
initialValue: true
|
|
1815
|
+
});
|
|
1816
|
+
if (p5.isCancel(allowLocal)) return 0;
|
|
1817
|
+
allowInsecureHttp = allowLocal === true;
|
|
1818
|
+
}
|
|
1819
|
+
const apiKey = await p5.password({
|
|
1820
|
+
message: "API key (leave empty to keep the current key):"
|
|
1821
|
+
});
|
|
1822
|
+
if (p5.isCancel(apiKey)) {
|
|
1823
|
+
p5.cancel("Cancelled.");
|
|
1824
|
+
return 0;
|
|
1825
|
+
}
|
|
1826
|
+
const editHeaders = await p5.confirm({
|
|
1827
|
+
message: `Replace custom headers? (current: ${headerSummary})`,
|
|
1828
|
+
initialValue: false
|
|
1829
|
+
});
|
|
1830
|
+
if (p5.isCancel(editHeaders)) {
|
|
1831
|
+
p5.cancel("Cancelled.");
|
|
1832
|
+
return 0;
|
|
1833
|
+
}
|
|
1834
|
+
let headers;
|
|
1835
|
+
if (editHeaders) {
|
|
1836
|
+
headers = {};
|
|
1837
|
+
p5.log.info("Enter the full header set. Leave the first one empty to remove all headers.");
|
|
1838
|
+
for (; ; ) {
|
|
1839
|
+
const headerLine = await p5.text({
|
|
1840
|
+
message: "Header (leave empty when done):",
|
|
1841
|
+
placeholder: "X-Plan: coding"
|
|
1842
|
+
});
|
|
1843
|
+
if (p5.isCancel(headerLine)) {
|
|
1844
|
+
p5.cancel("Cancelled.");
|
|
1845
|
+
return 0;
|
|
1846
|
+
}
|
|
1847
|
+
const trimmed = String(headerLine).trim();
|
|
1848
|
+
if (!trimmed) break;
|
|
1849
|
+
const idx = trimmed.indexOf(":");
|
|
1850
|
+
if (idx < 1) {
|
|
1851
|
+
p5.log.warn('Use the format "Name: Value" \u2014 skipped.');
|
|
1852
|
+
continue;
|
|
1853
|
+
}
|
|
1854
|
+
const name = trimmed.slice(0, idx).trim();
|
|
1855
|
+
const value = trimmed.slice(idx + 1).trim();
|
|
1856
|
+
if (name) headers[name] = value;
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
const runUpdate = (saveAnyway) => updateCustomEndpointProvider({
|
|
1860
|
+
providerId: provider.id,
|
|
1861
|
+
displayName: String(displayName).trim(),
|
|
1862
|
+
baseUrl: String(baseUrl).trim(),
|
|
1863
|
+
apiKey: String(apiKey ?? "").trim(),
|
|
1864
|
+
headers,
|
|
1865
|
+
allowInsecureLocal: allowInsecureHttp,
|
|
1866
|
+
saveAnyway
|
|
1867
|
+
});
|
|
1868
|
+
const spinner9 = p5.spinner();
|
|
1869
|
+
spinner9.start("Testing connection...");
|
|
1870
|
+
let result = await runUpdate(false);
|
|
1871
|
+
spinner9.stop("");
|
|
1872
|
+
if (!result.updated && result.error === "Nothing to change.") {
|
|
1873
|
+
p5.log.info("No changes made.");
|
|
1874
|
+
return 0;
|
|
1875
|
+
}
|
|
1876
|
+
if (!result.updated) {
|
|
1877
|
+
showProviderAddFailure(result.error, result.hint, "Could not update backend.");
|
|
1878
|
+
if (!result.canSaveAnyway) return 1;
|
|
1879
|
+
const saveAnyway = await p5.confirm({
|
|
1880
|
+
message: "Save these settings anyway? The model list will not be refreshed.",
|
|
1881
|
+
initialValue: false
|
|
1882
|
+
});
|
|
1883
|
+
if (p5.isCancel(saveAnyway) || !saveAnyway) return 1;
|
|
1884
|
+
result = await runUpdate(true);
|
|
1885
|
+
if (!result.updated) {
|
|
1886
|
+
showProviderAddFailure(result.error, result.hint, "Could not update backend.");
|
|
1887
|
+
return 1;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
if (result.modelsStale) {
|
|
1891
|
+
p5.log.warn(`${result.provider?.name ?? provider.name} saved, but the model list may be out of date.`);
|
|
1892
|
+
} else {
|
|
1893
|
+
logConnected(result.provider?.name ?? provider.name, result.modelCount ?? 0);
|
|
1894
|
+
}
|
|
1895
|
+
return 0;
|
|
1896
|
+
}
|
|
1769
1897
|
async function runProvidersAdd() {
|
|
1770
1898
|
const registry = loadRegistry();
|
|
1771
1899
|
const hasOpencode = findOpencodeBinary() !== null;
|
|
@@ -1891,7 +2019,14 @@ async function runProviderDetail(id) {
|
|
|
1891
2019
|
hint: "Refresh OAuth tokens or switch accounts"
|
|
1892
2020
|
});
|
|
1893
2021
|
}
|
|
1894
|
-
|
|
2022
|
+
const editableCustomKind = customEndpointKind(provider);
|
|
2023
|
+
if (editableCustomKind) {
|
|
2024
|
+
detailOptions.push({
|
|
2025
|
+
value: "edit-custom",
|
|
2026
|
+
label: "Edit backend settings",
|
|
2027
|
+
hint: "Name, base URL, API key, headers"
|
|
2028
|
+
});
|
|
2029
|
+
} else if (provider.authType !== "oauth" && provider.authRef.startsWith("keyring:")) {
|
|
1895
2030
|
detailOptions.push({
|
|
1896
2031
|
value: "change-key",
|
|
1897
2032
|
label: "Change API key",
|
|
@@ -1927,6 +2062,9 @@ async function runProviderDetail(id) {
|
|
|
1927
2062
|
if (action === "refresh") {
|
|
1928
2063
|
return await runProvidersRefreshModels(id) === 0 ? "back" : "failed";
|
|
1929
2064
|
}
|
|
2065
|
+
if (action === "edit-custom") {
|
|
2066
|
+
return await runCustomEndpointEditFlow(provider) === 0 ? "back" : "failed";
|
|
2067
|
+
}
|
|
1930
2068
|
if (action === "change-key") {
|
|
1931
2069
|
return await runProviderApiKeyChange(provider, registry) === 0 ? "back" : "failed";
|
|
1932
2070
|
}
|
|
@@ -10488,7 +10626,8 @@ function mergeAppConfig(existing, spec) {
|
|
|
10488
10626
|
apiBaseUrl: spec.route.baseURL,
|
|
10489
10627
|
supportedParameters: spec.route.supportedParameters,
|
|
10490
10628
|
reasoning: spec.route.reasoning,
|
|
10491
|
-
interleavedReasoningField: spec.route.interleavedReasoningField
|
|
10629
|
+
interleavedReasoningField: spec.route.interleavedReasoningField,
|
|
10630
|
+
upstreamModelId: spec.route.upstreamModelId
|
|
10492
10631
|
});
|
|
10493
10632
|
if (caps.levels.length === 0 || !caps.levels.includes(existingEffort)) {
|
|
10494
10633
|
if (caps.levels.length > 0 && caps.defaultLevel) {
|
|
@@ -14869,7 +15008,7 @@ Options:
|
|
|
14869
15008
|
--trace Write debug logs under ~/.relay-ai/logs/`);
|
|
14870
15009
|
return 0;
|
|
14871
15010
|
}
|
|
14872
|
-
const { runUiCommand } = await import("./ui-command-
|
|
15011
|
+
const { runUiCommand } = await import("./ui-command-JQPEZAMN.js");
|
|
14873
15012
|
return runUiCommand({ trace: parsed.trace, serverMode: parsed.uiServerMode });
|
|
14874
15013
|
}
|
|
14875
15014
|
if (parsed.command === "models") {
|