@bman654/clodex 2.14.1 → 2.16.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.
- package/README.md +15 -2
- package/dist/{chunk-3NCDMS7N.js → chunk-5TG736TX.js} +14 -1
- package/dist/{chunk-3NCDMS7N.js.map → chunk-5TG736TX.js.map} +1 -1
- package/dist/claude-wrapper.js +1 -1
- package/dist/cli.js +900 -537
- package/dist/cli.js.map +1 -1
- package/docs/opencode-go.md +3 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
clearActiveOAuthAccount,
|
|
11
11
|
collectPristineFacts,
|
|
12
12
|
contentAddressedBackupPath,
|
|
13
|
+
customProviderId,
|
|
13
14
|
emitParentNotice,
|
|
14
15
|
ensureSecureAppHome,
|
|
15
16
|
findClaudeBinary,
|
|
@@ -61,6 +62,7 @@ import {
|
|
|
61
62
|
setServerListenMode,
|
|
62
63
|
setServerMaskGatewayIds,
|
|
63
64
|
sha256File,
|
|
65
|
+
slugifyProviderId,
|
|
64
66
|
storeActiveOAuthAccount,
|
|
65
67
|
tcpListenerUrlHost,
|
|
66
68
|
tweakccMirrorBackupPath,
|
|
@@ -72,7 +74,7 @@ import {
|
|
|
72
74
|
withRegistryWriteLockSync,
|
|
73
75
|
writeParentNoticeLines,
|
|
74
76
|
writeParentNoticeLinesSync
|
|
75
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-5TG736TX.js";
|
|
76
78
|
|
|
77
79
|
// src/cli.ts
|
|
78
80
|
import pc13 from "picocolors";
|
|
@@ -141,8 +143,8 @@ function fmtProvider(name) {
|
|
|
141
143
|
}
|
|
142
144
|
function fmtProviderBracket(providerId, providerName, isFree) {
|
|
143
145
|
const color = providerTagColor(providerId);
|
|
144
|
-
const
|
|
145
|
-
return color(pc.bold(`(${
|
|
146
|
+
const text6 = isFree ? `${providerName} \xB7 free` : providerName;
|
|
147
|
+
return color(pc.bold(`(${text6})`));
|
|
146
148
|
}
|
|
147
149
|
function providerTagColor(providerId) {
|
|
148
150
|
switch (providerId) {
|
|
@@ -246,7 +248,7 @@ function printFavoritesOnlyPanel() {
|
|
|
246
248
|
}
|
|
247
249
|
|
|
248
250
|
// src/cli.ts
|
|
249
|
-
import * as
|
|
251
|
+
import * as p13 from "@clack/prompts";
|
|
250
252
|
import { realpathSync as realpathSync3 } from "fs";
|
|
251
253
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
252
254
|
|
|
@@ -344,7 +346,7 @@ import { join } from "path";
|
|
|
344
346
|
// package.json
|
|
345
347
|
var package_default = {
|
|
346
348
|
name: "@bman654/clodex",
|
|
347
|
-
version: "2.
|
|
349
|
+
version: "2.16.0",
|
|
348
350
|
publishConfig: {
|
|
349
351
|
access: "public"
|
|
350
352
|
},
|
|
@@ -692,20 +694,22 @@ function getCacheIndex() {
|
|
|
692
694
|
}
|
|
693
695
|
return cacheIndex;
|
|
694
696
|
}
|
|
695
|
-
function
|
|
696
|
-
|
|
697
|
-
if (cached !== void 0) return cached;
|
|
697
|
+
function matchHeuristicWindow(modelId) {
|
|
698
|
+
if (heuristicCache.has(modelId)) return heuristicCache.get(modelId);
|
|
698
699
|
for (const [pattern, size] of HEURISTIC_RULES) {
|
|
699
700
|
if (pattern.test(modelId)) {
|
|
700
701
|
heuristicCache.set(modelId, size);
|
|
701
702
|
return size;
|
|
702
703
|
}
|
|
703
704
|
}
|
|
704
|
-
heuristicCache.set(modelId,
|
|
705
|
-
return
|
|
705
|
+
heuristicCache.set(modelId, void 0);
|
|
706
|
+
return void 0;
|
|
707
|
+
}
|
|
708
|
+
function lookupKnownContextWindow(modelId) {
|
|
709
|
+
return getCacheIndex().get(modelId) ?? matchHeuristicWindow(modelId);
|
|
706
710
|
}
|
|
707
711
|
function lookupContextWindow(modelId) {
|
|
708
|
-
return
|
|
712
|
+
return lookupKnownContextWindow(modelId) ?? DEFAULT_CONTEXT_WINDOW;
|
|
709
713
|
}
|
|
710
714
|
function resolveContextWindow(modelId, explicit) {
|
|
711
715
|
if (typeof explicit === "number" && explicit > 0) return explicit;
|
|
@@ -3158,17 +3162,19 @@ function parseContextStop(value) {
|
|
|
3158
3162
|
return parsed;
|
|
3159
3163
|
}
|
|
3160
3164
|
function resolveContextStop(limits, stop = "standard") {
|
|
3161
|
-
const
|
|
3162
|
-
const
|
|
3163
|
-
const
|
|
3164
|
-
const
|
|
3165
|
+
const declaredWindow = positiveInteger(limits.contextWindow);
|
|
3166
|
+
const declaredCeiling = positiveInteger(limits.maxContextWindow);
|
|
3167
|
+
const standardRaw = declaredWindow ?? DEFAULT_CONTEXT_WINDOW;
|
|
3168
|
+
const ceiling = declaredCeiling ?? declaredWindow;
|
|
3169
|
+
const requested = stop === "standard" ? standardRaw : stop === "max" ? declaredCeiling ?? standardRaw : positiveInteger(stop) ?? standardRaw;
|
|
3170
|
+
const raw = ceiling === void 0 ? requested : Math.min(requested, ceiling);
|
|
3165
3171
|
const effective = effectiveContextWindow(raw, limits.effectiveContextPercent);
|
|
3166
3172
|
const boundary = positiveInteger(limits.pricingBoundary);
|
|
3167
3173
|
return {
|
|
3168
3174
|
stop,
|
|
3169
3175
|
raw,
|
|
3170
3176
|
effective,
|
|
3171
|
-
...requested > ceiling ? { clampedFrom: requested } : {},
|
|
3177
|
+
...ceiling !== void 0 && requested > ceiling ? { clampedFrom: requested } : {},
|
|
3172
3178
|
crossesPricingBoundary: boundary !== void 0 && effective > boundary
|
|
3173
3179
|
};
|
|
3174
3180
|
}
|
|
@@ -3537,24 +3543,24 @@ function applyClodexPatches(source, config) {
|
|
|
3537
3543
|
}
|
|
3538
3544
|
const reEsc = (s) => s.replace(/[.*+?^$\{\}()|[\]\\]/g, "\\$&");
|
|
3539
3545
|
const q = (s) => JSON.stringify(s);
|
|
3540
|
-
function
|
|
3546
|
+
function log13(status, name, extra) {
|
|
3541
3547
|
report.push(extra === void 0 ? { status, name } : { status, name, extra });
|
|
3542
3548
|
}
|
|
3543
3549
|
function applyOnce(name, regex, fn, { marker, required, noopIsSkip } = {}) {
|
|
3544
3550
|
if (marker && js.includes(marker)) {
|
|
3545
|
-
|
|
3551
|
+
log13("SKIP", name, "already patched");
|
|
3546
3552
|
return;
|
|
3547
3553
|
}
|
|
3548
3554
|
const g = new RegExp(regex.source, regex.flags.includes("g") ? regex.flags : regex.flags + "g");
|
|
3549
3555
|
const matches = js.match(g);
|
|
3550
3556
|
const count = matches ? matches.length : 0;
|
|
3551
3557
|
if (count === 0) {
|
|
3552
|
-
|
|
3558
|
+
log13("FAIL", name, "anchor not found");
|
|
3553
3559
|
if (required) fail("clodex patch: required patch failed: " + name);
|
|
3554
3560
|
return;
|
|
3555
3561
|
}
|
|
3556
3562
|
if (count > 1) {
|
|
3557
|
-
|
|
3563
|
+
log13("FAIL", name, "anchor matched " + count + " times (expected 1)");
|
|
3558
3564
|
if (required) fail("clodex patch: ambiguous anchor: " + name);
|
|
3559
3565
|
return;
|
|
3560
3566
|
}
|
|
@@ -3562,14 +3568,14 @@ function applyClodexPatches(source, config) {
|
|
|
3562
3568
|
js = js.replace(regex, fn);
|
|
3563
3569
|
if (js === before) {
|
|
3564
3570
|
if (noopIsSkip) {
|
|
3565
|
-
|
|
3571
|
+
log13("SKIP", name, "already patched");
|
|
3566
3572
|
return;
|
|
3567
3573
|
}
|
|
3568
|
-
|
|
3574
|
+
log13("FAIL", name, "replacement made no change");
|
|
3569
3575
|
if (required) fail(name);
|
|
3570
3576
|
return;
|
|
3571
3577
|
}
|
|
3572
|
-
|
|
3578
|
+
log13("OK", name);
|
|
3573
3579
|
}
|
|
3574
3580
|
function extendAliasArray(arrLiteral) {
|
|
3575
3581
|
const toAdd = IDENTITIES.filter((a) => !new RegExp('"' + reEsc(a) + '"').test(arrLiteral));
|
|
@@ -3600,7 +3606,7 @@ function applyClodexPatches(source, config) {
|
|
|
3600
3606
|
);
|
|
3601
3607
|
const cases = missing.map((a) => "case" + q(a) + ":return " + q(a) + ";").join("");
|
|
3602
3608
|
if (ALIASES.length === 0) {
|
|
3603
|
-
|
|
3609
|
+
log13("SKIP", "PATCH 6: alias resolver switch", "no aliases configured");
|
|
3604
3610
|
} else {
|
|
3605
3611
|
applyOnce(
|
|
3606
3612
|
"PATCH 6: alias resolver switch",
|
|
@@ -3623,9 +3629,9 @@ function applyClodexPatches(source, config) {
|
|
|
3623
3629
|
const pickerSite = "PATCH 5: model picker options";
|
|
3624
3630
|
const modelSelections = [...js.matchAll(/\(([\w$]+)==="opus"\|\|\1==="sonnet"\)/g)];
|
|
3625
3631
|
if (ALIASES.length === 0) {
|
|
3626
|
-
|
|
3632
|
+
log13("SKIP", pickerSite, "no aliases configured");
|
|
3627
3633
|
} else if (modelSelections.length !== 1) {
|
|
3628
|
-
|
|
3634
|
+
log13("FAIL", pickerSite, "model selection appears " + modelSelections.length + " times (expected 1)");
|
|
3629
3635
|
} else {
|
|
3630
3636
|
applyOnce(
|
|
3631
3637
|
pickerSite,
|
|
@@ -3753,11 +3759,11 @@ function applyClodexPatches(source, config) {
|
|
|
3753
3759
|
"CLAUDE_CODE_OTEL_DIAG_STDERR"
|
|
3754
3760
|
];
|
|
3755
3761
|
const MIN_SCRUBBED_ENV_NAMES = 1;
|
|
3756
|
-
const blockEndIndex = (
|
|
3762
|
+
const blockEndIndex = (text6, open2) => {
|
|
3757
3763
|
const depths = [0];
|
|
3758
3764
|
const templates = [false];
|
|
3759
|
-
for (let i = open2; i <
|
|
3760
|
-
const ch =
|
|
3765
|
+
for (let i = open2; i < text6.length; i++) {
|
|
3766
|
+
const ch = text6[i];
|
|
3761
3767
|
const inTemplate = templates[templates.length - 1];
|
|
3762
3768
|
if (inTemplate) {
|
|
3763
3769
|
if (ch === "\\") {
|
|
@@ -3769,7 +3775,7 @@ function applyClodexPatches(source, config) {
|
|
|
3769
3775
|
depths.pop();
|
|
3770
3776
|
continue;
|
|
3771
3777
|
}
|
|
3772
|
-
if (ch === "$" &&
|
|
3778
|
+
if (ch === "$" && text6[i + 1] === "{") {
|
|
3773
3779
|
templates.push(false);
|
|
3774
3780
|
depths.push(0);
|
|
3775
3781
|
i++;
|
|
@@ -3782,12 +3788,12 @@ function applyClodexPatches(source, config) {
|
|
|
3782
3788
|
}
|
|
3783
3789
|
if (ch === '"' || ch === "'") {
|
|
3784
3790
|
const quote = ch;
|
|
3785
|
-
for (i++; i <
|
|
3786
|
-
if (
|
|
3791
|
+
for (i++; i < text6.length; i++) {
|
|
3792
|
+
if (text6[i] === "\\") {
|
|
3787
3793
|
i++;
|
|
3788
3794
|
continue;
|
|
3789
3795
|
}
|
|
3790
|
-
if (
|
|
3796
|
+
if (text6[i] === quote) break;
|
|
3791
3797
|
}
|
|
3792
3798
|
continue;
|
|
3793
3799
|
}
|
|
@@ -3796,12 +3802,12 @@ function applyClodexPatches(source, config) {
|
|
|
3796
3802
|
depths.push(0);
|
|
3797
3803
|
continue;
|
|
3798
3804
|
}
|
|
3799
|
-
if (ch === "/" &&
|
|
3800
|
-
while (i <
|
|
3805
|
+
if (ch === "/" && text6[i + 1] === "/") {
|
|
3806
|
+
while (i < text6.length && text6[i] !== "\n") i++;
|
|
3801
3807
|
continue;
|
|
3802
3808
|
}
|
|
3803
|
-
if (ch === "/" &&
|
|
3804
|
-
i =
|
|
3809
|
+
if (ch === "/" && text6[i + 1] === "*") {
|
|
3810
|
+
i = text6.indexOf("*/", i + 2);
|
|
3805
3811
|
if (i < 0) return -1;
|
|
3806
3812
|
i++;
|
|
3807
3813
|
continue;
|
|
@@ -3824,7 +3830,7 @@ function applyClodexPatches(source, config) {
|
|
|
3824
3830
|
};
|
|
3825
3831
|
const passthroughSites = source.includes(marker) ? 1 : (source.match(/\)return process\.env;let [\w$]+=\{/g) ?? []).length;
|
|
3826
3832
|
if (passthroughSites !== 1) {
|
|
3827
|
-
|
|
3833
|
+
log13("FAIL", patchName, "child env passthrough appears " + passthroughSites + " times (expected 1)");
|
|
3828
3834
|
fail("clodex patch: required patch failed: " + patchName);
|
|
3829
3835
|
}
|
|
3830
3836
|
applyOnce(
|
|
@@ -3839,7 +3845,7 @@ function applyClodexPatches(source, config) {
|
|
|
3839
3845
|
const scrubbedSeen = scrubbedEnvNames.filter((name) => body.includes(name));
|
|
3840
3846
|
const why = missingLiteral !== void 0 ? "body does not contain " + missingLiteral : scrubbedSeen.length < MIN_SCRUBBED_ENV_NAMES ? "body spells only " + scrubbedSeen.length + " of the " + scrubbedEnvNames.length + " known child-env names (expected at least " + MIN_SCRUBBED_ENV_NAMES + ")" : /\bfunction\s*[\w$]*\(/.test(body) ? "body declares a nested function" : braceScanFailed ? "the brace walk never reached the function's closing brace" : bound !== 0 ? "match ends " + Math.abs(bound) + " characters " + (bound > 0 ? "before" : "after") + " the end of the function it started in" : void 0;
|
|
3841
3847
|
if (why !== void 0) {
|
|
3842
|
-
|
|
3848
|
+
log13("FAIL", patchName, "target validation failed: " + why);
|
|
3843
3849
|
fail("clodex patch: child network environment target validation failed: " + why);
|
|
3844
3850
|
}
|
|
3845
3851
|
const restoredBody = body.replace(/process\.env/g, "_clodexChildEnv");
|
|
@@ -5206,6 +5212,52 @@ var opencode_go_models_default = [
|
|
|
5206
5212
|
upstreamModelId: "minimax-m3",
|
|
5207
5213
|
family: "minimax"
|
|
5208
5214
|
},
|
|
5215
|
+
{
|
|
5216
|
+
id: "muse-spark-1.2-contributor",
|
|
5217
|
+
name: "Muse Spark 1.2 Contributor",
|
|
5218
|
+
contextWindow: 1048576,
|
|
5219
|
+
cost: {
|
|
5220
|
+
input: 0.1,
|
|
5221
|
+
output: 0.2,
|
|
5222
|
+
cache_read: 2e-3
|
|
5223
|
+
},
|
|
5224
|
+
modelFormat: "openai",
|
|
5225
|
+
npm: "@ai-sdk/openai",
|
|
5226
|
+
apiUrl: "https://opencode.ai/zen/go/v1",
|
|
5227
|
+
reasoning: true,
|
|
5228
|
+
modalities: [
|
|
5229
|
+
"text",
|
|
5230
|
+
"image"
|
|
5231
|
+
],
|
|
5232
|
+
compatibility: {
|
|
5233
|
+
supportsReasoningEffort: false
|
|
5234
|
+
},
|
|
5235
|
+
upstreamModelId: "muse-spark-1.2-contributor",
|
|
5236
|
+
family: "muse"
|
|
5237
|
+
},
|
|
5238
|
+
{
|
|
5239
|
+
id: "muse-spark-1.3-contributor",
|
|
5240
|
+
name: "Muse Spark 1.3 Contributor",
|
|
5241
|
+
contextWindow: 1048576,
|
|
5242
|
+
cost: {
|
|
5243
|
+
input: 0.1,
|
|
5244
|
+
output: 0.2,
|
|
5245
|
+
cache_read: 2e-3
|
|
5246
|
+
},
|
|
5247
|
+
modelFormat: "openai",
|
|
5248
|
+
npm: "@ai-sdk/openai",
|
|
5249
|
+
apiUrl: "https://opencode.ai/zen/go/v1",
|
|
5250
|
+
reasoning: true,
|
|
5251
|
+
modalities: [
|
|
5252
|
+
"text",
|
|
5253
|
+
"image"
|
|
5254
|
+
],
|
|
5255
|
+
compatibility: {
|
|
5256
|
+
supportsReasoningEffort: false
|
|
5257
|
+
},
|
|
5258
|
+
upstreamModelId: "muse-spark-1.3-contributor",
|
|
5259
|
+
family: "muse"
|
|
5260
|
+
},
|
|
5209
5261
|
{
|
|
5210
5262
|
id: "qwen3.6-plus",
|
|
5211
5263
|
name: "Qwen3.6 Plus",
|
|
@@ -5469,6 +5521,10 @@ function openCodeGoPinnedApiUrl(npm) {
|
|
|
5469
5521
|
if (npm === "@ai-sdk/anthropic") return OPENCODE_GO_ANTHROPIC_BASE_URL;
|
|
5470
5522
|
return null;
|
|
5471
5523
|
}
|
|
5524
|
+
function openCodeGoPinnedModelApiUrl(npm) {
|
|
5525
|
+
if (npm === "@ai-sdk/openai") return OPENCODE_GO_COMPLETIONS_BASE_URL;
|
|
5526
|
+
return openCodeGoPinnedApiUrl(npm);
|
|
5527
|
+
}
|
|
5472
5528
|
function isProviderConfiguredForTemplate(provider, templateId) {
|
|
5473
5529
|
if (provider.id === templateId) return true;
|
|
5474
5530
|
return templateId === OPENCODE_GO_PROVIDER_ID && isRetainedOpenCodeGoProvider(provider);
|
|
@@ -5955,10 +6011,10 @@ var OPENAI_OAUTH_MODEL_SEEDS = [
|
|
|
5955
6011
|
{ id: "gpt-5", name: "GPT-5", contextWindow: 272e3, reasoning: true },
|
|
5956
6012
|
// o-series reasoning (Plus+)
|
|
5957
6013
|
{ id: "o4-mini", name: "o4 Mini", reasoning: true },
|
|
5958
|
-
{ id: "o3", name: "o3", reasoning: true },
|
|
6014
|
+
{ id: "o3", name: "o3", contextWindow: 2e5, reasoning: true },
|
|
5959
6015
|
{ id: "o3-mini", name: "o3 Mini", reasoning: true },
|
|
5960
|
-
{ id: "o1", name: "o1", reasoning: true },
|
|
5961
|
-
{ id: "o1-mini", name: "o1 Mini", reasoning: true }
|
|
6016
|
+
{ id: "o1", name: "o1", contextWindow: 2e5, reasoning: true },
|
|
6017
|
+
{ id: "o1-mini", name: "o1 Mini", contextWindow: 128e3, reasoning: true }
|
|
5962
6018
|
];
|
|
5963
6019
|
function hasPricingBoundary(id) {
|
|
5964
6020
|
const version = /^gpt-(\d+)(?:\.(\d+))?(?:-|$)/i.exec(id);
|
|
@@ -6006,7 +6062,7 @@ function buildOpenAiOAuthModels() {
|
|
|
6006
6062
|
upstreamModelId: seed.id,
|
|
6007
6063
|
family: prefix,
|
|
6008
6064
|
brand: deriveBrand(prefix),
|
|
6009
|
-
contextWindow:
|
|
6065
|
+
contextWindow: seed.contextWindow ?? lookupKnownContextWindow(seed.id),
|
|
6010
6066
|
maxContextWindow: seed.maxContextWindow,
|
|
6011
6067
|
effectiveContextPercent: seed.effectiveContextPercent,
|
|
6012
6068
|
pricingBoundary: seed.pricingBoundary ?? pricing.pricingBoundary,
|
|
@@ -6046,6 +6102,22 @@ function normalizeGoogleDisplayName(rawName, bareId) {
|
|
|
6046
6102
|
return stripGoogleModelPrefix(trimmed);
|
|
6047
6103
|
}
|
|
6048
6104
|
|
|
6105
|
+
// src/registry/server-text.ts
|
|
6106
|
+
function isControlCode(code) {
|
|
6107
|
+
return code <= 31 || code >= 127 && code <= 159;
|
|
6108
|
+
}
|
|
6109
|
+
function hasControlChars(value) {
|
|
6110
|
+
for (let i = 0; i < value.length; i++) {
|
|
6111
|
+
if (isControlCode(value.charCodeAt(i))) return true;
|
|
6112
|
+
}
|
|
6113
|
+
return false;
|
|
6114
|
+
}
|
|
6115
|
+
function printableServerText(value) {
|
|
6116
|
+
let out = "";
|
|
6117
|
+
for (const ch of value) out += isControlCode(ch.charCodeAt(0)) ? " " : ch;
|
|
6118
|
+
return out.trim();
|
|
6119
|
+
}
|
|
6120
|
+
|
|
6049
6121
|
// src/trace-log.ts
|
|
6050
6122
|
import {
|
|
6051
6123
|
chmodSync as chmodSync3,
|
|
@@ -6135,8 +6207,8 @@ function compactLogValueWithMarker(value, max) {
|
|
|
6135
6207
|
function systemPreview(system) {
|
|
6136
6208
|
if (typeof system === "string") return compactLogValue(system, REQUEST_PREVIEW_MAX) || void 0;
|
|
6137
6209
|
if (!Array.isArray(system)) return void 0;
|
|
6138
|
-
const
|
|
6139
|
-
return compactLogValue(
|
|
6210
|
+
const text6 = system.map((block) => typeof block === "string" ? block : block && typeof block === "object" && typeof block.text === "string" ? block.text : "").filter(Boolean).join(" ");
|
|
6211
|
+
return compactLogValue(text6, REQUEST_PREVIEW_MAX) || void 0;
|
|
6140
6212
|
}
|
|
6141
6213
|
function inlineSystemPreview(messages) {
|
|
6142
6214
|
if (!Array.isArray(messages)) return void 0;
|
|
@@ -6162,9 +6234,9 @@ function getLatestMessagePreview(messages, system) {
|
|
|
6162
6234
|
if (typeof content === "string") {
|
|
6163
6235
|
summary = content;
|
|
6164
6236
|
} else if (Array.isArray(content)) {
|
|
6165
|
-
const
|
|
6166
|
-
if (
|
|
6167
|
-
summary =
|
|
6237
|
+
const text6 = content.filter((block) => Boolean(block && typeof block === "object")).filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text).join(" ");
|
|
6238
|
+
if (text6.trim()) {
|
|
6239
|
+
summary = text6;
|
|
6168
6240
|
} else {
|
|
6169
6241
|
const blockTypes = [...new Set(content.filter((block) => Boolean(block && typeof block === "object")).map((block) => typeof block.type === "string" ? block.type : "unknown"))];
|
|
6170
6242
|
if (blockTypes.length > 0) blockSummary = `${role}: [${blockTypes.join(", ")}]`;
|
|
@@ -6436,8 +6508,8 @@ function writeSecureLogLine(path, line) {
|
|
|
6436
6508
|
function printTraceLog(debugLogPath) {
|
|
6437
6509
|
if (!existsSync3(debugLogPath)) return;
|
|
6438
6510
|
const raw = readFileSync6(debugLogPath, "utf8");
|
|
6439
|
-
const
|
|
6440
|
-
const errorLines =
|
|
6511
|
+
const log13 = redactTraceLog(raw);
|
|
6512
|
+
const errorLines = log13.split("\n").filter(
|
|
6441
6513
|
(l) => l.includes("error") || l.includes("Error") || l.includes('"type":"error"') || l.includes("status") || l.includes("resolveModel failed") || l.includes("resolveModel fallback")
|
|
6442
6514
|
);
|
|
6443
6515
|
console.log("\n" + pc2.bold(pc2.cyan("\u2500\u2500 Debug trace \u2500\u2500")));
|
|
@@ -6499,13 +6571,16 @@ function parseModelList(body, npm, allowBareArray) {
|
|
|
6499
6571
|
for (const row of rows) {
|
|
6500
6572
|
const rawId = row.id?.trim();
|
|
6501
6573
|
if (!rawId) continue;
|
|
6574
|
+
if (hasControlChars(rawId) || typeof row.name === "string" && hasControlChars(row.name.trim())) continue;
|
|
6502
6575
|
const { id, upstreamModelId: upstreamModelId2 } = normalizeGoogleModelId(rawId, npm);
|
|
6503
6576
|
const family = id.split(/[-/:]/)[0] ?? id;
|
|
6504
6577
|
const cost = parseNativePricing(row.pricing);
|
|
6505
6578
|
const freeStatus = classifyFreeStatus({
|
|
6506
6579
|
model: { cost, isFree: row.isFree }
|
|
6507
6580
|
});
|
|
6508
|
-
const contextWindow = row.context_length ?? row.contextWindow ?? row.context_window ??
|
|
6581
|
+
const contextWindow = row.context_length ?? row.contextWindow ?? row.context_window ?? row.max_model_len ?? // Left undefined when nothing is known: persisting the invented 200,000
|
|
6582
|
+
// would make it this model's permanent ceiling.
|
|
6583
|
+
lookupKnownContextWindow(id);
|
|
6509
6584
|
models.push({
|
|
6510
6585
|
id,
|
|
6511
6586
|
name: normalizeGoogleDisplayName(row.name, id),
|
|
@@ -6537,7 +6612,7 @@ function materializeTemplateModel(template, model, baseUrl) {
|
|
|
6537
6612
|
upstreamModelId: model.upstreamModelId ?? normalizedUpstream,
|
|
6538
6613
|
family,
|
|
6539
6614
|
brand: model.brand ?? deriveBrand(family),
|
|
6540
|
-
contextWindow: model.contextWindow ??
|
|
6615
|
+
contextWindow: model.contextWindow ?? lookupKnownContextWindow(id),
|
|
6541
6616
|
isFree: model.isFree ?? isFreeStatus(freeStatus),
|
|
6542
6617
|
freeStatus,
|
|
6543
6618
|
modelFormat: model.modelFormat ?? modelFormatForNpm(npm),
|
|
@@ -6739,10 +6814,10 @@ function resolveMaterializedApiUrl(cached, provider, npm) {
|
|
|
6739
6814
|
if (!isRetainedOpenCodeGoProvider(provider)) {
|
|
6740
6815
|
return cached.apiUrl ?? provider.api.url ?? "";
|
|
6741
6816
|
}
|
|
6742
|
-
return
|
|
6817
|
+
return openCodeGoPinnedModelApiUrl(npm);
|
|
6743
6818
|
}
|
|
6744
6819
|
function projectContextStop(cached, providerId, modelId) {
|
|
6745
|
-
const limits = contextLimitsFrom(cached,
|
|
6820
|
+
const limits = contextLimitsFrom(cached, lookupKnownContextWindow(modelId));
|
|
6746
6821
|
const stop = selectContextStop(providerId, modelId);
|
|
6747
6822
|
const resolved = resolveContextStop(limits, stop);
|
|
6748
6823
|
return {
|
|
@@ -6930,9 +7005,9 @@ function collectTargets(contents, kind) {
|
|
|
6930
7005
|
function readNpmShim(shimPath) {
|
|
6931
7006
|
const kind = launcherKind(shimPath);
|
|
6932
7007
|
const unreadable = kind === "sh" ? { kind: "not-a-shim" } : { kind: "unreadable-launcher" };
|
|
6933
|
-
const
|
|
6934
|
-
if (
|
|
6935
|
-
const rawTargets = collectTargets(
|
|
7008
|
+
const text6 = readLauncherText(shimPath);
|
|
7009
|
+
if (text6 === null) return unreadable;
|
|
7010
|
+
const rawTargets = collectTargets(text6, kind);
|
|
6936
7011
|
if (rawTargets.length === 0) return unreadable;
|
|
6937
7012
|
if (!rawTargets.every(isLiteralPath)) return unreadable;
|
|
6938
7013
|
const absolute = rawTargets.map((target) => resolve2(
|
|
@@ -7060,11 +7135,11 @@ function inspectClaudeNativeBinaryPlaceholder(path) {
|
|
|
7060
7135
|
offset += bytesRead;
|
|
7061
7136
|
}
|
|
7062
7137
|
if (offset !== size) return null;
|
|
7063
|
-
const
|
|
7138
|
+
const text6 = bytes.toString("utf8");
|
|
7064
7139
|
const signals = [
|
|
7065
|
-
|
|
7066
|
-
|
|
7067
|
-
|
|
7140
|
+
text6.includes(MISSING_BINARY_MARKER),
|
|
7141
|
+
text6.includes(INSTALL_SCRIPT_MARKER),
|
|
7142
|
+
text6.includes(OMIT_OPTIONAL_MARKER) && text6.includes(IGNORE_SCRIPTS_MARKER)
|
|
7068
7143
|
];
|
|
7069
7144
|
if (signals.filter(Boolean).length < 2) return null;
|
|
7070
7145
|
return inspectNativePackage(path);
|
|
@@ -7874,7 +7949,7 @@ function outboundHttpProxyAgent(targetUrl, env = process.env) {
|
|
|
7874
7949
|
}
|
|
7875
7950
|
return new HttpsProxyAgent(parsedProxy, { keepAlive: true });
|
|
7876
7951
|
} catch (err) {
|
|
7877
|
-
|
|
7952
|
+
emitParentNotice(
|
|
7878
7953
|
`clodex: HTTP(S)_PROXY cannot be used for a CONNECT tunnel; using a direct connection (${err instanceof Error ? err.message : String(err)})`
|
|
7879
7954
|
);
|
|
7880
7955
|
return void 0;
|
|
@@ -8893,11 +8968,11 @@ function reasoningGapShape(expected, actual, full, storedTail, index) {
|
|
|
8893
8968
|
}
|
|
8894
8969
|
var warnedReasoningGaps = /* @__PURE__ */ new Set();
|
|
8895
8970
|
var MAX_REASONING_GAP_WARNINGS = 3;
|
|
8896
|
-
function warnReasoningNormalizationGap(fields,
|
|
8971
|
+
function warnReasoningNormalizationGap(fields, log13) {
|
|
8897
8972
|
const signature = fields.join(",");
|
|
8898
8973
|
const message = `clodex: warning: a reasoning item with identical encrypted_content failed the continuation match on field(s): ${signature}. Prompt caching is degraded for this turn \u2014 this is a clodex normalization gap, please report it at https://github.com/bman654/clodex/issues`;
|
|
8899
8974
|
try {
|
|
8900
|
-
|
|
8975
|
+
log13?.(`reasoning normalization gap: ${signature}`);
|
|
8901
8976
|
} catch {
|
|
8902
8977
|
}
|
|
8903
8978
|
if (warnedReasoningGaps.has(signature)) return;
|
|
@@ -8943,12 +9018,12 @@ function toolArgumentNormalizationGap(expected, actual, defaults, requiredProps)
|
|
|
8943
9018
|
}
|
|
8944
9019
|
var warnedToolArgumentGaps = /* @__PURE__ */ new Set();
|
|
8945
9020
|
var MAX_TOOL_ARGUMENT_GAP_WARNINGS = 3;
|
|
8946
|
-
function warnToolArgumentNormalizationGap(gap,
|
|
9021
|
+
function warnToolArgumentNormalizationGap(gap, log13) {
|
|
8947
9022
|
const tool3 = typeof gap.tool === "string" ? gap.tool : "unknown";
|
|
8948
9023
|
const signature = `${tool3}:filler`;
|
|
8949
9024
|
const message = `clodex: warning: tool call "${tool3}" failed the continuation match, but both sides are identical once clodex's filler-strip rule is applied, so the head should have matched. Prompt caching is degraded for this turn \u2014 please report it, with the adapter debug log from --trace if you can, at https://github.com/bman654/clodex/issues`;
|
|
8950
9025
|
try {
|
|
8951
|
-
|
|
9026
|
+
log13?.(`tool argument normalization gap: ${signature}`);
|
|
8952
9027
|
} catch {
|
|
8953
9028
|
}
|
|
8954
9029
|
if (warnedToolArgumentGaps.has(signature)) return;
|
|
@@ -8959,7 +9034,7 @@ function warnToolArgumentNormalizationGap(gap, log12) {
|
|
|
8959
9034
|
emitParentNotice("clodex: warning: further tool-argument normalization warnings suppressed.");
|
|
8960
9035
|
}
|
|
8961
9036
|
}
|
|
8962
|
-
function continuationMismatchDetails(entry, payload, defaults,
|
|
9037
|
+
function continuationMismatchDetails(entry, payload, defaults, log13, warnOnGap = false, deferWarnings) {
|
|
8963
9038
|
const raise = (warn) => {
|
|
8964
9039
|
if (deferWarnings) deferWarnings.push(warn);
|
|
8965
9040
|
else warn();
|
|
@@ -8977,7 +9052,7 @@ function continuationMismatchDetails(entry, payload, defaults, log12, warnOnGap
|
|
|
8977
9052
|
const expected = mismatch < prefix.length ? prefix[mismatch] : void 0;
|
|
8978
9053
|
const actual = mismatch < full.length ? full[mismatch] : void 0;
|
|
8979
9054
|
const reasoningGap = reasoningNormalizationGap(expected, actual, defaults);
|
|
8980
|
-
if (reasoningGap && warnOnGap) raise(() => warnReasoningNormalizationGap(reasoningGap,
|
|
9055
|
+
if (reasoningGap && warnOnGap) raise(() => warnReasoningNormalizationGap(reasoningGap, log13));
|
|
8981
9056
|
let gapExpected = expected;
|
|
8982
9057
|
if (conversationItemKind(expected) === "reasoning" && conversationItemKind(actual) === "function_call") {
|
|
8983
9058
|
for (let index = mismatch; index < prefix.length; index += 1) {
|
|
@@ -9001,10 +9076,10 @@ function continuationMismatchDetails(entry, payload, defaults, log12, warnOnGap
|
|
|
9001
9076
|
}
|
|
9002
9077
|
if (toolArgumentGap?.equalAfterStrip === true) {
|
|
9003
9078
|
const gap = toolArgumentGap;
|
|
9004
|
-
if (warnOnGap) raise(() => warnToolArgumentNormalizationGap(gap,
|
|
9079
|
+
if (warnOnGap) raise(() => warnToolArgumentNormalizationGap(gap, log13));
|
|
9005
9080
|
} else if (toolArgumentGap && warnOnGap) {
|
|
9006
9081
|
try {
|
|
9007
|
-
|
|
9082
|
+
log13?.(`tool argument mismatch beyond the strip rule: ${String(toolArgumentGap.tool)}`);
|
|
9008
9083
|
} catch {
|
|
9009
9084
|
}
|
|
9010
9085
|
}
|
|
@@ -9029,17 +9104,17 @@ function continuationMismatchDetails(entry, payload, defaults, log12, warnOnGap
|
|
|
9029
9104
|
...toolArgumentGap ? { toolArgumentNormalizationGap: toolArgumentGap } : {}
|
|
9030
9105
|
};
|
|
9031
9106
|
}
|
|
9032
|
-
function continuationMismatchSummary(entry, payload, defaults,
|
|
9033
|
-
const details = precomputedDetails ?? continuationMismatchDetails(entry, payload, defaults,
|
|
9107
|
+
function continuationMismatchSummary(entry, payload, defaults, log13, mismatchDump = false, precomputedDetails) {
|
|
9108
|
+
const details = precomputedDetails ?? continuationMismatchDetails(entry, payload, defaults, log13, true);
|
|
9034
9109
|
let summary = `full_items=${details.fullItems} expected_prefix_items=${details.expectedPrefixItems} first_mismatch=${details.firstMismatch} expected=${details.expectedKind} actual=${details.actualKind}`;
|
|
9035
9110
|
if (details.expectedHash || details.actualHash) {
|
|
9036
9111
|
summary += ` expected_hash=${details.expectedHash ?? "none"} actual_hash=${details.actualHash ?? "none"}`;
|
|
9037
|
-
if (mismatchDump &&
|
|
9112
|
+
if (mismatchDump && log13) {
|
|
9038
9113
|
const full = inputArray(payload);
|
|
9039
9114
|
const prefix = [...entry.requestInput ?? [], ...entry.expectedAssistant ?? []];
|
|
9040
9115
|
const index = details.firstMismatch;
|
|
9041
|
-
|
|
9042
|
-
|
|
9116
|
+
log13(`mismatch dump expected[${index}]: ${mismatchDumpLine(prefix, index, defaults)}`);
|
|
9117
|
+
log13(`mismatch dump actual[${index}]: ${mismatchDumpLine(full, index, defaults)}`);
|
|
9043
9118
|
}
|
|
9044
9119
|
}
|
|
9045
9120
|
return summary;
|
|
@@ -9382,7 +9457,7 @@ function requiredToolProps(payload) {
|
|
|
9382
9457
|
if (record.type !== "function" || typeof record.name !== "string") return;
|
|
9383
9458
|
const parameters = record.parameters;
|
|
9384
9459
|
const required = parameters && typeof parameters === "object" && Array.isArray(parameters.required) ? parameters.required : [];
|
|
9385
|
-
map.set(record.name, new Set(required.filter((
|
|
9460
|
+
map.set(record.name, new Set(required.filter((p14) => typeof p14 === "string")));
|
|
9386
9461
|
};
|
|
9387
9462
|
if (Array.isArray(payload.tools)) for (const tool3 of payload.tools) add(tool3);
|
|
9388
9463
|
return map;
|
|
@@ -9411,12 +9486,12 @@ function expectedAssistantItems(ctx) {
|
|
|
9411
9486
|
const type = accumulator.type ?? (typeof done.type === "string" ? done.type : void 0);
|
|
9412
9487
|
if (type === "message") {
|
|
9413
9488
|
const doneContent = Array.isArray(done.content) ? done.content : void 0;
|
|
9414
|
-
const
|
|
9415
|
-
output.push({ role: "assistant", content: [{ type: "output_text", text:
|
|
9489
|
+
const text6 = accumulator.text || (doneContent ? doneContent.filter((part) => part && typeof part === "object" && part.type === "output_text").map((part) => String(part.text ?? "")).join("") : "");
|
|
9490
|
+
output.push({ role: "assistant", content: [{ type: "output_text", text: text6 }] });
|
|
9416
9491
|
continue;
|
|
9417
9492
|
}
|
|
9418
9493
|
if (type === "reasoning") {
|
|
9419
|
-
const summary = accumulator.summaries.size ? [...accumulator.summaries.entries()].sort(([a], [b]) => a - b).map(([,
|
|
9494
|
+
const summary = accumulator.summaries.size ? [...accumulator.summaries.entries()].sort(([a], [b]) => a - b).map(([, text6]) => ({ type: "summary_text", text: text6 })) : Array.isArray(done.summary) ? done.summary : [];
|
|
9420
9495
|
output.push({ ...withoutEphemeralFields(done), type: "reasoning", summary });
|
|
9421
9496
|
continue;
|
|
9422
9497
|
}
|
|
@@ -9706,7 +9781,7 @@ function handleSocketMessage(entry, data) {
|
|
|
9706
9781
|
if (entry.connectionDiagnostic) observeIdleFrame(entry, data);
|
|
9707
9782
|
return;
|
|
9708
9783
|
}
|
|
9709
|
-
const
|
|
9784
|
+
const text6 = Array.isArray(data) ? Buffer.concat(data).toString("utf8") : data.toString("utf8");
|
|
9710
9785
|
ctx.frameCount += 1;
|
|
9711
9786
|
if (ctx.transportRetryPending) {
|
|
9712
9787
|
ctx.transportRetryPending = false;
|
|
@@ -9718,9 +9793,9 @@ function handleSocketMessage(entry, data) {
|
|
|
9718
9793
|
}
|
|
9719
9794
|
let event;
|
|
9720
9795
|
try {
|
|
9721
|
-
event = JSON.parse(
|
|
9796
|
+
event = JSON.parse(text6);
|
|
9722
9797
|
} catch {
|
|
9723
|
-
ctx.pendingEvents.push(
|
|
9798
|
+
ctx.pendingEvents.push(text6.replace(/\r?\n/g, " "));
|
|
9724
9799
|
flushPending(ctx);
|
|
9725
9800
|
return;
|
|
9726
9801
|
}
|
|
@@ -10068,23 +10143,23 @@ function createConnection(WebSocket, wsUrl, headers, persistent, key, options, d
|
|
|
10068
10143
|
function diagnosticCap(cap) {
|
|
10069
10144
|
return Number.isFinite(cap) ? cap : null;
|
|
10070
10145
|
}
|
|
10071
|
-
function envConnectionCap(name,
|
|
10146
|
+
function envConnectionCap(name, log13) {
|
|
10072
10147
|
const raw = process.env[name];
|
|
10073
10148
|
if (raw === void 0 || raw.trim() === "") return void 0;
|
|
10074
10149
|
const value = Number(raw.trim());
|
|
10075
10150
|
if (!Number.isInteger(value) || value < 1) {
|
|
10076
10151
|
try {
|
|
10077
|
-
|
|
10152
|
+
log13?.(`ws: ignoring ${name}=${raw} (expected a positive integer)`);
|
|
10078
10153
|
} catch {
|
|
10079
10154
|
}
|
|
10080
10155
|
return void 0;
|
|
10081
10156
|
}
|
|
10082
10157
|
return value;
|
|
10083
10158
|
}
|
|
10084
|
-
function createResponsesWebSocketFetch(wsUrl,
|
|
10159
|
+
function createResponsesWebSocketFetch(wsUrl, log13, options = {}) {
|
|
10085
10160
|
const debug = (message) => {
|
|
10086
10161
|
try {
|
|
10087
|
-
|
|
10162
|
+
log13?.(`ws: ${message}`);
|
|
10088
10163
|
} catch {
|
|
10089
10164
|
}
|
|
10090
10165
|
};
|
|
@@ -10093,8 +10168,8 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
|
|
|
10093
10168
|
hardTtlMs: options.hardTtlMs ?? RESPONSES_WS_HARD_TTL_MS,
|
|
10094
10169
|
idleTtlMs: options.idleTtlMs ?? RESPONSES_WS_IDLE_TTL_MS,
|
|
10095
10170
|
nurseryIdleTtlMs: options.nurseryIdleTtlMs ?? Math.min(RESPONSES_WS_NURSERY_IDLE_TTL_MS, options.idleTtlMs ?? RESPONSES_WS_IDLE_TTL_MS),
|
|
10096
|
-
maxConnections: options.maxConnections ?? envConnectionCap("CLODEX_WS_MAX_CONNECTIONS",
|
|
10097
|
-
maxNurseryConnections: options.maxNurseryConnections ?? envConnectionCap("CLODEX_WS_MAX_NURSERY_CONNECTIONS",
|
|
10171
|
+
maxConnections: options.maxConnections ?? envConnectionCap("CLODEX_WS_MAX_CONNECTIONS", log13) ?? RESPONSES_WS_MAX_CONNECTIONS,
|
|
10172
|
+
maxNurseryConnections: options.maxNurseryConnections ?? envConnectionCap("CLODEX_WS_MAX_NURSERY_CONNECTIONS", log13) ?? RESPONSES_WS_MAX_NURSERY_CONNECTIONS,
|
|
10098
10173
|
now: options.now ?? Date.now
|
|
10099
10174
|
};
|
|
10100
10175
|
return async (_input, init) => {
|
|
@@ -10529,8 +10604,8 @@ function buildClaudeCodeBillingSystemLine() {
|
|
|
10529
10604
|
function systemBlockText(block) {
|
|
10530
10605
|
if (typeof block === "string") return block;
|
|
10531
10606
|
if (block && typeof block === "object" && "text" in block) {
|
|
10532
|
-
const
|
|
10533
|
-
return typeof
|
|
10607
|
+
const text6 = block.text;
|
|
10608
|
+
return typeof text6 === "string" ? text6 : void 0;
|
|
10534
10609
|
}
|
|
10535
10610
|
return void 0;
|
|
10536
10611
|
}
|
|
@@ -10758,7 +10833,7 @@ async function createLanguageModel(spec) {
|
|
|
10758
10833
|
apiKey: "",
|
|
10759
10834
|
...spec.headers ? { headers: spec.headers } : {},
|
|
10760
10835
|
fetch: fetchWithoutCredentialHeaders
|
|
10761
|
-
} : { apiKey, ...spec.headers ? { headers: spec.headers } : {} };
|
|
10836
|
+
} : { apiKey, ...baseURL ? { baseURL } : {}, ...spec.headers ? { headers: spec.headers } : {} };
|
|
10762
10837
|
const openai = createOpenAI(oauthOptions);
|
|
10763
10838
|
return useResponsesEndpoint ? openai.responses(modelId) : openai.chat(modelId);
|
|
10764
10839
|
}
|
|
@@ -10903,7 +10978,7 @@ function toCamelCase(str) {
|
|
|
10903
10978
|
return str.replace(/[-_]([a-z])/g, (_, g) => g.toUpperCase());
|
|
10904
10979
|
}
|
|
10905
10980
|
function hasSupportedParameter(metadata, param) {
|
|
10906
|
-
return (metadata?.supportedParameters ?? []).some((
|
|
10981
|
+
return (metadata?.supportedParameters ?? []).some((p14) => p14 === param);
|
|
10907
10982
|
}
|
|
10908
10983
|
function isOpenRouterRoute(npm, metadata) {
|
|
10909
10984
|
return npm === "@openrouter/ai-sdk-provider" || metadata?.providerId === "openrouter" || metadata?.apiBaseUrl?.includes("openrouter.ai") === true;
|
|
@@ -11862,10 +11937,10 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
|
|
|
11862
11937
|
res.end(JSON.stringify({ type: "error", error: { type: "api_error", message: "Upstream returned empty response body" } }));
|
|
11863
11938
|
return;
|
|
11864
11939
|
}
|
|
11865
|
-
let
|
|
11940
|
+
let text6 = await upstreamRes.text();
|
|
11866
11941
|
let parsed;
|
|
11867
11942
|
try {
|
|
11868
|
-
parsed = JSON.parse(
|
|
11943
|
+
parsed = JSON.parse(text6);
|
|
11869
11944
|
} catch {
|
|
11870
11945
|
res.writeHead(502, { "Content-Type": "application/json" });
|
|
11871
11946
|
res.end(JSON.stringify({ type: "error", error: { type: "api_error", message: "Upstream response was not valid JSON" } }));
|
|
@@ -11885,13 +11960,13 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
|
|
|
11885
11960
|
changed = true;
|
|
11886
11961
|
}
|
|
11887
11962
|
}
|
|
11888
|
-
if (changed)
|
|
11963
|
+
if (changed) text6 = JSON.stringify(message);
|
|
11889
11964
|
}
|
|
11890
11965
|
res.writeHead(200, {
|
|
11891
11966
|
"Content-Type": "application/json",
|
|
11892
|
-
"Content-Length": Buffer.byteLength(
|
|
11967
|
+
"Content-Length": Buffer.byteLength(text6).toString()
|
|
11893
11968
|
});
|
|
11894
|
-
res.end(
|
|
11969
|
+
res.end(text6);
|
|
11895
11970
|
}
|
|
11896
11971
|
|
|
11897
11972
|
// src/proxy.ts
|
|
@@ -12243,12 +12318,12 @@ var OpenAiThinkingBlock = class {
|
|
|
12243
12318
|
append(part) {
|
|
12244
12319
|
const entry = this.parts.get(part.id ?? "");
|
|
12245
12320
|
if (!entry) throw new Error("OpenAI reasoning delta has no matching start");
|
|
12246
|
-
const
|
|
12247
|
-
if (!
|
|
12321
|
+
const text6 = part.text ?? "";
|
|
12322
|
+
if (!text6) return "";
|
|
12248
12323
|
const separator = entry.text.length === 0 && this.hasText ? "\n\n" : "";
|
|
12249
|
-
entry.text +=
|
|
12324
|
+
entry.text += text6;
|
|
12250
12325
|
this.hasText = true;
|
|
12251
|
-
return separator +
|
|
12326
|
+
return separator + text6;
|
|
12252
12327
|
}
|
|
12253
12328
|
end(part) {
|
|
12254
12329
|
const entry = this.parts.get(part.id ?? "");
|
|
@@ -12262,7 +12337,7 @@ var OpenAiThinkingBlock = class {
|
|
|
12262
12337
|
function isRecord(value) {
|
|
12263
12338
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
12264
12339
|
}
|
|
12265
|
-
function restoreOpenAiThinking(
|
|
12340
|
+
function restoreOpenAiThinking(text6, signature, npm) {
|
|
12266
12341
|
if (!signature?.startsWith(SIGNATURE_PREFIX)) return void 0;
|
|
12267
12342
|
if (!signature.startsWith(SIGNATURE_V1)) return [];
|
|
12268
12343
|
try {
|
|
@@ -12273,7 +12348,7 @@ function restoreOpenAiThinking(text5, signature, npm) {
|
|
|
12273
12348
|
if (!isRecord(part) || typeof part.itemId !== "string" || !part.itemId || typeof part.text !== "string" || part.encryptedContent !== void 0 && typeof part.encryptedContent !== "string") return [];
|
|
12274
12349
|
parts.push(part);
|
|
12275
12350
|
}
|
|
12276
|
-
if (npm !== "@ai-sdk/openai") return
|
|
12351
|
+
if (npm !== "@ai-sdk/openai") return text6 ? [{ type: "reasoning", text: text6 }] : [];
|
|
12277
12352
|
return parts.map((part) => ({
|
|
12278
12353
|
type: "reasoning",
|
|
12279
12354
|
text: part.text,
|
|
@@ -12347,10 +12422,10 @@ function supportsOpenAiPromptCacheBreakpoints(modelId) {
|
|
|
12347
12422
|
const minor = Number(match[2] ?? 0);
|
|
12348
12423
|
return major > 5 || major === 5 && minor >= 6;
|
|
12349
12424
|
}
|
|
12350
|
-
function stripClaudeCodeBillingHeader(
|
|
12351
|
-
if (!
|
|
12352
|
-
const newline =
|
|
12353
|
-
return newline === -1 ? void 0 :
|
|
12425
|
+
function stripClaudeCodeBillingHeader(text6) {
|
|
12426
|
+
if (!text6.startsWith(CLAUDE_CODE_BILLING_HEADER_PREFIX)) return text6;
|
|
12427
|
+
const newline = text6.indexOf("\n");
|
|
12428
|
+
return newline === -1 ? void 0 : text6.slice(newline + 1);
|
|
12354
12429
|
}
|
|
12355
12430
|
function systemToString(system, stripAnthropicBillingHeader = false) {
|
|
12356
12431
|
if (!system) return void 0;
|
|
@@ -12359,8 +12434,8 @@ function systemToString(system, stripAnthropicBillingHeader = false) {
|
|
|
12359
12434
|
}
|
|
12360
12435
|
const blocks = system.map((b) => typeof b === "string" ? b : b.text ?? "");
|
|
12361
12436
|
if (!stripAnthropicBillingHeader) return blocks.join("\n");
|
|
12362
|
-
return blocks.flatMap((
|
|
12363
|
-
const stripped = stripClaudeCodeBillingHeader(
|
|
12437
|
+
return blocks.flatMap((text6) => {
|
|
12438
|
+
const stripped = stripClaudeCodeBillingHeader(text6);
|
|
12364
12439
|
return stripped === void 0 ? [] : [stripped];
|
|
12365
12440
|
}).join("\n");
|
|
12366
12441
|
}
|
|
@@ -12376,12 +12451,12 @@ function translateTopLevelSystemForOpenAi(system) {
|
|
|
12376
12451
|
}
|
|
12377
12452
|
return system.flatMap((block) => {
|
|
12378
12453
|
const raw = typeof block === "string" ? block : block.text ?? "";
|
|
12379
|
-
const
|
|
12380
|
-
if (!
|
|
12454
|
+
const text6 = stripClaudeCodeBillingHeader(raw) ?? "";
|
|
12455
|
+
if (!text6.trim()) return [];
|
|
12381
12456
|
const cacheControl = typeof block === "string" ? void 0 : block.cache_control;
|
|
12382
12457
|
return [{
|
|
12383
12458
|
role: "system",
|
|
12384
|
-
content:
|
|
12459
|
+
content: text6,
|
|
12385
12460
|
...cacheControl ? { providerOptions: { openai: { promptCacheBreakpoint: { mode: "explicit" } } } } : {}
|
|
12386
12461
|
}];
|
|
12387
12462
|
});
|
|
@@ -12438,9 +12513,9 @@ function annotateToolNames(messages) {
|
|
|
12438
12513
|
}
|
|
12439
12514
|
}
|
|
12440
12515
|
function thinkingToSdkPart(block, npm) {
|
|
12441
|
-
const
|
|
12442
|
-
if (npm === "@ai-sdk/openai" && !block.signature && !
|
|
12443
|
-
const part = { type: "reasoning", text:
|
|
12516
|
+
const text6 = block.thinking ?? "";
|
|
12517
|
+
if (npm === "@ai-sdk/openai" && !block.signature && !text6.trim()) return null;
|
|
12518
|
+
const part = { type: "reasoning", text: text6 };
|
|
12444
12519
|
if (block.signature) {
|
|
12445
12520
|
if (npm === "@ai-sdk/google") {
|
|
12446
12521
|
part.providerOptions = { google: { thoughtSignature: block.signature } };
|
|
@@ -12475,10 +12550,10 @@ function translateMessages(messages, npm, openAiPromptCacheBreakpoints = false)
|
|
|
12475
12550
|
...openAiCacheBreakpoint(b, openAiPromptCacheBreakpoints) ? { providerOptions: openAiCacheBreakpoint(b, openAiPromptCacheBreakpoints) } : {}
|
|
12476
12551
|
});
|
|
12477
12552
|
} else if (b.type === "image") {
|
|
12478
|
-
const
|
|
12479
|
-
if (
|
|
12553
|
+
const p14 = imagePart(b);
|
|
12554
|
+
if (p14) {
|
|
12480
12555
|
parts.push({
|
|
12481
|
-
...
|
|
12556
|
+
...p14,
|
|
12482
12557
|
...openAiCacheBreakpoint(b, openAiPromptCacheBreakpoints) ? { providerOptions: openAiCacheBreakpoint(b, openAiPromptCacheBreakpoints) } : {}
|
|
12483
12558
|
});
|
|
12484
12559
|
}
|
|
@@ -12531,10 +12606,10 @@ function translateMessages(messages, npm, openAiPromptCacheBreakpoints = false)
|
|
|
12531
12606
|
function isPlainObject(value) {
|
|
12532
12607
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
12533
12608
|
}
|
|
12534
|
-
function parsesAsJson(
|
|
12535
|
-
if (
|
|
12609
|
+
function parsesAsJson(text6) {
|
|
12610
|
+
if (text6.trim() === "") return true;
|
|
12536
12611
|
try {
|
|
12537
|
-
JSON.parse(
|
|
12612
|
+
JSON.parse(text6);
|
|
12538
12613
|
return true;
|
|
12539
12614
|
} catch {
|
|
12540
12615
|
return false;
|
|
@@ -12583,7 +12658,7 @@ function isClaudeCodeCompactRequest(body) {
|
|
|
12583
12658
|
const finalMessage = body.messages.at(-1);
|
|
12584
12659
|
if (!finalMessage || finalMessage.role !== "user") return false;
|
|
12585
12660
|
const texts = typeof finalMessage.content === "string" ? [finalMessage.content] : finalMessage.content.filter((block) => block.type === "text").map((block) => block.text ?? "");
|
|
12586
|
-
return texts.some((
|
|
12661
|
+
return texts.some((text6) => text6.startsWith(COMPACT_TEXT_ONLY_START) && text6.includes(COMPACT_TEXT_ONLY_END));
|
|
12587
12662
|
}
|
|
12588
12663
|
var COMPACT_DRIFT_ANCHOR = "Tool calls will be REJECTED and will waste your only turn";
|
|
12589
12664
|
var COMPACT_DRIFT_ANCHOR_LINE = new RegExp(
|
|
@@ -12595,25 +12670,25 @@ function looksLikeDriftedClaudeCodeCompactRequest(body) {
|
|
|
12595
12670
|
const finalMessage = body.messages.at(-1);
|
|
12596
12671
|
if (!finalMessage || finalMessage.role !== "user") return false;
|
|
12597
12672
|
const texts = typeof finalMessage.content === "string" ? [finalMessage.content] : finalMessage.content.filter((block) => block.type === "text").map((block) => block.text ?? "");
|
|
12598
|
-
return texts.some((
|
|
12673
|
+
return texts.some((text6) => !text6.startsWith(COMPACT_TEXT_ONLY_START) && COMPACT_DRIFT_OPENING.test(text6) && COMPACT_DRIFT_ANCHOR_LINE.test(text6));
|
|
12599
12674
|
}
|
|
12600
12675
|
function claudeCodeVersionFromRequest(body) {
|
|
12601
12676
|
const texts = typeof body.system === "string" ? [body.system] : (body.system ?? []).map((block) => typeof block === "string" ? block : block.text ?? "");
|
|
12602
|
-
for (const
|
|
12603
|
-
if (!
|
|
12604
|
-
const match =
|
|
12677
|
+
for (const text6 of texts) {
|
|
12678
|
+
if (!text6.startsWith(CLAUDE_CODE_BILLING_HEADER_PREFIX)) continue;
|
|
12679
|
+
const match = text6.match(/\bcc_version=([0-9A-Za-z][0-9A-Za-z._+-]{0,63})(?:;|\s|$)/);
|
|
12605
12680
|
if (match) return match[1];
|
|
12606
12681
|
}
|
|
12607
12682
|
return void 0;
|
|
12608
12683
|
}
|
|
12609
12684
|
var warnedCompactPromptDrifts = /* @__PURE__ */ new Set();
|
|
12610
12685
|
var MAX_COMPACT_PROMPT_DRIFT_WARNINGS = 3;
|
|
12611
|
-
function reportClaudeCodeCompactPromptDrift(body,
|
|
12686
|
+
function reportClaudeCodeCompactPromptDrift(body, log13) {
|
|
12612
12687
|
if (!looksLikeDriftedClaudeCodeCompactRequest(body)) return;
|
|
12613
12688
|
const version = claudeCodeVersionFromRequest(body);
|
|
12614
12689
|
const signature = version ?? "unknown-version";
|
|
12615
12690
|
try {
|
|
12616
|
-
|
|
12691
|
+
log13?.(`possible Claude Code compact prompt drift: ${signature}`);
|
|
12617
12692
|
} catch {
|
|
12618
12693
|
}
|
|
12619
12694
|
if (warnedCompactPromptDrifts.has(signature)) return;
|
|
@@ -12749,7 +12824,7 @@ function forwardAbortSignal(source, target) {
|
|
|
12749
12824
|
function translatedMessageId() {
|
|
12750
12825
|
return "clodex_" + randomUUID5().replace(/-/g, "");
|
|
12751
12826
|
}
|
|
12752
|
-
async function writeAnthropicStream(stream, modelId, write,
|
|
12827
|
+
async function writeAnthropicStream(stream, modelId, write, log13, observer, tools) {
|
|
12753
12828
|
const messageId = translatedMessageId();
|
|
12754
12829
|
const requiredProps = toolRequiredProps(tools);
|
|
12755
12830
|
let blockIndex = -1;
|
|
@@ -12946,7 +13021,7 @@ async function writeAnthropicStream(stream, modelId, write, log12, observer, too
|
|
|
12946
13021
|
const errMsg = e?.message || (typeof part.error === "string" ? part.error : JSON.stringify(e?.data ?? part.error));
|
|
12947
13022
|
const transportCode = sdkUpstreamErrorDetails(part.error)?.transportCode;
|
|
12948
13023
|
const errorType = anthropicErrorType(upstreamHttpStatus(part.error, errMsg), transportCode);
|
|
12949
|
-
|
|
13024
|
+
log13?.(() => `sdk stream error (${errorType}): ${errMsg}`);
|
|
12950
13025
|
if (!(transportCode === "websocket_transport_error" && openType === "thinking")) closeOpen();
|
|
12951
13026
|
throw part.error instanceof Error || part.error && typeof part.error === "object" ? part.error : new Error(errMsg);
|
|
12952
13027
|
}
|
|
@@ -12960,7 +13035,7 @@ async function writeAnthropicStream(stream, modelId, write, log12, observer, too
|
|
|
12960
13035
|
emit("message_delta", { type: "message_delta", delta: { stop_reason: finishReason, stop_sequence: null }, usage });
|
|
12961
13036
|
emit("message_stop", { type: "message_stop" });
|
|
12962
13037
|
}
|
|
12963
|
-
async function streamAnthropicResponse(model, params, modelId, write,
|
|
13038
|
+
async function streamAnthropicResponse(model, params, modelId, write, log13, observer) {
|
|
12964
13039
|
const { idleTimeoutMs, totalTimeoutMs, maxRetries } = upstreamRequestBudget({
|
|
12965
13040
|
idleTimeoutMs: observer?.idleTimeoutMs
|
|
12966
13041
|
});
|
|
@@ -12999,7 +13074,7 @@ async function streamAnthropicResponse(model, params, modelId, write, log12, obs
|
|
|
12999
13074
|
clearTimeout(idleTimer);
|
|
13000
13075
|
}
|
|
13001
13076
|
})();
|
|
13002
|
-
await writeAnthropicStream(watchedStream, modelId, write,
|
|
13077
|
+
await writeAnthropicStream(watchedStream, modelId, write, log13, { ...observer, abortSignal }, params.tools);
|
|
13003
13078
|
} finally {
|
|
13004
13079
|
stopForwardingAbort();
|
|
13005
13080
|
clearTimeout(idleTimer);
|
|
@@ -13008,7 +13083,7 @@ async function streamAnthropicResponse(model, params, modelId, write, log12, obs
|
|
|
13008
13083
|
}
|
|
13009
13084
|
}
|
|
13010
13085
|
async function generateAnthropicResponse(model, params, modelId, options) {
|
|
13011
|
-
let
|
|
13086
|
+
let text6;
|
|
13012
13087
|
let toolCalls;
|
|
13013
13088
|
let finishReason;
|
|
13014
13089
|
let usage;
|
|
@@ -13074,7 +13149,7 @@ async function generateAnthropicResponse(model, params, modelId, options) {
|
|
|
13074
13149
|
clearTimeout(totalTimer);
|
|
13075
13150
|
if (!forceAbort.signal.aborted) forceAbort.abort();
|
|
13076
13151
|
}
|
|
13077
|
-
|
|
13152
|
+
text6 = streamedText.join("");
|
|
13078
13153
|
toolCalls = streamedToolCalls;
|
|
13079
13154
|
finishReason = streamedFinishReason;
|
|
13080
13155
|
usage = streamedUsage;
|
|
@@ -13094,7 +13169,7 @@ async function generateAnthropicResponse(model, params, modelId, options) {
|
|
|
13094
13169
|
maxRetries,
|
|
13095
13170
|
abortSignal: generateAbort.signal
|
|
13096
13171
|
});
|
|
13097
|
-
({ text:
|
|
13172
|
+
({ text: text6, toolCalls, finishReason, usage, warnings } = r);
|
|
13098
13173
|
} catch (error) {
|
|
13099
13174
|
if (generateAbort.signal.aborted) throw streamAbortError(generateAbort.signal);
|
|
13100
13175
|
throw error;
|
|
@@ -13113,7 +13188,7 @@ async function generateAnthropicResponse(model, params, modelId, options) {
|
|
|
13113
13188
|
role: "assistant",
|
|
13114
13189
|
model: modelId,
|
|
13115
13190
|
content: [
|
|
13116
|
-
...
|
|
13191
|
+
...text6 ? [{ type: "text", text: text6 }] : [],
|
|
13117
13192
|
...toolCalls.map((tc) => ({
|
|
13118
13193
|
type: "tool_use",
|
|
13119
13194
|
id: encodeToolUseId(tc.toolCallId, grabRoundTripSignature(tc)),
|
|
@@ -14090,7 +14165,7 @@ function buildDesiredPatchConfig(options = {}) {
|
|
|
14090
14165
|
compatibility: model.compatibility,
|
|
14091
14166
|
upstreamModelId: upstreamModelId2
|
|
14092
14167
|
});
|
|
14093
|
-
const limits = contextLimitsFrom(model,
|
|
14168
|
+
const limits = contextLimitsFrom(model, lookupKnownContextWindow(model.id));
|
|
14094
14169
|
const stop = resolveContextStop(
|
|
14095
14170
|
limits,
|
|
14096
14171
|
options.sessionStops ? selectContextStop(provider.id, model.id, prefs.modelContextModes) : readContextStop(prefs.modelContextModes, provider.id, model.id) ?? "standard"
|
|
@@ -14659,7 +14734,9 @@ async function runPatchCommand(opts = {}) {
|
|
|
14659
14734
|
return 1;
|
|
14660
14735
|
}
|
|
14661
14736
|
for (const id of desired.unknownWindows) {
|
|
14662
|
-
p2.log.warn(
|
|
14737
|
+
p2.log.warn(
|
|
14738
|
+
`No context window metadata for ${id} \u2014 Claude Code will assume the 200k default. Refresh the provider's models, then set a window with \`clodex models --context <model=stop> --save\`, then re-run \`clodex patch\`.`
|
|
14739
|
+
);
|
|
14663
14740
|
}
|
|
14664
14741
|
reportRejectedModelAliases(desired.rejectedAliasRejections);
|
|
14665
14742
|
const localPatches = inspectLocalPatchSource(
|
|
@@ -14812,11 +14889,11 @@ function formatModelMetadata(entries) {
|
|
|
14812
14889
|
|
|
14813
14890
|
// src/first-run.ts
|
|
14814
14891
|
import pc7 from "picocolors";
|
|
14815
|
-
import * as
|
|
14892
|
+
import * as p7 from "@clack/prompts";
|
|
14816
14893
|
|
|
14817
14894
|
// src/providers-command.ts
|
|
14818
14895
|
import pc6 from "picocolors";
|
|
14819
|
-
import * as
|
|
14896
|
+
import * as p6 from "@clack/prompts";
|
|
14820
14897
|
|
|
14821
14898
|
// src/registry/import-build.ts
|
|
14822
14899
|
function oauthAuthRef(providerId) {
|
|
@@ -14887,8 +14964,8 @@ async function fetchProviderCatalog(opts) {
|
|
|
14887
14964
|
return loadRegistryProviders(warn, { ...opts, warn });
|
|
14888
14965
|
}
|
|
14889
14966
|
function providersForPicker(providers) {
|
|
14890
|
-
for (const
|
|
14891
|
-
|
|
14967
|
+
for (const p14 of providers) {
|
|
14968
|
+
p14.models.sort((a, b) => {
|
|
14892
14969
|
const nameA = a.name || a.id;
|
|
14893
14970
|
const nameB = b.name || b.id;
|
|
14894
14971
|
return nameA.localeCompare(nameB, void 0, { sensitivity: "base", numeric: true });
|
|
@@ -14904,7 +14981,7 @@ async function resolveLocalProviderApiKey(provider) {
|
|
|
14904
14981
|
if (template?.apiKeyOptional || template?.anonymousFreeModels) {
|
|
14905
14982
|
return "";
|
|
14906
14983
|
}
|
|
14907
|
-
const reg = loadRegistry().providers.find((
|
|
14984
|
+
const reg = loadRegistry().providers.find((p14) => p14.id === provider.id);
|
|
14908
14985
|
const authRef = provider.authRef ?? reg?.authRef ?? oauthAuthRef(provider.id);
|
|
14909
14986
|
return resolveProviderCredential(provider.id, authRef);
|
|
14910
14987
|
}
|
|
@@ -15545,7 +15622,7 @@ async function addProviderFromTemplate(template, apiKey, opts) {
|
|
|
15545
15622
|
}
|
|
15546
15623
|
};
|
|
15547
15624
|
if (existing) {
|
|
15548
|
-
const idx = registry.providers.findIndex((
|
|
15625
|
+
const idx = registry.providers.findIndex((p14) => p14.id === template.id);
|
|
15549
15626
|
registry.providers[idx] = entry;
|
|
15550
15627
|
} else {
|
|
15551
15628
|
registry.providers.push(entry);
|
|
@@ -15593,127 +15670,8 @@ async function addProviderFromTemplate(template, apiKey, opts) {
|
|
|
15593
15670
|
return result;
|
|
15594
15671
|
}
|
|
15595
15672
|
|
|
15596
|
-
// src/
|
|
15597
|
-
|
|
15598
|
-
const removal = await withRegistryWriteLock(async () => {
|
|
15599
|
-
const registry = loadRegistryStrict();
|
|
15600
|
-
const index = registry.providers.findIndex((p13) => p13.id === id);
|
|
15601
|
-
if (index < 0) {
|
|
15602
|
-
return {
|
|
15603
|
-
result: {
|
|
15604
|
-
removed: false,
|
|
15605
|
-
id,
|
|
15606
|
-
credentialDeleted: false,
|
|
15607
|
-
error: `Provider not found: ${id}`
|
|
15608
|
-
},
|
|
15609
|
-
queuedRefs: []
|
|
15610
|
-
};
|
|
15611
|
-
}
|
|
15612
|
-
const [removedProvider] = registry.providers.splice(index, 1);
|
|
15613
|
-
const queuedRefs = [];
|
|
15614
|
-
if (opts?.deleteCredential !== false) {
|
|
15615
|
-
const credentialRefs = /* @__PURE__ */ new Set([
|
|
15616
|
-
removedProvider.authRef,
|
|
15617
|
-
...removedProvider.defaultAuthRef ? [removedProvider.defaultAuthRef] : [],
|
|
15618
|
-
...Object.values(removedProvider.authAccounts ?? {}).map((slot) => slot.authRef)
|
|
15619
|
-
]);
|
|
15620
|
-
for (const authRef of credentialRefs) {
|
|
15621
|
-
if (await queueCredentialDelete(authRef)) queuedRefs.push(authRef);
|
|
15622
|
-
}
|
|
15623
|
-
}
|
|
15624
|
-
saveRegistry(registry);
|
|
15625
|
-
return {
|
|
15626
|
-
result: {
|
|
15627
|
-
removed: true,
|
|
15628
|
-
id,
|
|
15629
|
-
name: removedProvider.name,
|
|
15630
|
-
credentialDeleted: false
|
|
15631
|
-
},
|
|
15632
|
-
queuedRefs
|
|
15633
|
-
};
|
|
15634
|
-
});
|
|
15635
|
-
if (removal.queuedRefs.length > 0) {
|
|
15636
|
-
try {
|
|
15637
|
-
const cleanup = await reconcilePendingCredentialDeletes();
|
|
15638
|
-
removal.result.credentialDeleted = removal.queuedRefs.every((ref) => cleanup.deleted.includes(ref));
|
|
15639
|
-
removal.result.credentialCleanupPending = removal.queuedRefs.some((ref) => cleanup.pending.includes(ref)) || cleanup.persistenceError !== void 0;
|
|
15640
|
-
} catch {
|
|
15641
|
-
removal.result.credentialCleanupPending = true;
|
|
15642
|
-
}
|
|
15643
|
-
removal.result.credentialCleanupReconciled = true;
|
|
15644
|
-
}
|
|
15645
|
-
return removal.result;
|
|
15646
|
-
}
|
|
15647
|
-
async function removeProviderFromRegistry(id, opts) {
|
|
15648
|
-
return withProviderMutationLock(id, () => removeProviderWithinLifecycle(id, opts));
|
|
15649
|
-
}
|
|
15650
|
-
async function setActiveOAuthAccount(id, account) {
|
|
15651
|
-
return withProviderMutationLock(id, () => withRegistryWriteLock(() => {
|
|
15652
|
-
const registry = loadRegistryStrict();
|
|
15653
|
-
const provider = registry.providers.find((p13) => p13.id === id);
|
|
15654
|
-
if (!provider) return { updated: false, error: `Provider not found: ${id}` };
|
|
15655
|
-
const name = account?.trim().toLowerCase();
|
|
15656
|
-
const previous = provider.activeAuthAccount?.trim() || void 0;
|
|
15657
|
-
const selectedSlot = name ? getOAuthAccountSlot(provider, name) : void 0;
|
|
15658
|
-
let selectionChanged = previous !== name;
|
|
15659
|
-
let storageChanged = false;
|
|
15660
|
-
if (name && !selectedSlot) {
|
|
15661
|
-
const slots = provider.authAccounts ?? {};
|
|
15662
|
-
const available = Object.keys(slots).sort().join(", ") || "none";
|
|
15663
|
-
return {
|
|
15664
|
-
updated: false,
|
|
15665
|
-
error: `${provider.name} has no account named "${name}" (available: ${available}).`
|
|
15666
|
-
};
|
|
15667
|
-
}
|
|
15668
|
-
if (name && provider.authType === "oauth") {
|
|
15669
|
-
storageChanged = storeActiveOAuthAccount(provider, name, selectedSlot.authRef);
|
|
15670
|
-
}
|
|
15671
|
-
if (selectionChanged && provider.authType === "oauth") {
|
|
15672
|
-
const previousSlot = previous ? getOAuthAccountSlot(provider, previous) : void 0;
|
|
15673
|
-
if (previous && previousSlot && provider.modelsCache && provider.authAccounts) {
|
|
15674
|
-
provider.authAccounts[previous] = {
|
|
15675
|
-
...previousSlot,
|
|
15676
|
-
modelsCache: provider.modelsCache
|
|
15677
|
-
};
|
|
15678
|
-
}
|
|
15679
|
-
const selectedCache = selectedSlot?.modelsCache;
|
|
15680
|
-
if (selectedCache) {
|
|
15681
|
-
provider.modelsCache = selectedCache;
|
|
15682
|
-
provider.refreshedAt = selectedCache.fetchedAt;
|
|
15683
|
-
} else {
|
|
15684
|
-
delete provider.modelsCache;
|
|
15685
|
-
delete provider.refreshedAt;
|
|
15686
|
-
}
|
|
15687
|
-
}
|
|
15688
|
-
if (name && provider.authType !== "oauth") {
|
|
15689
|
-
storageChanged = clearActiveOAuthAccount(provider) || storageChanged;
|
|
15690
|
-
if (provider.activeAuthAccount !== name) {
|
|
15691
|
-
provider.activeAuthAccount = name;
|
|
15692
|
-
storageChanged = true;
|
|
15693
|
-
}
|
|
15694
|
-
} else if (!name && (previous !== void 0 || provider.defaultAuthRef !== void 0)) {
|
|
15695
|
-
storageChanged = clearActiveOAuthAccount(provider);
|
|
15696
|
-
} else if (!name) {
|
|
15697
|
-
selectionChanged = false;
|
|
15698
|
-
}
|
|
15699
|
-
const migrationNeedsPersistence = registry.schemaVersion < REGISTRY_SCHEMA_VERSION_WITH_MATERIALIZED_ACTIVE_ACCOUNT && provider.defaultAuthRef !== void 0;
|
|
15700
|
-
if (selectionChanged || storageChanged || migrationNeedsPersistence) saveRegistry(registry);
|
|
15701
|
-
return { updated: true, changed: selectionChanged, ...name ? { account: name } : {}, provider };
|
|
15702
|
-
}));
|
|
15703
|
-
}
|
|
15704
|
-
function toggleProviderEnabled(id) {
|
|
15705
|
-
return withRegistryWriteLockSync(() => {
|
|
15706
|
-
const registry = loadRegistryStrict();
|
|
15707
|
-
const provider = registry.providers.find((p13) => p13.id === id);
|
|
15708
|
-
if (!provider) return { toggled: false, error: `Provider not found: ${id}` };
|
|
15709
|
-
provider.enabled = !provider.enabled;
|
|
15710
|
-
saveRegistry(registry);
|
|
15711
|
-
return { toggled: true, enabled: provider.enabled };
|
|
15712
|
-
});
|
|
15713
|
-
}
|
|
15714
|
-
|
|
15715
|
-
// src/registry/refresh-models.ts
|
|
15716
|
-
import { isDeepStrictEqual } from "util";
|
|
15673
|
+
// src/providers-custom-add.ts
|
|
15674
|
+
import * as p3 from "@clack/prompts";
|
|
15717
15675
|
|
|
15718
15676
|
// src/registry/url-security.ts
|
|
15719
15677
|
import { lookup } from "dns/promises";
|
|
@@ -15828,6 +15786,13 @@ async function validateCustomEndpointUrl(rawUrl, opts = {}) {
|
|
|
15828
15786
|
}
|
|
15829
15787
|
|
|
15830
15788
|
// src/registry/custom-endpoint.ts
|
|
15789
|
+
var CUSTOM_PROVIDER_ALLOCATION_SLOT = "custom-provider-allocation";
|
|
15790
|
+
function npmForKind(kind) {
|
|
15791
|
+
return kind === "anthropic" ? "@ai-sdk/anthropic" : "@ai-sdk/openai-compatible";
|
|
15792
|
+
}
|
|
15793
|
+
function modelFormatForKind(kind) {
|
|
15794
|
+
return kind === "anthropic" ? "anthropic" : "openai";
|
|
15795
|
+
}
|
|
15831
15796
|
async function fetchAnthropicModels(baseUrl, apiKey, extraHeaders) {
|
|
15832
15797
|
const root = baseUrl.replace(/\/v1\/?$/, "").replace(/\/$/, "");
|
|
15833
15798
|
const modelsUrl2 = `${root}/v1/models`;
|
|
@@ -15867,13 +15832,14 @@ async function fetchAnthropicModels(baseUrl, apiKey, extraHeaders) {
|
|
|
15867
15832
|
for (const row of json.data ?? []) {
|
|
15868
15833
|
const id = row.id?.trim();
|
|
15869
15834
|
if (!id) continue;
|
|
15835
|
+
if (hasControlChars(id) || typeof row.name === "string" && hasControlChars(row.name.trim())) continue;
|
|
15870
15836
|
models.push({
|
|
15871
15837
|
id,
|
|
15872
15838
|
name: row.name?.trim() || id,
|
|
15873
15839
|
upstreamModelId: id,
|
|
15874
15840
|
family: id.split("-")[0] ?? id,
|
|
15875
15841
|
brand: deriveBrand(id),
|
|
15876
|
-
contextWindow:
|
|
15842
|
+
contextWindow: lookupKnownContextWindow(id),
|
|
15877
15843
|
modelFormat: "anthropic",
|
|
15878
15844
|
npm: "@ai-sdk/anthropic",
|
|
15879
15845
|
apiUrl: root
|
|
@@ -15901,6 +15867,325 @@ async function fetchAnthropicModels(baseUrl, apiKey, extraHeaders) {
|
|
|
15901
15867
|
clearTimeout(timer);
|
|
15902
15868
|
}
|
|
15903
15869
|
}
|
|
15870
|
+
function uniqueProviderId(displayName, registry) {
|
|
15871
|
+
let base = customProviderId(displayName);
|
|
15872
|
+
if (!base.startsWith("custom-")) base = `custom-${slugifyProviderId(displayName)}`;
|
|
15873
|
+
if (!isValidProviderId(base)) base = "custom-provider";
|
|
15874
|
+
if (!registry.providers.some((p14) => p14.id === base)) return base;
|
|
15875
|
+
for (let i = 2; ; i++) {
|
|
15876
|
+
const candidate = `${base}-${i}`;
|
|
15877
|
+
if (isValidProviderId(candidate) && !registry.providers.some((p14) => p14.id === candidate)) {
|
|
15878
|
+
return candidate;
|
|
15879
|
+
}
|
|
15880
|
+
}
|
|
15881
|
+
}
|
|
15882
|
+
async function addCustomEndpointProvider(input) {
|
|
15883
|
+
const urlCheck = await validateCustomEndpointUrl(input.baseUrl, {
|
|
15884
|
+
allowInsecureLocal: input.allowInsecureLocal
|
|
15885
|
+
});
|
|
15886
|
+
if (!urlCheck.ok || !urlCheck.normalizedUrl) {
|
|
15887
|
+
return { added: false, error: urlCheck.error, hint: urlCheck.hint };
|
|
15888
|
+
}
|
|
15889
|
+
const normalizedUrl = urlCheck.normalizedUrl;
|
|
15890
|
+
const displayName = input.displayName.trim();
|
|
15891
|
+
const probeProviderId = uniqueProviderId(displayName, { providers: [] });
|
|
15892
|
+
const npm = npmForKind(input.kind);
|
|
15893
|
+
const apiKey = input.apiKey.trim();
|
|
15894
|
+
const anonymous = apiKey.length === 0;
|
|
15895
|
+
const headers = input.headers && Object.keys(input.headers).length > 0 ? input.headers : void 0;
|
|
15896
|
+
let fetched;
|
|
15897
|
+
if (input.kind === "anthropic") {
|
|
15898
|
+
fetched = await fetchAnthropicModels(normalizedUrl, apiKey, headers);
|
|
15899
|
+
} else {
|
|
15900
|
+
fetched = await fetchTemplateModels(
|
|
15901
|
+
{
|
|
15902
|
+
id: probeProviderId,
|
|
15903
|
+
name: displayName,
|
|
15904
|
+
authType: anonymous ? "none" : "api",
|
|
15905
|
+
npm,
|
|
15906
|
+
defaultBaseUrl: normalizedUrl,
|
|
15907
|
+
modelSource: "api-list",
|
|
15908
|
+
supported: true
|
|
15909
|
+
},
|
|
15910
|
+
apiKey,
|
|
15911
|
+
normalizedUrl,
|
|
15912
|
+
headers
|
|
15913
|
+
);
|
|
15914
|
+
}
|
|
15915
|
+
if (fetched.error || fetched.models.length === 0) {
|
|
15916
|
+
return { added: false, error: fetched.error ?? "No models returned.", hint: fetched.hint };
|
|
15917
|
+
}
|
|
15918
|
+
const result = await withProviderMutationLock(CUSTOM_PROVIDER_ALLOCATION_SLOT, async () => {
|
|
15919
|
+
const providerId = await withRegistryWriteLock(
|
|
15920
|
+
() => uniqueProviderId(displayName, loadRegistryStrict())
|
|
15921
|
+
);
|
|
15922
|
+
const account = `provider:${providerId}`;
|
|
15923
|
+
const authRef = anonymous ? "none:anonymous" : credentialInstanceAuthRef(account);
|
|
15924
|
+
const persistAndCommit = async () => {
|
|
15925
|
+
if (!anonymous) {
|
|
15926
|
+
await journalCredentialWrite(authRef);
|
|
15927
|
+
const saved = await provisionProviderCredential(authRef, apiKey);
|
|
15928
|
+
if (!saved) {
|
|
15929
|
+
return {
|
|
15930
|
+
added: false,
|
|
15931
|
+
error: "Could not save API key to the credential store.",
|
|
15932
|
+
hint: "Check credential-store access and try again."
|
|
15933
|
+
};
|
|
15934
|
+
}
|
|
15935
|
+
}
|
|
15936
|
+
return withRegistryWriteLock(async () => {
|
|
15937
|
+
const registry = loadRegistryStrict();
|
|
15938
|
+
if (registry.providers.some((provider) => provider.id === providerId)) {
|
|
15939
|
+
return {
|
|
15940
|
+
added: false,
|
|
15941
|
+
error: `Provider ID ${providerId} changed while its credential was being saved.`,
|
|
15942
|
+
hint: "Retry adding the custom provider."
|
|
15943
|
+
};
|
|
15944
|
+
}
|
|
15945
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
15946
|
+
const entry = {
|
|
15947
|
+
id: providerId,
|
|
15948
|
+
templateId: input.kind === "anthropic" ? "custom-anthropic" : "custom-openai",
|
|
15949
|
+
name: displayName,
|
|
15950
|
+
enabled: true,
|
|
15951
|
+
authRef,
|
|
15952
|
+
authType: anonymous ? "none" : "api",
|
|
15953
|
+
api: { npm, url: fetched.baseUrl, ...headers ? { headers } : {} },
|
|
15954
|
+
addedAt: now,
|
|
15955
|
+
refreshedAt: now,
|
|
15956
|
+
modelsCache: {
|
|
15957
|
+
fetchedAt: now,
|
|
15958
|
+
models: fetched.models.map((m) => ({
|
|
15959
|
+
...m,
|
|
15960
|
+
modelFormat: modelFormatForKind(input.kind),
|
|
15961
|
+
npm,
|
|
15962
|
+
apiUrl: fetched.baseUrl
|
|
15963
|
+
}))
|
|
15964
|
+
}
|
|
15965
|
+
};
|
|
15966
|
+
registry.providers.push(entry);
|
|
15967
|
+
saveRegistry(registry);
|
|
15968
|
+
let credentialCleanupPending = false;
|
|
15969
|
+
if (!anonymous) {
|
|
15970
|
+
try {
|
|
15971
|
+
await cancelCredentialDelete(authRef);
|
|
15972
|
+
} catch {
|
|
15973
|
+
credentialCleanupPending = true;
|
|
15974
|
+
}
|
|
15975
|
+
}
|
|
15976
|
+
return {
|
|
15977
|
+
added: true,
|
|
15978
|
+
provider: entry,
|
|
15979
|
+
modelCount: fetched.models.length,
|
|
15980
|
+
...credentialCleanupPending ? { credentialCleanupPending: true } : {}
|
|
15981
|
+
};
|
|
15982
|
+
});
|
|
15983
|
+
};
|
|
15984
|
+
if (anonymous) return persistAndCommit();
|
|
15985
|
+
return withCredentialMutationLock(authRef, persistAndCommit);
|
|
15986
|
+
});
|
|
15987
|
+
if (result.added) {
|
|
15988
|
+
try {
|
|
15989
|
+
const cleanup = await reconcilePendingCredentialDeletes();
|
|
15990
|
+
result.credentialCleanupPending = cleanup.pending.length > 0 || cleanup.persistenceError !== void 0;
|
|
15991
|
+
} catch {
|
|
15992
|
+
result.credentialCleanupPending = true;
|
|
15993
|
+
}
|
|
15994
|
+
} else {
|
|
15995
|
+
try {
|
|
15996
|
+
const cleanup = await reconcilePendingCredentialDeletes();
|
|
15997
|
+
result.credentialCleanupPending = cleanup.pending.length > 0 || cleanup.persistenceError !== void 0;
|
|
15998
|
+
} catch {
|
|
15999
|
+
result.credentialCleanupPending = true;
|
|
16000
|
+
}
|
|
16001
|
+
}
|
|
16002
|
+
result.credentialCleanupReconciled = true;
|
|
16003
|
+
return result;
|
|
16004
|
+
}
|
|
16005
|
+
|
|
16006
|
+
// src/providers-custom-add.ts
|
|
16007
|
+
async function runCustomEndpointAddFlow(onResult) {
|
|
16008
|
+
const displayName = await p3.text({
|
|
16009
|
+
message: "Name for this provider:",
|
|
16010
|
+
placeholder: "OpenRouter",
|
|
16011
|
+
validate: (value) => value.trim() ? void 0 : "Name is required"
|
|
16012
|
+
});
|
|
16013
|
+
if (p3.isCancel(displayName)) {
|
|
16014
|
+
p3.cancel("Cancelled.");
|
|
16015
|
+
return 0;
|
|
16016
|
+
}
|
|
16017
|
+
const baseUrl = await p3.text({
|
|
16018
|
+
message: "Base URL of the OpenAI-compatible API (the part before /chat/completions):",
|
|
16019
|
+
placeholder: "https://openrouter.ai/api/v1",
|
|
16020
|
+
validate: (value) => value.trim() ? void 0 : "URL is required"
|
|
16021
|
+
});
|
|
16022
|
+
if (p3.isCancel(baseUrl)) {
|
|
16023
|
+
p3.cancel("Cancelled.");
|
|
16024
|
+
return 0;
|
|
16025
|
+
}
|
|
16026
|
+
let allowInsecureLocal = false;
|
|
16027
|
+
if (/^http:\/\//i.test(String(baseUrl).trim())) {
|
|
16028
|
+
p3.log.warn("HTTP is not encrypted. Only use it for a trusted local or LAN server.");
|
|
16029
|
+
const allow = await p3.confirm({
|
|
16030
|
+
message: "Allow insecure HTTP for this local/LAN server?",
|
|
16031
|
+
initialValue: true
|
|
16032
|
+
});
|
|
16033
|
+
if (p3.isCancel(allow)) {
|
|
16034
|
+
p3.cancel("Cancelled.");
|
|
16035
|
+
return 0;
|
|
16036
|
+
}
|
|
16037
|
+
allowInsecureLocal = allow === true;
|
|
16038
|
+
}
|
|
16039
|
+
const apiKey = await p3.password({
|
|
16040
|
+
message: "API key (leave empty for a local server without auth):"
|
|
16041
|
+
});
|
|
16042
|
+
if (p3.isCancel(apiKey)) {
|
|
16043
|
+
p3.cancel("Cancelled.");
|
|
16044
|
+
return 0;
|
|
16045
|
+
}
|
|
16046
|
+
const spinner6 = p3.spinner();
|
|
16047
|
+
spinner6.start("Testing connection...");
|
|
16048
|
+
const result = await addCustomEndpointProvider({
|
|
16049
|
+
displayName: String(displayName).trim(),
|
|
16050
|
+
baseUrl: String(baseUrl).trim(),
|
|
16051
|
+
apiKey: String(apiKey ?? "").trim(),
|
|
16052
|
+
kind: "openai",
|
|
16053
|
+
allowInsecureLocal
|
|
16054
|
+
});
|
|
16055
|
+
spinner6.stop("");
|
|
16056
|
+
onResult(result);
|
|
16057
|
+
if (!result.added || !result.provider) {
|
|
16058
|
+
p3.log.error(printableServerText(result.error ?? "Could not add the provider."));
|
|
16059
|
+
const hint = printableServerText(result.hint ?? "");
|
|
16060
|
+
if (hint) p3.log.info(hint);
|
|
16061
|
+
return 1;
|
|
16062
|
+
}
|
|
16063
|
+
logConnected(result.provider.name, result.modelCount ?? 0);
|
|
16064
|
+
p3.log.info(`Provider id: ${result.provider.id}. Its models are named clodex:${result.provider.id}:<model>.`);
|
|
16065
|
+
return 0;
|
|
16066
|
+
}
|
|
16067
|
+
|
|
16068
|
+
// src/registry/crud.ts
|
|
16069
|
+
async function removeProviderWithinLifecycle(id, opts) {
|
|
16070
|
+
const removal = await withRegistryWriteLock(async () => {
|
|
16071
|
+
const registry = loadRegistryStrict();
|
|
16072
|
+
const index = registry.providers.findIndex((p14) => p14.id === id);
|
|
16073
|
+
if (index < 0) {
|
|
16074
|
+
return {
|
|
16075
|
+
result: {
|
|
16076
|
+
removed: false,
|
|
16077
|
+
id,
|
|
16078
|
+
credentialDeleted: false,
|
|
16079
|
+
error: `Provider not found: ${id}`
|
|
16080
|
+
},
|
|
16081
|
+
queuedRefs: []
|
|
16082
|
+
};
|
|
16083
|
+
}
|
|
16084
|
+
const [removedProvider] = registry.providers.splice(index, 1);
|
|
16085
|
+
const queuedRefs = [];
|
|
16086
|
+
if (opts?.deleteCredential !== false) {
|
|
16087
|
+
const credentialRefs = /* @__PURE__ */ new Set([
|
|
16088
|
+
removedProvider.authRef,
|
|
16089
|
+
...removedProvider.defaultAuthRef ? [removedProvider.defaultAuthRef] : [],
|
|
16090
|
+
...Object.values(removedProvider.authAccounts ?? {}).map((slot) => slot.authRef)
|
|
16091
|
+
]);
|
|
16092
|
+
for (const authRef of credentialRefs) {
|
|
16093
|
+
if (await queueCredentialDelete(authRef)) queuedRefs.push(authRef);
|
|
16094
|
+
}
|
|
16095
|
+
}
|
|
16096
|
+
saveRegistry(registry);
|
|
16097
|
+
return {
|
|
16098
|
+
result: {
|
|
16099
|
+
removed: true,
|
|
16100
|
+
id,
|
|
16101
|
+
name: removedProvider.name,
|
|
16102
|
+
credentialDeleted: false
|
|
16103
|
+
},
|
|
16104
|
+
queuedRefs
|
|
16105
|
+
};
|
|
16106
|
+
});
|
|
16107
|
+
if (removal.queuedRefs.length > 0) {
|
|
16108
|
+
try {
|
|
16109
|
+
const cleanup = await reconcilePendingCredentialDeletes();
|
|
16110
|
+
removal.result.credentialDeleted = removal.queuedRefs.every((ref) => cleanup.deleted.includes(ref));
|
|
16111
|
+
removal.result.credentialCleanupPending = removal.queuedRefs.some((ref) => cleanup.pending.includes(ref)) || cleanup.persistenceError !== void 0;
|
|
16112
|
+
} catch {
|
|
16113
|
+
removal.result.credentialCleanupPending = true;
|
|
16114
|
+
}
|
|
16115
|
+
removal.result.credentialCleanupReconciled = true;
|
|
16116
|
+
}
|
|
16117
|
+
return removal.result;
|
|
16118
|
+
}
|
|
16119
|
+
async function removeProviderFromRegistry(id, opts) {
|
|
16120
|
+
return withProviderMutationLock(id, () => removeProviderWithinLifecycle(id, opts));
|
|
16121
|
+
}
|
|
16122
|
+
async function setActiveOAuthAccount(id, account) {
|
|
16123
|
+
return withProviderMutationLock(id, () => withRegistryWriteLock(() => {
|
|
16124
|
+
const registry = loadRegistryStrict();
|
|
16125
|
+
const provider = registry.providers.find((p14) => p14.id === id);
|
|
16126
|
+
if (!provider) return { updated: false, error: `Provider not found: ${id}` };
|
|
16127
|
+
const name = account?.trim().toLowerCase();
|
|
16128
|
+
const previous = provider.activeAuthAccount?.trim() || void 0;
|
|
16129
|
+
const selectedSlot = name ? getOAuthAccountSlot(provider, name) : void 0;
|
|
16130
|
+
let selectionChanged = previous !== name;
|
|
16131
|
+
let storageChanged = false;
|
|
16132
|
+
if (name && !selectedSlot) {
|
|
16133
|
+
const slots = provider.authAccounts ?? {};
|
|
16134
|
+
const available = Object.keys(slots).sort().join(", ") || "none";
|
|
16135
|
+
return {
|
|
16136
|
+
updated: false,
|
|
16137
|
+
error: `${provider.name} has no account named "${name}" (available: ${available}).`
|
|
16138
|
+
};
|
|
16139
|
+
}
|
|
16140
|
+
if (name && provider.authType === "oauth") {
|
|
16141
|
+
storageChanged = storeActiveOAuthAccount(provider, name, selectedSlot.authRef);
|
|
16142
|
+
}
|
|
16143
|
+
if (selectionChanged && provider.authType === "oauth") {
|
|
16144
|
+
const previousSlot = previous ? getOAuthAccountSlot(provider, previous) : void 0;
|
|
16145
|
+
if (previous && previousSlot && provider.modelsCache && provider.authAccounts) {
|
|
16146
|
+
provider.authAccounts[previous] = {
|
|
16147
|
+
...previousSlot,
|
|
16148
|
+
modelsCache: provider.modelsCache
|
|
16149
|
+
};
|
|
16150
|
+
}
|
|
16151
|
+
const selectedCache = selectedSlot?.modelsCache;
|
|
16152
|
+
if (selectedCache) {
|
|
16153
|
+
provider.modelsCache = selectedCache;
|
|
16154
|
+
provider.refreshedAt = selectedCache.fetchedAt;
|
|
16155
|
+
} else {
|
|
16156
|
+
delete provider.modelsCache;
|
|
16157
|
+
delete provider.refreshedAt;
|
|
16158
|
+
}
|
|
16159
|
+
}
|
|
16160
|
+
if (name && provider.authType !== "oauth") {
|
|
16161
|
+
storageChanged = clearActiveOAuthAccount(provider) || storageChanged;
|
|
16162
|
+
if (provider.activeAuthAccount !== name) {
|
|
16163
|
+
provider.activeAuthAccount = name;
|
|
16164
|
+
storageChanged = true;
|
|
16165
|
+
}
|
|
16166
|
+
} else if (!name && (previous !== void 0 || provider.defaultAuthRef !== void 0)) {
|
|
16167
|
+
storageChanged = clearActiveOAuthAccount(provider);
|
|
16168
|
+
} else if (!name) {
|
|
16169
|
+
selectionChanged = false;
|
|
16170
|
+
}
|
|
16171
|
+
const migrationNeedsPersistence = registry.schemaVersion < REGISTRY_SCHEMA_VERSION_WITH_MATERIALIZED_ACTIVE_ACCOUNT && provider.defaultAuthRef !== void 0;
|
|
16172
|
+
if (selectionChanged || storageChanged || migrationNeedsPersistence) saveRegistry(registry);
|
|
16173
|
+
return { updated: true, changed: selectionChanged, ...name ? { account: name } : {}, provider };
|
|
16174
|
+
}));
|
|
16175
|
+
}
|
|
16176
|
+
function toggleProviderEnabled(id) {
|
|
16177
|
+
return withRegistryWriteLockSync(() => {
|
|
16178
|
+
const registry = loadRegistryStrict();
|
|
16179
|
+
const provider = registry.providers.find((p14) => p14.id === id);
|
|
16180
|
+
if (!provider) return { toggled: false, error: `Provider not found: ${id}` };
|
|
16181
|
+
provider.enabled = !provider.enabled;
|
|
16182
|
+
saveRegistry(registry);
|
|
16183
|
+
return { toggled: true, enabled: provider.enabled };
|
|
16184
|
+
});
|
|
16185
|
+
}
|
|
16186
|
+
|
|
16187
|
+
// src/registry/refresh-models.ts
|
|
16188
|
+
import { isDeepStrictEqual } from "util";
|
|
15904
16189
|
|
|
15905
16190
|
// src/registry/model-source.ts
|
|
15906
16191
|
var MANUAL_ONLY_TEMPLATE_IDS = /* @__PURE__ */ new Set(["vertex", "bedrock", "azure"]);
|
|
@@ -16101,7 +16386,7 @@ function buildDynamicOAuthModel(entry, seedById, codexCatalog) {
|
|
|
16101
16386
|
upstreamModelId: id,
|
|
16102
16387
|
family: prefix,
|
|
16103
16388
|
brand: deriveBrand(prefix),
|
|
16104
|
-
contextWindow: entry.context_window ??
|
|
16389
|
+
contextWindow: entry.context_window ?? lookupKnownContextWindow(id),
|
|
16105
16390
|
maxContextWindow: entry.max_context_window,
|
|
16106
16391
|
// Absent means no reduction. clodex reports the window the provider actually
|
|
16107
16392
|
// gives; deciding how much of it to leave free is the client's job, and Claude
|
|
@@ -16253,7 +16538,7 @@ async function refreshApiListProvider(provider, apiKey) {
|
|
|
16253
16538
|
};
|
|
16254
16539
|
}
|
|
16255
16540
|
function updateProviderCache(registry, providerId, models, baseUrl, credentialSnapshot) {
|
|
16256
|
-
const idx = registry.providers.findIndex((
|
|
16541
|
+
const idx = registry.providers.findIndex((p14) => p14.id === providerId);
|
|
16257
16542
|
if (idx < 0) return;
|
|
16258
16543
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
16259
16544
|
const existing = registry.providers[idx];
|
|
@@ -16324,7 +16609,7 @@ function assertRefreshCredentialStillCurrent(current, snapshot) {
|
|
|
16324
16609
|
}
|
|
16325
16610
|
async function refreshProviderModels(providerId, apiKey, registry, credentialSnapshot) {
|
|
16326
16611
|
const workingRegistry = registry ?? loadRegistryStrict();
|
|
16327
|
-
const provider = workingRegistry.providers.find((
|
|
16612
|
+
const provider = workingRegistry.providers.find((p14) => p14.id === providerId);
|
|
16328
16613
|
if (!provider) {
|
|
16329
16614
|
return { id: providerId, name: providerId, ok: false, reason: "Provider not found." };
|
|
16330
16615
|
}
|
|
@@ -16539,7 +16824,7 @@ async function refreshProviderModelsWithCredential(providerId, resolveKey, selec
|
|
|
16539
16824
|
async function refreshAllProviderModels(resolveKey) {
|
|
16540
16825
|
const refreshed = [];
|
|
16541
16826
|
const registry = loadRegistryStrict();
|
|
16542
|
-
const enabledProviders = registry.providers.filter((
|
|
16827
|
+
const enabledProviders = registry.providers.filter((p14) => p14.enabled);
|
|
16543
16828
|
for (const provider of enabledProviders) {
|
|
16544
16829
|
const accountOverride = process.env[OAUTH_ACCOUNT_ENV] ?? null;
|
|
16545
16830
|
try {
|
|
@@ -16563,7 +16848,7 @@ async function refreshAllProviderModels(resolveKey) {
|
|
|
16563
16848
|
|
|
16564
16849
|
// src/registry/provider-auth.ts
|
|
16565
16850
|
import pc4 from "picocolors";
|
|
16566
|
-
import * as
|
|
16851
|
+
import * as p4 from "@clack/prompts";
|
|
16567
16852
|
import open from "open";
|
|
16568
16853
|
function validateOAuthAccountName(name) {
|
|
16569
16854
|
const trimmed = name.trim().toLowerCase();
|
|
@@ -16586,39 +16871,39 @@ function openBrowser(url) {
|
|
|
16586
16871
|
async function runNativeDeviceCode(providerId) {
|
|
16587
16872
|
const label = PROVIDER_DISPLAY[providerId];
|
|
16588
16873
|
printOAuthStepsPanel(`${label} \u2014 Sign in`, label);
|
|
16589
|
-
const
|
|
16590
|
-
|
|
16874
|
+
const spinner6 = p4.spinner();
|
|
16875
|
+
spinner6.start("Waiting for authorization...");
|
|
16591
16876
|
try {
|
|
16592
16877
|
const { tokens, accountId } = await runOpenAiDeviceCodeFlow(({ url, userCode }) => {
|
|
16593
|
-
|
|
16594
|
-
|
|
16595
|
-
|
|
16878
|
+
spinner6.stop("");
|
|
16879
|
+
p4.log.info(`Visit: ${pc4.cyan(url)}`);
|
|
16880
|
+
p4.log.info(`Enter code: ${pc4.bold(userCode)}`);
|
|
16596
16881
|
openBrowser(url);
|
|
16597
|
-
|
|
16882
|
+
spinner6.start("Waiting for authorization...");
|
|
16598
16883
|
});
|
|
16599
|
-
|
|
16884
|
+
spinner6.stop(pc4.green("Signed in to OpenAI ChatGPT"));
|
|
16600
16885
|
return tokensToStoredCredential(tokens, void 0, accountId);
|
|
16601
16886
|
} catch (err) {
|
|
16602
|
-
|
|
16887
|
+
spinner6.stop("");
|
|
16603
16888
|
throw err;
|
|
16604
16889
|
}
|
|
16605
16890
|
}
|
|
16606
16891
|
async function runNativeBrowserSignIn(providerId) {
|
|
16607
16892
|
const label = PROVIDER_DISPLAY[providerId];
|
|
16608
16893
|
printOAuthBrowserPanel(`${label} \u2014 Sign in`, label);
|
|
16609
|
-
const
|
|
16610
|
-
|
|
16894
|
+
const spinner6 = p4.spinner();
|
|
16895
|
+
spinner6.start("Opening your browser...");
|
|
16611
16896
|
try {
|
|
16612
16897
|
const { tokens, accountId } = await runOpenAiBrowserFlow(({ url }) => {
|
|
16613
|
-
|
|
16614
|
-
|
|
16898
|
+
spinner6.stop("");
|
|
16899
|
+
p4.log.info(`If the browser did not open, visit: ${pc4.cyan(url)}`);
|
|
16615
16900
|
openBrowser(url);
|
|
16616
|
-
|
|
16901
|
+
spinner6.start("Waiting for sign-in in your browser...");
|
|
16617
16902
|
});
|
|
16618
|
-
|
|
16903
|
+
spinner6.stop(pc4.green("Signed in to OpenAI ChatGPT"));
|
|
16619
16904
|
return tokensToStoredCredential(tokens, void 0, accountId);
|
|
16620
16905
|
} catch (err) {
|
|
16621
|
-
|
|
16906
|
+
spinner6.stop("");
|
|
16622
16907
|
throw err;
|
|
16623
16908
|
}
|
|
16624
16909
|
}
|
|
@@ -16827,7 +17112,7 @@ async function authenticateProvider(providerId, options = {}) {
|
|
|
16827
17112
|
}
|
|
16828
17113
|
const cred = options.method === "browser" ? await runNativeBrowserSignIn(providerId) : await runNativeDeviceCode(providerId);
|
|
16829
17114
|
const persisted = await persistNativeOAuthCredential(providerId, cred, accountName);
|
|
16830
|
-
const refreshSpinner =
|
|
17115
|
+
const refreshSpinner = p4.spinner();
|
|
16831
17116
|
refreshSpinner.start("Refreshing model list...");
|
|
16832
17117
|
try {
|
|
16833
17118
|
const accountOverride = accountName ?? (persisted.registryProvider.activeAuthAccount === void 0 ? null : process.env[OAUTH_ACCOUNT_ENV] ?? null);
|
|
@@ -16882,7 +17167,7 @@ ${pc4.bold("Named accounts:")}
|
|
|
16882
17167
|
}
|
|
16883
17168
|
|
|
16884
17169
|
// src/prompts.ts
|
|
16885
|
-
import * as
|
|
17170
|
+
import * as p5 from "@clack/prompts";
|
|
16886
17171
|
import pc5 from "picocolors";
|
|
16887
17172
|
|
|
16888
17173
|
// src/model-search.ts
|
|
@@ -16991,12 +17276,12 @@ async function pickModelFromPagedList(list, toOption, messagePrefix, initialMode
|
|
|
16991
17276
|
options.push(navOption(SWITCH_SEARCH, "\u2190 New search", ""));
|
|
16992
17277
|
}
|
|
16993
17278
|
const initialValue = (initialModelId && pageItems.some((m) => m.id === initialModelId) ? initialModelId : pageItems[0]?.id) ?? options[0]?.value;
|
|
16994
|
-
const picked = await
|
|
17279
|
+
const picked = await p5.select({
|
|
16995
17280
|
message: `${messagePrefix} (page ${currentPage + 1} of ${totalPages})`,
|
|
16996
17281
|
options,
|
|
16997
17282
|
initialValue
|
|
16998
17283
|
});
|
|
16999
|
-
if (
|
|
17284
|
+
if (p5.isCancel(picked)) return "menu";
|
|
17000
17285
|
const choice = String(picked);
|
|
17001
17286
|
if (choice === PAGE_PREV) {
|
|
17002
17287
|
page = currentPage - 1;
|
|
@@ -17017,7 +17302,7 @@ async function selectLargeCatalog(models, browseList, toOption, message, initial
|
|
|
17017
17302
|
let mode = "choose";
|
|
17018
17303
|
while (true) {
|
|
17019
17304
|
if (mode === "choose") {
|
|
17020
|
-
const method = await
|
|
17305
|
+
const method = await p5.select({
|
|
17021
17306
|
message: `${message} (${models.length} available)`,
|
|
17022
17307
|
options: [
|
|
17023
17308
|
{ value: MODE_SEARCH, label: pc5.cyan("Search models"), hint: "Filter by name, id, or brand" },
|
|
@@ -17029,7 +17314,7 @@ async function selectLargeCatalog(models, browseList, toOption, message, initial
|
|
|
17029
17314
|
navOption("__back__", "\u2190 Go back", "Select a different provider")
|
|
17030
17315
|
]
|
|
17031
17316
|
});
|
|
17032
|
-
if (
|
|
17317
|
+
if (p5.isCancel(method) || String(method) === "__back__") {
|
|
17033
17318
|
return "back";
|
|
17034
17319
|
}
|
|
17035
17320
|
mode = method === MODE_BROWSE ? "browse" : "search";
|
|
@@ -17054,17 +17339,17 @@ async function selectLargeCatalog(models, browseList, toOption, message, initial
|
|
|
17054
17339
|
if (isSelectedModel(picked)) return picked;
|
|
17055
17340
|
continue;
|
|
17056
17341
|
}
|
|
17057
|
-
const searchInput = await
|
|
17342
|
+
const searchInput = await p5.text({
|
|
17058
17343
|
message: `Search models (${models.length} available):`,
|
|
17059
17344
|
placeholder: "e.g. claude, sonnet, llama"
|
|
17060
17345
|
});
|
|
17061
|
-
if (
|
|
17346
|
+
if (p5.isCancel(searchInput)) {
|
|
17062
17347
|
mode = "choose";
|
|
17063
17348
|
continue;
|
|
17064
17349
|
}
|
|
17065
17350
|
const matched = filterModelsBySearch(browseList, String(searchInput));
|
|
17066
17351
|
if (matched.length === 0) {
|
|
17067
|
-
|
|
17352
|
+
p5.log.warn("No models match \u2014 try a different search");
|
|
17068
17353
|
continue;
|
|
17069
17354
|
}
|
|
17070
17355
|
const result = await pickModelFromPagedList(
|
|
@@ -17095,12 +17380,12 @@ async function selectModelWithSearch(models, toOption, message, initialModelId,
|
|
|
17095
17380
|
navOption("__back__", "\u2190 Go back", "")
|
|
17096
17381
|
];
|
|
17097
17382
|
const initialValue = initialModelId && options.some((o) => o.value === initialModelId) ? initialModelId : options[0]?.value;
|
|
17098
|
-
const picked = await
|
|
17383
|
+
const picked = await p5.select({
|
|
17099
17384
|
message,
|
|
17100
17385
|
options,
|
|
17101
17386
|
initialValue
|
|
17102
17387
|
});
|
|
17103
|
-
if (
|
|
17388
|
+
if (p5.isCancel(picked) || String(picked) === "__back__") {
|
|
17104
17389
|
return "back";
|
|
17105
17390
|
}
|
|
17106
17391
|
const selected = models.find((m) => m.id === String(picked));
|
|
@@ -17134,12 +17419,12 @@ async function pickLocalModel(provider, conflicts, prefs) {
|
|
|
17134
17419
|
navOption(BROWSE_ALL, "Browse all models \u2192", `${provider.models.length} available`),
|
|
17135
17420
|
navOption("__back__", "\u2190 Go back", "Select a different provider")
|
|
17136
17421
|
];
|
|
17137
|
-
const picked = await
|
|
17422
|
+
const picked = await p5.select({
|
|
17138
17423
|
message: "Which model?",
|
|
17139
17424
|
options,
|
|
17140
17425
|
initialValue: recentModels[0].id
|
|
17141
17426
|
});
|
|
17142
|
-
if (
|
|
17427
|
+
if (p5.isCancel(picked) || String(picked) === "__back__") {
|
|
17143
17428
|
return "back";
|
|
17144
17429
|
}
|
|
17145
17430
|
if (String(picked) === BROWSE_ALL) {
|
|
@@ -17166,12 +17451,12 @@ async function pickLocalModel(provider, conflicts, prefs) {
|
|
|
17166
17451
|
}
|
|
17167
17452
|
noteEnvConflicts(conflicts);
|
|
17168
17453
|
const modelLabel = formatModelLabel(selectedModel);
|
|
17169
|
-
const confirmed = await
|
|
17454
|
+
const confirmed = await p5.confirm({
|
|
17170
17455
|
message: confirmLaunchMessage("Claude Code", modelLabel, selectedModel.id, provider.name),
|
|
17171
17456
|
initialValue: true
|
|
17172
17457
|
});
|
|
17173
|
-
if (
|
|
17174
|
-
|
|
17458
|
+
if (p5.isCancel(confirmed) || !confirmed) {
|
|
17459
|
+
p5.cancel("Cancelled.");
|
|
17175
17460
|
return null;
|
|
17176
17461
|
}
|
|
17177
17462
|
relayOutro("Launching", fmtModel(modelLabel, selectedModel.id));
|
|
@@ -17187,7 +17472,7 @@ function reportCredentialCleanup(pending, state, reconciled = false) {
|
|
|
17187
17472
|
return;
|
|
17188
17473
|
}
|
|
17189
17474
|
if (pending) {
|
|
17190
|
-
|
|
17475
|
+
p6.log.warn(CREDENTIAL_CLEANUP_PENDING_MESSAGE);
|
|
17191
17476
|
}
|
|
17192
17477
|
}
|
|
17193
17478
|
async function reconcileCredentialCleanup() {
|
|
@@ -17277,7 +17562,7 @@ ${pc6.bold("Usage:")}
|
|
|
17277
17562
|
|
|
17278
17563
|
${pc6.bold("Subcommands:")}
|
|
17279
17564
|
(none) Provider hub wizard
|
|
17280
|
-
add Add a built-in provider or sign in with ChatGPT
|
|
17565
|
+
add Add a built-in provider, a custom OpenAI-compatible server, or sign in with ChatGPT
|
|
17281
17566
|
auth Sign in with ChatGPT/Codex-plan OAuth (device code, or --browser)
|
|
17282
17567
|
list Show configured providers
|
|
17283
17568
|
remove Remove a provider by id
|
|
@@ -17404,7 +17689,7 @@ function providerLabel(name, modelCount, enabled) {
|
|
|
17404
17689
|
return `${fmtEnabledStar(enabled)} ${fmtProvider(name)} ${pc6.dim(`(${modelCount} model${modelCount === 1 ? "" : "s"})`)}`;
|
|
17405
17690
|
}
|
|
17406
17691
|
async function promptOAuthMethod() {
|
|
17407
|
-
const choice = await
|
|
17692
|
+
const choice = await p6.select({
|
|
17408
17693
|
message: "How do you want to sign in?",
|
|
17409
17694
|
initialValue: "native",
|
|
17410
17695
|
options: [
|
|
@@ -17412,8 +17697,8 @@ async function promptOAuthMethod() {
|
|
|
17412
17697
|
{ value: "browser", label: "Browser", hint: "for workspaces that disable device code authorization" }
|
|
17413
17698
|
]
|
|
17414
17699
|
});
|
|
17415
|
-
if (
|
|
17416
|
-
|
|
17700
|
+
if (p6.isCancel(choice)) {
|
|
17701
|
+
p6.cancel("Cancelled.");
|
|
17417
17702
|
return null;
|
|
17418
17703
|
}
|
|
17419
17704
|
return choice;
|
|
@@ -17422,17 +17707,17 @@ async function runProvidersAuthWithCleanupState(providerId, method, cleanupState
|
|
|
17422
17707
|
try {
|
|
17423
17708
|
const result = await authenticateProvider(providerId, { method, account });
|
|
17424
17709
|
const slot = account === void 0 ? void 0 : validateOAuthAccountName(account);
|
|
17425
|
-
|
|
17710
|
+
p6.log.success(
|
|
17426
17711
|
slot ? `Signed in to ${result.registryProvider.name} (account "${slot}") \u2014 make it the account every launch uses with: clodex providers` : `Signed in to ${result.registryProvider.name} \u2014 credential saved to the credential store.`
|
|
17427
17712
|
);
|
|
17428
17713
|
reportCredentialCleanup(result.credentialCleanupPending, cleanupState, true);
|
|
17429
17714
|
return 0;
|
|
17430
17715
|
} catch (err) {
|
|
17431
17716
|
if (err instanceof Error && err.message === "Cancelled") {
|
|
17432
|
-
|
|
17717
|
+
p6.cancel("Cancelled.");
|
|
17433
17718
|
return 0;
|
|
17434
17719
|
}
|
|
17435
|
-
|
|
17720
|
+
p6.log.error(err instanceof Error ? err.message : String(err));
|
|
17436
17721
|
return 1;
|
|
17437
17722
|
}
|
|
17438
17723
|
}
|
|
@@ -17443,13 +17728,13 @@ async function runProvidersRefreshModels(providerId, options = {}) {
|
|
|
17443
17728
|
const resolveKey = async (provider) => resolveProviderCredentialWithSource(provider.id, provider.authRef);
|
|
17444
17729
|
if (providerId) {
|
|
17445
17730
|
const registry = loadRegistry();
|
|
17446
|
-
const provider = registry.providers.find((
|
|
17731
|
+
const provider = registry.providers.find((p14) => p14.id === providerId);
|
|
17447
17732
|
if (!provider) {
|
|
17448
|
-
|
|
17733
|
+
p6.log.error(`Provider not found: ${providerId}`);
|
|
17449
17734
|
return 1;
|
|
17450
17735
|
}
|
|
17451
|
-
const
|
|
17452
|
-
|
|
17736
|
+
const spinner7 = p6.spinner();
|
|
17737
|
+
spinner7.start(`Refreshing ${provider.name}...`);
|
|
17453
17738
|
const accountOverride = options.accountOverride === void 0 ? process.env[OAUTH_ACCOUNT_ENV] ?? null : options.accountOverride;
|
|
17454
17739
|
let result;
|
|
17455
17740
|
try {
|
|
@@ -17465,59 +17750,59 @@ async function runProvidersRefreshModels(providerId, options = {}) {
|
|
|
17465
17750
|
{ ignoreProviderOverride: options.ignoreProviderCredentialOverride }
|
|
17466
17751
|
);
|
|
17467
17752
|
} catch (err) {
|
|
17468
|
-
|
|
17469
|
-
|
|
17753
|
+
spinner7.stop("");
|
|
17754
|
+
p6.log.error(err instanceof Error ? err.message : String(err));
|
|
17470
17755
|
return 1;
|
|
17471
17756
|
}
|
|
17472
|
-
|
|
17757
|
+
spinner7.stop("");
|
|
17473
17758
|
if (result.skipped) {
|
|
17474
17759
|
const countNote = result.modelCount ? ` (${result.modelCount} cached models kept)` : "";
|
|
17475
|
-
|
|
17760
|
+
p6.log.warn(`${result.name}: ${result.reason}${countNote}`);
|
|
17476
17761
|
return 0;
|
|
17477
17762
|
}
|
|
17478
17763
|
if (!result.ok) {
|
|
17479
|
-
|
|
17764
|
+
p6.log.error(`${result.name}: ${result.reason ?? "Refresh failed."}`);
|
|
17480
17765
|
return 1;
|
|
17481
17766
|
}
|
|
17482
17767
|
const diff = result.previousModelCount === void 0 ? 0 : (result.modelCount ?? 0) - result.previousModelCount;
|
|
17483
17768
|
const diffStr = result.previousModelCount === void 0 ? "" : diff > 0 ? ` (+${diff})` : diff < 0 ? ` (${diff})` : "";
|
|
17484
|
-
|
|
17769
|
+
p6.log.success(`${result.name}: ${result.modelCount} model${result.modelCount === 1 ? "" : "s"} updated${diffStr}.`);
|
|
17485
17770
|
if (result.reason) {
|
|
17486
|
-
|
|
17771
|
+
p6.log.warn(result.reason);
|
|
17487
17772
|
}
|
|
17488
17773
|
return 0;
|
|
17489
17774
|
}
|
|
17490
|
-
const
|
|
17491
|
-
|
|
17775
|
+
const spinner6 = p6.spinner();
|
|
17776
|
+
spinner6.start("Refreshing model lists...");
|
|
17492
17777
|
const { refreshed } = await refreshAllProviderModels(resolveKey);
|
|
17493
|
-
|
|
17778
|
+
spinner6.stop("");
|
|
17494
17779
|
const ok = refreshed.filter((r) => r.ok && !r.skipped);
|
|
17495
17780
|
const skipped = refreshed.filter((r) => r.skipped);
|
|
17496
17781
|
const failed = refreshed.filter((r) => !r.ok);
|
|
17497
17782
|
if (ok.length > 0) {
|
|
17498
|
-
|
|
17783
|
+
p6.log.success(`Updated ${ok.length} provider${ok.length === 1 ? "" : "s"}.`);
|
|
17499
17784
|
for (const r of ok) {
|
|
17500
17785
|
const diff = r.previousModelCount === void 0 ? 0 : (r.modelCount ?? 0) - r.previousModelCount;
|
|
17501
17786
|
const diffStr = r.previousModelCount === void 0 ? "" : diff > 0 ? ` (+${diff})` : diff < 0 ? ` (${diff})` : "";
|
|
17502
|
-
|
|
17787
|
+
p6.log.info(` ${r.name}: ${r.modelCount} model${r.modelCount === 1 ? "" : "s"}${diffStr}`);
|
|
17503
17788
|
if (r.reason) {
|
|
17504
|
-
|
|
17789
|
+
p6.log.warn(` ${r.reason}`);
|
|
17505
17790
|
}
|
|
17506
17791
|
}
|
|
17507
17792
|
}
|
|
17508
17793
|
for (const r of skipped) {
|
|
17509
17794
|
const countNote = r.modelCount ? ` (${r.modelCount} cached models kept)` : "";
|
|
17510
|
-
|
|
17795
|
+
p6.log.warn(`Skipped ${r.name}: ${r.reason}${countNote}`);
|
|
17511
17796
|
}
|
|
17512
17797
|
for (const r of failed) {
|
|
17513
|
-
|
|
17798
|
+
p6.log.error(`${r.name}: ${r.reason ?? "Refresh failed."}`);
|
|
17514
17799
|
}
|
|
17515
17800
|
return failed.length > 0 ? 1 : 0;
|
|
17516
17801
|
}
|
|
17517
17802
|
async function runProvidersList() {
|
|
17518
17803
|
const entries = await resolveProvidersForDisplay();
|
|
17519
17804
|
if (entries.length === 0) {
|
|
17520
|
-
|
|
17805
|
+
p6.log.info("No providers configured. Run clodex providers add or clodex providers auth openai.");
|
|
17521
17806
|
return 0;
|
|
17522
17807
|
}
|
|
17523
17808
|
console.log("");
|
|
@@ -17538,7 +17823,7 @@ async function runTemplateAddFlow(templateId, cleanupState) {
|
|
|
17538
17823
|
const template = listRegistryAddableTemplates(registry.providers).find((candidate) => candidate.id === templateId);
|
|
17539
17824
|
if (!template) {
|
|
17540
17825
|
const known = getTemplateById(templateId);
|
|
17541
|
-
|
|
17826
|
+
p6.log.info(known ? `${known.name} is already configured.` : `Unknown provider template: ${templateId}`);
|
|
17542
17827
|
return known ? 0 : 1;
|
|
17543
17828
|
}
|
|
17544
17829
|
if (template.signupUrl) {
|
|
@@ -17546,27 +17831,27 @@ async function runTemplateAddFlow(templateId, cleanupState) {
|
|
|
17546
17831
|
`${pc6.white("Get an API key at:")} ${fmtUrl(template.signupUrl)}`
|
|
17547
17832
|
]);
|
|
17548
17833
|
}
|
|
17549
|
-
const apiKeyInput = await
|
|
17834
|
+
const apiKeyInput = await p6.password({
|
|
17550
17835
|
message: `Paste your ${template.name} API key:`,
|
|
17551
17836
|
validate: (val) => val.trim() ? void 0 : "Key cannot be empty"
|
|
17552
17837
|
});
|
|
17553
|
-
if (
|
|
17554
|
-
|
|
17838
|
+
if (p6.isCancel(apiKeyInput)) {
|
|
17839
|
+
p6.cancel("Cancelled.");
|
|
17555
17840
|
return 0;
|
|
17556
17841
|
}
|
|
17557
17842
|
const apiKey = String(apiKeyInput).trim();
|
|
17558
|
-
const
|
|
17559
|
-
|
|
17843
|
+
const spinner6 = p6.spinner();
|
|
17844
|
+
spinner6.start(`Testing connection to ${template.name}...`);
|
|
17560
17845
|
const result = await addProviderFromTemplate(template, apiKey);
|
|
17561
|
-
|
|
17846
|
+
spinner6.stop("");
|
|
17562
17847
|
reportCredentialCleanup(
|
|
17563
17848
|
result.credentialCleanupPending === true,
|
|
17564
17849
|
cleanupState,
|
|
17565
17850
|
result.credentialCleanupReconciled === true
|
|
17566
17851
|
);
|
|
17567
17852
|
if (!result.added) {
|
|
17568
|
-
|
|
17569
|
-
if (result.hint)
|
|
17853
|
+
p6.log.error(result.error ?? "Could not add provider.");
|
|
17854
|
+
if (result.hint) p6.log.info(result.hint);
|
|
17570
17855
|
return 1;
|
|
17571
17856
|
}
|
|
17572
17857
|
logConnected(template.name, result.modelCount ?? 0);
|
|
@@ -17590,16 +17875,17 @@ async function runProvidersAddWithCleanupState(cleanupState) {
|
|
|
17590
17875
|
hint: template.id === "openai" ? "platform.openai.com key (usage-billed)" : "provider API key (usage-billed)"
|
|
17591
17876
|
});
|
|
17592
17877
|
}
|
|
17593
|
-
|
|
17594
|
-
|
|
17595
|
-
|
|
17596
|
-
|
|
17597
|
-
|
|
17878
|
+
options.push({
|
|
17879
|
+
value: "custom",
|
|
17880
|
+
label: "Custom OpenAI-compatible server",
|
|
17881
|
+
hint: "OpenRouter, Together, LM Studio, vLLM \u2014 any base URL"
|
|
17882
|
+
});
|
|
17883
|
+
const choice = await p6.select({
|
|
17598
17884
|
message: "Add a provider",
|
|
17599
17885
|
options
|
|
17600
17886
|
});
|
|
17601
|
-
if (
|
|
17602
|
-
|
|
17887
|
+
if (p6.isCancel(choice)) {
|
|
17888
|
+
p6.cancel("Cancelled.");
|
|
17603
17889
|
return 0;
|
|
17604
17890
|
}
|
|
17605
17891
|
if (choice === "oauth") {
|
|
@@ -17607,6 +17893,13 @@ async function runProvidersAddWithCleanupState(cleanupState) {
|
|
|
17607
17893
|
if (method === null) return 0;
|
|
17608
17894
|
return runProvidersAuthWithCleanupState("openai", method, cleanupState);
|
|
17609
17895
|
}
|
|
17896
|
+
if (choice === "custom") {
|
|
17897
|
+
return runCustomEndpointAddFlow((result) => reportCredentialCleanup(
|
|
17898
|
+
result.credentialCleanupPending === true,
|
|
17899
|
+
cleanupState,
|
|
17900
|
+
result.credentialCleanupReconciled === true
|
|
17901
|
+
));
|
|
17902
|
+
}
|
|
17610
17903
|
if (typeof choice === "string" && choice.startsWith("api:")) {
|
|
17611
17904
|
return runTemplateAddFlow(choice.slice("api:".length), cleanupState);
|
|
17612
17905
|
}
|
|
@@ -17619,16 +17912,16 @@ async function runProvidersRemoveWithCleanupState(id, interactive = false, clean
|
|
|
17619
17912
|
const registry = loadRegistry();
|
|
17620
17913
|
const provider = registry.providers.find((pr) => pr.id === id);
|
|
17621
17914
|
if (!provider) {
|
|
17622
|
-
|
|
17915
|
+
p6.log.error(`Provider not found: ${id}`);
|
|
17623
17916
|
return 1;
|
|
17624
17917
|
}
|
|
17625
17918
|
if (interactive) {
|
|
17626
|
-
const
|
|
17919
|
+
const confirm7 = await p6.confirm({
|
|
17627
17920
|
message: `Remove ${provider.name} (${id})?`,
|
|
17628
17921
|
initialValue: false
|
|
17629
17922
|
});
|
|
17630
|
-
if (
|
|
17631
|
-
|
|
17923
|
+
if (p6.isCancel(confirm7) || !confirm7) {
|
|
17924
|
+
p6.cancel("Cancelled.");
|
|
17632
17925
|
return 0;
|
|
17633
17926
|
}
|
|
17634
17927
|
}
|
|
@@ -17639,16 +17932,16 @@ async function runProvidersRemoveWithCleanupState(id, interactive = false, clean
|
|
|
17639
17932
|
result.credentialCleanupReconciled === true
|
|
17640
17933
|
);
|
|
17641
17934
|
if (!result.removed) {
|
|
17642
|
-
|
|
17935
|
+
p6.log.error(result.error ?? `Could not remove ${id}`);
|
|
17643
17936
|
return 1;
|
|
17644
17937
|
}
|
|
17645
17938
|
if (result.error) {
|
|
17646
|
-
|
|
17939
|
+
p6.log.error(result.error);
|
|
17647
17940
|
return 1;
|
|
17648
17941
|
}
|
|
17649
|
-
|
|
17942
|
+
p6.log.success(`Removed ${result.name ?? id}.`);
|
|
17650
17943
|
if (result.credentialDeleted) {
|
|
17651
|
-
|
|
17944
|
+
p6.log.info("Provider API key removed from the credential store.");
|
|
17652
17945
|
}
|
|
17653
17946
|
return 0;
|
|
17654
17947
|
}
|
|
@@ -17723,11 +18016,11 @@ async function runProviderDetail(id) {
|
|
|
17723
18016
|
{ value: "remove", label: "Remove provider", hint: "Delete from registry and credential store when safe" },
|
|
17724
18017
|
{ value: "back", label: "Back", hint: "" }
|
|
17725
18018
|
);
|
|
17726
|
-
const action = await
|
|
18019
|
+
const action = await p6.select({
|
|
17727
18020
|
message: "What would you like to do?",
|
|
17728
18021
|
options: detailOptions
|
|
17729
18022
|
});
|
|
17730
|
-
if (
|
|
18023
|
+
if (p6.isCancel(action) || action === "back") return "back";
|
|
17731
18024
|
if (action === "browse") {
|
|
17732
18025
|
const cachedModels = projectProviderCachedModels(modelProvider);
|
|
17733
18026
|
const localModels = cachedModels.map((m) => cachedModelToLocal(m, modelProvider)).filter((m) => m !== null);
|
|
@@ -17754,7 +18047,7 @@ async function runProviderDetail(id) {
|
|
|
17754
18047
|
const providerDefault = "<default>";
|
|
17755
18048
|
const stored = provider.activeAuthAccount;
|
|
17756
18049
|
const current = stored !== void 0 && accountSlots.includes(stored) ? stored : providerDefault;
|
|
17757
|
-
const chosen = await
|
|
18050
|
+
const chosen = await p6.select({
|
|
17758
18051
|
message: "Which account should every launch use?",
|
|
17759
18052
|
initialValue: current,
|
|
17760
18053
|
options: [
|
|
@@ -17770,15 +18063,15 @@ async function runProviderDetail(id) {
|
|
|
17770
18063
|
}))
|
|
17771
18064
|
]
|
|
17772
18065
|
});
|
|
17773
|
-
if (
|
|
18066
|
+
if (p6.isCancel(chosen)) return "back";
|
|
17774
18067
|
return withProviderMutationLock(id, async () => {
|
|
17775
18068
|
const result = await setActiveOAuthAccount(id, chosen === providerDefault ? void 0 : chosen);
|
|
17776
18069
|
if (!result.updated) {
|
|
17777
|
-
|
|
18070
|
+
p6.log.error(result.error ?? "Could not switch account.");
|
|
17778
18071
|
return "back";
|
|
17779
18072
|
}
|
|
17780
18073
|
if (!result.provider) {
|
|
17781
|
-
|
|
18074
|
+
p6.log.error("Account selection was saved, but the resulting provider state could not be read.");
|
|
17782
18075
|
return "back";
|
|
17783
18076
|
}
|
|
17784
18077
|
const refreshExitCode = await runProvidersRefreshModels(id, {
|
|
@@ -17787,7 +18080,7 @@ async function runProviderDetail(id) {
|
|
|
17787
18080
|
});
|
|
17788
18081
|
const currentProvider = loadRegistry().providers.find((candidate) => candidate.id === id);
|
|
17789
18082
|
if (!currentProvider) {
|
|
17790
|
-
|
|
18083
|
+
p6.log.error("Account selection was saved, but the resulting provider state could not be read.");
|
|
17791
18084
|
return "back";
|
|
17792
18085
|
}
|
|
17793
18086
|
const outcome = accountSwitchOutcome(
|
|
@@ -17799,26 +18092,26 @@ async function runProviderDetail(id) {
|
|
|
17799
18092
|
if (outcome.ok && currentProvider.enabled && currentProvider.authType === "oauth" && (refreshExitCode !== 0 || !selectedCatalogReady)) {
|
|
17800
18093
|
const savedLabel = result.account ?? PROVIDER_DEFAULT_ACCOUNT_LABEL;
|
|
17801
18094
|
const savedContext = outcome.confirmsLaunch ? `Saved ${savedLabel} for ${currentProvider.name}.` : outcome.message;
|
|
17802
|
-
|
|
18095
|
+
p6.log.warn(
|
|
17803
18096
|
`${savedContext} Automatic model refresh for the saved selection did not produce a usable catalog; choose Switch account again to retry it before relying on that selection for launches.`
|
|
17804
18097
|
);
|
|
17805
18098
|
} else if (outcome.ok) {
|
|
17806
|
-
|
|
18099
|
+
p6.log.success(outcome.message);
|
|
17807
18100
|
} else {
|
|
17808
|
-
|
|
18101
|
+
p6.log.warn(outcome.message);
|
|
17809
18102
|
}
|
|
17810
18103
|
const restartWarning = accountSwitchServerRestartWarning(
|
|
17811
18104
|
readLiveServerRuntimeStates().length,
|
|
17812
18105
|
result.changed
|
|
17813
18106
|
);
|
|
17814
|
-
if (restartWarning)
|
|
18107
|
+
if (restartWarning) p6.log.warn(restartWarning);
|
|
17815
18108
|
return "back";
|
|
17816
18109
|
});
|
|
17817
18110
|
}
|
|
17818
18111
|
if (action === "toggle") {
|
|
17819
18112
|
const result = toggleProviderEnabled(id);
|
|
17820
18113
|
if (result.toggled) {
|
|
17821
|
-
|
|
18114
|
+
p6.log.success(`${provider.name} ${result.enabled ? "enabled" : "disabled"}.`);
|
|
17822
18115
|
}
|
|
17823
18116
|
return "back";
|
|
17824
18117
|
}
|
|
@@ -17854,11 +18147,11 @@ async function runProvidersHub() {
|
|
|
17854
18147
|
options.push({ value: "refresh-all", label: "\u21BA Refresh all models", hint: "Update model lists for all providers" });
|
|
17855
18148
|
}
|
|
17856
18149
|
options.push({ value: "done", label: "Done", hint: "" });
|
|
17857
|
-
const choice = await
|
|
18150
|
+
const choice = await p6.select({
|
|
17858
18151
|
message: entries.length > 0 ? "Your providers" : "Get started",
|
|
17859
18152
|
options
|
|
17860
18153
|
});
|
|
17861
|
-
if (
|
|
18154
|
+
if (p6.isCancel(choice) || choice === "done") {
|
|
17862
18155
|
return 0;
|
|
17863
18156
|
}
|
|
17864
18157
|
if (choice === "add") {
|
|
@@ -17876,7 +18169,7 @@ async function runProvidersHub() {
|
|
|
17876
18169
|
continue;
|
|
17877
18170
|
}
|
|
17878
18171
|
if (choice === "auth-account") {
|
|
17879
|
-
const name = await
|
|
18172
|
+
const name = await p6.text({
|
|
17880
18173
|
message: "Name for this account (choose which one launches with: clodex providers)",
|
|
17881
18174
|
placeholder: "work",
|
|
17882
18175
|
validate: (value) => {
|
|
@@ -17888,7 +18181,7 @@ async function runProvidersHub() {
|
|
|
17888
18181
|
}
|
|
17889
18182
|
}
|
|
17890
18183
|
});
|
|
17891
|
-
if (
|
|
18184
|
+
if (p6.isCancel(name)) continue;
|
|
17892
18185
|
const accountMethod = await promptOAuthMethod();
|
|
17893
18186
|
if (accountMethod === null) continue;
|
|
17894
18187
|
await runWithCredentialCleanup((state) => runProvidersAuthWithCleanupState("openai", accountMethod, state, String(name)));
|
|
@@ -17904,7 +18197,7 @@ async function runProvidersHub() {
|
|
|
17904
18197
|
async function runProvidersCommand(args) {
|
|
17905
18198
|
const parsed = parseProvidersArgs(args);
|
|
17906
18199
|
if (parsed.error) {
|
|
17907
|
-
|
|
18200
|
+
p6.log.error(parsed.error);
|
|
17908
18201
|
return 1;
|
|
17909
18202
|
}
|
|
17910
18203
|
if (parsed.showHelp && parsed.subcommand !== "auth") {
|
|
@@ -17941,7 +18234,7 @@ async function needsFirstRunSetup() {
|
|
|
17941
18234
|
}
|
|
17942
18235
|
async function runFirstRunWizard(_trace = false) {
|
|
17943
18236
|
printWelcomePanel();
|
|
17944
|
-
const choice = await
|
|
18237
|
+
const choice = await p7.select({
|
|
17945
18238
|
message: "How do you want to connect to OpenAI?",
|
|
17946
18239
|
options: [
|
|
17947
18240
|
{
|
|
@@ -17956,8 +18249,8 @@ async function runFirstRunWizard(_trace = false) {
|
|
|
17956
18249
|
}
|
|
17957
18250
|
]
|
|
17958
18251
|
});
|
|
17959
|
-
if (
|
|
17960
|
-
|
|
18252
|
+
if (p7.isCancel(choice)) {
|
|
18253
|
+
p7.cancel("Cancelled.");
|
|
17961
18254
|
return "cancel";
|
|
17962
18255
|
}
|
|
17963
18256
|
let code;
|
|
@@ -17970,14 +18263,14 @@ async function runFirstRunWizard(_trace = false) {
|
|
|
17970
18263
|
}
|
|
17971
18264
|
if (code !== 0) return "cancel";
|
|
17972
18265
|
if (await needsFirstRunSetup()) return "cancel";
|
|
17973
|
-
|
|
18266
|
+
p7.log.success("OpenAI provider ready \u2014 picking a model next.");
|
|
17974
18267
|
return "continue";
|
|
17975
18268
|
}
|
|
17976
18269
|
|
|
17977
18270
|
// src/server/index.ts
|
|
17978
18271
|
import pc11 from "picocolors";
|
|
17979
18272
|
import { networkInterfaces as networkInterfaces2 } from "os";
|
|
17980
|
-
import * as
|
|
18273
|
+
import * as p11 from "@clack/prompts";
|
|
17981
18274
|
|
|
17982
18275
|
// src/target-compatibility.ts
|
|
17983
18276
|
function blacklistAgentForTarget(target) {
|
|
@@ -18015,10 +18308,10 @@ function providersForTarget(providers, target) {
|
|
|
18015
18308
|
}
|
|
18016
18309
|
|
|
18017
18310
|
// src/server/prompts.ts
|
|
18018
|
-
import * as
|
|
18311
|
+
import * as p8 from "@clack/prompts";
|
|
18019
18312
|
import pc8 from "picocolors";
|
|
18020
18313
|
async function askServerStartMode() {
|
|
18021
|
-
const mode = await
|
|
18314
|
+
const mode = await p8.select({
|
|
18022
18315
|
message: "How do you want to start the server?",
|
|
18023
18316
|
options: [
|
|
18024
18317
|
{ value: "configure", label: pc8.cyan("Configure & start"), hint: "Providers, discovery masking, listen mode" },
|
|
@@ -18026,37 +18319,37 @@ async function askServerStartMode() {
|
|
|
18026
18319
|
],
|
|
18027
18320
|
initialValue: "configure"
|
|
18028
18321
|
});
|
|
18029
|
-
if (
|
|
18030
|
-
|
|
18322
|
+
if (p8.isCancel(mode)) {
|
|
18323
|
+
p8.cancel("Cancelled.");
|
|
18031
18324
|
return null;
|
|
18032
18325
|
}
|
|
18033
18326
|
return mode;
|
|
18034
18327
|
}
|
|
18035
18328
|
async function askMaskGatewayIds(initialValue) {
|
|
18036
|
-
const mask = await
|
|
18329
|
+
const mask = await p8.confirm({
|
|
18037
18330
|
message: "Mask gateway model ids for discovery? (Needed for Claude Desktop / Cowork)",
|
|
18038
18331
|
initialValue
|
|
18039
18332
|
});
|
|
18040
|
-
if (
|
|
18041
|
-
|
|
18333
|
+
if (p8.isCancel(mask)) {
|
|
18334
|
+
p8.cancel("Cancelled.");
|
|
18042
18335
|
return null;
|
|
18043
18336
|
}
|
|
18044
18337
|
return Boolean(mask);
|
|
18045
18338
|
}
|
|
18046
18339
|
async function askFavoritesOnly(initialValue) {
|
|
18047
18340
|
printFavoritesOnlyPanel();
|
|
18048
|
-
const favoritesOnly = await
|
|
18341
|
+
const favoritesOnly = await p8.confirm({
|
|
18049
18342
|
message: "Expose only favorite models?",
|
|
18050
18343
|
initialValue
|
|
18051
18344
|
});
|
|
18052
|
-
if (
|
|
18053
|
-
|
|
18345
|
+
if (p8.isCancel(favoritesOnly)) {
|
|
18346
|
+
p8.cancel("Cancelled.");
|
|
18054
18347
|
return null;
|
|
18055
18348
|
}
|
|
18056
18349
|
return Boolean(favoritesOnly);
|
|
18057
18350
|
}
|
|
18058
18351
|
async function askListenMode() {
|
|
18059
|
-
const mode = await
|
|
18352
|
+
const mode = await p8.select({
|
|
18060
18353
|
message: "Where should the server listen?",
|
|
18061
18354
|
options: [
|
|
18062
18355
|
{ value: "local", label: pc8.cyan("Local only"), hint: "Only this computer can use it" },
|
|
@@ -18064,26 +18357,26 @@ async function askListenMode() {
|
|
|
18064
18357
|
],
|
|
18065
18358
|
initialValue: "local"
|
|
18066
18359
|
});
|
|
18067
|
-
if (
|
|
18068
|
-
|
|
18360
|
+
if (p8.isCancel(mode)) {
|
|
18361
|
+
p8.cancel("Cancelled.");
|
|
18069
18362
|
return null;
|
|
18070
18363
|
}
|
|
18071
18364
|
return mode;
|
|
18072
18365
|
}
|
|
18073
18366
|
async function askServerPassword() {
|
|
18074
18367
|
printNetworkWarningPanel();
|
|
18075
|
-
const
|
|
18368
|
+
const password3 = await p8.text({
|
|
18076
18369
|
message: "Choose a server password for this run:",
|
|
18077
18370
|
validate: (value) => value.trim() ? void 0 : "Password cannot be empty"
|
|
18078
18371
|
});
|
|
18079
|
-
if (
|
|
18080
|
-
|
|
18372
|
+
if (p8.isCancel(password3)) {
|
|
18373
|
+
p8.cancel("Cancelled.");
|
|
18081
18374
|
return null;
|
|
18082
18375
|
}
|
|
18083
|
-
return String(
|
|
18376
|
+
return String(password3).trim();
|
|
18084
18377
|
}
|
|
18085
18378
|
async function askUseSavedServerPassword() {
|
|
18086
|
-
const choice = await
|
|
18379
|
+
const choice = await p8.select({
|
|
18087
18380
|
message: "Use saved server password?",
|
|
18088
18381
|
options: [
|
|
18089
18382
|
{ value: "use-saved", label: pc8.cyan("Use saved password") },
|
|
@@ -18091,19 +18384,19 @@ async function askUseSavedServerPassword() {
|
|
|
18091
18384
|
],
|
|
18092
18385
|
initialValue: "use-saved"
|
|
18093
18386
|
});
|
|
18094
|
-
if (
|
|
18095
|
-
|
|
18387
|
+
if (p8.isCancel(choice)) {
|
|
18388
|
+
p8.cancel("Cancelled.");
|
|
18096
18389
|
return null;
|
|
18097
18390
|
}
|
|
18098
18391
|
return choice;
|
|
18099
18392
|
}
|
|
18100
18393
|
async function askSaveServerPassword() {
|
|
18101
|
-
const save = await
|
|
18394
|
+
const save = await p8.confirm({
|
|
18102
18395
|
message: "Save this server password for future server runs?",
|
|
18103
18396
|
initialValue: false
|
|
18104
18397
|
});
|
|
18105
|
-
if (
|
|
18106
|
-
|
|
18398
|
+
if (p8.isCancel(save)) {
|
|
18399
|
+
p8.cancel("Cancelled.");
|
|
18107
18400
|
return null;
|
|
18108
18401
|
}
|
|
18109
18402
|
return Boolean(save);
|
|
@@ -18217,24 +18510,24 @@ function translateOpenAiRequest(body, options) {
|
|
|
18217
18510
|
async function collectOpenAiStream(stream) {
|
|
18218
18511
|
const collected = { text: "", toolCalls: [], finishReason: void 0, usage: void 0 };
|
|
18219
18512
|
for await (const part of stream) {
|
|
18220
|
-
const
|
|
18221
|
-
switch (
|
|
18513
|
+
const p14 = part;
|
|
18514
|
+
switch (p14.type) {
|
|
18222
18515
|
case "text-delta":
|
|
18223
|
-
collected.text +=
|
|
18516
|
+
collected.text += p14.textDelta ?? p14.text ?? "";
|
|
18224
18517
|
break;
|
|
18225
18518
|
case "tool-call":
|
|
18226
18519
|
collected.toolCalls.push({
|
|
18227
|
-
toolCallId:
|
|
18228
|
-
toolName:
|
|
18229
|
-
input:
|
|
18520
|
+
toolCallId: p14.toolCallId ?? "",
|
|
18521
|
+
toolName: p14.toolName ?? "",
|
|
18522
|
+
input: p14.input
|
|
18230
18523
|
});
|
|
18231
18524
|
break;
|
|
18232
18525
|
case "finish":
|
|
18233
|
-
collected.finishReason =
|
|
18234
|
-
collected.usage =
|
|
18526
|
+
collected.finishReason = p14.finishReason ?? collected.finishReason;
|
|
18527
|
+
collected.usage = p14.totalUsage ?? p14.usage ?? collected.usage;
|
|
18235
18528
|
break;
|
|
18236
18529
|
case "error":
|
|
18237
|
-
throw
|
|
18530
|
+
throw p14.error instanceof Error || p14.error && typeof p14.error === "object" ? p14.error : new Error(typeof p14.error === "string" ? p14.error : "Upstream stream failed");
|
|
18238
18531
|
}
|
|
18239
18532
|
}
|
|
18240
18533
|
return collected;
|
|
@@ -18359,22 +18652,22 @@ async function streamOpenAiResponse(model, params, responseModelId, onChunk, opt
|
|
|
18359
18652
|
|
|
18360
18653
|
`);
|
|
18361
18654
|
for await (const part of watchOpenAiStream(stream, budget)) {
|
|
18362
|
-
const
|
|
18363
|
-
switch (
|
|
18655
|
+
const p14 = part;
|
|
18656
|
+
switch (p14.type) {
|
|
18364
18657
|
case "text-delta":
|
|
18365
|
-
send({ role: "assistant", content:
|
|
18658
|
+
send({ role: "assistant", content: p14.textDelta ?? p14.text ?? "" });
|
|
18366
18659
|
break;
|
|
18367
18660
|
case "tool-input-start":
|
|
18368
|
-
send({ role: "assistant", tool_calls: [{ index: 0, id:
|
|
18661
|
+
send({ role: "assistant", tool_calls: [{ index: 0, id: p14.id ?? p14.toolCallId, type: "function", function: { name: p14.toolName, arguments: "" } }] });
|
|
18369
18662
|
break;
|
|
18370
18663
|
case "tool-input-delta":
|
|
18371
|
-
send({ tool_calls: [{ index: 0, function: { arguments:
|
|
18664
|
+
send({ tool_calls: [{ index: 0, function: { arguments: p14.delta ?? p14.text ?? p14.argsTextDelta ?? "" } }] });
|
|
18372
18665
|
break;
|
|
18373
18666
|
case "finish":
|
|
18374
|
-
send({},
|
|
18667
|
+
send({}, p14.finishReason || "stop");
|
|
18375
18668
|
break;
|
|
18376
18669
|
case "error":
|
|
18377
|
-
throw
|
|
18670
|
+
throw p14.error instanceof Error || p14.error && typeof p14.error === "object" ? p14.error : new Error(typeof p14.error === "string" ? p14.error : "Upstream stream failed");
|
|
18378
18671
|
}
|
|
18379
18672
|
}
|
|
18380
18673
|
onChunk("data: [DONE]\n\n");
|
|
@@ -19108,7 +19401,7 @@ function summarizeServerProviders(models) {
|
|
|
19108
19401
|
|
|
19109
19402
|
// src/server/provider-select.ts
|
|
19110
19403
|
import pc9 from "picocolors";
|
|
19111
|
-
import * as
|
|
19404
|
+
import * as p9 from "@clack/prompts";
|
|
19112
19405
|
function isSelected(list, id) {
|
|
19113
19406
|
return list.includes(id);
|
|
19114
19407
|
}
|
|
@@ -19118,7 +19411,7 @@ function resolveInitialServerProviders(initial, available) {
|
|
|
19118
19411
|
}
|
|
19119
19412
|
async function selectServerProviders(available, initial) {
|
|
19120
19413
|
if (available.length === 0) {
|
|
19121
|
-
|
|
19414
|
+
p9.log.warn("No providers available to expose.");
|
|
19122
19415
|
return null;
|
|
19123
19416
|
}
|
|
19124
19417
|
let selected = resolveInitialServerProviders(initial, available);
|
|
@@ -19144,31 +19437,31 @@ async function selectServerProviders(available, initial) {
|
|
|
19144
19437
|
}
|
|
19145
19438
|
options.push({ value: "__done__", label: "Done", hint: "" });
|
|
19146
19439
|
const header = selected.length === 0 ? `Exposed providers (0/${available.length}) \u2014 add providers to expose` : `Exposed providers (${selected.length}/${available.length}) \u2014 select to stop exposing`;
|
|
19147
|
-
const choice = await
|
|
19440
|
+
const choice = await p9.select({
|
|
19148
19441
|
message: header,
|
|
19149
19442
|
options,
|
|
19150
19443
|
initialValue: "__done__"
|
|
19151
19444
|
});
|
|
19152
|
-
if (
|
|
19445
|
+
if (p9.isCancel(choice) || choice === "__done__") {
|
|
19153
19446
|
if (selected.length === 0) {
|
|
19154
|
-
|
|
19447
|
+
p9.log.warn("Select at least one provider to expose.");
|
|
19155
19448
|
continue;
|
|
19156
19449
|
}
|
|
19157
19450
|
break;
|
|
19158
19451
|
}
|
|
19159
19452
|
if (choice === "__all__") {
|
|
19160
19453
|
selected = available.map((provider) => provider.id);
|
|
19161
|
-
|
|
19454
|
+
p9.log.success(`Exposing all ${available.length} providers.`);
|
|
19162
19455
|
continue;
|
|
19163
19456
|
}
|
|
19164
19457
|
if (choice === "__clear__") {
|
|
19165
19458
|
selected = [];
|
|
19166
|
-
|
|
19459
|
+
p9.log.success("Cleared provider list \u2014 add the ones you want to expose.");
|
|
19167
19460
|
continue;
|
|
19168
19461
|
}
|
|
19169
19462
|
if (choice === "__add__") {
|
|
19170
19463
|
if (unselected.length === 0) continue;
|
|
19171
|
-
const picked = await
|
|
19464
|
+
const picked = await p9.select({
|
|
19172
19465
|
message: "Which provider?",
|
|
19173
19466
|
options: unselected.map((provider) => ({
|
|
19174
19467
|
value: provider.id,
|
|
@@ -19176,7 +19469,7 @@ async function selectServerProviders(available, initial) {
|
|
|
19176
19469
|
hint: `${provider.modelCount} model${provider.modelCount !== 1 ? "s" : ""}`
|
|
19177
19470
|
}))
|
|
19178
19471
|
});
|
|
19179
|
-
if (
|
|
19472
|
+
if (p9.isCancel(picked)) continue;
|
|
19180
19473
|
selected = [...selected, picked];
|
|
19181
19474
|
continue;
|
|
19182
19475
|
}
|
|
@@ -19186,7 +19479,7 @@ async function selectServerProviders(available, initial) {
|
|
|
19186
19479
|
if (!id) continue;
|
|
19187
19480
|
const provider = lookup2.get(id);
|
|
19188
19481
|
selected = selected.filter((_, i) => i !== idx);
|
|
19189
|
-
|
|
19482
|
+
p9.log.success(`Removed ${provider?.name ?? id}.`);
|
|
19190
19483
|
}
|
|
19191
19484
|
}
|
|
19192
19485
|
return selected;
|
|
@@ -19194,7 +19487,7 @@ async function selectServerProviders(available, initial) {
|
|
|
19194
19487
|
|
|
19195
19488
|
// src/http-proxy/index.ts
|
|
19196
19489
|
import pc10 from "picocolors";
|
|
19197
|
-
import * as
|
|
19490
|
+
import * as p10 from "@clack/prompts";
|
|
19198
19491
|
|
|
19199
19492
|
// src/http-proxy/server.ts
|
|
19200
19493
|
import * as http2 from "http";
|
|
@@ -20198,6 +20491,14 @@ async function startHttpProxy(options) {
|
|
|
20198
20491
|
);
|
|
20199
20492
|
});
|
|
20200
20493
|
const sockets = /* @__PURE__ */ new Set();
|
|
20494
|
+
let warnedConnectSelfProxy = false;
|
|
20495
|
+
const connectTunnelAgents = /* @__PURE__ */ new Map();
|
|
20496
|
+
const connectTunnelAgent = (proxyUrl, targetUrl) => {
|
|
20497
|
+
if (!connectTunnelAgents.has(proxyUrl)) {
|
|
20498
|
+
connectTunnelAgents.set(proxyUrl, outboundHttpProxyAgent(targetUrl));
|
|
20499
|
+
}
|
|
20500
|
+
return connectTunnelAgents.get(proxyUrl);
|
|
20501
|
+
};
|
|
20201
20502
|
mitmServer.on("upgrade", (req, socket, head) => {
|
|
20202
20503
|
forwardAnthropicUpgrade(
|
|
20203
20504
|
req,
|
|
@@ -20227,6 +20528,65 @@ async function startHttpProxy(options) {
|
|
|
20227
20528
|
clientSocket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
|
|
20228
20529
|
return;
|
|
20229
20530
|
}
|
|
20531
|
+
const targetUrl = `https://${req.url}`;
|
|
20532
|
+
const outboundProxyUrl = outboundProxyUrlForTarget(targetUrl);
|
|
20533
|
+
const bound = proxyServer.address();
|
|
20534
|
+
const selfTargeting = outboundProxyUrl !== void 0 && bound !== null && typeof bound !== "string" && proxyUrlTargetsListener(outboundProxyUrl, bound.address, bound.port);
|
|
20535
|
+
if (selfTargeting && !warnedConnectSelfProxy) {
|
|
20536
|
+
warnedConnectSelfProxy = true;
|
|
20537
|
+
emitParentNotice(
|
|
20538
|
+
"clodex: HTTP(S)_PROXY points at this proxy; tunneling CONNECT direct"
|
|
20539
|
+
);
|
|
20540
|
+
}
|
|
20541
|
+
const outboundAgent = outboundProxyUrl !== void 0 && !selfTargeting ? connectTunnelAgent(outboundProxyUrl, targetUrl) : void 0;
|
|
20542
|
+
if (outboundAgent) {
|
|
20543
|
+
let upstream2;
|
|
20544
|
+
let proxyConnectStatus;
|
|
20545
|
+
req.once("proxyConnect", (response) => {
|
|
20546
|
+
proxyConnectStatus = response.statusCode;
|
|
20547
|
+
});
|
|
20548
|
+
clientSocket.once("close", () => {
|
|
20549
|
+
if (upstream2 && !upstream2.destroyed) upstream2.destroy();
|
|
20550
|
+
});
|
|
20551
|
+
void outboundAgent.connect(req, {
|
|
20552
|
+
host: target.host,
|
|
20553
|
+
port: target.port,
|
|
20554
|
+
secureEndpoint: false
|
|
20555
|
+
}).then((connected) => {
|
|
20556
|
+
upstream2 = connected;
|
|
20557
|
+
if (proxyConnectStatus !== 200 || clientSocket.destroyed) {
|
|
20558
|
+
connected.destroy();
|
|
20559
|
+
if (!clientSocket.destroyed) {
|
|
20560
|
+
clientSocket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n", () => clientSocket.destroy());
|
|
20561
|
+
}
|
|
20562
|
+
return;
|
|
20563
|
+
}
|
|
20564
|
+
let tunnelEstablished2 = false;
|
|
20565
|
+
sockets.add(connected);
|
|
20566
|
+
connected.once("close", () => {
|
|
20567
|
+
sockets.delete(connected);
|
|
20568
|
+
if (tunnelEstablished2 && !clientSocket.destroyed) clientSocket.destroy();
|
|
20569
|
+
});
|
|
20570
|
+
connected.once("error", () => {
|
|
20571
|
+
if (clientSocket.destroyed) return;
|
|
20572
|
+
if (tunnelEstablished2) {
|
|
20573
|
+
clientSocket.destroy();
|
|
20574
|
+
return;
|
|
20575
|
+
}
|
|
20576
|
+
clientSocket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n", () => clientSocket.destroy());
|
|
20577
|
+
});
|
|
20578
|
+
tunnelEstablished2 = true;
|
|
20579
|
+
clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
|
|
20580
|
+
if (head.length > 0) connected.write(head);
|
|
20581
|
+
clientSocket.pipe(connected);
|
|
20582
|
+
connected.pipe(clientSocket);
|
|
20583
|
+
}).catch(() => {
|
|
20584
|
+
if (!clientSocket.destroyed) {
|
|
20585
|
+
clientSocket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n", () => clientSocket.destroy());
|
|
20586
|
+
}
|
|
20587
|
+
});
|
|
20588
|
+
return;
|
|
20589
|
+
}
|
|
20230
20590
|
const upstream = net.connect(target.port, target.host);
|
|
20231
20591
|
let tunnelEstablished = false;
|
|
20232
20592
|
sockets.add(upstream);
|
|
@@ -20292,6 +20652,8 @@ async function startHttpProxy(options) {
|
|
|
20292
20652
|
for (const socket of sockets) socket.destroy();
|
|
20293
20653
|
await new Promise((resolve4) => proxyServer.close(() => resolve4()));
|
|
20294
20654
|
mitmServer.close();
|
|
20655
|
+
for (const agent of connectTunnelAgents.values()) agent?.destroy();
|
|
20656
|
+
connectTunnelAgents.clear();
|
|
20295
20657
|
anthropicAgent?.destroy();
|
|
20296
20658
|
adapter?.close();
|
|
20297
20659
|
}
|
|
@@ -20348,10 +20710,10 @@ function printHttpProxyModels(routes, aliases = []) {
|
|
|
20348
20710
|
}
|
|
20349
20711
|
function reportSkippedHttpProxyFavorites(loaded) {
|
|
20350
20712
|
if (loaded.unavailable.length > 0) {
|
|
20351
|
-
|
|
20713
|
+
p10.log.warn(`${loaded.unavailable.length} favorite${loaded.unavailable.length === 1 ? "" : "s"} unavailable or missing credentials.`);
|
|
20352
20714
|
}
|
|
20353
20715
|
if (loaded.unsupported.length > 0) {
|
|
20354
|
-
|
|
20716
|
+
p10.log.warn(
|
|
20355
20717
|
`${loaded.unsupported.length} favorite${loaded.unsupported.length === 1 ? "" : "s"} skipped \u2014 HTTP proxy mode supports non-Anthropic AI SDK routes only.`
|
|
20356
20718
|
);
|
|
20357
20719
|
}
|
|
@@ -20363,7 +20725,7 @@ function reportSkippedHttpProxyFavorites(loaded) {
|
|
|
20363
20725
|
describeModelAliasRejection(rejection.reason)
|
|
20364
20726
|
])
|
|
20365
20727
|
);
|
|
20366
|
-
|
|
20728
|
+
p10.log.warn(
|
|
20367
20729
|
`${loaded.unavailableAliases.length} model alias${loaded.unavailableAliases.length === 1 ? "" : "es"} skipped. Saved entries were preserved.
|
|
20368
20730
|
` + loaded.unavailableAliases.map((alias) => ` ${JSON.stringify(alias.name)} \u2014 ${reasonByAlias.get(alias) ?? "target unavailable"}`).join("\n")
|
|
20369
20731
|
);
|
|
@@ -20444,7 +20806,7 @@ async function runHttpProxyServerCommand(debug = false, webSocketDiagnostics = f
|
|
|
20444
20806
|
webSocketDiagnosticsLogPath
|
|
20445
20807
|
);
|
|
20446
20808
|
} catch (err) {
|
|
20447
|
-
|
|
20809
|
+
p10.log.error(`Failed to start HTTP proxy: ${err instanceof Error ? err.message : String(err)}`);
|
|
20448
20810
|
return 1;
|
|
20449
20811
|
}
|
|
20450
20812
|
const { handle, loaded, caWarning } = started;
|
|
@@ -20647,8 +21009,8 @@ async function getServerPasswordForQuickMode(mode, passwordOverride) {
|
|
|
20647
21009
|
if (trimmedOverride) return { password: trimmedOverride, wasSaved: false };
|
|
20648
21010
|
const savedPassword = await getSavedServerPassword();
|
|
20649
21011
|
if (savedPassword) return { password: savedPassword, wasSaved: true };
|
|
20650
|
-
|
|
20651
|
-
|
|
21012
|
+
p11.log.error("Network server quick-start needs a saved server password or `--password <value>`.");
|
|
21013
|
+
p11.log.info("Run `clodex server` and choose Configure & start to save one, or pass a one-run password.");
|
|
20652
21014
|
return void 0;
|
|
20653
21015
|
}
|
|
20654
21016
|
function savedServerRunConfig() {
|
|
@@ -20682,16 +21044,16 @@ function shouldUseQuickServerMode(options) {
|
|
|
20682
21044
|
return Boolean(options.quick || hasServerRunOverrides(options) || !process.stdin.isTTY);
|
|
20683
21045
|
}
|
|
20684
21046
|
async function configureExposedProviders() {
|
|
20685
|
-
|
|
20686
|
-
const
|
|
20687
|
-
|
|
21047
|
+
p11.log.info("Add providers to expose. Listed providers are removed when selected \u2014 like favorites.");
|
|
21048
|
+
const spinner6 = p11.spinner();
|
|
21049
|
+
spinner6.start("Loading providers...");
|
|
20688
21050
|
const catalog = await fetchProviderCatalog({ agent: "server" });
|
|
20689
|
-
|
|
21051
|
+
spinner6.stop("");
|
|
20690
21052
|
const available = providerOptionsFromCatalog(catalog);
|
|
20691
21053
|
const picked = await selectServerProviders(available, getServerExposedProviders() ?? void 0);
|
|
20692
21054
|
if (!picked) return void 0;
|
|
20693
21055
|
setServerExposedProviders(picked);
|
|
20694
|
-
|
|
21056
|
+
p11.log.success(`Saved ${picked.length} provider${picked.length !== 1 ? "s" : ""} for future server runs.`);
|
|
20695
21057
|
return picked;
|
|
20696
21058
|
}
|
|
20697
21059
|
async function runServerWizard() {
|
|
@@ -20705,7 +21067,7 @@ async function runServerWizard() {
|
|
|
20705
21067
|
if (favoritesOnly === null) return void 0;
|
|
20706
21068
|
setServerFavoritesOnly(favoritesOnly);
|
|
20707
21069
|
if (favoritesOnly) {
|
|
20708
|
-
|
|
21070
|
+
p11.log.info("Manage favorites with `clodex models`.");
|
|
20709
21071
|
}
|
|
20710
21072
|
let exposedProviders = null;
|
|
20711
21073
|
if (!favoritesOnly) {
|
|
@@ -20735,14 +21097,14 @@ async function runServerCommand(options = {}) {
|
|
|
20735
21097
|
if (options.httpProxy) {
|
|
20736
21098
|
const hasGatewayOptions = options.quick || options.listenMode !== void 0 || options.providersMode !== void 0 || options.maskGatewayIds !== void 0 || options.password !== void 0;
|
|
20737
21099
|
if (hasGatewayOptions) {
|
|
20738
|
-
|
|
21100
|
+
p11.log.error("--proxy is a local-only server mode and cannot be combined with endpoint-mode server options.");
|
|
20739
21101
|
return 1;
|
|
20740
21102
|
}
|
|
20741
21103
|
return runHttpProxyServerCommand(false, options.wsDiagnostics, options.port, noDiscovery);
|
|
20742
21104
|
}
|
|
20743
21105
|
const apiKey = await resolveServerUpstreamApiKey();
|
|
20744
21106
|
if (!apiKey) {
|
|
20745
|
-
|
|
21107
|
+
p11.log.error("No providers configured. Run `clodex providers` to add OpenAI.");
|
|
20746
21108
|
return 1;
|
|
20747
21109
|
}
|
|
20748
21110
|
const quickMode = shouldUseQuickServerMode(options);
|
|
@@ -20757,8 +21119,8 @@ async function runServerCommand(options = {}) {
|
|
|
20757
21119
|
const { password: serverPassword, wasSaved: passwordWasSaved } = pwResult;
|
|
20758
21120
|
const mode = runConfig.listenMode;
|
|
20759
21121
|
const host = mode === "network" ? "0.0.0.0" : "127.0.0.1";
|
|
20760
|
-
const
|
|
20761
|
-
|
|
21122
|
+
const spinner6 = p11.spinner();
|
|
21123
|
+
spinner6.start("Fetching available models...");
|
|
20762
21124
|
let models;
|
|
20763
21125
|
try {
|
|
20764
21126
|
models = await loadServerModels();
|
|
@@ -20768,26 +21130,26 @@ async function runServerCommand(options = {}) {
|
|
|
20768
21130
|
if (runConfig.favoritesOnly) {
|
|
20769
21131
|
const favorites = loadPreferences().favoriteModels ?? [];
|
|
20770
21132
|
if (favorites.length === 0) {
|
|
20771
|
-
|
|
20772
|
-
|
|
21133
|
+
spinner6.stop(pc11.red("No favorite models configured"));
|
|
21134
|
+
p11.log.error("Run `clodex models` to add favorites, or turn off favorites-only in the server wizard.");
|
|
20773
21135
|
return 1;
|
|
20774
21136
|
}
|
|
20775
21137
|
models = filterServerModelsByFavorites(models, favorites).slice(0, MAX_MODEL_CATALOG);
|
|
20776
21138
|
if (models.length === 0) {
|
|
20777
|
-
|
|
20778
|
-
|
|
21139
|
+
spinner6.stop(pc11.red("No favorite models matched the current provider filter"));
|
|
21140
|
+
p11.log.error("Adjust favorites with `clodex models` or change exposed providers in the server wizard.");
|
|
20779
21141
|
return 1;
|
|
20780
21142
|
}
|
|
20781
21143
|
}
|
|
20782
21144
|
if (runConfig.favoritesOnly) {
|
|
20783
|
-
|
|
21145
|
+
p11.log.info(
|
|
20784
21146
|
`Favorites-only mode active \u2014 GET /anthropic/v1/models returns ${models.length} favorites.`
|
|
20785
21147
|
);
|
|
20786
|
-
|
|
21148
|
+
p11.log.info("Desktop/Cowork picker will only show these. Edit with `clodex models`.");
|
|
20787
21149
|
}
|
|
20788
21150
|
if (models.length === 0) {
|
|
20789
|
-
|
|
20790
|
-
|
|
21151
|
+
spinner6.stop(pc11.red("No models to expose"));
|
|
21152
|
+
p11.log.error("Add providers with `clodex providers add` or configure exposed providers in the server wizard.");
|
|
20791
21153
|
return 1;
|
|
20792
21154
|
}
|
|
20793
21155
|
const localCount = models.filter((m) => m.apiKey !== void 0).length;
|
|
@@ -20795,10 +21157,10 @@ async function runServerCommand(options = {}) {
|
|
|
20795
21157
|
const filterNote = runConfig.exposedProviders ? ` \u2014 ${runConfig.exposedProviders.length} provider${runConfig.exposedProviders.length !== 1 ? "s" : ""}` : "";
|
|
20796
21158
|
const favoritesNote = runConfig.favoritesOnly ? " \u2014 favorites only" : "";
|
|
20797
21159
|
const maskNote = runConfig.maskGatewayIds ? " \u2014 discovery ids masked" : "";
|
|
20798
|
-
|
|
20799
|
-
if (summary)
|
|
21160
|
+
spinner6.stop(`Loaded ${models.length} models (${localCount} from registry providers)${filterNote}${favoritesNote}${maskNote}`);
|
|
21161
|
+
if (summary) p11.log.info(summary);
|
|
20800
21162
|
} catch (err) {
|
|
20801
|
-
|
|
21163
|
+
spinner6.stop(pc11.red("Failed to load models"));
|
|
20802
21164
|
console.error(pc11.red(String(err instanceof Error ? err.message : err)));
|
|
20803
21165
|
return 1;
|
|
20804
21166
|
}
|
|
@@ -20820,7 +21182,7 @@ async function runServerCommand(options = {}) {
|
|
|
20820
21182
|
...collidingAliases.flatMap(({ sources }) => sources.map((source) => `${JSON.stringify(source.name)} \u2014 conflicts with a catalog model id`))
|
|
20821
21183
|
];
|
|
20822
21184
|
if (aliasWarnings.length > 0) {
|
|
20823
|
-
|
|
21185
|
+
p11.log.warn(
|
|
20824
21186
|
`${aliasWarnings.length} saved model alias${aliasWarnings.length === 1 ? "" : "es"} inactive. Saved entries were preserved.
|
|
20825
21187
|
${aliasWarnings.join("\n ")}`
|
|
20826
21188
|
);
|
|
@@ -20902,7 +21264,7 @@ function removeFavorite(list, fav) {
|
|
|
20902
21264
|
}
|
|
20903
21265
|
|
|
20904
21266
|
// src/favorites-picker.ts
|
|
20905
|
-
import * as
|
|
21267
|
+
import * as p12 from "@clack/prompts";
|
|
20906
21268
|
import pc12 from "picocolors";
|
|
20907
21269
|
|
|
20908
21270
|
// src/favorite-provider-display.ts
|
|
@@ -20980,25 +21342,25 @@ async function pickGlobalFavoriteModel(providers, favorites, opts) {
|
|
|
20980
21342
|
if (index.length === 0) return null;
|
|
20981
21343
|
const freeOnly = opts?.freeOnly === true;
|
|
20982
21344
|
while (true) {
|
|
20983
|
-
const searchInput = await
|
|
21345
|
+
const searchInput = await p12.text({
|
|
20984
21346
|
message: freeOnly ? `Search free models (${filterGlobalFavoriteIndex(index, "", { freeOnly: true }).length} models):` : `Search all providers (${index.length} models):`,
|
|
20985
21347
|
placeholder: "e.g. deepseek, claude, sonnet"
|
|
20986
21348
|
});
|
|
20987
|
-
if (
|
|
20988
|
-
const fallback = await
|
|
21349
|
+
if (p12.isCancel(searchInput)) {
|
|
21350
|
+
const fallback = await p12.select({
|
|
20989
21351
|
message: "Add a favorite",
|
|
20990
21352
|
options: [
|
|
20991
21353
|
{ value: "back", label: pc12.cyan("\u2190 Back to favorites"), hint: "" },
|
|
20992
21354
|
{ value: ADD_BY_PROVIDER, label: pc12.cyan("Browse by provider \u2192"), hint: "Pick one provider first" }
|
|
20993
21355
|
]
|
|
20994
21356
|
});
|
|
20995
|
-
if (
|
|
21357
|
+
if (p12.isCancel(fallback) || fallback === "back") return null;
|
|
20996
21358
|
if (fallback === ADD_BY_PROVIDER) return ADD_BY_PROVIDER;
|
|
20997
21359
|
continue;
|
|
20998
21360
|
}
|
|
20999
21361
|
const matched = filterGlobalFavoriteIndex(index, String(searchInput), { freeOnly });
|
|
21000
21362
|
if (matched.length === 0) {
|
|
21001
|
-
|
|
21363
|
+
p12.log.warn("No models match \u2014 try a different search");
|
|
21002
21364
|
continue;
|
|
21003
21365
|
}
|
|
21004
21366
|
const result = await pickModelFromPagedList(
|
|
@@ -21016,7 +21378,7 @@ async function pickGlobalFavoriteModel(providers, favorites, opts) {
|
|
|
21016
21378
|
const picked = parseGlobalFavoritePickKey(result.id, matched);
|
|
21017
21379
|
if (!picked) continue;
|
|
21018
21380
|
if (isFavorite(favorites, { providerId: picked.providerId, modelId: picked.model.id })) {
|
|
21019
|
-
|
|
21381
|
+
p12.log.warn(`${picked.model.name || picked.model.id} (${picked.providerName}) is already in your favorites.`);
|
|
21020
21382
|
continue;
|
|
21021
21383
|
}
|
|
21022
21384
|
return picked;
|
|
@@ -21086,7 +21448,7 @@ function resolveLaunchTarget(explicit, prefs, _agent = "claude") {
|
|
|
21086
21448
|
}
|
|
21087
21449
|
function findProviderAndModel(providers, target) {
|
|
21088
21450
|
if (!target.providerId || !target.modelId) return null;
|
|
21089
|
-
const provider = providers.find((
|
|
21451
|
+
const provider = providers.find((p14) => p14.id === target.providerId);
|
|
21090
21452
|
if (!provider) return null;
|
|
21091
21453
|
const model = provider.models.find((m) => m.id === target.modelId);
|
|
21092
21454
|
if (!model) return null;
|
|
@@ -21292,7 +21654,7 @@ function buildAndPublish(step) {
|
|
|
21292
21654
|
const compiled = step.compile(cscPath, CSC_ARGS, scratch);
|
|
21293
21655
|
const builtExe = join9(scratch, LAUNCHER_EXE_NAME);
|
|
21294
21656
|
if (compiled.error || compiled.status !== 0 || !existsSync7(builtExe)) {
|
|
21295
|
-
const output = [compiled.stdout, compiled.stderr].map((
|
|
21657
|
+
const output = [compiled.stdout, compiled.stderr].map((text6) => text6.trim()).filter(Boolean).join("\n");
|
|
21296
21658
|
return {
|
|
21297
21659
|
ok: false,
|
|
21298
21660
|
reason: "compile-failed",
|
|
@@ -21703,6 +22065,7 @@ ${pc13.bold("Providers:")}
|
|
|
21703
22065
|
openai OpenAI API key (platform.openai.com)
|
|
21704
22066
|
openai-oauth ChatGPT/Codex plan OAuth \u2014 sign in with clodex providers auth openai
|
|
21705
22067
|
opencode-go OpenCode Go API key \u2014 add with clodex providers add
|
|
22068
|
+
custom-<name> Any OpenAI-compatible server (OpenRouter, LM Studio, \u2026) \u2014 add with clodex providers add
|
|
21706
22069
|
|
|
21707
22070
|
${pc13.bold("Model switching:")}
|
|
21708
22071
|
Run clodex models to save favorites (max ${MAX_MODEL_CATALOG}).
|
|
@@ -21927,9 +22290,9 @@ ${pc13.bold("Behavior:")}
|
|
|
21927
22290
|
${pc13.bold("Setup guide:")}
|
|
21928
22291
|
docs/windows-setup.md in the clodex package (also on GitHub).`;
|
|
21929
22292
|
}
|
|
21930
|
-
function printHelp(
|
|
22293
|
+
function printHelp(text6) {
|
|
21931
22294
|
console.log(`
|
|
21932
|
-
${
|
|
22295
|
+
${text6}
|
|
21933
22296
|
`);
|
|
21934
22297
|
}
|
|
21935
22298
|
function reportInactiveCatalogAliases(modelAliases) {
|
|
@@ -21938,7 +22301,7 @@ function reportInactiveCatalogAliases(modelAliases) {
|
|
|
21938
22301
|
const warningLines = unavailableAliases.flatMap((alias) => alias.sourceNames?.length ? alias.sourceNames.map((name) => ` ${JSON.stringify(name)} \u2014 ${alias.unavailableReason}`) : [
|
|
21939
22302
|
` ${JSON.stringify(alias.savedName ?? alias.name)} \u2014 ${alias.unavailableReason}`
|
|
21940
22303
|
]);
|
|
21941
|
-
|
|
22304
|
+
p13.log.warn(
|
|
21942
22305
|
`${warningLines.length} saved model alias${warningLines.length === 1 ? "" : "es"} inactive. Saved entries were preserved.
|
|
21943
22306
|
` + warningLines.join("\n")
|
|
21944
22307
|
);
|
|
@@ -21956,11 +22319,11 @@ async function launchClaudeViaCatalog(catalogRoutes, startingRoute, modelAliases
|
|
|
21956
22319
|
void 0,
|
|
21957
22320
|
modelAliases
|
|
21958
22321
|
);
|
|
21959
|
-
|
|
22322
|
+
p13.log.info(
|
|
21960
22323
|
`Switch menu active \u2014 proxy on port ${proxyHandle.port} ` + pc13.dim(`(${catalogRoutes.length} model${catalogRoutes.length !== 1 ? "s" : ""} in /model)`)
|
|
21961
22324
|
);
|
|
21962
22325
|
} catch (err) {
|
|
21963
|
-
|
|
22326
|
+
p13.log.error(`Failed to start proxy: ${err instanceof Error ? err.message : String(err)}`);
|
|
21964
22327
|
return 1;
|
|
21965
22328
|
}
|
|
21966
22329
|
const childEnv = buildChildEnv(
|
|
@@ -21973,7 +22336,7 @@ async function launchClaudeViaCatalog(catalogRoutes, startingRoute, modelAliases
|
|
|
21973
22336
|
);
|
|
21974
22337
|
const debugLogPath = prepareClaudeTraceLog();
|
|
21975
22338
|
const traceArgs = trace ? ["--debug-file", debugLogPath] : [];
|
|
21976
|
-
if (trace)
|
|
22339
|
+
if (trace) p13.log.info(`Debug log: ${debugLogPath}`);
|
|
21977
22340
|
const exitCode = await launchClaude(
|
|
21978
22341
|
childEnv,
|
|
21979
22342
|
claudeCodeClientModelId(startingRoute.aliasId, contextWindow),
|
|
@@ -21984,9 +22347,9 @@ async function launchClaudeViaCatalog(catalogRoutes, startingRoute, modelAliases
|
|
|
21984
22347
|
return exitCode;
|
|
21985
22348
|
}
|
|
21986
22349
|
var CLACK_CONTEXT_REPORTER = {
|
|
21987
|
-
info: (message) =>
|
|
21988
|
-
warn: (message) =>
|
|
21989
|
-
success: (message) =>
|
|
22350
|
+
info: (message) => p13.log.info(message),
|
|
22351
|
+
warn: (message) => p13.log.warn(message),
|
|
22352
|
+
success: (message) => p13.log.success(message)
|
|
21990
22353
|
};
|
|
21991
22354
|
var STDERR_CONTEXT_REPORTER = {
|
|
21992
22355
|
info: (message) => console.error(message),
|
|
@@ -22001,12 +22364,12 @@ function applyContextStopAssignments(assignments, save, report, saveHint = "Add
|
|
|
22001
22364
|
for (const assignment of assignments) {
|
|
22002
22365
|
const cached = modelsByProvider.get(assignment.providerId)?.find((model) => model.id === assignment.modelId);
|
|
22003
22366
|
if (!cached) {
|
|
22004
|
-
|
|
22367
|
+
p13.log.error(
|
|
22005
22368
|
`${modelAliasTarget(assignment)} is not in the saved provider catalog. Add the provider and refresh its models first.`
|
|
22006
22369
|
);
|
|
22007
22370
|
return 1;
|
|
22008
22371
|
}
|
|
22009
|
-
const limits = contextLimitsFrom(cached,
|
|
22372
|
+
const limits = contextLimitsFrom(cached, lookupKnownContextWindow(assignment.modelId));
|
|
22010
22373
|
const resolved = resolveContextStop(limits, assignment.stop ?? "standard");
|
|
22011
22374
|
const target = modelAliasTarget(assignment);
|
|
22012
22375
|
const label = assignment.label === target ? target : `${assignment.label} (${target})`;
|
|
@@ -22035,12 +22398,12 @@ function applyContextStopAssignments(assignments, save, report, saveHint = "Add
|
|
|
22035
22398
|
async function runModelsCommand(opts = {}) {
|
|
22036
22399
|
const changesAlias = opts.alias !== void 0 || opts.unalias !== void 0;
|
|
22037
22400
|
if (changesAlias && (opts.list || opts.alias !== void 0 && opts.unalias !== void 0)) {
|
|
22038
|
-
|
|
22401
|
+
p13.log.error("--alias/--unalias apply one at a time to proxy-mode favorites.");
|
|
22039
22402
|
return 1;
|
|
22040
22403
|
}
|
|
22041
22404
|
const assignments = opts.contextAssignments ?? [];
|
|
22042
22405
|
if (opts.saveContext && assignments.length === 0) {
|
|
22043
|
-
|
|
22406
|
+
p13.log.error("--save applies to --context assignments; none were given.");
|
|
22044
22407
|
return 1;
|
|
22045
22408
|
}
|
|
22046
22409
|
if (assignments.length > 0) {
|
|
@@ -22059,7 +22422,7 @@ async function runModelsCommand(opts = {}) {
|
|
|
22059
22422
|
if (opts.alias !== void 0) {
|
|
22060
22423
|
const parsed = parseModelAliasAssignment(opts.alias);
|
|
22061
22424
|
if ("error" in parsed) {
|
|
22062
|
-
|
|
22425
|
+
p13.log.error(parsed.error);
|
|
22063
22426
|
return 1;
|
|
22064
22427
|
}
|
|
22065
22428
|
const prefs2 = loadPreferences();
|
|
@@ -22067,19 +22430,19 @@ async function runModelsCommand(opts = {}) {
|
|
|
22067
22430
|
(favorite) => favorite.providerId === parsed.providerId && favorite.modelId === parsed.modelId
|
|
22068
22431
|
);
|
|
22069
22432
|
if (!isSavedFavorite) {
|
|
22070
|
-
|
|
22071
|
-
|
|
22433
|
+
p13.log.error(`${modelAliasTarget(parsed)} is not a saved favorite.`);
|
|
22434
|
+
p13.log.info("Add it with `clodex models`, then save the alias.");
|
|
22072
22435
|
return 1;
|
|
22073
22436
|
}
|
|
22074
22437
|
if (prefs2.modelAliases !== void 0 && !Array.isArray(prefs2.modelAliases)) {
|
|
22075
|
-
|
|
22438
|
+
p13.log.error('Saved model aliases are malformed: "modelAliases" must be an array.');
|
|
22076
22439
|
return 1;
|
|
22077
22440
|
}
|
|
22078
22441
|
const aliases = prefs2.modelAliases ?? [];
|
|
22079
22442
|
const modelAliases = aliases.filter((alias) => !modelAliasMatchesName(alias, parsed.name));
|
|
22080
22443
|
modelAliases.push(parsed);
|
|
22081
22444
|
savePreferences({ modelAliases });
|
|
22082
|
-
|
|
22445
|
+
p13.log.success(`Saved model alias ${parsed.name} \u2192 ${modelAliasTarget(parsed)}.`);
|
|
22083
22446
|
return 0;
|
|
22084
22447
|
}
|
|
22085
22448
|
if (opts.unalias !== void 0) {
|
|
@@ -22087,18 +22450,18 @@ async function runModelsCommand(opts = {}) {
|
|
|
22087
22450
|
const name = canonicalModelAliasName(requestedName);
|
|
22088
22451
|
const prefs2 = loadPreferences();
|
|
22089
22452
|
if (prefs2.modelAliases !== void 0 && !Array.isArray(prefs2.modelAliases)) {
|
|
22090
|
-
|
|
22453
|
+
p13.log.error('Saved model aliases are malformed: "modelAliases" must be an array.');
|
|
22091
22454
|
return 1;
|
|
22092
22455
|
}
|
|
22093
22456
|
const aliases = prefs2.modelAliases ?? [];
|
|
22094
22457
|
const modelAliases = aliases.filter((alias) => !modelAliasMatchesStoredName(alias, requestedName));
|
|
22095
22458
|
const removedCount = aliases.length - modelAliases.length;
|
|
22096
22459
|
if (removedCount === 0) {
|
|
22097
|
-
|
|
22460
|
+
p13.log.error(`No model alias named ${JSON.stringify(requestedName)} is saved.`);
|
|
22098
22461
|
return 1;
|
|
22099
22462
|
}
|
|
22100
22463
|
savePreferences({ modelAliases });
|
|
22101
|
-
|
|
22464
|
+
p13.log.success(
|
|
22102
22465
|
removedCount === 1 ? `Removed model alias ${name || JSON.stringify(requestedName)}.` : `Removed ${removedCount} model aliases named ${name || JSON.stringify(requestedName)}.`
|
|
22103
22466
|
);
|
|
22104
22467
|
return 0;
|
|
@@ -22110,25 +22473,25 @@ async function runModelsCommand(opts = {}) {
|
|
|
22110
22473
|
reportSkippedHttpProxyFavorites(loaded);
|
|
22111
22474
|
return 0;
|
|
22112
22475
|
} catch (err) {
|
|
22113
|
-
|
|
22476
|
+
p13.log.error(`Could not load proxy models: ${err instanceof Error ? err.message : String(err)}`);
|
|
22114
22477
|
return 1;
|
|
22115
22478
|
}
|
|
22116
22479
|
}
|
|
22117
22480
|
const maxFavorites = MAX_MODEL_CATALOG;
|
|
22118
22481
|
const scopeName = "Favorite Models";
|
|
22119
22482
|
relayIntro(scopeName);
|
|
22120
|
-
const
|
|
22121
|
-
|
|
22483
|
+
const spinner6 = p13.spinner();
|
|
22484
|
+
spinner6.start("Loading providers...");
|
|
22122
22485
|
const catalog = await fetchProviderCatalog();
|
|
22123
|
-
|
|
22486
|
+
spinner6.stop("");
|
|
22124
22487
|
const allProviders = providersForPicker(catalog);
|
|
22125
22488
|
const favoriteProviders = allProviders.map((provider) => ({
|
|
22126
22489
|
...provider,
|
|
22127
22490
|
name: favoriteProviderDisplayName(provider)
|
|
22128
22491
|
}));
|
|
22129
22492
|
if (favoriteProviders.length === 0) {
|
|
22130
|
-
|
|
22131
|
-
|
|
22493
|
+
p13.log.warn("No providers found.");
|
|
22494
|
+
p13.log.info(`${pc13.dim("Add a provider with ")}${pc13.cyan("clodex providers")}${pc13.dim(".")}`);
|
|
22132
22495
|
relayOutro("Done");
|
|
22133
22496
|
return 0;
|
|
22134
22497
|
}
|
|
@@ -22157,19 +22520,19 @@ async function runModelsCommand(opts = {}) {
|
|
|
22157
22520
|
});
|
|
22158
22521
|
options.push({ value: "__done__", label: "Done", hint: "" });
|
|
22159
22522
|
const header = favorites.length === 0 ? `${scopeName} (0/${maxFavorites})` : `${scopeName} (${favorites.length}/${maxFavorites}) \u2014 select to remove`;
|
|
22160
|
-
const choice = await
|
|
22523
|
+
const choice = await p13.select({
|
|
22161
22524
|
message: header,
|
|
22162
22525
|
options,
|
|
22163
22526
|
initialValue: "__done__"
|
|
22164
22527
|
});
|
|
22165
|
-
if (
|
|
22528
|
+
if (p13.isCancel(choice) || choice === "__done__") break;
|
|
22166
22529
|
if (choice === "__add__") {
|
|
22167
22530
|
if (atCap) {
|
|
22168
|
-
|
|
22531
|
+
p13.log.warn(`Limit of ${maxFavorites} favorites reached \u2014 remove one first.`);
|
|
22169
22532
|
continue;
|
|
22170
22533
|
}
|
|
22171
22534
|
const globalCount = buildGlobalFavoriteIndex(favoriteProviders).length;
|
|
22172
|
-
const addPath = await
|
|
22535
|
+
const addPath = await p13.select({
|
|
22173
22536
|
message: "Add a favorite",
|
|
22174
22537
|
options: [
|
|
22175
22538
|
{
|
|
@@ -22184,7 +22547,7 @@ async function runModelsCommand(opts = {}) {
|
|
|
22184
22547
|
}
|
|
22185
22548
|
]
|
|
22186
22549
|
});
|
|
22187
|
-
if (
|
|
22550
|
+
if (p13.isCancel(addPath)) continue;
|
|
22188
22551
|
let provider;
|
|
22189
22552
|
let browsedMultiple = [];
|
|
22190
22553
|
if (addPath === "global") {
|
|
@@ -22199,12 +22562,12 @@ async function runModelsCommand(opts = {}) {
|
|
|
22199
22562
|
let currentInitialProvider = void 0;
|
|
22200
22563
|
while (true) {
|
|
22201
22564
|
const providerOptions = favoriteProviders.map((ap) => providerSelectOption(ap));
|
|
22202
|
-
const pickedProviderId = await
|
|
22565
|
+
const pickedProviderId = await p13.select({
|
|
22203
22566
|
message: "Which provider?",
|
|
22204
22567
|
options: providerOptions,
|
|
22205
22568
|
initialValue: currentInitialProvider
|
|
22206
22569
|
});
|
|
22207
|
-
if (
|
|
22570
|
+
if (p13.isCancel(pickedProviderId)) break;
|
|
22208
22571
|
provider = favoriteProviders.find((ap) => ap.id === pickedProviderId);
|
|
22209
22572
|
const options2 = provider.models.map((m) => {
|
|
22210
22573
|
const favorited = isFavorite(favorites, { providerId: provider.id, modelId: m.id });
|
|
@@ -22215,12 +22578,12 @@ async function runModelsCommand(opts = {}) {
|
|
|
22215
22578
|
hint: favorited ? pc13.yellow("\u2605 already favorite") : ""
|
|
22216
22579
|
};
|
|
22217
22580
|
});
|
|
22218
|
-
const pickedModelIds = await
|
|
22581
|
+
const pickedModelIds = await p13.multiselect({
|
|
22219
22582
|
message: `Select models to add from ${provider.name} ${pc13.dim("(Space to select, Enter to confirm)")}`,
|
|
22220
22583
|
options: options2,
|
|
22221
22584
|
required: false
|
|
22222
22585
|
});
|
|
22223
|
-
if (
|
|
22586
|
+
if (p13.isCancel(pickedModelIds)) {
|
|
22224
22587
|
currentInitialProvider = provider.id;
|
|
22225
22588
|
continue;
|
|
22226
22589
|
}
|
|
@@ -22255,27 +22618,27 @@ async function runModelsCommand(opts = {}) {
|
|
|
22255
22618
|
if (addedModels.length > 0) {
|
|
22256
22619
|
if (addedModels.length === 1) {
|
|
22257
22620
|
const modelName = addedModels[0].name || addedModels[0].id;
|
|
22258
|
-
|
|
22621
|
+
p13.log.success(`Added ${modelName} (${provider.name}) to favorites.`);
|
|
22259
22622
|
} else {
|
|
22260
|
-
|
|
22623
|
+
p13.log.success(`Added ${addedModels.length} models from ${provider.name} to favorites.`);
|
|
22261
22624
|
}
|
|
22262
22625
|
}
|
|
22263
22626
|
if (duplicateCount > 0) {
|
|
22264
|
-
|
|
22627
|
+
p13.log.warn(`${duplicateCount} selected model(s) were already in your favorites.`);
|
|
22265
22628
|
}
|
|
22266
22629
|
if (limitReached) {
|
|
22267
|
-
|
|
22630
|
+
p13.log.warn(`Limit of ${maxFavorites} favorites reached \u2014 some selected models could not be added.`);
|
|
22268
22631
|
}
|
|
22269
22632
|
} else if (choice.startsWith("fav-")) {
|
|
22270
22633
|
const idx = parseInt(choice.slice(4), 10);
|
|
22271
22634
|
const fav = favorites[idx];
|
|
22272
22635
|
const entry = modelLookup.get(`${fav.providerId}:${fav.modelId}`);
|
|
22273
22636
|
const label = entry ? `${entry.modelName} (${entry.providerName})` : fav.modelId;
|
|
22274
|
-
const confirmed = await
|
|
22275
|
-
if (
|
|
22637
|
+
const confirmed = await p13.confirm({ message: `Remove ${label} from favorites?` });
|
|
22638
|
+
if (p13.isCancel(confirmed) || !confirmed) continue;
|
|
22276
22639
|
favorites = removeFavorite(favorites, fav);
|
|
22277
22640
|
favoritesDirty = true;
|
|
22278
|
-
|
|
22641
|
+
p13.log.success(`Removed ${label} from favorites.`);
|
|
22279
22642
|
}
|
|
22280
22643
|
}
|
|
22281
22644
|
if (favoritesDirty) {
|
|
@@ -22289,8 +22652,8 @@ async function runModelsCommand(opts = {}) {
|
|
|
22289
22652
|
}
|
|
22290
22653
|
async function runClaudeHttpProxyCommand(parsed, claudeArgs, agentStdout) {
|
|
22291
22654
|
if (parsed.launchProvider || parsed.launchModel) {
|
|
22292
|
-
|
|
22293
|
-
|
|
22655
|
+
p13.log.error("--provider/--model select endpoint-mode routes and cannot be combined with --proxy.");
|
|
22656
|
+
p13.log.info("Use `-- --model clodex:<provider-id>:<model-id>` to start on a listed proxy-mode favorite.");
|
|
22294
22657
|
return 1;
|
|
22295
22658
|
}
|
|
22296
22659
|
if (!agentStdout) relayIntro("Claude Code \u2014 Proxy Mode");
|
|
@@ -22308,7 +22671,7 @@ async function runClaudeHttpProxyCommand(parsed, claudeArgs, agentStdout) {
|
|
|
22308
22671
|
console.log("");
|
|
22309
22672
|
return 0;
|
|
22310
22673
|
} catch (err) {
|
|
22311
|
-
|
|
22674
|
+
p13.log.error(`Could not load proxy models: ${err instanceof Error ? err.message : String(err)}`);
|
|
22312
22675
|
return 1;
|
|
22313
22676
|
}
|
|
22314
22677
|
}
|
|
@@ -22318,13 +22681,13 @@ async function runClaudeHttpProxyCommand(parsed, claudeArgs, agentStdout) {
|
|
|
22318
22681
|
try {
|
|
22319
22682
|
started = await startConfiguredHttpProxy(0, parsed.trace, inferenceLogPath, proxyDebugLogPath);
|
|
22320
22683
|
} catch (err) {
|
|
22321
|
-
|
|
22684
|
+
p13.log.error(`Failed to start proxy: ${err instanceof Error ? err.message : String(err)}`);
|
|
22322
22685
|
return 1;
|
|
22323
22686
|
}
|
|
22324
22687
|
const { handle, loaded, caWarning } = started;
|
|
22325
22688
|
if (caWarning) {
|
|
22326
22689
|
if (agentStdout) emitParentNotice(`clodex: ${caWarning}`);
|
|
22327
|
-
else
|
|
22690
|
+
else p13.log.warn(caWarning);
|
|
22328
22691
|
}
|
|
22329
22692
|
const inheritedProxyPort = (() => {
|
|
22330
22693
|
const value = process.env["HTTPS_PROXY"] ?? process.env["HTTP_PROXY"] ?? process.env["https_proxy"] ?? process.env["http_proxy"];
|
|
@@ -22358,20 +22721,20 @@ async function runClaudeHttpProxyCommand(parsed, claudeArgs, agentStdout) {
|
|
|
22358
22721
|
};
|
|
22359
22722
|
process.once("exit", onProcessExit);
|
|
22360
22723
|
if (!agentStdout) {
|
|
22361
|
-
|
|
22362
|
-
|
|
22724
|
+
p13.log.info(`Proxy started on port ${handle.port}; Claude Code's Anthropic auth remains active.`);
|
|
22725
|
+
p13.log.info(`Inference request log: ${handle.inferenceLogPath}`);
|
|
22363
22726
|
printHttpProxyModels(loaded.routes, loaded.aliases);
|
|
22364
22727
|
reportSkippedHttpProxyFavorites(loaded);
|
|
22365
22728
|
if (loaded.routes.length > 0) {
|
|
22366
|
-
|
|
22729
|
+
p13.log.info("Switch with `/model <listed-name>`.");
|
|
22367
22730
|
}
|
|
22368
22731
|
}
|
|
22369
22732
|
const childEnv = buildHttpProxyChildEnv(handle.port, handle.caCertPath);
|
|
22370
22733
|
const debugLogPath = parsed.trace ? prepareClaudeTraceLog(getSessionLogPath("claude-debug")) : void 0;
|
|
22371
22734
|
const traceArgs = debugLogPath ? ["--debug-file", debugLogPath] : [];
|
|
22372
22735
|
if (debugLogPath && !agentStdout) {
|
|
22373
|
-
|
|
22374
|
-
if (proxyDebugLogPath)
|
|
22736
|
+
p13.log.info(`Claude debug log: ${debugLogPath}`);
|
|
22737
|
+
if (proxyDebugLogPath) p13.log.info(`Adapter debug log: ${proxyDebugLogPath}`);
|
|
22375
22738
|
}
|
|
22376
22739
|
try {
|
|
22377
22740
|
const exitCode = await launchClaude(childEnv, void 0, [...traceArgs, ...claudeArgs]);
|
|
@@ -22456,7 +22819,7 @@ Error: ${launchPlan.error}
|
|
|
22456
22819
|
return 1;
|
|
22457
22820
|
}
|
|
22458
22821
|
} else {
|
|
22459
|
-
const catalogSpinner =
|
|
22822
|
+
const catalogSpinner = p13.spinner();
|
|
22460
22823
|
catalogSpinner.start("Loading your providers...");
|
|
22461
22824
|
try {
|
|
22462
22825
|
catalog = await fetchProviderCatalog();
|
|
@@ -22471,11 +22834,11 @@ Error: ${launchPlan.error}
|
|
|
22471
22834
|
const blockedLaunchReason = launchPlan.skip && launchPlan.target?.providerId ? catalog.blockedProviders.get(launchPlan.target.providerId) : void 0;
|
|
22472
22835
|
if (allProviders.length === 0) {
|
|
22473
22836
|
if (blockedLaunchReason) {
|
|
22474
|
-
|
|
22837
|
+
p13.log.error(blockedLaunchReason);
|
|
22475
22838
|
return 1;
|
|
22476
22839
|
}
|
|
22477
|
-
|
|
22478
|
-
|
|
22840
|
+
p13.log.warn("No providers available.");
|
|
22841
|
+
p13.log.info(pc13.dim("Run clodex providers to get started."));
|
|
22479
22842
|
return 0;
|
|
22480
22843
|
}
|
|
22481
22844
|
const providerOptions = allProviders.map((lp) => providerSelectOption(lp));
|
|
@@ -22492,7 +22855,7 @@ Error: ${launchPlan.error}
|
|
|
22492
22855
|
if (launchPlan.skip && launchPlan.target) {
|
|
22493
22856
|
const resolved = findProviderAndModel(allProviders, launchPlan.target);
|
|
22494
22857
|
if (!resolved) {
|
|
22495
|
-
|
|
22858
|
+
p13.log.error(
|
|
22496
22859
|
blockedLaunchReason ?? `Provider/model not found: ${launchPlan.target.providerId} / ${launchPlan.target.modelId}`
|
|
22497
22860
|
);
|
|
22498
22861
|
return 1;
|
|
@@ -22500,19 +22863,19 @@ Error: ${launchPlan.error}
|
|
|
22500
22863
|
activeProvider = resolved.provider;
|
|
22501
22864
|
selectedModel = resolved.model;
|
|
22502
22865
|
if (!agentStdout) {
|
|
22503
|
-
|
|
22866
|
+
p13.log.step(`Using ${selectedModel.name || selectedModel.id} (${activeProvider.name})`);
|
|
22504
22867
|
}
|
|
22505
22868
|
if (!dryRun) recordLaunchSelection("claude", activeProvider.id, selectedModel.id, prefs);
|
|
22506
22869
|
} else {
|
|
22507
22870
|
let currentInitialProvider = initialProvider;
|
|
22508
22871
|
while (true) {
|
|
22509
|
-
const chosen = await
|
|
22872
|
+
const chosen = await p13.select({
|
|
22510
22873
|
message: "Which provider?",
|
|
22511
22874
|
options: providerOptions,
|
|
22512
22875
|
initialValue: currentInitialProvider
|
|
22513
22876
|
});
|
|
22514
|
-
if (
|
|
22515
|
-
|
|
22877
|
+
if (p13.isCancel(chosen)) {
|
|
22878
|
+
p13.cancel("Cancelled.");
|
|
22516
22879
|
return 0;
|
|
22517
22880
|
}
|
|
22518
22881
|
const providerChoice = chosen;
|
|
@@ -22524,7 +22887,7 @@ Error: ${launchPlan.error}
|
|
|
22524
22887
|
if (prov && mod) available.push({ provider: prov, model: mod });
|
|
22525
22888
|
}
|
|
22526
22889
|
if (available.length === 0) {
|
|
22527
|
-
|
|
22890
|
+
p13.log.warn("No saved favorites are currently available.");
|
|
22528
22891
|
return 0;
|
|
22529
22892
|
}
|
|
22530
22893
|
const favOptions = available.map((f, i) => ({
|
|
@@ -22532,13 +22895,13 @@ Error: ${launchPlan.error}
|
|
|
22532
22895
|
label: `${f.model.name || f.model.id} \u2014 ${f.provider.name}`,
|
|
22533
22896
|
hint: f.model.id
|
|
22534
22897
|
}));
|
|
22535
|
-
const pickedIdx = await
|
|
22898
|
+
const pickedIdx = await p13.select({
|
|
22536
22899
|
message: "Starting model?",
|
|
22537
22900
|
options: favOptions,
|
|
22538
22901
|
initialValue: "0"
|
|
22539
22902
|
});
|
|
22540
|
-
if (
|
|
22541
|
-
|
|
22903
|
+
if (p13.isCancel(pickedIdx)) {
|
|
22904
|
+
p13.cancel("Cancelled.");
|
|
22542
22905
|
return 0;
|
|
22543
22906
|
}
|
|
22544
22907
|
const sel = available[Number(pickedIdx)];
|
|
@@ -22567,12 +22930,12 @@ Error: ${launchPlan.error}
|
|
|
22567
22930
|
);
|
|
22568
22931
|
const startingRoute = resolveRoute(activeProvider.id, selectedModel.id) ?? null;
|
|
22569
22932
|
if (!startingRoute) {
|
|
22570
|
-
|
|
22933
|
+
p13.log.error("Could not resolve a proxy route for the selected model.");
|
|
22571
22934
|
return 1;
|
|
22572
22935
|
}
|
|
22573
22936
|
const { routes: catalogRoutes, droppedFavorites } = buildCatalogRoutes(startingRoute, favorites, resolveRoute);
|
|
22574
22937
|
if (droppedFavorites.length > 0) {
|
|
22575
|
-
|
|
22938
|
+
p13.log.warn(
|
|
22576
22939
|
`Skipping ${droppedFavorites.length} favorite${droppedFavorites.length === 1 ? "" : "s"} that are no longer available in /model`
|
|
22577
22940
|
);
|
|
22578
22941
|
}
|
|
@@ -22626,7 +22989,7 @@ Error: ${launchPlan.error}
|
|
|
22626
22989
|
}
|
|
22627
22990
|
const launchApiKey = await resolveLocalProviderApiKey(activeProvider);
|
|
22628
22991
|
if (!anonymousProvider && !launchApiKey?.trim()) {
|
|
22629
|
-
|
|
22992
|
+
p13.log.error(
|
|
22630
22993
|
`No credential found for ${activeProvider.name}. Add a key or sign in with clodex providers.`
|
|
22631
22994
|
);
|
|
22632
22995
|
return 1;
|
|
@@ -22652,9 +23015,9 @@ Error: ${launchPlan.error}
|
|
|
22652
23015
|
},
|
|
22653
23016
|
launchApiKey ?? ""
|
|
22654
23017
|
);
|
|
22655
|
-
if (!isAgentStdoutMode())
|
|
23018
|
+
if (!isAgentStdoutMode()) p13.log.info(`Anthropic proxy started on port ${proxyHandle.port}`);
|
|
22656
23019
|
} catch (err) {
|
|
22657
|
-
|
|
23020
|
+
p13.log.error(`Failed to start Anthropic proxy: ${err instanceof Error ? err.message : String(err)}`);
|
|
22658
23021
|
return 1;
|
|
22659
23022
|
}
|
|
22660
23023
|
childEnv = buildChildEnv(
|
|
@@ -22698,12 +23061,12 @@ Error: ${launchPlan.error}
|
|
|
22698
23061
|
launchApiKey ?? ""
|
|
22699
23062
|
);
|
|
22700
23063
|
if (!isAgentStdoutMode()) {
|
|
22701
|
-
|
|
23064
|
+
p13.log.info(
|
|
22702
23065
|
`SDK adapter proxy started on port ${proxyHandle.port}` + (selectedModel.npm ? pc13.dim(` (${selectedModel.npm})`) : "")
|
|
22703
23066
|
);
|
|
22704
23067
|
}
|
|
22705
23068
|
} catch (err) {
|
|
22706
|
-
|
|
23069
|
+
p13.log.error(`Failed to start SDK adapter proxy: ${err instanceof Error ? err.message : String(err)}`);
|
|
22707
23070
|
return 1;
|
|
22708
23071
|
}
|
|
22709
23072
|
childEnv = buildChildEnv(
|
|
@@ -22719,7 +23082,7 @@ Error: ${launchPlan.error}
|
|
|
22719
23082
|
}
|
|
22720
23083
|
const debugLogPath = prepareClaudeTraceLog();
|
|
22721
23084
|
const traceArgs = trace ? ["--debug-file", debugLogPath] : [];
|
|
22722
|
-
if (trace)
|
|
23085
|
+
if (trace) p13.log.info(`Debug log: ${debugLogPath}`);
|
|
22723
23086
|
const exitCode = await launchClaude(
|
|
22724
23087
|
childEnv,
|
|
22725
23088
|
claudeCodeClientModelId(selectedModel.id, selectedModel.contextWindow),
|