@jacobbd/relay-ai 0.11.0 → 0.11.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-JIDIH7DS.js +28 -0
- package/dist/chunk-O2XZFXHG.js +4763 -0
- package/dist/chunk-O2XZFXHG.js.map +1 -0
- package/dist/{chunk-SFN33VAE.js → chunk-TRM2WGI6.js} +5 -22
- package/dist/{chunk-SFN33VAE.js.map → chunk-TRM2WGI6.js.map} +1 -1
- package/dist/chunk-VXQ7ZVTW.js +152 -0
- package/dist/chunk-VXQ7ZVTW.js.map +1 -0
- package/dist/{chunk-G6QWYYAY.js → chunk-YD6A3ZB3.js} +2386 -6945
- package/dist/chunk-YD6A3ZB3.js.map +1 -0
- package/dist/cli.js +1180 -1049
- package/dist/cli.js.map +1 -1
- package/dist/core/index.js +55 -4
- package/dist/core/index.js.map +1 -1
- package/dist/manual-models-VEBTRQFK.js +12 -0
- package/dist/manual-models-VEBTRQFK.js.map +1 -0
- package/dist/{provider-templates-TZOM62WC.js → provider-templates-JY3NXZK7.js} +3 -2
- package/dist/provider-templates-JY3NXZK7.js.map +1 -0
- package/dist/ui/public/app.js +153 -2
- package/dist/ui/public/style.css +28 -0
- package/dist/{ui-command-RJVKDGGG.js → ui-command-WKPJI2CU.js} +118 -30
- package/dist/ui-command-WKPJI2CU.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-G6QWYYAY.js.map +0 -1
- package/dist/ui-command-RJVKDGGG.js.map +0 -1
- /package/dist/{provider-templates-TZOM62WC.js.map → chunk-JIDIH7DS.js.map} +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
addManualModel,
|
|
4
|
+
removeManualModel
|
|
5
|
+
} from "./chunk-VXQ7ZVTW.js";
|
|
6
|
+
import "./chunk-O2XZFXHG.js";
|
|
7
|
+
import "./chunk-JIDIH7DS.js";
|
|
8
|
+
export {
|
|
9
|
+
addManualModel,
|
|
10
|
+
removeManualModel
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=manual-models-VEBTRQFK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
listAddableTemplates,
|
|
8
8
|
listSupportedTemplates,
|
|
9
9
|
listVisibleOAuthTemplates
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-TRM2WGI6.js";
|
|
11
|
+
import "./chunk-JIDIH7DS.js";
|
|
11
12
|
init_provider_templates();
|
|
12
13
|
export {
|
|
13
14
|
PROVIDER_TEMPLATES,
|
|
@@ -17,4 +18,4 @@ export {
|
|
|
17
18
|
listSupportedTemplates,
|
|
18
19
|
listVisibleOAuthTemplates
|
|
19
20
|
};
|
|
20
|
-
//# sourceMappingURL=provider-templates-
|
|
21
|
+
//# sourceMappingURL=provider-templates-JY3NXZK7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/ui/public/app.js
CHANGED
|
@@ -31,6 +31,7 @@ const state = {
|
|
|
31
31
|
providerModelMinCtx: 0,
|
|
32
32
|
providerModelFreeOnly: false,
|
|
33
33
|
providerModelPage: 1,
|
|
34
|
+
manualModelForms: new Map(), // provider ID → draft and in-flight action, survives browser re-renders
|
|
34
35
|
modelFilter: '',
|
|
35
36
|
modelFreeOnly: false,
|
|
36
37
|
agyFilter: '',
|
|
@@ -643,6 +644,7 @@ function renderProviderModelBrowser() {
|
|
|
643
644
|
<p class="section-sub">${provider.models?.length ?? 0} models available · prices shown per 1M tokens</p>
|
|
644
645
|
</div>
|
|
645
646
|
</div>
|
|
647
|
+
${provider.supportsManualModels ? '<div id="manual-model-panel"></div>' : ''}
|
|
646
648
|
<div class="provider-browser-tools">
|
|
647
649
|
<div class="search-field">
|
|
648
650
|
<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>
|
|
@@ -661,7 +663,7 @@ function renderProviderModelBrowser() {
|
|
|
661
663
|
<span>Free models only</span>
|
|
662
664
|
</label>
|
|
663
665
|
</div>
|
|
664
|
-
<button class="btn btn-ghost" id="provider-model-refresh" type="button">Refresh</button>
|
|
666
|
+
<button class="btn btn-ghost" id="provider-model-refresh" type="button" ${state.manualModelForms.get(provider.id)?.pending ? 'disabled' : ''}>Refresh</button>
|
|
665
667
|
</div>
|
|
666
668
|
<div class="provider-model-table-wrap">
|
|
667
669
|
<table class="provider-model-table">
|
|
@@ -669,7 +671,7 @@ function renderProviderModelBrowser() {
|
|
|
669
671
|
<tbody>
|
|
670
672
|
${result.items.map(model => `
|
|
671
673
|
<tr>
|
|
672
|
-
<td><strong>${escapeHtml(model.name || model.id)}</strong
|
|
674
|
+
<td><strong>${escapeHtml(model.name || model.id)}</strong>${model.source === 'manual' ? ' <span class="manual-model-badge">Manual</span>' : ''}<code>${escapeHtml(model.id)}</code></td>
|
|
673
675
|
<td>${fmtCtx(model.contextWindow) || '—'}</td>
|
|
674
676
|
<td>${formatModelPrice(model.cost, isFreeModel(model), freeBadgeLabel(model))}</td>
|
|
675
677
|
${showActions ? `
|
|
@@ -692,6 +694,10 @@ function renderProviderModelBrowser() {
|
|
|
692
694
|
</div>
|
|
693
695
|
`;
|
|
694
696
|
|
|
697
|
+
if (provider.supportsManualModels) {
|
|
698
|
+
document.getElementById('manual-model-panel').appendChild(buildManualModelPanel(provider));
|
|
699
|
+
}
|
|
700
|
+
|
|
695
701
|
document.getElementById('provider-model-search').addEventListener('input', event => {
|
|
696
702
|
state.providerModelFilter = event.target.value;
|
|
697
703
|
state.providerModelPage = 1;
|
|
@@ -746,6 +752,143 @@ function renderProviderModelBrowser() {
|
|
|
746
752
|
});
|
|
747
753
|
}
|
|
748
754
|
|
|
755
|
+
function buildManualModelPanel(provider) {
|
|
756
|
+
let draft = state.manualModelForms.get(provider.id);
|
|
757
|
+
if (!draft) {
|
|
758
|
+
draft = { modelId: '', displayName: '', contextWindow: '', pending: false, message: '', status: '' };
|
|
759
|
+
state.manualModelForms.set(provider.id, draft);
|
|
760
|
+
}
|
|
761
|
+
const panel = document.createElement('section');
|
|
762
|
+
panel.className = 'manual-model-panel';
|
|
763
|
+
const heading = document.createElement('h2');
|
|
764
|
+
heading.textContent = 'Add model manually';
|
|
765
|
+
const note = document.createElement('p');
|
|
766
|
+
note.className = 'section-sub';
|
|
767
|
+
note.id = 'manual-model-note';
|
|
768
|
+
note.textContent = 'Validation makes 3 small API calls and may incur provider charges. The model is saved only after all checks pass.';
|
|
769
|
+
const form = document.createElement('form');
|
|
770
|
+
form.className = 'manual-model-form';
|
|
771
|
+
form.setAttribute('aria-describedby', note.id);
|
|
772
|
+
for (const [name, labelText, type] of [
|
|
773
|
+
['modelId', 'Model ID (required)', 'text'],
|
|
774
|
+
['displayName', 'Display name (optional)', 'text'],
|
|
775
|
+
['contextWindow', 'Context tokens (optional)', 'number'],
|
|
776
|
+
]) {
|
|
777
|
+
const label = document.createElement('label');
|
|
778
|
+
label.textContent = labelText;
|
|
779
|
+
const input = document.createElement('input');
|
|
780
|
+
input.className = 'key-input';
|
|
781
|
+
input.name = name;
|
|
782
|
+
input.type = type;
|
|
783
|
+
input.value = draft[name];
|
|
784
|
+
input.disabled = draft.pending;
|
|
785
|
+
input.required = name === 'modelId';
|
|
786
|
+
if (type === 'number') { input.min = '1'; input.step = '1'; input.max = String(Number.MAX_SAFE_INTEGER); }
|
|
787
|
+
input.addEventListener('input', () => { draft[name] = input.value; });
|
|
788
|
+
label.appendChild(input);
|
|
789
|
+
form.appendChild(label);
|
|
790
|
+
}
|
|
791
|
+
const addButton = document.createElement('button');
|
|
792
|
+
addButton.type = 'submit';
|
|
793
|
+
addButton.className = 'btn btn-primary';
|
|
794
|
+
addButton.textContent = draft.pending ? 'Please wait…' : 'Test & Add';
|
|
795
|
+
addButton.disabled = draft.pending;
|
|
796
|
+
form.appendChild(addButton);
|
|
797
|
+
const feedback = document.createElement('div');
|
|
798
|
+
feedback.className = `key-feedback ${draft.status}`;
|
|
799
|
+
feedback.setAttribute('role', 'status');
|
|
800
|
+
feedback.setAttribute('aria-live', 'polite');
|
|
801
|
+
feedback.textContent = draft.message;
|
|
802
|
+
panel.append(heading, note, form, feedback);
|
|
803
|
+
|
|
804
|
+
async function perform(action, modelId, payload = {}) {
|
|
805
|
+
if (draft.pending) return;
|
|
806
|
+
draft.pending = true;
|
|
807
|
+
draft.status = 'muted';
|
|
808
|
+
draft.message = action === 'add' ? 'Validating model with the provider… This may take a moment.' : 'Removing manual entry…';
|
|
809
|
+
renderProviderModelBrowser();
|
|
810
|
+
try {
|
|
811
|
+
const result = await api('POST', `/api/providers/models/${action}`, { providerId: provider.id, modelId, ...payload });
|
|
812
|
+
if (!result.ok) {
|
|
813
|
+
draft.status = 'error';
|
|
814
|
+
draft.message = result.error || 'The operation failed. Please try again.';
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
draft.status = 'success';
|
|
818
|
+
draft.message = action === 'add' ? `✓ ${modelId} validated and added.` : `✓ Manual entry for ${modelId} removed.`;
|
|
819
|
+
if (action === 'add') {
|
|
820
|
+
draft.modelId = '';
|
|
821
|
+
draft.displayName = '';
|
|
822
|
+
draft.contextWindow = '';
|
|
823
|
+
if (state.activeProviderId === provider.id) {
|
|
824
|
+
state.providerModelFilter = '';
|
|
825
|
+
state.providerModelMinCtx = 0;
|
|
826
|
+
state.providerModelFreeOnly = false;
|
|
827
|
+
state.providerModelPage = 1;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
state.appModelsByTarget = {};
|
|
831
|
+
await initModels();
|
|
832
|
+
if (state.modelsError) draft.message += ' Catalog reload failed; refresh the model list to see the change.';
|
|
833
|
+
renderProviders();
|
|
834
|
+
renderFavList();
|
|
835
|
+
if (!isServerAdminUi()) {
|
|
836
|
+
renderCodexSubagentList();
|
|
837
|
+
renderAgyList();
|
|
838
|
+
renderApps();
|
|
839
|
+
}
|
|
840
|
+
} catch {
|
|
841
|
+
draft.status = 'error';
|
|
842
|
+
draft.message = 'Could not complete the request. Refresh the model list to check whether it saved before retrying.';
|
|
843
|
+
} finally {
|
|
844
|
+
draft.pending = false;
|
|
845
|
+
renderProviderModelBrowser();
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
form.addEventListener('submit', event => {
|
|
850
|
+
event.preventDefault();
|
|
851
|
+
if (draft.pending || !form.reportValidity()) return;
|
|
852
|
+
const modelId = draft.modelId.trim();
|
|
853
|
+
const contextWindow = draft.contextWindow === '' ? undefined : Number(draft.contextWindow);
|
|
854
|
+
if (!modelId || (contextWindow !== undefined && (!Number.isSafeInteger(contextWindow) || contextWindow <= 0))) {
|
|
855
|
+
draft.status = 'error';
|
|
856
|
+
draft.message = 'Enter a model ID and, if provided, a positive whole number of context tokens.';
|
|
857
|
+
renderProviderModelBrowser();
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
void perform('add', modelId, {
|
|
861
|
+
...(draft.displayName.trim() ? { displayName: draft.displayName.trim() } : {}),
|
|
862
|
+
...(contextWindow !== undefined ? { contextWindow } : {}),
|
|
863
|
+
});
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
for (const model of provider.manualModels ?? []) {
|
|
867
|
+
const row = document.createElement('div');
|
|
868
|
+
row.className = 'manual-model-entry';
|
|
869
|
+
const badge = document.createElement('span');
|
|
870
|
+
badge.className = 'manual-model-badge';
|
|
871
|
+
badge.textContent = 'Manual';
|
|
872
|
+
const label = document.createElement('span');
|
|
873
|
+
label.className = 'manual-model-entry-name';
|
|
874
|
+
label.textContent = `${model.name || model.id}${model.name && model.name !== model.id ? ` (${model.id})` : ''}`;
|
|
875
|
+
label.title = model.validatedAt ? `Validated ${model.validatedAt}` : '';
|
|
876
|
+
const removeButton = document.createElement('button');
|
|
877
|
+
removeButton.type = 'button';
|
|
878
|
+
removeButton.className = 'btn btn-ghost';
|
|
879
|
+
removeButton.textContent = 'Remove manual entry';
|
|
880
|
+
removeButton.setAttribute('aria-label', `Remove manual entry for ${model.id}`);
|
|
881
|
+
removeButton.disabled = draft.pending;
|
|
882
|
+
removeButton.addEventListener('click', () => {
|
|
883
|
+
if (draft.pending || !window.confirm(`Remove the manual entry for ${model.id}? If the provider also lists this model, its discovered entry will remain.`)) return;
|
|
884
|
+
void perform('remove', model.id);
|
|
885
|
+
});
|
|
886
|
+
row.append(badge, label, removeButton);
|
|
887
|
+
panel.appendChild(row);
|
|
888
|
+
}
|
|
889
|
+
return panel;
|
|
890
|
+
}
|
|
891
|
+
|
|
749
892
|
function buildTemplateCard(template) {
|
|
750
893
|
const [c1, c2] = providerPalette(template.id);
|
|
751
894
|
const card = document.createElement('div');
|
|
@@ -1383,6 +1526,14 @@ function buildProviderBodyContent(provider) {
|
|
|
1383
1526
|
setTimeout(() => { feedback.textContent = ''; feedback.className = 'key-feedback'; }, 4000);
|
|
1384
1527
|
});
|
|
1385
1528
|
|
|
1529
|
+
if (provider.supportsManualModels) {
|
|
1530
|
+
const manualButton = document.createElement('button');
|
|
1531
|
+
manualButton.type = 'button';
|
|
1532
|
+
manualButton.className = 'btn btn-ghost';
|
|
1533
|
+
manualButton.textContent = 'Add model manually';
|
|
1534
|
+
manualButton.addEventListener('click', () => openProviderModelBrowser(provider.id));
|
|
1535
|
+
content.appendChild(manualButton);
|
|
1536
|
+
}
|
|
1386
1537
|
if (provider.customEndpoint) content.appendChild(buildEditCustomEndpointRow(provider));
|
|
1387
1538
|
content.appendChild(buildDeleteProviderRow(provider));
|
|
1388
1539
|
return content;
|
package/dist/ui/public/style.css
CHANGED
|
@@ -1469,6 +1469,34 @@ input { font-family: inherit; }
|
|
|
1469
1469
|
.key-feedback.error { color: var(--error); }
|
|
1470
1470
|
.key-feedback.muted { color: var(--text-3); }
|
|
1471
1471
|
|
|
1472
|
+
.manual-model-panel {
|
|
1473
|
+
margin-bottom: 24px;
|
|
1474
|
+
padding: 18px;
|
|
1475
|
+
background: var(--surface-2);
|
|
1476
|
+
border: 1px solid var(--border);
|
|
1477
|
+
border-radius: 12px;
|
|
1478
|
+
}
|
|
1479
|
+
.manual-model-panel h2 { margin: 0 0 8px; font-size: 16px; }
|
|
1480
|
+
.manual-model-form {
|
|
1481
|
+
display: flex;
|
|
1482
|
+
align-items: end;
|
|
1483
|
+
gap: 12px;
|
|
1484
|
+
flex-wrap: wrap;
|
|
1485
|
+
margin-top: 16px;
|
|
1486
|
+
}
|
|
1487
|
+
.manual-model-form label { flex: 1 1 180px; font-size: 12px; color: var(--text-2); }
|
|
1488
|
+
.manual-model-form .key-input { display: block; width: 100%; margin-top: 6px; }
|
|
1489
|
+
.manual-model-badge {
|
|
1490
|
+
display: inline-block;
|
|
1491
|
+
padding: 2px 7px;
|
|
1492
|
+
border: 1px solid var(--border);
|
|
1493
|
+
border-radius: 6px;
|
|
1494
|
+
font-size: 11px;
|
|
1495
|
+
color: var(--text-2);
|
|
1496
|
+
}
|
|
1497
|
+
.manual-model-entry { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
|
|
1498
|
+
.manual-model-entry-name { flex: 1 1 180px; overflow-wrap: anywhere; font-size: 13px; }
|
|
1499
|
+
|
|
1472
1500
|
.oauth-device-note {
|
|
1473
1501
|
color: var(--text-2);
|
|
1474
1502
|
font-size: 14px;
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
BACKENDS,
|
|
4
|
-
CODEX_SUBAGENT_MODEL_CAP,
|
|
5
|
-
MAX_MODEL_CATALOG,
|
|
6
|
-
VERSION,
|
|
7
3
|
addCustomEndpointProvider,
|
|
8
4
|
addProviderFromTemplate,
|
|
9
|
-
buildAntigravityAuthUrl,
|
|
10
5
|
buildDedupedModelRows,
|
|
11
6
|
checkForUpdates,
|
|
12
|
-
completeAntigravityExchange,
|
|
13
7
|
copilotPlanTier,
|
|
14
8
|
createGatewayModelCatalog,
|
|
15
9
|
ensureOpencodeCloudProviders,
|
|
@@ -24,7 +18,6 @@ import {
|
|
|
24
18
|
formatGatewayUrls,
|
|
25
19
|
freeStatusLabel,
|
|
26
20
|
gatewayProviderLabel,
|
|
27
|
-
getAppHome,
|
|
28
21
|
getAppPathOverride,
|
|
29
22
|
getEnvServerPassword,
|
|
30
23
|
getSavedServerPassword,
|
|
@@ -35,41 +28,26 @@ import {
|
|
|
35
28
|
getServerListenMode,
|
|
36
29
|
getServerMaskGatewayIds,
|
|
37
30
|
getUiDebugLogPath,
|
|
38
|
-
guiCallbackRedirectUri,
|
|
39
31
|
hostFromHeader,
|
|
40
32
|
loadPreferences,
|
|
41
|
-
loadRegistry,
|
|
42
33
|
loadServerModels,
|
|
43
34
|
makeTraceLogger,
|
|
44
35
|
normalizeFavoriteModels,
|
|
45
|
-
openAiDeviceCodeUrl,
|
|
46
36
|
openAiIdCollisions,
|
|
47
|
-
pollClinePassDeviceCode,
|
|
48
|
-
pollGithubDeviceCodeToken,
|
|
49
|
-
pollOpenAiDeviceCodeToken,
|
|
50
|
-
pollXaiDeviceCodeToken,
|
|
51
|
-
preferredRelayCredentialAuthRef,
|
|
52
37
|
providerOptionsFromCatalog,
|
|
53
38
|
providersForCodexSubagents,
|
|
54
39
|
providersForTarget,
|
|
55
40
|
readBody,
|
|
56
|
-
readStoredProviderCredential,
|
|
57
41
|
recordLaunchFolder,
|
|
58
42
|
refreshAllProviderModels,
|
|
59
43
|
refreshProviderModels,
|
|
60
44
|
removeProviderFromRegistry,
|
|
61
|
-
requestClinePassDeviceCode,
|
|
62
|
-
requestGithubDeviceCode,
|
|
63
|
-
requestOpenAiDeviceCode,
|
|
64
|
-
requestXaiDeviceCode,
|
|
65
45
|
resolveAdvertiseAddresses,
|
|
66
46
|
resolveAdvertiseGatewayPort,
|
|
67
|
-
resolveProviderCredential,
|
|
68
47
|
resolveServerAutostart,
|
|
69
48
|
resolveServerUpstreamApiKey,
|
|
70
49
|
saveNativeOAuthCredential,
|
|
71
50
|
savePreferences,
|
|
72
|
-
saveProviderCredential,
|
|
73
51
|
sendJson,
|
|
74
52
|
setAppPathOverride,
|
|
75
53
|
setSavedServerPassword,
|
|
@@ -83,16 +61,44 @@ import {
|
|
|
83
61
|
summarizeServerProviders,
|
|
84
62
|
supportsClaudeTransparentMode,
|
|
85
63
|
updateCustomEndpointProvider,
|
|
86
|
-
validateCustomEndpointUrl,
|
|
87
64
|
writeSecureLogLine
|
|
88
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-YD6A3ZB3.js";
|
|
89
66
|
import {
|
|
90
|
-
__toCommonJS,
|
|
91
67
|
init_provider_templates,
|
|
92
68
|
listAddableTemplates,
|
|
93
69
|
listVisibleOAuthTemplates,
|
|
94
70
|
provider_templates_exports
|
|
95
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-TRM2WGI6.js";
|
|
72
|
+
import {
|
|
73
|
+
BACKENDS,
|
|
74
|
+
CODEX_SUBAGENT_MODEL_CAP,
|
|
75
|
+
MAX_MODEL_CATALOG,
|
|
76
|
+
VERSION,
|
|
77
|
+
buildAntigravityAuthUrl,
|
|
78
|
+
completeAntigravityExchange,
|
|
79
|
+
getAppHome,
|
|
80
|
+
getProviderModels,
|
|
81
|
+
guiCallbackRedirectUri,
|
|
82
|
+
loadRegistry,
|
|
83
|
+
openAiDeviceCodeUrl,
|
|
84
|
+
pollClinePassDeviceCode,
|
|
85
|
+
pollGithubDeviceCodeToken,
|
|
86
|
+
pollOpenAiDeviceCodeToken,
|
|
87
|
+
pollXaiDeviceCodeToken,
|
|
88
|
+
preferredRelayCredentialAuthRef,
|
|
89
|
+
readStoredProviderCredential,
|
|
90
|
+
requestClinePassDeviceCode,
|
|
91
|
+
requestGithubDeviceCode,
|
|
92
|
+
requestOpenAiDeviceCode,
|
|
93
|
+
requestXaiDeviceCode,
|
|
94
|
+
resolveProviderCredential,
|
|
95
|
+
saveProviderCredential,
|
|
96
|
+
supportsManualModels,
|
|
97
|
+
validateCustomEndpointUrl
|
|
98
|
+
} from "./chunk-O2XZFXHG.js";
|
|
99
|
+
import {
|
|
100
|
+
__toCommonJS
|
|
101
|
+
} from "./chunk-JIDIH7DS.js";
|
|
96
102
|
|
|
97
103
|
// src/ui-command.ts
|
|
98
104
|
import { createServer } from "http";
|
|
@@ -673,6 +679,10 @@ function handleUiApiRequest(req, res, opts = {}) {
|
|
|
673
679
|
handleEditCustomProvider(req, res);
|
|
674
680
|
} else if (url === "/api/providers/delete" && req.method === "POST") {
|
|
675
681
|
handleDeleteProvider(req, res);
|
|
682
|
+
} else if (url === "/api/providers/models/add" && req.method === "POST") {
|
|
683
|
+
handleManualModel(req, res, "add");
|
|
684
|
+
} else if (url === "/api/providers/models/remove" && req.method === "POST") {
|
|
685
|
+
handleManualModel(req, res, "remove");
|
|
676
686
|
} else if (url === "/api/providers/oauth/start" && req.method === "POST") {
|
|
677
687
|
handleOAuthStart(req, res);
|
|
678
688
|
} else if (url.startsWith("/api/providers/oauth/status") && req.method === "GET") {
|
|
@@ -740,7 +750,19 @@ async function handleGetModels(res, target, codexSubagents = false, uiMode) {
|
|
|
740
750
|
if (codexSubagents) catalog = providersForCodexSubagents(catalog);
|
|
741
751
|
else if (target) catalog = providersForTarget(catalog, target);
|
|
742
752
|
const registry = loadRegistry();
|
|
743
|
-
const
|
|
753
|
+
const registryById = new Map(registry.providers.map((p2) => [p2.id, p2]));
|
|
754
|
+
const rawCountById = new Map(registry.providers.map((p2) => [p2.id, getProviderModels(p2).length]));
|
|
755
|
+
const manualIdsByProvider = new Map(registry.providers.map((p2) => [
|
|
756
|
+
p2.id,
|
|
757
|
+
new Set(getProviderModels(p2).filter((m) => m.source === "manual").map((m) => m.id))
|
|
758
|
+
]));
|
|
759
|
+
const manualMetadata = (id) => {
|
|
760
|
+
const provider = registryById.get(id);
|
|
761
|
+
return {
|
|
762
|
+
supportsManualModels: provider ? provider.enabled && supportsManualModels(provider) : false,
|
|
763
|
+
manualModels: (provider?.manualModels ?? []).map(publicManualModel)
|
|
764
|
+
};
|
|
765
|
+
};
|
|
744
766
|
const customById = new Map(
|
|
745
767
|
registry.providers.filter((rp) => rp.templateId === "custom-openai" || rp.templateId === "custom-anthropic").map((rp) => [rp.id, {
|
|
746
768
|
kind: rp.templateId === "custom-anthropic" ? "anthropic" : "openai",
|
|
@@ -759,6 +781,7 @@ async function handleGetModels(res, target, codexSubagents = false, uiMode) {
|
|
|
759
781
|
return getTemplateById(t)?.anonymousFreeModels === true;
|
|
760
782
|
})(),
|
|
761
783
|
authType: p2.authType ?? "api",
|
|
784
|
+
...manualMetadata(p2.id),
|
|
762
785
|
// Copilot's runtime catalog is policy-filtered by account plan. Never replace
|
|
763
786
|
// that safe count with the larger raw cache count.
|
|
764
787
|
modelCount: p2.id === "github-copilot" ? p2.models.length : rawCountById.get(p2.id) ?? p2.models.length,
|
|
@@ -772,7 +795,8 @@ async function handleGetModels(res, target, codexSubagents = false, uiMode) {
|
|
|
772
795
|
freeLabel: freeStatusLabel(m.freeStatus),
|
|
773
796
|
contextWindow: m.contextWindow,
|
|
774
797
|
cost: m.cost,
|
|
775
|
-
claudeTransparentCompatible: supportsClaudeTransparentMode(m)
|
|
798
|
+
claudeTransparentCompatible: supportsClaudeTransparentMode(m),
|
|
799
|
+
...manualIdsByProvider.get(p2.id)?.has(m.id) ? { source: "manual" } : {}
|
|
776
800
|
}))
|
|
777
801
|
}));
|
|
778
802
|
const materializedIds = new Set(catalog.map((p2) => p2.id));
|
|
@@ -787,16 +811,80 @@ async function handleGetModels(res, target, codexSubagents = false, uiMode) {
|
|
|
787
811
|
hasKey: true,
|
|
788
812
|
freeAccess: false,
|
|
789
813
|
authType: "oauth",
|
|
814
|
+
...manualMetadata(rp.id),
|
|
790
815
|
modelCount: 0,
|
|
791
816
|
...rp.id === "github-copilot" ? { subscription: copilotSubscription(void 0) } : {},
|
|
792
817
|
models: []
|
|
793
818
|
});
|
|
794
819
|
}
|
|
820
|
+
if (!target && !codexSubagents) {
|
|
821
|
+
for (const rp of registry.providers) {
|
|
822
|
+
if (!rp.enabled || !supportsManualModels(rp) || materializedIds.has(rp.id) || getProviderModels(rp).length !== 0) continue;
|
|
823
|
+
const credential = await resolveProviderCredential(rp.id, rp.authRef).catch(() => null);
|
|
824
|
+
providers.push({
|
|
825
|
+
id: rp.id,
|
|
826
|
+
name: rp.name,
|
|
827
|
+
favoriteName: favoriteProviderDisplayName({ id: rp.id, name: rp.name, authType: rp.authType }),
|
|
828
|
+
hasKey: Boolean(credential),
|
|
829
|
+
freeAccess: false,
|
|
830
|
+
authType: rp.authType ?? "api",
|
|
831
|
+
modelCount: 0,
|
|
832
|
+
...manualMetadata(rp.id),
|
|
833
|
+
...customById.has(rp.id) ? { customEndpoint: customById.get(rp.id) } : {},
|
|
834
|
+
models: []
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
}
|
|
795
838
|
sendJson(res, 200, { providers });
|
|
796
839
|
} catch (err) {
|
|
797
840
|
sendCatalogFetchError(res, err, "Model fetch");
|
|
798
841
|
}
|
|
799
842
|
}
|
|
843
|
+
function publicManualModel(model) {
|
|
844
|
+
return {
|
|
845
|
+
id: model.id,
|
|
846
|
+
name: model.name,
|
|
847
|
+
contextWindow: model.contextWindow,
|
|
848
|
+
validatedAt: model.validatedAt,
|
|
849
|
+
source: "manual"
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
async function handleManualModel(req, res, action) {
|
|
853
|
+
let body;
|
|
854
|
+
try {
|
|
855
|
+
const parsed = JSON.parse(await readBody(req));
|
|
856
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("object required");
|
|
857
|
+
body = parsed;
|
|
858
|
+
} catch {
|
|
859
|
+
sendJson(res, 400, { ok: false, error: "Request body must be a JSON object" });
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
const { providerId, modelId, displayName, contextWindow } = body;
|
|
863
|
+
if (typeof providerId !== "string" || !providerId.trim() || typeof modelId !== "string" || !modelId.trim()) {
|
|
864
|
+
sendJson(res, 400, { ok: false, error: "providerId and modelId must be non-empty strings" });
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
if (action === "add" && (displayName !== void 0 && typeof displayName !== "string" || contextWindow !== void 0 && (typeof contextWindow !== "number" || !Number.isSafeInteger(contextWindow) || contextWindow <= 0))) {
|
|
868
|
+
sendJson(res, 400, { ok: false, error: "displayName must be a string and contextWindow must be a positive integer when provided" });
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
try {
|
|
872
|
+
const { addManualModel, removeManualModel } = await import("./manual-models-VEBTRQFK.js");
|
|
873
|
+
const result = action === "add" ? await addManualModel({
|
|
874
|
+
providerId: providerId.trim(),
|
|
875
|
+
modelId: modelId.trim(),
|
|
876
|
+
...typeof displayName === "string" && displayName.trim() ? { displayName: displayName.trim() } : {},
|
|
877
|
+
...typeof contextWindow === "number" ? { contextWindow } : {}
|
|
878
|
+
}) : removeManualModel(providerId.trim(), modelId.trim());
|
|
879
|
+
sendJson(res, 200, {
|
|
880
|
+
ok: result.ok,
|
|
881
|
+
...!result.ok ? { error: result.error ?? "Manual model operation failed" } : {},
|
|
882
|
+
...result.ok && "model" in result && result.model ? { model: publicManualModel(result.model) } : {}
|
|
883
|
+
});
|
|
884
|
+
} catch {
|
|
885
|
+
sendJson(res, 500, { ok: false, error: `Unable to ${action} manual model. Please try again.` });
|
|
886
|
+
}
|
|
887
|
+
}
|
|
800
888
|
function copilotSubscription(providerData) {
|
|
801
889
|
const tier = copilotPlanTier(providerData);
|
|
802
890
|
if (tier === "free") return { tier, label: "Copilot Free" };
|
|
@@ -958,7 +1046,7 @@ async function handleAddProvider(req, res) {
|
|
|
958
1046
|
sendJson(res, 400, { error: "templateId required" });
|
|
959
1047
|
return;
|
|
960
1048
|
}
|
|
961
|
-
const { listSupportedTemplates } = await import("./provider-templates-
|
|
1049
|
+
const { listSupportedTemplates } = await import("./provider-templates-JY3NXZK7.js");
|
|
962
1050
|
const template = listSupportedTemplates().find((t) => t.id === templateId);
|
|
963
1051
|
if (!template) {
|
|
964
1052
|
sendJson(res, 404, { error: `Template '${templateId}' not found` });
|
|
@@ -1810,4 +1898,4 @@ export {
|
|
|
1810
1898
|
resolveUiShutdownDecision,
|
|
1811
1899
|
runUiCommand
|
|
1812
1900
|
};
|
|
1813
|
-
//# sourceMappingURL=ui-command-
|
|
1901
|
+
//# sourceMappingURL=ui-command-WKPJI2CU.js.map
|