@bman654/clodex 2.14.0 → 2.15.0

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