@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/dist/ui/public/app.js
CHANGED
|
@@ -34,6 +34,7 @@ const state = {
|
|
|
34
34
|
appModelFilters: {},
|
|
35
35
|
appModelOpen: null,
|
|
36
36
|
appSelections: {},
|
|
37
|
+
appHttpProxy: {},
|
|
37
38
|
server: {
|
|
38
39
|
status: null,
|
|
39
40
|
error: null,
|
|
@@ -302,6 +303,7 @@ async function loadModels() {
|
|
|
302
303
|
freeStatus: m.freeStatus,
|
|
303
304
|
freeLabel: m.freeLabel,
|
|
304
305
|
cost: m.cost,
|
|
306
|
+
claudeTransparentCompatible: Boolean(m.claudeTransparentCompatible),
|
|
305
307
|
});
|
|
306
308
|
if (typeof p.modelCount === 'number') p._rawCount = p.modelCount;
|
|
307
309
|
}
|
|
@@ -1798,6 +1800,16 @@ function appModelInputValue(appId) {
|
|
|
1798
1800
|
return getAppSelection(appId).label;
|
|
1799
1801
|
}
|
|
1800
1802
|
|
|
1803
|
+
function claudeHttpProxyAvailable(appId) {
|
|
1804
|
+
if (appId !== 'claude') return false;
|
|
1805
|
+
const selection = getAppSelection(appId);
|
|
1806
|
+
if (selection.mode !== 'model') return true;
|
|
1807
|
+
const model = state.allModels.find(
|
|
1808
|
+
candidate => candidate.providerId === selection.providerId && candidate.id === selection.modelId,
|
|
1809
|
+
);
|
|
1810
|
+
return Boolean(model?.claudeTransparentCompatible);
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1801
1813
|
function matchedAppModels(appId) {
|
|
1802
1814
|
const localFilter = state.appModelFilters[appId] ?? '';
|
|
1803
1815
|
const q = (localFilter || state.appModelFilter).trim().toLowerCase();
|
|
@@ -1898,6 +1910,7 @@ function renderApps() {
|
|
|
1898
1910
|
const modelInputValue = appModelInputValue(app.id);
|
|
1899
1911
|
const modelResults = state.appModelOpen === app.id ? buildAppModelResults(app.id) : '';
|
|
1900
1912
|
const launchFolder = state.appLaunchFolders[app.id] ?? '';
|
|
1913
|
+
const httpProxyAvailable = claudeHttpProxyAvailable(app.id);
|
|
1901
1914
|
const recentFolders = state.recentLaunchFolders
|
|
1902
1915
|
.map(folder => `<button class="launch-folder-chip" type="button" onclick="selectLaunchFolder('${app.id}', '${encodeURIComponent(folder)}')">${escapeHtml(folder)}</button>`)
|
|
1903
1916
|
.join('');
|
|
@@ -1931,6 +1944,16 @@ function renderApps() {
|
|
|
1931
1944
|
` : ''}
|
|
1932
1945
|
</div>
|
|
1933
1946
|
</div>
|
|
1947
|
+
${app.id === 'claude' ? `
|
|
1948
|
+
<label class="claude-proxy-option${httpProxyAvailable ? '' : ' is-disabled'}">
|
|
1949
|
+
<input type="checkbox" ${state.appHttpProxy[app.id] && httpProxyAvailable ? 'checked' : ''} ${httpProxyAvailable ? '' : 'disabled'} onchange="setClaudeHttpProxy('${app.id}', this.checked)">
|
|
1950
|
+
<span class="claude-proxy-label">
|
|
1951
|
+
Keep my Anthropic login and add Relay models
|
|
1952
|
+
<span class="claude-proxy-tooltip" tabindex="0" role="img" aria-label="Launches Claude Code through a temporary local connection. Your normal Anthropic login and models continue to work, while compatible Relay AI favorites become available for model switching. The connection closes automatically when Claude Code exits." data-tooltip="Launches Claude Code through a temporary local connection. Your normal Anthropic login and models continue to work, while compatible Relay AI favorites become available for model switching. The connection closes automatically when Claude Code exits.">?</span>
|
|
1953
|
+
${httpProxyAvailable ? '' : '<span class="claude-proxy-unavailable">This selected model cannot be combined with your Anthropic login.</span>'}
|
|
1954
|
+
</span>
|
|
1955
|
+
</label>
|
|
1956
|
+
` : ''}
|
|
1934
1957
|
${app.type !== 'app' ? `
|
|
1935
1958
|
<div class="launch-folder-control">
|
|
1936
1959
|
<label style="font-size: 12px; font-weight: 500; color: var(--color-muted);">Launch folder 📁</label>
|
|
@@ -2013,6 +2036,9 @@ async function launchApp(appId) {
|
|
|
2013
2036
|
body.providerId = selection.providerId;
|
|
2014
2037
|
body.modelId = selection.modelId;
|
|
2015
2038
|
}
|
|
2039
|
+
if (appId === 'claude' && state.appHttpProxy[appId] && claudeHttpProxyAvailable(appId)) {
|
|
2040
|
+
body.httpProxy = true;
|
|
2041
|
+
}
|
|
2016
2042
|
|
|
2017
2043
|
const folder = (state.appLaunchFolders[appId] ?? '').trim();
|
|
2018
2044
|
if (folder) {
|
|
@@ -2117,6 +2143,9 @@ function selectLaunchModel(appId, encodedProviderId, encodedModelId) {
|
|
|
2117
2143
|
const favorite = isGeneralFavorite(providerId, modelId);
|
|
2118
2144
|
const label = `${favorite ? '★ ' : ''}${model?.name || modelId}`;
|
|
2119
2145
|
state.appSelections[appId] = { mode: 'model', providerId, modelId, label };
|
|
2146
|
+
if (appId === 'claude' && !model?.claudeTransparentCompatible) {
|
|
2147
|
+
state.appHttpProxy[appId] = false;
|
|
2148
|
+
}
|
|
2120
2149
|
state.appModelOpen = null;
|
|
2121
2150
|
state.appModelFilters[appId] = '';
|
|
2122
2151
|
renderApps();
|
|
@@ -2126,6 +2155,10 @@ function setLaunchFolder(appId, value) {
|
|
|
2126
2155
|
state.appLaunchFolders[appId] = value;
|
|
2127
2156
|
}
|
|
2128
2157
|
|
|
2158
|
+
function setClaudeHttpProxy(appId, checked) {
|
|
2159
|
+
state.appHttpProxy[appId] = claudeHttpProxyAvailable(appId) && Boolean(checked);
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2129
2162
|
function selectLaunchFolder(appId, encodedFolder) {
|
|
2130
2163
|
state.appLaunchFolders[appId] = decodeURIComponent(encodedFolder);
|
|
2131
2164
|
renderApps();
|
|
@@ -2182,6 +2215,7 @@ window.selectLaunchDefault = selectLaunchDefault;
|
|
|
2182
2215
|
window.selectLaunchFavorites = selectLaunchFavorites;
|
|
2183
2216
|
window.selectLaunchModel = selectLaunchModel;
|
|
2184
2217
|
window.setLaunchFolder = setLaunchFolder;
|
|
2218
|
+
window.setClaudeHttpProxy = setClaudeHttpProxy;
|
|
2185
2219
|
window.selectLaunchFolder = selectLaunchFolder;
|
|
2186
2220
|
window.browseLaunchFolder = browseLaunchFolder;
|
|
2187
2221
|
window.saveAppPath = saveAppPath;
|
|
@@ -2298,10 +2332,14 @@ function renderServerProviderPicker() {
|
|
|
2298
2332
|
const label = p ? p.name : id;
|
|
2299
2333
|
return `<span class="server-provider-chip">${escapeHtml(label)}<button type="button" onclick="toggleServerProvider('${id}')" aria-label="Remove ${escapeHtml(label)}">×</button></span>`;
|
|
2300
2334
|
}).join('');
|
|
2335
|
+
const allFreeNote = s.form.freeModelsOnly && s.form.exposedProviders.length === 0
|
|
2336
|
+
? '<div class="server-field-hint">No providers selected — exposing free models from every available provider.</div>'
|
|
2337
|
+
: '';
|
|
2301
2338
|
|
|
2302
2339
|
return `
|
|
2303
2340
|
<div class="server-providers-picker">
|
|
2304
2341
|
${chips ? `<div class="server-provider-chips">${chips}</div>` : ''}
|
|
2342
|
+
${allFreeNote}
|
|
2305
2343
|
<div class="search-field">
|
|
2306
2344
|
<svg class="search-icon" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
|
|
2307
2345
|
<input type="search" class="search-input" placeholder="Search providers to expose…" value="${escapeHtml(s.form.providerSearch)}" oninput="setServerProviderSearch(this.value)">
|
|
@@ -2343,13 +2381,27 @@ function renderServerNetworkOptions() {
|
|
|
2343
2381
|
function renderServerSetup(s) {
|
|
2344
2382
|
const f = s.form;
|
|
2345
2383
|
const specific = f.expose === 'specific';
|
|
2346
|
-
const
|
|
2384
|
+
const hasProviders = f.exposedProviders.length > 0;
|
|
2385
|
+
// Free-only with no providers selected = expose free models from every provider
|
|
2386
|
+
// (backend: exposedProviders null + freeModelsOnly). Otherwise require a selection.
|
|
2387
|
+
const providersOk = f.expose === 'favorites'
|
|
2388
|
+
|| (specific && hasProviders)
|
|
2389
|
+
|| (specific && f.freeModelsOnly);
|
|
2347
2390
|
const passwordOk = f.listenMode !== 'network' || f.passwordMode === 'saved' || f.password.trim().length > 0;
|
|
2348
2391
|
const canStart = providersOk && passwordOk && !s.starting;
|
|
2349
2392
|
|
|
2350
2393
|
let hint = '';
|
|
2351
|
-
if (!providersOk)
|
|
2352
|
-
|
|
2394
|
+
if (!providersOk) {
|
|
2395
|
+
hint = f.freeModelsOnly
|
|
2396
|
+
? 'Select at least one provider, or leave none selected to expose free models from all providers.'
|
|
2397
|
+
: 'Select at least one provider to expose.';
|
|
2398
|
+
} else if (!passwordOk) {
|
|
2399
|
+
hint = 'Enter a server password for network mode.';
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2402
|
+
const freeModelsHint = specific
|
|
2403
|
+
? 'With providers selected, only their free models are exposed. With none selected, free models from every available provider are exposed.'
|
|
2404
|
+
: 'Includes verified $0 models and free developer access.';
|
|
2353
2405
|
|
|
2354
2406
|
return `
|
|
2355
2407
|
<div class="server-setup">
|
|
@@ -2371,6 +2423,7 @@ function renderServerSetup(s) {
|
|
|
2371
2423
|
<input type="checkbox" ${f.freeModelsOnly ? 'checked' : ''} onchange="setServerFreeModelsOnly(this.checked)">
|
|
2372
2424
|
<span>Free models only <span class="server-field-hint">(includes verified $0 models and free developer access)</span></span>
|
|
2373
2425
|
</label>
|
|
2426
|
+
<div class="server-field-hint">${escapeHtml(freeModelsHint)}</div>
|
|
2374
2427
|
</div>
|
|
2375
2428
|
|
|
2376
2429
|
<div class="server-field">
|
|
@@ -2441,8 +2494,8 @@ function renderServerRunning(status) {
|
|
|
2441
2494
|
<tr>
|
|
2442
2495
|
<td>${escapeHtml(m.providerLabel)}</td>
|
|
2443
2496
|
<td>${escapeHtml(m.name)}</td>
|
|
2444
|
-
<td
|
|
2445
|
-
<td
|
|
2497
|
+
<td>${serverIdCell(m.anthropicId)}</td>
|
|
2498
|
+
<td>${serverIdCell(m.openaiId)}</td>
|
|
2446
2499
|
</tr>
|
|
2447
2500
|
`).join('');
|
|
2448
2501
|
|
|
@@ -2497,6 +2550,8 @@ function setServerMask(checked) {
|
|
|
2497
2550
|
|
|
2498
2551
|
function setServerFreeModelsOnly(checked) {
|
|
2499
2552
|
state.server.form.freeModelsOnly = checked;
|
|
2553
|
+
state.server.error = null;
|
|
2554
|
+
renderServerPanel();
|
|
2500
2555
|
}
|
|
2501
2556
|
|
|
2502
2557
|
function setServerListenMode(mode) {
|
|
@@ -2527,7 +2582,10 @@ async function submitServerStart() {
|
|
|
2527
2582
|
const body = {
|
|
2528
2583
|
favoritesOnly: f.expose === 'favorites',
|
|
2529
2584
|
freeModelsOnly: f.freeModelsOnly,
|
|
2530
|
-
|
|
2585
|
+
// Empty specific selection + free-only → null (all providers, free filter applied server-side).
|
|
2586
|
+
exposedProviders: f.expose === 'specific' && f.exposedProviders.length > 0
|
|
2587
|
+
? f.exposedProviders
|
|
2588
|
+
: null,
|
|
2531
2589
|
maskGatewayIds: f.maskGatewayIds,
|
|
2532
2590
|
listenMode: f.listenMode,
|
|
2533
2591
|
passwordMode: f.passwordMode,
|
|
@@ -2565,10 +2623,18 @@ async function stopServerGateway() {
|
|
|
2565
2623
|
}
|
|
2566
2624
|
}
|
|
2567
2625
|
|
|
2568
|
-
|
|
2569
|
-
const
|
|
2570
|
-
if (!
|
|
2571
|
-
|
|
2626
|
+
function serverIdCell(id) {
|
|
2627
|
+
const value = String(id ?? '');
|
|
2628
|
+
if (!value) return '<span class="server-id-empty">—</span>';
|
|
2629
|
+
return `
|
|
2630
|
+
<div class="server-id-cell">
|
|
2631
|
+
<code title="${escapeHtml(value)}">${escapeHtml(value)}</code>
|
|
2632
|
+
<button type="button" class="btn btn-ghost btn-sm server-id-copy" onclick='copyPlainText(${JSON.stringify(value)})'>Copy</button>
|
|
2633
|
+
</div>
|
|
2634
|
+
`;
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2637
|
+
async function copyPlainText(text) {
|
|
2572
2638
|
try {
|
|
2573
2639
|
await navigator.clipboard.writeText(text);
|
|
2574
2640
|
showToast('Copied to clipboard');
|
|
@@ -2577,6 +2643,12 @@ async function copyServerValue(id) {
|
|
|
2577
2643
|
}
|
|
2578
2644
|
}
|
|
2579
2645
|
|
|
2646
|
+
async function copyServerValue(id) {
|
|
2647
|
+
const el = document.getElementById(id);
|
|
2648
|
+
if (!el) return;
|
|
2649
|
+
await copyPlainText(el.dataset.value ?? el.textContent);
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2580
2652
|
function toggleServerApiKeyVisibility() {
|
|
2581
2653
|
const el = document.getElementById('server-api-key-value');
|
|
2582
2654
|
if (!el) return;
|
|
@@ -2596,4 +2668,5 @@ window.setServerSavePassword = setServerSavePassword;
|
|
|
2596
2668
|
window.submitServerStart = submitServerStart;
|
|
2597
2669
|
window.stopServerGateway = stopServerGateway;
|
|
2598
2670
|
window.copyServerValue = copyServerValue;
|
|
2671
|
+
window.copyPlainText = copyPlainText;
|
|
2599
2672
|
window.toggleServerApiKeyVisibility = toggleServerApiKeyVisibility;
|
package/dist/ui/public/style.css
CHANGED
|
@@ -401,6 +401,10 @@ input { font-family: inherit; }
|
|
|
401
401
|
max-width: 680px;
|
|
402
402
|
}
|
|
403
403
|
|
|
404
|
+
#server.section {
|
|
405
|
+
max-width: 920px;
|
|
406
|
+
}
|
|
407
|
+
|
|
404
408
|
.section + .section {
|
|
405
409
|
padding-top: 64px;
|
|
406
410
|
margin-top: 12px;
|
|
@@ -774,6 +778,95 @@ input { font-family: inherit; }
|
|
|
774
778
|
gap: 4px;
|
|
775
779
|
}
|
|
776
780
|
|
|
781
|
+
.claude-proxy-option {
|
|
782
|
+
display: flex;
|
|
783
|
+
align-items: flex-start;
|
|
784
|
+
gap: 9px;
|
|
785
|
+
padding: 10px 12px;
|
|
786
|
+
border: 1px solid var(--border);
|
|
787
|
+
border-radius: var(--radius);
|
|
788
|
+
background: var(--surface);
|
|
789
|
+
color: var(--text-2);
|
|
790
|
+
cursor: pointer;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
.claude-proxy-option input {
|
|
794
|
+
width: 15px;
|
|
795
|
+
height: 15px;
|
|
796
|
+
margin-top: 2px;
|
|
797
|
+
accent-color: var(--accent);
|
|
798
|
+
flex: 0 0 auto;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
.claude-proxy-option.is-disabled {
|
|
802
|
+
cursor: not-allowed;
|
|
803
|
+
opacity: 0.62;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
.claude-proxy-label {
|
|
807
|
+
display: inline-flex;
|
|
808
|
+
align-items: center;
|
|
809
|
+
gap: 7px;
|
|
810
|
+
font-size: 12px;
|
|
811
|
+
font-weight: 600;
|
|
812
|
+
line-height: 1.45;
|
|
813
|
+
flex-wrap: wrap;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
.claude-proxy-unavailable {
|
|
817
|
+
flex-basis: 100%;
|
|
818
|
+
color: var(--text-3);
|
|
819
|
+
font-size: 11px;
|
|
820
|
+
font-weight: 500;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
.claude-proxy-tooltip {
|
|
824
|
+
position: relative;
|
|
825
|
+
display: inline-flex;
|
|
826
|
+
align-items: center;
|
|
827
|
+
justify-content: center;
|
|
828
|
+
width: 17px;
|
|
829
|
+
height: 17px;
|
|
830
|
+
border: 1px solid var(--border-bright);
|
|
831
|
+
border-radius: 999px;
|
|
832
|
+
color: var(--text-3);
|
|
833
|
+
font-size: 11px;
|
|
834
|
+
font-weight: 700;
|
|
835
|
+
cursor: help;
|
|
836
|
+
outline: none;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
.claude-proxy-tooltip::after {
|
|
840
|
+
content: attr(data-tooltip);
|
|
841
|
+
position: absolute;
|
|
842
|
+
z-index: 90;
|
|
843
|
+
right: -4px;
|
|
844
|
+
bottom: calc(100% + 9px);
|
|
845
|
+
width: min(310px, 72vw);
|
|
846
|
+
padding: 9px 11px;
|
|
847
|
+
border: 1px solid var(--border-bright);
|
|
848
|
+
border-radius: var(--radius-sm);
|
|
849
|
+
background: var(--surface-raised);
|
|
850
|
+
box-shadow: 0 12px 28px oklch(0% 0 0 / 0.26);
|
|
851
|
+
color: var(--text-1);
|
|
852
|
+
font-size: 12px;
|
|
853
|
+
font-weight: 500;
|
|
854
|
+
line-height: 1.45;
|
|
855
|
+
text-align: left;
|
|
856
|
+
transform: translateY(4px);
|
|
857
|
+
opacity: 0;
|
|
858
|
+
visibility: hidden;
|
|
859
|
+
pointer-events: none;
|
|
860
|
+
transition: opacity var(--dur-sm) var(--ease), transform var(--dur-sm) var(--ease);
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
.claude-proxy-tooltip:hover::after,
|
|
864
|
+
.claude-proxy-tooltip:focus-visible::after {
|
|
865
|
+
opacity: 1;
|
|
866
|
+
visibility: visible;
|
|
867
|
+
transform: translateY(0);
|
|
868
|
+
}
|
|
869
|
+
|
|
777
870
|
.launch-model-input-wrap {
|
|
778
871
|
position: relative;
|
|
779
872
|
}
|
|
@@ -1794,6 +1887,7 @@ input { font-family: inherit; }
|
|
|
1794
1887
|
|
|
1795
1888
|
.server-model-table {
|
|
1796
1889
|
width: 100%;
|
|
1890
|
+
table-layout: fixed;
|
|
1797
1891
|
border-collapse: collapse;
|
|
1798
1892
|
font-size: 14px;
|
|
1799
1893
|
}
|
|
@@ -1810,16 +1904,45 @@ input { font-family: inherit; }
|
|
|
1810
1904
|
border-bottom: 1px solid var(--border);
|
|
1811
1905
|
}
|
|
1812
1906
|
|
|
1907
|
+
.server-model-table th:nth-child(1) { width: 18%; }
|
|
1908
|
+
.server-model-table th:nth-child(2) { width: 22%; }
|
|
1909
|
+
.server-model-table th:nth-child(3),
|
|
1910
|
+
.server-model-table th:nth-child(4) { width: 30%; }
|
|
1911
|
+
|
|
1813
1912
|
.server-model-table td {
|
|
1814
1913
|
padding: 9px 14px;
|
|
1815
1914
|
border-bottom: 1px solid var(--border);
|
|
1816
1915
|
color: var(--text-1);
|
|
1916
|
+
vertical-align: middle;
|
|
1917
|
+
overflow: hidden;
|
|
1817
1918
|
}
|
|
1818
1919
|
|
|
1819
1920
|
.server-model-table tr:last-child td { border-bottom: none; }
|
|
1820
1921
|
|
|
1821
|
-
.server-
|
|
1922
|
+
.server-id-cell {
|
|
1923
|
+
display: flex;
|
|
1924
|
+
align-items: center;
|
|
1925
|
+
gap: 8px;
|
|
1926
|
+
min-width: 0;
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
.server-id-cell code {
|
|
1930
|
+
flex: 1;
|
|
1931
|
+
min-width: 0;
|
|
1932
|
+
overflow: hidden;
|
|
1933
|
+
text-overflow: ellipsis;
|
|
1934
|
+
white-space: nowrap;
|
|
1822
1935
|
font-family: 'SF Mono', 'Fira Code', ui-monospace, monospace;
|
|
1823
1936
|
font-size: 12px;
|
|
1824
1937
|
color: var(--text-2);
|
|
1825
1938
|
}
|
|
1939
|
+
|
|
1940
|
+
.server-id-copy {
|
|
1941
|
+
flex-shrink: 0;
|
|
1942
|
+
padding: 3px 8px;
|
|
1943
|
+
font-size: 12px;
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
.server-id-empty {
|
|
1947
|
+
color: var(--text-3);
|
|
1948
|
+
}
|
|
@@ -63,9 +63,10 @@ import {
|
|
|
63
63
|
setServerMaskGatewayIds,
|
|
64
64
|
startServer,
|
|
65
65
|
summarizeServerProviders,
|
|
66
|
+
supportsClaudeTransparentMode,
|
|
66
67
|
validateCustomEndpointUrl,
|
|
67
68
|
writeSecureLogLine
|
|
68
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-I3SSHXSP.js";
|
|
69
70
|
import {
|
|
70
71
|
__toCommonJS,
|
|
71
72
|
init_provider_templates,
|
|
@@ -313,6 +314,12 @@ function getRelayLaunchCommand(appId, options = {}) {
|
|
|
313
314
|
if (options.trace) {
|
|
314
315
|
args.push("--trace");
|
|
315
316
|
}
|
|
317
|
+
if (options.httpProxy) {
|
|
318
|
+
if (app.id !== "claude") {
|
|
319
|
+
throw new Error("Transparent proxy mode is available only for Claude Code CLI.");
|
|
320
|
+
}
|
|
321
|
+
args.push("--http-proxy");
|
|
322
|
+
}
|
|
316
323
|
if (options.providerId && options.modelId) {
|
|
317
324
|
args.push("--provider", options.providerId, "--model", options.modelId);
|
|
318
325
|
} else if (options.providerId || options.modelId) {
|
|
@@ -471,7 +478,9 @@ async function doStartGatewayServer(req) {
|
|
|
471
478
|
}
|
|
472
479
|
setServerFavoritesOnly(req.favoritesOnly);
|
|
473
480
|
setServerFreeModelsOnly(req.freeModelsOnly);
|
|
474
|
-
if (req.
|
|
481
|
+
if (!req.favoritesOnly) {
|
|
482
|
+
setServerExposedProviders(req.exposedProviders ?? []);
|
|
483
|
+
}
|
|
475
484
|
setServerMaskGatewayIds(req.maskGatewayIds);
|
|
476
485
|
setServerListenMode(req.listenMode);
|
|
477
486
|
const host = req.listenMode === "network" ? "0.0.0.0" : "127.0.0.1";
|
|
@@ -661,7 +670,8 @@ async function handleGetModels(res) {
|
|
|
661
670
|
freeStatus: m.freeStatus,
|
|
662
671
|
freeLabel: freeStatusLabel(m.freeStatus),
|
|
663
672
|
contextWindow: m.contextWindow,
|
|
664
|
-
cost: m.cost
|
|
673
|
+
cost: m.cost,
|
|
674
|
+
claudeTransparentCompatible: supportsClaudeTransparentMode(m)
|
|
665
675
|
}))
|
|
666
676
|
}));
|
|
667
677
|
const materializedIds = new Set(catalog.map((p2) => p2.id));
|
|
@@ -1069,6 +1079,7 @@ async function handleLaunchApp(req, res, opts) {
|
|
|
1069
1079
|
try {
|
|
1070
1080
|
const body = JSON.parse(await readBody(req));
|
|
1071
1081
|
const { appId, favorites, cwd } = body;
|
|
1082
|
+
const httpProxy = body.httpProxy === true;
|
|
1072
1083
|
let { providerId, modelId } = body;
|
|
1073
1084
|
if (!appId) {
|
|
1074
1085
|
sendJson(res, 400, { error: "Missing appId" });
|
|
@@ -1078,6 +1089,14 @@ async function handleLaunchApp(req, res, opts) {
|
|
|
1078
1089
|
sendJson(res, 400, { error: `Unknown app: ${appId}` });
|
|
1079
1090
|
return;
|
|
1080
1091
|
}
|
|
1092
|
+
if (body.httpProxy !== void 0 && typeof body.httpProxy !== "boolean") {
|
|
1093
|
+
sendJson(res, 400, { error: "httpProxy must be true or false." });
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
if (httpProxy && appId !== "claude") {
|
|
1097
|
+
sendJson(res, 400, { error: "Anthropic + Relay mode is available only for Claude Code CLI." });
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1081
1100
|
const { installed, path } = detectApp(appId);
|
|
1082
1101
|
if (!installed || !path) {
|
|
1083
1102
|
sendJson(res, 400, { error: `App ${appId} is not installed on this system.` });
|
|
@@ -1087,7 +1106,23 @@ async function handleLaunchApp(req, res, opts) {
|
|
|
1087
1106
|
sendJson(res, 400, { error: "Both providerId and modelId are required to launch a specific Relay model." });
|
|
1088
1107
|
return;
|
|
1089
1108
|
}
|
|
1090
|
-
if (
|
|
1109
|
+
if (httpProxy && providerId && modelId) {
|
|
1110
|
+
let catalog;
|
|
1111
|
+
try {
|
|
1112
|
+
catalog = await fetchModelsWithTimeout();
|
|
1113
|
+
} catch (err) {
|
|
1114
|
+
sendCatalogFetchError(res, err, "Model validation");
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1117
|
+
const selectedModel = catalog.find((provider) => provider.id === providerId)?.models.find((model) => model.id === modelId);
|
|
1118
|
+
if (!selectedModel || !supportsClaudeTransparentMode(selectedModel)) {
|
|
1119
|
+
sendJson(res, 400, {
|
|
1120
|
+
error: "The selected model cannot be combined with your Anthropic login."
|
|
1121
|
+
});
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
if (favorites && !httpProxy && !providerId && !modelId) {
|
|
1091
1126
|
const prefs = loadPreferences();
|
|
1092
1127
|
const favList = AGY_APP_IDS.has(appId) ? prefs.antigravityCliFavoriteModels ?? [] : prefs.favoriteModels ?? [];
|
|
1093
1128
|
if (favList.length > 0) {
|
|
@@ -1112,11 +1147,12 @@ async function handleLaunchApp(req, res, opts) {
|
|
|
1112
1147
|
providerId,
|
|
1113
1148
|
modelId,
|
|
1114
1149
|
cwd: launchFolder,
|
|
1115
|
-
trace: opts.trace
|
|
1150
|
+
trace: opts.trace,
|
|
1151
|
+
httpProxy
|
|
1116
1152
|
});
|
|
1117
1153
|
traceUi(
|
|
1118
1154
|
opts,
|
|
1119
|
-
`launch app=${appId} provider=${providerId ?? ""} model=${modelId ?? ""} favorites=${Boolean(favorites)} resolved-from-favorites=${Boolean(favorites && providerId)} cwd=${launchFolder ?? ""} command=${launchCmd}`
|
|
1155
|
+
`launch app=${appId} provider=${providerId ?? ""} model=${modelId ?? ""} favorites=${Boolean(favorites)} http-proxy=${httpProxy} resolved-from-favorites=${Boolean(favorites && providerId)} cwd=${launchFolder ?? ""} command=${launchCmd}`
|
|
1120
1156
|
);
|
|
1121
1157
|
exec(launchCmd, (err) => {
|
|
1122
1158
|
if (err) {
|
|
@@ -1453,4 +1489,4 @@ export {
|
|
|
1453
1489
|
resolveUiShutdownDecision,
|
|
1454
1490
|
runUiCommand
|
|
1455
1491
|
};
|
|
1456
|
-
//# sourceMappingURL=ui-command-
|
|
1492
|
+
//# sourceMappingURL=ui-command-P27KHEJG.js.map
|