@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-3NCDMS7N.js";
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 text5 = isFree ? `${providerName} \xB7 free` : providerName;
145
- return color(pc.bold(`(${text5})`));
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 p12 from "@clack/prompts";
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.14.1",
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 log12(status, name, extra) {
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
- log12("SKIP", name, "already patched");
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
- log12("FAIL", name, "anchor not found");
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
- log12("FAIL", name, "anchor matched " + count + " times (expected 1)");
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
- log12("SKIP", name, "already patched");
3567
+ log13("SKIP", name, "already patched");
3566
3568
  return;
3567
3569
  }
3568
- log12("FAIL", name, "replacement made no change");
3570
+ log13("FAIL", name, "replacement made no change");
3569
3571
  if (required) fail(name);
3570
3572
  return;
3571
3573
  }
3572
- log12("OK", name);
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
- log12("SKIP", "PATCH 6: alias resolver switch", "no aliases configured");
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
- log12("SKIP", pickerSite, "no aliases configured");
3628
+ log13("SKIP", pickerSite, "no aliases configured");
3627
3629
  } else if (modelSelections.length !== 1) {
3628
- log12("FAIL", pickerSite, "model selection appears " + modelSelections.length + " times (expected 1)");
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 = (text5, open2) => {
3758
+ const blockEndIndex = (text6, open2) => {
3757
3759
  const depths = [0];
3758
3760
  const templates = [false];
3759
- for (let i = open2; i < text5.length; i++) {
3760
- const ch = text5[i];
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 === "$" && text5[i + 1] === "{") {
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 < text5.length; i++) {
3786
- if (text5[i] === "\\") {
3787
+ for (i++; i < text6.length; i++) {
3788
+ if (text6[i] === "\\") {
3787
3789
  i++;
3788
3790
  continue;
3789
3791
  }
3790
- if (text5[i] === quote) break;
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 === "/" && text5[i + 1] === "/") {
3800
- while (i < text5.length && text5[i] !== "\n") i++;
3801
+ if (ch === "/" && text6[i + 1] === "/") {
3802
+ while (i < text6.length && text6[i] !== "\n") i++;
3801
3803
  continue;
3802
3804
  }
3803
- if (ch === "/" && text5[i + 1] === "*") {
3804
- i = text5.indexOf("*/", i + 2);
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
- log12("FAIL", patchName, "child env passthrough appears " + passthroughSites + " times (expected 1)");
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
- log12("FAIL", patchName, "target validation failed: " + why);
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 text5 = system.map((block) => typeof block === "string" ? block : block && typeof block === "object" && typeof block.text === "string" ? block.text : "").filter(Boolean).join(" ");
6139
- return compactLogValue(text5, REQUEST_PREVIEW_MAX) || void 0;
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 text5 = content.filter((block) => Boolean(block && typeof block === "object")).filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text).join(" ");
6166
- if (text5.trim()) {
6167
- summary = text5;
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 log12 = redactTraceLog(raw);
6440
- const errorLines = log12.split("\n").filter(
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 text5 = readLauncherText(shimPath);
6934
- if (text5 === null) return unreadable;
6935
- const rawTargets = collectTargets(text5, kind);
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 text5 = bytes.toString("utf8");
7082
+ const text6 = bytes.toString("utf8");
7064
7083
  const signals = [
7065
- text5.includes(MISSING_BINARY_MARKER),
7066
- text5.includes(INSTALL_SCRIPT_MARKER),
7067
- text5.includes(OMIT_OPTIONAL_MARKER) && text5.includes(IGNORE_SCRIPTS_MARKER)
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, log12) {
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
- log12?.(`reasoning normalization gap: ${signature}`);
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, log12) {
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
- log12?.(`tool argument normalization gap: ${signature}`);
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, log12, warnOnGap = false, deferWarnings) {
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, log12));
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, log12));
9023
+ if (warnOnGap) raise(() => warnToolArgumentNormalizationGap(gap, log13));
9005
9024
  } else if (toolArgumentGap && warnOnGap) {
9006
9025
  try {
9007
- log12?.(`tool argument mismatch beyond the strip rule: ${String(toolArgumentGap.tool)}`);
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, log12, mismatchDump = false, precomputedDetails) {
9033
- const details = precomputedDetails ?? continuationMismatchDetails(entry, payload, defaults, log12, true);
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 && log12) {
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
- log12(`mismatch dump expected[${index}]: ${mismatchDumpLine(prefix, index, defaults)}`);
9042
- log12(`mismatch dump actual[${index}]: ${mismatchDumpLine(full, index, defaults)}`);
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((p13) => typeof p13 === "string")));
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 text5 = accumulator.text || (doneContent ? doneContent.filter((part) => part && typeof part === "object" && part.type === "output_text").map((part) => String(part.text ?? "")).join("") : "");
9415
- output.push({ role: "assistant", content: [{ type: "output_text", text: text5 }] });
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(([, text5]) => ({ type: "summary_text", text: text5 })) : Array.isArray(done.summary) ? done.summary : [];
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 text5 = Array.isArray(data) ? Buffer.concat(data).toString("utf8") : data.toString("utf8");
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(text5);
9740
+ event = JSON.parse(text6);
9722
9741
  } catch {
9723
- ctx.pendingEvents.push(text5.replace(/\r?\n/g, " "));
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, log12) {
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
- log12?.(`ws: ignoring ${name}=${raw} (expected a positive integer)`);
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, log12, options = {}) {
10103
+ function createResponsesWebSocketFetch(wsUrl, log13, options = {}) {
10085
10104
  const debug = (message) => {
10086
10105
  try {
10087
- log12?.(`ws: ${message}`);
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", log12) ?? RESPONSES_WS_MAX_CONNECTIONS,
10097
- maxNurseryConnections: options.maxNurseryConnections ?? envConnectionCap("CLODEX_WS_MAX_NURSERY_CONNECTIONS", log12) ?? RESPONSES_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 text5 = block.text;
10533
- return typeof text5 === "string" ? text5 : void 0;
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((p13) => p13 === param);
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 text5 = await upstreamRes.text();
11884
+ let text6 = await upstreamRes.text();
11866
11885
  let parsed;
11867
11886
  try {
11868
- parsed = JSON.parse(text5);
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) text5 = JSON.stringify(message);
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(text5).toString()
11911
+ "Content-Length": Buffer.byteLength(text6).toString()
11893
11912
  });
11894
- res.end(text5);
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 text5 = part.text ?? "";
12247
- if (!text5) return "";
12265
+ const text6 = part.text ?? "";
12266
+ if (!text6) return "";
12248
12267
  const separator = entry.text.length === 0 && this.hasText ? "\n\n" : "";
12249
- entry.text += text5;
12268
+ entry.text += text6;
12250
12269
  this.hasText = true;
12251
- return separator + text5;
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(text5, signature, npm) {
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 text5 ? [{ type: "reasoning", text: text5 }] : [];
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(text5) {
12351
- if (!text5.startsWith(CLAUDE_CODE_BILLING_HEADER_PREFIX)) return text5;
12352
- const newline = text5.indexOf("\n");
12353
- return newline === -1 ? void 0 : text5.slice(newline + 1);
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((text5) => {
12363
- const stripped = stripClaudeCodeBillingHeader(text5);
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 text5 = stripClaudeCodeBillingHeader(raw) ?? "";
12380
- if (!text5.trim()) return [];
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: text5,
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 text5 = block.thinking ?? "";
12442
- if (npm === "@ai-sdk/openai" && !block.signature && !text5.trim()) return null;
12443
- const part = { type: "reasoning", text: text5 };
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 p13 = imagePart(b);
12479
- if (p13) {
12497
+ const p14 = imagePart(b);
12498
+ if (p14) {
12480
12499
  parts.push({
12481
- ...p13,
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(text5) {
12535
- if (text5.trim() === "") return true;
12553
+ function parsesAsJson(text6) {
12554
+ if (text6.trim() === "") return true;
12536
12555
  try {
12537
- JSON.parse(text5);
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((text5) => text5.startsWith(COMPACT_TEXT_ONLY_START) && text5.includes(COMPACT_TEXT_ONLY_END));
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((text5) => !text5.startsWith(COMPACT_TEXT_ONLY_START) && COMPACT_DRIFT_OPENING.test(text5) && COMPACT_DRIFT_ANCHOR_LINE.test(text5));
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 text5 of texts) {
12603
- if (!text5.startsWith(CLAUDE_CODE_BILLING_HEADER_PREFIX)) continue;
12604
- const match = text5.match(/\bcc_version=([0-9A-Za-z][0-9A-Za-z._+-]{0,63})(?:;|\s|$)/);
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, log12) {
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
- log12?.(`possible Claude Code compact prompt drift: ${signature}`);
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, log12, observer, tools) {
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
- log12?.(() => `sdk stream error (${errorType}): ${errMsg}`);
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, log12, observer) {
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, log12, { ...observer, abortSignal }, params.tools);
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 text5;
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
- text5 = streamedText.join("");
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: text5, toolCalls, finishReason, usage, warnings } = r);
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
- ...text5 ? [{ type: "text", text: text5 }] : [],
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 p6 from "@clack/prompts";
14834
+ import * as p7 from "@clack/prompts";
14816
14835
 
14817
14836
  // src/providers-command.ts
14818
14837
  import pc6 from "picocolors";
14819
- import * as p5 from "@clack/prompts";
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 p13 of providers) {
14891
- p13.models.sort((a, b) => {
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((p13) => p13.id === provider.id);
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((p13) => p13.id === template.id);
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/registry/crud.ts
15597
- async function removeProviderWithinLifecycle(id, opts) {
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((p13) => p13.id === providerId);
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((p13) => p13.id === providerId);
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((p13) => p13.enabled);
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 p3 from "@clack/prompts";
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 spinner5 = p3.spinner();
16590
- spinner5.start("Waiting for authorization...");
16816
+ const spinner6 = p4.spinner();
16817
+ spinner6.start("Waiting for authorization...");
16591
16818
  try {
16592
16819
  const { tokens, accountId } = await runOpenAiDeviceCodeFlow(({ url, userCode }) => {
16593
- spinner5.stop("");
16594
- p3.log.info(`Visit: ${pc4.cyan(url)}`);
16595
- p3.log.info(`Enter code: ${pc4.bold(userCode)}`);
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
- spinner5.start("Waiting for authorization...");
16824
+ spinner6.start("Waiting for authorization...");
16598
16825
  });
16599
- spinner5.stop(pc4.green("Signed in to OpenAI ChatGPT"));
16826
+ spinner6.stop(pc4.green("Signed in to OpenAI ChatGPT"));
16600
16827
  return tokensToStoredCredential(tokens, void 0, accountId);
16601
16828
  } catch (err) {
16602
- spinner5.stop("");
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 spinner5 = p3.spinner();
16610
- spinner5.start("Opening your browser...");
16836
+ const spinner6 = p4.spinner();
16837
+ spinner6.start("Opening your browser...");
16611
16838
  try {
16612
16839
  const { tokens, accountId } = await runOpenAiBrowserFlow(({ url }) => {
16613
- spinner5.stop("");
16614
- p3.log.info(`If the browser did not open, visit: ${pc4.cyan(url)}`);
16840
+ spinner6.stop("");
16841
+ p4.log.info(`If the browser did not open, visit: ${pc4.cyan(url)}`);
16615
16842
  openBrowser(url);
16616
- spinner5.start("Waiting for sign-in in your browser...");
16843
+ spinner6.start("Waiting for sign-in in your browser...");
16617
16844
  });
16618
- spinner5.stop(pc4.green("Signed in to OpenAI ChatGPT"));
16845
+ spinner6.stop(pc4.green("Signed in to OpenAI ChatGPT"));
16619
16846
  return tokensToStoredCredential(tokens, void 0, accountId);
16620
16847
  } catch (err) {
16621
- spinner5.stop("");
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 = p3.spinner();
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 p4 from "@clack/prompts";
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 p4.select({
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 (p4.isCancel(picked)) return "menu";
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 p4.select({
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 (p4.isCancel(method) || String(method) === "__back__") {
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 p4.text({
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 (p4.isCancel(searchInput)) {
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
- p4.log.warn("No models match \u2014 try a different search");
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 p4.select({
17325
+ const picked = await p5.select({
17099
17326
  message,
17100
17327
  options,
17101
17328
  initialValue
17102
17329
  });
17103
- if (p4.isCancel(picked) || String(picked) === "__back__") {
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 p4.select({
17364
+ const picked = await p5.select({
17138
17365
  message: "Which model?",
17139
17366
  options,
17140
17367
  initialValue: recentModels[0].id
17141
17368
  });
17142
- if (p4.isCancel(picked) || String(picked) === "__back__") {
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 p4.confirm({
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 (p4.isCancel(confirmed) || !confirmed) {
17174
- p4.cancel("Cancelled.");
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
- p5.log.warn(CREDENTIAL_CLEANUP_PENDING_MESSAGE);
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 p5.select({
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 (p5.isCancel(choice)) {
17416
- p5.cancel("Cancelled.");
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
- p5.log.success(
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
- p5.cancel("Cancelled.");
17659
+ p6.cancel("Cancelled.");
17433
17660
  return 0;
17434
17661
  }
17435
- p5.log.error(err instanceof Error ? err.message : String(err));
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((p13) => p13.id === providerId);
17673
+ const provider = registry.providers.find((p14) => p14.id === providerId);
17447
17674
  if (!provider) {
17448
- p5.log.error(`Provider not found: ${providerId}`);
17675
+ p6.log.error(`Provider not found: ${providerId}`);
17449
17676
  return 1;
17450
17677
  }
17451
- const spinner6 = p5.spinner();
17452
- spinner6.start(`Refreshing ${provider.name}...`);
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
- spinner6.stop("");
17469
- p5.log.error(err instanceof Error ? err.message : String(err));
17695
+ spinner7.stop("");
17696
+ p6.log.error(err instanceof Error ? err.message : String(err));
17470
17697
  return 1;
17471
17698
  }
17472
- spinner6.stop("");
17699
+ spinner7.stop("");
17473
17700
  if (result.skipped) {
17474
17701
  const countNote = result.modelCount ? ` (${result.modelCount} cached models kept)` : "";
17475
- p5.log.warn(`${result.name}: ${result.reason}${countNote}`);
17702
+ p6.log.warn(`${result.name}: ${result.reason}${countNote}`);
17476
17703
  return 0;
17477
17704
  }
17478
17705
  if (!result.ok) {
17479
- p5.log.error(`${result.name}: ${result.reason ?? "Refresh failed."}`);
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
- p5.log.success(`${result.name}: ${result.modelCount} model${result.modelCount === 1 ? "" : "s"} updated${diffStr}.`);
17711
+ p6.log.success(`${result.name}: ${result.modelCount} model${result.modelCount === 1 ? "" : "s"} updated${diffStr}.`);
17485
17712
  if (result.reason) {
17486
- p5.log.warn(result.reason);
17713
+ p6.log.warn(result.reason);
17487
17714
  }
17488
17715
  return 0;
17489
17716
  }
17490
- const spinner5 = p5.spinner();
17491
- spinner5.start("Refreshing model lists...");
17717
+ const spinner6 = p6.spinner();
17718
+ spinner6.start("Refreshing model lists...");
17492
17719
  const { refreshed } = await refreshAllProviderModels(resolveKey);
17493
- spinner5.stop("");
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
- p5.log.success(`Updated ${ok.length} provider${ok.length === 1 ? "" : "s"}.`);
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
- p5.log.info(` ${r.name}: ${r.modelCount} model${r.modelCount === 1 ? "" : "s"}${diffStr}`);
17729
+ p6.log.info(` ${r.name}: ${r.modelCount} model${r.modelCount === 1 ? "" : "s"}${diffStr}`);
17503
17730
  if (r.reason) {
17504
- p5.log.warn(` ${r.reason}`);
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
- p5.log.warn(`Skipped ${r.name}: ${r.reason}${countNote}`);
17737
+ p6.log.warn(`Skipped ${r.name}: ${r.reason}${countNote}`);
17511
17738
  }
17512
17739
  for (const r of failed) {
17513
- p5.log.error(`${r.name}: ${r.reason ?? "Refresh failed."}`);
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
- p5.log.info("No providers configured. Run clodex providers add or clodex providers auth openai.");
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
- p5.log.info(known ? `${known.name} is already configured.` : `Unknown provider template: ${templateId}`);
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 p5.password({
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 (p5.isCancel(apiKeyInput)) {
17554
- p5.cancel("Cancelled.");
17780
+ if (p6.isCancel(apiKeyInput)) {
17781
+ p6.cancel("Cancelled.");
17555
17782
  return 0;
17556
17783
  }
17557
17784
  const apiKey = String(apiKeyInput).trim();
17558
- const spinner5 = p5.spinner();
17559
- spinner5.start(`Testing connection to ${template.name}...`);
17785
+ const spinner6 = p6.spinner();
17786
+ spinner6.start(`Testing connection to ${template.name}...`);
17560
17787
  const result = await addProviderFromTemplate(template, apiKey);
17561
- spinner5.stop("");
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
- p5.log.error(result.error ?? "Could not add provider.");
17569
- if (result.hint) p5.log.info(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
- if (options.length === 0) {
17594
- p5.log.info("All built-in providers are already configured.");
17595
- return 0;
17596
- }
17597
- const choice = await p5.select({
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 (p5.isCancel(choice)) {
17602
- p5.cancel("Cancelled.");
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
- p5.log.error(`Provider not found: ${id}`);
17857
+ p6.log.error(`Provider not found: ${id}`);
17623
17858
  return 1;
17624
17859
  }
17625
17860
  if (interactive) {
17626
- const confirm6 = await p5.confirm({
17861
+ const confirm7 = await p6.confirm({
17627
17862
  message: `Remove ${provider.name} (${id})?`,
17628
17863
  initialValue: false
17629
17864
  });
17630
- if (p5.isCancel(confirm6) || !confirm6) {
17631
- p5.cancel("Cancelled.");
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
- p5.log.error(result.error ?? `Could not remove ${id}`);
17877
+ p6.log.error(result.error ?? `Could not remove ${id}`);
17643
17878
  return 1;
17644
17879
  }
17645
17880
  if (result.error) {
17646
- p5.log.error(result.error);
17881
+ p6.log.error(result.error);
17647
17882
  return 1;
17648
17883
  }
17649
- p5.log.success(`Removed ${result.name ?? id}.`);
17884
+ p6.log.success(`Removed ${result.name ?? id}.`);
17650
17885
  if (result.credentialDeleted) {
17651
- p5.log.info("Provider API key removed from the credential store.");
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 p5.select({
17961
+ const action = await p6.select({
17727
17962
  message: "What would you like to do?",
17728
17963
  options: detailOptions
17729
17964
  });
17730
- if (p5.isCancel(action) || action === "back") return "back";
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 p5.select({
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 (p5.isCancel(chosen)) return "back";
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
- p5.log.error(result.error ?? "Could not switch account.");
18012
+ p6.log.error(result.error ?? "Could not switch account.");
17778
18013
  return "back";
17779
18014
  }
17780
18015
  if (!result.provider) {
17781
- p5.log.error("Account selection was saved, but the resulting provider state could not be read.");
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
- p5.log.error("Account selection was saved, but the resulting provider state could not be read.");
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
- p5.log.warn(
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
- p5.log.success(outcome.message);
18041
+ p6.log.success(outcome.message);
17807
18042
  } else {
17808
- p5.log.warn(outcome.message);
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) p5.log.warn(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
- p5.log.success(`${provider.name} ${result.enabled ? "enabled" : "disabled"}.`);
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 p5.select({
18092
+ const choice = await p6.select({
17858
18093
  message: entries.length > 0 ? "Your providers" : "Get started",
17859
18094
  options
17860
18095
  });
17861
- if (p5.isCancel(choice) || choice === "done") {
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 p5.text({
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 (p5.isCancel(name)) continue;
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
- p5.log.error(parsed.error);
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 p6.select({
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 (p6.isCancel(choice)) {
17960
- p6.cancel("Cancelled.");
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
- p6.log.success("OpenAI provider ready \u2014 picking a model next.");
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 p10 from "@clack/prompts";
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 p7 from "@clack/prompts";
18253
+ import * as p8 from "@clack/prompts";
18019
18254
  import pc8 from "picocolors";
18020
18255
  async function askServerStartMode() {
18021
- const mode = await p7.select({
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 (p7.isCancel(mode)) {
18030
- p7.cancel("Cancelled.");
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 p7.confirm({
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 (p7.isCancel(mask)) {
18041
- p7.cancel("Cancelled.");
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 p7.confirm({
18283
+ const favoritesOnly = await p8.confirm({
18049
18284
  message: "Expose only favorite models?",
18050
18285
  initialValue
18051
18286
  });
18052
- if (p7.isCancel(favoritesOnly)) {
18053
- p7.cancel("Cancelled.");
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 p7.select({
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 (p7.isCancel(mode)) {
18068
- p7.cancel("Cancelled.");
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 password2 = await p7.text({
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 (p7.isCancel(password2)) {
18080
- p7.cancel("Cancelled.");
18314
+ if (p8.isCancel(password3)) {
18315
+ p8.cancel("Cancelled.");
18081
18316
  return null;
18082
18317
  }
18083
- return String(password2).trim();
18318
+ return String(password3).trim();
18084
18319
  }
18085
18320
  async function askUseSavedServerPassword() {
18086
- const choice = await p7.select({
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 (p7.isCancel(choice)) {
18095
- p7.cancel("Cancelled.");
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 p7.confirm({
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 (p7.isCancel(save)) {
18106
- p7.cancel("Cancelled.");
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 p13 = part;
18221
- switch (p13.type) {
18455
+ const p14 = part;
18456
+ switch (p14.type) {
18222
18457
  case "text-delta":
18223
- collected.text += p13.textDelta ?? p13.text ?? "";
18458
+ collected.text += p14.textDelta ?? p14.text ?? "";
18224
18459
  break;
18225
18460
  case "tool-call":
18226
18461
  collected.toolCalls.push({
18227
- toolCallId: p13.toolCallId ?? "",
18228
- toolName: p13.toolName ?? "",
18229
- input: p13.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 = p13.finishReason ?? collected.finishReason;
18234
- collected.usage = p13.totalUsage ?? p13.usage ?? 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 p13.error instanceof Error || p13.error && typeof p13.error === "object" ? p13.error : new Error(typeof p13.error === "string" ? p13.error : "Upstream stream failed");
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 p13 = part;
18363
- switch (p13.type) {
18597
+ const p14 = part;
18598
+ switch (p14.type) {
18364
18599
  case "text-delta":
18365
- send({ role: "assistant", content: p13.textDelta ?? p13.text ?? "" });
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: p13.id ?? p13.toolCallId, type: "function", function: { name: p13.toolName, arguments: "" } }] });
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: p13.delta ?? p13.text ?? p13.argsTextDelta ?? "" } }] });
18606
+ send({ tool_calls: [{ index: 0, function: { arguments: p14.delta ?? p14.text ?? p14.argsTextDelta ?? "" } }] });
18372
18607
  break;
18373
18608
  case "finish":
18374
- send({}, p13.finishReason || "stop");
18609
+ send({}, p14.finishReason || "stop");
18375
18610
  break;
18376
18611
  case "error":
18377
- throw p13.error instanceof Error || p13.error && typeof p13.error === "object" ? p13.error : new Error(typeof p13.error === "string" ? p13.error : "Upstream stream failed");
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 p8 from "@clack/prompts";
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
- p8.log.warn("No providers available to expose.");
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 p8.select({
19382
+ const choice = await p9.select({
19148
19383
  message: header,
19149
19384
  options,
19150
19385
  initialValue: "__done__"
19151
19386
  });
19152
- if (p8.isCancel(choice) || choice === "__done__") {
19387
+ if (p9.isCancel(choice) || choice === "__done__") {
19153
19388
  if (selected.length === 0) {
19154
- p8.log.warn("Select at least one provider to expose.");
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
- p8.log.success(`Exposing all ${available.length} providers.`);
19396
+ p9.log.success(`Exposing all ${available.length} providers.`);
19162
19397
  continue;
19163
19398
  }
19164
19399
  if (choice === "__clear__") {
19165
19400
  selected = [];
19166
- p8.log.success("Cleared provider list \u2014 add the ones you want to expose.");
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 p8.select({
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 (p8.isCancel(picked)) continue;
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
- p8.log.success(`Removed ${provider?.name ?? id}.`);
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 p9 from "@clack/prompts";
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
- p9.log.warn(`${loaded.unavailable.length} favorite${loaded.unavailable.length === 1 ? "" : "s"} unavailable or missing credentials.`);
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
- p9.log.warn(
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
- p9.log.warn(
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
- p9.log.error(`Failed to start HTTP proxy: ${err instanceof Error ? err.message : String(err)}`);
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
- p10.log.error("Network server quick-start needs a saved server password or `--password <value>`.");
20651
- p10.log.info("Run `clodex server` and choose Configure & start to save one, or pass a one-run password.");
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
- p10.log.info("Add providers to expose. Listed providers are removed when selected \u2014 like favorites.");
20686
- const spinner5 = p10.spinner();
20687
- spinner5.start("Loading providers...");
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
- spinner5.stop("");
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
- p10.log.success(`Saved ${picked.length} provider${picked.length !== 1 ? "s" : ""} for future server runs.`);
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
- p10.log.info("Manage favorites with `clodex models`.");
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
- p10.log.error("--proxy is a local-only server mode and cannot be combined with endpoint-mode server options.");
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
- p10.log.error("No providers configured. Run `clodex providers` to add OpenAI.");
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 spinner5 = p10.spinner();
20761
- spinner5.start("Fetching available models...");
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
- spinner5.stop(pc11.red("No favorite models configured"));
20772
- p10.log.error("Run `clodex models` to add favorites, or turn off favorites-only in the server wizard.");
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
- spinner5.stop(pc11.red("No favorite models matched the current provider filter"));
20778
- p10.log.error("Adjust favorites with `clodex models` or change exposed providers in the server wizard.");
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
- p10.log.info(
21018
+ p11.log.info(
20784
21019
  `Favorites-only mode active \u2014 GET /anthropic/v1/models returns ${models.length} favorites.`
20785
21020
  );
20786
- p10.log.info("Desktop/Cowork picker will only show these. Edit with `clodex models`.");
21021
+ p11.log.info("Desktop/Cowork picker will only show these. Edit with `clodex models`.");
20787
21022
  }
20788
21023
  if (models.length === 0) {
20789
- spinner5.stop(pc11.red("No models to expose"));
20790
- p10.log.error("Add providers with `clodex providers add` or configure exposed providers in the server wizard.");
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
- spinner5.stop(`Loaded ${models.length} models (${localCount} from registry providers)${filterNote}${favoritesNote}${maskNote}`);
20799
- if (summary) p10.log.info(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
- spinner5.stop(pc11.red("Failed to load models"));
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
- p10.log.warn(
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 p11 from "@clack/prompts";
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 p11.text({
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 (p11.isCancel(searchInput)) {
20988
- const fallback = await p11.select({
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 (p11.isCancel(fallback) || fallback === "back") return null;
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
- p11.log.warn("No models match \u2014 try a different search");
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
- p11.log.warn(`${picked.model.name || picked.model.id} (${picked.providerName}) is already in your favorites.`);
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((p13) => p13.id === target.providerId);
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((text5) => text5.trim()).filter(Boolean).join("\n");
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(text5) {
22166
+ function printHelp(text6) {
21931
22167
  console.log(`
21932
- ${text5}
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
- p12.log.warn(
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
- p12.log.info(
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
- p12.log.error(`Failed to start proxy: ${err instanceof Error ? err.message : String(err)}`);
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) p12.log.info(`Debug log: ${debugLogPath}`);
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) => p12.log.info(message),
21988
- warn: (message) => p12.log.warn(message),
21989
- success: (message) => p12.log.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
- p12.log.error(
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
- p12.log.error("--alias/--unalias apply one at a time to proxy-mode favorites.");
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
- p12.log.error("--save applies to --context assignments; none were given.");
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
- p12.log.error(parsed.error);
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
- p12.log.error(`${modelAliasTarget(parsed)} is not a saved favorite.`);
22071
- p12.log.info("Add it with `clodex models`, then save the alias.");
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
- p12.log.error('Saved model aliases are malformed: "modelAliases" must be an array.');
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
- p12.log.success(`Saved model alias ${parsed.name} \u2192 ${modelAliasTarget(parsed)}.`);
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
- p12.log.error('Saved model aliases are malformed: "modelAliases" must be an array.');
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
- p12.log.error(`No model alias named ${JSON.stringify(requestedName)} is saved.`);
22333
+ p13.log.error(`No model alias named ${JSON.stringify(requestedName)} is saved.`);
22098
22334
  return 1;
22099
22335
  }
22100
22336
  savePreferences({ modelAliases });
22101
- p12.log.success(
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
- p12.log.error(`Could not load proxy models: ${err instanceof Error ? err.message : String(err)}`);
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 spinner5 = p12.spinner();
22121
- spinner5.start("Loading providers...");
22356
+ const spinner6 = p13.spinner();
22357
+ spinner6.start("Loading providers...");
22122
22358
  const catalog = await fetchProviderCatalog();
22123
- spinner5.stop("");
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
- p12.log.warn("No providers found.");
22131
- p12.log.info(`${pc13.dim("Add a provider with ")}${pc13.cyan("clodex providers")}${pc13.dim(".")}`);
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 p12.select({
22396
+ const choice = await p13.select({
22161
22397
  message: header,
22162
22398
  options,
22163
22399
  initialValue: "__done__"
22164
22400
  });
22165
- if (p12.isCancel(choice) || choice === "__done__") break;
22401
+ if (p13.isCancel(choice) || choice === "__done__") break;
22166
22402
  if (choice === "__add__") {
22167
22403
  if (atCap) {
22168
- p12.log.warn(`Limit of ${maxFavorites} favorites reached \u2014 remove one first.`);
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 p12.select({
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 (p12.isCancel(addPath)) continue;
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 p12.select({
22438
+ const pickedProviderId = await p13.select({
22203
22439
  message: "Which provider?",
22204
22440
  options: providerOptions,
22205
22441
  initialValue: currentInitialProvider
22206
22442
  });
22207
- if (p12.isCancel(pickedProviderId)) break;
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 p12.multiselect({
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 (p12.isCancel(pickedModelIds)) {
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
- p12.log.success(`Added ${modelName} (${provider.name}) to favorites.`);
22494
+ p13.log.success(`Added ${modelName} (${provider.name}) to favorites.`);
22259
22495
  } else {
22260
- p12.log.success(`Added ${addedModels.length} models from ${provider.name} to favorites.`);
22496
+ p13.log.success(`Added ${addedModels.length} models from ${provider.name} to favorites.`);
22261
22497
  }
22262
22498
  }
22263
22499
  if (duplicateCount > 0) {
22264
- p12.log.warn(`${duplicateCount} selected model(s) were already in your favorites.`);
22500
+ p13.log.warn(`${duplicateCount} selected model(s) were already in your favorites.`);
22265
22501
  }
22266
22502
  if (limitReached) {
22267
- p12.log.warn(`Limit of ${maxFavorites} favorites reached \u2014 some selected models could not be added.`);
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 p12.confirm({ message: `Remove ${label} from favorites?` });
22275
- if (p12.isCancel(confirmed) || !confirmed) continue;
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
- p12.log.success(`Removed ${label} from favorites.`);
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
- p12.log.error("--provider/--model select endpoint-mode routes and cannot be combined with --proxy.");
22293
- p12.log.info("Use `-- --model clodex:<provider-id>:<model-id>` to start on a listed proxy-mode favorite.");
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
- p12.log.error(`Could not load proxy models: ${err instanceof Error ? err.message : String(err)}`);
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
- p12.log.error(`Failed to start proxy: ${err instanceof Error ? err.message : String(err)}`);
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 p12.log.warn(caWarning);
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
- p12.log.info(`Proxy started on port ${handle.port}; Claude Code's Anthropic auth remains active.`);
22362
- p12.log.info(`Inference request log: ${handle.inferenceLogPath}`);
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
- p12.log.info("Switch with `/model <listed-name>`.");
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
- p12.log.info(`Claude debug log: ${debugLogPath}`);
22374
- if (proxyDebugLogPath) p12.log.info(`Adapter debug log: ${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 = p12.spinner();
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
- p12.log.error(blockedLaunchReason);
22710
+ p13.log.error(blockedLaunchReason);
22475
22711
  return 1;
22476
22712
  }
22477
- p12.log.warn("No providers available.");
22478
- p12.log.info(pc13.dim("Run clodex providers to get started."));
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
- p12.log.error(
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
- p12.log.step(`Using ${selectedModel.name || selectedModel.id} (${activeProvider.name})`);
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 p12.select({
22745
+ const chosen = await p13.select({
22510
22746
  message: "Which provider?",
22511
22747
  options: providerOptions,
22512
22748
  initialValue: currentInitialProvider
22513
22749
  });
22514
- if (p12.isCancel(chosen)) {
22515
- p12.cancel("Cancelled.");
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
- p12.log.warn("No saved favorites are currently available.");
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 p12.select({
22771
+ const pickedIdx = await p13.select({
22536
22772
  message: "Starting model?",
22537
22773
  options: favOptions,
22538
22774
  initialValue: "0"
22539
22775
  });
22540
- if (p12.isCancel(pickedIdx)) {
22541
- p12.cancel("Cancelled.");
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
- p12.log.error("Could not resolve a proxy route for the selected model.");
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
- p12.log.warn(
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
- p12.log.error(
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()) p12.log.info(`Anthropic proxy started on port ${proxyHandle.port}`);
22891
+ if (!isAgentStdoutMode()) p13.log.info(`Anthropic proxy started on port ${proxyHandle.port}`);
22656
22892
  } catch (err) {
22657
- p12.log.error(`Failed to start Anthropic proxy: ${err instanceof Error ? err.message : String(err)}`);
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
- p12.log.info(
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
- p12.log.error(`Failed to start SDK adapter proxy: ${err instanceof Error ? err.message : String(err)}`);
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) p12.log.info(`Debug log: ${debugLogPath}`);
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),