@bman654/clodex 2.14.1 → 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
|
},
|
|
@@ -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));
|
|
@@ -3600,7 +3602,7 @@ function applyClodexPatches(source, config) {
|
|
|
3600
3602
|
);
|
|
3601
3603
|
const cases = missing.map((a) => "case" + q(a) + ":return " + q(a) + ";").join("");
|
|
3602
3604
|
if (ALIASES.length === 0) {
|
|
3603
|
-
|
|
3605
|
+
log13("SKIP", "PATCH 6: alias resolver switch", "no aliases configured");
|
|
3604
3606
|
} else {
|
|
3605
3607
|
applyOnce(
|
|
3606
3608
|
"PATCH 6: alias resolver switch",
|
|
@@ -3623,9 +3625,9 @@ function applyClodexPatches(source, config) {
|
|
|
3623
3625
|
const pickerSite = "PATCH 5: model picker options";
|
|
3624
3626
|
const modelSelections = [...js.matchAll(/\(([\w$]+)==="opus"\|\|\1==="sonnet"\)/g)];
|
|
3625
3627
|
if (ALIASES.length === 0) {
|
|
3626
|
-
|
|
3628
|
+
log13("SKIP", pickerSite, "no aliases configured");
|
|
3627
3629
|
} else if (modelSelections.length !== 1) {
|
|
3628
|
-
|
|
3630
|
+
log13("FAIL", pickerSite, "model selection appears " + modelSelections.length + " times (expected 1)");
|
|
3629
3631
|
} else {
|
|
3630
3632
|
applyOnce(
|
|
3631
3633
|
pickerSite,
|
|
@@ -3753,11 +3755,11 @@ function applyClodexPatches(source, config) {
|
|
|
3753
3755
|
"CLAUDE_CODE_OTEL_DIAG_STDERR"
|
|
3754
3756
|
];
|
|
3755
3757
|
const MIN_SCRUBBED_ENV_NAMES = 1;
|
|
3756
|
-
const blockEndIndex = (
|
|
3758
|
+
const blockEndIndex = (text6, open2) => {
|
|
3757
3759
|
const depths = [0];
|
|
3758
3760
|
const templates = [false];
|
|
3759
|
-
for (let i = open2; i <
|
|
3760
|
-
const ch =
|
|
3761
|
+
for (let i = open2; i < text6.length; i++) {
|
|
3762
|
+
const ch = text6[i];
|
|
3761
3763
|
const inTemplate = templates[templates.length - 1];
|
|
3762
3764
|
if (inTemplate) {
|
|
3763
3765
|
if (ch === "\\") {
|
|
@@ -3769,7 +3771,7 @@ function applyClodexPatches(source, config) {
|
|
|
3769
3771
|
depths.pop();
|
|
3770
3772
|
continue;
|
|
3771
3773
|
}
|
|
3772
|
-
if (ch === "$" &&
|
|
3774
|
+
if (ch === "$" && text6[i + 1] === "{") {
|
|
3773
3775
|
templates.push(false);
|
|
3774
3776
|
depths.push(0);
|
|
3775
3777
|
i++;
|
|
@@ -3782,12 +3784,12 @@ function applyClodexPatches(source, config) {
|
|
|
3782
3784
|
}
|
|
3783
3785
|
if (ch === '"' || ch === "'") {
|
|
3784
3786
|
const quote = ch;
|
|
3785
|
-
for (i++; i <
|
|
3786
|
-
if (
|
|
3787
|
+
for (i++; i < text6.length; i++) {
|
|
3788
|
+
if (text6[i] === "\\") {
|
|
3787
3789
|
i++;
|
|
3788
3790
|
continue;
|
|
3789
3791
|
}
|
|
3790
|
-
if (
|
|
3792
|
+
if (text6[i] === quote) break;
|
|
3791
3793
|
}
|
|
3792
3794
|
continue;
|
|
3793
3795
|
}
|
|
@@ -3796,12 +3798,12 @@ function applyClodexPatches(source, config) {
|
|
|
3796
3798
|
depths.push(0);
|
|
3797
3799
|
continue;
|
|
3798
3800
|
}
|
|
3799
|
-
if (ch === "/" &&
|
|
3800
|
-
while (i <
|
|
3801
|
+
if (ch === "/" && text6[i + 1] === "/") {
|
|
3802
|
+
while (i < text6.length && text6[i] !== "\n") i++;
|
|
3801
3803
|
continue;
|
|
3802
3804
|
}
|
|
3803
|
-
if (ch === "/" &&
|
|
3804
|
-
i =
|
|
3805
|
+
if (ch === "/" && text6[i + 1] === "*") {
|
|
3806
|
+
i = text6.indexOf("*/", i + 2);
|
|
3805
3807
|
if (i < 0) return -1;
|
|
3806
3808
|
i++;
|
|
3807
3809
|
continue;
|
|
@@ -3824,7 +3826,7 @@ function applyClodexPatches(source, config) {
|
|
|
3824
3826
|
};
|
|
3825
3827
|
const passthroughSites = source.includes(marker) ? 1 : (source.match(/\)return process\.env;let [\w$]+=\{/g) ?? []).length;
|
|
3826
3828
|
if (passthroughSites !== 1) {
|
|
3827
|
-
|
|
3829
|
+
log13("FAIL", patchName, "child env passthrough appears " + passthroughSites + " times (expected 1)");
|
|
3828
3830
|
fail("clodex patch: required patch failed: " + patchName);
|
|
3829
3831
|
}
|
|
3830
3832
|
applyOnce(
|
|
@@ -3839,7 +3841,7 @@ function applyClodexPatches(source, config) {
|
|
|
3839
3841
|
const scrubbedSeen = scrubbedEnvNames.filter((name) => body.includes(name));
|
|
3840
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;
|
|
3841
3843
|
if (why !== void 0) {
|
|
3842
|
-
|
|
3844
|
+
log13("FAIL", patchName, "target validation failed: " + why);
|
|
3843
3845
|
fail("clodex patch: child network environment target validation failed: " + why);
|
|
3844
3846
|
}
|
|
3845
3847
|
const restoredBody = body.replace(/process\.env/g, "_clodexChildEnv");
|
|
@@ -6046,6 +6048,22 @@ function normalizeGoogleDisplayName(rawName, bareId) {
|
|
|
6046
6048
|
return stripGoogleModelPrefix(trimmed);
|
|
6047
6049
|
}
|
|
6048
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
|
+
|
|
6049
6067
|
// src/trace-log.ts
|
|
6050
6068
|
import {
|
|
6051
6069
|
chmodSync as chmodSync3,
|
|
@@ -6135,8 +6153,8 @@ function compactLogValueWithMarker(value, max) {
|
|
|
6135
6153
|
function systemPreview(system) {
|
|
6136
6154
|
if (typeof system === "string") return compactLogValue(system, REQUEST_PREVIEW_MAX) || void 0;
|
|
6137
6155
|
if (!Array.isArray(system)) return void 0;
|
|
6138
|
-
const
|
|
6139
|
-
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;
|
|
6140
6158
|
}
|
|
6141
6159
|
function inlineSystemPreview(messages) {
|
|
6142
6160
|
if (!Array.isArray(messages)) return void 0;
|
|
@@ -6162,9 +6180,9 @@ function getLatestMessagePreview(messages, system) {
|
|
|
6162
6180
|
if (typeof content === "string") {
|
|
6163
6181
|
summary = content;
|
|
6164
6182
|
} else if (Array.isArray(content)) {
|
|
6165
|
-
const
|
|
6166
|
-
if (
|
|
6167
|
-
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;
|
|
6168
6186
|
} else {
|
|
6169
6187
|
const blockTypes = [...new Set(content.filter((block) => Boolean(block && typeof block === "object")).map((block) => typeof block.type === "string" ? block.type : "unknown"))];
|
|
6170
6188
|
if (blockTypes.length > 0) blockSummary = `${role}: [${blockTypes.join(", ")}]`;
|
|
@@ -6436,8 +6454,8 @@ function writeSecureLogLine(path, line) {
|
|
|
6436
6454
|
function printTraceLog(debugLogPath) {
|
|
6437
6455
|
if (!existsSync3(debugLogPath)) return;
|
|
6438
6456
|
const raw = readFileSync6(debugLogPath, "utf8");
|
|
6439
|
-
const
|
|
6440
|
-
const errorLines =
|
|
6457
|
+
const log13 = redactTraceLog(raw);
|
|
6458
|
+
const errorLines = log13.split("\n").filter(
|
|
6441
6459
|
(l) => l.includes("error") || l.includes("Error") || l.includes('"type":"error"') || l.includes("status") || l.includes("resolveModel failed") || l.includes("resolveModel fallback")
|
|
6442
6460
|
);
|
|
6443
6461
|
console.log("\n" + pc2.bold(pc2.cyan("\u2500\u2500 Debug trace \u2500\u2500")));
|
|
@@ -6499,13 +6517,14 @@ function parseModelList(body, npm, allowBareArray) {
|
|
|
6499
6517
|
for (const row of rows) {
|
|
6500
6518
|
const rawId = row.id?.trim();
|
|
6501
6519
|
if (!rawId) continue;
|
|
6520
|
+
if (hasControlChars(rawId) || typeof row.name === "string" && hasControlChars(row.name.trim())) continue;
|
|
6502
6521
|
const { id, upstreamModelId: upstreamModelId2 } = normalizeGoogleModelId(rawId, npm);
|
|
6503
6522
|
const family = id.split(/[-/:]/)[0] ?? id;
|
|
6504
6523
|
const cost = parseNativePricing(row.pricing);
|
|
6505
6524
|
const freeStatus = classifyFreeStatus({
|
|
6506
6525
|
model: { cost, isFree: row.isFree }
|
|
6507
6526
|
});
|
|
6508
|
-
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);
|
|
6509
6528
|
models.push({
|
|
6510
6529
|
id,
|
|
6511
6530
|
name: normalizeGoogleDisplayName(row.name, id),
|
|
@@ -6930,9 +6949,9 @@ function collectTargets(contents, kind) {
|
|
|
6930
6949
|
function readNpmShim(shimPath) {
|
|
6931
6950
|
const kind = launcherKind(shimPath);
|
|
6932
6951
|
const unreadable = kind === "sh" ? { kind: "not-a-shim" } : { kind: "unreadable-launcher" };
|
|
6933
|
-
const
|
|
6934
|
-
if (
|
|
6935
|
-
const rawTargets = collectTargets(
|
|
6952
|
+
const text6 = readLauncherText(shimPath);
|
|
6953
|
+
if (text6 === null) return unreadable;
|
|
6954
|
+
const rawTargets = collectTargets(text6, kind);
|
|
6936
6955
|
if (rawTargets.length === 0) return unreadable;
|
|
6937
6956
|
if (!rawTargets.every(isLiteralPath)) return unreadable;
|
|
6938
6957
|
const absolute = rawTargets.map((target) => resolve2(
|
|
@@ -7060,11 +7079,11 @@ function inspectClaudeNativeBinaryPlaceholder(path) {
|
|
|
7060
7079
|
offset += bytesRead;
|
|
7061
7080
|
}
|
|
7062
7081
|
if (offset !== size) return null;
|
|
7063
|
-
const
|
|
7082
|
+
const text6 = bytes.toString("utf8");
|
|
7064
7083
|
const signals = [
|
|
7065
|
-
|
|
7066
|
-
|
|
7067
|
-
|
|
7084
|
+
text6.includes(MISSING_BINARY_MARKER),
|
|
7085
|
+
text6.includes(INSTALL_SCRIPT_MARKER),
|
|
7086
|
+
text6.includes(OMIT_OPTIONAL_MARKER) && text6.includes(IGNORE_SCRIPTS_MARKER)
|
|
7068
7087
|
];
|
|
7069
7088
|
if (signals.filter(Boolean).length < 2) return null;
|
|
7070
7089
|
return inspectNativePackage(path);
|
|
@@ -8893,11 +8912,11 @@ function reasoningGapShape(expected, actual, full, storedTail, index) {
|
|
|
8893
8912
|
}
|
|
8894
8913
|
var warnedReasoningGaps = /* @__PURE__ */ new Set();
|
|
8895
8914
|
var MAX_REASONING_GAP_WARNINGS = 3;
|
|
8896
|
-
function warnReasoningNormalizationGap(fields,
|
|
8915
|
+
function warnReasoningNormalizationGap(fields, log13) {
|
|
8897
8916
|
const signature = fields.join(",");
|
|
8898
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`;
|
|
8899
8918
|
try {
|
|
8900
|
-
|
|
8919
|
+
log13?.(`reasoning normalization gap: ${signature}`);
|
|
8901
8920
|
} catch {
|
|
8902
8921
|
}
|
|
8903
8922
|
if (warnedReasoningGaps.has(signature)) return;
|
|
@@ -8943,12 +8962,12 @@ function toolArgumentNormalizationGap(expected, actual, defaults, requiredProps)
|
|
|
8943
8962
|
}
|
|
8944
8963
|
var warnedToolArgumentGaps = /* @__PURE__ */ new Set();
|
|
8945
8964
|
var MAX_TOOL_ARGUMENT_GAP_WARNINGS = 3;
|
|
8946
|
-
function warnToolArgumentNormalizationGap(gap,
|
|
8965
|
+
function warnToolArgumentNormalizationGap(gap, log13) {
|
|
8947
8966
|
const tool3 = typeof gap.tool === "string" ? gap.tool : "unknown";
|
|
8948
8967
|
const signature = `${tool3}:filler`;
|
|
8949
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`;
|
|
8950
8969
|
try {
|
|
8951
|
-
|
|
8970
|
+
log13?.(`tool argument normalization gap: ${signature}`);
|
|
8952
8971
|
} catch {
|
|
8953
8972
|
}
|
|
8954
8973
|
if (warnedToolArgumentGaps.has(signature)) return;
|
|
@@ -8959,7 +8978,7 @@ function warnToolArgumentNormalizationGap(gap, log12) {
|
|
|
8959
8978
|
emitParentNotice("clodex: warning: further tool-argument normalization warnings suppressed.");
|
|
8960
8979
|
}
|
|
8961
8980
|
}
|
|
8962
|
-
function continuationMismatchDetails(entry, payload, defaults,
|
|
8981
|
+
function continuationMismatchDetails(entry, payload, defaults, log13, warnOnGap = false, deferWarnings) {
|
|
8963
8982
|
const raise = (warn) => {
|
|
8964
8983
|
if (deferWarnings) deferWarnings.push(warn);
|
|
8965
8984
|
else warn();
|
|
@@ -8977,7 +8996,7 @@ function continuationMismatchDetails(entry, payload, defaults, log12, warnOnGap
|
|
|
8977
8996
|
const expected = mismatch < prefix.length ? prefix[mismatch] : void 0;
|
|
8978
8997
|
const actual = mismatch < full.length ? full[mismatch] : void 0;
|
|
8979
8998
|
const reasoningGap = reasoningNormalizationGap(expected, actual, defaults);
|
|
8980
|
-
if (reasoningGap && warnOnGap) raise(() => warnReasoningNormalizationGap(reasoningGap,
|
|
8999
|
+
if (reasoningGap && warnOnGap) raise(() => warnReasoningNormalizationGap(reasoningGap, log13));
|
|
8981
9000
|
let gapExpected = expected;
|
|
8982
9001
|
if (conversationItemKind(expected) === "reasoning" && conversationItemKind(actual) === "function_call") {
|
|
8983
9002
|
for (let index = mismatch; index < prefix.length; index += 1) {
|
|
@@ -9001,10 +9020,10 @@ function continuationMismatchDetails(entry, payload, defaults, log12, warnOnGap
|
|
|
9001
9020
|
}
|
|
9002
9021
|
if (toolArgumentGap?.equalAfterStrip === true) {
|
|
9003
9022
|
const gap = toolArgumentGap;
|
|
9004
|
-
if (warnOnGap) raise(() => warnToolArgumentNormalizationGap(gap,
|
|
9023
|
+
if (warnOnGap) raise(() => warnToolArgumentNormalizationGap(gap, log13));
|
|
9005
9024
|
} else if (toolArgumentGap && warnOnGap) {
|
|
9006
9025
|
try {
|
|
9007
|
-
|
|
9026
|
+
log13?.(`tool argument mismatch beyond the strip rule: ${String(toolArgumentGap.tool)}`);
|
|
9008
9027
|
} catch {
|
|
9009
9028
|
}
|
|
9010
9029
|
}
|
|
@@ -9029,17 +9048,17 @@ function continuationMismatchDetails(entry, payload, defaults, log12, warnOnGap
|
|
|
9029
9048
|
...toolArgumentGap ? { toolArgumentNormalizationGap: toolArgumentGap } : {}
|
|
9030
9049
|
};
|
|
9031
9050
|
}
|
|
9032
|
-
function continuationMismatchSummary(entry, payload, defaults,
|
|
9033
|
-
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);
|
|
9034
9053
|
let summary = `full_items=${details.fullItems} expected_prefix_items=${details.expectedPrefixItems} first_mismatch=${details.firstMismatch} expected=${details.expectedKind} actual=${details.actualKind}`;
|
|
9035
9054
|
if (details.expectedHash || details.actualHash) {
|
|
9036
9055
|
summary += ` expected_hash=${details.expectedHash ?? "none"} actual_hash=${details.actualHash ?? "none"}`;
|
|
9037
|
-
if (mismatchDump &&
|
|
9056
|
+
if (mismatchDump && log13) {
|
|
9038
9057
|
const full = inputArray(payload);
|
|
9039
9058
|
const prefix = [...entry.requestInput ?? [], ...entry.expectedAssistant ?? []];
|
|
9040
9059
|
const index = details.firstMismatch;
|
|
9041
|
-
|
|
9042
|
-
|
|
9060
|
+
log13(`mismatch dump expected[${index}]: ${mismatchDumpLine(prefix, index, defaults)}`);
|
|
9061
|
+
log13(`mismatch dump actual[${index}]: ${mismatchDumpLine(full, index, defaults)}`);
|
|
9043
9062
|
}
|
|
9044
9063
|
}
|
|
9045
9064
|
return summary;
|
|
@@ -9382,7 +9401,7 @@ function requiredToolProps(payload) {
|
|
|
9382
9401
|
if (record.type !== "function" || typeof record.name !== "string") return;
|
|
9383
9402
|
const parameters = record.parameters;
|
|
9384
9403
|
const required = parameters && typeof parameters === "object" && Array.isArray(parameters.required) ? parameters.required : [];
|
|
9385
|
-
map.set(record.name, new Set(required.filter((
|
|
9404
|
+
map.set(record.name, new Set(required.filter((p14) => typeof p14 === "string")));
|
|
9386
9405
|
};
|
|
9387
9406
|
if (Array.isArray(payload.tools)) for (const tool3 of payload.tools) add(tool3);
|
|
9388
9407
|
return map;
|
|
@@ -9411,12 +9430,12 @@ function expectedAssistantItems(ctx) {
|
|
|
9411
9430
|
const type = accumulator.type ?? (typeof done.type === "string" ? done.type : void 0);
|
|
9412
9431
|
if (type === "message") {
|
|
9413
9432
|
const doneContent = Array.isArray(done.content) ? done.content : void 0;
|
|
9414
|
-
const
|
|
9415
|
-
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 }] });
|
|
9416
9435
|
continue;
|
|
9417
9436
|
}
|
|
9418
9437
|
if (type === "reasoning") {
|
|
9419
|
-
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 : [];
|
|
9420
9439
|
output.push({ ...withoutEphemeralFields(done), type: "reasoning", summary });
|
|
9421
9440
|
continue;
|
|
9422
9441
|
}
|
|
@@ -9706,7 +9725,7 @@ function handleSocketMessage(entry, data) {
|
|
|
9706
9725
|
if (entry.connectionDiagnostic) observeIdleFrame(entry, data);
|
|
9707
9726
|
return;
|
|
9708
9727
|
}
|
|
9709
|
-
const
|
|
9728
|
+
const text6 = Array.isArray(data) ? Buffer.concat(data).toString("utf8") : data.toString("utf8");
|
|
9710
9729
|
ctx.frameCount += 1;
|
|
9711
9730
|
if (ctx.transportRetryPending) {
|
|
9712
9731
|
ctx.transportRetryPending = false;
|
|
@@ -9718,9 +9737,9 @@ function handleSocketMessage(entry, data) {
|
|
|
9718
9737
|
}
|
|
9719
9738
|
let event;
|
|
9720
9739
|
try {
|
|
9721
|
-
event = JSON.parse(
|
|
9740
|
+
event = JSON.parse(text6);
|
|
9722
9741
|
} catch {
|
|
9723
|
-
ctx.pendingEvents.push(
|
|
9742
|
+
ctx.pendingEvents.push(text6.replace(/\r?\n/g, " "));
|
|
9724
9743
|
flushPending(ctx);
|
|
9725
9744
|
return;
|
|
9726
9745
|
}
|
|
@@ -10068,23 +10087,23 @@ function createConnection(WebSocket, wsUrl, headers, persistent, key, options, d
|
|
|
10068
10087
|
function diagnosticCap(cap) {
|
|
10069
10088
|
return Number.isFinite(cap) ? cap : null;
|
|
10070
10089
|
}
|
|
10071
|
-
function envConnectionCap(name,
|
|
10090
|
+
function envConnectionCap(name, log13) {
|
|
10072
10091
|
const raw = process.env[name];
|
|
10073
10092
|
if (raw === void 0 || raw.trim() === "") return void 0;
|
|
10074
10093
|
const value = Number(raw.trim());
|
|
10075
10094
|
if (!Number.isInteger(value) || value < 1) {
|
|
10076
10095
|
try {
|
|
10077
|
-
|
|
10096
|
+
log13?.(`ws: ignoring ${name}=${raw} (expected a positive integer)`);
|
|
10078
10097
|
} catch {
|
|
10079
10098
|
}
|
|
10080
10099
|
return void 0;
|
|
10081
10100
|
}
|
|
10082
10101
|
return value;
|
|
10083
10102
|
}
|
|
10084
|
-
function createResponsesWebSocketFetch(wsUrl,
|
|
10103
|
+
function createResponsesWebSocketFetch(wsUrl, log13, options = {}) {
|
|
10085
10104
|
const debug = (message) => {
|
|
10086
10105
|
try {
|
|
10087
|
-
|
|
10106
|
+
log13?.(`ws: ${message}`);
|
|
10088
10107
|
} catch {
|
|
10089
10108
|
}
|
|
10090
10109
|
};
|
|
@@ -10093,8 +10112,8 @@ function createResponsesWebSocketFetch(wsUrl, log12, options = {}) {
|
|
|
10093
10112
|
hardTtlMs: options.hardTtlMs ?? RESPONSES_WS_HARD_TTL_MS,
|
|
10094
10113
|
idleTtlMs: options.idleTtlMs ?? RESPONSES_WS_IDLE_TTL_MS,
|
|
10095
10114
|
nurseryIdleTtlMs: options.nurseryIdleTtlMs ?? Math.min(RESPONSES_WS_NURSERY_IDLE_TTL_MS, options.idleTtlMs ?? RESPONSES_WS_IDLE_TTL_MS),
|
|
10096
|
-
maxConnections: options.maxConnections ?? envConnectionCap("CLODEX_WS_MAX_CONNECTIONS",
|
|
10097
|
-
maxNurseryConnections: options.maxNurseryConnections ?? envConnectionCap("CLODEX_WS_MAX_NURSERY_CONNECTIONS",
|
|
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,
|
|
10098
10117
|
now: options.now ?? Date.now
|
|
10099
10118
|
};
|
|
10100
10119
|
return async (_input, init) => {
|
|
@@ -10529,8 +10548,8 @@ function buildClaudeCodeBillingSystemLine() {
|
|
|
10529
10548
|
function systemBlockText(block) {
|
|
10530
10549
|
if (typeof block === "string") return block;
|
|
10531
10550
|
if (block && typeof block === "object" && "text" in block) {
|
|
10532
|
-
const
|
|
10533
|
-
return typeof
|
|
10551
|
+
const text6 = block.text;
|
|
10552
|
+
return typeof text6 === "string" ? text6 : void 0;
|
|
10534
10553
|
}
|
|
10535
10554
|
return void 0;
|
|
10536
10555
|
}
|
|
@@ -10903,7 +10922,7 @@ function toCamelCase(str) {
|
|
|
10903
10922
|
return str.replace(/[-_]([a-z])/g, (_, g) => g.toUpperCase());
|
|
10904
10923
|
}
|
|
10905
10924
|
function hasSupportedParameter(metadata, param) {
|
|
10906
|
-
return (metadata?.supportedParameters ?? []).some((
|
|
10925
|
+
return (metadata?.supportedParameters ?? []).some((p14) => p14 === param);
|
|
10907
10926
|
}
|
|
10908
10927
|
function isOpenRouterRoute(npm, metadata) {
|
|
10909
10928
|
return npm === "@openrouter/ai-sdk-provider" || metadata?.providerId === "openrouter" || metadata?.apiBaseUrl?.includes("openrouter.ai") === true;
|
|
@@ -11862,10 +11881,10 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
|
|
|
11862
11881
|
res.end(JSON.stringify({ type: "error", error: { type: "api_error", message: "Upstream returned empty response body" } }));
|
|
11863
11882
|
return;
|
|
11864
11883
|
}
|
|
11865
|
-
let
|
|
11884
|
+
let text6 = await upstreamRes.text();
|
|
11866
11885
|
let parsed;
|
|
11867
11886
|
try {
|
|
11868
|
-
parsed = JSON.parse(
|
|
11887
|
+
parsed = JSON.parse(text6);
|
|
11869
11888
|
} catch {
|
|
11870
11889
|
res.writeHead(502, { "Content-Type": "application/json" });
|
|
11871
11890
|
res.end(JSON.stringify({ type: "error", error: { type: "api_error", message: "Upstream response was not valid JSON" } }));
|
|
@@ -11885,13 +11904,13 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
|
|
|
11885
11904
|
changed = true;
|
|
11886
11905
|
}
|
|
11887
11906
|
}
|
|
11888
|
-
if (changed)
|
|
11907
|
+
if (changed) text6 = JSON.stringify(message);
|
|
11889
11908
|
}
|
|
11890
11909
|
res.writeHead(200, {
|
|
11891
11910
|
"Content-Type": "application/json",
|
|
11892
|
-
"Content-Length": Buffer.byteLength(
|
|
11911
|
+
"Content-Length": Buffer.byteLength(text6).toString()
|
|
11893
11912
|
});
|
|
11894
|
-
res.end(
|
|
11913
|
+
res.end(text6);
|
|
11895
11914
|
}
|
|
11896
11915
|
|
|
11897
11916
|
// src/proxy.ts
|
|
@@ -12243,12 +12262,12 @@ var OpenAiThinkingBlock = class {
|
|
|
12243
12262
|
append(part) {
|
|
12244
12263
|
const entry = this.parts.get(part.id ?? "");
|
|
12245
12264
|
if (!entry) throw new Error("OpenAI reasoning delta has no matching start");
|
|
12246
|
-
const
|
|
12247
|
-
if (!
|
|
12265
|
+
const text6 = part.text ?? "";
|
|
12266
|
+
if (!text6) return "";
|
|
12248
12267
|
const separator = entry.text.length === 0 && this.hasText ? "\n\n" : "";
|
|
12249
|
-
entry.text +=
|
|
12268
|
+
entry.text += text6;
|
|
12250
12269
|
this.hasText = true;
|
|
12251
|
-
return separator +
|
|
12270
|
+
return separator + text6;
|
|
12252
12271
|
}
|
|
12253
12272
|
end(part) {
|
|
12254
12273
|
const entry = this.parts.get(part.id ?? "");
|
|
@@ -12262,7 +12281,7 @@ var OpenAiThinkingBlock = class {
|
|
|
12262
12281
|
function isRecord(value) {
|
|
12263
12282
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
12264
12283
|
}
|
|
12265
|
-
function restoreOpenAiThinking(
|
|
12284
|
+
function restoreOpenAiThinking(text6, signature, npm) {
|
|
12266
12285
|
if (!signature?.startsWith(SIGNATURE_PREFIX)) return void 0;
|
|
12267
12286
|
if (!signature.startsWith(SIGNATURE_V1)) return [];
|
|
12268
12287
|
try {
|
|
@@ -12273,7 +12292,7 @@ function restoreOpenAiThinking(text5, signature, npm) {
|
|
|
12273
12292
|
if (!isRecord(part) || typeof part.itemId !== "string" || !part.itemId || typeof part.text !== "string" || part.encryptedContent !== void 0 && typeof part.encryptedContent !== "string") return [];
|
|
12274
12293
|
parts.push(part);
|
|
12275
12294
|
}
|
|
12276
|
-
if (npm !== "@ai-sdk/openai") return
|
|
12295
|
+
if (npm !== "@ai-sdk/openai") return text6 ? [{ type: "reasoning", text: text6 }] : [];
|
|
12277
12296
|
return parts.map((part) => ({
|
|
12278
12297
|
type: "reasoning",
|
|
12279
12298
|
text: part.text,
|
|
@@ -12347,10 +12366,10 @@ function supportsOpenAiPromptCacheBreakpoints(modelId) {
|
|
|
12347
12366
|
const minor = Number(match[2] ?? 0);
|
|
12348
12367
|
return major > 5 || major === 5 && minor >= 6;
|
|
12349
12368
|
}
|
|
12350
|
-
function stripClaudeCodeBillingHeader(
|
|
12351
|
-
if (!
|
|
12352
|
-
const newline =
|
|
12353
|
-
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);
|
|
12354
12373
|
}
|
|
12355
12374
|
function systemToString(system, stripAnthropicBillingHeader = false) {
|
|
12356
12375
|
if (!system) return void 0;
|
|
@@ -12359,8 +12378,8 @@ function systemToString(system, stripAnthropicBillingHeader = false) {
|
|
|
12359
12378
|
}
|
|
12360
12379
|
const blocks = system.map((b) => typeof b === "string" ? b : b.text ?? "");
|
|
12361
12380
|
if (!stripAnthropicBillingHeader) return blocks.join("\n");
|
|
12362
|
-
return blocks.flatMap((
|
|
12363
|
-
const stripped = stripClaudeCodeBillingHeader(
|
|
12381
|
+
return blocks.flatMap((text6) => {
|
|
12382
|
+
const stripped = stripClaudeCodeBillingHeader(text6);
|
|
12364
12383
|
return stripped === void 0 ? [] : [stripped];
|
|
12365
12384
|
}).join("\n");
|
|
12366
12385
|
}
|
|
@@ -12376,12 +12395,12 @@ function translateTopLevelSystemForOpenAi(system) {
|
|
|
12376
12395
|
}
|
|
12377
12396
|
return system.flatMap((block) => {
|
|
12378
12397
|
const raw = typeof block === "string" ? block : block.text ?? "";
|
|
12379
|
-
const
|
|
12380
|
-
if (!
|
|
12398
|
+
const text6 = stripClaudeCodeBillingHeader(raw) ?? "";
|
|
12399
|
+
if (!text6.trim()) return [];
|
|
12381
12400
|
const cacheControl = typeof block === "string" ? void 0 : block.cache_control;
|
|
12382
12401
|
return [{
|
|
12383
12402
|
role: "system",
|
|
12384
|
-
content:
|
|
12403
|
+
content: text6,
|
|
12385
12404
|
...cacheControl ? { providerOptions: { openai: { promptCacheBreakpoint: { mode: "explicit" } } } } : {}
|
|
12386
12405
|
}];
|
|
12387
12406
|
});
|
|
@@ -12438,9 +12457,9 @@ function annotateToolNames(messages) {
|
|
|
12438
12457
|
}
|
|
12439
12458
|
}
|
|
12440
12459
|
function thinkingToSdkPart(block, npm) {
|
|
12441
|
-
const
|
|
12442
|
-
if (npm === "@ai-sdk/openai" && !block.signature && !
|
|
12443
|
-
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 };
|
|
12444
12463
|
if (block.signature) {
|
|
12445
12464
|
if (npm === "@ai-sdk/google") {
|
|
12446
12465
|
part.providerOptions = { google: { thoughtSignature: block.signature } };
|
|
@@ -12475,10 +12494,10 @@ function translateMessages(messages, npm, openAiPromptCacheBreakpoints = false)
|
|
|
12475
12494
|
...openAiCacheBreakpoint(b, openAiPromptCacheBreakpoints) ? { providerOptions: openAiCacheBreakpoint(b, openAiPromptCacheBreakpoints) } : {}
|
|
12476
12495
|
});
|
|
12477
12496
|
} else if (b.type === "image") {
|
|
12478
|
-
const
|
|
12479
|
-
if (
|
|
12497
|
+
const p14 = imagePart(b);
|
|
12498
|
+
if (p14) {
|
|
12480
12499
|
parts.push({
|
|
12481
|
-
...
|
|
12500
|
+
...p14,
|
|
12482
12501
|
...openAiCacheBreakpoint(b, openAiPromptCacheBreakpoints) ? { providerOptions: openAiCacheBreakpoint(b, openAiPromptCacheBreakpoints) } : {}
|
|
12483
12502
|
});
|
|
12484
12503
|
}
|
|
@@ -12531,10 +12550,10 @@ function translateMessages(messages, npm, openAiPromptCacheBreakpoints = false)
|
|
|
12531
12550
|
function isPlainObject(value) {
|
|
12532
12551
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
12533
12552
|
}
|
|
12534
|
-
function parsesAsJson(
|
|
12535
|
-
if (
|
|
12553
|
+
function parsesAsJson(text6) {
|
|
12554
|
+
if (text6.trim() === "") return true;
|
|
12536
12555
|
try {
|
|
12537
|
-
JSON.parse(
|
|
12556
|
+
JSON.parse(text6);
|
|
12538
12557
|
return true;
|
|
12539
12558
|
} catch {
|
|
12540
12559
|
return false;
|
|
@@ -12583,7 +12602,7 @@ function isClaudeCodeCompactRequest(body) {
|
|
|
12583
12602
|
const finalMessage = body.messages.at(-1);
|
|
12584
12603
|
if (!finalMessage || finalMessage.role !== "user") return false;
|
|
12585
12604
|
const texts = typeof finalMessage.content === "string" ? [finalMessage.content] : finalMessage.content.filter((block) => block.type === "text").map((block) => block.text ?? "");
|
|
12586
|
-
return texts.some((
|
|
12605
|
+
return texts.some((text6) => text6.startsWith(COMPACT_TEXT_ONLY_START) && text6.includes(COMPACT_TEXT_ONLY_END));
|
|
12587
12606
|
}
|
|
12588
12607
|
var COMPACT_DRIFT_ANCHOR = "Tool calls will be REJECTED and will waste your only turn";
|
|
12589
12608
|
var COMPACT_DRIFT_ANCHOR_LINE = new RegExp(
|
|
@@ -12595,25 +12614,25 @@ function looksLikeDriftedClaudeCodeCompactRequest(body) {
|
|
|
12595
12614
|
const finalMessage = body.messages.at(-1);
|
|
12596
12615
|
if (!finalMessage || finalMessage.role !== "user") return false;
|
|
12597
12616
|
const texts = typeof finalMessage.content === "string" ? [finalMessage.content] : finalMessage.content.filter((block) => block.type === "text").map((block) => block.text ?? "");
|
|
12598
|
-
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));
|
|
12599
12618
|
}
|
|
12600
12619
|
function claudeCodeVersionFromRequest(body) {
|
|
12601
12620
|
const texts = typeof body.system === "string" ? [body.system] : (body.system ?? []).map((block) => typeof block === "string" ? block : block.text ?? "");
|
|
12602
|
-
for (const
|
|
12603
|
-
if (!
|
|
12604
|
-
const match =
|
|
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|$)/);
|
|
12605
12624
|
if (match) return match[1];
|
|
12606
12625
|
}
|
|
12607
12626
|
return void 0;
|
|
12608
12627
|
}
|
|
12609
12628
|
var warnedCompactPromptDrifts = /* @__PURE__ */ new Set();
|
|
12610
12629
|
var MAX_COMPACT_PROMPT_DRIFT_WARNINGS = 3;
|
|
12611
|
-
function reportClaudeCodeCompactPromptDrift(body,
|
|
12630
|
+
function reportClaudeCodeCompactPromptDrift(body, log13) {
|
|
12612
12631
|
if (!looksLikeDriftedClaudeCodeCompactRequest(body)) return;
|
|
12613
12632
|
const version = claudeCodeVersionFromRequest(body);
|
|
12614
12633
|
const signature = version ?? "unknown-version";
|
|
12615
12634
|
try {
|
|
12616
|
-
|
|
12635
|
+
log13?.(`possible Claude Code compact prompt drift: ${signature}`);
|
|
12617
12636
|
} catch {
|
|
12618
12637
|
}
|
|
12619
12638
|
if (warnedCompactPromptDrifts.has(signature)) return;
|
|
@@ -12749,7 +12768,7 @@ function forwardAbortSignal(source, target) {
|
|
|
12749
12768
|
function translatedMessageId() {
|
|
12750
12769
|
return "clodex_" + randomUUID5().replace(/-/g, "");
|
|
12751
12770
|
}
|
|
12752
|
-
async function writeAnthropicStream(stream, modelId, write,
|
|
12771
|
+
async function writeAnthropicStream(stream, modelId, write, log13, observer, tools) {
|
|
12753
12772
|
const messageId = translatedMessageId();
|
|
12754
12773
|
const requiredProps = toolRequiredProps(tools);
|
|
12755
12774
|
let blockIndex = -1;
|
|
@@ -12946,7 +12965,7 @@ async function writeAnthropicStream(stream, modelId, write, log12, observer, too
|
|
|
12946
12965
|
const errMsg = e?.message || (typeof part.error === "string" ? part.error : JSON.stringify(e?.data ?? part.error));
|
|
12947
12966
|
const transportCode = sdkUpstreamErrorDetails(part.error)?.transportCode;
|
|
12948
12967
|
const errorType = anthropicErrorType(upstreamHttpStatus(part.error, errMsg), transportCode);
|
|
12949
|
-
|
|
12968
|
+
log13?.(() => `sdk stream error (${errorType}): ${errMsg}`);
|
|
12950
12969
|
if (!(transportCode === "websocket_transport_error" && openType === "thinking")) closeOpen();
|
|
12951
12970
|
throw part.error instanceof Error || part.error && typeof part.error === "object" ? part.error : new Error(errMsg);
|
|
12952
12971
|
}
|
|
@@ -12960,7 +12979,7 @@ async function writeAnthropicStream(stream, modelId, write, log12, observer, too
|
|
|
12960
12979
|
emit("message_delta", { type: "message_delta", delta: { stop_reason: finishReason, stop_sequence: null }, usage });
|
|
12961
12980
|
emit("message_stop", { type: "message_stop" });
|
|
12962
12981
|
}
|
|
12963
|
-
async function streamAnthropicResponse(model, params, modelId, write,
|
|
12982
|
+
async function streamAnthropicResponse(model, params, modelId, write, log13, observer) {
|
|
12964
12983
|
const { idleTimeoutMs, totalTimeoutMs, maxRetries } = upstreamRequestBudget({
|
|
12965
12984
|
idleTimeoutMs: observer?.idleTimeoutMs
|
|
12966
12985
|
});
|
|
@@ -12999,7 +13018,7 @@ async function streamAnthropicResponse(model, params, modelId, write, log12, obs
|
|
|
12999
13018
|
clearTimeout(idleTimer);
|
|
13000
13019
|
}
|
|
13001
13020
|
})();
|
|
13002
|
-
await writeAnthropicStream(watchedStream, modelId, write,
|
|
13021
|
+
await writeAnthropicStream(watchedStream, modelId, write, log13, { ...observer, abortSignal }, params.tools);
|
|
13003
13022
|
} finally {
|
|
13004
13023
|
stopForwardingAbort();
|
|
13005
13024
|
clearTimeout(idleTimer);
|
|
@@ -13008,7 +13027,7 @@ async function streamAnthropicResponse(model, params, modelId, write, log12, obs
|
|
|
13008
13027
|
}
|
|
13009
13028
|
}
|
|
13010
13029
|
async function generateAnthropicResponse(model, params, modelId, options) {
|
|
13011
|
-
let
|
|
13030
|
+
let text6;
|
|
13012
13031
|
let toolCalls;
|
|
13013
13032
|
let finishReason;
|
|
13014
13033
|
let usage;
|
|
@@ -13074,7 +13093,7 @@ async function generateAnthropicResponse(model, params, modelId, options) {
|
|
|
13074
13093
|
clearTimeout(totalTimer);
|
|
13075
13094
|
if (!forceAbort.signal.aborted) forceAbort.abort();
|
|
13076
13095
|
}
|
|
13077
|
-
|
|
13096
|
+
text6 = streamedText.join("");
|
|
13078
13097
|
toolCalls = streamedToolCalls;
|
|
13079
13098
|
finishReason = streamedFinishReason;
|
|
13080
13099
|
usage = streamedUsage;
|
|
@@ -13094,7 +13113,7 @@ async function generateAnthropicResponse(model, params, modelId, options) {
|
|
|
13094
13113
|
maxRetries,
|
|
13095
13114
|
abortSignal: generateAbort.signal
|
|
13096
13115
|
});
|
|
13097
|
-
({ text:
|
|
13116
|
+
({ text: text6, toolCalls, finishReason, usage, warnings } = r);
|
|
13098
13117
|
} catch (error) {
|
|
13099
13118
|
if (generateAbort.signal.aborted) throw streamAbortError(generateAbort.signal);
|
|
13100
13119
|
throw error;
|
|
@@ -13113,7 +13132,7 @@ async function generateAnthropicResponse(model, params, modelId, options) {
|
|
|
13113
13132
|
role: "assistant",
|
|
13114
13133
|
model: modelId,
|
|
13115
13134
|
content: [
|
|
13116
|
-
...
|
|
13135
|
+
...text6 ? [{ type: "text", text: text6 }] : [],
|
|
13117
13136
|
...toolCalls.map((tc) => ({
|
|
13118
13137
|
type: "tool_use",
|
|
13119
13138
|
id: encodeToolUseId(tc.toolCallId, grabRoundTripSignature(tc)),
|
|
@@ -14812,11 +14831,11 @@ function formatModelMetadata(entries) {
|
|
|
14812
14831
|
|
|
14813
14832
|
// src/first-run.ts
|
|
14814
14833
|
import pc7 from "picocolors";
|
|
14815
|
-
import * as
|
|
14834
|
+
import * as p7 from "@clack/prompts";
|
|
14816
14835
|
|
|
14817
14836
|
// src/providers-command.ts
|
|
14818
14837
|
import pc6 from "picocolors";
|
|
14819
|
-
import * as
|
|
14838
|
+
import * as p6 from "@clack/prompts";
|
|
14820
14839
|
|
|
14821
14840
|
// src/registry/import-build.ts
|
|
14822
14841
|
function oauthAuthRef(providerId) {
|
|
@@ -14887,8 +14906,8 @@ async function fetchProviderCatalog(opts) {
|
|
|
14887
14906
|
return loadRegistryProviders(warn, { ...opts, warn });
|
|
14888
14907
|
}
|
|
14889
14908
|
function providersForPicker(providers) {
|
|
14890
|
-
for (const
|
|
14891
|
-
|
|
14909
|
+
for (const p14 of providers) {
|
|
14910
|
+
p14.models.sort((a, b) => {
|
|
14892
14911
|
const nameA = a.name || a.id;
|
|
14893
14912
|
const nameB = b.name || b.id;
|
|
14894
14913
|
return nameA.localeCompare(nameB, void 0, { sensitivity: "base", numeric: true });
|
|
@@ -14904,7 +14923,7 @@ async function resolveLocalProviderApiKey(provider) {
|
|
|
14904
14923
|
if (template?.apiKeyOptional || template?.anonymousFreeModels) {
|
|
14905
14924
|
return "";
|
|
14906
14925
|
}
|
|
14907
|
-
const reg = loadRegistry().providers.find((
|
|
14926
|
+
const reg = loadRegistry().providers.find((p14) => p14.id === provider.id);
|
|
14908
14927
|
const authRef = provider.authRef ?? reg?.authRef ?? oauthAuthRef(provider.id);
|
|
14909
14928
|
return resolveProviderCredential(provider.id, authRef);
|
|
14910
14929
|
}
|
|
@@ -15545,7 +15564,7 @@ async function addProviderFromTemplate(template, apiKey, opts) {
|
|
|
15545
15564
|
}
|
|
15546
15565
|
};
|
|
15547
15566
|
if (existing) {
|
|
15548
|
-
const idx = registry.providers.findIndex((
|
|
15567
|
+
const idx = registry.providers.findIndex((p14) => p14.id === template.id);
|
|
15549
15568
|
registry.providers[idx] = entry;
|
|
15550
15569
|
} else {
|
|
15551
15570
|
registry.providers.push(entry);
|
|
@@ -15593,127 +15612,8 @@ async function addProviderFromTemplate(template, apiKey, opts) {
|
|
|
15593
15612
|
return result;
|
|
15594
15613
|
}
|
|
15595
15614
|
|
|
15596
|
-
// src/
|
|
15597
|
-
|
|
15598
|
-
const removal = await withRegistryWriteLock(async () => {
|
|
15599
|
-
const registry = loadRegistryStrict();
|
|
15600
|
-
const index = registry.providers.findIndex((p13) => p13.id === id);
|
|
15601
|
-
if (index < 0) {
|
|
15602
|
-
return {
|
|
15603
|
-
result: {
|
|
15604
|
-
removed: false,
|
|
15605
|
-
id,
|
|
15606
|
-
credentialDeleted: false,
|
|
15607
|
-
error: `Provider not found: ${id}`
|
|
15608
|
-
},
|
|
15609
|
-
queuedRefs: []
|
|
15610
|
-
};
|
|
15611
|
-
}
|
|
15612
|
-
const [removedProvider] = registry.providers.splice(index, 1);
|
|
15613
|
-
const queuedRefs = [];
|
|
15614
|
-
if (opts?.deleteCredential !== false) {
|
|
15615
|
-
const credentialRefs = /* @__PURE__ */ new Set([
|
|
15616
|
-
removedProvider.authRef,
|
|
15617
|
-
...removedProvider.defaultAuthRef ? [removedProvider.defaultAuthRef] : [],
|
|
15618
|
-
...Object.values(removedProvider.authAccounts ?? {}).map((slot) => slot.authRef)
|
|
15619
|
-
]);
|
|
15620
|
-
for (const authRef of credentialRefs) {
|
|
15621
|
-
if (await queueCredentialDelete(authRef)) queuedRefs.push(authRef);
|
|
15622
|
-
}
|
|
15623
|
-
}
|
|
15624
|
-
saveRegistry(registry);
|
|
15625
|
-
return {
|
|
15626
|
-
result: {
|
|
15627
|
-
removed: true,
|
|
15628
|
-
id,
|
|
15629
|
-
name: removedProvider.name,
|
|
15630
|
-
credentialDeleted: false
|
|
15631
|
-
},
|
|
15632
|
-
queuedRefs
|
|
15633
|
-
};
|
|
15634
|
-
});
|
|
15635
|
-
if (removal.queuedRefs.length > 0) {
|
|
15636
|
-
try {
|
|
15637
|
-
const cleanup = await reconcilePendingCredentialDeletes();
|
|
15638
|
-
removal.result.credentialDeleted = removal.queuedRefs.every((ref) => cleanup.deleted.includes(ref));
|
|
15639
|
-
removal.result.credentialCleanupPending = removal.queuedRefs.some((ref) => cleanup.pending.includes(ref)) || cleanup.persistenceError !== void 0;
|
|
15640
|
-
} catch {
|
|
15641
|
-
removal.result.credentialCleanupPending = true;
|
|
15642
|
-
}
|
|
15643
|
-
removal.result.credentialCleanupReconciled = true;
|
|
15644
|
-
}
|
|
15645
|
-
return removal.result;
|
|
15646
|
-
}
|
|
15647
|
-
async function removeProviderFromRegistry(id, opts) {
|
|
15648
|
-
return withProviderMutationLock(id, () => removeProviderWithinLifecycle(id, opts));
|
|
15649
|
-
}
|
|
15650
|
-
async function setActiveOAuthAccount(id, account) {
|
|
15651
|
-
return withProviderMutationLock(id, () => withRegistryWriteLock(() => {
|
|
15652
|
-
const registry = loadRegistryStrict();
|
|
15653
|
-
const provider = registry.providers.find((p13) => p13.id === id);
|
|
15654
|
-
if (!provider) return { updated: false, error: `Provider not found: ${id}` };
|
|
15655
|
-
const name = account?.trim().toLowerCase();
|
|
15656
|
-
const previous = provider.activeAuthAccount?.trim() || void 0;
|
|
15657
|
-
const selectedSlot = name ? getOAuthAccountSlot(provider, name) : void 0;
|
|
15658
|
-
let selectionChanged = previous !== name;
|
|
15659
|
-
let storageChanged = false;
|
|
15660
|
-
if (name && !selectedSlot) {
|
|
15661
|
-
const slots = provider.authAccounts ?? {};
|
|
15662
|
-
const available = Object.keys(slots).sort().join(", ") || "none";
|
|
15663
|
-
return {
|
|
15664
|
-
updated: false,
|
|
15665
|
-
error: `${provider.name} has no account named "${name}" (available: ${available}).`
|
|
15666
|
-
};
|
|
15667
|
-
}
|
|
15668
|
-
if (name && provider.authType === "oauth") {
|
|
15669
|
-
storageChanged = storeActiveOAuthAccount(provider, name, selectedSlot.authRef);
|
|
15670
|
-
}
|
|
15671
|
-
if (selectionChanged && provider.authType === "oauth") {
|
|
15672
|
-
const previousSlot = previous ? getOAuthAccountSlot(provider, previous) : void 0;
|
|
15673
|
-
if (previous && previousSlot && provider.modelsCache && provider.authAccounts) {
|
|
15674
|
-
provider.authAccounts[previous] = {
|
|
15675
|
-
...previousSlot,
|
|
15676
|
-
modelsCache: provider.modelsCache
|
|
15677
|
-
};
|
|
15678
|
-
}
|
|
15679
|
-
const selectedCache = selectedSlot?.modelsCache;
|
|
15680
|
-
if (selectedCache) {
|
|
15681
|
-
provider.modelsCache = selectedCache;
|
|
15682
|
-
provider.refreshedAt = selectedCache.fetchedAt;
|
|
15683
|
-
} else {
|
|
15684
|
-
delete provider.modelsCache;
|
|
15685
|
-
delete provider.refreshedAt;
|
|
15686
|
-
}
|
|
15687
|
-
}
|
|
15688
|
-
if (name && provider.authType !== "oauth") {
|
|
15689
|
-
storageChanged = clearActiveOAuthAccount(provider) || storageChanged;
|
|
15690
|
-
if (provider.activeAuthAccount !== name) {
|
|
15691
|
-
provider.activeAuthAccount = name;
|
|
15692
|
-
storageChanged = true;
|
|
15693
|
-
}
|
|
15694
|
-
} else if (!name && (previous !== void 0 || provider.defaultAuthRef !== void 0)) {
|
|
15695
|
-
storageChanged = clearActiveOAuthAccount(provider);
|
|
15696
|
-
} else if (!name) {
|
|
15697
|
-
selectionChanged = false;
|
|
15698
|
-
}
|
|
15699
|
-
const migrationNeedsPersistence = registry.schemaVersion < REGISTRY_SCHEMA_VERSION_WITH_MATERIALIZED_ACTIVE_ACCOUNT && provider.defaultAuthRef !== void 0;
|
|
15700
|
-
if (selectionChanged || storageChanged || migrationNeedsPersistence) saveRegistry(registry);
|
|
15701
|
-
return { updated: true, changed: selectionChanged, ...name ? { account: name } : {}, provider };
|
|
15702
|
-
}));
|
|
15703
|
-
}
|
|
15704
|
-
function toggleProviderEnabled(id) {
|
|
15705
|
-
return withRegistryWriteLockSync(() => {
|
|
15706
|
-
const registry = loadRegistryStrict();
|
|
15707
|
-
const provider = registry.providers.find((p13) => p13.id === id);
|
|
15708
|
-
if (!provider) return { toggled: false, error: `Provider not found: ${id}` };
|
|
15709
|
-
provider.enabled = !provider.enabled;
|
|
15710
|
-
saveRegistry(registry);
|
|
15711
|
-
return { toggled: true, enabled: provider.enabled };
|
|
15712
|
-
});
|
|
15713
|
-
}
|
|
15714
|
-
|
|
15715
|
-
// src/registry/refresh-models.ts
|
|
15716
|
-
import { isDeepStrictEqual } from "util";
|
|
15615
|
+
// src/providers-custom-add.ts
|
|
15616
|
+
import * as p3 from "@clack/prompts";
|
|
15717
15617
|
|
|
15718
15618
|
// src/registry/url-security.ts
|
|
15719
15619
|
import { lookup } from "dns/promises";
|
|
@@ -15828,6 +15728,13 @@ async function validateCustomEndpointUrl(rawUrl, opts = {}) {
|
|
|
15828
15728
|
}
|
|
15829
15729
|
|
|
15830
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
|
+
}
|
|
15831
15738
|
async function fetchAnthropicModels(baseUrl, apiKey, extraHeaders) {
|
|
15832
15739
|
const root = baseUrl.replace(/\/v1\/?$/, "").replace(/\/$/, "");
|
|
15833
15740
|
const modelsUrl2 = `${root}/v1/models`;
|
|
@@ -15867,6 +15774,7 @@ async function fetchAnthropicModels(baseUrl, apiKey, extraHeaders) {
|
|
|
15867
15774
|
for (const row of json.data ?? []) {
|
|
15868
15775
|
const id = row.id?.trim();
|
|
15869
15776
|
if (!id) continue;
|
|
15777
|
+
if (hasControlChars(id) || typeof row.name === "string" && hasControlChars(row.name.trim())) continue;
|
|
15870
15778
|
models.push({
|
|
15871
15779
|
id,
|
|
15872
15780
|
name: row.name?.trim() || id,
|
|
@@ -15901,6 +15809,325 @@ async function fetchAnthropicModels(baseUrl, apiKey, extraHeaders) {
|
|
|
15901
15809
|
clearTimeout(timer);
|
|
15902
15810
|
}
|
|
15903
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";
|
|
15904
16131
|
|
|
15905
16132
|
// src/registry/model-source.ts
|
|
15906
16133
|
var MANUAL_ONLY_TEMPLATE_IDS = /* @__PURE__ */ new Set(["vertex", "bedrock", "azure"]);
|
|
@@ -16253,7 +16480,7 @@ async function refreshApiListProvider(provider, apiKey) {
|
|
|
16253
16480
|
};
|
|
16254
16481
|
}
|
|
16255
16482
|
function updateProviderCache(registry, providerId, models, baseUrl, credentialSnapshot) {
|
|
16256
|
-
const idx = registry.providers.findIndex((
|
|
16483
|
+
const idx = registry.providers.findIndex((p14) => p14.id === providerId);
|
|
16257
16484
|
if (idx < 0) return;
|
|
16258
16485
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
16259
16486
|
const existing = registry.providers[idx];
|
|
@@ -16324,7 +16551,7 @@ function assertRefreshCredentialStillCurrent(current, snapshot) {
|
|
|
16324
16551
|
}
|
|
16325
16552
|
async function refreshProviderModels(providerId, apiKey, registry, credentialSnapshot) {
|
|
16326
16553
|
const workingRegistry = registry ?? loadRegistryStrict();
|
|
16327
|
-
const provider = workingRegistry.providers.find((
|
|
16554
|
+
const provider = workingRegistry.providers.find((p14) => p14.id === providerId);
|
|
16328
16555
|
if (!provider) {
|
|
16329
16556
|
return { id: providerId, name: providerId, ok: false, reason: "Provider not found." };
|
|
16330
16557
|
}
|
|
@@ -16539,7 +16766,7 @@ async function refreshProviderModelsWithCredential(providerId, resolveKey, selec
|
|
|
16539
16766
|
async function refreshAllProviderModels(resolveKey) {
|
|
16540
16767
|
const refreshed = [];
|
|
16541
16768
|
const registry = loadRegistryStrict();
|
|
16542
|
-
const enabledProviders = registry.providers.filter((
|
|
16769
|
+
const enabledProviders = registry.providers.filter((p14) => p14.enabled);
|
|
16543
16770
|
for (const provider of enabledProviders) {
|
|
16544
16771
|
const accountOverride = process.env[OAUTH_ACCOUNT_ENV] ?? null;
|
|
16545
16772
|
try {
|
|
@@ -16563,7 +16790,7 @@ async function refreshAllProviderModels(resolveKey) {
|
|
|
16563
16790
|
|
|
16564
16791
|
// src/registry/provider-auth.ts
|
|
16565
16792
|
import pc4 from "picocolors";
|
|
16566
|
-
import * as
|
|
16793
|
+
import * as p4 from "@clack/prompts";
|
|
16567
16794
|
import open from "open";
|
|
16568
16795
|
function validateOAuthAccountName(name) {
|
|
16569
16796
|
const trimmed = name.trim().toLowerCase();
|
|
@@ -16586,39 +16813,39 @@ function openBrowser(url) {
|
|
|
16586
16813
|
async function runNativeDeviceCode(providerId) {
|
|
16587
16814
|
const label = PROVIDER_DISPLAY[providerId];
|
|
16588
16815
|
printOAuthStepsPanel(`${label} \u2014 Sign in`, label);
|
|
16589
|
-
const
|
|
16590
|
-
|
|
16816
|
+
const spinner6 = p4.spinner();
|
|
16817
|
+
spinner6.start("Waiting for authorization...");
|
|
16591
16818
|
try {
|
|
16592
16819
|
const { tokens, accountId } = await runOpenAiDeviceCodeFlow(({ url, userCode }) => {
|
|
16593
|
-
|
|
16594
|
-
|
|
16595
|
-
|
|
16820
|
+
spinner6.stop("");
|
|
16821
|
+
p4.log.info(`Visit: ${pc4.cyan(url)}`);
|
|
16822
|
+
p4.log.info(`Enter code: ${pc4.bold(userCode)}`);
|
|
16596
16823
|
openBrowser(url);
|
|
16597
|
-
|
|
16824
|
+
spinner6.start("Waiting for authorization...");
|
|
16598
16825
|
});
|
|
16599
|
-
|
|
16826
|
+
spinner6.stop(pc4.green("Signed in to OpenAI ChatGPT"));
|
|
16600
16827
|
return tokensToStoredCredential(tokens, void 0, accountId);
|
|
16601
16828
|
} catch (err) {
|
|
16602
|
-
|
|
16829
|
+
spinner6.stop("");
|
|
16603
16830
|
throw err;
|
|
16604
16831
|
}
|
|
16605
16832
|
}
|
|
16606
16833
|
async function runNativeBrowserSignIn(providerId) {
|
|
16607
16834
|
const label = PROVIDER_DISPLAY[providerId];
|
|
16608
16835
|
printOAuthBrowserPanel(`${label} \u2014 Sign in`, label);
|
|
16609
|
-
const
|
|
16610
|
-
|
|
16836
|
+
const spinner6 = p4.spinner();
|
|
16837
|
+
spinner6.start("Opening your browser...");
|
|
16611
16838
|
try {
|
|
16612
16839
|
const { tokens, accountId } = await runOpenAiBrowserFlow(({ url }) => {
|
|
16613
|
-
|
|
16614
|
-
|
|
16840
|
+
spinner6.stop("");
|
|
16841
|
+
p4.log.info(`If the browser did not open, visit: ${pc4.cyan(url)}`);
|
|
16615
16842
|
openBrowser(url);
|
|
16616
|
-
|
|
16843
|
+
spinner6.start("Waiting for sign-in in your browser...");
|
|
16617
16844
|
});
|
|
16618
|
-
|
|
16845
|
+
spinner6.stop(pc4.green("Signed in to OpenAI ChatGPT"));
|
|
16619
16846
|
return tokensToStoredCredential(tokens, void 0, accountId);
|
|
16620
16847
|
} catch (err) {
|
|
16621
|
-
|
|
16848
|
+
spinner6.stop("");
|
|
16622
16849
|
throw err;
|
|
16623
16850
|
}
|
|
16624
16851
|
}
|
|
@@ -16827,7 +17054,7 @@ async function authenticateProvider(providerId, options = {}) {
|
|
|
16827
17054
|
}
|
|
16828
17055
|
const cred = options.method === "browser" ? await runNativeBrowserSignIn(providerId) : await runNativeDeviceCode(providerId);
|
|
16829
17056
|
const persisted = await persistNativeOAuthCredential(providerId, cred, accountName);
|
|
16830
|
-
const refreshSpinner =
|
|
17057
|
+
const refreshSpinner = p4.spinner();
|
|
16831
17058
|
refreshSpinner.start("Refreshing model list...");
|
|
16832
17059
|
try {
|
|
16833
17060
|
const accountOverride = accountName ?? (persisted.registryProvider.activeAuthAccount === void 0 ? null : process.env[OAUTH_ACCOUNT_ENV] ?? null);
|
|
@@ -16882,7 +17109,7 @@ ${pc4.bold("Named accounts:")}
|
|
|
16882
17109
|
}
|
|
16883
17110
|
|
|
16884
17111
|
// src/prompts.ts
|
|
16885
|
-
import * as
|
|
17112
|
+
import * as p5 from "@clack/prompts";
|
|
16886
17113
|
import pc5 from "picocolors";
|
|
16887
17114
|
|
|
16888
17115
|
// src/model-search.ts
|
|
@@ -16991,12 +17218,12 @@ async function pickModelFromPagedList(list, toOption, messagePrefix, initialMode
|
|
|
16991
17218
|
options.push(navOption(SWITCH_SEARCH, "\u2190 New search", ""));
|
|
16992
17219
|
}
|
|
16993
17220
|
const initialValue = (initialModelId && pageItems.some((m) => m.id === initialModelId) ? initialModelId : pageItems[0]?.id) ?? options[0]?.value;
|
|
16994
|
-
const picked = await
|
|
17221
|
+
const picked = await p5.select({
|
|
16995
17222
|
message: `${messagePrefix} (page ${currentPage + 1} of ${totalPages})`,
|
|
16996
17223
|
options,
|
|
16997
17224
|
initialValue
|
|
16998
17225
|
});
|
|
16999
|
-
if (
|
|
17226
|
+
if (p5.isCancel(picked)) return "menu";
|
|
17000
17227
|
const choice = String(picked);
|
|
17001
17228
|
if (choice === PAGE_PREV) {
|
|
17002
17229
|
page = currentPage - 1;
|
|
@@ -17017,7 +17244,7 @@ async function selectLargeCatalog(models, browseList, toOption, message, initial
|
|
|
17017
17244
|
let mode = "choose";
|
|
17018
17245
|
while (true) {
|
|
17019
17246
|
if (mode === "choose") {
|
|
17020
|
-
const method = await
|
|
17247
|
+
const method = await p5.select({
|
|
17021
17248
|
message: `${message} (${models.length} available)`,
|
|
17022
17249
|
options: [
|
|
17023
17250
|
{ value: MODE_SEARCH, label: pc5.cyan("Search models"), hint: "Filter by name, id, or brand" },
|
|
@@ -17029,7 +17256,7 @@ async function selectLargeCatalog(models, browseList, toOption, message, initial
|
|
|
17029
17256
|
navOption("__back__", "\u2190 Go back", "Select a different provider")
|
|
17030
17257
|
]
|
|
17031
17258
|
});
|
|
17032
|
-
if (
|
|
17259
|
+
if (p5.isCancel(method) || String(method) === "__back__") {
|
|
17033
17260
|
return "back";
|
|
17034
17261
|
}
|
|
17035
17262
|
mode = method === MODE_BROWSE ? "browse" : "search";
|
|
@@ -17054,17 +17281,17 @@ async function selectLargeCatalog(models, browseList, toOption, message, initial
|
|
|
17054
17281
|
if (isSelectedModel(picked)) return picked;
|
|
17055
17282
|
continue;
|
|
17056
17283
|
}
|
|
17057
|
-
const searchInput = await
|
|
17284
|
+
const searchInput = await p5.text({
|
|
17058
17285
|
message: `Search models (${models.length} available):`,
|
|
17059
17286
|
placeholder: "e.g. claude, sonnet, llama"
|
|
17060
17287
|
});
|
|
17061
|
-
if (
|
|
17288
|
+
if (p5.isCancel(searchInput)) {
|
|
17062
17289
|
mode = "choose";
|
|
17063
17290
|
continue;
|
|
17064
17291
|
}
|
|
17065
17292
|
const matched = filterModelsBySearch(browseList, String(searchInput));
|
|
17066
17293
|
if (matched.length === 0) {
|
|
17067
|
-
|
|
17294
|
+
p5.log.warn("No models match \u2014 try a different search");
|
|
17068
17295
|
continue;
|
|
17069
17296
|
}
|
|
17070
17297
|
const result = await pickModelFromPagedList(
|
|
@@ -17095,12 +17322,12 @@ async function selectModelWithSearch(models, toOption, message, initialModelId,
|
|
|
17095
17322
|
navOption("__back__", "\u2190 Go back", "")
|
|
17096
17323
|
];
|
|
17097
17324
|
const initialValue = initialModelId && options.some((o) => o.value === initialModelId) ? initialModelId : options[0]?.value;
|
|
17098
|
-
const picked = await
|
|
17325
|
+
const picked = await p5.select({
|
|
17099
17326
|
message,
|
|
17100
17327
|
options,
|
|
17101
17328
|
initialValue
|
|
17102
17329
|
});
|
|
17103
|
-
if (
|
|
17330
|
+
if (p5.isCancel(picked) || String(picked) === "__back__") {
|
|
17104
17331
|
return "back";
|
|
17105
17332
|
}
|
|
17106
17333
|
const selected = models.find((m) => m.id === String(picked));
|
|
@@ -17134,12 +17361,12 @@ async function pickLocalModel(provider, conflicts, prefs) {
|
|
|
17134
17361
|
navOption(BROWSE_ALL, "Browse all models \u2192", `${provider.models.length} available`),
|
|
17135
17362
|
navOption("__back__", "\u2190 Go back", "Select a different provider")
|
|
17136
17363
|
];
|
|
17137
|
-
const picked = await
|
|
17364
|
+
const picked = await p5.select({
|
|
17138
17365
|
message: "Which model?",
|
|
17139
17366
|
options,
|
|
17140
17367
|
initialValue: recentModels[0].id
|
|
17141
17368
|
});
|
|
17142
|
-
if (
|
|
17369
|
+
if (p5.isCancel(picked) || String(picked) === "__back__") {
|
|
17143
17370
|
return "back";
|
|
17144
17371
|
}
|
|
17145
17372
|
if (String(picked) === BROWSE_ALL) {
|
|
@@ -17166,12 +17393,12 @@ async function pickLocalModel(provider, conflicts, prefs) {
|
|
|
17166
17393
|
}
|
|
17167
17394
|
noteEnvConflicts(conflicts);
|
|
17168
17395
|
const modelLabel = formatModelLabel(selectedModel);
|
|
17169
|
-
const confirmed = await
|
|
17396
|
+
const confirmed = await p5.confirm({
|
|
17170
17397
|
message: confirmLaunchMessage("Claude Code", modelLabel, selectedModel.id, provider.name),
|
|
17171
17398
|
initialValue: true
|
|
17172
17399
|
});
|
|
17173
|
-
if (
|
|
17174
|
-
|
|
17400
|
+
if (p5.isCancel(confirmed) || !confirmed) {
|
|
17401
|
+
p5.cancel("Cancelled.");
|
|
17175
17402
|
return null;
|
|
17176
17403
|
}
|
|
17177
17404
|
relayOutro("Launching", fmtModel(modelLabel, selectedModel.id));
|
|
@@ -17187,7 +17414,7 @@ function reportCredentialCleanup(pending, state, reconciled = false) {
|
|
|
17187
17414
|
return;
|
|
17188
17415
|
}
|
|
17189
17416
|
if (pending) {
|
|
17190
|
-
|
|
17417
|
+
p6.log.warn(CREDENTIAL_CLEANUP_PENDING_MESSAGE);
|
|
17191
17418
|
}
|
|
17192
17419
|
}
|
|
17193
17420
|
async function reconcileCredentialCleanup() {
|
|
@@ -17277,7 +17504,7 @@ ${pc6.bold("Usage:")}
|
|
|
17277
17504
|
|
|
17278
17505
|
${pc6.bold("Subcommands:")}
|
|
17279
17506
|
(none) Provider hub wizard
|
|
17280
|
-
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
|
|
17281
17508
|
auth Sign in with ChatGPT/Codex-plan OAuth (device code, or --browser)
|
|
17282
17509
|
list Show configured providers
|
|
17283
17510
|
remove Remove a provider by id
|
|
@@ -17404,7 +17631,7 @@ function providerLabel(name, modelCount, enabled) {
|
|
|
17404
17631
|
return `${fmtEnabledStar(enabled)} ${fmtProvider(name)} ${pc6.dim(`(${modelCount} model${modelCount === 1 ? "" : "s"})`)}`;
|
|
17405
17632
|
}
|
|
17406
17633
|
async function promptOAuthMethod() {
|
|
17407
|
-
const choice = await
|
|
17634
|
+
const choice = await p6.select({
|
|
17408
17635
|
message: "How do you want to sign in?",
|
|
17409
17636
|
initialValue: "native",
|
|
17410
17637
|
options: [
|
|
@@ -17412,8 +17639,8 @@ async function promptOAuthMethod() {
|
|
|
17412
17639
|
{ value: "browser", label: "Browser", hint: "for workspaces that disable device code authorization" }
|
|
17413
17640
|
]
|
|
17414
17641
|
});
|
|
17415
|
-
if (
|
|
17416
|
-
|
|
17642
|
+
if (p6.isCancel(choice)) {
|
|
17643
|
+
p6.cancel("Cancelled.");
|
|
17417
17644
|
return null;
|
|
17418
17645
|
}
|
|
17419
17646
|
return choice;
|
|
@@ -17422,17 +17649,17 @@ async function runProvidersAuthWithCleanupState(providerId, method, cleanupState
|
|
|
17422
17649
|
try {
|
|
17423
17650
|
const result = await authenticateProvider(providerId, { method, account });
|
|
17424
17651
|
const slot = account === void 0 ? void 0 : validateOAuthAccountName(account);
|
|
17425
|
-
|
|
17652
|
+
p6.log.success(
|
|
17426
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.`
|
|
17427
17654
|
);
|
|
17428
17655
|
reportCredentialCleanup(result.credentialCleanupPending, cleanupState, true);
|
|
17429
17656
|
return 0;
|
|
17430
17657
|
} catch (err) {
|
|
17431
17658
|
if (err instanceof Error && err.message === "Cancelled") {
|
|
17432
|
-
|
|
17659
|
+
p6.cancel("Cancelled.");
|
|
17433
17660
|
return 0;
|
|
17434
17661
|
}
|
|
17435
|
-
|
|
17662
|
+
p6.log.error(err instanceof Error ? err.message : String(err));
|
|
17436
17663
|
return 1;
|
|
17437
17664
|
}
|
|
17438
17665
|
}
|
|
@@ -17443,13 +17670,13 @@ async function runProvidersRefreshModels(providerId, options = {}) {
|
|
|
17443
17670
|
const resolveKey = async (provider) => resolveProviderCredentialWithSource(provider.id, provider.authRef);
|
|
17444
17671
|
if (providerId) {
|
|
17445
17672
|
const registry = loadRegistry();
|
|
17446
|
-
const provider = registry.providers.find((
|
|
17673
|
+
const provider = registry.providers.find((p14) => p14.id === providerId);
|
|
17447
17674
|
if (!provider) {
|
|
17448
|
-
|
|
17675
|
+
p6.log.error(`Provider not found: ${providerId}`);
|
|
17449
17676
|
return 1;
|
|
17450
17677
|
}
|
|
17451
|
-
const
|
|
17452
|
-
|
|
17678
|
+
const spinner7 = p6.spinner();
|
|
17679
|
+
spinner7.start(`Refreshing ${provider.name}...`);
|
|
17453
17680
|
const accountOverride = options.accountOverride === void 0 ? process.env[OAUTH_ACCOUNT_ENV] ?? null : options.accountOverride;
|
|
17454
17681
|
let result;
|
|
17455
17682
|
try {
|
|
@@ -17465,59 +17692,59 @@ async function runProvidersRefreshModels(providerId, options = {}) {
|
|
|
17465
17692
|
{ ignoreProviderOverride: options.ignoreProviderCredentialOverride }
|
|
17466
17693
|
);
|
|
17467
17694
|
} catch (err) {
|
|
17468
|
-
|
|
17469
|
-
|
|
17695
|
+
spinner7.stop("");
|
|
17696
|
+
p6.log.error(err instanceof Error ? err.message : String(err));
|
|
17470
17697
|
return 1;
|
|
17471
17698
|
}
|
|
17472
|
-
|
|
17699
|
+
spinner7.stop("");
|
|
17473
17700
|
if (result.skipped) {
|
|
17474
17701
|
const countNote = result.modelCount ? ` (${result.modelCount} cached models kept)` : "";
|
|
17475
|
-
|
|
17702
|
+
p6.log.warn(`${result.name}: ${result.reason}${countNote}`);
|
|
17476
17703
|
return 0;
|
|
17477
17704
|
}
|
|
17478
17705
|
if (!result.ok) {
|
|
17479
|
-
|
|
17706
|
+
p6.log.error(`${result.name}: ${result.reason ?? "Refresh failed."}`);
|
|
17480
17707
|
return 1;
|
|
17481
17708
|
}
|
|
17482
17709
|
const diff = result.previousModelCount === void 0 ? 0 : (result.modelCount ?? 0) - result.previousModelCount;
|
|
17483
17710
|
const diffStr = result.previousModelCount === void 0 ? "" : diff > 0 ? ` (+${diff})` : diff < 0 ? ` (${diff})` : "";
|
|
17484
|
-
|
|
17711
|
+
p6.log.success(`${result.name}: ${result.modelCount} model${result.modelCount === 1 ? "" : "s"} updated${diffStr}.`);
|
|
17485
17712
|
if (result.reason) {
|
|
17486
|
-
|
|
17713
|
+
p6.log.warn(result.reason);
|
|
17487
17714
|
}
|
|
17488
17715
|
return 0;
|
|
17489
17716
|
}
|
|
17490
|
-
const
|
|
17491
|
-
|
|
17717
|
+
const spinner6 = p6.spinner();
|
|
17718
|
+
spinner6.start("Refreshing model lists...");
|
|
17492
17719
|
const { refreshed } = await refreshAllProviderModels(resolveKey);
|
|
17493
|
-
|
|
17720
|
+
spinner6.stop("");
|
|
17494
17721
|
const ok = refreshed.filter((r) => r.ok && !r.skipped);
|
|
17495
17722
|
const skipped = refreshed.filter((r) => r.skipped);
|
|
17496
17723
|
const failed = refreshed.filter((r) => !r.ok);
|
|
17497
17724
|
if (ok.length > 0) {
|
|
17498
|
-
|
|
17725
|
+
p6.log.success(`Updated ${ok.length} provider${ok.length === 1 ? "" : "s"}.`);
|
|
17499
17726
|
for (const r of ok) {
|
|
17500
17727
|
const diff = r.previousModelCount === void 0 ? 0 : (r.modelCount ?? 0) - r.previousModelCount;
|
|
17501
17728
|
const diffStr = r.previousModelCount === void 0 ? "" : diff > 0 ? ` (+${diff})` : diff < 0 ? ` (${diff})` : "";
|
|
17502
|
-
|
|
17729
|
+
p6.log.info(` ${r.name}: ${r.modelCount} model${r.modelCount === 1 ? "" : "s"}${diffStr}`);
|
|
17503
17730
|
if (r.reason) {
|
|
17504
|
-
|
|
17731
|
+
p6.log.warn(` ${r.reason}`);
|
|
17505
17732
|
}
|
|
17506
17733
|
}
|
|
17507
17734
|
}
|
|
17508
17735
|
for (const r of skipped) {
|
|
17509
17736
|
const countNote = r.modelCount ? ` (${r.modelCount} cached models kept)` : "";
|
|
17510
|
-
|
|
17737
|
+
p6.log.warn(`Skipped ${r.name}: ${r.reason}${countNote}`);
|
|
17511
17738
|
}
|
|
17512
17739
|
for (const r of failed) {
|
|
17513
|
-
|
|
17740
|
+
p6.log.error(`${r.name}: ${r.reason ?? "Refresh failed."}`);
|
|
17514
17741
|
}
|
|
17515
17742
|
return failed.length > 0 ? 1 : 0;
|
|
17516
17743
|
}
|
|
17517
17744
|
async function runProvidersList() {
|
|
17518
17745
|
const entries = await resolveProvidersForDisplay();
|
|
17519
17746
|
if (entries.length === 0) {
|
|
17520
|
-
|
|
17747
|
+
p6.log.info("No providers configured. Run clodex providers add or clodex providers auth openai.");
|
|
17521
17748
|
return 0;
|
|
17522
17749
|
}
|
|
17523
17750
|
console.log("");
|
|
@@ -17538,7 +17765,7 @@ async function runTemplateAddFlow(templateId, cleanupState) {
|
|
|
17538
17765
|
const template = listRegistryAddableTemplates(registry.providers).find((candidate) => candidate.id === templateId);
|
|
17539
17766
|
if (!template) {
|
|
17540
17767
|
const known = getTemplateById(templateId);
|
|
17541
|
-
|
|
17768
|
+
p6.log.info(known ? `${known.name} is already configured.` : `Unknown provider template: ${templateId}`);
|
|
17542
17769
|
return known ? 0 : 1;
|
|
17543
17770
|
}
|
|
17544
17771
|
if (template.signupUrl) {
|
|
@@ -17546,27 +17773,27 @@ async function runTemplateAddFlow(templateId, cleanupState) {
|
|
|
17546
17773
|
`${pc6.white("Get an API key at:")} ${fmtUrl(template.signupUrl)}`
|
|
17547
17774
|
]);
|
|
17548
17775
|
}
|
|
17549
|
-
const apiKeyInput = await
|
|
17776
|
+
const apiKeyInput = await p6.password({
|
|
17550
17777
|
message: `Paste your ${template.name} API key:`,
|
|
17551
17778
|
validate: (val) => val.trim() ? void 0 : "Key cannot be empty"
|
|
17552
17779
|
});
|
|
17553
|
-
if (
|
|
17554
|
-
|
|
17780
|
+
if (p6.isCancel(apiKeyInput)) {
|
|
17781
|
+
p6.cancel("Cancelled.");
|
|
17555
17782
|
return 0;
|
|
17556
17783
|
}
|
|
17557
17784
|
const apiKey = String(apiKeyInput).trim();
|
|
17558
|
-
const
|
|
17559
|
-
|
|
17785
|
+
const spinner6 = p6.spinner();
|
|
17786
|
+
spinner6.start(`Testing connection to ${template.name}...`);
|
|
17560
17787
|
const result = await addProviderFromTemplate(template, apiKey);
|
|
17561
|
-
|
|
17788
|
+
spinner6.stop("");
|
|
17562
17789
|
reportCredentialCleanup(
|
|
17563
17790
|
result.credentialCleanupPending === true,
|
|
17564
17791
|
cleanupState,
|
|
17565
17792
|
result.credentialCleanupReconciled === true
|
|
17566
17793
|
);
|
|
17567
17794
|
if (!result.added) {
|
|
17568
|
-
|
|
17569
|
-
if (result.hint)
|
|
17795
|
+
p6.log.error(result.error ?? "Could not add provider.");
|
|
17796
|
+
if (result.hint) p6.log.info(result.hint);
|
|
17570
17797
|
return 1;
|
|
17571
17798
|
}
|
|
17572
17799
|
logConnected(template.name, result.modelCount ?? 0);
|
|
@@ -17590,16 +17817,17 @@ async function runProvidersAddWithCleanupState(cleanupState) {
|
|
|
17590
17817
|
hint: template.id === "openai" ? "platform.openai.com key (usage-billed)" : "provider API key (usage-billed)"
|
|
17591
17818
|
});
|
|
17592
17819
|
}
|
|
17593
|
-
|
|
17594
|
-
|
|
17595
|
-
|
|
17596
|
-
|
|
17597
|
-
|
|
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({
|
|
17598
17826
|
message: "Add a provider",
|
|
17599
17827
|
options
|
|
17600
17828
|
});
|
|
17601
|
-
if (
|
|
17602
|
-
|
|
17829
|
+
if (p6.isCancel(choice)) {
|
|
17830
|
+
p6.cancel("Cancelled.");
|
|
17603
17831
|
return 0;
|
|
17604
17832
|
}
|
|
17605
17833
|
if (choice === "oauth") {
|
|
@@ -17607,6 +17835,13 @@ async function runProvidersAddWithCleanupState(cleanupState) {
|
|
|
17607
17835
|
if (method === null) return 0;
|
|
17608
17836
|
return runProvidersAuthWithCleanupState("openai", method, cleanupState);
|
|
17609
17837
|
}
|
|
17838
|
+
if (choice === "custom") {
|
|
17839
|
+
return runCustomEndpointAddFlow((result) => reportCredentialCleanup(
|
|
17840
|
+
result.credentialCleanupPending === true,
|
|
17841
|
+
cleanupState,
|
|
17842
|
+
result.credentialCleanupReconciled === true
|
|
17843
|
+
));
|
|
17844
|
+
}
|
|
17610
17845
|
if (typeof choice === "string" && choice.startsWith("api:")) {
|
|
17611
17846
|
return runTemplateAddFlow(choice.slice("api:".length), cleanupState);
|
|
17612
17847
|
}
|
|
@@ -17619,16 +17854,16 @@ async function runProvidersRemoveWithCleanupState(id, interactive = false, clean
|
|
|
17619
17854
|
const registry = loadRegistry();
|
|
17620
17855
|
const provider = registry.providers.find((pr) => pr.id === id);
|
|
17621
17856
|
if (!provider) {
|
|
17622
|
-
|
|
17857
|
+
p6.log.error(`Provider not found: ${id}`);
|
|
17623
17858
|
return 1;
|
|
17624
17859
|
}
|
|
17625
17860
|
if (interactive) {
|
|
17626
|
-
const
|
|
17861
|
+
const confirm7 = await p6.confirm({
|
|
17627
17862
|
message: `Remove ${provider.name} (${id})?`,
|
|
17628
17863
|
initialValue: false
|
|
17629
17864
|
});
|
|
17630
|
-
if (
|
|
17631
|
-
|
|
17865
|
+
if (p6.isCancel(confirm7) || !confirm7) {
|
|
17866
|
+
p6.cancel("Cancelled.");
|
|
17632
17867
|
return 0;
|
|
17633
17868
|
}
|
|
17634
17869
|
}
|
|
@@ -17639,16 +17874,16 @@ async function runProvidersRemoveWithCleanupState(id, interactive = false, clean
|
|
|
17639
17874
|
result.credentialCleanupReconciled === true
|
|
17640
17875
|
);
|
|
17641
17876
|
if (!result.removed) {
|
|
17642
|
-
|
|
17877
|
+
p6.log.error(result.error ?? `Could not remove ${id}`);
|
|
17643
17878
|
return 1;
|
|
17644
17879
|
}
|
|
17645
17880
|
if (result.error) {
|
|
17646
|
-
|
|
17881
|
+
p6.log.error(result.error);
|
|
17647
17882
|
return 1;
|
|
17648
17883
|
}
|
|
17649
|
-
|
|
17884
|
+
p6.log.success(`Removed ${result.name ?? id}.`);
|
|
17650
17885
|
if (result.credentialDeleted) {
|
|
17651
|
-
|
|
17886
|
+
p6.log.info("Provider API key removed from the credential store.");
|
|
17652
17887
|
}
|
|
17653
17888
|
return 0;
|
|
17654
17889
|
}
|
|
@@ -17723,11 +17958,11 @@ async function runProviderDetail(id) {
|
|
|
17723
17958
|
{ value: "remove", label: "Remove provider", hint: "Delete from registry and credential store when safe" },
|
|
17724
17959
|
{ value: "back", label: "Back", hint: "" }
|
|
17725
17960
|
);
|
|
17726
|
-
const action = await
|
|
17961
|
+
const action = await p6.select({
|
|
17727
17962
|
message: "What would you like to do?",
|
|
17728
17963
|
options: detailOptions
|
|
17729
17964
|
});
|
|
17730
|
-
if (
|
|
17965
|
+
if (p6.isCancel(action) || action === "back") return "back";
|
|
17731
17966
|
if (action === "browse") {
|
|
17732
17967
|
const cachedModels = projectProviderCachedModels(modelProvider);
|
|
17733
17968
|
const localModels = cachedModels.map((m) => cachedModelToLocal(m, modelProvider)).filter((m) => m !== null);
|
|
@@ -17754,7 +17989,7 @@ async function runProviderDetail(id) {
|
|
|
17754
17989
|
const providerDefault = "<default>";
|
|
17755
17990
|
const stored = provider.activeAuthAccount;
|
|
17756
17991
|
const current = stored !== void 0 && accountSlots.includes(stored) ? stored : providerDefault;
|
|
17757
|
-
const chosen = await
|
|
17992
|
+
const chosen = await p6.select({
|
|
17758
17993
|
message: "Which account should every launch use?",
|
|
17759
17994
|
initialValue: current,
|
|
17760
17995
|
options: [
|
|
@@ -17770,15 +18005,15 @@ async function runProviderDetail(id) {
|
|
|
17770
18005
|
}))
|
|
17771
18006
|
]
|
|
17772
18007
|
});
|
|
17773
|
-
if (
|
|
18008
|
+
if (p6.isCancel(chosen)) return "back";
|
|
17774
18009
|
return withProviderMutationLock(id, async () => {
|
|
17775
18010
|
const result = await setActiveOAuthAccount(id, chosen === providerDefault ? void 0 : chosen);
|
|
17776
18011
|
if (!result.updated) {
|
|
17777
|
-
|
|
18012
|
+
p6.log.error(result.error ?? "Could not switch account.");
|
|
17778
18013
|
return "back";
|
|
17779
18014
|
}
|
|
17780
18015
|
if (!result.provider) {
|
|
17781
|
-
|
|
18016
|
+
p6.log.error("Account selection was saved, but the resulting provider state could not be read.");
|
|
17782
18017
|
return "back";
|
|
17783
18018
|
}
|
|
17784
18019
|
const refreshExitCode = await runProvidersRefreshModels(id, {
|
|
@@ -17787,7 +18022,7 @@ async function runProviderDetail(id) {
|
|
|
17787
18022
|
});
|
|
17788
18023
|
const currentProvider = loadRegistry().providers.find((candidate) => candidate.id === id);
|
|
17789
18024
|
if (!currentProvider) {
|
|
17790
|
-
|
|
18025
|
+
p6.log.error("Account selection was saved, but the resulting provider state could not be read.");
|
|
17791
18026
|
return "back";
|
|
17792
18027
|
}
|
|
17793
18028
|
const outcome = accountSwitchOutcome(
|
|
@@ -17799,26 +18034,26 @@ async function runProviderDetail(id) {
|
|
|
17799
18034
|
if (outcome.ok && currentProvider.enabled && currentProvider.authType === "oauth" && (refreshExitCode !== 0 || !selectedCatalogReady)) {
|
|
17800
18035
|
const savedLabel = result.account ?? PROVIDER_DEFAULT_ACCOUNT_LABEL;
|
|
17801
18036
|
const savedContext = outcome.confirmsLaunch ? `Saved ${savedLabel} for ${currentProvider.name}.` : outcome.message;
|
|
17802
|
-
|
|
18037
|
+
p6.log.warn(
|
|
17803
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.`
|
|
17804
18039
|
);
|
|
17805
18040
|
} else if (outcome.ok) {
|
|
17806
|
-
|
|
18041
|
+
p6.log.success(outcome.message);
|
|
17807
18042
|
} else {
|
|
17808
|
-
|
|
18043
|
+
p6.log.warn(outcome.message);
|
|
17809
18044
|
}
|
|
17810
18045
|
const restartWarning = accountSwitchServerRestartWarning(
|
|
17811
18046
|
readLiveServerRuntimeStates().length,
|
|
17812
18047
|
result.changed
|
|
17813
18048
|
);
|
|
17814
|
-
if (restartWarning)
|
|
18049
|
+
if (restartWarning) p6.log.warn(restartWarning);
|
|
17815
18050
|
return "back";
|
|
17816
18051
|
});
|
|
17817
18052
|
}
|
|
17818
18053
|
if (action === "toggle") {
|
|
17819
18054
|
const result = toggleProviderEnabled(id);
|
|
17820
18055
|
if (result.toggled) {
|
|
17821
|
-
|
|
18056
|
+
p6.log.success(`${provider.name} ${result.enabled ? "enabled" : "disabled"}.`);
|
|
17822
18057
|
}
|
|
17823
18058
|
return "back";
|
|
17824
18059
|
}
|
|
@@ -17854,11 +18089,11 @@ async function runProvidersHub() {
|
|
|
17854
18089
|
options.push({ value: "refresh-all", label: "\u21BA Refresh all models", hint: "Update model lists for all providers" });
|
|
17855
18090
|
}
|
|
17856
18091
|
options.push({ value: "done", label: "Done", hint: "" });
|
|
17857
|
-
const choice = await
|
|
18092
|
+
const choice = await p6.select({
|
|
17858
18093
|
message: entries.length > 0 ? "Your providers" : "Get started",
|
|
17859
18094
|
options
|
|
17860
18095
|
});
|
|
17861
|
-
if (
|
|
18096
|
+
if (p6.isCancel(choice) || choice === "done") {
|
|
17862
18097
|
return 0;
|
|
17863
18098
|
}
|
|
17864
18099
|
if (choice === "add") {
|
|
@@ -17876,7 +18111,7 @@ async function runProvidersHub() {
|
|
|
17876
18111
|
continue;
|
|
17877
18112
|
}
|
|
17878
18113
|
if (choice === "auth-account") {
|
|
17879
|
-
const name = await
|
|
18114
|
+
const name = await p6.text({
|
|
17880
18115
|
message: "Name for this account (choose which one launches with: clodex providers)",
|
|
17881
18116
|
placeholder: "work",
|
|
17882
18117
|
validate: (value) => {
|
|
@@ -17888,7 +18123,7 @@ async function runProvidersHub() {
|
|
|
17888
18123
|
}
|
|
17889
18124
|
}
|
|
17890
18125
|
});
|
|
17891
|
-
if (
|
|
18126
|
+
if (p6.isCancel(name)) continue;
|
|
17892
18127
|
const accountMethod = await promptOAuthMethod();
|
|
17893
18128
|
if (accountMethod === null) continue;
|
|
17894
18129
|
await runWithCredentialCleanup((state) => runProvidersAuthWithCleanupState("openai", accountMethod, state, String(name)));
|
|
@@ -17904,7 +18139,7 @@ async function runProvidersHub() {
|
|
|
17904
18139
|
async function runProvidersCommand(args) {
|
|
17905
18140
|
const parsed = parseProvidersArgs(args);
|
|
17906
18141
|
if (parsed.error) {
|
|
17907
|
-
|
|
18142
|
+
p6.log.error(parsed.error);
|
|
17908
18143
|
return 1;
|
|
17909
18144
|
}
|
|
17910
18145
|
if (parsed.showHelp && parsed.subcommand !== "auth") {
|
|
@@ -17941,7 +18176,7 @@ async function needsFirstRunSetup() {
|
|
|
17941
18176
|
}
|
|
17942
18177
|
async function runFirstRunWizard(_trace = false) {
|
|
17943
18178
|
printWelcomePanel();
|
|
17944
|
-
const choice = await
|
|
18179
|
+
const choice = await p7.select({
|
|
17945
18180
|
message: "How do you want to connect to OpenAI?",
|
|
17946
18181
|
options: [
|
|
17947
18182
|
{
|
|
@@ -17956,8 +18191,8 @@ async function runFirstRunWizard(_trace = false) {
|
|
|
17956
18191
|
}
|
|
17957
18192
|
]
|
|
17958
18193
|
});
|
|
17959
|
-
if (
|
|
17960
|
-
|
|
18194
|
+
if (p7.isCancel(choice)) {
|
|
18195
|
+
p7.cancel("Cancelled.");
|
|
17961
18196
|
return "cancel";
|
|
17962
18197
|
}
|
|
17963
18198
|
let code;
|
|
@@ -17970,14 +18205,14 @@ async function runFirstRunWizard(_trace = false) {
|
|
|
17970
18205
|
}
|
|
17971
18206
|
if (code !== 0) return "cancel";
|
|
17972
18207
|
if (await needsFirstRunSetup()) return "cancel";
|
|
17973
|
-
|
|
18208
|
+
p7.log.success("OpenAI provider ready \u2014 picking a model next.");
|
|
17974
18209
|
return "continue";
|
|
17975
18210
|
}
|
|
17976
18211
|
|
|
17977
18212
|
// src/server/index.ts
|
|
17978
18213
|
import pc11 from "picocolors";
|
|
17979
18214
|
import { networkInterfaces as networkInterfaces2 } from "os";
|
|
17980
|
-
import * as
|
|
18215
|
+
import * as p11 from "@clack/prompts";
|
|
17981
18216
|
|
|
17982
18217
|
// src/target-compatibility.ts
|
|
17983
18218
|
function blacklistAgentForTarget(target) {
|
|
@@ -18015,10 +18250,10 @@ function providersForTarget(providers, target) {
|
|
|
18015
18250
|
}
|
|
18016
18251
|
|
|
18017
18252
|
// src/server/prompts.ts
|
|
18018
|
-
import * as
|
|
18253
|
+
import * as p8 from "@clack/prompts";
|
|
18019
18254
|
import pc8 from "picocolors";
|
|
18020
18255
|
async function askServerStartMode() {
|
|
18021
|
-
const mode = await
|
|
18256
|
+
const mode = await p8.select({
|
|
18022
18257
|
message: "How do you want to start the server?",
|
|
18023
18258
|
options: [
|
|
18024
18259
|
{ value: "configure", label: pc8.cyan("Configure & start"), hint: "Providers, discovery masking, listen mode" },
|
|
@@ -18026,37 +18261,37 @@ async function askServerStartMode() {
|
|
|
18026
18261
|
],
|
|
18027
18262
|
initialValue: "configure"
|
|
18028
18263
|
});
|
|
18029
|
-
if (
|
|
18030
|
-
|
|
18264
|
+
if (p8.isCancel(mode)) {
|
|
18265
|
+
p8.cancel("Cancelled.");
|
|
18031
18266
|
return null;
|
|
18032
18267
|
}
|
|
18033
18268
|
return mode;
|
|
18034
18269
|
}
|
|
18035
18270
|
async function askMaskGatewayIds(initialValue) {
|
|
18036
|
-
const mask = await
|
|
18271
|
+
const mask = await p8.confirm({
|
|
18037
18272
|
message: "Mask gateway model ids for discovery? (Needed for Claude Desktop / Cowork)",
|
|
18038
18273
|
initialValue
|
|
18039
18274
|
});
|
|
18040
|
-
if (
|
|
18041
|
-
|
|
18275
|
+
if (p8.isCancel(mask)) {
|
|
18276
|
+
p8.cancel("Cancelled.");
|
|
18042
18277
|
return null;
|
|
18043
18278
|
}
|
|
18044
18279
|
return Boolean(mask);
|
|
18045
18280
|
}
|
|
18046
18281
|
async function askFavoritesOnly(initialValue) {
|
|
18047
18282
|
printFavoritesOnlyPanel();
|
|
18048
|
-
const favoritesOnly = await
|
|
18283
|
+
const favoritesOnly = await p8.confirm({
|
|
18049
18284
|
message: "Expose only favorite models?",
|
|
18050
18285
|
initialValue
|
|
18051
18286
|
});
|
|
18052
|
-
if (
|
|
18053
|
-
|
|
18287
|
+
if (p8.isCancel(favoritesOnly)) {
|
|
18288
|
+
p8.cancel("Cancelled.");
|
|
18054
18289
|
return null;
|
|
18055
18290
|
}
|
|
18056
18291
|
return Boolean(favoritesOnly);
|
|
18057
18292
|
}
|
|
18058
18293
|
async function askListenMode() {
|
|
18059
|
-
const mode = await
|
|
18294
|
+
const mode = await p8.select({
|
|
18060
18295
|
message: "Where should the server listen?",
|
|
18061
18296
|
options: [
|
|
18062
18297
|
{ value: "local", label: pc8.cyan("Local only"), hint: "Only this computer can use it" },
|
|
@@ -18064,26 +18299,26 @@ async function askListenMode() {
|
|
|
18064
18299
|
],
|
|
18065
18300
|
initialValue: "local"
|
|
18066
18301
|
});
|
|
18067
|
-
if (
|
|
18068
|
-
|
|
18302
|
+
if (p8.isCancel(mode)) {
|
|
18303
|
+
p8.cancel("Cancelled.");
|
|
18069
18304
|
return null;
|
|
18070
18305
|
}
|
|
18071
18306
|
return mode;
|
|
18072
18307
|
}
|
|
18073
18308
|
async function askServerPassword() {
|
|
18074
18309
|
printNetworkWarningPanel();
|
|
18075
|
-
const
|
|
18310
|
+
const password3 = await p8.text({
|
|
18076
18311
|
message: "Choose a server password for this run:",
|
|
18077
18312
|
validate: (value) => value.trim() ? void 0 : "Password cannot be empty"
|
|
18078
18313
|
});
|
|
18079
|
-
if (
|
|
18080
|
-
|
|
18314
|
+
if (p8.isCancel(password3)) {
|
|
18315
|
+
p8.cancel("Cancelled.");
|
|
18081
18316
|
return null;
|
|
18082
18317
|
}
|
|
18083
|
-
return String(
|
|
18318
|
+
return String(password3).trim();
|
|
18084
18319
|
}
|
|
18085
18320
|
async function askUseSavedServerPassword() {
|
|
18086
|
-
const choice = await
|
|
18321
|
+
const choice = await p8.select({
|
|
18087
18322
|
message: "Use saved server password?",
|
|
18088
18323
|
options: [
|
|
18089
18324
|
{ value: "use-saved", label: pc8.cyan("Use saved password") },
|
|
@@ -18091,19 +18326,19 @@ async function askUseSavedServerPassword() {
|
|
|
18091
18326
|
],
|
|
18092
18327
|
initialValue: "use-saved"
|
|
18093
18328
|
});
|
|
18094
|
-
if (
|
|
18095
|
-
|
|
18329
|
+
if (p8.isCancel(choice)) {
|
|
18330
|
+
p8.cancel("Cancelled.");
|
|
18096
18331
|
return null;
|
|
18097
18332
|
}
|
|
18098
18333
|
return choice;
|
|
18099
18334
|
}
|
|
18100
18335
|
async function askSaveServerPassword() {
|
|
18101
|
-
const save = await
|
|
18336
|
+
const save = await p8.confirm({
|
|
18102
18337
|
message: "Save this server password for future server runs?",
|
|
18103
18338
|
initialValue: false
|
|
18104
18339
|
});
|
|
18105
|
-
if (
|
|
18106
|
-
|
|
18340
|
+
if (p8.isCancel(save)) {
|
|
18341
|
+
p8.cancel("Cancelled.");
|
|
18107
18342
|
return null;
|
|
18108
18343
|
}
|
|
18109
18344
|
return Boolean(save);
|
|
@@ -18217,24 +18452,24 @@ function translateOpenAiRequest(body, options) {
|
|
|
18217
18452
|
async function collectOpenAiStream(stream) {
|
|
18218
18453
|
const collected = { text: "", toolCalls: [], finishReason: void 0, usage: void 0 };
|
|
18219
18454
|
for await (const part of stream) {
|
|
18220
|
-
const
|
|
18221
|
-
switch (
|
|
18455
|
+
const p14 = part;
|
|
18456
|
+
switch (p14.type) {
|
|
18222
18457
|
case "text-delta":
|
|
18223
|
-
collected.text +=
|
|
18458
|
+
collected.text += p14.textDelta ?? p14.text ?? "";
|
|
18224
18459
|
break;
|
|
18225
18460
|
case "tool-call":
|
|
18226
18461
|
collected.toolCalls.push({
|
|
18227
|
-
toolCallId:
|
|
18228
|
-
toolName:
|
|
18229
|
-
input:
|
|
18462
|
+
toolCallId: p14.toolCallId ?? "",
|
|
18463
|
+
toolName: p14.toolName ?? "",
|
|
18464
|
+
input: p14.input
|
|
18230
18465
|
});
|
|
18231
18466
|
break;
|
|
18232
18467
|
case "finish":
|
|
18233
|
-
collected.finishReason =
|
|
18234
|
-
collected.usage =
|
|
18468
|
+
collected.finishReason = p14.finishReason ?? collected.finishReason;
|
|
18469
|
+
collected.usage = p14.totalUsage ?? p14.usage ?? collected.usage;
|
|
18235
18470
|
break;
|
|
18236
18471
|
case "error":
|
|
18237
|
-
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");
|
|
18238
18473
|
}
|
|
18239
18474
|
}
|
|
18240
18475
|
return collected;
|
|
@@ -18359,22 +18594,22 @@ async function streamOpenAiResponse(model, params, responseModelId, onChunk, opt
|
|
|
18359
18594
|
|
|
18360
18595
|
`);
|
|
18361
18596
|
for await (const part of watchOpenAiStream(stream, budget)) {
|
|
18362
|
-
const
|
|
18363
|
-
switch (
|
|
18597
|
+
const p14 = part;
|
|
18598
|
+
switch (p14.type) {
|
|
18364
18599
|
case "text-delta":
|
|
18365
|
-
send({ role: "assistant", content:
|
|
18600
|
+
send({ role: "assistant", content: p14.textDelta ?? p14.text ?? "" });
|
|
18366
18601
|
break;
|
|
18367
18602
|
case "tool-input-start":
|
|
18368
|
-
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: "" } }] });
|
|
18369
18604
|
break;
|
|
18370
18605
|
case "tool-input-delta":
|
|
18371
|
-
send({ tool_calls: [{ index: 0, function: { arguments:
|
|
18606
|
+
send({ tool_calls: [{ index: 0, function: { arguments: p14.delta ?? p14.text ?? p14.argsTextDelta ?? "" } }] });
|
|
18372
18607
|
break;
|
|
18373
18608
|
case "finish":
|
|
18374
|
-
send({},
|
|
18609
|
+
send({}, p14.finishReason || "stop");
|
|
18375
18610
|
break;
|
|
18376
18611
|
case "error":
|
|
18377
|
-
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");
|
|
18378
18613
|
}
|
|
18379
18614
|
}
|
|
18380
18615
|
onChunk("data: [DONE]\n\n");
|
|
@@ -19108,7 +19343,7 @@ function summarizeServerProviders(models) {
|
|
|
19108
19343
|
|
|
19109
19344
|
// src/server/provider-select.ts
|
|
19110
19345
|
import pc9 from "picocolors";
|
|
19111
|
-
import * as
|
|
19346
|
+
import * as p9 from "@clack/prompts";
|
|
19112
19347
|
function isSelected(list, id) {
|
|
19113
19348
|
return list.includes(id);
|
|
19114
19349
|
}
|
|
@@ -19118,7 +19353,7 @@ function resolveInitialServerProviders(initial, available) {
|
|
|
19118
19353
|
}
|
|
19119
19354
|
async function selectServerProviders(available, initial) {
|
|
19120
19355
|
if (available.length === 0) {
|
|
19121
|
-
|
|
19356
|
+
p9.log.warn("No providers available to expose.");
|
|
19122
19357
|
return null;
|
|
19123
19358
|
}
|
|
19124
19359
|
let selected = resolveInitialServerProviders(initial, available);
|
|
@@ -19144,31 +19379,31 @@ async function selectServerProviders(available, initial) {
|
|
|
19144
19379
|
}
|
|
19145
19380
|
options.push({ value: "__done__", label: "Done", hint: "" });
|
|
19146
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`;
|
|
19147
|
-
const choice = await
|
|
19382
|
+
const choice = await p9.select({
|
|
19148
19383
|
message: header,
|
|
19149
19384
|
options,
|
|
19150
19385
|
initialValue: "__done__"
|
|
19151
19386
|
});
|
|
19152
|
-
if (
|
|
19387
|
+
if (p9.isCancel(choice) || choice === "__done__") {
|
|
19153
19388
|
if (selected.length === 0) {
|
|
19154
|
-
|
|
19389
|
+
p9.log.warn("Select at least one provider to expose.");
|
|
19155
19390
|
continue;
|
|
19156
19391
|
}
|
|
19157
19392
|
break;
|
|
19158
19393
|
}
|
|
19159
19394
|
if (choice === "__all__") {
|
|
19160
19395
|
selected = available.map((provider) => provider.id);
|
|
19161
|
-
|
|
19396
|
+
p9.log.success(`Exposing all ${available.length} providers.`);
|
|
19162
19397
|
continue;
|
|
19163
19398
|
}
|
|
19164
19399
|
if (choice === "__clear__") {
|
|
19165
19400
|
selected = [];
|
|
19166
|
-
|
|
19401
|
+
p9.log.success("Cleared provider list \u2014 add the ones you want to expose.");
|
|
19167
19402
|
continue;
|
|
19168
19403
|
}
|
|
19169
19404
|
if (choice === "__add__") {
|
|
19170
19405
|
if (unselected.length === 0) continue;
|
|
19171
|
-
const picked = await
|
|
19406
|
+
const picked = await p9.select({
|
|
19172
19407
|
message: "Which provider?",
|
|
19173
19408
|
options: unselected.map((provider) => ({
|
|
19174
19409
|
value: provider.id,
|
|
@@ -19176,7 +19411,7 @@ async function selectServerProviders(available, initial) {
|
|
|
19176
19411
|
hint: `${provider.modelCount} model${provider.modelCount !== 1 ? "s" : ""}`
|
|
19177
19412
|
}))
|
|
19178
19413
|
});
|
|
19179
|
-
if (
|
|
19414
|
+
if (p9.isCancel(picked)) continue;
|
|
19180
19415
|
selected = [...selected, picked];
|
|
19181
19416
|
continue;
|
|
19182
19417
|
}
|
|
@@ -19186,7 +19421,7 @@ async function selectServerProviders(available, initial) {
|
|
|
19186
19421
|
if (!id) continue;
|
|
19187
19422
|
const provider = lookup2.get(id);
|
|
19188
19423
|
selected = selected.filter((_, i) => i !== idx);
|
|
19189
|
-
|
|
19424
|
+
p9.log.success(`Removed ${provider?.name ?? id}.`);
|
|
19190
19425
|
}
|
|
19191
19426
|
}
|
|
19192
19427
|
return selected;
|
|
@@ -19194,7 +19429,7 @@ async function selectServerProviders(available, initial) {
|
|
|
19194
19429
|
|
|
19195
19430
|
// src/http-proxy/index.ts
|
|
19196
19431
|
import pc10 from "picocolors";
|
|
19197
|
-
import * as
|
|
19432
|
+
import * as p10 from "@clack/prompts";
|
|
19198
19433
|
|
|
19199
19434
|
// src/http-proxy/server.ts
|
|
19200
19435
|
import * as http2 from "http";
|
|
@@ -20348,10 +20583,10 @@ function printHttpProxyModels(routes, aliases = []) {
|
|
|
20348
20583
|
}
|
|
20349
20584
|
function reportSkippedHttpProxyFavorites(loaded) {
|
|
20350
20585
|
if (loaded.unavailable.length > 0) {
|
|
20351
|
-
|
|
20586
|
+
p10.log.warn(`${loaded.unavailable.length} favorite${loaded.unavailable.length === 1 ? "" : "s"} unavailable or missing credentials.`);
|
|
20352
20587
|
}
|
|
20353
20588
|
if (loaded.unsupported.length > 0) {
|
|
20354
|
-
|
|
20589
|
+
p10.log.warn(
|
|
20355
20590
|
`${loaded.unsupported.length} favorite${loaded.unsupported.length === 1 ? "" : "s"} skipped \u2014 HTTP proxy mode supports non-Anthropic AI SDK routes only.`
|
|
20356
20591
|
);
|
|
20357
20592
|
}
|
|
@@ -20363,7 +20598,7 @@ function reportSkippedHttpProxyFavorites(loaded) {
|
|
|
20363
20598
|
describeModelAliasRejection(rejection.reason)
|
|
20364
20599
|
])
|
|
20365
20600
|
);
|
|
20366
|
-
|
|
20601
|
+
p10.log.warn(
|
|
20367
20602
|
`${loaded.unavailableAliases.length} model alias${loaded.unavailableAliases.length === 1 ? "" : "es"} skipped. Saved entries were preserved.
|
|
20368
20603
|
` + loaded.unavailableAliases.map((alias) => ` ${JSON.stringify(alias.name)} \u2014 ${reasonByAlias.get(alias) ?? "target unavailable"}`).join("\n")
|
|
20369
20604
|
);
|
|
@@ -20444,7 +20679,7 @@ async function runHttpProxyServerCommand(debug = false, webSocketDiagnostics = f
|
|
|
20444
20679
|
webSocketDiagnosticsLogPath
|
|
20445
20680
|
);
|
|
20446
20681
|
} catch (err) {
|
|
20447
|
-
|
|
20682
|
+
p10.log.error(`Failed to start HTTP proxy: ${err instanceof Error ? err.message : String(err)}`);
|
|
20448
20683
|
return 1;
|
|
20449
20684
|
}
|
|
20450
20685
|
const { handle, loaded, caWarning } = started;
|
|
@@ -20647,8 +20882,8 @@ async function getServerPasswordForQuickMode(mode, passwordOverride) {
|
|
|
20647
20882
|
if (trimmedOverride) return { password: trimmedOverride, wasSaved: false };
|
|
20648
20883
|
const savedPassword = await getSavedServerPassword();
|
|
20649
20884
|
if (savedPassword) return { password: savedPassword, wasSaved: true };
|
|
20650
|
-
|
|
20651
|
-
|
|
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.");
|
|
20652
20887
|
return void 0;
|
|
20653
20888
|
}
|
|
20654
20889
|
function savedServerRunConfig() {
|
|
@@ -20682,16 +20917,16 @@ function shouldUseQuickServerMode(options) {
|
|
|
20682
20917
|
return Boolean(options.quick || hasServerRunOverrides(options) || !process.stdin.isTTY);
|
|
20683
20918
|
}
|
|
20684
20919
|
async function configureExposedProviders() {
|
|
20685
|
-
|
|
20686
|
-
const
|
|
20687
|
-
|
|
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...");
|
|
20688
20923
|
const catalog = await fetchProviderCatalog({ agent: "server" });
|
|
20689
|
-
|
|
20924
|
+
spinner6.stop("");
|
|
20690
20925
|
const available = providerOptionsFromCatalog(catalog);
|
|
20691
20926
|
const picked = await selectServerProviders(available, getServerExposedProviders() ?? void 0);
|
|
20692
20927
|
if (!picked) return void 0;
|
|
20693
20928
|
setServerExposedProviders(picked);
|
|
20694
|
-
|
|
20929
|
+
p11.log.success(`Saved ${picked.length} provider${picked.length !== 1 ? "s" : ""} for future server runs.`);
|
|
20695
20930
|
return picked;
|
|
20696
20931
|
}
|
|
20697
20932
|
async function runServerWizard() {
|
|
@@ -20705,7 +20940,7 @@ async function runServerWizard() {
|
|
|
20705
20940
|
if (favoritesOnly === null) return void 0;
|
|
20706
20941
|
setServerFavoritesOnly(favoritesOnly);
|
|
20707
20942
|
if (favoritesOnly) {
|
|
20708
|
-
|
|
20943
|
+
p11.log.info("Manage favorites with `clodex models`.");
|
|
20709
20944
|
}
|
|
20710
20945
|
let exposedProviders = null;
|
|
20711
20946
|
if (!favoritesOnly) {
|
|
@@ -20735,14 +20970,14 @@ async function runServerCommand(options = {}) {
|
|
|
20735
20970
|
if (options.httpProxy) {
|
|
20736
20971
|
const hasGatewayOptions = options.quick || options.listenMode !== void 0 || options.providersMode !== void 0 || options.maskGatewayIds !== void 0 || options.password !== void 0;
|
|
20737
20972
|
if (hasGatewayOptions) {
|
|
20738
|
-
|
|
20973
|
+
p11.log.error("--proxy is a local-only server mode and cannot be combined with endpoint-mode server options.");
|
|
20739
20974
|
return 1;
|
|
20740
20975
|
}
|
|
20741
20976
|
return runHttpProxyServerCommand(false, options.wsDiagnostics, options.port, noDiscovery);
|
|
20742
20977
|
}
|
|
20743
20978
|
const apiKey = await resolveServerUpstreamApiKey();
|
|
20744
20979
|
if (!apiKey) {
|
|
20745
|
-
|
|
20980
|
+
p11.log.error("No providers configured. Run `clodex providers` to add OpenAI.");
|
|
20746
20981
|
return 1;
|
|
20747
20982
|
}
|
|
20748
20983
|
const quickMode = shouldUseQuickServerMode(options);
|
|
@@ -20757,8 +20992,8 @@ async function runServerCommand(options = {}) {
|
|
|
20757
20992
|
const { password: serverPassword, wasSaved: passwordWasSaved } = pwResult;
|
|
20758
20993
|
const mode = runConfig.listenMode;
|
|
20759
20994
|
const host = mode === "network" ? "0.0.0.0" : "127.0.0.1";
|
|
20760
|
-
const
|
|
20761
|
-
|
|
20995
|
+
const spinner6 = p11.spinner();
|
|
20996
|
+
spinner6.start("Fetching available models...");
|
|
20762
20997
|
let models;
|
|
20763
20998
|
try {
|
|
20764
20999
|
models = await loadServerModels();
|
|
@@ -20768,26 +21003,26 @@ async function runServerCommand(options = {}) {
|
|
|
20768
21003
|
if (runConfig.favoritesOnly) {
|
|
20769
21004
|
const favorites = loadPreferences().favoriteModels ?? [];
|
|
20770
21005
|
if (favorites.length === 0) {
|
|
20771
|
-
|
|
20772
|
-
|
|
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.");
|
|
20773
21008
|
return 1;
|
|
20774
21009
|
}
|
|
20775
21010
|
models = filterServerModelsByFavorites(models, favorites).slice(0, MAX_MODEL_CATALOG);
|
|
20776
21011
|
if (models.length === 0) {
|
|
20777
|
-
|
|
20778
|
-
|
|
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.");
|
|
20779
21014
|
return 1;
|
|
20780
21015
|
}
|
|
20781
21016
|
}
|
|
20782
21017
|
if (runConfig.favoritesOnly) {
|
|
20783
|
-
|
|
21018
|
+
p11.log.info(
|
|
20784
21019
|
`Favorites-only mode active \u2014 GET /anthropic/v1/models returns ${models.length} favorites.`
|
|
20785
21020
|
);
|
|
20786
|
-
|
|
21021
|
+
p11.log.info("Desktop/Cowork picker will only show these. Edit with `clodex models`.");
|
|
20787
21022
|
}
|
|
20788
21023
|
if (models.length === 0) {
|
|
20789
|
-
|
|
20790
|
-
|
|
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.");
|
|
20791
21026
|
return 1;
|
|
20792
21027
|
}
|
|
20793
21028
|
const localCount = models.filter((m) => m.apiKey !== void 0).length;
|
|
@@ -20795,10 +21030,10 @@ async function runServerCommand(options = {}) {
|
|
|
20795
21030
|
const filterNote = runConfig.exposedProviders ? ` \u2014 ${runConfig.exposedProviders.length} provider${runConfig.exposedProviders.length !== 1 ? "s" : ""}` : "";
|
|
20796
21031
|
const favoritesNote = runConfig.favoritesOnly ? " \u2014 favorites only" : "";
|
|
20797
21032
|
const maskNote = runConfig.maskGatewayIds ? " \u2014 discovery ids masked" : "";
|
|
20798
|
-
|
|
20799
|
-
if (summary)
|
|
21033
|
+
spinner6.stop(`Loaded ${models.length} models (${localCount} from registry providers)${filterNote}${favoritesNote}${maskNote}`);
|
|
21034
|
+
if (summary) p11.log.info(summary);
|
|
20800
21035
|
} catch (err) {
|
|
20801
|
-
|
|
21036
|
+
spinner6.stop(pc11.red("Failed to load models"));
|
|
20802
21037
|
console.error(pc11.red(String(err instanceof Error ? err.message : err)));
|
|
20803
21038
|
return 1;
|
|
20804
21039
|
}
|
|
@@ -20820,7 +21055,7 @@ async function runServerCommand(options = {}) {
|
|
|
20820
21055
|
...collidingAliases.flatMap(({ sources }) => sources.map((source) => `${JSON.stringify(source.name)} \u2014 conflicts with a catalog model id`))
|
|
20821
21056
|
];
|
|
20822
21057
|
if (aliasWarnings.length > 0) {
|
|
20823
|
-
|
|
21058
|
+
p11.log.warn(
|
|
20824
21059
|
`${aliasWarnings.length} saved model alias${aliasWarnings.length === 1 ? "" : "es"} inactive. Saved entries were preserved.
|
|
20825
21060
|
${aliasWarnings.join("\n ")}`
|
|
20826
21061
|
);
|
|
@@ -20902,7 +21137,7 @@ function removeFavorite(list, fav) {
|
|
|
20902
21137
|
}
|
|
20903
21138
|
|
|
20904
21139
|
// src/favorites-picker.ts
|
|
20905
|
-
import * as
|
|
21140
|
+
import * as p12 from "@clack/prompts";
|
|
20906
21141
|
import pc12 from "picocolors";
|
|
20907
21142
|
|
|
20908
21143
|
// src/favorite-provider-display.ts
|
|
@@ -20980,25 +21215,25 @@ async function pickGlobalFavoriteModel(providers, favorites, opts) {
|
|
|
20980
21215
|
if (index.length === 0) return null;
|
|
20981
21216
|
const freeOnly = opts?.freeOnly === true;
|
|
20982
21217
|
while (true) {
|
|
20983
|
-
const searchInput = await
|
|
21218
|
+
const searchInput = await p12.text({
|
|
20984
21219
|
message: freeOnly ? `Search free models (${filterGlobalFavoriteIndex(index, "", { freeOnly: true }).length} models):` : `Search all providers (${index.length} models):`,
|
|
20985
21220
|
placeholder: "e.g. deepseek, claude, sonnet"
|
|
20986
21221
|
});
|
|
20987
|
-
if (
|
|
20988
|
-
const fallback = await
|
|
21222
|
+
if (p12.isCancel(searchInput)) {
|
|
21223
|
+
const fallback = await p12.select({
|
|
20989
21224
|
message: "Add a favorite",
|
|
20990
21225
|
options: [
|
|
20991
21226
|
{ value: "back", label: pc12.cyan("\u2190 Back to favorites"), hint: "" },
|
|
20992
21227
|
{ value: ADD_BY_PROVIDER, label: pc12.cyan("Browse by provider \u2192"), hint: "Pick one provider first" }
|
|
20993
21228
|
]
|
|
20994
21229
|
});
|
|
20995
|
-
if (
|
|
21230
|
+
if (p12.isCancel(fallback) || fallback === "back") return null;
|
|
20996
21231
|
if (fallback === ADD_BY_PROVIDER) return ADD_BY_PROVIDER;
|
|
20997
21232
|
continue;
|
|
20998
21233
|
}
|
|
20999
21234
|
const matched = filterGlobalFavoriteIndex(index, String(searchInput), { freeOnly });
|
|
21000
21235
|
if (matched.length === 0) {
|
|
21001
|
-
|
|
21236
|
+
p12.log.warn("No models match \u2014 try a different search");
|
|
21002
21237
|
continue;
|
|
21003
21238
|
}
|
|
21004
21239
|
const result = await pickModelFromPagedList(
|
|
@@ -21016,7 +21251,7 @@ async function pickGlobalFavoriteModel(providers, favorites, opts) {
|
|
|
21016
21251
|
const picked = parseGlobalFavoritePickKey(result.id, matched);
|
|
21017
21252
|
if (!picked) continue;
|
|
21018
21253
|
if (isFavorite(favorites, { providerId: picked.providerId, modelId: picked.model.id })) {
|
|
21019
|
-
|
|
21254
|
+
p12.log.warn(`${picked.model.name || picked.model.id} (${picked.providerName}) is already in your favorites.`);
|
|
21020
21255
|
continue;
|
|
21021
21256
|
}
|
|
21022
21257
|
return picked;
|
|
@@ -21086,7 +21321,7 @@ function resolveLaunchTarget(explicit, prefs, _agent = "claude") {
|
|
|
21086
21321
|
}
|
|
21087
21322
|
function findProviderAndModel(providers, target) {
|
|
21088
21323
|
if (!target.providerId || !target.modelId) return null;
|
|
21089
|
-
const provider = providers.find((
|
|
21324
|
+
const provider = providers.find((p14) => p14.id === target.providerId);
|
|
21090
21325
|
if (!provider) return null;
|
|
21091
21326
|
const model = provider.models.find((m) => m.id === target.modelId);
|
|
21092
21327
|
if (!model) return null;
|
|
@@ -21292,7 +21527,7 @@ function buildAndPublish(step) {
|
|
|
21292
21527
|
const compiled = step.compile(cscPath, CSC_ARGS, scratch);
|
|
21293
21528
|
const builtExe = join9(scratch, LAUNCHER_EXE_NAME);
|
|
21294
21529
|
if (compiled.error || compiled.status !== 0 || !existsSync7(builtExe)) {
|
|
21295
|
-
const output = [compiled.stdout, compiled.stderr].map((
|
|
21530
|
+
const output = [compiled.stdout, compiled.stderr].map((text6) => text6.trim()).filter(Boolean).join("\n");
|
|
21296
21531
|
return {
|
|
21297
21532
|
ok: false,
|
|
21298
21533
|
reason: "compile-failed",
|
|
@@ -21703,6 +21938,7 @@ ${pc13.bold("Providers:")}
|
|
|
21703
21938
|
openai OpenAI API key (platform.openai.com)
|
|
21704
21939
|
openai-oauth ChatGPT/Codex plan OAuth \u2014 sign in with clodex providers auth openai
|
|
21705
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
|
|
21706
21942
|
|
|
21707
21943
|
${pc13.bold("Model switching:")}
|
|
21708
21944
|
Run clodex models to save favorites (max ${MAX_MODEL_CATALOG}).
|
|
@@ -21927,9 +22163,9 @@ ${pc13.bold("Behavior:")}
|
|
|
21927
22163
|
${pc13.bold("Setup guide:")}
|
|
21928
22164
|
docs/windows-setup.md in the clodex package (also on GitHub).`;
|
|
21929
22165
|
}
|
|
21930
|
-
function printHelp(
|
|
22166
|
+
function printHelp(text6) {
|
|
21931
22167
|
console.log(`
|
|
21932
|
-
${
|
|
22168
|
+
${text6}
|
|
21933
22169
|
`);
|
|
21934
22170
|
}
|
|
21935
22171
|
function reportInactiveCatalogAliases(modelAliases) {
|
|
@@ -21938,7 +22174,7 @@ function reportInactiveCatalogAliases(modelAliases) {
|
|
|
21938
22174
|
const warningLines = unavailableAliases.flatMap((alias) => alias.sourceNames?.length ? alias.sourceNames.map((name) => ` ${JSON.stringify(name)} \u2014 ${alias.unavailableReason}`) : [
|
|
21939
22175
|
` ${JSON.stringify(alias.savedName ?? alias.name)} \u2014 ${alias.unavailableReason}`
|
|
21940
22176
|
]);
|
|
21941
|
-
|
|
22177
|
+
p13.log.warn(
|
|
21942
22178
|
`${warningLines.length} saved model alias${warningLines.length === 1 ? "" : "es"} inactive. Saved entries were preserved.
|
|
21943
22179
|
` + warningLines.join("\n")
|
|
21944
22180
|
);
|
|
@@ -21956,11 +22192,11 @@ async function launchClaudeViaCatalog(catalogRoutes, startingRoute, modelAliases
|
|
|
21956
22192
|
void 0,
|
|
21957
22193
|
modelAliases
|
|
21958
22194
|
);
|
|
21959
|
-
|
|
22195
|
+
p13.log.info(
|
|
21960
22196
|
`Switch menu active \u2014 proxy on port ${proxyHandle.port} ` + pc13.dim(`(${catalogRoutes.length} model${catalogRoutes.length !== 1 ? "s" : ""} in /model)`)
|
|
21961
22197
|
);
|
|
21962
22198
|
} catch (err) {
|
|
21963
|
-
|
|
22199
|
+
p13.log.error(`Failed to start proxy: ${err instanceof Error ? err.message : String(err)}`);
|
|
21964
22200
|
return 1;
|
|
21965
22201
|
}
|
|
21966
22202
|
const childEnv = buildChildEnv(
|
|
@@ -21973,7 +22209,7 @@ async function launchClaudeViaCatalog(catalogRoutes, startingRoute, modelAliases
|
|
|
21973
22209
|
);
|
|
21974
22210
|
const debugLogPath = prepareClaudeTraceLog();
|
|
21975
22211
|
const traceArgs = trace ? ["--debug-file", debugLogPath] : [];
|
|
21976
|
-
if (trace)
|
|
22212
|
+
if (trace) p13.log.info(`Debug log: ${debugLogPath}`);
|
|
21977
22213
|
const exitCode = await launchClaude(
|
|
21978
22214
|
childEnv,
|
|
21979
22215
|
claudeCodeClientModelId(startingRoute.aliasId, contextWindow),
|
|
@@ -21984,9 +22220,9 @@ async function launchClaudeViaCatalog(catalogRoutes, startingRoute, modelAliases
|
|
|
21984
22220
|
return exitCode;
|
|
21985
22221
|
}
|
|
21986
22222
|
var CLACK_CONTEXT_REPORTER = {
|
|
21987
|
-
info: (message) =>
|
|
21988
|
-
warn: (message) =>
|
|
21989
|
-
success: (message) =>
|
|
22223
|
+
info: (message) => p13.log.info(message),
|
|
22224
|
+
warn: (message) => p13.log.warn(message),
|
|
22225
|
+
success: (message) => p13.log.success(message)
|
|
21990
22226
|
};
|
|
21991
22227
|
var STDERR_CONTEXT_REPORTER = {
|
|
21992
22228
|
info: (message) => console.error(message),
|
|
@@ -22001,7 +22237,7 @@ function applyContextStopAssignments(assignments, save, report, saveHint = "Add
|
|
|
22001
22237
|
for (const assignment of assignments) {
|
|
22002
22238
|
const cached = modelsByProvider.get(assignment.providerId)?.find((model) => model.id === assignment.modelId);
|
|
22003
22239
|
if (!cached) {
|
|
22004
|
-
|
|
22240
|
+
p13.log.error(
|
|
22005
22241
|
`${modelAliasTarget(assignment)} is not in the saved provider catalog. Add the provider and refresh its models first.`
|
|
22006
22242
|
);
|
|
22007
22243
|
return 1;
|
|
@@ -22035,12 +22271,12 @@ function applyContextStopAssignments(assignments, save, report, saveHint = "Add
|
|
|
22035
22271
|
async function runModelsCommand(opts = {}) {
|
|
22036
22272
|
const changesAlias = opts.alias !== void 0 || opts.unalias !== void 0;
|
|
22037
22273
|
if (changesAlias && (opts.list || opts.alias !== void 0 && opts.unalias !== void 0)) {
|
|
22038
|
-
|
|
22274
|
+
p13.log.error("--alias/--unalias apply one at a time to proxy-mode favorites.");
|
|
22039
22275
|
return 1;
|
|
22040
22276
|
}
|
|
22041
22277
|
const assignments = opts.contextAssignments ?? [];
|
|
22042
22278
|
if (opts.saveContext && assignments.length === 0) {
|
|
22043
|
-
|
|
22279
|
+
p13.log.error("--save applies to --context assignments; none were given.");
|
|
22044
22280
|
return 1;
|
|
22045
22281
|
}
|
|
22046
22282
|
if (assignments.length > 0) {
|
|
@@ -22059,7 +22295,7 @@ async function runModelsCommand(opts = {}) {
|
|
|
22059
22295
|
if (opts.alias !== void 0) {
|
|
22060
22296
|
const parsed = parseModelAliasAssignment(opts.alias);
|
|
22061
22297
|
if ("error" in parsed) {
|
|
22062
|
-
|
|
22298
|
+
p13.log.error(parsed.error);
|
|
22063
22299
|
return 1;
|
|
22064
22300
|
}
|
|
22065
22301
|
const prefs2 = loadPreferences();
|
|
@@ -22067,19 +22303,19 @@ async function runModelsCommand(opts = {}) {
|
|
|
22067
22303
|
(favorite) => favorite.providerId === parsed.providerId && favorite.modelId === parsed.modelId
|
|
22068
22304
|
);
|
|
22069
22305
|
if (!isSavedFavorite) {
|
|
22070
|
-
|
|
22071
|
-
|
|
22306
|
+
p13.log.error(`${modelAliasTarget(parsed)} is not a saved favorite.`);
|
|
22307
|
+
p13.log.info("Add it with `clodex models`, then save the alias.");
|
|
22072
22308
|
return 1;
|
|
22073
22309
|
}
|
|
22074
22310
|
if (prefs2.modelAliases !== void 0 && !Array.isArray(prefs2.modelAliases)) {
|
|
22075
|
-
|
|
22311
|
+
p13.log.error('Saved model aliases are malformed: "modelAliases" must be an array.');
|
|
22076
22312
|
return 1;
|
|
22077
22313
|
}
|
|
22078
22314
|
const aliases = prefs2.modelAliases ?? [];
|
|
22079
22315
|
const modelAliases = aliases.filter((alias) => !modelAliasMatchesName(alias, parsed.name));
|
|
22080
22316
|
modelAliases.push(parsed);
|
|
22081
22317
|
savePreferences({ modelAliases });
|
|
22082
|
-
|
|
22318
|
+
p13.log.success(`Saved model alias ${parsed.name} \u2192 ${modelAliasTarget(parsed)}.`);
|
|
22083
22319
|
return 0;
|
|
22084
22320
|
}
|
|
22085
22321
|
if (opts.unalias !== void 0) {
|
|
@@ -22087,18 +22323,18 @@ async function runModelsCommand(opts = {}) {
|
|
|
22087
22323
|
const name = canonicalModelAliasName(requestedName);
|
|
22088
22324
|
const prefs2 = loadPreferences();
|
|
22089
22325
|
if (prefs2.modelAliases !== void 0 && !Array.isArray(prefs2.modelAliases)) {
|
|
22090
|
-
|
|
22326
|
+
p13.log.error('Saved model aliases are malformed: "modelAliases" must be an array.');
|
|
22091
22327
|
return 1;
|
|
22092
22328
|
}
|
|
22093
22329
|
const aliases = prefs2.modelAliases ?? [];
|
|
22094
22330
|
const modelAliases = aliases.filter((alias) => !modelAliasMatchesStoredName(alias, requestedName));
|
|
22095
22331
|
const removedCount = aliases.length - modelAliases.length;
|
|
22096
22332
|
if (removedCount === 0) {
|
|
22097
|
-
|
|
22333
|
+
p13.log.error(`No model alias named ${JSON.stringify(requestedName)} is saved.`);
|
|
22098
22334
|
return 1;
|
|
22099
22335
|
}
|
|
22100
22336
|
savePreferences({ modelAliases });
|
|
22101
|
-
|
|
22337
|
+
p13.log.success(
|
|
22102
22338
|
removedCount === 1 ? `Removed model alias ${name || JSON.stringify(requestedName)}.` : `Removed ${removedCount} model aliases named ${name || JSON.stringify(requestedName)}.`
|
|
22103
22339
|
);
|
|
22104
22340
|
return 0;
|
|
@@ -22110,25 +22346,25 @@ async function runModelsCommand(opts = {}) {
|
|
|
22110
22346
|
reportSkippedHttpProxyFavorites(loaded);
|
|
22111
22347
|
return 0;
|
|
22112
22348
|
} catch (err) {
|
|
22113
|
-
|
|
22349
|
+
p13.log.error(`Could not load proxy models: ${err instanceof Error ? err.message : String(err)}`);
|
|
22114
22350
|
return 1;
|
|
22115
22351
|
}
|
|
22116
22352
|
}
|
|
22117
22353
|
const maxFavorites = MAX_MODEL_CATALOG;
|
|
22118
22354
|
const scopeName = "Favorite Models";
|
|
22119
22355
|
relayIntro(scopeName);
|
|
22120
|
-
const
|
|
22121
|
-
|
|
22356
|
+
const spinner6 = p13.spinner();
|
|
22357
|
+
spinner6.start("Loading providers...");
|
|
22122
22358
|
const catalog = await fetchProviderCatalog();
|
|
22123
|
-
|
|
22359
|
+
spinner6.stop("");
|
|
22124
22360
|
const allProviders = providersForPicker(catalog);
|
|
22125
22361
|
const favoriteProviders = allProviders.map((provider) => ({
|
|
22126
22362
|
...provider,
|
|
22127
22363
|
name: favoriteProviderDisplayName(provider)
|
|
22128
22364
|
}));
|
|
22129
22365
|
if (favoriteProviders.length === 0) {
|
|
22130
|
-
|
|
22131
|
-
|
|
22366
|
+
p13.log.warn("No providers found.");
|
|
22367
|
+
p13.log.info(`${pc13.dim("Add a provider with ")}${pc13.cyan("clodex providers")}${pc13.dim(".")}`);
|
|
22132
22368
|
relayOutro("Done");
|
|
22133
22369
|
return 0;
|
|
22134
22370
|
}
|
|
@@ -22157,19 +22393,19 @@ async function runModelsCommand(opts = {}) {
|
|
|
22157
22393
|
});
|
|
22158
22394
|
options.push({ value: "__done__", label: "Done", hint: "" });
|
|
22159
22395
|
const header = favorites.length === 0 ? `${scopeName} (0/${maxFavorites})` : `${scopeName} (${favorites.length}/${maxFavorites}) \u2014 select to remove`;
|
|
22160
|
-
const choice = await
|
|
22396
|
+
const choice = await p13.select({
|
|
22161
22397
|
message: header,
|
|
22162
22398
|
options,
|
|
22163
22399
|
initialValue: "__done__"
|
|
22164
22400
|
});
|
|
22165
|
-
if (
|
|
22401
|
+
if (p13.isCancel(choice) || choice === "__done__") break;
|
|
22166
22402
|
if (choice === "__add__") {
|
|
22167
22403
|
if (atCap) {
|
|
22168
|
-
|
|
22404
|
+
p13.log.warn(`Limit of ${maxFavorites} favorites reached \u2014 remove one first.`);
|
|
22169
22405
|
continue;
|
|
22170
22406
|
}
|
|
22171
22407
|
const globalCount = buildGlobalFavoriteIndex(favoriteProviders).length;
|
|
22172
|
-
const addPath = await
|
|
22408
|
+
const addPath = await p13.select({
|
|
22173
22409
|
message: "Add a favorite",
|
|
22174
22410
|
options: [
|
|
22175
22411
|
{
|
|
@@ -22184,7 +22420,7 @@ async function runModelsCommand(opts = {}) {
|
|
|
22184
22420
|
}
|
|
22185
22421
|
]
|
|
22186
22422
|
});
|
|
22187
|
-
if (
|
|
22423
|
+
if (p13.isCancel(addPath)) continue;
|
|
22188
22424
|
let provider;
|
|
22189
22425
|
let browsedMultiple = [];
|
|
22190
22426
|
if (addPath === "global") {
|
|
@@ -22199,12 +22435,12 @@ async function runModelsCommand(opts = {}) {
|
|
|
22199
22435
|
let currentInitialProvider = void 0;
|
|
22200
22436
|
while (true) {
|
|
22201
22437
|
const providerOptions = favoriteProviders.map((ap) => providerSelectOption(ap));
|
|
22202
|
-
const pickedProviderId = await
|
|
22438
|
+
const pickedProviderId = await p13.select({
|
|
22203
22439
|
message: "Which provider?",
|
|
22204
22440
|
options: providerOptions,
|
|
22205
22441
|
initialValue: currentInitialProvider
|
|
22206
22442
|
});
|
|
22207
|
-
if (
|
|
22443
|
+
if (p13.isCancel(pickedProviderId)) break;
|
|
22208
22444
|
provider = favoriteProviders.find((ap) => ap.id === pickedProviderId);
|
|
22209
22445
|
const options2 = provider.models.map((m) => {
|
|
22210
22446
|
const favorited = isFavorite(favorites, { providerId: provider.id, modelId: m.id });
|
|
@@ -22215,12 +22451,12 @@ async function runModelsCommand(opts = {}) {
|
|
|
22215
22451
|
hint: favorited ? pc13.yellow("\u2605 already favorite") : ""
|
|
22216
22452
|
};
|
|
22217
22453
|
});
|
|
22218
|
-
const pickedModelIds = await
|
|
22454
|
+
const pickedModelIds = await p13.multiselect({
|
|
22219
22455
|
message: `Select models to add from ${provider.name} ${pc13.dim("(Space to select, Enter to confirm)")}`,
|
|
22220
22456
|
options: options2,
|
|
22221
22457
|
required: false
|
|
22222
22458
|
});
|
|
22223
|
-
if (
|
|
22459
|
+
if (p13.isCancel(pickedModelIds)) {
|
|
22224
22460
|
currentInitialProvider = provider.id;
|
|
22225
22461
|
continue;
|
|
22226
22462
|
}
|
|
@@ -22255,27 +22491,27 @@ async function runModelsCommand(opts = {}) {
|
|
|
22255
22491
|
if (addedModels.length > 0) {
|
|
22256
22492
|
if (addedModels.length === 1) {
|
|
22257
22493
|
const modelName = addedModels[0].name || addedModels[0].id;
|
|
22258
|
-
|
|
22494
|
+
p13.log.success(`Added ${modelName} (${provider.name}) to favorites.`);
|
|
22259
22495
|
} else {
|
|
22260
|
-
|
|
22496
|
+
p13.log.success(`Added ${addedModels.length} models from ${provider.name} to favorites.`);
|
|
22261
22497
|
}
|
|
22262
22498
|
}
|
|
22263
22499
|
if (duplicateCount > 0) {
|
|
22264
|
-
|
|
22500
|
+
p13.log.warn(`${duplicateCount} selected model(s) were already in your favorites.`);
|
|
22265
22501
|
}
|
|
22266
22502
|
if (limitReached) {
|
|
22267
|
-
|
|
22503
|
+
p13.log.warn(`Limit of ${maxFavorites} favorites reached \u2014 some selected models could not be added.`);
|
|
22268
22504
|
}
|
|
22269
22505
|
} else if (choice.startsWith("fav-")) {
|
|
22270
22506
|
const idx = parseInt(choice.slice(4), 10);
|
|
22271
22507
|
const fav = favorites[idx];
|
|
22272
22508
|
const entry = modelLookup.get(`${fav.providerId}:${fav.modelId}`);
|
|
22273
22509
|
const label = entry ? `${entry.modelName} (${entry.providerName})` : fav.modelId;
|
|
22274
|
-
const confirmed = await
|
|
22275
|
-
if (
|
|
22510
|
+
const confirmed = await p13.confirm({ message: `Remove ${label} from favorites?` });
|
|
22511
|
+
if (p13.isCancel(confirmed) || !confirmed) continue;
|
|
22276
22512
|
favorites = removeFavorite(favorites, fav);
|
|
22277
22513
|
favoritesDirty = true;
|
|
22278
|
-
|
|
22514
|
+
p13.log.success(`Removed ${label} from favorites.`);
|
|
22279
22515
|
}
|
|
22280
22516
|
}
|
|
22281
22517
|
if (favoritesDirty) {
|
|
@@ -22289,8 +22525,8 @@ async function runModelsCommand(opts = {}) {
|
|
|
22289
22525
|
}
|
|
22290
22526
|
async function runClaudeHttpProxyCommand(parsed, claudeArgs, agentStdout) {
|
|
22291
22527
|
if (parsed.launchProvider || parsed.launchModel) {
|
|
22292
|
-
|
|
22293
|
-
|
|
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.");
|
|
22294
22530
|
return 1;
|
|
22295
22531
|
}
|
|
22296
22532
|
if (!agentStdout) relayIntro("Claude Code \u2014 Proxy Mode");
|
|
@@ -22308,7 +22544,7 @@ async function runClaudeHttpProxyCommand(parsed, claudeArgs, agentStdout) {
|
|
|
22308
22544
|
console.log("");
|
|
22309
22545
|
return 0;
|
|
22310
22546
|
} catch (err) {
|
|
22311
|
-
|
|
22547
|
+
p13.log.error(`Could not load proxy models: ${err instanceof Error ? err.message : String(err)}`);
|
|
22312
22548
|
return 1;
|
|
22313
22549
|
}
|
|
22314
22550
|
}
|
|
@@ -22318,13 +22554,13 @@ async function runClaudeHttpProxyCommand(parsed, claudeArgs, agentStdout) {
|
|
|
22318
22554
|
try {
|
|
22319
22555
|
started = await startConfiguredHttpProxy(0, parsed.trace, inferenceLogPath, proxyDebugLogPath);
|
|
22320
22556
|
} catch (err) {
|
|
22321
|
-
|
|
22557
|
+
p13.log.error(`Failed to start proxy: ${err instanceof Error ? err.message : String(err)}`);
|
|
22322
22558
|
return 1;
|
|
22323
22559
|
}
|
|
22324
22560
|
const { handle, loaded, caWarning } = started;
|
|
22325
22561
|
if (caWarning) {
|
|
22326
22562
|
if (agentStdout) emitParentNotice(`clodex: ${caWarning}`);
|
|
22327
|
-
else
|
|
22563
|
+
else p13.log.warn(caWarning);
|
|
22328
22564
|
}
|
|
22329
22565
|
const inheritedProxyPort = (() => {
|
|
22330
22566
|
const value = process.env["HTTPS_PROXY"] ?? process.env["HTTP_PROXY"] ?? process.env["https_proxy"] ?? process.env["http_proxy"];
|
|
@@ -22358,20 +22594,20 @@ async function runClaudeHttpProxyCommand(parsed, claudeArgs, agentStdout) {
|
|
|
22358
22594
|
};
|
|
22359
22595
|
process.once("exit", onProcessExit);
|
|
22360
22596
|
if (!agentStdout) {
|
|
22361
|
-
|
|
22362
|
-
|
|
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}`);
|
|
22363
22599
|
printHttpProxyModels(loaded.routes, loaded.aliases);
|
|
22364
22600
|
reportSkippedHttpProxyFavorites(loaded);
|
|
22365
22601
|
if (loaded.routes.length > 0) {
|
|
22366
|
-
|
|
22602
|
+
p13.log.info("Switch with `/model <listed-name>`.");
|
|
22367
22603
|
}
|
|
22368
22604
|
}
|
|
22369
22605
|
const childEnv = buildHttpProxyChildEnv(handle.port, handle.caCertPath);
|
|
22370
22606
|
const debugLogPath = parsed.trace ? prepareClaudeTraceLog(getSessionLogPath("claude-debug")) : void 0;
|
|
22371
22607
|
const traceArgs = debugLogPath ? ["--debug-file", debugLogPath] : [];
|
|
22372
22608
|
if (debugLogPath && !agentStdout) {
|
|
22373
|
-
|
|
22374
|
-
if (proxyDebugLogPath)
|
|
22609
|
+
p13.log.info(`Claude debug log: ${debugLogPath}`);
|
|
22610
|
+
if (proxyDebugLogPath) p13.log.info(`Adapter debug log: ${proxyDebugLogPath}`);
|
|
22375
22611
|
}
|
|
22376
22612
|
try {
|
|
22377
22613
|
const exitCode = await launchClaude(childEnv, void 0, [...traceArgs, ...claudeArgs]);
|
|
@@ -22456,7 +22692,7 @@ Error: ${launchPlan.error}
|
|
|
22456
22692
|
return 1;
|
|
22457
22693
|
}
|
|
22458
22694
|
} else {
|
|
22459
|
-
const catalogSpinner =
|
|
22695
|
+
const catalogSpinner = p13.spinner();
|
|
22460
22696
|
catalogSpinner.start("Loading your providers...");
|
|
22461
22697
|
try {
|
|
22462
22698
|
catalog = await fetchProviderCatalog();
|
|
@@ -22471,11 +22707,11 @@ Error: ${launchPlan.error}
|
|
|
22471
22707
|
const blockedLaunchReason = launchPlan.skip && launchPlan.target?.providerId ? catalog.blockedProviders.get(launchPlan.target.providerId) : void 0;
|
|
22472
22708
|
if (allProviders.length === 0) {
|
|
22473
22709
|
if (blockedLaunchReason) {
|
|
22474
|
-
|
|
22710
|
+
p13.log.error(blockedLaunchReason);
|
|
22475
22711
|
return 1;
|
|
22476
22712
|
}
|
|
22477
|
-
|
|
22478
|
-
|
|
22713
|
+
p13.log.warn("No providers available.");
|
|
22714
|
+
p13.log.info(pc13.dim("Run clodex providers to get started."));
|
|
22479
22715
|
return 0;
|
|
22480
22716
|
}
|
|
22481
22717
|
const providerOptions = allProviders.map((lp) => providerSelectOption(lp));
|
|
@@ -22492,7 +22728,7 @@ Error: ${launchPlan.error}
|
|
|
22492
22728
|
if (launchPlan.skip && launchPlan.target) {
|
|
22493
22729
|
const resolved = findProviderAndModel(allProviders, launchPlan.target);
|
|
22494
22730
|
if (!resolved) {
|
|
22495
|
-
|
|
22731
|
+
p13.log.error(
|
|
22496
22732
|
blockedLaunchReason ?? `Provider/model not found: ${launchPlan.target.providerId} / ${launchPlan.target.modelId}`
|
|
22497
22733
|
);
|
|
22498
22734
|
return 1;
|
|
@@ -22500,19 +22736,19 @@ Error: ${launchPlan.error}
|
|
|
22500
22736
|
activeProvider = resolved.provider;
|
|
22501
22737
|
selectedModel = resolved.model;
|
|
22502
22738
|
if (!agentStdout) {
|
|
22503
|
-
|
|
22739
|
+
p13.log.step(`Using ${selectedModel.name || selectedModel.id} (${activeProvider.name})`);
|
|
22504
22740
|
}
|
|
22505
22741
|
if (!dryRun) recordLaunchSelection("claude", activeProvider.id, selectedModel.id, prefs);
|
|
22506
22742
|
} else {
|
|
22507
22743
|
let currentInitialProvider = initialProvider;
|
|
22508
22744
|
while (true) {
|
|
22509
|
-
const chosen = await
|
|
22745
|
+
const chosen = await p13.select({
|
|
22510
22746
|
message: "Which provider?",
|
|
22511
22747
|
options: providerOptions,
|
|
22512
22748
|
initialValue: currentInitialProvider
|
|
22513
22749
|
});
|
|
22514
|
-
if (
|
|
22515
|
-
|
|
22750
|
+
if (p13.isCancel(chosen)) {
|
|
22751
|
+
p13.cancel("Cancelled.");
|
|
22516
22752
|
return 0;
|
|
22517
22753
|
}
|
|
22518
22754
|
const providerChoice = chosen;
|
|
@@ -22524,7 +22760,7 @@ Error: ${launchPlan.error}
|
|
|
22524
22760
|
if (prov && mod) available.push({ provider: prov, model: mod });
|
|
22525
22761
|
}
|
|
22526
22762
|
if (available.length === 0) {
|
|
22527
|
-
|
|
22763
|
+
p13.log.warn("No saved favorites are currently available.");
|
|
22528
22764
|
return 0;
|
|
22529
22765
|
}
|
|
22530
22766
|
const favOptions = available.map((f, i) => ({
|
|
@@ -22532,13 +22768,13 @@ Error: ${launchPlan.error}
|
|
|
22532
22768
|
label: `${f.model.name || f.model.id} \u2014 ${f.provider.name}`,
|
|
22533
22769
|
hint: f.model.id
|
|
22534
22770
|
}));
|
|
22535
|
-
const pickedIdx = await
|
|
22771
|
+
const pickedIdx = await p13.select({
|
|
22536
22772
|
message: "Starting model?",
|
|
22537
22773
|
options: favOptions,
|
|
22538
22774
|
initialValue: "0"
|
|
22539
22775
|
});
|
|
22540
|
-
if (
|
|
22541
|
-
|
|
22776
|
+
if (p13.isCancel(pickedIdx)) {
|
|
22777
|
+
p13.cancel("Cancelled.");
|
|
22542
22778
|
return 0;
|
|
22543
22779
|
}
|
|
22544
22780
|
const sel = available[Number(pickedIdx)];
|
|
@@ -22567,12 +22803,12 @@ Error: ${launchPlan.error}
|
|
|
22567
22803
|
);
|
|
22568
22804
|
const startingRoute = resolveRoute(activeProvider.id, selectedModel.id) ?? null;
|
|
22569
22805
|
if (!startingRoute) {
|
|
22570
|
-
|
|
22806
|
+
p13.log.error("Could not resolve a proxy route for the selected model.");
|
|
22571
22807
|
return 1;
|
|
22572
22808
|
}
|
|
22573
22809
|
const { routes: catalogRoutes, droppedFavorites } = buildCatalogRoutes(startingRoute, favorites, resolveRoute);
|
|
22574
22810
|
if (droppedFavorites.length > 0) {
|
|
22575
|
-
|
|
22811
|
+
p13.log.warn(
|
|
22576
22812
|
`Skipping ${droppedFavorites.length} favorite${droppedFavorites.length === 1 ? "" : "s"} that are no longer available in /model`
|
|
22577
22813
|
);
|
|
22578
22814
|
}
|
|
@@ -22626,7 +22862,7 @@ Error: ${launchPlan.error}
|
|
|
22626
22862
|
}
|
|
22627
22863
|
const launchApiKey = await resolveLocalProviderApiKey(activeProvider);
|
|
22628
22864
|
if (!anonymousProvider && !launchApiKey?.trim()) {
|
|
22629
|
-
|
|
22865
|
+
p13.log.error(
|
|
22630
22866
|
`No credential found for ${activeProvider.name}. Add a key or sign in with clodex providers.`
|
|
22631
22867
|
);
|
|
22632
22868
|
return 1;
|
|
@@ -22652,9 +22888,9 @@ Error: ${launchPlan.error}
|
|
|
22652
22888
|
},
|
|
22653
22889
|
launchApiKey ?? ""
|
|
22654
22890
|
);
|
|
22655
|
-
if (!isAgentStdoutMode())
|
|
22891
|
+
if (!isAgentStdoutMode()) p13.log.info(`Anthropic proxy started on port ${proxyHandle.port}`);
|
|
22656
22892
|
} catch (err) {
|
|
22657
|
-
|
|
22893
|
+
p13.log.error(`Failed to start Anthropic proxy: ${err instanceof Error ? err.message : String(err)}`);
|
|
22658
22894
|
return 1;
|
|
22659
22895
|
}
|
|
22660
22896
|
childEnv = buildChildEnv(
|
|
@@ -22698,12 +22934,12 @@ Error: ${launchPlan.error}
|
|
|
22698
22934
|
launchApiKey ?? ""
|
|
22699
22935
|
);
|
|
22700
22936
|
if (!isAgentStdoutMode()) {
|
|
22701
|
-
|
|
22937
|
+
p13.log.info(
|
|
22702
22938
|
`SDK adapter proxy started on port ${proxyHandle.port}` + (selectedModel.npm ? pc13.dim(` (${selectedModel.npm})`) : "")
|
|
22703
22939
|
);
|
|
22704
22940
|
}
|
|
22705
22941
|
} catch (err) {
|
|
22706
|
-
|
|
22942
|
+
p13.log.error(`Failed to start SDK adapter proxy: ${err instanceof Error ? err.message : String(err)}`);
|
|
22707
22943
|
return 1;
|
|
22708
22944
|
}
|
|
22709
22945
|
childEnv = buildChildEnv(
|
|
@@ -22719,7 +22955,7 @@ Error: ${launchPlan.error}
|
|
|
22719
22955
|
}
|
|
22720
22956
|
const debugLogPath = prepareClaudeTraceLog();
|
|
22721
22957
|
const traceArgs = trace ? ["--debug-file", debugLogPath] : [];
|
|
22722
|
-
if (trace)
|
|
22958
|
+
if (trace) p13.log.info(`Debug log: ${debugLogPath}`);
|
|
22723
22959
|
const exitCode = await launchClaude(
|
|
22724
22960
|
childEnv,
|
|
22725
22961
|
claudeCodeClientModelId(selectedModel.id, selectedModel.contextWindow),
|