@bman654/clodex 2.14.0 → 2.15.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/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.15.0",
|
|
348
350
|
publishConfig: {
|
|
349
351
|
access: "public"
|
|
350
352
|
},
|
|
@@ -3432,7 +3434,7 @@ function savedStopsAfter(current, assignments) {
|
|
|
3432
3434
|
}
|
|
3433
3435
|
|
|
3434
3436
|
// src/patch-transforms.ts
|
|
3435
|
-
var PATCH_TRANSFORMS_VERSION =
|
|
3437
|
+
var PATCH_TRANSFORMS_VERSION = 13;
|
|
3436
3438
|
var NATIVE_EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
3437
3439
|
var BASE_EFFORT_LEVELS = ["low", "medium", "high"];
|
|
3438
3440
|
function projectNativeEffort(effort) {
|
|
@@ -3537,24 +3539,24 @@ function applyClodexPatches(source, config) {
|
|
|
3537
3539
|
}
|
|
3538
3540
|
const reEsc = (s) => s.replace(/[.*+?^$\{\}()|[\]\\]/g, "\\$&");
|
|
3539
3541
|
const q = (s) => JSON.stringify(s);
|
|
3540
|
-
function
|
|
3542
|
+
function log13(status, name, extra) {
|
|
3541
3543
|
report.push(extra === void 0 ? { status, name } : { status, name, extra });
|
|
3542
3544
|
}
|
|
3543
3545
|
function applyOnce(name, regex, fn, { marker, required, noopIsSkip } = {}) {
|
|
3544
3546
|
if (marker && js.includes(marker)) {
|
|
3545
|
-
|
|
3547
|
+
log13("SKIP", name, "already patched");
|
|
3546
3548
|
return;
|
|
3547
3549
|
}
|
|
3548
3550
|
const g = new RegExp(regex.source, regex.flags.includes("g") ? regex.flags : regex.flags + "g");
|
|
3549
3551
|
const matches = js.match(g);
|
|
3550
3552
|
const count = matches ? matches.length : 0;
|
|
3551
3553
|
if (count === 0) {
|
|
3552
|
-
|
|
3554
|
+
log13("FAIL", name, "anchor not found");
|
|
3553
3555
|
if (required) fail("clodex patch: required patch failed: " + name);
|
|
3554
3556
|
return;
|
|
3555
3557
|
}
|
|
3556
3558
|
if (count > 1) {
|
|
3557
|
-
|
|
3559
|
+
log13("FAIL", name, "anchor matched " + count + " times (expected 1)");
|
|
3558
3560
|
if (required) fail("clodex patch: ambiguous anchor: " + name);
|
|
3559
3561
|
return;
|
|
3560
3562
|
}
|
|
@@ -3562,14 +3564,14 @@ function applyClodexPatches(source, config) {
|
|
|
3562
3564
|
js = js.replace(regex, fn);
|
|
3563
3565
|
if (js === before) {
|
|
3564
3566
|
if (noopIsSkip) {
|
|
3565
|
-
|
|
3567
|
+
log13("SKIP", name, "already patched");
|
|
3566
3568
|
return;
|
|
3567
3569
|
}
|
|
3568
|
-
|
|
3570
|
+
log13("FAIL", name, "replacement made no change");
|
|
3569
3571
|
if (required) fail(name);
|
|
3570
3572
|
return;
|
|
3571
3573
|
}
|
|
3572
|
-
|
|
3574
|
+
log13("OK", name);
|
|
3573
3575
|
}
|
|
3574
3576
|
function extendAliasArray(arrLiteral) {
|
|
3575
3577
|
const toAdd = IDENTITIES.filter((a) => !new RegExp('"' + reEsc(a) + '"').test(arrLiteral));
|
|
@@ -3589,14 +3591,22 @@ function applyClodexPatches(source, config) {
|
|
|
3589
3591
|
{ required: true, noopIsSkip: true }
|
|
3590
3592
|
);
|
|
3591
3593
|
{
|
|
3592
|
-
const
|
|
3594
|
+
const RESOLVER_ANCHOR = /(case"best":\{[^{}]*\})/;
|
|
3595
|
+
const RESOLVER_BUDGET = 2e3 + ALIASES.reduce((n, a) => n + 2 * a.length + 17, 0);
|
|
3596
|
+
const RESOLVER_SWITCH = new RegExp(
|
|
3597
|
+
'case"best":\\{[^{}]*\\}[\\s\\S]{0,' + RESOLVER_BUDGET + "}?default:return"
|
|
3598
|
+
);
|
|
3599
|
+
const resolver = js.match(RESOLVER_SWITCH)?.[0] ?? "";
|
|
3600
|
+
const missing = ALIASES.filter(
|
|
3601
|
+
(a) => !new RegExp("case" + reEsc(q(a)) + ":return").test(resolver)
|
|
3602
|
+
);
|
|
3593
3603
|
const cases = missing.map((a) => "case" + q(a) + ":return " + q(a) + ";").join("");
|
|
3594
3604
|
if (ALIASES.length === 0) {
|
|
3595
|
-
|
|
3605
|
+
log13("SKIP", "PATCH 6: alias resolver switch", "no aliases configured");
|
|
3596
3606
|
} else {
|
|
3597
3607
|
applyOnce(
|
|
3598
3608
|
"PATCH 6: alias resolver switch",
|
|
3599
|
-
|
|
3609
|
+
RESOLVER_ANCHOR,
|
|
3600
3610
|
(m) => m + cases,
|
|
3601
3611
|
{ required: true, noopIsSkip: true }
|
|
3602
3612
|
);
|
|
@@ -3615,9 +3625,9 @@ function applyClodexPatches(source, config) {
|
|
|
3615
3625
|
const pickerSite = "PATCH 5: model picker options";
|
|
3616
3626
|
const modelSelections = [...js.matchAll(/\(([\w$]+)==="opus"\|\|\1==="sonnet"\)/g)];
|
|
3617
3627
|
if (ALIASES.length === 0) {
|
|
3618
|
-
|
|
3628
|
+
log13("SKIP", pickerSite, "no aliases configured");
|
|
3619
3629
|
} else if (modelSelections.length !== 1) {
|
|
3620
|
-
|
|
3630
|
+
log13("FAIL", pickerSite, "model selection appears " + modelSelections.length + " times (expected 1)");
|
|
3621
3631
|
} else {
|
|
3622
3632
|
applyOnce(
|
|
3623
3633
|
pickerSite,
|
|
@@ -3745,11 +3755,11 @@ function applyClodexPatches(source, config) {
|
|
|
3745
3755
|
"CLAUDE_CODE_OTEL_DIAG_STDERR"
|
|
3746
3756
|
];
|
|
3747
3757
|
const MIN_SCRUBBED_ENV_NAMES = 1;
|
|
3748
|
-
const blockEndIndex = (
|
|
3758
|
+
const blockEndIndex = (text6, open2) => {
|
|
3749
3759
|
const depths = [0];
|
|
3750
3760
|
const templates = [false];
|
|
3751
|
-
for (let i = open2; i <
|
|
3752
|
-
const ch =
|
|
3761
|
+
for (let i = open2; i < text6.length; i++) {
|
|
3762
|
+
const ch = text6[i];
|
|
3753
3763
|
const inTemplate = templates[templates.length - 1];
|
|
3754
3764
|
if (inTemplate) {
|
|
3755
3765
|
if (ch === "\\") {
|
|
@@ -3761,7 +3771,7 @@ function applyClodexPatches(source, config) {
|
|
|
3761
3771
|
depths.pop();
|
|
3762
3772
|
continue;
|
|
3763
3773
|
}
|
|
3764
|
-
if (ch === "$" &&
|
|
3774
|
+
if (ch === "$" && text6[i + 1] === "{") {
|
|
3765
3775
|
templates.push(false);
|
|
3766
3776
|
depths.push(0);
|
|
3767
3777
|
i++;
|
|
@@ -3774,12 +3784,12 @@ function applyClodexPatches(source, config) {
|
|
|
3774
3784
|
}
|
|
3775
3785
|
if (ch === '"' || ch === "'") {
|
|
3776
3786
|
const quote = ch;
|
|
3777
|
-
for (i++; i <
|
|
3778
|
-
if (
|
|
3787
|
+
for (i++; i < text6.length; i++) {
|
|
3788
|
+
if (text6[i] === "\\") {
|
|
3779
3789
|
i++;
|
|
3780
3790
|
continue;
|
|
3781
3791
|
}
|
|
3782
|
-
if (
|
|
3792
|
+
if (text6[i] === quote) break;
|
|
3783
3793
|
}
|
|
3784
3794
|
continue;
|
|
3785
3795
|
}
|
|
@@ -3788,12 +3798,12 @@ function applyClodexPatches(source, config) {
|
|
|
3788
3798
|
depths.push(0);
|
|
3789
3799
|
continue;
|
|
3790
3800
|
}
|
|
3791
|
-
if (ch === "/" &&
|
|
3792
|
-
while (i <
|
|
3801
|
+
if (ch === "/" && text6[i + 1] === "/") {
|
|
3802
|
+
while (i < text6.length && text6[i] !== "\n") i++;
|
|
3793
3803
|
continue;
|
|
3794
3804
|
}
|
|
3795
|
-
if (ch === "/" &&
|
|
3796
|
-
i =
|
|
3805
|
+
if (ch === "/" && text6[i + 1] === "*") {
|
|
3806
|
+
i = text6.indexOf("*/", i + 2);
|
|
3797
3807
|
if (i < 0) return -1;
|
|
3798
3808
|
i++;
|
|
3799
3809
|
continue;
|
|
@@ -3816,7 +3826,7 @@ function applyClodexPatches(source, config) {
|
|
|
3816
3826
|
};
|
|
3817
3827
|
const passthroughSites = source.includes(marker) ? 1 : (source.match(/\)return process\.env;let [\w$]+=\{/g) ?? []).length;
|
|
3818
3828
|
if (passthroughSites !== 1) {
|
|
3819
|
-
|
|
3829
|
+
log13("FAIL", patchName, "child env passthrough appears " + passthroughSites + " times (expected 1)");
|
|
3820
3830
|
fail("clodex patch: required patch failed: " + patchName);
|
|
3821
3831
|
}
|
|
3822
3832
|
applyOnce(
|
|
@@ -3831,7 +3841,7 @@ function applyClodexPatches(source, config) {
|
|
|
3831
3841
|
const scrubbedSeen = scrubbedEnvNames.filter((name) => body.includes(name));
|
|
3832
3842
|
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;
|
|
3833
3843
|
if (why !== void 0) {
|
|
3834
|
-
|
|
3844
|
+
log13("FAIL", patchName, "target validation failed: " + why);
|
|
3835
3845
|
fail("clodex patch: child network environment target validation failed: " + why);
|
|
3836
3846
|
}
|
|
3837
3847
|
const restoredBody = body.replace(/process\.env/g, "_clodexChildEnv");
|
|
@@ -6038,6 +6048,22 @@ function normalizeGoogleDisplayName(rawName, bareId) {
|
|
|
6038
6048
|
return stripGoogleModelPrefix(trimmed);
|
|
6039
6049
|
}
|
|
6040
6050
|
|
|
6051
|
+
// src/registry/server-text.ts
|
|
6052
|
+
function isControlCode(code) {
|
|
6053
|
+
return code <= 31 || code >= 127 && code <= 159;
|
|
6054
|
+
}
|
|
6055
|
+
function hasControlChars(value) {
|
|
6056
|
+
for (let i = 0; i < value.length; i++) {
|
|
6057
|
+
if (isControlCode(value.charCodeAt(i))) return true;
|
|
6058
|
+
}
|
|
6059
|
+
return false;
|
|
6060
|
+
}
|
|
6061
|
+
function printableServerText(value) {
|
|
6062
|
+
let out = "";
|
|
6063
|
+
for (const ch of value) out += isControlCode(ch.charCodeAt(0)) ? " " : ch;
|
|
6064
|
+
return out.trim();
|
|
6065
|
+
}
|
|
6066
|
+
|
|
6041
6067
|
// src/trace-log.ts
|
|
6042
6068
|
import {
|
|
6043
6069
|
chmodSync as chmodSync3,
|
|
@@ -6127,8 +6153,8 @@ function compactLogValueWithMarker(value, max) {
|
|
|
6127
6153
|
function systemPreview(system) {
|
|
6128
6154
|
if (typeof system === "string") return compactLogValue(system, REQUEST_PREVIEW_MAX) || void 0;
|
|
6129
6155
|
if (!Array.isArray(system)) return void 0;
|
|
6130
|
-
const
|
|
6131
|
-
return compactLogValue(
|
|
6156
|
+
const text6 = system.map((block) => typeof block === "string" ? block : block && typeof block === "object" && typeof block.text === "string" ? block.text : "").filter(Boolean).join(" ");
|
|
6157
|
+
return compactLogValue(text6, REQUEST_PREVIEW_MAX) || void 0;
|
|
6132
6158
|
}
|
|
6133
6159
|
function inlineSystemPreview(messages) {
|
|
6134
6160
|
if (!Array.isArray(messages)) return void 0;
|
|
@@ -6154,9 +6180,9 @@ function getLatestMessagePreview(messages, system) {
|
|
|
6154
6180
|
if (typeof content === "string") {
|
|
6155
6181
|
summary = content;
|
|
6156
6182
|
} else if (Array.isArray(content)) {
|
|
6157
|
-
const
|
|
6158
|
-
if (
|
|
6159
|
-
summary =
|
|
6183
|
+
const text6 = content.filter((block) => Boolean(block && typeof block === "object")).filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text).join(" ");
|
|
6184
|
+
if (text6.trim()) {
|
|
6185
|
+
summary = text6;
|
|
6160
6186
|
} else {
|
|
6161
6187
|
const blockTypes = [...new Set(content.filter((block) => Boolean(block && typeof block === "object")).map((block) => typeof block.type === "string" ? block.type : "unknown"))];
|
|
6162
6188
|
if (blockTypes.length > 0) blockSummary = `${role}: [${blockTypes.join(", ")}]`;
|
|
@@ -6428,8 +6454,8 @@ function writeSecureLogLine(path, line) {
|
|
|
6428
6454
|
function printTraceLog(debugLogPath) {
|
|
6429
6455
|
if (!existsSync3(debugLogPath)) return;
|
|
6430
6456
|
const raw = readFileSync6(debugLogPath, "utf8");
|
|
6431
|
-
const
|
|
6432
|
-
const errorLines =
|
|
6457
|
+
const log13 = redactTraceLog(raw);
|
|
6458
|
+
const errorLines = log13.split("\n").filter(
|
|
6433
6459
|
(l) => l.includes("error") || l.includes("Error") || l.includes('"type":"error"') || l.includes("status") || l.includes("resolveModel failed") || l.includes("resolveModel fallback")
|
|
6434
6460
|
);
|
|
6435
6461
|
console.log("\n" + pc2.bold(pc2.cyan("\u2500\u2500 Debug trace \u2500\u2500")));
|
|
@@ -6491,13 +6517,14 @@ function parseModelList(body, npm, allowBareArray) {
|
|
|
6491
6517
|
for (const row of rows) {
|
|
6492
6518
|
const rawId = row.id?.trim();
|
|
6493
6519
|
if (!rawId) continue;
|
|
6520
|
+
if (hasControlChars(rawId) || typeof row.name === "string" && hasControlChars(row.name.trim())) continue;
|
|
6494
6521
|
const { id, upstreamModelId: upstreamModelId2 } = normalizeGoogleModelId(rawId, npm);
|
|
6495
6522
|
const family = id.split(/[-/:]/)[0] ?? id;
|
|
6496
6523
|
const cost = parseNativePricing(row.pricing);
|
|
6497
6524
|
const freeStatus = classifyFreeStatus({
|
|
6498
6525
|
model: { cost, isFree: row.isFree }
|
|
6499
6526
|
});
|
|
6500
|
-
const contextWindow = row.context_length ?? row.contextWindow ?? row.context_window ?? resolveContextWindow(id);
|
|
6527
|
+
const contextWindow = row.context_length ?? row.contextWindow ?? row.context_window ?? row.max_model_len ?? resolveContextWindow(id);
|
|
6501
6528
|
models.push({
|
|
6502
6529
|
id,
|
|
6503
6530
|
name: normalizeGoogleDisplayName(row.name, id),
|
|
@@ -6922,9 +6949,9 @@ function collectTargets(contents, kind) {
|
|
|
6922
6949
|
function readNpmShim(shimPath) {
|
|
6923
6950
|
const kind = launcherKind(shimPath);
|
|
6924
6951
|
const unreadable = kind === "sh" ? { kind: "not-a-shim" } : { kind: "unreadable-launcher" };
|
|
6925
|
-
const
|
|
6926
|
-
if (
|
|
6927
|
-
const rawTargets = collectTargets(
|
|
6952
|
+
const text6 = readLauncherText(shimPath);
|
|
6953
|
+
if (text6 === null) return unreadable;
|
|
6954
|
+
const rawTargets = collectTargets(text6, kind);
|
|
6928
6955
|
if (rawTargets.length === 0) return unreadable;
|
|
6929
6956
|
if (!rawTargets.every(isLiteralPath)) return unreadable;
|
|
6930
6957
|
const absolute = rawTargets.map((target) => resolve2(
|
|
@@ -7052,11 +7079,11 @@ function inspectClaudeNativeBinaryPlaceholder(path) {
|
|
|
7052
7079
|
offset += bytesRead;
|
|
7053
7080
|
}
|
|
7054
7081
|
if (offset !== size) return null;
|
|
7055
|
-
const
|
|
7082
|
+
const text6 = bytes.toString("utf8");
|
|
7056
7083
|
const signals = [
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7084
|
+
text6.includes(MISSING_BINARY_MARKER),
|
|
7085
|
+
text6.includes(INSTALL_SCRIPT_MARKER),
|
|
7086
|
+
text6.includes(OMIT_OPTIONAL_MARKER) && text6.includes(IGNORE_SCRIPTS_MARKER)
|
|
7060
7087
|
];
|
|
7061
7088
|
if (signals.filter(Boolean).length < 2) return null;
|
|
7062
7089
|
return inspectNativePackage(path);
|
|
@@ -8885,11 +8912,11 @@ function reasoningGapShape(expected, actual, full, storedTail, index) {
|
|
|
8885
8912
|
}
|
|
8886
8913
|
var warnedReasoningGaps = /* @__PURE__ */ new Set();
|
|
8887
8914
|
var MAX_REASONING_GAP_WARNINGS = 3;
|
|
8888
|
-
function warnReasoningNormalizationGap(fields,
|
|
8915
|
+
function warnReasoningNormalizationGap(fields, log13) {
|
|
8889
8916
|
const signature = fields.join(",");
|
|
8890
8917
|
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`;
|
|
8891
8918
|
try {
|
|
8892
|
-
|
|
8919
|
+
log13?.(`reasoning normalization gap: ${signature}`);
|
|
8893
8920
|
} catch {
|
|
8894
8921
|
}
|
|
8895
8922
|
if (warnedReasoningGaps.has(signature)) return;
|
|
@@ -8935,12 +8962,12 @@ function toolArgumentNormalizationGap(expected, actual, defaults, requiredProps)
|
|
|
8935
8962
|
}
|
|
8936
8963
|
var warnedToolArgumentGaps = /* @__PURE__ */ new Set();
|
|
8937
8964
|
var MAX_TOOL_ARGUMENT_GAP_WARNINGS = 3;
|
|
8938
|
-
function warnToolArgumentNormalizationGap(gap,
|
|
8965
|
+
function warnToolArgumentNormalizationGap(gap, log13) {
|
|
8939
8966
|
const tool3 = typeof gap.tool === "string" ? gap.tool : "unknown";
|
|
8940
8967
|
const signature = `${tool3}:filler`;
|
|
8941
8968
|
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`;
|
|
8942
8969
|
try {
|
|
8943
|
-
|
|
8970
|
+
log13?.(`tool argument normalization gap: ${signature}`);
|
|
8944
8971
|
} catch {
|
|
8945
8972
|
}
|
|
8946
8973
|
if (warnedToolArgumentGaps.has(signature)) return;
|
|
@@ -8951,7 +8978,7 @@ function warnToolArgumentNormalizationGap(gap, log12) {
|
|
|
8951
8978
|
emitParentNotice("clodex: warning: further tool-argument normalization warnings suppressed.");
|
|
8952
8979
|
}
|
|
8953
8980
|
}
|
|
8954
|
-
function continuationMismatchDetails(entry, payload, defaults,
|
|
8981
|
+
function continuationMismatchDetails(entry, payload, defaults, log13, warnOnGap = false, deferWarnings) {
|
|
8955
8982
|
const raise = (warn) => {
|
|
8956
8983
|
if (deferWarnings) deferWarnings.push(warn);
|
|
8957
8984
|
else warn();
|
|
@@ -8969,7 +8996,7 @@ function continuationMismatchDetails(entry, payload, defaults, log12, warnOnGap
|
|
|
8969
8996
|
const expected = mismatch < prefix.length ? prefix[mismatch] : void 0;
|
|
8970
8997
|
const actual = mismatch < full.length ? full[mismatch] : void 0;
|
|
8971
8998
|
const reasoningGap = reasoningNormalizationGap(expected, actual, defaults);
|
|
8972
|
-
if (reasoningGap && warnOnGap) raise(() => warnReasoningNormalizationGap(reasoningGap,
|
|
8999
|
+
if (reasoningGap && warnOnGap) raise(() => warnReasoningNormalizationGap(reasoningGap, log13));
|
|
8973
9000
|
let gapExpected = expected;
|
|
8974
9001
|
if (conversationItemKind(expected) === "reasoning" && conversationItemKind(actual) === "function_call") {
|
|
8975
9002
|
for (let index = mismatch; index < prefix.length; index += 1) {
|
|
@@ -8993,10 +9020,10 @@ function continuationMismatchDetails(entry, payload, defaults, log12, warnOnGap
|
|
|
8993
9020
|
}
|
|
8994
9021
|
if (toolArgumentGap?.equalAfterStrip === true) {
|
|
8995
9022
|
const gap = toolArgumentGap;
|
|
8996
|
-
if (warnOnGap) raise(() => warnToolArgumentNormalizationGap(gap,
|
|
9023
|
+
if (warnOnGap) raise(() => warnToolArgumentNormalizationGap(gap, log13));
|
|
8997
9024
|
} else if (toolArgumentGap && warnOnGap) {
|
|
8998
9025
|
try {
|
|
8999
|
-
|
|
9026
|
+
log13?.(`tool argument mismatch beyond the strip rule: ${String(toolArgumentGap.tool)}`);
|
|
9000
9027
|
} catch {
|
|
9001
9028
|
}
|
|
9002
9029
|
}
|
|
@@ -9021,17 +9048,17 @@ function continuationMismatchDetails(entry, payload, defaults, log12, warnOnGap
|
|
|
9021
9048
|
...toolArgumentGap ? { toolArgumentNormalizationGap: toolArgumentGap } : {}
|
|
9022
9049
|
};
|
|
9023
9050
|
}
|
|
9024
|
-
function continuationMismatchSummary(entry, payload, defaults,
|
|
9025
|
-
const details = precomputedDetails ?? continuationMismatchDetails(entry, payload, defaults,
|
|
9051
|
+
function continuationMismatchSummary(entry, payload, defaults, log13, mismatchDump = false, precomputedDetails) {
|
|
9052
|
+
const details = precomputedDetails ?? continuationMismatchDetails(entry, payload, defaults, log13, true);
|
|
9026
9053
|
let summary = `full_items=${details.fullItems} expected_prefix_items=${details.expectedPrefixItems} first_mismatch=${details.firstMismatch} expected=${details.expectedKind} actual=${details.actualKind}`;
|
|
9027
9054
|
if (details.expectedHash || details.actualHash) {
|
|
9028
9055
|
summary += ` expected_hash=${details.expectedHash ?? "none"} actual_hash=${details.actualHash ?? "none"}`;
|
|
9029
|
-
if (mismatchDump &&
|
|
9056
|
+
if (mismatchDump && log13) {
|
|
9030
9057
|
const full = inputArray(payload);
|
|
9031
9058
|
const prefix = [...entry.requestInput ?? [], ...entry.expectedAssistant ?? []];
|
|
9032
9059
|
const index = details.firstMismatch;
|
|
9033
|
-
|
|
9034
|
-
|
|
9060
|
+
log13(`mismatch dump expected[${index}]: ${mismatchDumpLine(prefix, index, defaults)}`);
|
|
9061
|
+
log13(`mismatch dump actual[${index}]: ${mismatchDumpLine(full, index, defaults)}`);
|
|
9035
9062
|
}
|
|
9036
9063
|
}
|
|
9037
9064
|
return summary;
|
|
@@ -9374,7 +9401,7 @@ function requiredToolProps(payload) {
|
|
|
9374
9401
|
if (record.type !== "function" || typeof record.name !== "string") return;
|
|
9375
9402
|
const parameters = record.parameters;
|
|
9376
9403
|
const required = parameters && typeof parameters === "object" && Array.isArray(parameters.required) ? parameters.required : [];
|
|
9377
|
-
map.set(record.name, new Set(required.filter((
|
|
9404
|
+
map.set(record.name, new Set(required.filter((p14) => typeof p14 === "string")));
|
|
9378
9405
|
};
|
|
9379
9406
|
if (Array.isArray(payload.tools)) for (const tool3 of payload.tools) add(tool3);
|
|
9380
9407
|
return map;
|
|
@@ -9403,12 +9430,12 @@ function expectedAssistantItems(ctx) {
|
|
|
9403
9430
|
const type = accumulator.type ?? (typeof done.type === "string" ? done.type : void 0);
|
|
9404
9431
|
if (type === "message") {
|
|
9405
9432
|
const doneContent = Array.isArray(done.content) ? done.content : void 0;
|
|
9406
|
-
const
|
|
9407
|
-
output.push({ role: "assistant", content: [{ type: "output_text", text:
|
|
9433
|
+
const text6 = accumulator.text || (doneContent ? doneContent.filter((part) => part && typeof part === "object" && part.type === "output_text").map((part) => String(part.text ?? "")).join("") : "");
|
|
9434
|
+
output.push({ role: "assistant", content: [{ type: "output_text", text: text6 }] });
|
|
9408
9435
|
continue;
|
|
9409
9436
|
}
|
|
9410
9437
|
if (type === "reasoning") {
|
|
9411
|
-
const summary = accumulator.summaries.size ? [...accumulator.summaries.entries()].sort(([a], [b]) => a - b).map(([,
|
|
9438
|
+
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 : [];
|
|
9412
9439
|
output.push({ ...withoutEphemeralFields(done), type: "reasoning", summary });
|
|
9413
9440
|
continue;
|
|
9414
9441
|
}
|
|
@@ -9698,7 +9725,7 @@ function handleSocketMessage(entry, data) {
|
|
|
9698
9725
|
if (entry.connectionDiagnostic) observeIdleFrame(entry, data);
|
|
9699
9726
|
return;
|
|
9700
9727
|
}
|
|
9701
|
-
const
|
|
9728
|
+
const text6 = Array.isArray(data) ? Buffer.concat(data).toString("utf8") : data.toString("utf8");
|
|
9702
9729
|
ctx.frameCount += 1;
|
|
9703
9730
|
if (ctx.transportRetryPending) {
|
|
9704
9731
|
ctx.transportRetryPending = false;
|
|
@@ -9710,9 +9737,9 @@ function handleSocketMessage(entry, data) {
|
|
|
9710
9737
|
}
|
|
9711
9738
|
let event;
|
|
9712
9739
|
try {
|
|
9713
|
-
event = JSON.parse(
|
|
9740
|
+
event = JSON.parse(text6);
|
|
9714
9741
|
} catch {
|
|
9715
|
-
ctx.pendingEvents.push(
|
|
9742
|
+
ctx.pendingEvents.push(text6.replace(/\r?\n/g, " "));
|
|
9716
9743
|
flushPending(ctx);
|
|
9717
9744
|
return;
|
|
9718
9745
|
}
|
|
@@ -10060,23 +10087,23 @@ function createConnection(WebSocket, wsUrl, headers, persistent, key, options, d
|
|
|
10060
10087
|
function diagnosticCap(cap) {
|
|
10061
10088
|
return Number.isFinite(cap) ? cap : null;
|
|
10062
10089
|
}
|
|
10063
|
-
function envConnectionCap(name,
|
|
10090
|
+
function envConnectionCap(name, log13) {
|
|
10064
10091
|
const raw = process.env[name];
|
|
10065
10092
|
if (raw === void 0 || raw.trim() === "") return void 0;
|
|
10066
10093
|
const value = Number(raw.trim());
|
|
10067
10094
|
if (!Number.isInteger(value) || value < 1) {
|
|
10068
10095
|
try {
|
|
10069
|
-
|
|
10096
|
+
log13?.(`ws: ignoring ${name}=${raw} (expected a positive integer)`);
|
|
10070
10097
|
} catch {
|
|
10071
10098
|
}
|
|
10072
10099
|
return void 0;
|
|
10073
10100
|
}
|
|
10074
10101
|
return value;
|
|
10075
10102
|
}
|
|
10076
|
-
function createResponsesWebSocketFetch(wsUrl,
|
|
10103
|
+
function createResponsesWebSocketFetch(wsUrl, log13, options = {}) {
|
|
10077
10104
|
const debug = (message) => {
|
|
10078
10105
|
try {
|
|
10079
|
-
|
|
10106
|
+
log13?.(`ws: ${message}`);
|
|
10080
10107
|
} catch {
|
|
10081
10108
|
}
|
|
10082
10109
|
};
|
|
@@ -10085,8 +10112,8 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
|
|
|
10085
10112
|
hardTtlMs: options.hardTtlMs ?? RESPONSES_WS_HARD_TTL_MS,
|
|
10086
10113
|
idleTtlMs: options.idleTtlMs ?? RESPONSES_WS_IDLE_TTL_MS,
|
|
10087
10114
|
nurseryIdleTtlMs: options.nurseryIdleTtlMs ?? Math.min(RESPONSES_WS_NURSERY_IDLE_TTL_MS, options.idleTtlMs ?? RESPONSES_WS_IDLE_TTL_MS),
|
|
10088
|
-
maxConnections: options.maxConnections ?? envConnectionCap("CLODEX_WS_MAX_CONNECTIONS",
|
|
10089
|
-
maxNurseryConnections: options.maxNurseryConnections ?? envConnectionCap("CLODEX_WS_MAX_NURSERY_CONNECTIONS",
|
|
10115
|
+
maxConnections: options.maxConnections ?? envConnectionCap("CLODEX_WS_MAX_CONNECTIONS", log13) ?? RESPONSES_WS_MAX_CONNECTIONS,
|
|
10116
|
+
maxNurseryConnections: options.maxNurseryConnections ?? envConnectionCap("CLODEX_WS_MAX_NURSERY_CONNECTIONS", log13) ?? RESPONSES_WS_MAX_NURSERY_CONNECTIONS,
|
|
10090
10117
|
now: options.now ?? Date.now
|
|
10091
10118
|
};
|
|
10092
10119
|
return async (_input, init) => {
|
|
@@ -10521,8 +10548,8 @@ function buildClaudeCodeBillingSystemLine() {
|
|
|
10521
10548
|
function systemBlockText(block) {
|
|
10522
10549
|
if (typeof block === "string") return block;
|
|
10523
10550
|
if (block && typeof block === "object" && "text" in block) {
|
|
10524
|
-
const
|
|
10525
|
-
return typeof
|
|
10551
|
+
const text6 = block.text;
|
|
10552
|
+
return typeof text6 === "string" ? text6 : void 0;
|
|
10526
10553
|
}
|
|
10527
10554
|
return void 0;
|
|
10528
10555
|
}
|
|
@@ -10895,7 +10922,7 @@ function toCamelCase(str) {
|
|
|
10895
10922
|
return str.replace(/[-_]([a-z])/g, (_, g) => g.toUpperCase());
|
|
10896
10923
|
}
|
|
10897
10924
|
function hasSupportedParameter(metadata, param) {
|
|
10898
|
-
return (metadata?.supportedParameters ?? []).some((
|
|
10925
|
+
return (metadata?.supportedParameters ?? []).some((p14) => p14 === param);
|
|
10899
10926
|
}
|
|
10900
10927
|
function isOpenRouterRoute(npm, metadata) {
|
|
10901
10928
|
return npm === "@openrouter/ai-sdk-provider" || metadata?.providerId === "openrouter" || metadata?.apiBaseUrl?.includes("openrouter.ai") === true;
|
|
@@ -11641,6 +11668,7 @@ function routeUnavailableMessage(modelId, reason) {
|
|
|
11641
11668
|
}
|
|
11642
11669
|
|
|
11643
11670
|
// src/upstream-forward.ts
|
|
11671
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
11644
11672
|
import { Readable, Transform } from "stream";
|
|
11645
11673
|
import { StringDecoder } from "string_decoder";
|
|
11646
11674
|
|
|
@@ -11729,18 +11757,50 @@ async function fetchWithOAuthRetry(apiKey, request3, refreshToken) {
|
|
|
11729
11757
|
response = await request3(refreshed);
|
|
11730
11758
|
return { response, apiKey: refreshed, refreshed: true };
|
|
11731
11759
|
}
|
|
11760
|
+
function upstreamHoldsThreads(url) {
|
|
11761
|
+
try {
|
|
11762
|
+
return new URL(url).hostname.toLowerCase() === "api.anthropic.com";
|
|
11763
|
+
} catch {
|
|
11764
|
+
return false;
|
|
11765
|
+
}
|
|
11766
|
+
}
|
|
11767
|
+
function anchorSafeMessageId(id) {
|
|
11768
|
+
return typeof id === "string" && id.startsWith("msg_") ? "clodex_" + randomUUID4().replace(/-/g, "") : id;
|
|
11769
|
+
}
|
|
11770
|
+
function isThreadContinuation(body) {
|
|
11771
|
+
const thread = body.thread;
|
|
11772
|
+
return typeof thread === "object" && thread !== null && thread.type === "continue";
|
|
11773
|
+
}
|
|
11774
|
+
var THREAD_UNSUPPORTED_BODY = JSON.stringify({
|
|
11775
|
+
type: "error",
|
|
11776
|
+
error: {
|
|
11777
|
+
type: "invalid_request_error",
|
|
11778
|
+
message: "thread continuation is not supported on this route; resend the full conversation",
|
|
11779
|
+
details: { error_code: "thread_unsupported_request" }
|
|
11780
|
+
}
|
|
11781
|
+
});
|
|
11732
11782
|
var SSE_LINE_SPLIT = /(\r\n|\r|\n)/;
|
|
11733
|
-
function
|
|
11783
|
+
function anthropicSseMessageStartRewrite(rewrite) {
|
|
11734
11784
|
const decoder = new StringDecoder("utf8");
|
|
11735
11785
|
let tail = "";
|
|
11736
11786
|
const rewriteLine = (line) => {
|
|
11737
11787
|
if (!line.startsWith("data:") || !line.includes('"message_start"')) return line;
|
|
11738
11788
|
try {
|
|
11739
11789
|
const parsed = JSON.parse(line.slice(5));
|
|
11740
|
-
if (parsed.type
|
|
11741
|
-
|
|
11742
|
-
|
|
11790
|
+
if (parsed.type !== "message_start" || !parsed.message) return line;
|
|
11791
|
+
let changed = false;
|
|
11792
|
+
if (rewrite.model !== void 0 && typeof parsed.message.model === "string") {
|
|
11793
|
+
parsed.message.model = rewrite.model;
|
|
11794
|
+
changed = true;
|
|
11743
11795
|
}
|
|
11796
|
+
if (rewrite.anchorSafeId) {
|
|
11797
|
+
const id = anchorSafeMessageId(parsed.message.id);
|
|
11798
|
+
if (id !== parsed.message.id) {
|
|
11799
|
+
parsed.message.id = id;
|
|
11800
|
+
changed = true;
|
|
11801
|
+
}
|
|
11802
|
+
}
|
|
11803
|
+
if (changed) return "data: " + JSON.stringify(parsed);
|
|
11744
11804
|
} catch {
|
|
11745
11805
|
}
|
|
11746
11806
|
return line;
|
|
@@ -11806,8 +11866,11 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
|
|
|
11806
11866
|
"Connection": "keep-alive"
|
|
11807
11867
|
});
|
|
11808
11868
|
const upstream = Readable.fromWeb(upstreamRes.body).on("error", () => res.destroy());
|
|
11809
|
-
if (options.responseModelOverride) {
|
|
11810
|
-
upstream.pipe(
|
|
11869
|
+
if (options.responseModelOverride || options.anchorSafeMessageIds) {
|
|
11870
|
+
upstream.pipe(anthropicSseMessageStartRewrite({
|
|
11871
|
+
model: options.responseModelOverride,
|
|
11872
|
+
anchorSafeId: options.anchorSafeMessageIds
|
|
11873
|
+
})).on("error", () => res.destroy()).pipe(res);
|
|
11811
11874
|
} else {
|
|
11812
11875
|
upstream.pipe(res);
|
|
11813
11876
|
}
|
|
@@ -11818,31 +11881,43 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
|
|
|
11818
11881
|
res.end(JSON.stringify({ type: "error", error: { type: "api_error", message: "Upstream returned empty response body" } }));
|
|
11819
11882
|
return;
|
|
11820
11883
|
}
|
|
11821
|
-
let
|
|
11884
|
+
let text6 = await upstreamRes.text();
|
|
11822
11885
|
let parsed;
|
|
11823
11886
|
try {
|
|
11824
|
-
parsed = JSON.parse(
|
|
11887
|
+
parsed = JSON.parse(text6);
|
|
11825
11888
|
} catch {
|
|
11826
11889
|
res.writeHead(502, { "Content-Type": "application/json" });
|
|
11827
11890
|
res.end(JSON.stringify({ type: "error", error: { type: "api_error", message: "Upstream response was not valid JSON" } }));
|
|
11828
11891
|
return;
|
|
11829
11892
|
}
|
|
11830
|
-
if (
|
|
11831
|
-
|
|
11832
|
-
|
|
11893
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && parsed.type === "message") {
|
|
11894
|
+
const message = parsed;
|
|
11895
|
+
let changed = false;
|
|
11896
|
+
if (options.responseModelOverride && typeof message.model === "string") {
|
|
11897
|
+
message.model = options.responseModelOverride;
|
|
11898
|
+
changed = true;
|
|
11899
|
+
}
|
|
11900
|
+
if (options.anchorSafeMessageIds) {
|
|
11901
|
+
const id = anchorSafeMessageId(message.id);
|
|
11902
|
+
if (id !== message.id) {
|
|
11903
|
+
message.id = id;
|
|
11904
|
+
changed = true;
|
|
11905
|
+
}
|
|
11906
|
+
}
|
|
11907
|
+
if (changed) text6 = JSON.stringify(message);
|
|
11833
11908
|
}
|
|
11834
11909
|
res.writeHead(200, {
|
|
11835
11910
|
"Content-Type": "application/json",
|
|
11836
|
-
"Content-Length": Buffer.byteLength(
|
|
11911
|
+
"Content-Length": Buffer.byteLength(text6).toString()
|
|
11837
11912
|
});
|
|
11838
|
-
res.end(
|
|
11913
|
+
res.end(text6);
|
|
11839
11914
|
}
|
|
11840
11915
|
|
|
11841
11916
|
// src/proxy.ts
|
|
11842
|
-
import { randomUUID as
|
|
11917
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
11843
11918
|
|
|
11844
11919
|
// src/sdk-adapter.ts
|
|
11845
|
-
import { createHash as createHash7 } from "crypto";
|
|
11920
|
+
import { createHash as createHash7, randomUUID as randomUUID5 } from "crypto";
|
|
11846
11921
|
import { streamText, generateText, tool, jsonSchema } from "ai";
|
|
11847
11922
|
|
|
11848
11923
|
// src/proxy-shared.ts
|
|
@@ -12187,12 +12262,12 @@ var OpenAiThinkingBlock = class {
|
|
|
12187
12262
|
append(part) {
|
|
12188
12263
|
const entry = this.parts.get(part.id ?? "");
|
|
12189
12264
|
if (!entry) throw new Error("OpenAI reasoning delta has no matching start");
|
|
12190
|
-
const
|
|
12191
|
-
if (!
|
|
12265
|
+
const text6 = part.text ?? "";
|
|
12266
|
+
if (!text6) return "";
|
|
12192
12267
|
const separator = entry.text.length === 0 && this.hasText ? "\n\n" : "";
|
|
12193
|
-
entry.text +=
|
|
12268
|
+
entry.text += text6;
|
|
12194
12269
|
this.hasText = true;
|
|
12195
|
-
return separator +
|
|
12270
|
+
return separator + text6;
|
|
12196
12271
|
}
|
|
12197
12272
|
end(part) {
|
|
12198
12273
|
const entry = this.parts.get(part.id ?? "");
|
|
@@ -12206,7 +12281,7 @@ var OpenAiThinkingBlock = class {
|
|
|
12206
12281
|
function isRecord(value) {
|
|
12207
12282
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
12208
12283
|
}
|
|
12209
|
-
function restoreOpenAiThinking(
|
|
12284
|
+
function restoreOpenAiThinking(text6, signature, npm) {
|
|
12210
12285
|
if (!signature?.startsWith(SIGNATURE_PREFIX)) return void 0;
|
|
12211
12286
|
if (!signature.startsWith(SIGNATURE_V1)) return [];
|
|
12212
12287
|
try {
|
|
@@ -12217,7 +12292,7 @@ function restoreOpenAiThinking(text5, signature, npm) {
|
|
|
12217
12292
|
if (!isRecord(part) || typeof part.itemId !== "string" || !part.itemId || typeof part.text !== "string" || part.encryptedContent !== void 0 && typeof part.encryptedContent !== "string") return [];
|
|
12218
12293
|
parts.push(part);
|
|
12219
12294
|
}
|
|
12220
|
-
if (npm !== "@ai-sdk/openai") return
|
|
12295
|
+
if (npm !== "@ai-sdk/openai") return text6 ? [{ type: "reasoning", text: text6 }] : [];
|
|
12221
12296
|
return parts.map((part) => ({
|
|
12222
12297
|
type: "reasoning",
|
|
12223
12298
|
text: part.text,
|
|
@@ -12291,10 +12366,10 @@ function supportsOpenAiPromptCacheBreakpoints(modelId) {
|
|
|
12291
12366
|
const minor = Number(match[2] ?? 0);
|
|
12292
12367
|
return major > 5 || major === 5 && minor >= 6;
|
|
12293
12368
|
}
|
|
12294
|
-
function stripClaudeCodeBillingHeader(
|
|
12295
|
-
if (!
|
|
12296
|
-
const newline =
|
|
12297
|
-
return newline === -1 ? void 0 :
|
|
12369
|
+
function stripClaudeCodeBillingHeader(text6) {
|
|
12370
|
+
if (!text6.startsWith(CLAUDE_CODE_BILLING_HEADER_PREFIX)) return text6;
|
|
12371
|
+
const newline = text6.indexOf("\n");
|
|
12372
|
+
return newline === -1 ? void 0 : text6.slice(newline + 1);
|
|
12298
12373
|
}
|
|
12299
12374
|
function systemToString(system, stripAnthropicBillingHeader = false) {
|
|
12300
12375
|
if (!system) return void 0;
|
|
@@ -12303,8 +12378,8 @@ function systemToString(system, stripAnthropicBillingHeader = false) {
|
|
|
12303
12378
|
}
|
|
12304
12379
|
const blocks = system.map((b) => typeof b === "string" ? b : b.text ?? "");
|
|
12305
12380
|
if (!stripAnthropicBillingHeader) return blocks.join("\n");
|
|
12306
|
-
return blocks.flatMap((
|
|
12307
|
-
const stripped = stripClaudeCodeBillingHeader(
|
|
12381
|
+
return blocks.flatMap((text6) => {
|
|
12382
|
+
const stripped = stripClaudeCodeBillingHeader(text6);
|
|
12308
12383
|
return stripped === void 0 ? [] : [stripped];
|
|
12309
12384
|
}).join("\n");
|
|
12310
12385
|
}
|
|
@@ -12320,12 +12395,12 @@ function translateTopLevelSystemForOpenAi(system) {
|
|
|
12320
12395
|
}
|
|
12321
12396
|
return system.flatMap((block) => {
|
|
12322
12397
|
const raw = typeof block === "string" ? block : block.text ?? "";
|
|
12323
|
-
const
|
|
12324
|
-
if (!
|
|
12398
|
+
const text6 = stripClaudeCodeBillingHeader(raw) ?? "";
|
|
12399
|
+
if (!text6.trim()) return [];
|
|
12325
12400
|
const cacheControl = typeof block === "string" ? void 0 : block.cache_control;
|
|
12326
12401
|
return [{
|
|
12327
12402
|
role: "system",
|
|
12328
|
-
content:
|
|
12403
|
+
content: text6,
|
|
12329
12404
|
...cacheControl ? { providerOptions: { openai: { promptCacheBreakpoint: { mode: "explicit" } } } } : {}
|
|
12330
12405
|
}];
|
|
12331
12406
|
});
|
|
@@ -12382,9 +12457,9 @@ function annotateToolNames(messages) {
|
|
|
12382
12457
|
}
|
|
12383
12458
|
}
|
|
12384
12459
|
function thinkingToSdkPart(block, npm) {
|
|
12385
|
-
const
|
|
12386
|
-
if (npm === "@ai-sdk/openai" && !block.signature && !
|
|
12387
|
-
const part = { type: "reasoning", text:
|
|
12460
|
+
const text6 = block.thinking ?? "";
|
|
12461
|
+
if (npm === "@ai-sdk/openai" && !block.signature && !text6.trim()) return null;
|
|
12462
|
+
const part = { type: "reasoning", text: text6 };
|
|
12388
12463
|
if (block.signature) {
|
|
12389
12464
|
if (npm === "@ai-sdk/google") {
|
|
12390
12465
|
part.providerOptions = { google: { thoughtSignature: block.signature } };
|
|
@@ -12419,10 +12494,10 @@ function translateMessages(messages, npm, openAiPromptCacheBreakpoints = false)
|
|
|
12419
12494
|
...openAiCacheBreakpoint(b, openAiPromptCacheBreakpoints) ? { providerOptions: openAiCacheBreakpoint(b, openAiPromptCacheBreakpoints) } : {}
|
|
12420
12495
|
});
|
|
12421
12496
|
} else if (b.type === "image") {
|
|
12422
|
-
const
|
|
12423
|
-
if (
|
|
12497
|
+
const p14 = imagePart(b);
|
|
12498
|
+
if (p14) {
|
|
12424
12499
|
parts.push({
|
|
12425
|
-
...
|
|
12500
|
+
...p14,
|
|
12426
12501
|
...openAiCacheBreakpoint(b, openAiPromptCacheBreakpoints) ? { providerOptions: openAiCacheBreakpoint(b, openAiPromptCacheBreakpoints) } : {}
|
|
12427
12502
|
});
|
|
12428
12503
|
}
|
|
@@ -12475,10 +12550,10 @@ function translateMessages(messages, npm, openAiPromptCacheBreakpoints = false)
|
|
|
12475
12550
|
function isPlainObject(value) {
|
|
12476
12551
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
12477
12552
|
}
|
|
12478
|
-
function parsesAsJson(
|
|
12479
|
-
if (
|
|
12553
|
+
function parsesAsJson(text6) {
|
|
12554
|
+
if (text6.trim() === "") return true;
|
|
12480
12555
|
try {
|
|
12481
|
-
JSON.parse(
|
|
12556
|
+
JSON.parse(text6);
|
|
12482
12557
|
return true;
|
|
12483
12558
|
} catch {
|
|
12484
12559
|
return false;
|
|
@@ -12527,7 +12602,7 @@ function isClaudeCodeCompactRequest(body) {
|
|
|
12527
12602
|
const finalMessage = body.messages.at(-1);
|
|
12528
12603
|
if (!finalMessage || finalMessage.role !== "user") return false;
|
|
12529
12604
|
const texts = typeof finalMessage.content === "string" ? [finalMessage.content] : finalMessage.content.filter((block) => block.type === "text").map((block) => block.text ?? "");
|
|
12530
|
-
return texts.some((
|
|
12605
|
+
return texts.some((text6) => text6.startsWith(COMPACT_TEXT_ONLY_START) && text6.includes(COMPACT_TEXT_ONLY_END));
|
|
12531
12606
|
}
|
|
12532
12607
|
var COMPACT_DRIFT_ANCHOR = "Tool calls will be REJECTED and will waste your only turn";
|
|
12533
12608
|
var COMPACT_DRIFT_ANCHOR_LINE = new RegExp(
|
|
@@ -12539,25 +12614,25 @@ function looksLikeDriftedClaudeCodeCompactRequest(body) {
|
|
|
12539
12614
|
const finalMessage = body.messages.at(-1);
|
|
12540
12615
|
if (!finalMessage || finalMessage.role !== "user") return false;
|
|
12541
12616
|
const texts = typeof finalMessage.content === "string" ? [finalMessage.content] : finalMessage.content.filter((block) => block.type === "text").map((block) => block.text ?? "");
|
|
12542
|
-
return texts.some((
|
|
12617
|
+
return texts.some((text6) => !text6.startsWith(COMPACT_TEXT_ONLY_START) && COMPACT_DRIFT_OPENING.test(text6) && COMPACT_DRIFT_ANCHOR_LINE.test(text6));
|
|
12543
12618
|
}
|
|
12544
12619
|
function claudeCodeVersionFromRequest(body) {
|
|
12545
12620
|
const texts = typeof body.system === "string" ? [body.system] : (body.system ?? []).map((block) => typeof block === "string" ? block : block.text ?? "");
|
|
12546
|
-
for (const
|
|
12547
|
-
if (!
|
|
12548
|
-
const match =
|
|
12621
|
+
for (const text6 of texts) {
|
|
12622
|
+
if (!text6.startsWith(CLAUDE_CODE_BILLING_HEADER_PREFIX)) continue;
|
|
12623
|
+
const match = text6.match(/\bcc_version=([0-9A-Za-z][0-9A-Za-z._+-]{0,63})(?:;|\s|$)/);
|
|
12549
12624
|
if (match) return match[1];
|
|
12550
12625
|
}
|
|
12551
12626
|
return void 0;
|
|
12552
12627
|
}
|
|
12553
12628
|
var warnedCompactPromptDrifts = /* @__PURE__ */ new Set();
|
|
12554
12629
|
var MAX_COMPACT_PROMPT_DRIFT_WARNINGS = 3;
|
|
12555
|
-
function reportClaudeCodeCompactPromptDrift(body,
|
|
12630
|
+
function reportClaudeCodeCompactPromptDrift(body, log13) {
|
|
12556
12631
|
if (!looksLikeDriftedClaudeCodeCompactRequest(body)) return;
|
|
12557
12632
|
const version = claudeCodeVersionFromRequest(body);
|
|
12558
12633
|
const signature = version ?? "unknown-version";
|
|
12559
12634
|
try {
|
|
12560
|
-
|
|
12635
|
+
log13?.(`possible Claude Code compact prompt drift: ${signature}`);
|
|
12561
12636
|
} catch {
|
|
12562
12637
|
}
|
|
12563
12638
|
if (warnedCompactPromptDrifts.has(signature)) return;
|
|
@@ -12690,8 +12765,11 @@ function forwardAbortSignal(source, target) {
|
|
|
12690
12765
|
source.addEventListener("abort", forward, { once: true });
|
|
12691
12766
|
return () => source.removeEventListener("abort", forward);
|
|
12692
12767
|
}
|
|
12693
|
-
|
|
12694
|
-
|
|
12768
|
+
function translatedMessageId() {
|
|
12769
|
+
return "clodex_" + randomUUID5().replace(/-/g, "");
|
|
12770
|
+
}
|
|
12771
|
+
async function writeAnthropicStream(stream, modelId, write, log13, observer, tools) {
|
|
12772
|
+
const messageId = translatedMessageId();
|
|
12695
12773
|
const requiredProps = toolRequiredProps(tools);
|
|
12696
12774
|
let blockIndex = -1;
|
|
12697
12775
|
let started = false;
|
|
@@ -12887,7 +12965,7 @@ async function writeAnthropicStream(stream, modelId, write, log12, observer, too
|
|
|
12887
12965
|
const errMsg = e?.message || (typeof part.error === "string" ? part.error : JSON.stringify(e?.data ?? part.error));
|
|
12888
12966
|
const transportCode = sdkUpstreamErrorDetails(part.error)?.transportCode;
|
|
12889
12967
|
const errorType = anthropicErrorType(upstreamHttpStatus(part.error, errMsg), transportCode);
|
|
12890
|
-
|
|
12968
|
+
log13?.(() => `sdk stream error (${errorType}): ${errMsg}`);
|
|
12891
12969
|
if (!(transportCode === "websocket_transport_error" && openType === "thinking")) closeOpen();
|
|
12892
12970
|
throw part.error instanceof Error || part.error && typeof part.error === "object" ? part.error : new Error(errMsg);
|
|
12893
12971
|
}
|
|
@@ -12901,7 +12979,7 @@ async function writeAnthropicStream(stream, modelId, write, log12, observer, too
|
|
|
12901
12979
|
emit("message_delta", { type: "message_delta", delta: { stop_reason: finishReason, stop_sequence: null }, usage });
|
|
12902
12980
|
emit("message_stop", { type: "message_stop" });
|
|
12903
12981
|
}
|
|
12904
|
-
async function streamAnthropicResponse(model, params, modelId, write,
|
|
12982
|
+
async function streamAnthropicResponse(model, params, modelId, write, log13, observer) {
|
|
12905
12983
|
const { idleTimeoutMs, totalTimeoutMs, maxRetries } = upstreamRequestBudget({
|
|
12906
12984
|
idleTimeoutMs: observer?.idleTimeoutMs
|
|
12907
12985
|
});
|
|
@@ -12940,7 +13018,7 @@ async function streamAnthropicResponse(model, params, modelId, write, log12, obs
|
|
|
12940
13018
|
clearTimeout(idleTimer);
|
|
12941
13019
|
}
|
|
12942
13020
|
})();
|
|
12943
|
-
await writeAnthropicStream(watchedStream, modelId, write,
|
|
13021
|
+
await writeAnthropicStream(watchedStream, modelId, write, log13, { ...observer, abortSignal }, params.tools);
|
|
12944
13022
|
} finally {
|
|
12945
13023
|
stopForwardingAbort();
|
|
12946
13024
|
clearTimeout(idleTimer);
|
|
@@ -12949,7 +13027,7 @@ async function streamAnthropicResponse(model, params, modelId, write, log12, obs
|
|
|
12949
13027
|
}
|
|
12950
13028
|
}
|
|
12951
13029
|
async function generateAnthropicResponse(model, params, modelId, options) {
|
|
12952
|
-
let
|
|
13030
|
+
let text6;
|
|
12953
13031
|
let toolCalls;
|
|
12954
13032
|
let finishReason;
|
|
12955
13033
|
let usage;
|
|
@@ -13015,7 +13093,7 @@ async function generateAnthropicResponse(model, params, modelId, options) {
|
|
|
13015
13093
|
clearTimeout(totalTimer);
|
|
13016
13094
|
if (!forceAbort.signal.aborted) forceAbort.abort();
|
|
13017
13095
|
}
|
|
13018
|
-
|
|
13096
|
+
text6 = streamedText.join("");
|
|
13019
13097
|
toolCalls = streamedToolCalls;
|
|
13020
13098
|
finishReason = streamedFinishReason;
|
|
13021
13099
|
usage = streamedUsage;
|
|
@@ -13035,7 +13113,7 @@ async function generateAnthropicResponse(model, params, modelId, options) {
|
|
|
13035
13113
|
maxRetries,
|
|
13036
13114
|
abortSignal: generateAbort.signal
|
|
13037
13115
|
});
|
|
13038
|
-
({ text:
|
|
13116
|
+
({ text: text6, toolCalls, finishReason, usage, warnings } = r);
|
|
13039
13117
|
} catch (error) {
|
|
13040
13118
|
if (generateAbort.signal.aborted) throw streamAbortError(generateAbort.signal);
|
|
13041
13119
|
throw error;
|
|
@@ -13049,12 +13127,12 @@ async function generateAnthropicResponse(model, params, modelId, options) {
|
|
|
13049
13127
|
reportPromptTokens({ onPromptTokens: options?.onPromptTokens }, usage);
|
|
13050
13128
|
const requiredProps = toolRequiredProps(params.tools);
|
|
13051
13129
|
return {
|
|
13052
|
-
id:
|
|
13130
|
+
id: translatedMessageId(),
|
|
13053
13131
|
type: "message",
|
|
13054
13132
|
role: "assistant",
|
|
13055
13133
|
model: modelId,
|
|
13056
13134
|
content: [
|
|
13057
|
-
...
|
|
13135
|
+
...text6 ? [{ type: "text", text: text6 }] : [],
|
|
13058
13136
|
...toolCalls.map((tc) => ({
|
|
13059
13137
|
type: "tool_use",
|
|
13060
13138
|
id: encodeToolUseId(tc.toolCallId, grabRoundTripSignature(tc)),
|
|
@@ -13265,7 +13343,7 @@ function configuredAliasLookupNames(alias) {
|
|
|
13265
13343
|
}))].map(normalizeRouteLookupId);
|
|
13266
13344
|
}
|
|
13267
13345
|
async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenceLogPath, debugLogPath, webSocketDiagnosticsLogPath, modelAliases) {
|
|
13268
|
-
const proxyToken =
|
|
13346
|
+
const proxyToken = randomUUID6();
|
|
13269
13347
|
silenceSdkWarnings();
|
|
13270
13348
|
if (routes.length === 0) {
|
|
13271
13349
|
throw new Error("Proxy catalog requires at least one route");
|
|
@@ -13417,6 +13495,13 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
|
|
|
13417
13495
|
}
|
|
13418
13496
|
return;
|
|
13419
13497
|
}
|
|
13498
|
+
const holdsThreads = route.modelFormat === "anthropic" && upstreamHoldsThreads(upstreamUrl);
|
|
13499
|
+
if (!holdsThreads && isThreadContinuation(anthropicBody)) {
|
|
13500
|
+
plog(() => `thread continuation refused: route=${route.realModelId} upstream does not hold threads`);
|
|
13501
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
13502
|
+
res.end(THREAD_UNSUPPORTED_BODY);
|
|
13503
|
+
return;
|
|
13504
|
+
}
|
|
13420
13505
|
if (!apiKey && routeAuthType !== "none" && !usesSdkAdapter) {
|
|
13421
13506
|
anthropicError(res, 401, "Missing API key");
|
|
13422
13507
|
return;
|
|
@@ -13470,6 +13555,9 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
|
|
|
13470
13555
|
// the client would believe it reached a model it never reached, and
|
|
13471
13556
|
// would key its context window off that phantom id.
|
|
13472
13557
|
responseModelOverride: resolvedRoute && typeof originalModel === "string" && originalModel !== route.realModelId ? originalModel : void 0,
|
|
13558
|
+
// An upstream that holds no threads must not hand Claude Code an id
|
|
13559
|
+
// it would anchor the next request on.
|
|
13560
|
+
anchorSafeMessageIds: !holdsThreads,
|
|
13473
13561
|
onUpstreamError: inferenceLogPath ? (statusCode, errorContent) => writeInferenceResponseErrorLog(inferenceLogPath, {
|
|
13474
13562
|
modelId: originalModel,
|
|
13475
13563
|
provider: route.providerId ?? route.aliasId.split(":")[1] ?? "unknown",
|
|
@@ -13678,7 +13766,7 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
|
|
|
13678
13766
|
res,
|
|
13679
13767
|
upstreamStatus === 500 ? 502 : upstreamStatus,
|
|
13680
13768
|
clientMessage,
|
|
13681
|
-
contextLengthExceeded ? relayRequestId ??
|
|
13769
|
+
contextLengthExceeded ? relayRequestId ?? randomUUID6() : void 0
|
|
13682
13770
|
);
|
|
13683
13771
|
} else {
|
|
13684
13772
|
const errorType = anthropicErrorType(upstreamStatus, details?.transportCode);
|
|
@@ -13686,7 +13774,7 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
|
|
|
13686
13774
|
data: ${JSON.stringify({
|
|
13687
13775
|
type: "error",
|
|
13688
13776
|
error: { type: errorType, message: clientMessage },
|
|
13689
|
-
...contextLengthExceeded ? { request_id: relayRequestId ??
|
|
13777
|
+
...contextLengthExceeded ? { request_id: relayRequestId ?? randomUUID6() } : {}
|
|
13690
13778
|
})}
|
|
13691
13779
|
|
|
13692
13780
|
`);
|
|
@@ -14743,11 +14831,11 @@ function formatModelMetadata(entries) {
|
|
|
14743
14831
|
|
|
14744
14832
|
// src/first-run.ts
|
|
14745
14833
|
import pc7 from "picocolors";
|
|
14746
|
-
import * as
|
|
14834
|
+
import * as p7 from "@clack/prompts";
|
|
14747
14835
|
|
|
14748
14836
|
// src/providers-command.ts
|
|
14749
14837
|
import pc6 from "picocolors";
|
|
14750
|
-
import * as
|
|
14838
|
+
import * as p6 from "@clack/prompts";
|
|
14751
14839
|
|
|
14752
14840
|
// src/registry/import-build.ts
|
|
14753
14841
|
function oauthAuthRef(providerId) {
|
|
@@ -14818,8 +14906,8 @@ async function fetchProviderCatalog(opts) {
|
|
|
14818
14906
|
return loadRegistryProviders(warn, { ...opts, warn });
|
|
14819
14907
|
}
|
|
14820
14908
|
function providersForPicker(providers) {
|
|
14821
|
-
for (const
|
|
14822
|
-
|
|
14909
|
+
for (const p14 of providers) {
|
|
14910
|
+
p14.models.sort((a, b) => {
|
|
14823
14911
|
const nameA = a.name || a.id;
|
|
14824
14912
|
const nameB = b.name || b.id;
|
|
14825
14913
|
return nameA.localeCompare(nameB, void 0, { sensitivity: "base", numeric: true });
|
|
@@ -14835,7 +14923,7 @@ async function resolveLocalProviderApiKey(provider) {
|
|
|
14835
14923
|
if (template?.apiKeyOptional || template?.anonymousFreeModels) {
|
|
14836
14924
|
return "";
|
|
14837
14925
|
}
|
|
14838
|
-
const reg = loadRegistry().providers.find((
|
|
14926
|
+
const reg = loadRegistry().providers.find((p14) => p14.id === provider.id);
|
|
14839
14927
|
const authRef = provider.authRef ?? reg?.authRef ?? oauthAuthRef(provider.id);
|
|
14840
14928
|
return resolveProviderCredential(provider.id, authRef);
|
|
14841
14929
|
}
|
|
@@ -15009,7 +15097,7 @@ function localProvidersToServerModels(localProviders) {
|
|
|
15009
15097
|
}
|
|
15010
15098
|
|
|
15011
15099
|
// src/registry/credential-cleanup-journal.ts
|
|
15012
|
-
import { randomUUID as
|
|
15100
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
15013
15101
|
import {
|
|
15014
15102
|
closeSync as closeSync9,
|
|
15015
15103
|
existsSync as existsSync5,
|
|
@@ -15149,7 +15237,7 @@ function writeJournalUnlocked(journal, path) {
|
|
|
15149
15237
|
assertRegistryWriteOwnership(path);
|
|
15150
15238
|
ensureSecureAppHome();
|
|
15151
15239
|
mkdirSync6(dirname6(path), { recursive: true, mode: DIR_MODE2 });
|
|
15152
|
-
const tmp = `${path}.${process.pid}.${
|
|
15240
|
+
const tmp = `${path}.${process.pid}.${randomUUID7()}.tmp`;
|
|
15153
15241
|
let fd;
|
|
15154
15242
|
try {
|
|
15155
15243
|
fd = openSync9(tmp, "wx", FILE_MODE4);
|
|
@@ -15476,7 +15564,7 @@ async function addProviderFromTemplate(template, apiKey, opts) {
|
|
|
15476
15564
|
}
|
|
15477
15565
|
};
|
|
15478
15566
|
if (existing) {
|
|
15479
|
-
const idx = registry.providers.findIndex((
|
|
15567
|
+
const idx = registry.providers.findIndex((p14) => p14.id === template.id);
|
|
15480
15568
|
registry.providers[idx] = entry;
|
|
15481
15569
|
} else {
|
|
15482
15570
|
registry.providers.push(entry);
|
|
@@ -15524,127 +15612,8 @@ async function addProviderFromTemplate(template, apiKey, opts) {
|
|
|
15524
15612
|
return result;
|
|
15525
15613
|
}
|
|
15526
15614
|
|
|
15527
|
-
// src/
|
|
15528
|
-
|
|
15529
|
-
const removal = await withRegistryWriteLock(async () => {
|
|
15530
|
-
const registry = loadRegistryStrict();
|
|
15531
|
-
const index = registry.providers.findIndex((p13) => p13.id === id);
|
|
15532
|
-
if (index < 0) {
|
|
15533
|
-
return {
|
|
15534
|
-
result: {
|
|
15535
|
-
removed: false,
|
|
15536
|
-
id,
|
|
15537
|
-
credentialDeleted: false,
|
|
15538
|
-
error: `Provider not found: ${id}`
|
|
15539
|
-
},
|
|
15540
|
-
queuedRefs: []
|
|
15541
|
-
};
|
|
15542
|
-
}
|
|
15543
|
-
const [removedProvider] = registry.providers.splice(index, 1);
|
|
15544
|
-
const queuedRefs = [];
|
|
15545
|
-
if (opts?.deleteCredential !== false) {
|
|
15546
|
-
const credentialRefs = /* @__PURE__ */ new Set([
|
|
15547
|
-
removedProvider.authRef,
|
|
15548
|
-
...removedProvider.defaultAuthRef ? [removedProvider.defaultAuthRef] : [],
|
|
15549
|
-
...Object.values(removedProvider.authAccounts ?? {}).map((slot) => slot.authRef)
|
|
15550
|
-
]);
|
|
15551
|
-
for (const authRef of credentialRefs) {
|
|
15552
|
-
if (await queueCredentialDelete(authRef)) queuedRefs.push(authRef);
|
|
15553
|
-
}
|
|
15554
|
-
}
|
|
15555
|
-
saveRegistry(registry);
|
|
15556
|
-
return {
|
|
15557
|
-
result: {
|
|
15558
|
-
removed: true,
|
|
15559
|
-
id,
|
|
15560
|
-
name: removedProvider.name,
|
|
15561
|
-
credentialDeleted: false
|
|
15562
|
-
},
|
|
15563
|
-
queuedRefs
|
|
15564
|
-
};
|
|
15565
|
-
});
|
|
15566
|
-
if (removal.queuedRefs.length > 0) {
|
|
15567
|
-
try {
|
|
15568
|
-
const cleanup = await reconcilePendingCredentialDeletes();
|
|
15569
|
-
removal.result.credentialDeleted = removal.queuedRefs.every((ref) => cleanup.deleted.includes(ref));
|
|
15570
|
-
removal.result.credentialCleanupPending = removal.queuedRefs.some((ref) => cleanup.pending.includes(ref)) || cleanup.persistenceError !== void 0;
|
|
15571
|
-
} catch {
|
|
15572
|
-
removal.result.credentialCleanupPending = true;
|
|
15573
|
-
}
|
|
15574
|
-
removal.result.credentialCleanupReconciled = true;
|
|
15575
|
-
}
|
|
15576
|
-
return removal.result;
|
|
15577
|
-
}
|
|
15578
|
-
async function removeProviderFromRegistry(id, opts) {
|
|
15579
|
-
return withProviderMutationLock(id, () => removeProviderWithinLifecycle(id, opts));
|
|
15580
|
-
}
|
|
15581
|
-
async function setActiveOAuthAccount(id, account) {
|
|
15582
|
-
return withProviderMutationLock(id, () => withRegistryWriteLock(() => {
|
|
15583
|
-
const registry = loadRegistryStrict();
|
|
15584
|
-
const provider = registry.providers.find((p13) => p13.id === id);
|
|
15585
|
-
if (!provider) return { updated: false, error: `Provider not found: ${id}` };
|
|
15586
|
-
const name = account?.trim().toLowerCase();
|
|
15587
|
-
const previous = provider.activeAuthAccount?.trim() || void 0;
|
|
15588
|
-
const selectedSlot = name ? getOAuthAccountSlot(provider, name) : void 0;
|
|
15589
|
-
let selectionChanged = previous !== name;
|
|
15590
|
-
let storageChanged = false;
|
|
15591
|
-
if (name && !selectedSlot) {
|
|
15592
|
-
const slots = provider.authAccounts ?? {};
|
|
15593
|
-
const available = Object.keys(slots).sort().join(", ") || "none";
|
|
15594
|
-
return {
|
|
15595
|
-
updated: false,
|
|
15596
|
-
error: `${provider.name} has no account named "${name}" (available: ${available}).`
|
|
15597
|
-
};
|
|
15598
|
-
}
|
|
15599
|
-
if (name && provider.authType === "oauth") {
|
|
15600
|
-
storageChanged = storeActiveOAuthAccount(provider, name, selectedSlot.authRef);
|
|
15601
|
-
}
|
|
15602
|
-
if (selectionChanged && provider.authType === "oauth") {
|
|
15603
|
-
const previousSlot = previous ? getOAuthAccountSlot(provider, previous) : void 0;
|
|
15604
|
-
if (previous && previousSlot && provider.modelsCache && provider.authAccounts) {
|
|
15605
|
-
provider.authAccounts[previous] = {
|
|
15606
|
-
...previousSlot,
|
|
15607
|
-
modelsCache: provider.modelsCache
|
|
15608
|
-
};
|
|
15609
|
-
}
|
|
15610
|
-
const selectedCache = selectedSlot?.modelsCache;
|
|
15611
|
-
if (selectedCache) {
|
|
15612
|
-
provider.modelsCache = selectedCache;
|
|
15613
|
-
provider.refreshedAt = selectedCache.fetchedAt;
|
|
15614
|
-
} else {
|
|
15615
|
-
delete provider.modelsCache;
|
|
15616
|
-
delete provider.refreshedAt;
|
|
15617
|
-
}
|
|
15618
|
-
}
|
|
15619
|
-
if (name && provider.authType !== "oauth") {
|
|
15620
|
-
storageChanged = clearActiveOAuthAccount(provider) || storageChanged;
|
|
15621
|
-
if (provider.activeAuthAccount !== name) {
|
|
15622
|
-
provider.activeAuthAccount = name;
|
|
15623
|
-
storageChanged = true;
|
|
15624
|
-
}
|
|
15625
|
-
} else if (!name && (previous !== void 0 || provider.defaultAuthRef !== void 0)) {
|
|
15626
|
-
storageChanged = clearActiveOAuthAccount(provider);
|
|
15627
|
-
} else if (!name) {
|
|
15628
|
-
selectionChanged = false;
|
|
15629
|
-
}
|
|
15630
|
-
const migrationNeedsPersistence = registry.schemaVersion < REGISTRY_SCHEMA_VERSION_WITH_MATERIALIZED_ACTIVE_ACCOUNT && provider.defaultAuthRef !== void 0;
|
|
15631
|
-
if (selectionChanged || storageChanged || migrationNeedsPersistence) saveRegistry(registry);
|
|
15632
|
-
return { updated: true, changed: selectionChanged, ...name ? { account: name } : {}, provider };
|
|
15633
|
-
}));
|
|
15634
|
-
}
|
|
15635
|
-
function toggleProviderEnabled(id) {
|
|
15636
|
-
return withRegistryWriteLockSync(() => {
|
|
15637
|
-
const registry = loadRegistryStrict();
|
|
15638
|
-
const provider = registry.providers.find((p13) => p13.id === id);
|
|
15639
|
-
if (!provider) return { toggled: false, error: `Provider not found: ${id}` };
|
|
15640
|
-
provider.enabled = !provider.enabled;
|
|
15641
|
-
saveRegistry(registry);
|
|
15642
|
-
return { toggled: true, enabled: provider.enabled };
|
|
15643
|
-
});
|
|
15644
|
-
}
|
|
15645
|
-
|
|
15646
|
-
// src/registry/refresh-models.ts
|
|
15647
|
-
import { isDeepStrictEqual } from "util";
|
|
15615
|
+
// src/providers-custom-add.ts
|
|
15616
|
+
import * as p3 from "@clack/prompts";
|
|
15648
15617
|
|
|
15649
15618
|
// src/registry/url-security.ts
|
|
15650
15619
|
import { lookup } from "dns/promises";
|
|
@@ -15759,6 +15728,13 @@ async function validateCustomEndpointUrl(rawUrl, opts = {}) {
|
|
|
15759
15728
|
}
|
|
15760
15729
|
|
|
15761
15730
|
// src/registry/custom-endpoint.ts
|
|
15731
|
+
var CUSTOM_PROVIDER_ALLOCATION_SLOT = "custom-provider-allocation";
|
|
15732
|
+
function npmForKind(kind) {
|
|
15733
|
+
return kind === "anthropic" ? "@ai-sdk/anthropic" : "@ai-sdk/openai-compatible";
|
|
15734
|
+
}
|
|
15735
|
+
function modelFormatForKind(kind) {
|
|
15736
|
+
return kind === "anthropic" ? "anthropic" : "openai";
|
|
15737
|
+
}
|
|
15762
15738
|
async function fetchAnthropicModels(baseUrl, apiKey, extraHeaders) {
|
|
15763
15739
|
const root = baseUrl.replace(/\/v1\/?$/, "").replace(/\/$/, "");
|
|
15764
15740
|
const modelsUrl2 = `${root}/v1/models`;
|
|
@@ -15798,6 +15774,7 @@ async function fetchAnthropicModels(baseUrl, apiKey, extraHeaders) {
|
|
|
15798
15774
|
for (const row of json.data ?? []) {
|
|
15799
15775
|
const id = row.id?.trim();
|
|
15800
15776
|
if (!id) continue;
|
|
15777
|
+
if (hasControlChars(id) || typeof row.name === "string" && hasControlChars(row.name.trim())) continue;
|
|
15801
15778
|
models.push({
|
|
15802
15779
|
id,
|
|
15803
15780
|
name: row.name?.trim() || id,
|
|
@@ -15832,6 +15809,325 @@ async function fetchAnthropicModels(baseUrl, apiKey, extraHeaders) {
|
|
|
15832
15809
|
clearTimeout(timer);
|
|
15833
15810
|
}
|
|
15834
15811
|
}
|
|
15812
|
+
function uniqueProviderId(displayName, registry) {
|
|
15813
|
+
let base = customProviderId(displayName);
|
|
15814
|
+
if (!base.startsWith("custom-")) base = `custom-${slugifyProviderId(displayName)}`;
|
|
15815
|
+
if (!isValidProviderId(base)) base = "custom-provider";
|
|
15816
|
+
if (!registry.providers.some((p14) => p14.id === base)) return base;
|
|
15817
|
+
for (let i = 2; ; i++) {
|
|
15818
|
+
const candidate = `${base}-${i}`;
|
|
15819
|
+
if (isValidProviderId(candidate) && !registry.providers.some((p14) => p14.id === candidate)) {
|
|
15820
|
+
return candidate;
|
|
15821
|
+
}
|
|
15822
|
+
}
|
|
15823
|
+
}
|
|
15824
|
+
async function addCustomEndpointProvider(input) {
|
|
15825
|
+
const urlCheck = await validateCustomEndpointUrl(input.baseUrl, {
|
|
15826
|
+
allowInsecureLocal: input.allowInsecureLocal
|
|
15827
|
+
});
|
|
15828
|
+
if (!urlCheck.ok || !urlCheck.normalizedUrl) {
|
|
15829
|
+
return { added: false, error: urlCheck.error, hint: urlCheck.hint };
|
|
15830
|
+
}
|
|
15831
|
+
const normalizedUrl = urlCheck.normalizedUrl;
|
|
15832
|
+
const displayName = input.displayName.trim();
|
|
15833
|
+
const probeProviderId = uniqueProviderId(displayName, { providers: [] });
|
|
15834
|
+
const npm = npmForKind(input.kind);
|
|
15835
|
+
const apiKey = input.apiKey.trim();
|
|
15836
|
+
const anonymous = apiKey.length === 0;
|
|
15837
|
+
const headers = input.headers && Object.keys(input.headers).length > 0 ? input.headers : void 0;
|
|
15838
|
+
let fetched;
|
|
15839
|
+
if (input.kind === "anthropic") {
|
|
15840
|
+
fetched = await fetchAnthropicModels(normalizedUrl, apiKey, headers);
|
|
15841
|
+
} else {
|
|
15842
|
+
fetched = await fetchTemplateModels(
|
|
15843
|
+
{
|
|
15844
|
+
id: probeProviderId,
|
|
15845
|
+
name: displayName,
|
|
15846
|
+
authType: anonymous ? "none" : "api",
|
|
15847
|
+
npm,
|
|
15848
|
+
defaultBaseUrl: normalizedUrl,
|
|
15849
|
+
modelSource: "api-list",
|
|
15850
|
+
supported: true
|
|
15851
|
+
},
|
|
15852
|
+
apiKey,
|
|
15853
|
+
normalizedUrl,
|
|
15854
|
+
headers
|
|
15855
|
+
);
|
|
15856
|
+
}
|
|
15857
|
+
if (fetched.error || fetched.models.length === 0) {
|
|
15858
|
+
return { added: false, error: fetched.error ?? "No models returned.", hint: fetched.hint };
|
|
15859
|
+
}
|
|
15860
|
+
const result = await withProviderMutationLock(CUSTOM_PROVIDER_ALLOCATION_SLOT, async () => {
|
|
15861
|
+
const providerId = await withRegistryWriteLock(
|
|
15862
|
+
() => uniqueProviderId(displayName, loadRegistryStrict())
|
|
15863
|
+
);
|
|
15864
|
+
const account = `provider:${providerId}`;
|
|
15865
|
+
const authRef = anonymous ? "none:anonymous" : credentialInstanceAuthRef(account);
|
|
15866
|
+
const persistAndCommit = async () => {
|
|
15867
|
+
if (!anonymous) {
|
|
15868
|
+
await journalCredentialWrite(authRef);
|
|
15869
|
+
const saved = await provisionProviderCredential(authRef, apiKey);
|
|
15870
|
+
if (!saved) {
|
|
15871
|
+
return {
|
|
15872
|
+
added: false,
|
|
15873
|
+
error: "Could not save API key to the credential store.",
|
|
15874
|
+
hint: "Check credential-store access and try again."
|
|
15875
|
+
};
|
|
15876
|
+
}
|
|
15877
|
+
}
|
|
15878
|
+
return withRegistryWriteLock(async () => {
|
|
15879
|
+
const registry = loadRegistryStrict();
|
|
15880
|
+
if (registry.providers.some((provider) => provider.id === providerId)) {
|
|
15881
|
+
return {
|
|
15882
|
+
added: false,
|
|
15883
|
+
error: `Provider ID ${providerId} changed while its credential was being saved.`,
|
|
15884
|
+
hint: "Retry adding the custom provider."
|
|
15885
|
+
};
|
|
15886
|
+
}
|
|
15887
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
15888
|
+
const entry = {
|
|
15889
|
+
id: providerId,
|
|
15890
|
+
templateId: input.kind === "anthropic" ? "custom-anthropic" : "custom-openai",
|
|
15891
|
+
name: displayName,
|
|
15892
|
+
enabled: true,
|
|
15893
|
+
authRef,
|
|
15894
|
+
authType: anonymous ? "none" : "api",
|
|
15895
|
+
api: { npm, url: fetched.baseUrl, ...headers ? { headers } : {} },
|
|
15896
|
+
addedAt: now,
|
|
15897
|
+
refreshedAt: now,
|
|
15898
|
+
modelsCache: {
|
|
15899
|
+
fetchedAt: now,
|
|
15900
|
+
models: fetched.models.map((m) => ({
|
|
15901
|
+
...m,
|
|
15902
|
+
modelFormat: modelFormatForKind(input.kind),
|
|
15903
|
+
npm,
|
|
15904
|
+
apiUrl: fetched.baseUrl
|
|
15905
|
+
}))
|
|
15906
|
+
}
|
|
15907
|
+
};
|
|
15908
|
+
registry.providers.push(entry);
|
|
15909
|
+
saveRegistry(registry);
|
|
15910
|
+
let credentialCleanupPending = false;
|
|
15911
|
+
if (!anonymous) {
|
|
15912
|
+
try {
|
|
15913
|
+
await cancelCredentialDelete(authRef);
|
|
15914
|
+
} catch {
|
|
15915
|
+
credentialCleanupPending = true;
|
|
15916
|
+
}
|
|
15917
|
+
}
|
|
15918
|
+
return {
|
|
15919
|
+
added: true,
|
|
15920
|
+
provider: entry,
|
|
15921
|
+
modelCount: fetched.models.length,
|
|
15922
|
+
...credentialCleanupPending ? { credentialCleanupPending: true } : {}
|
|
15923
|
+
};
|
|
15924
|
+
});
|
|
15925
|
+
};
|
|
15926
|
+
if (anonymous) return persistAndCommit();
|
|
15927
|
+
return withCredentialMutationLock(authRef, persistAndCommit);
|
|
15928
|
+
});
|
|
15929
|
+
if (result.added) {
|
|
15930
|
+
try {
|
|
15931
|
+
const cleanup = await reconcilePendingCredentialDeletes();
|
|
15932
|
+
result.credentialCleanupPending = cleanup.pending.length > 0 || cleanup.persistenceError !== void 0;
|
|
15933
|
+
} catch {
|
|
15934
|
+
result.credentialCleanupPending = true;
|
|
15935
|
+
}
|
|
15936
|
+
} else {
|
|
15937
|
+
try {
|
|
15938
|
+
const cleanup = await reconcilePendingCredentialDeletes();
|
|
15939
|
+
result.credentialCleanupPending = cleanup.pending.length > 0 || cleanup.persistenceError !== void 0;
|
|
15940
|
+
} catch {
|
|
15941
|
+
result.credentialCleanupPending = true;
|
|
15942
|
+
}
|
|
15943
|
+
}
|
|
15944
|
+
result.credentialCleanupReconciled = true;
|
|
15945
|
+
return result;
|
|
15946
|
+
}
|
|
15947
|
+
|
|
15948
|
+
// src/providers-custom-add.ts
|
|
15949
|
+
async function runCustomEndpointAddFlow(onResult) {
|
|
15950
|
+
const displayName = await p3.text({
|
|
15951
|
+
message: "Name for this provider:",
|
|
15952
|
+
placeholder: "OpenRouter",
|
|
15953
|
+
validate: (value) => value.trim() ? void 0 : "Name is required"
|
|
15954
|
+
});
|
|
15955
|
+
if (p3.isCancel(displayName)) {
|
|
15956
|
+
p3.cancel("Cancelled.");
|
|
15957
|
+
return 0;
|
|
15958
|
+
}
|
|
15959
|
+
const baseUrl = await p3.text({
|
|
15960
|
+
message: "Base URL of the OpenAI-compatible API (the part before /chat/completions):",
|
|
15961
|
+
placeholder: "https://openrouter.ai/api/v1",
|
|
15962
|
+
validate: (value) => value.trim() ? void 0 : "URL is required"
|
|
15963
|
+
});
|
|
15964
|
+
if (p3.isCancel(baseUrl)) {
|
|
15965
|
+
p3.cancel("Cancelled.");
|
|
15966
|
+
return 0;
|
|
15967
|
+
}
|
|
15968
|
+
let allowInsecureLocal = false;
|
|
15969
|
+
if (/^http:\/\//i.test(String(baseUrl).trim())) {
|
|
15970
|
+
p3.log.warn("HTTP is not encrypted. Only use it for a trusted local or LAN server.");
|
|
15971
|
+
const allow = await p3.confirm({
|
|
15972
|
+
message: "Allow insecure HTTP for this local/LAN server?",
|
|
15973
|
+
initialValue: true
|
|
15974
|
+
});
|
|
15975
|
+
if (p3.isCancel(allow)) {
|
|
15976
|
+
p3.cancel("Cancelled.");
|
|
15977
|
+
return 0;
|
|
15978
|
+
}
|
|
15979
|
+
allowInsecureLocal = allow === true;
|
|
15980
|
+
}
|
|
15981
|
+
const apiKey = await p3.password({
|
|
15982
|
+
message: "API key (leave empty for a local server without auth):"
|
|
15983
|
+
});
|
|
15984
|
+
if (p3.isCancel(apiKey)) {
|
|
15985
|
+
p3.cancel("Cancelled.");
|
|
15986
|
+
return 0;
|
|
15987
|
+
}
|
|
15988
|
+
const spinner6 = p3.spinner();
|
|
15989
|
+
spinner6.start("Testing connection...");
|
|
15990
|
+
const result = await addCustomEndpointProvider({
|
|
15991
|
+
displayName: String(displayName).trim(),
|
|
15992
|
+
baseUrl: String(baseUrl).trim(),
|
|
15993
|
+
apiKey: String(apiKey ?? "").trim(),
|
|
15994
|
+
kind: "openai",
|
|
15995
|
+
allowInsecureLocal
|
|
15996
|
+
});
|
|
15997
|
+
spinner6.stop("");
|
|
15998
|
+
onResult(result);
|
|
15999
|
+
if (!result.added || !result.provider) {
|
|
16000
|
+
p3.log.error(printableServerText(result.error ?? "Could not add the provider."));
|
|
16001
|
+
const hint = printableServerText(result.hint ?? "");
|
|
16002
|
+
if (hint) p3.log.info(hint);
|
|
16003
|
+
return 1;
|
|
16004
|
+
}
|
|
16005
|
+
logConnected(result.provider.name, result.modelCount ?? 0);
|
|
16006
|
+
p3.log.info(`Provider id: ${result.provider.id}. Its models are named clodex:${result.provider.id}:<model>.`);
|
|
16007
|
+
return 0;
|
|
16008
|
+
}
|
|
16009
|
+
|
|
16010
|
+
// src/registry/crud.ts
|
|
16011
|
+
async function removeProviderWithinLifecycle(id, opts) {
|
|
16012
|
+
const removal = await withRegistryWriteLock(async () => {
|
|
16013
|
+
const registry = loadRegistryStrict();
|
|
16014
|
+
const index = registry.providers.findIndex((p14) => p14.id === id);
|
|
16015
|
+
if (index < 0) {
|
|
16016
|
+
return {
|
|
16017
|
+
result: {
|
|
16018
|
+
removed: false,
|
|
16019
|
+
id,
|
|
16020
|
+
credentialDeleted: false,
|
|
16021
|
+
error: `Provider not found: ${id}`
|
|
16022
|
+
},
|
|
16023
|
+
queuedRefs: []
|
|
16024
|
+
};
|
|
16025
|
+
}
|
|
16026
|
+
const [removedProvider] = registry.providers.splice(index, 1);
|
|
16027
|
+
const queuedRefs = [];
|
|
16028
|
+
if (opts?.deleteCredential !== false) {
|
|
16029
|
+
const credentialRefs = /* @__PURE__ */ new Set([
|
|
16030
|
+
removedProvider.authRef,
|
|
16031
|
+
...removedProvider.defaultAuthRef ? [removedProvider.defaultAuthRef] : [],
|
|
16032
|
+
...Object.values(removedProvider.authAccounts ?? {}).map((slot) => slot.authRef)
|
|
16033
|
+
]);
|
|
16034
|
+
for (const authRef of credentialRefs) {
|
|
16035
|
+
if (await queueCredentialDelete(authRef)) queuedRefs.push(authRef);
|
|
16036
|
+
}
|
|
16037
|
+
}
|
|
16038
|
+
saveRegistry(registry);
|
|
16039
|
+
return {
|
|
16040
|
+
result: {
|
|
16041
|
+
removed: true,
|
|
16042
|
+
id,
|
|
16043
|
+
name: removedProvider.name,
|
|
16044
|
+
credentialDeleted: false
|
|
16045
|
+
},
|
|
16046
|
+
queuedRefs
|
|
16047
|
+
};
|
|
16048
|
+
});
|
|
16049
|
+
if (removal.queuedRefs.length > 0) {
|
|
16050
|
+
try {
|
|
16051
|
+
const cleanup = await reconcilePendingCredentialDeletes();
|
|
16052
|
+
removal.result.credentialDeleted = removal.queuedRefs.every((ref) => cleanup.deleted.includes(ref));
|
|
16053
|
+
removal.result.credentialCleanupPending = removal.queuedRefs.some((ref) => cleanup.pending.includes(ref)) || cleanup.persistenceError !== void 0;
|
|
16054
|
+
} catch {
|
|
16055
|
+
removal.result.credentialCleanupPending = true;
|
|
16056
|
+
}
|
|
16057
|
+
removal.result.credentialCleanupReconciled = true;
|
|
16058
|
+
}
|
|
16059
|
+
return removal.result;
|
|
16060
|
+
}
|
|
16061
|
+
async function removeProviderFromRegistry(id, opts) {
|
|
16062
|
+
return withProviderMutationLock(id, () => removeProviderWithinLifecycle(id, opts));
|
|
16063
|
+
}
|
|
16064
|
+
async function setActiveOAuthAccount(id, account) {
|
|
16065
|
+
return withProviderMutationLock(id, () => withRegistryWriteLock(() => {
|
|
16066
|
+
const registry = loadRegistryStrict();
|
|
16067
|
+
const provider = registry.providers.find((p14) => p14.id === id);
|
|
16068
|
+
if (!provider) return { updated: false, error: `Provider not found: ${id}` };
|
|
16069
|
+
const name = account?.trim().toLowerCase();
|
|
16070
|
+
const previous = provider.activeAuthAccount?.trim() || void 0;
|
|
16071
|
+
const selectedSlot = name ? getOAuthAccountSlot(provider, name) : void 0;
|
|
16072
|
+
let selectionChanged = previous !== name;
|
|
16073
|
+
let storageChanged = false;
|
|
16074
|
+
if (name && !selectedSlot) {
|
|
16075
|
+
const slots = provider.authAccounts ?? {};
|
|
16076
|
+
const available = Object.keys(slots).sort().join(", ") || "none";
|
|
16077
|
+
return {
|
|
16078
|
+
updated: false,
|
|
16079
|
+
error: `${provider.name} has no account named "${name}" (available: ${available}).`
|
|
16080
|
+
};
|
|
16081
|
+
}
|
|
16082
|
+
if (name && provider.authType === "oauth") {
|
|
16083
|
+
storageChanged = storeActiveOAuthAccount(provider, name, selectedSlot.authRef);
|
|
16084
|
+
}
|
|
16085
|
+
if (selectionChanged && provider.authType === "oauth") {
|
|
16086
|
+
const previousSlot = previous ? getOAuthAccountSlot(provider, previous) : void 0;
|
|
16087
|
+
if (previous && previousSlot && provider.modelsCache && provider.authAccounts) {
|
|
16088
|
+
provider.authAccounts[previous] = {
|
|
16089
|
+
...previousSlot,
|
|
16090
|
+
modelsCache: provider.modelsCache
|
|
16091
|
+
};
|
|
16092
|
+
}
|
|
16093
|
+
const selectedCache = selectedSlot?.modelsCache;
|
|
16094
|
+
if (selectedCache) {
|
|
16095
|
+
provider.modelsCache = selectedCache;
|
|
16096
|
+
provider.refreshedAt = selectedCache.fetchedAt;
|
|
16097
|
+
} else {
|
|
16098
|
+
delete provider.modelsCache;
|
|
16099
|
+
delete provider.refreshedAt;
|
|
16100
|
+
}
|
|
16101
|
+
}
|
|
16102
|
+
if (name && provider.authType !== "oauth") {
|
|
16103
|
+
storageChanged = clearActiveOAuthAccount(provider) || storageChanged;
|
|
16104
|
+
if (provider.activeAuthAccount !== name) {
|
|
16105
|
+
provider.activeAuthAccount = name;
|
|
16106
|
+
storageChanged = true;
|
|
16107
|
+
}
|
|
16108
|
+
} else if (!name && (previous !== void 0 || provider.defaultAuthRef !== void 0)) {
|
|
16109
|
+
storageChanged = clearActiveOAuthAccount(provider);
|
|
16110
|
+
} else if (!name) {
|
|
16111
|
+
selectionChanged = false;
|
|
16112
|
+
}
|
|
16113
|
+
const migrationNeedsPersistence = registry.schemaVersion < REGISTRY_SCHEMA_VERSION_WITH_MATERIALIZED_ACTIVE_ACCOUNT && provider.defaultAuthRef !== void 0;
|
|
16114
|
+
if (selectionChanged || storageChanged || migrationNeedsPersistence) saveRegistry(registry);
|
|
16115
|
+
return { updated: true, changed: selectionChanged, ...name ? { account: name } : {}, provider };
|
|
16116
|
+
}));
|
|
16117
|
+
}
|
|
16118
|
+
function toggleProviderEnabled(id) {
|
|
16119
|
+
return withRegistryWriteLockSync(() => {
|
|
16120
|
+
const registry = loadRegistryStrict();
|
|
16121
|
+
const provider = registry.providers.find((p14) => p14.id === id);
|
|
16122
|
+
if (!provider) return { toggled: false, error: `Provider not found: ${id}` };
|
|
16123
|
+
provider.enabled = !provider.enabled;
|
|
16124
|
+
saveRegistry(registry);
|
|
16125
|
+
return { toggled: true, enabled: provider.enabled };
|
|
16126
|
+
});
|
|
16127
|
+
}
|
|
16128
|
+
|
|
16129
|
+
// src/registry/refresh-models.ts
|
|
16130
|
+
import { isDeepStrictEqual } from "util";
|
|
15835
16131
|
|
|
15836
16132
|
// src/registry/model-source.ts
|
|
15837
16133
|
var MANUAL_ONLY_TEMPLATE_IDS = /* @__PURE__ */ new Set(["vertex", "bedrock", "azure"]);
|
|
@@ -16184,7 +16480,7 @@ async function refreshApiListProvider(provider, apiKey) {
|
|
|
16184
16480
|
};
|
|
16185
16481
|
}
|
|
16186
16482
|
function updateProviderCache(registry, providerId, models, baseUrl, credentialSnapshot) {
|
|
16187
|
-
const idx = registry.providers.findIndex((
|
|
16483
|
+
const idx = registry.providers.findIndex((p14) => p14.id === providerId);
|
|
16188
16484
|
if (idx < 0) return;
|
|
16189
16485
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
16190
16486
|
const existing = registry.providers[idx];
|
|
@@ -16255,7 +16551,7 @@ function assertRefreshCredentialStillCurrent(current, snapshot) {
|
|
|
16255
16551
|
}
|
|
16256
16552
|
async function refreshProviderModels(providerId, apiKey, registry, credentialSnapshot) {
|
|
16257
16553
|
const workingRegistry = registry ?? loadRegistryStrict();
|
|
16258
|
-
const provider = workingRegistry.providers.find((
|
|
16554
|
+
const provider = workingRegistry.providers.find((p14) => p14.id === providerId);
|
|
16259
16555
|
if (!provider) {
|
|
16260
16556
|
return { id: providerId, name: providerId, ok: false, reason: "Provider not found." };
|
|
16261
16557
|
}
|
|
@@ -16470,7 +16766,7 @@ async function refreshProviderModelsWithCredential(providerId, resolveKey, selec
|
|
|
16470
16766
|
async function refreshAllProviderModels(resolveKey) {
|
|
16471
16767
|
const refreshed = [];
|
|
16472
16768
|
const registry = loadRegistryStrict();
|
|
16473
|
-
const enabledProviders = registry.providers.filter((
|
|
16769
|
+
const enabledProviders = registry.providers.filter((p14) => p14.enabled);
|
|
16474
16770
|
for (const provider of enabledProviders) {
|
|
16475
16771
|
const accountOverride = process.env[OAUTH_ACCOUNT_ENV] ?? null;
|
|
16476
16772
|
try {
|
|
@@ -16494,7 +16790,7 @@ async function refreshAllProviderModels(resolveKey) {
|
|
|
16494
16790
|
|
|
16495
16791
|
// src/registry/provider-auth.ts
|
|
16496
16792
|
import pc4 from "picocolors";
|
|
16497
|
-
import * as
|
|
16793
|
+
import * as p4 from "@clack/prompts";
|
|
16498
16794
|
import open from "open";
|
|
16499
16795
|
function validateOAuthAccountName(name) {
|
|
16500
16796
|
const trimmed = name.trim().toLowerCase();
|
|
@@ -16517,39 +16813,39 @@ function openBrowser(url) {
|
|
|
16517
16813
|
async function runNativeDeviceCode(providerId) {
|
|
16518
16814
|
const label = PROVIDER_DISPLAY[providerId];
|
|
16519
16815
|
printOAuthStepsPanel(`${label} \u2014 Sign in`, label);
|
|
16520
|
-
const
|
|
16521
|
-
|
|
16816
|
+
const spinner6 = p4.spinner();
|
|
16817
|
+
spinner6.start("Waiting for authorization...");
|
|
16522
16818
|
try {
|
|
16523
16819
|
const { tokens, accountId } = await runOpenAiDeviceCodeFlow(({ url, userCode }) => {
|
|
16524
|
-
|
|
16525
|
-
|
|
16526
|
-
|
|
16820
|
+
spinner6.stop("");
|
|
16821
|
+
p4.log.info(`Visit: ${pc4.cyan(url)}`);
|
|
16822
|
+
p4.log.info(`Enter code: ${pc4.bold(userCode)}`);
|
|
16527
16823
|
openBrowser(url);
|
|
16528
|
-
|
|
16824
|
+
spinner6.start("Waiting for authorization...");
|
|
16529
16825
|
});
|
|
16530
|
-
|
|
16826
|
+
spinner6.stop(pc4.green("Signed in to OpenAI ChatGPT"));
|
|
16531
16827
|
return tokensToStoredCredential(tokens, void 0, accountId);
|
|
16532
16828
|
} catch (err) {
|
|
16533
|
-
|
|
16829
|
+
spinner6.stop("");
|
|
16534
16830
|
throw err;
|
|
16535
16831
|
}
|
|
16536
16832
|
}
|
|
16537
16833
|
async function runNativeBrowserSignIn(providerId) {
|
|
16538
16834
|
const label = PROVIDER_DISPLAY[providerId];
|
|
16539
16835
|
printOAuthBrowserPanel(`${label} \u2014 Sign in`, label);
|
|
16540
|
-
const
|
|
16541
|
-
|
|
16836
|
+
const spinner6 = p4.spinner();
|
|
16837
|
+
spinner6.start("Opening your browser...");
|
|
16542
16838
|
try {
|
|
16543
16839
|
const { tokens, accountId } = await runOpenAiBrowserFlow(({ url }) => {
|
|
16544
|
-
|
|
16545
|
-
|
|
16840
|
+
spinner6.stop("");
|
|
16841
|
+
p4.log.info(`If the browser did not open, visit: ${pc4.cyan(url)}`);
|
|
16546
16842
|
openBrowser(url);
|
|
16547
|
-
|
|
16843
|
+
spinner6.start("Waiting for sign-in in your browser...");
|
|
16548
16844
|
});
|
|
16549
|
-
|
|
16845
|
+
spinner6.stop(pc4.green("Signed in to OpenAI ChatGPT"));
|
|
16550
16846
|
return tokensToStoredCredential(tokens, void 0, accountId);
|
|
16551
16847
|
} catch (err) {
|
|
16552
|
-
|
|
16848
|
+
spinner6.stop("");
|
|
16553
16849
|
throw err;
|
|
16554
16850
|
}
|
|
16555
16851
|
}
|
|
@@ -16758,7 +17054,7 @@ async function authenticateProvider(providerId, options = {}) {
|
|
|
16758
17054
|
}
|
|
16759
17055
|
const cred = options.method === "browser" ? await runNativeBrowserSignIn(providerId) : await runNativeDeviceCode(providerId);
|
|
16760
17056
|
const persisted = await persistNativeOAuthCredential(providerId, cred, accountName);
|
|
16761
|
-
const refreshSpinner =
|
|
17057
|
+
const refreshSpinner = p4.spinner();
|
|
16762
17058
|
refreshSpinner.start("Refreshing model list...");
|
|
16763
17059
|
try {
|
|
16764
17060
|
const accountOverride = accountName ?? (persisted.registryProvider.activeAuthAccount === void 0 ? null : process.env[OAUTH_ACCOUNT_ENV] ?? null);
|
|
@@ -16813,7 +17109,7 @@ ${pc4.bold("Named accounts:")}
|
|
|
16813
17109
|
}
|
|
16814
17110
|
|
|
16815
17111
|
// src/prompts.ts
|
|
16816
|
-
import * as
|
|
17112
|
+
import * as p5 from "@clack/prompts";
|
|
16817
17113
|
import pc5 from "picocolors";
|
|
16818
17114
|
|
|
16819
17115
|
// src/model-search.ts
|
|
@@ -16922,12 +17218,12 @@ async function pickModelFromPagedList(list, toOption, messagePrefix, initialMode
|
|
|
16922
17218
|
options.push(navOption(SWITCH_SEARCH, "\u2190 New search", ""));
|
|
16923
17219
|
}
|
|
16924
17220
|
const initialValue = (initialModelId && pageItems.some((m) => m.id === initialModelId) ? initialModelId : pageItems[0]?.id) ?? options[0]?.value;
|
|
16925
|
-
const picked = await
|
|
17221
|
+
const picked = await p5.select({
|
|
16926
17222
|
message: `${messagePrefix} (page ${currentPage + 1} of ${totalPages})`,
|
|
16927
17223
|
options,
|
|
16928
17224
|
initialValue
|
|
16929
17225
|
});
|
|
16930
|
-
if (
|
|
17226
|
+
if (p5.isCancel(picked)) return "menu";
|
|
16931
17227
|
const choice = String(picked);
|
|
16932
17228
|
if (choice === PAGE_PREV) {
|
|
16933
17229
|
page = currentPage - 1;
|
|
@@ -16948,7 +17244,7 @@ async function selectLargeCatalog(models, browseList, toOption, message, initial
|
|
|
16948
17244
|
let mode = "choose";
|
|
16949
17245
|
while (true) {
|
|
16950
17246
|
if (mode === "choose") {
|
|
16951
|
-
const method = await
|
|
17247
|
+
const method = await p5.select({
|
|
16952
17248
|
message: `${message} (${models.length} available)`,
|
|
16953
17249
|
options: [
|
|
16954
17250
|
{ value: MODE_SEARCH, label: pc5.cyan("Search models"), hint: "Filter by name, id, or brand" },
|
|
@@ -16960,7 +17256,7 @@ async function selectLargeCatalog(models, browseList, toOption, message, initial
|
|
|
16960
17256
|
navOption("__back__", "\u2190 Go back", "Select a different provider")
|
|
16961
17257
|
]
|
|
16962
17258
|
});
|
|
16963
|
-
if (
|
|
17259
|
+
if (p5.isCancel(method) || String(method) === "__back__") {
|
|
16964
17260
|
return "back";
|
|
16965
17261
|
}
|
|
16966
17262
|
mode = method === MODE_BROWSE ? "browse" : "search";
|
|
@@ -16985,17 +17281,17 @@ async function selectLargeCatalog(models, browseList, toOption, message, initial
|
|
|
16985
17281
|
if (isSelectedModel(picked)) return picked;
|
|
16986
17282
|
continue;
|
|
16987
17283
|
}
|
|
16988
|
-
const searchInput = await
|
|
17284
|
+
const searchInput = await p5.text({
|
|
16989
17285
|
message: `Search models (${models.length} available):`,
|
|
16990
17286
|
placeholder: "e.g. claude, sonnet, llama"
|
|
16991
17287
|
});
|
|
16992
|
-
if (
|
|
17288
|
+
if (p5.isCancel(searchInput)) {
|
|
16993
17289
|
mode = "choose";
|
|
16994
17290
|
continue;
|
|
16995
17291
|
}
|
|
16996
17292
|
const matched = filterModelsBySearch(browseList, String(searchInput));
|
|
16997
17293
|
if (matched.length === 0) {
|
|
16998
|
-
|
|
17294
|
+
p5.log.warn("No models match \u2014 try a different search");
|
|
16999
17295
|
continue;
|
|
17000
17296
|
}
|
|
17001
17297
|
const result = await pickModelFromPagedList(
|
|
@@ -17026,12 +17322,12 @@ async function selectModelWithSearch(models, toOption, message, initialModelId,
|
|
|
17026
17322
|
navOption("__back__", "\u2190 Go back", "")
|
|
17027
17323
|
];
|
|
17028
17324
|
const initialValue = initialModelId && options.some((o) => o.value === initialModelId) ? initialModelId : options[0]?.value;
|
|
17029
|
-
const picked = await
|
|
17325
|
+
const picked = await p5.select({
|
|
17030
17326
|
message,
|
|
17031
17327
|
options,
|
|
17032
17328
|
initialValue
|
|
17033
17329
|
});
|
|
17034
|
-
if (
|
|
17330
|
+
if (p5.isCancel(picked) || String(picked) === "__back__") {
|
|
17035
17331
|
return "back";
|
|
17036
17332
|
}
|
|
17037
17333
|
const selected = models.find((m) => m.id === String(picked));
|
|
@@ -17065,12 +17361,12 @@ async function pickLocalModel(provider, conflicts, prefs) {
|
|
|
17065
17361
|
navOption(BROWSE_ALL, "Browse all models \u2192", `${provider.models.length} available`),
|
|
17066
17362
|
navOption("__back__", "\u2190 Go back", "Select a different provider")
|
|
17067
17363
|
];
|
|
17068
|
-
const picked = await
|
|
17364
|
+
const picked = await p5.select({
|
|
17069
17365
|
message: "Which model?",
|
|
17070
17366
|
options,
|
|
17071
17367
|
initialValue: recentModels[0].id
|
|
17072
17368
|
});
|
|
17073
|
-
if (
|
|
17369
|
+
if (p5.isCancel(picked) || String(picked) === "__back__") {
|
|
17074
17370
|
return "back";
|
|
17075
17371
|
}
|
|
17076
17372
|
if (String(picked) === BROWSE_ALL) {
|
|
@@ -17097,12 +17393,12 @@ async function pickLocalModel(provider, conflicts, prefs) {
|
|
|
17097
17393
|
}
|
|
17098
17394
|
noteEnvConflicts(conflicts);
|
|
17099
17395
|
const modelLabel = formatModelLabel(selectedModel);
|
|
17100
|
-
const confirmed = await
|
|
17396
|
+
const confirmed = await p5.confirm({
|
|
17101
17397
|
message: confirmLaunchMessage("Claude Code", modelLabel, selectedModel.id, provider.name),
|
|
17102
17398
|
initialValue: true
|
|
17103
17399
|
});
|
|
17104
|
-
if (
|
|
17105
|
-
|
|
17400
|
+
if (p5.isCancel(confirmed) || !confirmed) {
|
|
17401
|
+
p5.cancel("Cancelled.");
|
|
17106
17402
|
return null;
|
|
17107
17403
|
}
|
|
17108
17404
|
relayOutro("Launching", fmtModel(modelLabel, selectedModel.id));
|
|
@@ -17118,7 +17414,7 @@ function reportCredentialCleanup(pending, state, reconciled = false) {
|
|
|
17118
17414
|
return;
|
|
17119
17415
|
}
|
|
17120
17416
|
if (pending) {
|
|
17121
|
-
|
|
17417
|
+
p6.log.warn(CREDENTIAL_CLEANUP_PENDING_MESSAGE);
|
|
17122
17418
|
}
|
|
17123
17419
|
}
|
|
17124
17420
|
async function reconcileCredentialCleanup() {
|
|
@@ -17208,7 +17504,7 @@ ${pc6.bold("Usage:")}
|
|
|
17208
17504
|
|
|
17209
17505
|
${pc6.bold("Subcommands:")}
|
|
17210
17506
|
(none) Provider hub wizard
|
|
17211
|
-
add Add a built-in provider or sign in with ChatGPT
|
|
17507
|
+
add Add a built-in provider, a custom OpenAI-compatible server, or sign in with ChatGPT
|
|
17212
17508
|
auth Sign in with ChatGPT/Codex-plan OAuth (device code, or --browser)
|
|
17213
17509
|
list Show configured providers
|
|
17214
17510
|
remove Remove a provider by id
|
|
@@ -17335,7 +17631,7 @@ function providerLabel(name, modelCount, enabled) {
|
|
|
17335
17631
|
return `${fmtEnabledStar(enabled)} ${fmtProvider(name)} ${pc6.dim(`(${modelCount} model${modelCount === 1 ? "" : "s"})`)}`;
|
|
17336
17632
|
}
|
|
17337
17633
|
async function promptOAuthMethod() {
|
|
17338
|
-
const choice = await
|
|
17634
|
+
const choice = await p6.select({
|
|
17339
17635
|
message: "How do you want to sign in?",
|
|
17340
17636
|
initialValue: "native",
|
|
17341
17637
|
options: [
|
|
@@ -17343,8 +17639,8 @@ async function promptOAuthMethod() {
|
|
|
17343
17639
|
{ value: "browser", label: "Browser", hint: "for workspaces that disable device code authorization" }
|
|
17344
17640
|
]
|
|
17345
17641
|
});
|
|
17346
|
-
if (
|
|
17347
|
-
|
|
17642
|
+
if (p6.isCancel(choice)) {
|
|
17643
|
+
p6.cancel("Cancelled.");
|
|
17348
17644
|
return null;
|
|
17349
17645
|
}
|
|
17350
17646
|
return choice;
|
|
@@ -17353,17 +17649,17 @@ async function runProvidersAuthWithCleanupState(providerId, method, cleanupState
|
|
|
17353
17649
|
try {
|
|
17354
17650
|
const result = await authenticateProvider(providerId, { method, account });
|
|
17355
17651
|
const slot = account === void 0 ? void 0 : validateOAuthAccountName(account);
|
|
17356
|
-
|
|
17652
|
+
p6.log.success(
|
|
17357
17653
|
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.`
|
|
17358
17654
|
);
|
|
17359
17655
|
reportCredentialCleanup(result.credentialCleanupPending, cleanupState, true);
|
|
17360
17656
|
return 0;
|
|
17361
17657
|
} catch (err) {
|
|
17362
17658
|
if (err instanceof Error && err.message === "Cancelled") {
|
|
17363
|
-
|
|
17659
|
+
p6.cancel("Cancelled.");
|
|
17364
17660
|
return 0;
|
|
17365
17661
|
}
|
|
17366
|
-
|
|
17662
|
+
p6.log.error(err instanceof Error ? err.message : String(err));
|
|
17367
17663
|
return 1;
|
|
17368
17664
|
}
|
|
17369
17665
|
}
|
|
@@ -17374,13 +17670,13 @@ async function runProvidersRefreshModels(providerId, options = {}) {
|
|
|
17374
17670
|
const resolveKey = async (provider) => resolveProviderCredentialWithSource(provider.id, provider.authRef);
|
|
17375
17671
|
if (providerId) {
|
|
17376
17672
|
const registry = loadRegistry();
|
|
17377
|
-
const provider = registry.providers.find((
|
|
17673
|
+
const provider = registry.providers.find((p14) => p14.id === providerId);
|
|
17378
17674
|
if (!provider) {
|
|
17379
|
-
|
|
17675
|
+
p6.log.error(`Provider not found: ${providerId}`);
|
|
17380
17676
|
return 1;
|
|
17381
17677
|
}
|
|
17382
|
-
const
|
|
17383
|
-
|
|
17678
|
+
const spinner7 = p6.spinner();
|
|
17679
|
+
spinner7.start(`Refreshing ${provider.name}...`);
|
|
17384
17680
|
const accountOverride = options.accountOverride === void 0 ? process.env[OAUTH_ACCOUNT_ENV] ?? null : options.accountOverride;
|
|
17385
17681
|
let result;
|
|
17386
17682
|
try {
|
|
@@ -17396,59 +17692,59 @@ async function runProvidersRefreshModels(providerId, options = {}) {
|
|
|
17396
17692
|
{ ignoreProviderOverride: options.ignoreProviderCredentialOverride }
|
|
17397
17693
|
);
|
|
17398
17694
|
} catch (err) {
|
|
17399
|
-
|
|
17400
|
-
|
|
17695
|
+
spinner7.stop("");
|
|
17696
|
+
p6.log.error(err instanceof Error ? err.message : String(err));
|
|
17401
17697
|
return 1;
|
|
17402
17698
|
}
|
|
17403
|
-
|
|
17699
|
+
spinner7.stop("");
|
|
17404
17700
|
if (result.skipped) {
|
|
17405
17701
|
const countNote = result.modelCount ? ` (${result.modelCount} cached models kept)` : "";
|
|
17406
|
-
|
|
17702
|
+
p6.log.warn(`${result.name}: ${result.reason}${countNote}`);
|
|
17407
17703
|
return 0;
|
|
17408
17704
|
}
|
|
17409
17705
|
if (!result.ok) {
|
|
17410
|
-
|
|
17706
|
+
p6.log.error(`${result.name}: ${result.reason ?? "Refresh failed."}`);
|
|
17411
17707
|
return 1;
|
|
17412
17708
|
}
|
|
17413
17709
|
const diff = result.previousModelCount === void 0 ? 0 : (result.modelCount ?? 0) - result.previousModelCount;
|
|
17414
17710
|
const diffStr = result.previousModelCount === void 0 ? "" : diff > 0 ? ` (+${diff})` : diff < 0 ? ` (${diff})` : "";
|
|
17415
|
-
|
|
17711
|
+
p6.log.success(`${result.name}: ${result.modelCount} model${result.modelCount === 1 ? "" : "s"} updated${diffStr}.`);
|
|
17416
17712
|
if (result.reason) {
|
|
17417
|
-
|
|
17713
|
+
p6.log.warn(result.reason);
|
|
17418
17714
|
}
|
|
17419
17715
|
return 0;
|
|
17420
17716
|
}
|
|
17421
|
-
const
|
|
17422
|
-
|
|
17717
|
+
const spinner6 = p6.spinner();
|
|
17718
|
+
spinner6.start("Refreshing model lists...");
|
|
17423
17719
|
const { refreshed } = await refreshAllProviderModels(resolveKey);
|
|
17424
|
-
|
|
17720
|
+
spinner6.stop("");
|
|
17425
17721
|
const ok = refreshed.filter((r) => r.ok && !r.skipped);
|
|
17426
17722
|
const skipped = refreshed.filter((r) => r.skipped);
|
|
17427
17723
|
const failed = refreshed.filter((r) => !r.ok);
|
|
17428
17724
|
if (ok.length > 0) {
|
|
17429
|
-
|
|
17725
|
+
p6.log.success(`Updated ${ok.length} provider${ok.length === 1 ? "" : "s"}.`);
|
|
17430
17726
|
for (const r of ok) {
|
|
17431
17727
|
const diff = r.previousModelCount === void 0 ? 0 : (r.modelCount ?? 0) - r.previousModelCount;
|
|
17432
17728
|
const diffStr = r.previousModelCount === void 0 ? "" : diff > 0 ? ` (+${diff})` : diff < 0 ? ` (${diff})` : "";
|
|
17433
|
-
|
|
17729
|
+
p6.log.info(` ${r.name}: ${r.modelCount} model${r.modelCount === 1 ? "" : "s"}${diffStr}`);
|
|
17434
17730
|
if (r.reason) {
|
|
17435
|
-
|
|
17731
|
+
p6.log.warn(` ${r.reason}`);
|
|
17436
17732
|
}
|
|
17437
17733
|
}
|
|
17438
17734
|
}
|
|
17439
17735
|
for (const r of skipped) {
|
|
17440
17736
|
const countNote = r.modelCount ? ` (${r.modelCount} cached models kept)` : "";
|
|
17441
|
-
|
|
17737
|
+
p6.log.warn(`Skipped ${r.name}: ${r.reason}${countNote}`);
|
|
17442
17738
|
}
|
|
17443
17739
|
for (const r of failed) {
|
|
17444
|
-
|
|
17740
|
+
p6.log.error(`${r.name}: ${r.reason ?? "Refresh failed."}`);
|
|
17445
17741
|
}
|
|
17446
17742
|
return failed.length > 0 ? 1 : 0;
|
|
17447
17743
|
}
|
|
17448
17744
|
async function runProvidersList() {
|
|
17449
17745
|
const entries = await resolveProvidersForDisplay();
|
|
17450
17746
|
if (entries.length === 0) {
|
|
17451
|
-
|
|
17747
|
+
p6.log.info("No providers configured. Run clodex providers add or clodex providers auth openai.");
|
|
17452
17748
|
return 0;
|
|
17453
17749
|
}
|
|
17454
17750
|
console.log("");
|
|
@@ -17469,7 +17765,7 @@ async function runTemplateAddFlow(templateId, cleanupState) {
|
|
|
17469
17765
|
const template = listRegistryAddableTemplates(registry.providers).find((candidate) => candidate.id === templateId);
|
|
17470
17766
|
if (!template) {
|
|
17471
17767
|
const known = getTemplateById(templateId);
|
|
17472
|
-
|
|
17768
|
+
p6.log.info(known ? `${known.name} is already configured.` : `Unknown provider template: ${templateId}`);
|
|
17473
17769
|
return known ? 0 : 1;
|
|
17474
17770
|
}
|
|
17475
17771
|
if (template.signupUrl) {
|
|
@@ -17477,27 +17773,27 @@ async function runTemplateAddFlow(templateId, cleanupState) {
|
|
|
17477
17773
|
`${pc6.white("Get an API key at:")} ${fmtUrl(template.signupUrl)}`
|
|
17478
17774
|
]);
|
|
17479
17775
|
}
|
|
17480
|
-
const apiKeyInput = await
|
|
17776
|
+
const apiKeyInput = await p6.password({
|
|
17481
17777
|
message: `Paste your ${template.name} API key:`,
|
|
17482
17778
|
validate: (val) => val.trim() ? void 0 : "Key cannot be empty"
|
|
17483
17779
|
});
|
|
17484
|
-
if (
|
|
17485
|
-
|
|
17780
|
+
if (p6.isCancel(apiKeyInput)) {
|
|
17781
|
+
p6.cancel("Cancelled.");
|
|
17486
17782
|
return 0;
|
|
17487
17783
|
}
|
|
17488
17784
|
const apiKey = String(apiKeyInput).trim();
|
|
17489
|
-
const
|
|
17490
|
-
|
|
17785
|
+
const spinner6 = p6.spinner();
|
|
17786
|
+
spinner6.start(`Testing connection to ${template.name}...`);
|
|
17491
17787
|
const result = await addProviderFromTemplate(template, apiKey);
|
|
17492
|
-
|
|
17788
|
+
spinner6.stop("");
|
|
17493
17789
|
reportCredentialCleanup(
|
|
17494
17790
|
result.credentialCleanupPending === true,
|
|
17495
17791
|
cleanupState,
|
|
17496
17792
|
result.credentialCleanupReconciled === true
|
|
17497
17793
|
);
|
|
17498
17794
|
if (!result.added) {
|
|
17499
|
-
|
|
17500
|
-
if (result.hint)
|
|
17795
|
+
p6.log.error(result.error ?? "Could not add provider.");
|
|
17796
|
+
if (result.hint) p6.log.info(result.hint);
|
|
17501
17797
|
return 1;
|
|
17502
17798
|
}
|
|
17503
17799
|
logConnected(template.name, result.modelCount ?? 0);
|
|
@@ -17521,16 +17817,17 @@ async function runProvidersAddWithCleanupState(cleanupState) {
|
|
|
17521
17817
|
hint: template.id === "openai" ? "platform.openai.com key (usage-billed)" : "provider API key (usage-billed)"
|
|
17522
17818
|
});
|
|
17523
17819
|
}
|
|
17524
|
-
|
|
17525
|
-
|
|
17526
|
-
|
|
17527
|
-
|
|
17528
|
-
|
|
17820
|
+
options.push({
|
|
17821
|
+
value: "custom",
|
|
17822
|
+
label: "Custom OpenAI-compatible server",
|
|
17823
|
+
hint: "OpenRouter, Together, LM Studio, vLLM \u2014 any base URL"
|
|
17824
|
+
});
|
|
17825
|
+
const choice = await p6.select({
|
|
17529
17826
|
message: "Add a provider",
|
|
17530
17827
|
options
|
|
17531
17828
|
});
|
|
17532
|
-
if (
|
|
17533
|
-
|
|
17829
|
+
if (p6.isCancel(choice)) {
|
|
17830
|
+
p6.cancel("Cancelled.");
|
|
17534
17831
|
return 0;
|
|
17535
17832
|
}
|
|
17536
17833
|
if (choice === "oauth") {
|
|
@@ -17538,6 +17835,13 @@ async function runProvidersAddWithCleanupState(cleanupState) {
|
|
|
17538
17835
|
if (method === null) return 0;
|
|
17539
17836
|
return runProvidersAuthWithCleanupState("openai", method, cleanupState);
|
|
17540
17837
|
}
|
|
17838
|
+
if (choice === "custom") {
|
|
17839
|
+
return runCustomEndpointAddFlow((result) => reportCredentialCleanup(
|
|
17840
|
+
result.credentialCleanupPending === true,
|
|
17841
|
+
cleanupState,
|
|
17842
|
+
result.credentialCleanupReconciled === true
|
|
17843
|
+
));
|
|
17844
|
+
}
|
|
17541
17845
|
if (typeof choice === "string" && choice.startsWith("api:")) {
|
|
17542
17846
|
return runTemplateAddFlow(choice.slice("api:".length), cleanupState);
|
|
17543
17847
|
}
|
|
@@ -17550,16 +17854,16 @@ async function runProvidersRemoveWithCleanupState(id, interactive = false, clean
|
|
|
17550
17854
|
const registry = loadRegistry();
|
|
17551
17855
|
const provider = registry.providers.find((pr) => pr.id === id);
|
|
17552
17856
|
if (!provider) {
|
|
17553
|
-
|
|
17857
|
+
p6.log.error(`Provider not found: ${id}`);
|
|
17554
17858
|
return 1;
|
|
17555
17859
|
}
|
|
17556
17860
|
if (interactive) {
|
|
17557
|
-
const
|
|
17861
|
+
const confirm7 = await p6.confirm({
|
|
17558
17862
|
message: `Remove ${provider.name} (${id})?`,
|
|
17559
17863
|
initialValue: false
|
|
17560
17864
|
});
|
|
17561
|
-
if (
|
|
17562
|
-
|
|
17865
|
+
if (p6.isCancel(confirm7) || !confirm7) {
|
|
17866
|
+
p6.cancel("Cancelled.");
|
|
17563
17867
|
return 0;
|
|
17564
17868
|
}
|
|
17565
17869
|
}
|
|
@@ -17570,16 +17874,16 @@ async function runProvidersRemoveWithCleanupState(id, interactive = false, clean
|
|
|
17570
17874
|
result.credentialCleanupReconciled === true
|
|
17571
17875
|
);
|
|
17572
17876
|
if (!result.removed) {
|
|
17573
|
-
|
|
17877
|
+
p6.log.error(result.error ?? `Could not remove ${id}`);
|
|
17574
17878
|
return 1;
|
|
17575
17879
|
}
|
|
17576
17880
|
if (result.error) {
|
|
17577
|
-
|
|
17881
|
+
p6.log.error(result.error);
|
|
17578
17882
|
return 1;
|
|
17579
17883
|
}
|
|
17580
|
-
|
|
17884
|
+
p6.log.success(`Removed ${result.name ?? id}.`);
|
|
17581
17885
|
if (result.credentialDeleted) {
|
|
17582
|
-
|
|
17886
|
+
p6.log.info("Provider API key removed from the credential store.");
|
|
17583
17887
|
}
|
|
17584
17888
|
return 0;
|
|
17585
17889
|
}
|
|
@@ -17654,11 +17958,11 @@ async function runProviderDetail(id) {
|
|
|
17654
17958
|
{ value: "remove", label: "Remove provider", hint: "Delete from registry and credential store when safe" },
|
|
17655
17959
|
{ value: "back", label: "Back", hint: "" }
|
|
17656
17960
|
);
|
|
17657
|
-
const action = await
|
|
17961
|
+
const action = await p6.select({
|
|
17658
17962
|
message: "What would you like to do?",
|
|
17659
17963
|
options: detailOptions
|
|
17660
17964
|
});
|
|
17661
|
-
if (
|
|
17965
|
+
if (p6.isCancel(action) || action === "back") return "back";
|
|
17662
17966
|
if (action === "browse") {
|
|
17663
17967
|
const cachedModels = projectProviderCachedModels(modelProvider);
|
|
17664
17968
|
const localModels = cachedModels.map((m) => cachedModelToLocal(m, modelProvider)).filter((m) => m !== null);
|
|
@@ -17685,7 +17989,7 @@ async function runProviderDetail(id) {
|
|
|
17685
17989
|
const providerDefault = "<default>";
|
|
17686
17990
|
const stored = provider.activeAuthAccount;
|
|
17687
17991
|
const current = stored !== void 0 && accountSlots.includes(stored) ? stored : providerDefault;
|
|
17688
|
-
const chosen = await
|
|
17992
|
+
const chosen = await p6.select({
|
|
17689
17993
|
message: "Which account should every launch use?",
|
|
17690
17994
|
initialValue: current,
|
|
17691
17995
|
options: [
|
|
@@ -17701,15 +18005,15 @@ async function runProviderDetail(id) {
|
|
|
17701
18005
|
}))
|
|
17702
18006
|
]
|
|
17703
18007
|
});
|
|
17704
|
-
if (
|
|
18008
|
+
if (p6.isCancel(chosen)) return "back";
|
|
17705
18009
|
return withProviderMutationLock(id, async () => {
|
|
17706
18010
|
const result = await setActiveOAuthAccount(id, chosen === providerDefault ? void 0 : chosen);
|
|
17707
18011
|
if (!result.updated) {
|
|
17708
|
-
|
|
18012
|
+
p6.log.error(result.error ?? "Could not switch account.");
|
|
17709
18013
|
return "back";
|
|
17710
18014
|
}
|
|
17711
18015
|
if (!result.provider) {
|
|
17712
|
-
|
|
18016
|
+
p6.log.error("Account selection was saved, but the resulting provider state could not be read.");
|
|
17713
18017
|
return "back";
|
|
17714
18018
|
}
|
|
17715
18019
|
const refreshExitCode = await runProvidersRefreshModels(id, {
|
|
@@ -17718,7 +18022,7 @@ async function runProviderDetail(id) {
|
|
|
17718
18022
|
});
|
|
17719
18023
|
const currentProvider = loadRegistry().providers.find((candidate) => candidate.id === id);
|
|
17720
18024
|
if (!currentProvider) {
|
|
17721
|
-
|
|
18025
|
+
p6.log.error("Account selection was saved, but the resulting provider state could not be read.");
|
|
17722
18026
|
return "back";
|
|
17723
18027
|
}
|
|
17724
18028
|
const outcome = accountSwitchOutcome(
|
|
@@ -17730,26 +18034,26 @@ async function runProviderDetail(id) {
|
|
|
17730
18034
|
if (outcome.ok && currentProvider.enabled && currentProvider.authType === "oauth" && (refreshExitCode !== 0 || !selectedCatalogReady)) {
|
|
17731
18035
|
const savedLabel = result.account ?? PROVIDER_DEFAULT_ACCOUNT_LABEL;
|
|
17732
18036
|
const savedContext = outcome.confirmsLaunch ? `Saved ${savedLabel} for ${currentProvider.name}.` : outcome.message;
|
|
17733
|
-
|
|
18037
|
+
p6.log.warn(
|
|
17734
18038
|
`${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.`
|
|
17735
18039
|
);
|
|
17736
18040
|
} else if (outcome.ok) {
|
|
17737
|
-
|
|
18041
|
+
p6.log.success(outcome.message);
|
|
17738
18042
|
} else {
|
|
17739
|
-
|
|
18043
|
+
p6.log.warn(outcome.message);
|
|
17740
18044
|
}
|
|
17741
18045
|
const restartWarning = accountSwitchServerRestartWarning(
|
|
17742
18046
|
readLiveServerRuntimeStates().length,
|
|
17743
18047
|
result.changed
|
|
17744
18048
|
);
|
|
17745
|
-
if (restartWarning)
|
|
18049
|
+
if (restartWarning) p6.log.warn(restartWarning);
|
|
17746
18050
|
return "back";
|
|
17747
18051
|
});
|
|
17748
18052
|
}
|
|
17749
18053
|
if (action === "toggle") {
|
|
17750
18054
|
const result = toggleProviderEnabled(id);
|
|
17751
18055
|
if (result.toggled) {
|
|
17752
|
-
|
|
18056
|
+
p6.log.success(`${provider.name} ${result.enabled ? "enabled" : "disabled"}.`);
|
|
17753
18057
|
}
|
|
17754
18058
|
return "back";
|
|
17755
18059
|
}
|
|
@@ -17785,11 +18089,11 @@ async function runProvidersHub() {
|
|
|
17785
18089
|
options.push({ value: "refresh-all", label: "\u21BA Refresh all models", hint: "Update model lists for all providers" });
|
|
17786
18090
|
}
|
|
17787
18091
|
options.push({ value: "done", label: "Done", hint: "" });
|
|
17788
|
-
const choice = await
|
|
18092
|
+
const choice = await p6.select({
|
|
17789
18093
|
message: entries.length > 0 ? "Your providers" : "Get started",
|
|
17790
18094
|
options
|
|
17791
18095
|
});
|
|
17792
|
-
if (
|
|
18096
|
+
if (p6.isCancel(choice) || choice === "done") {
|
|
17793
18097
|
return 0;
|
|
17794
18098
|
}
|
|
17795
18099
|
if (choice === "add") {
|
|
@@ -17807,7 +18111,7 @@ async function runProvidersHub() {
|
|
|
17807
18111
|
continue;
|
|
17808
18112
|
}
|
|
17809
18113
|
if (choice === "auth-account") {
|
|
17810
|
-
const name = await
|
|
18114
|
+
const name = await p6.text({
|
|
17811
18115
|
message: "Name for this account (choose which one launches with: clodex providers)",
|
|
17812
18116
|
placeholder: "work",
|
|
17813
18117
|
validate: (value) => {
|
|
@@ -17819,7 +18123,7 @@ async function runProvidersHub() {
|
|
|
17819
18123
|
}
|
|
17820
18124
|
}
|
|
17821
18125
|
});
|
|
17822
|
-
if (
|
|
18126
|
+
if (p6.isCancel(name)) continue;
|
|
17823
18127
|
const accountMethod = await promptOAuthMethod();
|
|
17824
18128
|
if (accountMethod === null) continue;
|
|
17825
18129
|
await runWithCredentialCleanup((state) => runProvidersAuthWithCleanupState("openai", accountMethod, state, String(name)));
|
|
@@ -17835,7 +18139,7 @@ async function runProvidersHub() {
|
|
|
17835
18139
|
async function runProvidersCommand(args) {
|
|
17836
18140
|
const parsed = parseProvidersArgs(args);
|
|
17837
18141
|
if (parsed.error) {
|
|
17838
|
-
|
|
18142
|
+
p6.log.error(parsed.error);
|
|
17839
18143
|
return 1;
|
|
17840
18144
|
}
|
|
17841
18145
|
if (parsed.showHelp && parsed.subcommand !== "auth") {
|
|
@@ -17872,7 +18176,7 @@ async function needsFirstRunSetup() {
|
|
|
17872
18176
|
}
|
|
17873
18177
|
async function runFirstRunWizard(_trace = false) {
|
|
17874
18178
|
printWelcomePanel();
|
|
17875
|
-
const choice = await
|
|
18179
|
+
const choice = await p7.select({
|
|
17876
18180
|
message: "How do you want to connect to OpenAI?",
|
|
17877
18181
|
options: [
|
|
17878
18182
|
{
|
|
@@ -17887,8 +18191,8 @@ async function runFirstRunWizard(_trace = false) {
|
|
|
17887
18191
|
}
|
|
17888
18192
|
]
|
|
17889
18193
|
});
|
|
17890
|
-
if (
|
|
17891
|
-
|
|
18194
|
+
if (p7.isCancel(choice)) {
|
|
18195
|
+
p7.cancel("Cancelled.");
|
|
17892
18196
|
return "cancel";
|
|
17893
18197
|
}
|
|
17894
18198
|
let code;
|
|
@@ -17901,14 +18205,14 @@ async function runFirstRunWizard(_trace = false) {
|
|
|
17901
18205
|
}
|
|
17902
18206
|
if (code !== 0) return "cancel";
|
|
17903
18207
|
if (await needsFirstRunSetup()) return "cancel";
|
|
17904
|
-
|
|
18208
|
+
p7.log.success("OpenAI provider ready \u2014 picking a model next.");
|
|
17905
18209
|
return "continue";
|
|
17906
18210
|
}
|
|
17907
18211
|
|
|
17908
18212
|
// src/server/index.ts
|
|
17909
18213
|
import pc11 from "picocolors";
|
|
17910
18214
|
import { networkInterfaces as networkInterfaces2 } from "os";
|
|
17911
|
-
import * as
|
|
18215
|
+
import * as p11 from "@clack/prompts";
|
|
17912
18216
|
|
|
17913
18217
|
// src/target-compatibility.ts
|
|
17914
18218
|
function blacklistAgentForTarget(target) {
|
|
@@ -17946,10 +18250,10 @@ function providersForTarget(providers, target) {
|
|
|
17946
18250
|
}
|
|
17947
18251
|
|
|
17948
18252
|
// src/server/prompts.ts
|
|
17949
|
-
import * as
|
|
18253
|
+
import * as p8 from "@clack/prompts";
|
|
17950
18254
|
import pc8 from "picocolors";
|
|
17951
18255
|
async function askServerStartMode() {
|
|
17952
|
-
const mode = await
|
|
18256
|
+
const mode = await p8.select({
|
|
17953
18257
|
message: "How do you want to start the server?",
|
|
17954
18258
|
options: [
|
|
17955
18259
|
{ value: "configure", label: pc8.cyan("Configure & start"), hint: "Providers, discovery masking, listen mode" },
|
|
@@ -17957,37 +18261,37 @@ async function askServerStartMode() {
|
|
|
17957
18261
|
],
|
|
17958
18262
|
initialValue: "configure"
|
|
17959
18263
|
});
|
|
17960
|
-
if (
|
|
17961
|
-
|
|
18264
|
+
if (p8.isCancel(mode)) {
|
|
18265
|
+
p8.cancel("Cancelled.");
|
|
17962
18266
|
return null;
|
|
17963
18267
|
}
|
|
17964
18268
|
return mode;
|
|
17965
18269
|
}
|
|
17966
18270
|
async function askMaskGatewayIds(initialValue) {
|
|
17967
|
-
const mask = await
|
|
18271
|
+
const mask = await p8.confirm({
|
|
17968
18272
|
message: "Mask gateway model ids for discovery? (Needed for Claude Desktop / Cowork)",
|
|
17969
18273
|
initialValue
|
|
17970
18274
|
});
|
|
17971
|
-
if (
|
|
17972
|
-
|
|
18275
|
+
if (p8.isCancel(mask)) {
|
|
18276
|
+
p8.cancel("Cancelled.");
|
|
17973
18277
|
return null;
|
|
17974
18278
|
}
|
|
17975
18279
|
return Boolean(mask);
|
|
17976
18280
|
}
|
|
17977
18281
|
async function askFavoritesOnly(initialValue) {
|
|
17978
18282
|
printFavoritesOnlyPanel();
|
|
17979
|
-
const favoritesOnly = await
|
|
18283
|
+
const favoritesOnly = await p8.confirm({
|
|
17980
18284
|
message: "Expose only favorite models?",
|
|
17981
18285
|
initialValue
|
|
17982
18286
|
});
|
|
17983
|
-
if (
|
|
17984
|
-
|
|
18287
|
+
if (p8.isCancel(favoritesOnly)) {
|
|
18288
|
+
p8.cancel("Cancelled.");
|
|
17985
18289
|
return null;
|
|
17986
18290
|
}
|
|
17987
18291
|
return Boolean(favoritesOnly);
|
|
17988
18292
|
}
|
|
17989
18293
|
async function askListenMode() {
|
|
17990
|
-
const mode = await
|
|
18294
|
+
const mode = await p8.select({
|
|
17991
18295
|
message: "Where should the server listen?",
|
|
17992
18296
|
options: [
|
|
17993
18297
|
{ value: "local", label: pc8.cyan("Local only"), hint: "Only this computer can use it" },
|
|
@@ -17995,26 +18299,26 @@ async function askListenMode() {
|
|
|
17995
18299
|
],
|
|
17996
18300
|
initialValue: "local"
|
|
17997
18301
|
});
|
|
17998
|
-
if (
|
|
17999
|
-
|
|
18302
|
+
if (p8.isCancel(mode)) {
|
|
18303
|
+
p8.cancel("Cancelled.");
|
|
18000
18304
|
return null;
|
|
18001
18305
|
}
|
|
18002
18306
|
return mode;
|
|
18003
18307
|
}
|
|
18004
18308
|
async function askServerPassword() {
|
|
18005
18309
|
printNetworkWarningPanel();
|
|
18006
|
-
const
|
|
18310
|
+
const password3 = await p8.text({
|
|
18007
18311
|
message: "Choose a server password for this run:",
|
|
18008
18312
|
validate: (value) => value.trim() ? void 0 : "Password cannot be empty"
|
|
18009
18313
|
});
|
|
18010
|
-
if (
|
|
18011
|
-
|
|
18314
|
+
if (p8.isCancel(password3)) {
|
|
18315
|
+
p8.cancel("Cancelled.");
|
|
18012
18316
|
return null;
|
|
18013
18317
|
}
|
|
18014
|
-
return String(
|
|
18318
|
+
return String(password3).trim();
|
|
18015
18319
|
}
|
|
18016
18320
|
async function askUseSavedServerPassword() {
|
|
18017
|
-
const choice = await
|
|
18321
|
+
const choice = await p8.select({
|
|
18018
18322
|
message: "Use saved server password?",
|
|
18019
18323
|
options: [
|
|
18020
18324
|
{ value: "use-saved", label: pc8.cyan("Use saved password") },
|
|
@@ -18022,19 +18326,19 @@ async function askUseSavedServerPassword() {
|
|
|
18022
18326
|
],
|
|
18023
18327
|
initialValue: "use-saved"
|
|
18024
18328
|
});
|
|
18025
|
-
if (
|
|
18026
|
-
|
|
18329
|
+
if (p8.isCancel(choice)) {
|
|
18330
|
+
p8.cancel("Cancelled.");
|
|
18027
18331
|
return null;
|
|
18028
18332
|
}
|
|
18029
18333
|
return choice;
|
|
18030
18334
|
}
|
|
18031
18335
|
async function askSaveServerPassword() {
|
|
18032
|
-
const save = await
|
|
18336
|
+
const save = await p8.confirm({
|
|
18033
18337
|
message: "Save this server password for future server runs?",
|
|
18034
18338
|
initialValue: false
|
|
18035
18339
|
});
|
|
18036
|
-
if (
|
|
18037
|
-
|
|
18340
|
+
if (p8.isCancel(save)) {
|
|
18341
|
+
p8.cancel("Cancelled.");
|
|
18038
18342
|
return null;
|
|
18039
18343
|
}
|
|
18040
18344
|
return Boolean(save);
|
|
@@ -18042,7 +18346,7 @@ async function askSaveServerPassword() {
|
|
|
18042
18346
|
|
|
18043
18347
|
// src/server/router.ts
|
|
18044
18348
|
import { createServer as createServer2 } from "http";
|
|
18045
|
-
import { randomUUID as
|
|
18349
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
18046
18350
|
|
|
18047
18351
|
// src/openai-adapter.ts
|
|
18048
18352
|
import { tool as tool2, jsonSchema as jsonSchema2, streamText as streamText2, generateText as generateText2 } from "ai";
|
|
@@ -18148,24 +18452,24 @@ function translateOpenAiRequest(body, options) {
|
|
|
18148
18452
|
async function collectOpenAiStream(stream) {
|
|
18149
18453
|
const collected = { text: "", toolCalls: [], finishReason: void 0, usage: void 0 };
|
|
18150
18454
|
for await (const part of stream) {
|
|
18151
|
-
const
|
|
18152
|
-
switch (
|
|
18455
|
+
const p14 = part;
|
|
18456
|
+
switch (p14.type) {
|
|
18153
18457
|
case "text-delta":
|
|
18154
|
-
collected.text +=
|
|
18458
|
+
collected.text += p14.textDelta ?? p14.text ?? "";
|
|
18155
18459
|
break;
|
|
18156
18460
|
case "tool-call":
|
|
18157
18461
|
collected.toolCalls.push({
|
|
18158
|
-
toolCallId:
|
|
18159
|
-
toolName:
|
|
18160
|
-
input:
|
|
18462
|
+
toolCallId: p14.toolCallId ?? "",
|
|
18463
|
+
toolName: p14.toolName ?? "",
|
|
18464
|
+
input: p14.input
|
|
18161
18465
|
});
|
|
18162
18466
|
break;
|
|
18163
18467
|
case "finish":
|
|
18164
|
-
collected.finishReason =
|
|
18165
|
-
collected.usage =
|
|
18468
|
+
collected.finishReason = p14.finishReason ?? collected.finishReason;
|
|
18469
|
+
collected.usage = p14.totalUsage ?? p14.usage ?? collected.usage;
|
|
18166
18470
|
break;
|
|
18167
18471
|
case "error":
|
|
18168
|
-
throw
|
|
18472
|
+
throw p14.error instanceof Error || p14.error && typeof p14.error === "object" ? p14.error : new Error(typeof p14.error === "string" ? p14.error : "Upstream stream failed");
|
|
18169
18473
|
}
|
|
18170
18474
|
}
|
|
18171
18475
|
return collected;
|
|
@@ -18290,22 +18594,22 @@ async function streamOpenAiResponse(model, params, responseModelId, onChunk, opt
|
|
|
18290
18594
|
|
|
18291
18595
|
`);
|
|
18292
18596
|
for await (const part of watchOpenAiStream(stream, budget)) {
|
|
18293
|
-
const
|
|
18294
|
-
switch (
|
|
18597
|
+
const p14 = part;
|
|
18598
|
+
switch (p14.type) {
|
|
18295
18599
|
case "text-delta":
|
|
18296
|
-
send({ role: "assistant", content:
|
|
18600
|
+
send({ role: "assistant", content: p14.textDelta ?? p14.text ?? "" });
|
|
18297
18601
|
break;
|
|
18298
18602
|
case "tool-input-start":
|
|
18299
|
-
send({ role: "assistant", tool_calls: [{ index: 0, id:
|
|
18603
|
+
send({ role: "assistant", tool_calls: [{ index: 0, id: p14.id ?? p14.toolCallId, type: "function", function: { name: p14.toolName, arguments: "" } }] });
|
|
18300
18604
|
break;
|
|
18301
18605
|
case "tool-input-delta":
|
|
18302
|
-
send({ tool_calls: [{ index: 0, function: { arguments:
|
|
18606
|
+
send({ tool_calls: [{ index: 0, function: { arguments: p14.delta ?? p14.text ?? p14.argsTextDelta ?? "" } }] });
|
|
18303
18607
|
break;
|
|
18304
18608
|
case "finish":
|
|
18305
|
-
send({},
|
|
18609
|
+
send({}, p14.finishReason || "stop");
|
|
18306
18610
|
break;
|
|
18307
18611
|
case "error":
|
|
18308
|
-
throw
|
|
18612
|
+
throw p14.error instanceof Error || p14.error && typeof p14.error === "object" ? p14.error : new Error(typeof p14.error === "string" ? p14.error : "Upstream stream failed");
|
|
18309
18613
|
}
|
|
18310
18614
|
}
|
|
18311
18615
|
onChunk("data: [DONE]\n\n");
|
|
@@ -18458,7 +18762,7 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
|
|
|
18458
18762
|
plog(`model not found: ${body.model}`);
|
|
18459
18763
|
return;
|
|
18460
18764
|
}
|
|
18461
|
-
const requestId =
|
|
18765
|
+
const requestId = randomUUID8();
|
|
18462
18766
|
const claudeSessionIdHeader = Array.isArray(req.headers["x-claude-code-session-id"]) ? req.headers["x-claude-code-session-id"][0] : req.headers["x-claude-code-session-id"];
|
|
18463
18767
|
const claudeSessionId = extractClaudeSessionId(body, claudeSessionIdHeader);
|
|
18464
18768
|
const claudeAgentIds = extractClaudeAgentIds(req.headers);
|
|
@@ -19039,7 +19343,7 @@ function summarizeServerProviders(models) {
|
|
|
19039
19343
|
|
|
19040
19344
|
// src/server/provider-select.ts
|
|
19041
19345
|
import pc9 from "picocolors";
|
|
19042
|
-
import * as
|
|
19346
|
+
import * as p9 from "@clack/prompts";
|
|
19043
19347
|
function isSelected(list, id) {
|
|
19044
19348
|
return list.includes(id);
|
|
19045
19349
|
}
|
|
@@ -19049,7 +19353,7 @@ function resolveInitialServerProviders(initial, available) {
|
|
|
19049
19353
|
}
|
|
19050
19354
|
async function selectServerProviders(available, initial) {
|
|
19051
19355
|
if (available.length === 0) {
|
|
19052
|
-
|
|
19356
|
+
p9.log.warn("No providers available to expose.");
|
|
19053
19357
|
return null;
|
|
19054
19358
|
}
|
|
19055
19359
|
let selected = resolveInitialServerProviders(initial, available);
|
|
@@ -19075,31 +19379,31 @@ async function selectServerProviders(available, initial) {
|
|
|
19075
19379
|
}
|
|
19076
19380
|
options.push({ value: "__done__", label: "Done", hint: "" });
|
|
19077
19381
|
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`;
|
|
19078
|
-
const choice = await
|
|
19382
|
+
const choice = await p9.select({
|
|
19079
19383
|
message: header,
|
|
19080
19384
|
options,
|
|
19081
19385
|
initialValue: "__done__"
|
|
19082
19386
|
});
|
|
19083
|
-
if (
|
|
19387
|
+
if (p9.isCancel(choice) || choice === "__done__") {
|
|
19084
19388
|
if (selected.length === 0) {
|
|
19085
|
-
|
|
19389
|
+
p9.log.warn("Select at least one provider to expose.");
|
|
19086
19390
|
continue;
|
|
19087
19391
|
}
|
|
19088
19392
|
break;
|
|
19089
19393
|
}
|
|
19090
19394
|
if (choice === "__all__") {
|
|
19091
19395
|
selected = available.map((provider) => provider.id);
|
|
19092
|
-
|
|
19396
|
+
p9.log.success(`Exposing all ${available.length} providers.`);
|
|
19093
19397
|
continue;
|
|
19094
19398
|
}
|
|
19095
19399
|
if (choice === "__clear__") {
|
|
19096
19400
|
selected = [];
|
|
19097
|
-
|
|
19401
|
+
p9.log.success("Cleared provider list \u2014 add the ones you want to expose.");
|
|
19098
19402
|
continue;
|
|
19099
19403
|
}
|
|
19100
19404
|
if (choice === "__add__") {
|
|
19101
19405
|
if (unselected.length === 0) continue;
|
|
19102
|
-
const picked = await
|
|
19406
|
+
const picked = await p9.select({
|
|
19103
19407
|
message: "Which provider?",
|
|
19104
19408
|
options: unselected.map((provider) => ({
|
|
19105
19409
|
value: provider.id,
|
|
@@ -19107,7 +19411,7 @@ async function selectServerProviders(available, initial) {
|
|
|
19107
19411
|
hint: `${provider.modelCount} model${provider.modelCount !== 1 ? "s" : ""}`
|
|
19108
19412
|
}))
|
|
19109
19413
|
});
|
|
19110
|
-
if (
|
|
19414
|
+
if (p9.isCancel(picked)) continue;
|
|
19111
19415
|
selected = [...selected, picked];
|
|
19112
19416
|
continue;
|
|
19113
19417
|
}
|
|
@@ -19117,7 +19421,7 @@ async function selectServerProviders(available, initial) {
|
|
|
19117
19421
|
if (!id) continue;
|
|
19118
19422
|
const provider = lookup2.get(id);
|
|
19119
19423
|
selected = selected.filter((_, i) => i !== idx);
|
|
19120
|
-
|
|
19424
|
+
p9.log.success(`Removed ${provider?.name ?? id}.`);
|
|
19121
19425
|
}
|
|
19122
19426
|
}
|
|
19123
19427
|
return selected;
|
|
@@ -19125,14 +19429,14 @@ async function selectServerProviders(available, initial) {
|
|
|
19125
19429
|
|
|
19126
19430
|
// src/http-proxy/index.ts
|
|
19127
19431
|
import pc10 from "picocolors";
|
|
19128
|
-
import * as
|
|
19432
|
+
import * as p10 from "@clack/prompts";
|
|
19129
19433
|
|
|
19130
19434
|
// src/http-proxy/server.ts
|
|
19131
19435
|
import * as http2 from "http";
|
|
19132
19436
|
import * as https from "https";
|
|
19133
19437
|
import * as net from "net";
|
|
19134
19438
|
import { PassThrough } from "stream";
|
|
19135
|
-
import { randomUUID as
|
|
19439
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
19136
19440
|
import { URL as URL2 } from "url";
|
|
19137
19441
|
import { createBrotliDecompress, createGunzip, createInflate } from "zlib";
|
|
19138
19442
|
|
|
@@ -19990,7 +20294,7 @@ async function startHttpProxy(options) {
|
|
|
19990
20294
|
}
|
|
19991
20295
|
const messagesEndpoint = anthropicMessagesEndpoint(req.url);
|
|
19992
20296
|
if (req.method === "POST" && messagesEndpoint) {
|
|
19993
|
-
const requestId =
|
|
20297
|
+
const requestId = randomUUID9();
|
|
19994
20298
|
let parsed = null;
|
|
19995
20299
|
let route;
|
|
19996
20300
|
let adapterBody = rawBody;
|
|
@@ -20279,10 +20583,10 @@ function printHttpProxyModels(routes, aliases = []) {
|
|
|
20279
20583
|
}
|
|
20280
20584
|
function reportSkippedHttpProxyFavorites(loaded) {
|
|
20281
20585
|
if (loaded.unavailable.length > 0) {
|
|
20282
|
-
|
|
20586
|
+
p10.log.warn(`${loaded.unavailable.length} favorite${loaded.unavailable.length === 1 ? "" : "s"} unavailable or missing credentials.`);
|
|
20283
20587
|
}
|
|
20284
20588
|
if (loaded.unsupported.length > 0) {
|
|
20285
|
-
|
|
20589
|
+
p10.log.warn(
|
|
20286
20590
|
`${loaded.unsupported.length} favorite${loaded.unsupported.length === 1 ? "" : "s"} skipped \u2014 HTTP proxy mode supports non-Anthropic AI SDK routes only.`
|
|
20287
20591
|
);
|
|
20288
20592
|
}
|
|
@@ -20294,7 +20598,7 @@ function reportSkippedHttpProxyFavorites(loaded) {
|
|
|
20294
20598
|
describeModelAliasRejection(rejection.reason)
|
|
20295
20599
|
])
|
|
20296
20600
|
);
|
|
20297
|
-
|
|
20601
|
+
p10.log.warn(
|
|
20298
20602
|
`${loaded.unavailableAliases.length} model alias${loaded.unavailableAliases.length === 1 ? "" : "es"} skipped. Saved entries were preserved.
|
|
20299
20603
|
` + loaded.unavailableAliases.map((alias) => ` ${JSON.stringify(alias.name)} \u2014 ${reasonByAlias.get(alias) ?? "target unavailable"}`).join("\n")
|
|
20300
20604
|
);
|
|
@@ -20375,7 +20679,7 @@ async function runHttpProxyServerCommand(debug = false, webSocketDiagnostics = f
|
|
|
20375
20679
|
webSocketDiagnosticsLogPath
|
|
20376
20680
|
);
|
|
20377
20681
|
} catch (err) {
|
|
20378
|
-
|
|
20682
|
+
p10.log.error(`Failed to start HTTP proxy: ${err instanceof Error ? err.message : String(err)}`);
|
|
20379
20683
|
return 1;
|
|
20380
20684
|
}
|
|
20381
20685
|
const { handle, loaded, caWarning } = started;
|
|
@@ -20578,8 +20882,8 @@ async function getServerPasswordForQuickMode(mode, passwordOverride) {
|
|
|
20578
20882
|
if (trimmedOverride) return { password: trimmedOverride, wasSaved: false };
|
|
20579
20883
|
const savedPassword = await getSavedServerPassword();
|
|
20580
20884
|
if (savedPassword) return { password: savedPassword, wasSaved: true };
|
|
20581
|
-
|
|
20582
|
-
|
|
20885
|
+
p11.log.error("Network server quick-start needs a saved server password or `--password <value>`.");
|
|
20886
|
+
p11.log.info("Run `clodex server` and choose Configure & start to save one, or pass a one-run password.");
|
|
20583
20887
|
return void 0;
|
|
20584
20888
|
}
|
|
20585
20889
|
function savedServerRunConfig() {
|
|
@@ -20613,16 +20917,16 @@ function shouldUseQuickServerMode(options) {
|
|
|
20613
20917
|
return Boolean(options.quick || hasServerRunOverrides(options) || !process.stdin.isTTY);
|
|
20614
20918
|
}
|
|
20615
20919
|
async function configureExposedProviders() {
|
|
20616
|
-
|
|
20617
|
-
const
|
|
20618
|
-
|
|
20920
|
+
p11.log.info("Add providers to expose. Listed providers are removed when selected \u2014 like favorites.");
|
|
20921
|
+
const spinner6 = p11.spinner();
|
|
20922
|
+
spinner6.start("Loading providers...");
|
|
20619
20923
|
const catalog = await fetchProviderCatalog({ agent: "server" });
|
|
20620
|
-
|
|
20924
|
+
spinner6.stop("");
|
|
20621
20925
|
const available = providerOptionsFromCatalog(catalog);
|
|
20622
20926
|
const picked = await selectServerProviders(available, getServerExposedProviders() ?? void 0);
|
|
20623
20927
|
if (!picked) return void 0;
|
|
20624
20928
|
setServerExposedProviders(picked);
|
|
20625
|
-
|
|
20929
|
+
p11.log.success(`Saved ${picked.length} provider${picked.length !== 1 ? "s" : ""} for future server runs.`);
|
|
20626
20930
|
return picked;
|
|
20627
20931
|
}
|
|
20628
20932
|
async function runServerWizard() {
|
|
@@ -20636,7 +20940,7 @@ async function runServerWizard() {
|
|
|
20636
20940
|
if (favoritesOnly === null) return void 0;
|
|
20637
20941
|
setServerFavoritesOnly(favoritesOnly);
|
|
20638
20942
|
if (favoritesOnly) {
|
|
20639
|
-
|
|
20943
|
+
p11.log.info("Manage favorites with `clodex models`.");
|
|
20640
20944
|
}
|
|
20641
20945
|
let exposedProviders = null;
|
|
20642
20946
|
if (!favoritesOnly) {
|
|
@@ -20666,14 +20970,14 @@ async function runServerCommand(options = {}) {
|
|
|
20666
20970
|
if (options.httpProxy) {
|
|
20667
20971
|
const hasGatewayOptions = options.quick || options.listenMode !== void 0 || options.providersMode !== void 0 || options.maskGatewayIds !== void 0 || options.password !== void 0;
|
|
20668
20972
|
if (hasGatewayOptions) {
|
|
20669
|
-
|
|
20973
|
+
p11.log.error("--proxy is a local-only server mode and cannot be combined with endpoint-mode server options.");
|
|
20670
20974
|
return 1;
|
|
20671
20975
|
}
|
|
20672
20976
|
return runHttpProxyServerCommand(false, options.wsDiagnostics, options.port, noDiscovery);
|
|
20673
20977
|
}
|
|
20674
20978
|
const apiKey = await resolveServerUpstreamApiKey();
|
|
20675
20979
|
if (!apiKey) {
|
|
20676
|
-
|
|
20980
|
+
p11.log.error("No providers configured. Run `clodex providers` to add OpenAI.");
|
|
20677
20981
|
return 1;
|
|
20678
20982
|
}
|
|
20679
20983
|
const quickMode = shouldUseQuickServerMode(options);
|
|
@@ -20688,8 +20992,8 @@ async function runServerCommand(options = {}) {
|
|
|
20688
20992
|
const { password: serverPassword, wasSaved: passwordWasSaved } = pwResult;
|
|
20689
20993
|
const mode = runConfig.listenMode;
|
|
20690
20994
|
const host = mode === "network" ? "0.0.0.0" : "127.0.0.1";
|
|
20691
|
-
const
|
|
20692
|
-
|
|
20995
|
+
const spinner6 = p11.spinner();
|
|
20996
|
+
spinner6.start("Fetching available models...");
|
|
20693
20997
|
let models;
|
|
20694
20998
|
try {
|
|
20695
20999
|
models = await loadServerModels();
|
|
@@ -20699,26 +21003,26 @@ async function runServerCommand(options = {}) {
|
|
|
20699
21003
|
if (runConfig.favoritesOnly) {
|
|
20700
21004
|
const favorites = loadPreferences().favoriteModels ?? [];
|
|
20701
21005
|
if (favorites.length === 0) {
|
|
20702
|
-
|
|
20703
|
-
|
|
21006
|
+
spinner6.stop(pc11.red("No favorite models configured"));
|
|
21007
|
+
p11.log.error("Run `clodex models` to add favorites, or turn off favorites-only in the server wizard.");
|
|
20704
21008
|
return 1;
|
|
20705
21009
|
}
|
|
20706
21010
|
models = filterServerModelsByFavorites(models, favorites).slice(0, MAX_MODEL_CATALOG);
|
|
20707
21011
|
if (models.length === 0) {
|
|
20708
|
-
|
|
20709
|
-
|
|
21012
|
+
spinner6.stop(pc11.red("No favorite models matched the current provider filter"));
|
|
21013
|
+
p11.log.error("Adjust favorites with `clodex models` or change exposed providers in the server wizard.");
|
|
20710
21014
|
return 1;
|
|
20711
21015
|
}
|
|
20712
21016
|
}
|
|
20713
21017
|
if (runConfig.favoritesOnly) {
|
|
20714
|
-
|
|
21018
|
+
p11.log.info(
|
|
20715
21019
|
`Favorites-only mode active \u2014 GET /anthropic/v1/models returns ${models.length} favorites.`
|
|
20716
21020
|
);
|
|
20717
|
-
|
|
21021
|
+
p11.log.info("Desktop/Cowork picker will only show these. Edit with `clodex models`.");
|
|
20718
21022
|
}
|
|
20719
21023
|
if (models.length === 0) {
|
|
20720
|
-
|
|
20721
|
-
|
|
21024
|
+
spinner6.stop(pc11.red("No models to expose"));
|
|
21025
|
+
p11.log.error("Add providers with `clodex providers add` or configure exposed providers in the server wizard.");
|
|
20722
21026
|
return 1;
|
|
20723
21027
|
}
|
|
20724
21028
|
const localCount = models.filter((m) => m.apiKey !== void 0).length;
|
|
@@ -20726,10 +21030,10 @@ async function runServerCommand(options = {}) {
|
|
|
20726
21030
|
const filterNote = runConfig.exposedProviders ? ` \u2014 ${runConfig.exposedProviders.length} provider${runConfig.exposedProviders.length !== 1 ? "s" : ""}` : "";
|
|
20727
21031
|
const favoritesNote = runConfig.favoritesOnly ? " \u2014 favorites only" : "";
|
|
20728
21032
|
const maskNote = runConfig.maskGatewayIds ? " \u2014 discovery ids masked" : "";
|
|
20729
|
-
|
|
20730
|
-
if (summary)
|
|
21033
|
+
spinner6.stop(`Loaded ${models.length} models (${localCount} from registry providers)${filterNote}${favoritesNote}${maskNote}`);
|
|
21034
|
+
if (summary) p11.log.info(summary);
|
|
20731
21035
|
} catch (err) {
|
|
20732
|
-
|
|
21036
|
+
spinner6.stop(pc11.red("Failed to load models"));
|
|
20733
21037
|
console.error(pc11.red(String(err instanceof Error ? err.message : err)));
|
|
20734
21038
|
return 1;
|
|
20735
21039
|
}
|
|
@@ -20751,7 +21055,7 @@ async function runServerCommand(options = {}) {
|
|
|
20751
21055
|
...collidingAliases.flatMap(({ sources }) => sources.map((source) => `${JSON.stringify(source.name)} \u2014 conflicts with a catalog model id`))
|
|
20752
21056
|
];
|
|
20753
21057
|
if (aliasWarnings.length > 0) {
|
|
20754
|
-
|
|
21058
|
+
p11.log.warn(
|
|
20755
21059
|
`${aliasWarnings.length} saved model alias${aliasWarnings.length === 1 ? "" : "es"} inactive. Saved entries were preserved.
|
|
20756
21060
|
${aliasWarnings.join("\n ")}`
|
|
20757
21061
|
);
|
|
@@ -20833,7 +21137,7 @@ function removeFavorite(list, fav) {
|
|
|
20833
21137
|
}
|
|
20834
21138
|
|
|
20835
21139
|
// src/favorites-picker.ts
|
|
20836
|
-
import * as
|
|
21140
|
+
import * as p12 from "@clack/prompts";
|
|
20837
21141
|
import pc12 from "picocolors";
|
|
20838
21142
|
|
|
20839
21143
|
// src/favorite-provider-display.ts
|
|
@@ -20911,25 +21215,25 @@ async function pickGlobalFavoriteModel(providers, favorites, opts) {
|
|
|
20911
21215
|
if (index.length === 0) return null;
|
|
20912
21216
|
const freeOnly = opts?.freeOnly === true;
|
|
20913
21217
|
while (true) {
|
|
20914
|
-
const searchInput = await
|
|
21218
|
+
const searchInput = await p12.text({
|
|
20915
21219
|
message: freeOnly ? `Search free models (${filterGlobalFavoriteIndex(index, "", { freeOnly: true }).length} models):` : `Search all providers (${index.length} models):`,
|
|
20916
21220
|
placeholder: "e.g. deepseek, claude, sonnet"
|
|
20917
21221
|
});
|
|
20918
|
-
if (
|
|
20919
|
-
const fallback = await
|
|
21222
|
+
if (p12.isCancel(searchInput)) {
|
|
21223
|
+
const fallback = await p12.select({
|
|
20920
21224
|
message: "Add a favorite",
|
|
20921
21225
|
options: [
|
|
20922
21226
|
{ value: "back", label: pc12.cyan("\u2190 Back to favorites"), hint: "" },
|
|
20923
21227
|
{ value: ADD_BY_PROVIDER, label: pc12.cyan("Browse by provider \u2192"), hint: "Pick one provider first" }
|
|
20924
21228
|
]
|
|
20925
21229
|
});
|
|
20926
|
-
if (
|
|
21230
|
+
if (p12.isCancel(fallback) || fallback === "back") return null;
|
|
20927
21231
|
if (fallback === ADD_BY_PROVIDER) return ADD_BY_PROVIDER;
|
|
20928
21232
|
continue;
|
|
20929
21233
|
}
|
|
20930
21234
|
const matched = filterGlobalFavoriteIndex(index, String(searchInput), { freeOnly });
|
|
20931
21235
|
if (matched.length === 0) {
|
|
20932
|
-
|
|
21236
|
+
p12.log.warn("No models match \u2014 try a different search");
|
|
20933
21237
|
continue;
|
|
20934
21238
|
}
|
|
20935
21239
|
const result = await pickModelFromPagedList(
|
|
@@ -20947,7 +21251,7 @@ async function pickGlobalFavoriteModel(providers, favorites, opts) {
|
|
|
20947
21251
|
const picked = parseGlobalFavoritePickKey(result.id, matched);
|
|
20948
21252
|
if (!picked) continue;
|
|
20949
21253
|
if (isFavorite(favorites, { providerId: picked.providerId, modelId: picked.model.id })) {
|
|
20950
|
-
|
|
21254
|
+
p12.log.warn(`${picked.model.name || picked.model.id} (${picked.providerName}) is already in your favorites.`);
|
|
20951
21255
|
continue;
|
|
20952
21256
|
}
|
|
20953
21257
|
return picked;
|
|
@@ -21017,7 +21321,7 @@ function resolveLaunchTarget(explicit, prefs, _agent = "claude") {
|
|
|
21017
21321
|
}
|
|
21018
21322
|
function findProviderAndModel(providers, target) {
|
|
21019
21323
|
if (!target.providerId || !target.modelId) return null;
|
|
21020
|
-
const provider = providers.find((
|
|
21324
|
+
const provider = providers.find((p14) => p14.id === target.providerId);
|
|
21021
21325
|
if (!provider) return null;
|
|
21022
21326
|
const model = provider.models.find((m) => m.id === target.modelId);
|
|
21023
21327
|
if (!model) return null;
|
|
@@ -21223,7 +21527,7 @@ function buildAndPublish(step) {
|
|
|
21223
21527
|
const compiled = step.compile(cscPath, CSC_ARGS, scratch);
|
|
21224
21528
|
const builtExe = join9(scratch, LAUNCHER_EXE_NAME);
|
|
21225
21529
|
if (compiled.error || compiled.status !== 0 || !existsSync7(builtExe)) {
|
|
21226
|
-
const output = [compiled.stdout, compiled.stderr].map((
|
|
21530
|
+
const output = [compiled.stdout, compiled.stderr].map((text6) => text6.trim()).filter(Boolean).join("\n");
|
|
21227
21531
|
return {
|
|
21228
21532
|
ok: false,
|
|
21229
21533
|
reason: "compile-failed",
|
|
@@ -21634,6 +21938,7 @@ ${pc13.bold("Providers:")}
|
|
|
21634
21938
|
openai OpenAI API key (platform.openai.com)
|
|
21635
21939
|
openai-oauth ChatGPT/Codex plan OAuth \u2014 sign in with clodex providers auth openai
|
|
21636
21940
|
opencode-go OpenCode Go API key \u2014 add with clodex providers add
|
|
21941
|
+
custom-<name> Any OpenAI-compatible server (OpenRouter, LM Studio, \u2026) \u2014 add with clodex providers add
|
|
21637
21942
|
|
|
21638
21943
|
${pc13.bold("Model switching:")}
|
|
21639
21944
|
Run clodex models to save favorites (max ${MAX_MODEL_CATALOG}).
|
|
@@ -21858,9 +22163,9 @@ ${pc13.bold("Behavior:")}
|
|
|
21858
22163
|
${pc13.bold("Setup guide:")}
|
|
21859
22164
|
docs/windows-setup.md in the clodex package (also on GitHub).`;
|
|
21860
22165
|
}
|
|
21861
|
-
function printHelp(
|
|
22166
|
+
function printHelp(text6) {
|
|
21862
22167
|
console.log(`
|
|
21863
|
-
${
|
|
22168
|
+
${text6}
|
|
21864
22169
|
`);
|
|
21865
22170
|
}
|
|
21866
22171
|
function reportInactiveCatalogAliases(modelAliases) {
|
|
@@ -21869,7 +22174,7 @@ function reportInactiveCatalogAliases(modelAliases) {
|
|
|
21869
22174
|
const warningLines = unavailableAliases.flatMap((alias) => alias.sourceNames?.length ? alias.sourceNames.map((name) => ` ${JSON.stringify(name)} \u2014 ${alias.unavailableReason}`) : [
|
|
21870
22175
|
` ${JSON.stringify(alias.savedName ?? alias.name)} \u2014 ${alias.unavailableReason}`
|
|
21871
22176
|
]);
|
|
21872
|
-
|
|
22177
|
+
p13.log.warn(
|
|
21873
22178
|
`${warningLines.length} saved model alias${warningLines.length === 1 ? "" : "es"} inactive. Saved entries were preserved.
|
|
21874
22179
|
` + warningLines.join("\n")
|
|
21875
22180
|
);
|
|
@@ -21887,11 +22192,11 @@ async function launchClaudeViaCatalog(catalogRoutes, startingRoute, modelAliases
|
|
|
21887
22192
|
void 0,
|
|
21888
22193
|
modelAliases
|
|
21889
22194
|
);
|
|
21890
|
-
|
|
22195
|
+
p13.log.info(
|
|
21891
22196
|
`Switch menu active \u2014 proxy on port ${proxyHandle.port} ` + pc13.dim(`(${catalogRoutes.length} model${catalogRoutes.length !== 1 ? "s" : ""} in /model)`)
|
|
21892
22197
|
);
|
|
21893
22198
|
} catch (err) {
|
|
21894
|
-
|
|
22199
|
+
p13.log.error(`Failed to start proxy: ${err instanceof Error ? err.message : String(err)}`);
|
|
21895
22200
|
return 1;
|
|
21896
22201
|
}
|
|
21897
22202
|
const childEnv = buildChildEnv(
|
|
@@ -21904,7 +22209,7 @@ async function launchClaudeViaCatalog(catalogRoutes, startingRoute, modelAliases
|
|
|
21904
22209
|
);
|
|
21905
22210
|
const debugLogPath = prepareClaudeTraceLog();
|
|
21906
22211
|
const traceArgs = trace ? ["--debug-file", debugLogPath] : [];
|
|
21907
|
-
if (trace)
|
|
22212
|
+
if (trace) p13.log.info(`Debug log: ${debugLogPath}`);
|
|
21908
22213
|
const exitCode = await launchClaude(
|
|
21909
22214
|
childEnv,
|
|
21910
22215
|
claudeCodeClientModelId(startingRoute.aliasId, contextWindow),
|
|
@@ -21915,9 +22220,9 @@ async function launchClaudeViaCatalog(catalogRoutes, startingRoute, modelAliases
|
|
|
21915
22220
|
return exitCode;
|
|
21916
22221
|
}
|
|
21917
22222
|
var CLACK_CONTEXT_REPORTER = {
|
|
21918
|
-
info: (message) =>
|
|
21919
|
-
warn: (message) =>
|
|
21920
|
-
success: (message) =>
|
|
22223
|
+
info: (message) => p13.log.info(message),
|
|
22224
|
+
warn: (message) => p13.log.warn(message),
|
|
22225
|
+
success: (message) => p13.log.success(message)
|
|
21921
22226
|
};
|
|
21922
22227
|
var STDERR_CONTEXT_REPORTER = {
|
|
21923
22228
|
info: (message) => console.error(message),
|
|
@@ -21932,7 +22237,7 @@ function applyContextStopAssignments(assignments, save, report, saveHint = "Add
|
|
|
21932
22237
|
for (const assignment of assignments) {
|
|
21933
22238
|
const cached = modelsByProvider.get(assignment.providerId)?.find((model) => model.id === assignment.modelId);
|
|
21934
22239
|
if (!cached) {
|
|
21935
|
-
|
|
22240
|
+
p13.log.error(
|
|
21936
22241
|
`${modelAliasTarget(assignment)} is not in the saved provider catalog. Add the provider and refresh its models first.`
|
|
21937
22242
|
);
|
|
21938
22243
|
return 1;
|
|
@@ -21966,12 +22271,12 @@ function applyContextStopAssignments(assignments, save, report, saveHint = "Add
|
|
|
21966
22271
|
async function runModelsCommand(opts = {}) {
|
|
21967
22272
|
const changesAlias = opts.alias !== void 0 || opts.unalias !== void 0;
|
|
21968
22273
|
if (changesAlias && (opts.list || opts.alias !== void 0 && opts.unalias !== void 0)) {
|
|
21969
|
-
|
|
22274
|
+
p13.log.error("--alias/--unalias apply one at a time to proxy-mode favorites.");
|
|
21970
22275
|
return 1;
|
|
21971
22276
|
}
|
|
21972
22277
|
const assignments = opts.contextAssignments ?? [];
|
|
21973
22278
|
if (opts.saveContext && assignments.length === 0) {
|
|
21974
|
-
|
|
22279
|
+
p13.log.error("--save applies to --context assignments; none were given.");
|
|
21975
22280
|
return 1;
|
|
21976
22281
|
}
|
|
21977
22282
|
if (assignments.length > 0) {
|
|
@@ -21990,7 +22295,7 @@ async function runModelsCommand(opts = {}) {
|
|
|
21990
22295
|
if (opts.alias !== void 0) {
|
|
21991
22296
|
const parsed = parseModelAliasAssignment(opts.alias);
|
|
21992
22297
|
if ("error" in parsed) {
|
|
21993
|
-
|
|
22298
|
+
p13.log.error(parsed.error);
|
|
21994
22299
|
return 1;
|
|
21995
22300
|
}
|
|
21996
22301
|
const prefs2 = loadPreferences();
|
|
@@ -21998,19 +22303,19 @@ async function runModelsCommand(opts = {}) {
|
|
|
21998
22303
|
(favorite) => favorite.providerId === parsed.providerId && favorite.modelId === parsed.modelId
|
|
21999
22304
|
);
|
|
22000
22305
|
if (!isSavedFavorite) {
|
|
22001
|
-
|
|
22002
|
-
|
|
22306
|
+
p13.log.error(`${modelAliasTarget(parsed)} is not a saved favorite.`);
|
|
22307
|
+
p13.log.info("Add it with `clodex models`, then save the alias.");
|
|
22003
22308
|
return 1;
|
|
22004
22309
|
}
|
|
22005
22310
|
if (prefs2.modelAliases !== void 0 && !Array.isArray(prefs2.modelAliases)) {
|
|
22006
|
-
|
|
22311
|
+
p13.log.error('Saved model aliases are malformed: "modelAliases" must be an array.');
|
|
22007
22312
|
return 1;
|
|
22008
22313
|
}
|
|
22009
22314
|
const aliases = prefs2.modelAliases ?? [];
|
|
22010
22315
|
const modelAliases = aliases.filter((alias) => !modelAliasMatchesName(alias, parsed.name));
|
|
22011
22316
|
modelAliases.push(parsed);
|
|
22012
22317
|
savePreferences({ modelAliases });
|
|
22013
|
-
|
|
22318
|
+
p13.log.success(`Saved model alias ${parsed.name} \u2192 ${modelAliasTarget(parsed)}.`);
|
|
22014
22319
|
return 0;
|
|
22015
22320
|
}
|
|
22016
22321
|
if (opts.unalias !== void 0) {
|
|
@@ -22018,18 +22323,18 @@ async function runModelsCommand(opts = {}) {
|
|
|
22018
22323
|
const name = canonicalModelAliasName(requestedName);
|
|
22019
22324
|
const prefs2 = loadPreferences();
|
|
22020
22325
|
if (prefs2.modelAliases !== void 0 && !Array.isArray(prefs2.modelAliases)) {
|
|
22021
|
-
|
|
22326
|
+
p13.log.error('Saved model aliases are malformed: "modelAliases" must be an array.');
|
|
22022
22327
|
return 1;
|
|
22023
22328
|
}
|
|
22024
22329
|
const aliases = prefs2.modelAliases ?? [];
|
|
22025
22330
|
const modelAliases = aliases.filter((alias) => !modelAliasMatchesStoredName(alias, requestedName));
|
|
22026
22331
|
const removedCount = aliases.length - modelAliases.length;
|
|
22027
22332
|
if (removedCount === 0) {
|
|
22028
|
-
|
|
22333
|
+
p13.log.error(`No model alias named ${JSON.stringify(requestedName)} is saved.`);
|
|
22029
22334
|
return 1;
|
|
22030
22335
|
}
|
|
22031
22336
|
savePreferences({ modelAliases });
|
|
22032
|
-
|
|
22337
|
+
p13.log.success(
|
|
22033
22338
|
removedCount === 1 ? `Removed model alias ${name || JSON.stringify(requestedName)}.` : `Removed ${removedCount} model aliases named ${name || JSON.stringify(requestedName)}.`
|
|
22034
22339
|
);
|
|
22035
22340
|
return 0;
|
|
@@ -22041,25 +22346,25 @@ async function runModelsCommand(opts = {}) {
|
|
|
22041
22346
|
reportSkippedHttpProxyFavorites(loaded);
|
|
22042
22347
|
return 0;
|
|
22043
22348
|
} catch (err) {
|
|
22044
|
-
|
|
22349
|
+
p13.log.error(`Could not load proxy models: ${err instanceof Error ? err.message : String(err)}`);
|
|
22045
22350
|
return 1;
|
|
22046
22351
|
}
|
|
22047
22352
|
}
|
|
22048
22353
|
const maxFavorites = MAX_MODEL_CATALOG;
|
|
22049
22354
|
const scopeName = "Favorite Models";
|
|
22050
22355
|
relayIntro(scopeName);
|
|
22051
|
-
const
|
|
22052
|
-
|
|
22356
|
+
const spinner6 = p13.spinner();
|
|
22357
|
+
spinner6.start("Loading providers...");
|
|
22053
22358
|
const catalog = await fetchProviderCatalog();
|
|
22054
|
-
|
|
22359
|
+
spinner6.stop("");
|
|
22055
22360
|
const allProviders = providersForPicker(catalog);
|
|
22056
22361
|
const favoriteProviders = allProviders.map((provider) => ({
|
|
22057
22362
|
...provider,
|
|
22058
22363
|
name: favoriteProviderDisplayName(provider)
|
|
22059
22364
|
}));
|
|
22060
22365
|
if (favoriteProviders.length === 0) {
|
|
22061
|
-
|
|
22062
|
-
|
|
22366
|
+
p13.log.warn("No providers found.");
|
|
22367
|
+
p13.log.info(`${pc13.dim("Add a provider with ")}${pc13.cyan("clodex providers")}${pc13.dim(".")}`);
|
|
22063
22368
|
relayOutro("Done");
|
|
22064
22369
|
return 0;
|
|
22065
22370
|
}
|
|
@@ -22088,19 +22393,19 @@ async function runModelsCommand(opts = {}) {
|
|
|
22088
22393
|
});
|
|
22089
22394
|
options.push({ value: "__done__", label: "Done", hint: "" });
|
|
22090
22395
|
const header = favorites.length === 0 ? `${scopeName} (0/${maxFavorites})` : `${scopeName} (${favorites.length}/${maxFavorites}) \u2014 select to remove`;
|
|
22091
|
-
const choice = await
|
|
22396
|
+
const choice = await p13.select({
|
|
22092
22397
|
message: header,
|
|
22093
22398
|
options,
|
|
22094
22399
|
initialValue: "__done__"
|
|
22095
22400
|
});
|
|
22096
|
-
if (
|
|
22401
|
+
if (p13.isCancel(choice) || choice === "__done__") break;
|
|
22097
22402
|
if (choice === "__add__") {
|
|
22098
22403
|
if (atCap) {
|
|
22099
|
-
|
|
22404
|
+
p13.log.warn(`Limit of ${maxFavorites} favorites reached \u2014 remove one first.`);
|
|
22100
22405
|
continue;
|
|
22101
22406
|
}
|
|
22102
22407
|
const globalCount = buildGlobalFavoriteIndex(favoriteProviders).length;
|
|
22103
|
-
const addPath = await
|
|
22408
|
+
const addPath = await p13.select({
|
|
22104
22409
|
message: "Add a favorite",
|
|
22105
22410
|
options: [
|
|
22106
22411
|
{
|
|
@@ -22115,7 +22420,7 @@ async function runModelsCommand(opts = {}) {
|
|
|
22115
22420
|
}
|
|
22116
22421
|
]
|
|
22117
22422
|
});
|
|
22118
|
-
if (
|
|
22423
|
+
if (p13.isCancel(addPath)) continue;
|
|
22119
22424
|
let provider;
|
|
22120
22425
|
let browsedMultiple = [];
|
|
22121
22426
|
if (addPath === "global") {
|
|
@@ -22130,12 +22435,12 @@ async function runModelsCommand(opts = {}) {
|
|
|
22130
22435
|
let currentInitialProvider = void 0;
|
|
22131
22436
|
while (true) {
|
|
22132
22437
|
const providerOptions = favoriteProviders.map((ap) => providerSelectOption(ap));
|
|
22133
|
-
const pickedProviderId = await
|
|
22438
|
+
const pickedProviderId = await p13.select({
|
|
22134
22439
|
message: "Which provider?",
|
|
22135
22440
|
options: providerOptions,
|
|
22136
22441
|
initialValue: currentInitialProvider
|
|
22137
22442
|
});
|
|
22138
|
-
if (
|
|
22443
|
+
if (p13.isCancel(pickedProviderId)) break;
|
|
22139
22444
|
provider = favoriteProviders.find((ap) => ap.id === pickedProviderId);
|
|
22140
22445
|
const options2 = provider.models.map((m) => {
|
|
22141
22446
|
const favorited = isFavorite(favorites, { providerId: provider.id, modelId: m.id });
|
|
@@ -22146,12 +22451,12 @@ async function runModelsCommand(opts = {}) {
|
|
|
22146
22451
|
hint: favorited ? pc13.yellow("\u2605 already favorite") : ""
|
|
22147
22452
|
};
|
|
22148
22453
|
});
|
|
22149
|
-
const pickedModelIds = await
|
|
22454
|
+
const pickedModelIds = await p13.multiselect({
|
|
22150
22455
|
message: `Select models to add from ${provider.name} ${pc13.dim("(Space to select, Enter to confirm)")}`,
|
|
22151
22456
|
options: options2,
|
|
22152
22457
|
required: false
|
|
22153
22458
|
});
|
|
22154
|
-
if (
|
|
22459
|
+
if (p13.isCancel(pickedModelIds)) {
|
|
22155
22460
|
currentInitialProvider = provider.id;
|
|
22156
22461
|
continue;
|
|
22157
22462
|
}
|
|
@@ -22186,27 +22491,27 @@ async function runModelsCommand(opts = {}) {
|
|
|
22186
22491
|
if (addedModels.length > 0) {
|
|
22187
22492
|
if (addedModels.length === 1) {
|
|
22188
22493
|
const modelName = addedModels[0].name || addedModels[0].id;
|
|
22189
|
-
|
|
22494
|
+
p13.log.success(`Added ${modelName} (${provider.name}) to favorites.`);
|
|
22190
22495
|
} else {
|
|
22191
|
-
|
|
22496
|
+
p13.log.success(`Added ${addedModels.length} models from ${provider.name} to favorites.`);
|
|
22192
22497
|
}
|
|
22193
22498
|
}
|
|
22194
22499
|
if (duplicateCount > 0) {
|
|
22195
|
-
|
|
22500
|
+
p13.log.warn(`${duplicateCount} selected model(s) were already in your favorites.`);
|
|
22196
22501
|
}
|
|
22197
22502
|
if (limitReached) {
|
|
22198
|
-
|
|
22503
|
+
p13.log.warn(`Limit of ${maxFavorites} favorites reached \u2014 some selected models could not be added.`);
|
|
22199
22504
|
}
|
|
22200
22505
|
} else if (choice.startsWith("fav-")) {
|
|
22201
22506
|
const idx = parseInt(choice.slice(4), 10);
|
|
22202
22507
|
const fav = favorites[idx];
|
|
22203
22508
|
const entry = modelLookup.get(`${fav.providerId}:${fav.modelId}`);
|
|
22204
22509
|
const label = entry ? `${entry.modelName} (${entry.providerName})` : fav.modelId;
|
|
22205
|
-
const confirmed = await
|
|
22206
|
-
if (
|
|
22510
|
+
const confirmed = await p13.confirm({ message: `Remove ${label} from favorites?` });
|
|
22511
|
+
if (p13.isCancel(confirmed) || !confirmed) continue;
|
|
22207
22512
|
favorites = removeFavorite(favorites, fav);
|
|
22208
22513
|
favoritesDirty = true;
|
|
22209
|
-
|
|
22514
|
+
p13.log.success(`Removed ${label} from favorites.`);
|
|
22210
22515
|
}
|
|
22211
22516
|
}
|
|
22212
22517
|
if (favoritesDirty) {
|
|
@@ -22220,8 +22525,8 @@ async function runModelsCommand(opts = {}) {
|
|
|
22220
22525
|
}
|
|
22221
22526
|
async function runClaudeHttpProxyCommand(parsed, claudeArgs, agentStdout) {
|
|
22222
22527
|
if (parsed.launchProvider || parsed.launchModel) {
|
|
22223
|
-
|
|
22224
|
-
|
|
22528
|
+
p13.log.error("--provider/--model select endpoint-mode routes and cannot be combined with --proxy.");
|
|
22529
|
+
p13.log.info("Use `-- --model clodex:<provider-id>:<model-id>` to start on a listed proxy-mode favorite.");
|
|
22225
22530
|
return 1;
|
|
22226
22531
|
}
|
|
22227
22532
|
if (!agentStdout) relayIntro("Claude Code \u2014 Proxy Mode");
|
|
@@ -22239,7 +22544,7 @@ async function runClaudeHttpProxyCommand(parsed, claudeArgs, agentStdout) {
|
|
|
22239
22544
|
console.log("");
|
|
22240
22545
|
return 0;
|
|
22241
22546
|
} catch (err) {
|
|
22242
|
-
|
|
22547
|
+
p13.log.error(`Could not load proxy models: ${err instanceof Error ? err.message : String(err)}`);
|
|
22243
22548
|
return 1;
|
|
22244
22549
|
}
|
|
22245
22550
|
}
|
|
@@ -22249,13 +22554,13 @@ async function runClaudeHttpProxyCommand(parsed, claudeArgs, agentStdout) {
|
|
|
22249
22554
|
try {
|
|
22250
22555
|
started = await startConfiguredHttpProxy(0, parsed.trace, inferenceLogPath, proxyDebugLogPath);
|
|
22251
22556
|
} catch (err) {
|
|
22252
|
-
|
|
22557
|
+
p13.log.error(`Failed to start proxy: ${err instanceof Error ? err.message : String(err)}`);
|
|
22253
22558
|
return 1;
|
|
22254
22559
|
}
|
|
22255
22560
|
const { handle, loaded, caWarning } = started;
|
|
22256
22561
|
if (caWarning) {
|
|
22257
22562
|
if (agentStdout) emitParentNotice(`clodex: ${caWarning}`);
|
|
22258
|
-
else
|
|
22563
|
+
else p13.log.warn(caWarning);
|
|
22259
22564
|
}
|
|
22260
22565
|
const inheritedProxyPort = (() => {
|
|
22261
22566
|
const value = process.env["HTTPS_PROXY"] ?? process.env["HTTP_PROXY"] ?? process.env["https_proxy"] ?? process.env["http_proxy"];
|
|
@@ -22289,20 +22594,20 @@ async function runClaudeHttpProxyCommand(parsed, claudeArgs, agentStdout) {
|
|
|
22289
22594
|
};
|
|
22290
22595
|
process.once("exit", onProcessExit);
|
|
22291
22596
|
if (!agentStdout) {
|
|
22292
|
-
|
|
22293
|
-
|
|
22597
|
+
p13.log.info(`Proxy started on port ${handle.port}; Claude Code's Anthropic auth remains active.`);
|
|
22598
|
+
p13.log.info(`Inference request log: ${handle.inferenceLogPath}`);
|
|
22294
22599
|
printHttpProxyModels(loaded.routes, loaded.aliases);
|
|
22295
22600
|
reportSkippedHttpProxyFavorites(loaded);
|
|
22296
22601
|
if (loaded.routes.length > 0) {
|
|
22297
|
-
|
|
22602
|
+
p13.log.info("Switch with `/model <listed-name>`.");
|
|
22298
22603
|
}
|
|
22299
22604
|
}
|
|
22300
22605
|
const childEnv = buildHttpProxyChildEnv(handle.port, handle.caCertPath);
|
|
22301
22606
|
const debugLogPath = parsed.trace ? prepareClaudeTraceLog(getSessionLogPath("claude-debug")) : void 0;
|
|
22302
22607
|
const traceArgs = debugLogPath ? ["--debug-file", debugLogPath] : [];
|
|
22303
22608
|
if (debugLogPath && !agentStdout) {
|
|
22304
|
-
|
|
22305
|
-
if (proxyDebugLogPath)
|
|
22609
|
+
p13.log.info(`Claude debug log: ${debugLogPath}`);
|
|
22610
|
+
if (proxyDebugLogPath) p13.log.info(`Adapter debug log: ${proxyDebugLogPath}`);
|
|
22306
22611
|
}
|
|
22307
22612
|
try {
|
|
22308
22613
|
const exitCode = await launchClaude(childEnv, void 0, [...traceArgs, ...claudeArgs]);
|
|
@@ -22387,7 +22692,7 @@ Error: ${launchPlan.error}
|
|
|
22387
22692
|
return 1;
|
|
22388
22693
|
}
|
|
22389
22694
|
} else {
|
|
22390
|
-
const catalogSpinner =
|
|
22695
|
+
const catalogSpinner = p13.spinner();
|
|
22391
22696
|
catalogSpinner.start("Loading your providers...");
|
|
22392
22697
|
try {
|
|
22393
22698
|
catalog = await fetchProviderCatalog();
|
|
@@ -22402,11 +22707,11 @@ Error: ${launchPlan.error}
|
|
|
22402
22707
|
const blockedLaunchReason = launchPlan.skip && launchPlan.target?.providerId ? catalog.blockedProviders.get(launchPlan.target.providerId) : void 0;
|
|
22403
22708
|
if (allProviders.length === 0) {
|
|
22404
22709
|
if (blockedLaunchReason) {
|
|
22405
|
-
|
|
22710
|
+
p13.log.error(blockedLaunchReason);
|
|
22406
22711
|
return 1;
|
|
22407
22712
|
}
|
|
22408
|
-
|
|
22409
|
-
|
|
22713
|
+
p13.log.warn("No providers available.");
|
|
22714
|
+
p13.log.info(pc13.dim("Run clodex providers to get started."));
|
|
22410
22715
|
return 0;
|
|
22411
22716
|
}
|
|
22412
22717
|
const providerOptions = allProviders.map((lp) => providerSelectOption(lp));
|
|
@@ -22423,7 +22728,7 @@ Error: ${launchPlan.error}
|
|
|
22423
22728
|
if (launchPlan.skip && launchPlan.target) {
|
|
22424
22729
|
const resolved = findProviderAndModel(allProviders, launchPlan.target);
|
|
22425
22730
|
if (!resolved) {
|
|
22426
|
-
|
|
22731
|
+
p13.log.error(
|
|
22427
22732
|
blockedLaunchReason ?? `Provider/model not found: ${launchPlan.target.providerId} / ${launchPlan.target.modelId}`
|
|
22428
22733
|
);
|
|
22429
22734
|
return 1;
|
|
@@ -22431,19 +22736,19 @@ Error: ${launchPlan.error}
|
|
|
22431
22736
|
activeProvider = resolved.provider;
|
|
22432
22737
|
selectedModel = resolved.model;
|
|
22433
22738
|
if (!agentStdout) {
|
|
22434
|
-
|
|
22739
|
+
p13.log.step(`Using ${selectedModel.name || selectedModel.id} (${activeProvider.name})`);
|
|
22435
22740
|
}
|
|
22436
22741
|
if (!dryRun) recordLaunchSelection("claude", activeProvider.id, selectedModel.id, prefs);
|
|
22437
22742
|
} else {
|
|
22438
22743
|
let currentInitialProvider = initialProvider;
|
|
22439
22744
|
while (true) {
|
|
22440
|
-
const chosen = await
|
|
22745
|
+
const chosen = await p13.select({
|
|
22441
22746
|
message: "Which provider?",
|
|
22442
22747
|
options: providerOptions,
|
|
22443
22748
|
initialValue: currentInitialProvider
|
|
22444
22749
|
});
|
|
22445
|
-
if (
|
|
22446
|
-
|
|
22750
|
+
if (p13.isCancel(chosen)) {
|
|
22751
|
+
p13.cancel("Cancelled.");
|
|
22447
22752
|
return 0;
|
|
22448
22753
|
}
|
|
22449
22754
|
const providerChoice = chosen;
|
|
@@ -22455,7 +22760,7 @@ Error: ${launchPlan.error}
|
|
|
22455
22760
|
if (prov && mod) available.push({ provider: prov, model: mod });
|
|
22456
22761
|
}
|
|
22457
22762
|
if (available.length === 0) {
|
|
22458
|
-
|
|
22763
|
+
p13.log.warn("No saved favorites are currently available.");
|
|
22459
22764
|
return 0;
|
|
22460
22765
|
}
|
|
22461
22766
|
const favOptions = available.map((f, i) => ({
|
|
@@ -22463,13 +22768,13 @@ Error: ${launchPlan.error}
|
|
|
22463
22768
|
label: `${f.model.name || f.model.id} \u2014 ${f.provider.name}`,
|
|
22464
22769
|
hint: f.model.id
|
|
22465
22770
|
}));
|
|
22466
|
-
const pickedIdx = await
|
|
22771
|
+
const pickedIdx = await p13.select({
|
|
22467
22772
|
message: "Starting model?",
|
|
22468
22773
|
options: favOptions,
|
|
22469
22774
|
initialValue: "0"
|
|
22470
22775
|
});
|
|
22471
|
-
if (
|
|
22472
|
-
|
|
22776
|
+
if (p13.isCancel(pickedIdx)) {
|
|
22777
|
+
p13.cancel("Cancelled.");
|
|
22473
22778
|
return 0;
|
|
22474
22779
|
}
|
|
22475
22780
|
const sel = available[Number(pickedIdx)];
|
|
@@ -22498,12 +22803,12 @@ Error: ${launchPlan.error}
|
|
|
22498
22803
|
);
|
|
22499
22804
|
const startingRoute = resolveRoute(activeProvider.id, selectedModel.id) ?? null;
|
|
22500
22805
|
if (!startingRoute) {
|
|
22501
|
-
|
|
22806
|
+
p13.log.error("Could not resolve a proxy route for the selected model.");
|
|
22502
22807
|
return 1;
|
|
22503
22808
|
}
|
|
22504
22809
|
const { routes: catalogRoutes, droppedFavorites } = buildCatalogRoutes(startingRoute, favorites, resolveRoute);
|
|
22505
22810
|
if (droppedFavorites.length > 0) {
|
|
22506
|
-
|
|
22811
|
+
p13.log.warn(
|
|
22507
22812
|
`Skipping ${droppedFavorites.length} favorite${droppedFavorites.length === 1 ? "" : "s"} that are no longer available in /model`
|
|
22508
22813
|
);
|
|
22509
22814
|
}
|
|
@@ -22557,7 +22862,7 @@ Error: ${launchPlan.error}
|
|
|
22557
22862
|
}
|
|
22558
22863
|
const launchApiKey = await resolveLocalProviderApiKey(activeProvider);
|
|
22559
22864
|
if (!anonymousProvider && !launchApiKey?.trim()) {
|
|
22560
|
-
|
|
22865
|
+
p13.log.error(
|
|
22561
22866
|
`No credential found for ${activeProvider.name}. Add a key or sign in with clodex providers.`
|
|
22562
22867
|
);
|
|
22563
22868
|
return 1;
|
|
@@ -22583,9 +22888,9 @@ Error: ${launchPlan.error}
|
|
|
22583
22888
|
},
|
|
22584
22889
|
launchApiKey ?? ""
|
|
22585
22890
|
);
|
|
22586
|
-
if (!isAgentStdoutMode())
|
|
22891
|
+
if (!isAgentStdoutMode()) p13.log.info(`Anthropic proxy started on port ${proxyHandle.port}`);
|
|
22587
22892
|
} catch (err) {
|
|
22588
|
-
|
|
22893
|
+
p13.log.error(`Failed to start Anthropic proxy: ${err instanceof Error ? err.message : String(err)}`);
|
|
22589
22894
|
return 1;
|
|
22590
22895
|
}
|
|
22591
22896
|
childEnv = buildChildEnv(
|
|
@@ -22629,12 +22934,12 @@ Error: ${launchPlan.error}
|
|
|
22629
22934
|
launchApiKey ?? ""
|
|
22630
22935
|
);
|
|
22631
22936
|
if (!isAgentStdoutMode()) {
|
|
22632
|
-
|
|
22937
|
+
p13.log.info(
|
|
22633
22938
|
`SDK adapter proxy started on port ${proxyHandle.port}` + (selectedModel.npm ? pc13.dim(` (${selectedModel.npm})`) : "")
|
|
22634
22939
|
);
|
|
22635
22940
|
}
|
|
22636
22941
|
} catch (err) {
|
|
22637
|
-
|
|
22942
|
+
p13.log.error(`Failed to start SDK adapter proxy: ${err instanceof Error ? err.message : String(err)}`);
|
|
22638
22943
|
return 1;
|
|
22639
22944
|
}
|
|
22640
22945
|
childEnv = buildChildEnv(
|
|
@@ -22650,7 +22955,7 @@ Error: ${launchPlan.error}
|
|
|
22650
22955
|
}
|
|
22651
22956
|
const debugLogPath = prepareClaudeTraceLog();
|
|
22652
22957
|
const traceArgs = trace ? ["--debug-file", debugLogPath] : [];
|
|
22653
|
-
if (trace)
|
|
22958
|
+
if (trace) p13.log.info(`Debug log: ${debugLogPath}`);
|
|
22654
22959
|
const exitCode = await launchClaude(
|
|
22655
22960
|
childEnv,
|
|
22656
22961
|
claudeCodeClientModelId(selectedModel.id, selectedModel.contextWindow),
|