claudish 7.44.0 → 7.46.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.
Files changed (2) hide show
  1. package/dist/index.js +378 -218
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -729,7 +729,7 @@ var init_onepassword_config = __esm(() => {
729
729
  });
730
730
 
731
731
  // src/version.ts
732
- var VERSION = "7.44.0";
732
+ var VERSION = "7.46.0";
733
733
 
734
734
  // src/logger.ts
735
735
  var exports_logger = {};
@@ -1219,11 +1219,6 @@ function writeJsonlCapped(payload) {
1219
1219
  `);
1220
1220
  }
1221
1221
  }
1222
- function printSlowLine(payload) {
1223
- const top = [...payload.spans].sort((a, b) => b.durMs - a.durMs).slice(0, 3).map((s) => `${s.name} ${fmtDur(s.durMs)}${fmtWaitExec(s.meta)}`);
1224
- const detail = top.length > 0 ? ` \u2014 ${top.join(", ")}` : "";
1225
- seams.stderr(`[claudish] slow start ${fmtDur(payload.totalMs)}${detail} \u2026 full data: ` + `${displayPath(seams.outPath)} (CLAUDISH_STARTUP_TRACE=1 for live detail)`);
1226
- }
1227
1222
  function printTable(payload) {
1228
1223
  seams.stderr(`[claudish] startup trace (${payload.argvKind}) \u2014 total ${fmtDur(payload.totalMs)}` + ` \xB7 auth ${payload.authKind} \xB7 v${payload.version}`);
1229
1224
  seams.stderr(" start dur span");
@@ -1234,7 +1229,7 @@ function printTable(payload) {
1234
1229
  }
1235
1230
  seams.stderr(` metrics: ${displayPath(seams.outPath)}`);
1236
1231
  }
1237
- function finalizeStartupTrace(context, opts = {}) {
1232
+ function finalizeStartupTrace(context, _opts = {}) {
1238
1233
  try {
1239
1234
  if (finalized)
1240
1235
  return;
@@ -1254,8 +1249,6 @@ function finalizeStartupTrace(context, opts = {}) {
1254
1249
  try {
1255
1250
  if (terminalSuppressed) {} else if (traceModeOn()) {
1256
1251
  printTable(payload);
1257
- } else if (totalMs > seams.slowThresholdMs && !opts.quiet) {
1258
- printSlowLine(payload);
1259
1252
  }
1260
1253
  } catch {}
1261
1254
  } catch {}
@@ -1345,6 +1338,20 @@ function isAbandoned(path) {
1345
1338
  return true;
1346
1339
  return Date.now() - holder.at > timing.staleMs;
1347
1340
  }
1341
+ function peerHoldsHandshakeLock() {
1342
+ if (lastHandshake?.heldByUs)
1343
+ return false;
1344
+ const holder = readHolder(currentLockPath());
1345
+ if (!holder || holder.pid === process.pid)
1346
+ return false;
1347
+ if (!holderAlive(holder.pid))
1348
+ return false;
1349
+ if (Date.now() - holder.at > timing.staleMs)
1350
+ return false;
1351
+ if (lastHandshake && holder.at > lastHandshake.startedAt)
1352
+ return false;
1353
+ return true;
1354
+ }
1348
1355
  async function acquire(path) {
1349
1356
  const deadline = Date.now() + timing.timeoutMs;
1350
1357
  let madeDir = false;
@@ -1385,6 +1392,7 @@ function release(path) {
1385
1392
  async function withHandshakeLock(handshake) {
1386
1393
  if (process.env.CLAUDISH_NO_OP_HANDSHAKE_LOCK === "1") {
1387
1394
  trace("bypassed (CLAUDISH_NO_OP_HANDSHAKE_LOCK=1)");
1395
+ lastHandshake = { heldByUs: false, startedAt: Date.now() };
1388
1396
  return handshake();
1389
1397
  }
1390
1398
  const path = currentLockPath();
@@ -1396,6 +1404,7 @@ async function withHandshakeLock(handshake) {
1396
1404
  held = false;
1397
1405
  }
1398
1406
  trace(`${held ? "acquired" : "NOT held (timeout or unwritable)"} after ${Date.now() - t0}ms`);
1407
+ lastHandshake = { heldByUs: held, startedAt: t0 };
1399
1408
  try {
1400
1409
  return await handshake();
1401
1410
  } finally {
@@ -1404,7 +1413,7 @@ async function withHandshakeLock(handshake) {
1404
1413
  trace(`handshake done after ${Date.now() - t0}ms${held ? ", released" : ""}`);
1405
1414
  }
1406
1415
  }
1407
- var DEFAULT_STALE_MS = 120000, DEFAULT_TIMEOUT_MS = 45000, DEFAULT_POLL_MS = 60, timing, lockPath, sleep = (ms) => new Promise((r) => setTimeout(r, ms));
1416
+ var DEFAULT_STALE_MS = 120000, DEFAULT_TIMEOUT_MS = 45000, DEFAULT_POLL_MS = 60, timing, lockPath, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), lastHandshake;
1408
1417
  var init_onepassword_handshake_lock = __esm(() => {
1409
1418
  timing = {
1410
1419
  staleMs: DEFAULT_STALE_MS,
@@ -3897,6 +3906,7 @@ __export(exports_onepassword, {
3897
3906
  wasOpAuthorizationDenied: () => wasOpAuthorizationDenied,
3898
3907
  valueTail: () => valueTail,
3899
3908
  setScreenLockProbe: () => setScreenLockProbe,
3909
+ setPeerLockProbe: () => setPeerLockProbe,
3900
3910
  setLockRetryTiming: () => setLockRetryTiming,
3901
3911
  setAppLockProbe: () => setAppLockProbe,
3902
3912
  resolveSecretsPartial: () => resolveSecretsPartial,
@@ -3920,11 +3930,13 @@ __export(exports_onepassword, {
3920
3930
  listItems: () => listItems,
3921
3931
  isTransientSdkError: () => isTransientSdkError,
3922
3932
  isScreenLocked: () => isScreenLocked,
3933
+ isPeerHoldingPrompt: () => isPeerHoldingPrompt,
3923
3934
  isOpReference: () => isOpReference,
3924
3935
  isOpHydratedVar: () => isOpHydratedVar,
3925
3936
  isLockedDenial: () => isLockedDenial,
3926
3937
  isGlobImport: () => isGlobImport,
3927
3938
  isAppLocked: () => isAppLocked,
3939
+ humanizeOpError: () => humanizeOpError,
3928
3940
  globToRegExp: () => globToRegExp,
3929
3941
  getOpFailures: () => getOpFailures,
3930
3942
  filterGlobFields: () => filterGlobFields,
@@ -3938,6 +3950,7 @@ __export(exports_onepassword, {
3938
3950
  defaultAppLockProbe: () => defaultAppLockProbe,
3939
3951
  currentLockCause: () => currentLockCause,
3940
3952
  collectConfigImports: () => collectConfigImports,
3953
+ clearOpSkip: () => clearOpSkip,
3941
3954
  classifyLockedDenial: () => classifyLockedDenial,
3942
3955
  buildAuthError: () => buildAuthError,
3943
3956
  appLockedFromSettings: () => appLockedFromSettings,
@@ -3971,6 +3984,21 @@ function resetOpFailures() {
3971
3984
  function wasOpAuthorizationDenied() {
3972
3985
  return opSourceFailures.some((f) => /denied authorization/i.test(f.message));
3973
3986
  }
3987
+ function humanizeOpError(err) {
3988
+ const raw = (err instanceof Error ? err.message : String(err)).trim();
3989
+ if (/denied authorization/i.test(raw)) {
3990
+ const cause = currentLockCause();
3991
+ if (cause === "screen")
3992
+ return "your Mac is locked, so the approval prompt couldn't be shown";
3993
+ if (cause === "app")
3994
+ return "the 1Password app is locked, so it declined without prompting";
3995
+ if (cause === "peer")
3996
+ return "another claudish process is holding the 1Password prompt";
3997
+ return "the 1Password approval prompt was dismissed (or never answered)";
3998
+ }
3999
+ const unwrapped = raw.match(/Error\s*\{\s*msg:\s*(.*?)(?:,\s*inner:|\s*\})/s);
4000
+ return (unwrapped?.[1] ?? raw).trim();
4001
+ }
3974
4002
  function renderOpFailureNotice(envVar) {
3975
4003
  return renderOpFailureBlock(`for ${envVar} this run`);
3976
4004
  }
@@ -3997,6 +4025,12 @@ function renderOpFailureBlock(subject) {
3997
4025
  lines.push("");
3998
4026
  lines.push(" Fix: unlock 1Password (Touch ID is enough) and re-run.");
3999
4027
  lines.push(" To stop it re-locking mid-session, raise Settings \u2192 Security \u2192 auto-lock.");
4028
+ } else if (cause === "peer") {
4029
+ lines.push(" Another claudish process is at the 1Password prompt right now. 1Password");
4030
+ lines.push(" authorizes ONE client at a time and denies every peer instantly, so this");
4031
+ lines.push(" run lost a race rather than being refused.");
4032
+ lines.push("");
4033
+ lines.push(" Fix: approve the prompt in the other window, then re-run this one.");
4000
4034
  } else {
4001
4035
  lines.push(" The 1Password desktop app declined to release secrets. The approval prompt");
4002
4036
  lines.push(" was most likely dismissed.");
@@ -4373,11 +4407,19 @@ function setAppLockProbe(probe) {
4373
4407
  function isAppLocked() {
4374
4408
  return appLockProbe();
4375
4409
  }
4410
+ function setPeerLockProbe(probe) {
4411
+ peerLockProbe = probe ?? peerHoldsHandshakeLock;
4412
+ }
4413
+ function isPeerHoldingPrompt() {
4414
+ return peerLockProbe();
4415
+ }
4376
4416
  function currentLockCause() {
4377
4417
  if (isScreenLocked())
4378
4418
  return "screen";
4379
4419
  if (isAppLocked())
4380
4420
  return "app";
4421
+ if (isPeerHoldingPrompt())
4422
+ return "peer";
4381
4423
  return null;
4382
4424
  }
4383
4425
  function classifyLockedDenial(err, env = process.env) {
@@ -4395,23 +4437,28 @@ async function countdownForUnlock(round, rounds, cause) {
4395
4437
  const ttyOut = process.stderr.isTTY === true;
4396
4438
  const ttyIn = process.stdin.isTTY === true;
4397
4439
  let cancelled = false;
4440
+ let skipped = false;
4398
4441
  if (round === 1) {
4399
4442
  const explain = cause === "screen" ? `${bold("\uD83D\uDD10 1Password needs your OK \u2014 but your Mac is locked, so it can't ask.")}
4400
- Unlock your Mac and approve the popup. Claudish picks it up from there.` : `${bold("\uD83D\uDD10 1Password is locked, so it turned claudish away without asking you.")}
4443
+ Unlock your Mac and approve the popup. Claudish picks it up from there.` : cause === "app" ? `${bold("\uD83D\uDD10 1Password is locked, so it turned claudish away without asking you.")}
4401
4444
  Unlock 1Password (Touch ID is enough). Claudish retries on its own \u2014
4402
- no popup will appear until it's unlocked.`;
4445
+ no popup will appear until it's unlocked.` : `${bold("\uD83D\uDD10 Another claudish is already at the 1Password prompt.")}
4446
+ 1Password only lets one through at a time, so this run has to queue.
4447
+ Approve it in the other window (or wait \u2014 it usually takes a second).`;
4403
4448
  process.stderr.write(`
4404
4449
  ${explain}
4405
4450
 
4406
4451
  `);
4407
4452
  }
4408
- const stillLocked = () => cause === "screen" ? isScreenLocked() : isAppLocked();
4453
+ const stillLocked = () => cause === "screen" ? isScreenLocked() : cause === "app" ? isAppLocked() : isPeerHoldingPrompt();
4409
4454
  let restoreInput = () => {};
4410
4455
  if (ttyIn) {
4411
4456
  const onKey = (buf) => {
4412
4457
  const k = buf.toString();
4413
4458
  if (k === "\x1B" || k === "q" || k === "\x03")
4414
4459
  cancelled = true;
4460
+ if (k === "s" || k === "S")
4461
+ skipped = true;
4415
4462
  };
4416
4463
  const wasRaw = process.stdin.isRaw === true;
4417
4464
  try {
@@ -4427,14 +4474,20 @@ ${explain}
4427
4474
  restoreInput = () => {};
4428
4475
  }
4429
4476
  }
4430
- const stop = ttyIn ? "Esc to stop waiting" : "Ctrl-C to stop waiting";
4431
- const line = (secs) => dim(` waiting ${secs}s \xB7 try ${round} of ${rounds} \xB7 ${stop}`);
4477
+ const stop = ttyIn ? "Esc stop waiting" : "Ctrl-C stop waiting";
4478
+ const skip = ttyIn ? " \xB7 s skip 1Password" : "";
4479
+ const bar = (secs) => {
4480
+ const total = Math.max(1, lockRetrySeconds);
4481
+ const filled = Math.round((total - secs) / total * 10);
4482
+ return `${"\u2501".repeat(filled)}${"\u2500".repeat(Math.max(0, 10 - filled))}`;
4483
+ };
4484
+ const line = (secs) => dim(` ${bar(secs)} ${secs}s \xB7 try ${round}/${rounds} \xB7 ${stop}${skip}`);
4432
4485
  if (!ttyOut)
4433
4486
  process.stderr.write(`${line(lockRetrySeconds)}
4434
4487
  `);
4435
4488
  try {
4436
4489
  for (let remaining = lockRetrySeconds;remaining > 0; remaining--) {
4437
- if (cancelled)
4490
+ if (cancelled || skipped)
4438
4491
  break;
4439
4492
  if (!stillLocked())
4440
4493
  break;
@@ -4447,6 +4500,12 @@ ${explain}
4447
4500
  if (ttyOut)
4448
4501
  process.stderr.write("\r\x1B[2K");
4449
4502
  }
4503
+ if (skipped) {
4504
+ process.stderr.write(` Skipping 1Password for this run. Keys already in your environment still work;
4505
+ ` + ` anything only 1Password has will report as missing.
4506
+ `);
4507
+ return "skip";
4508
+ }
4450
4509
  if (cancelled) {
4451
4510
  process.stderr.write(` OK, not waiting. You can unlock and re-run any time.
4452
4511
  `);
@@ -4454,6 +4513,17 @@ ${explain}
4454
4513
  }
4455
4514
  return "retry";
4456
4515
  }
4516
+ function latchOpSkip() {
4517
+ opSkipLatchedByPrompt = true;
4518
+ process.env.CLAUDISH_DISABLE_OP = "1";
4519
+ }
4520
+ function clearOpSkip() {
4521
+ if (!opSkipLatchedByPrompt)
4522
+ return false;
4523
+ opSkipLatchedByPrompt = false;
4524
+ delete process.env.CLAUDISH_DISABLE_OP;
4525
+ return true;
4526
+ }
4457
4527
  async function withSdkRetry(op, label = "op:sdk-op") {
4458
4528
  for (let round = 1;; round++) {
4459
4529
  try {
@@ -4462,7 +4532,12 @@ async function withSdkRetry(op, label = "op:sdk-op") {
4462
4532
  const cause = classifyLockedDenial(err);
4463
4533
  if (round > LOCK_RETRY_ROUNDS || cause === null)
4464
4534
  throw err;
4465
- if (await countdownForUnlock(round, LOCK_RETRY_ROUNDS, cause) === "cancel")
4535
+ const choice = await countdownForUnlock(round, LOCK_RETRY_ROUNDS, cause);
4536
+ if (choice === "skip") {
4537
+ latchOpSkip();
4538
+ throw err;
4539
+ }
4540
+ if (choice === "cancel")
4466
4541
  throw err;
4467
4542
  resetSdkClientCache();
4468
4543
  }
@@ -4680,7 +4755,7 @@ var OP_REF_RE, opHydratedVars, opSourceFailures, ENV_VAR_NAME_RE, sdkClientCache
4680
4755
  } catch {
4681
4756
  return false;
4682
4757
  }
4683
- }, screenLockProbe, defaultAppLockProbe = () => false, appLockProbe, OP_PROBE_TIMEOUT_MS = 5000, defaultOpAccountLister = () => {
4758
+ }, screenLockProbe, defaultAppLockProbe = () => false, appLockProbe, peerLockProbe, opSkipLatchedByPrompt = false, OP_PROBE_TIMEOUT_MS = 5000, defaultOpAccountLister = () => {
4684
4759
  try {
4685
4760
  const res = spawnSync("op", ["--cache=false", "account", "list", "--format=json"], {
4686
4761
  encoding: "utf-8",
@@ -4723,6 +4798,7 @@ var init_onepassword = __esm(() => {
4723
4798
  lockRetrySeconds = LOCK_RETRY_SECONDS;
4724
4799
  screenLockProbe = defaultScreenLockProbe;
4725
4800
  appLockProbe = defaultAppLockProbe;
4801
+ peerLockProbe = peerHoldsHandshakeLock;
4726
4802
  });
4727
4803
 
4728
4804
  // src/auth/credentials/op-source.ts
@@ -4859,6 +4935,8 @@ function readConfigRaw() {
4859
4935
  }
4860
4936
  }
4861
4937
  function hasOpSources() {
4938
+ if (process.env.CLAUDISH_DISABLE_OP === "1")
4939
+ return false;
4862
4940
  if (sniffed !== undefined)
4863
4941
  return sniffed;
4864
4942
  sniffed = computeHasOpSources();
@@ -5137,7 +5215,8 @@ async function resolveOpKeyForEnvVarsInner(wanted, opts = {}, span) {
5137
5215
  resolveSecrets: resolveSecrets2,
5138
5216
  recordOpHydratedVars: recordOpHydratedVars2,
5139
5217
  recordOpFailure: recordOpFailure2,
5140
- withSdkRetry: withSdkRetry2
5218
+ withSdkRetry: withSdkRetry2,
5219
+ humanizeOpError: humanizeOpError2
5141
5220
  } = await Promise.resolve().then(() => (init_onepassword(), exports_onepassword));
5142
5221
  const cfg = readConfigRaw();
5143
5222
  const out = {};
@@ -5198,7 +5277,7 @@ async function resolveOpKeyForEnvVarsInner(wanted, opts = {}, span) {
5198
5277
  }
5199
5278
  } catch (globErr) {
5200
5279
  const m = globErr instanceof Error ? globErr.message : String(globErr);
5201
- warnOnce(`[claudish] 1Password import skipped: ${m}`);
5280
+ warnOnce(`[claudish] 1Password import skipped: ${humanizeOpError2(globErr)}`);
5202
5281
  recordOpFailure2({ kind: "import", source: globPath, message: m });
5203
5282
  }
5204
5283
  }
@@ -5239,7 +5318,7 @@ async function resolveOpKeyForEnvVarsInner(wanted, opts = {}, span) {
5239
5318
  }
5240
5319
  } catch (envErr) {
5241
5320
  const m = envErr instanceof Error ? envErr.message : String(envErr);
5242
- warnOnce(`[claudish] 1Password environment skipped: ${m}`);
5321
+ warnOnce(`[claudish] 1Password environment skipped: ${humanizeOpError2(envErr)}`);
5243
5322
  recordOpFailure2({ kind: "environment", source: envId, message: m });
5244
5323
  }
5245
5324
  }
@@ -5251,7 +5330,7 @@ async function resolveOpKeyForEnvVarsInner(wanted, opts = {}, span) {
5251
5330
  return out;
5252
5331
  }
5253
5332
  const message = err instanceof Error ? err.message : String(err);
5254
- warnOnce(`[claudish] 1Password secret resolution failed: ${message}`);
5333
+ warnOnce(`[claudish] 1Password secret resolution failed: ${humanizeOpError2(err)}`);
5255
5334
  recordOpFailure2({ kind: "reference", message });
5256
5335
  if (onAuthFailure === "throw")
5257
5336
  throw err;
@@ -28693,6 +28772,15 @@ function resolveModelNameSync(userInput, targetProvider) {
28693
28772
  }
28694
28773
  return { resolvedId: resolved, wasResolved: true, sourceLabel: `${targetProvider} catalog` };
28695
28774
  }
28775
+ function resolveTargetForCatalog(target, isExplicitProvider, model, provider, resolve = resolveModelNameSync) {
28776
+ if (!isExplicitProvider)
28777
+ return { target, resolution: null };
28778
+ const resolution = resolve(model, provider);
28779
+ return {
28780
+ target: resolution.wasResolved ? `${provider}@${resolution.resolvedId}` : target,
28781
+ resolution
28782
+ };
28783
+ }
28696
28784
  function logResolution(userInput, result, quiet = false) {
28697
28785
  if (result.wasResolved && !quiet) {
28698
28786
  process.stderr.write(`[Model] Resolved "${userInput}" \u2192 "${result.resolvedId}" (${result.sourceLabel})
@@ -28802,7 +28890,9 @@ var init_remote_provider_types = __esm(() => {
28802
28890
  "kimi-coding",
28803
28891
  "glm-coding",
28804
28892
  "qwen-cloud",
28805
- "devin"
28893
+ "devin",
28894
+ "antigravity",
28895
+ "sakana-subscription"
28806
28896
  ]);
28807
28897
  PROVIDER_ALIAS = {
28808
28898
  google: "gemini",
@@ -29902,23 +29992,33 @@ import { execFileSync } from "child_process";
29902
29992
  import { existsSync as existsSync7 } from "fs";
29903
29993
  import { homedir as homedir9 } from "os";
29904
29994
  import { join as join9 } from "path";
29995
+ function invalidateReadStoreMemo() {
29996
+ cachedRawStore = null;
29997
+ }
29905
29998
  function defaultReadStore() {
29906
29999
  if (process.platform !== "darwin") {
29907
30000
  logStderr("[Antigravity] Shared token store is macOS-only for now (other keyring backends are a follow-up).");
29908
30001
  return null;
29909
30002
  }
30003
+ const now = Date.now();
30004
+ if (cachedRawStore && now - cachedRawStore.at < READ_STORE_TTL_MS)
30005
+ return cachedRawStore.value;
30006
+ let value = null;
29910
30007
  try {
29911
30008
  const out = execFileSync("security", ["find-generic-password", "-s", KC_SERVICE, "-a", KC_ACCOUNT, "-w"], { encoding: "utf8" });
29912
30009
  const trimmed2 = out.trim();
29913
- return trimmed2.length > 0 ? trimmed2 : null;
30010
+ value = trimmed2.length > 0 ? trimmed2 : null;
29914
30011
  } catch {
29915
- return null;
30012
+ value = null;
29916
30013
  }
30014
+ cachedRawStore = { at: now, value };
30015
+ return value;
29917
30016
  }
29918
30017
  function defaultWriteStore(rawValue) {
29919
30018
  if (process.platform !== "darwin") {
29920
30019
  throw new Error("[Antigravity] Cannot write the shared token store on a non-macOS platform.");
29921
30020
  }
30021
+ invalidateReadStoreMemo();
29922
30022
  execFileSync("security", ["add-generic-password", "-U", "-s", KC_SERVICE, "-a", KC_ACCOUNT, "-w", rawValue], { stdio: ["ignore", "ignore", "ignore"] });
29923
30023
  }
29924
30024
  function locateAgyBinary() {
@@ -29933,6 +30033,7 @@ function locateAgyBinary() {
29933
30033
  function defaultDeleteStore() {
29934
30034
  if (process.platform !== "darwin")
29935
30035
  return;
30036
+ invalidateReadStoreMemo();
29936
30037
  try {
29937
30038
  execFileSync("security", ["delete-generic-password", "-s", KC_SERVICE, "-a", KC_ACCOUNT], {
29938
30039
  stdio: ["ignore", "ignore", "ignore"]
@@ -29948,7 +30049,9 @@ function defaultRunAgyRefresh() {
29948
30049
  stdio: ["ignore", "ignore", "ignore"],
29949
30050
  timeout: AGY_REFRESH_TIMEOUT_MS
29950
30051
  });
29951
- } catch {}
30052
+ } catch {} finally {
30053
+ invalidateReadStoreMemo();
30054
+ }
29952
30055
  }
29953
30056
  function parseRecord(raw) {
29954
30057
  if (!raw)
@@ -30026,8 +30129,9 @@ function getValidAntigravityAccessToken(deps = defaultDeps) {
30026
30129
  function _resetAntigravityTokenState() {
30027
30130
  inFlight = null;
30028
30131
  cachedHasToken = null;
30132
+ invalidateReadStoreMemo();
30029
30133
  }
30030
- var KC_SERVICE = "gemini", KC_ACCOUNT = "antigravity", PREFIX = "go-keyring-base64:", EXPIRY_SKEW_MS = 120000, AGY_REFRESH_TIMEOUT_MS = 40000, defaultDeps, cachedHasToken = null, HAS_TOKEN_TTL_MS = 5000, inFlight = null;
30134
+ var KC_SERVICE = "gemini", KC_ACCOUNT = "antigravity", PREFIX = "go-keyring-base64:", EXPIRY_SKEW_MS = 120000, AGY_REFRESH_TIMEOUT_MS = 40000, READ_STORE_TTL_MS = 3000, cachedRawStore = null, defaultDeps, cachedHasToken = null, HAS_TOKEN_TTL_MS = 5000, inFlight = null;
30031
30135
  var init_antigravity_token = __esm(() => {
30032
30136
  init_logger();
30033
30137
  defaultDeps = {
@@ -36662,6 +36766,10 @@ function warnGoAliasDeprecatedOnce() {
36662
36766
  process.stderr.write(`[claudish] go@ is deprecated \u2014 use ag@<model> (Antigravity). Routing there.
36663
36767
  `);
36664
36768
  }
36769
+ function parseModelChain(modelSpec) {
36770
+ const parts = modelSpec.split(MODEL_CHAIN_SEPARATOR).map((s) => s.trim()).filter(Boolean);
36771
+ return parts.length > 0 ? parts : [modelSpec];
36772
+ }
36665
36773
  function parseModelSpec(modelSpec) {
36666
36774
  const original = modelSpec;
36667
36775
  if (modelSpec.startsWith("http://") || modelSpec.startsWith("https://")) {
@@ -36761,7 +36869,7 @@ function getLegacySyntaxWarning(parsed) {
36761
36869
  return `Deprecation warning: "${parsed.original}" uses legacy prefix syntax.
36762
36870
  ` + ` Consider using: ${newSyntax}`;
36763
36871
  }
36764
- var PROVIDER_SHORTCUTS, _goDeprecationWarned = false, LOCAL_PROVIDERS, NATIVE_MODEL_PATTERNS, LEGACY_PREFIX_PATTERNS;
36872
+ var PROVIDER_SHORTCUTS, _goDeprecationWarned = false, LOCAL_PROVIDERS, NATIVE_MODEL_PATTERNS, LEGACY_PREFIX_PATTERNS, MODEL_CHAIN_SEPARATOR = "+";
36765
36873
  var init_model_parser = __esm(() => {
36766
36874
  init_provider_definitions();
36767
36875
  PROVIDER_SHORTCUTS = getShortcuts();
@@ -40668,6 +40776,13 @@ class ComposedHandler {
40668
40776
  response = next;
40669
40777
  }
40670
40778
  }
40779
+ describeComposition() {
40780
+ return {
40781
+ transport: this.provider.name,
40782
+ streamFormat: this.resolveStreamFormat(),
40783
+ endpoint: this.provider.getEndpoint(this.bareModelName)
40784
+ };
40785
+ }
40671
40786
  resolveStreamFormat() {
40672
40787
  return this.provider.overrideStreamFormat?.() ?? this.explicitAdapter?.getStreamFormat() ?? this.modelAdapter?.getStreamFormat() ?? this.getAdapter().getStreamFormat();
40673
40788
  }
@@ -43170,11 +43285,15 @@ async function pinSpecFor(model, router = route) {
43170
43285
  const plan = await router(model);
43171
43286
  if (plan.kind !== "ok")
43172
43287
  return null;
43173
- return normalizePinnedSpec(plan.primary);
43288
+ return joinPinnedChain([plan.primary, ...plan.fallbacks]);
43174
43289
  } catch {
43175
43290
  return null;
43176
43291
  }
43177
43292
  }
43293
+ function joinPinnedChain(routes) {
43294
+ const specs = routes.map(normalizePinnedSpec).filter((s) => !!s);
43295
+ return specs.length > 0 ? specs.join(MODEL_CHAIN_SEPARATOR) : null;
43296
+ }
43178
43297
  function normalizePinnedSpec(r) {
43179
43298
  const spec = r.modelSpec?.trim();
43180
43299
  if (!spec)
@@ -43458,6 +43577,18 @@ var init_signal_watcher = __esm(() => {
43458
43577
  QUESTION_PATTERNS = [/\?\s*$/m, /\bchoose\b.*:/im, /\bselect\b.*:/im, /\benter\b.*:/im];
43459
43578
  });
43460
43579
 
43580
+ // src/spawn-claudish.ts
43581
+ function resolveClaudishSpawn(env = process.env) {
43582
+ const bin = env[CLAUDISH_BIN_ENV]?.trim();
43583
+ if (!bin)
43584
+ return { command: "claudish", prefixArgs: [] };
43585
+ if (/\.(ts|tsx|js|mjs|cjs)$/.test(bin)) {
43586
+ return { command: process.execPath, prefixArgs: ["run", bin] };
43587
+ }
43588
+ return { command: bin, prefixArgs: [] };
43589
+ }
43590
+ var CLAUDISH_BIN_ENV = "CLAUDISH_BIN";
43591
+
43461
43592
  // src/channel/session-manager.ts
43462
43593
  import { spawn } from "child_process";
43463
43594
  import { randomUUID as randomUUID4 } from "crypto";
@@ -43498,7 +43629,8 @@ class SessionManager {
43498
43629
  "--quiet",
43499
43630
  ...opts.claudishFlags ?? []
43500
43631
  ];
43501
- const proc = spawn("claudish", args, {
43632
+ const spawnTarget = resolveClaudishSpawn();
43633
+ const proc = spawn(spawnTarget.command, [...spawnTarget.prefixArgs, ...args], {
43502
43634
  cwd: opts.cwd ?? process.cwd(),
43503
43635
  stdio: ["pipe", "pipe", "pipe"],
43504
43636
  shell: false
@@ -46149,7 +46281,7 @@ ${summary}`);
46149
46281
  error: truncate(parseErrorMessage(e.message), 200)
46150
46282
  }))
46151
46283
  }
46152
- }, 502);
46284
+ }, exhaustedChainStatus(errors3));
46153
46285
  }
46154
46286
  async shutdown() {
46155
46287
  for (const { handler } of this.candidates) {
@@ -46183,6 +46315,9 @@ function isRetryableError(status, errorBody, provider) {
46183
46315
  if (provider?.toLowerCase().includes("antigravity") && lower.includes("invalid argument")) {
46184
46316
  return true;
46185
46317
  }
46318
+ if (isProvider(provider, "opencodezen") && (lower.includes("upstream request failed") || lower.includes("error from provider ("))) {
46319
+ return true;
46320
+ }
46186
46321
  }
46187
46322
  if (status === 500) {
46188
46323
  if (lower.includes("insufficient balance") || lower.includes("insufficient credit") || lower.includes("quota exceeded") || lower.includes("billing")) {
@@ -46191,6 +46326,17 @@ function isRetryableError(status, errorBody, provider) {
46191
46326
  }
46192
46327
  return false;
46193
46328
  }
46329
+ function exhaustedChainStatus(errors3) {
46330
+ if (errors3.length === 0)
46331
+ return 400;
46332
+ const allTransient = errors3.every((e) => e.status === 429 || e.status === 503 || hasQuotaExhaustionWording(e.message));
46333
+ return allTransient ? 503 : 400;
46334
+ }
46335
+ function isProvider(provider, needle) {
46336
+ if (!provider)
46337
+ return false;
46338
+ return provider.toLowerCase().replace(/[^a-z0-9]/g, "").includes(needle);
46339
+ }
46194
46340
  function parseErrorMessage(body) {
46195
46341
  try {
46196
46342
  const parsed = JSON.parse(body);
@@ -47795,17 +47941,6 @@ var init_provider_profiles = __esm(() => {
47795
47941
  createHandler(ctx) {
47796
47942
  const zenApiKey = ctx.apiKey;
47797
47943
  const isGoProvider = ctx.provider.name === "opencode-zen-go";
47798
- if (ctx.modelName.toLowerCase().includes("minimax")) {
47799
- const bearerProvider = { ...ctx.provider, authScheme: "bearer" };
47800
- const transport2 = new AnthropicProviderTransport(bearerProvider, zenApiKey);
47801
- const adapter2 = new AnthropicAPIFormat(ctx.modelName, ctx.provider.name);
47802
- const handler2 = new ComposedHandler(transport2, ctx.targetModel, ctx.modelName, ctx.port, {
47803
- adapter: adapter2,
47804
- ...ctx.sharedOpts
47805
- });
47806
- log(`[Proxy] Created OpenCode Zen${isGoProvider ? " Go" : ""} (Anthropic composed): ${ctx.modelName}`);
47807
- return handler2;
47808
- }
47809
47944
  if (ctx.modelName.toLowerCase().startsWith("gpt-")) {
47810
47945
  const responsesProvider = { ...ctx.provider, apiPath: "/v1/responses" };
47811
47946
  const transport2 = new OpenAIProviderTransport(responsesProvider, ctx.modelName, zenApiKey);
@@ -48920,13 +49055,38 @@ async function createProxyServer(port, _openrouterApiKey, model, monitorMode = f
48920
49055
  target = model;
48921
49056
  }
48922
49057
  const invocationMode = detectInvocationMode(target, wasFromModelMap);
49058
+ if (options.modelChain && options.modelChain.length > 1 && target === options.modelChain[0]) {
49059
+ const cacheKey = `chain:${options.modelChain.join("+")}`;
49060
+ const cached2 = fallbackHandlerCache.get(cacheKey);
49061
+ if (cached2)
49062
+ return cached2;
49063
+ await ensureCatalogReady(5000);
49064
+ const candidates = [];
49065
+ for (const spec of options.modelChain) {
49066
+ const parsed = parseModelSpec(spec);
49067
+ const resolvedSpec = resolveTargetForCatalog(spec, parsed.isExplicitProvider, parsed.model, parsed.provider).target;
49068
+ const handler = parsed.provider === "openrouter" ? getOpenRouterHandler(resolvedSpec, invocationMode) : await getRemoteProviderHandler(resolvedSpec, invocationMode) ?? getLocalProviderHandler(resolvedSpec, invocationMode);
49069
+ if (handler) {
49070
+ candidates.push({ name: DISPLAY_NAMES[parsed.provider] ?? parsed.provider, handler });
49071
+ }
49072
+ }
49073
+ if (candidates.length > 0) {
49074
+ const resultHandler = candidates.length > 1 ? new FallbackHandler(candidates) : candidates[0].handler;
49075
+ fallbackHandlerCache.set(cacheKey, resultHandler);
49076
+ if (!options.quiet && candidates.length > 1) {
49077
+ logStderr(`[Route] ${candidates.length} pinned providers for ${target}: ${candidates.map((c) => c.name).join(" \u2192 ")}`);
49078
+ }
49079
+ return resultHandler;
49080
+ }
49081
+ }
48923
49082
  {
48924
49083
  const parsedTarget = parseModelSpec(target);
48925
- await ensureCatalogReady(5000);
48926
- const resolution = resolveModelNameSync(parsedTarget.model, parsedTarget.provider);
48927
- logResolution(parsedTarget.model, resolution, options.quiet);
48928
- if (resolution.wasResolved) {
48929
- target = `${parsedTarget.provider}@${resolution.resolvedId}`;
49084
+ if (parsedTarget.isExplicitProvider) {
49085
+ await ensureCatalogReady(5000);
49086
+ const outcome = resolveTargetForCatalog(target, parsedTarget.isExplicitProvider, parsedTarget.model, parsedTarget.provider);
49087
+ if (outcome.resolution)
49088
+ logResolution(parsedTarget.model, outcome.resolution, options.quiet);
49089
+ target = outcome.target;
48930
49090
  }
48931
49091
  }
48932
49092
  {
@@ -49143,6 +49303,7 @@ var init_proxy_server = __esm(() => {
49143
49303
  init_model_loader();
49144
49304
  init_profile_config();
49145
49305
  init_api_key_map();
49306
+ init_auto_route();
49146
49307
  init_catalog_client();
49147
49308
  init_custom_endpoints_loader();
49148
49309
  init_model_parser();
@@ -49537,7 +49698,8 @@ async function runModels(sessionPath, opts = {}) {
49537
49698
  state: "RUNNING",
49538
49699
  startedAt: new Date().toISOString()
49539
49700
  });
49540
- const proc = spawn2("claudish", args, {
49701
+ const teamSpawnTarget = resolveClaudishSpawn();
49702
+ const proc = spawn2(teamSpawnTarget.command, [...teamSpawnTarget.prefixArgs, ...args], {
49541
49703
  stdio: ["pipe", "pipe", "pipe"],
49542
49704
  shell: false,
49543
49705
  env: {
@@ -64452,8 +64614,11 @@ __export(exports_model_selector, {
64452
64614
  promptForApiKey: () => promptForApiKey,
64453
64615
  pickerProviderToFirebaseSlug: () => pickerProviderToFirebaseSlug,
64454
64616
  isUserDeployedProvider: () => isUserDeployedProvider,
64617
+ isPickableProvider: () => isPickableProvider,
64618
+ getProviderFilterAliases: () => getProviderFilterAliases,
64455
64619
  confirmAction: () => confirmAction,
64456
64620
  compareByReleaseDateDesc: () => compareByReleaseDateDesc,
64621
+ buildProviderChoices: () => buildProviderChoices,
64457
64622
  buildExplicitModelSpec: () => buildExplicitModelSpec,
64458
64623
  buildDiscoveredModelRows: () => buildDiscoveredModelRows
64459
64624
  });
@@ -64612,6 +64777,18 @@ function dedupeModels(models) {
64612
64777
  }
64613
64778
  return deduped;
64614
64779
  }
64780
+ function dedupeByProviderSpec(provider, models) {
64781
+ const seen = new Set;
64782
+ const deduped = [];
64783
+ for (const model of models) {
64784
+ const spec = buildExplicitModelSpec(provider, resolveProviderExternalId(provider, model));
64785
+ if (seen.has(spec))
64786
+ continue;
64787
+ seen.add(spec);
64788
+ deduped.push(model);
64789
+ }
64790
+ return deduped;
64791
+ }
64615
64792
  function sortModelsNewestFirst(models) {
64616
64793
  return [...models].sort(compareByReleaseDateDesc);
64617
64794
  }
@@ -64662,6 +64839,16 @@ function formatModelChoiceAsSpec(model, spec, priceStr) {
64662
64839
  const dateStr = model.releaseDate ? `, ${model.releaseDate.slice(0, 7)}` : "";
64663
64840
  return `${spec} (${priceStr}, ${ctxStr}${capsStr}${dateStr})`;
64664
64841
  }
64842
+ function getProviderFilterAliases() {
64843
+ const aliases = {};
64844
+ for (const def of pickableProvidersInPickerOrder()) {
64845
+ aliases[def.name.toLowerCase()] = def.name;
64846
+ for (const shortcut of def.shortcuts) {
64847
+ aliases[shortcut.toLowerCase()] = def.name;
64848
+ }
64849
+ }
64850
+ return { ...aliases, ...PROVIDER_FILTER_ALIAS_EXTRA };
64851
+ }
64665
64852
  function parseProviderFilter(term, providers = []) {
64666
64853
  if (!term.startsWith("@")) {
64667
64854
  return { provider: null, searchTerm: term };
@@ -64677,7 +64864,7 @@ function parseProviderFilter(term, providers = []) {
64677
64864
  prefix = withoutAt.slice(0, spaceIdx);
64678
64865
  rest = withoutAt.slice(spaceIdx + 1).trim();
64679
64866
  }
64680
- const source = PROVIDER_FILTER_ALIASES[prefix.toLowerCase()];
64867
+ const source = getProviderFilterAliases()[prefix.toLowerCase()];
64681
64868
  if (source) {
64682
64869
  return { provider: source, searchTerm: rest };
64683
64870
  }
@@ -64685,7 +64872,7 @@ function parseProviderFilter(term, providers = []) {
64685
64872
  if (exactMatch) {
64686
64873
  return { provider: exactMatch.slug, searchTerm: rest };
64687
64874
  }
64688
- const partialMatch = Object.entries(PROVIDER_FILTER_ALIASES).find(([alias]) => alias.startsWith(prefix.toLowerCase()));
64875
+ const partialMatch = Object.entries(getProviderFilterAliases()).find(([alias]) => alias.startsWith(prefix.toLowerCase()));
64689
64876
  if (partialMatch) {
64690
64877
  return { provider: partialMatch[1], searchTerm: rest };
64691
64878
  }
@@ -64699,7 +64886,7 @@ async function fetchPickerModels(providerSlug, searchTerm, defaultModels, catalo
64699
64886
  if (providerSlug) {
64700
64887
  const firebaseSlug = pickerProviderToFirebaseSlug[providerSlug] ?? providerSlug;
64701
64888
  const vendorModels = await catalog.modelsByVendor(firebaseSlug);
64702
- const infos = sortModelsNewestFirst(dedupeModels(vendorModels.map(catalogModelToModelInfo)));
64889
+ const infos = dedupeByProviderSpec(providerSlug, sortModelsNewestFirst(dedupeModels(vendorModels.map(catalogModelToModelInfo))));
64703
64890
  if (!searchTerm)
64704
64891
  return infos;
64705
64892
  const needle = searchTerm.toLowerCase();
@@ -64717,6 +64904,7 @@ async function selectModel(options = {}) {
64717
64904
  let models;
64718
64905
  let recommendedModels = [];
64719
64906
  let pickerProviders = [];
64907
+ let interactiveProviderChoices = [];
64720
64908
  const remoteQueryCache = new Map;
64721
64909
  if (freeOnly) {
64722
64910
  models = await getFreeModels();
@@ -64731,7 +64919,8 @@ async function selectModel(options = {}) {
64731
64919
  const topModels = top100Result.status === "fulfilled" ? sortModelsNewestFirst(dedupeModels(top100Result.value.models.map(modelDocToModelInfo))) : [];
64732
64920
  recommendedModels = recommendedResult.status === "fulfilled" ? recommendedResult.value : [];
64733
64921
  models = topModels.length > 0 ? topModels : recommendedModels;
64734
- pickerProviders = toPickerProviders(await getInteractiveProviderChoices());
64922
+ interactiveProviderChoices = await getInteractiveProviderChoices();
64923
+ pickerProviders = toPickerProviders(interactiveProviderChoices);
64735
64924
  }
64736
64925
  const loadRemoteModels = async (providerSlug, searchTerm) => {
64737
64926
  const cacheKey = `${providerSlug || "__all__"}::${searchTerm}`;
@@ -64760,7 +64949,6 @@ async function selectModel(options = {}) {
64760
64949
  const cleanupKeypress = () => process.stdin.removeListener("data", onData);
64761
64950
  try {
64762
64951
  if (!freeOnly && !message && pickerProviders.length > 1) {
64763
- const interactiveProviderChoices = await getInteractiveProviderChoices();
64764
64952
  const providerChoices = [
64765
64953
  {
64766
64954
  name: "All providers",
@@ -64793,11 +64981,14 @@ async function selectModel(options = {}) {
64793
64981
  const { provider: filterProvider, searchTerm } = parseProviderFilter(normalizedTerm, pickerProviders);
64794
64982
  const effectiveProvider = filterProvider;
64795
64983
  const remoteModels = await loadRemoteModels(effectiveProvider, searchTerm);
64796
- return remoteModels.slice(0, 100).map((model) => ({
64797
- name: formatModelChoice(model, true),
64798
- value: effectiveProvider ? buildExplicitModelSpec(effectiveProvider, model.id) : model.id,
64799
- description: model.description?.slice(0, 160)
64800
- }));
64984
+ return remoteModels.slice(0, 100).map((model) => {
64985
+ const spec = effectiveProvider ? buildExplicitModelSpec(effectiveProvider, resolveProviderExternalId(effectiveProvider, model)) : model.id;
64986
+ return {
64987
+ name: effectiveProvider ? formatModelChoiceAsSpec(model, spec, resolveProviderDisplayPrice(effectiveProvider, model)) : formatModelChoice(model, true),
64988
+ value: spec,
64989
+ description: model.description?.slice(0, 160)
64990
+ };
64991
+ });
64801
64992
  }
64802
64993
  }, { signal: ac.signal });
64803
64994
  return selected;
@@ -64811,13 +65002,59 @@ async function selectModel(options = {}) {
64811
65002
  cleanupKeypress();
64812
65003
  }
64813
65004
  }
65005
+ function isPickableProvider(def) {
65006
+ return def.shortcuts.length > 0;
65007
+ }
65008
+ function pickableProvidersInPickerOrder() {
65009
+ const rank = new Map(PICKER_ORDER.map((name, i) => [name, i]));
65010
+ return getAllProviders().filter(isPickableProvider).sort((a, b) => {
65011
+ const ra = rank.get(a.name) ?? Number.MAX_SAFE_INTEGER;
65012
+ const rb = rank.get(b.name) ?? Number.MAX_SAFE_INTEGER;
65013
+ return ra !== rb ? ra - rb : a.displayName.localeCompare(b.displayName);
65014
+ });
65015
+ }
65016
+ function buildProviderChoices() {
65017
+ const derived = pickableProvidersInPickerOrder().map((def) => {
65018
+ const copy = PICKER_COPY[def.name] ?? {};
65019
+ return {
65020
+ name: copy.name ?? def.displayName,
65021
+ value: def.name,
65022
+ description: copy.description ?? def.description ?? "",
65023
+ provider: def.name
65024
+ };
65025
+ });
65026
+ return [
65027
+ {
65028
+ name: "Skip (keep Claude default)",
65029
+ value: "skip",
65030
+ description: "Use native Claude model for this tier"
65031
+ },
65032
+ ...derived,
65033
+ {
65034
+ name: "Enter custom model",
65035
+ value: "custom",
65036
+ description: "Type a provider@model specification"
65037
+ }
65038
+ ];
65039
+ }
64814
65040
  async function getProviderChoices() {
64815
- const checks4 = await Promise.all(ALL_PROVIDER_CHOICES.map(async (choice) => {
65041
+ const all = buildProviderChoices();
65042
+ const checks4 = await Promise.all(all.map(async (choice) => {
64816
65043
  if (!choice.provider)
64817
65044
  return true;
64818
65045
  return credentials.isAvailable(choice.provider);
64819
65046
  }));
64820
- return ALL_PROVIDER_CHOICES.filter((_, i) => checks4[i]);
65047
+ return all.filter((_, i) => checks4[i]);
65048
+ }
65049
+ function pickerModelPrefix(provider) {
65050
+ const override = PROVIDER_MODEL_PREFIX_OVERRIDE[provider];
65051
+ if (override)
65052
+ return override;
65053
+ const def = getProviderByName(provider);
65054
+ if (!def || !isPickableProvider(def))
65055
+ return;
65056
+ const prefix = def.shortestPrefix || def.shortcuts[0];
65057
+ return prefix ? `${prefix}@` : undefined;
64821
65058
  }
64822
65059
  async function getInteractiveProviderChoices() {
64823
65060
  return (await getProviderChoices()).filter((choice) => choice.value !== "skip");
@@ -64830,7 +65067,7 @@ function toPickerProviders(choices) {
64830
65067
  }));
64831
65068
  }
64832
65069
  function buildExplicitModelSpec(provider, modelId) {
64833
- const prefix = PROVIDER_MODEL_PREFIX[provider];
65070
+ const prefix = pickerModelPrefix(provider);
64834
65071
  if (!prefix) {
64835
65072
  return modelId;
64836
65073
  }
@@ -64849,6 +65086,8 @@ function resolveProviderAggregatorEntry(provider, model) {
64849
65086
  return model.aggregators.find((a) => a.provider.toLowerCase() === firebaseSlug.toLowerCase());
64850
65087
  }
64851
65088
  function resolveProviderDisplayPrice(provider, model) {
65089
+ if (isSubscriptionProvider(provider))
65090
+ return "SUB";
64852
65091
  const entry = resolveProviderAggregatorEntry(provider, model);
64853
65092
  const entryPrice = formatAveragePricing(entry?.pricing);
64854
65093
  if (entryPrice?.average)
@@ -64856,7 +65095,7 @@ function resolveProviderDisplayPrice(provider, model) {
64856
65095
  return model.pricing?.average || "N/A";
64857
65096
  }
64858
65097
  function getPickerDisplayName(providerValue) {
64859
- const choice = ALL_PROVIDER_CHOICES.find((c) => c.value === providerValue);
65098
+ const choice = buildProviderChoices().find((c) => c.value === providerValue);
64860
65099
  if (choice)
64861
65100
  return choice.name;
64862
65101
  return getDisplayName(providerValue);
@@ -64865,7 +65104,7 @@ async function loadModelsForPickerProvider(providerValue, catalog) {
64865
65104
  const firebaseSlug = pickerProviderToFirebaseSlug[providerValue] ?? providerValue;
64866
65105
  try {
64867
65106
  const vendorModels = await catalog.modelsByVendor(firebaseSlug);
64868
- return sortModelsNewestFirst(dedupeModels(vendorModels.map(catalogModelToModelInfo)));
65107
+ return dedupeByProviderSpec(providerValue, sortModelsNewestFirst(dedupeModels(vendorModels.map(catalogModelToModelInfo))));
64869
65108
  } catch {
64870
65109
  return [];
64871
65110
  }
@@ -64957,7 +65196,7 @@ async function buildDiscoveredModelRows(provider, displayName, catalog) {
64957
65196
  return sortModelsNewestFirst(rows);
64958
65197
  }
64959
65198
  async function selectModelFromProvider(provider, tierName, recommendedModels, _forceUpdate, catalog) {
64960
- const prefix = PROVIDER_MODEL_PREFIX[provider] || `${provider}@`;
65199
+ const prefix = pickerModelPrefix(provider) ?? `${provider}@`;
64961
65200
  const displayName = getPickerDisplayName(provider);
64962
65201
  const def = getProviderByName(provider);
64963
65202
  if (def?.modelDiscovery) {
@@ -65122,7 +65361,7 @@ async function selectProfile(profiles) {
65122
65361
  async function confirmAction(message) {
65123
65362
  return dist_default4({ message, default: false });
65124
65363
  }
65125
- var pickerProviderToFirebaseSlug, LOCAL_OR_USER_DEPLOYED, SUBSCRIPTION_PRICING, PROVIDER_FILTER_ALIASES, ALL_PROVIDER_CHOICES, PROVIDER_MODEL_PREFIX;
65364
+ var pickerProviderToFirebaseSlug, LOCAL_OR_USER_DEPLOYED, SUBSCRIPTION_PRICING, PROVIDER_FILTER_ALIAS_EXTRA, PICKER_COPY, PICKER_ORDER, PROVIDER_MODEL_PREFIX_OVERRIDE;
65126
65365
  var init_model_selector = __esm(() => {
65127
65366
  init_dist16();
65128
65367
  init_model_catalog();
@@ -65160,160 +65399,73 @@ var init_model_selector = __esm(() => {
65160
65399
  output: "SUB",
65161
65400
  average: "SUB"
65162
65401
  };
65163
- PROVIDER_FILTER_ALIASES = {
65164
- openrouter: "openrouter",
65165
- or: "openrouter",
65166
- google: "google",
65167
- gemini: "google",
65402
+ PROVIDER_FILTER_ALIAS_EXTRA = {
65168
65403
  gem: "google",
65169
- openai: "openai",
65170
- oai: "openai",
65171
- codex: "openai-codex",
65172
- cx: "openai-codex",
65173
- "x-ai": "x-ai",
65174
- xai: "x-ai",
65175
- grok: "x-ai",
65176
- minimax: "minimax",
65177
- mm: "minimax",
65178
- "minimax-coding": "minimax-coding",
65179
- mmc: "minimax-coding",
65180
- kimi: "kimi",
65181
- moon: "kimi",
65182
- moonshot: "kimi",
65183
- "kimi-coding": "kimi-coding",
65184
- kc: "kimi-coding",
65185
- glm: "glm",
65186
- "glm-coding": "glm-coding",
65187
- gc: "glm-coding",
65188
- "z-ai": "z-ai",
65189
- zai: "z-ai",
65190
- zen: "zen",
65191
- ollamacloud: "ollamacloud",
65192
- oc: "ollamacloud",
65193
- litellm: "litellm",
65194
- ll: "litellm",
65195
- deepseek: "deepseek",
65196
- mistralai: "mistralai",
65197
- mistral: "mistralai",
65198
- sakana: "sakana",
65199
- fugu: "sakana",
65200
- "sakana-subscription": "sakana-subscription",
65201
- sc: "sakana-subscription",
65202
- "qwen-cloud": "qwen-cloud",
65203
- qc: "qwen-cloud"
65204
- };
65205
- ALL_PROVIDER_CHOICES = [
65206
- {
65207
- name: "Skip (keep Claude default)",
65208
- value: "skip",
65209
- description: "Use native Claude model for this tier"
65210
- },
65211
- {
65212
- name: "OpenRouter",
65213
- value: "openrouter",
65214
- description: "580+ models via unified API",
65215
- provider: "openrouter"
65216
- },
65217
- {
65218
- name: "OpenCode Zen",
65219
- value: "zen",
65220
- description: "Free models, no API key needed",
65221
- provider: "opencode-zen"
65222
- },
65223
- { name: "Google Gemini", value: "google", description: "Direct API", provider: "google" },
65224
- { name: "OpenAI", value: "openai", description: "Direct API", provider: "openai" },
65225
- {
65226
- name: "OpenAI Codex",
65227
- value: "openai-codex",
65228
- description: "ChatGPT Plus/Pro subscription (Responses API)",
65229
- provider: "openai-codex"
65230
- },
65231
- { name: "xAI / Grok", value: "x-ai", description: "Direct API", provider: "x-ai" },
65232
- { name: "DeepSeek", value: "deepseek", description: "Direct API", provider: "deepseek" },
65233
- { name: "Mistral", value: "mistralai", description: "Direct API", provider: "mistralai" },
65234
- { name: "Sakana Fugu", value: "sakana", description: "Direct API", provider: "sakana" },
65235
- {
65236
- name: "Sakana Fugu Subscription",
65237
- value: "sakana-subscription",
65238
- description: "Subscription plan",
65239
- provider: "sakana-subscription"
65240
- },
65241
- { name: "MiniMax", value: "minimax", description: "Direct API", provider: "minimax" },
65242
- {
65243
- name: "MiniMax Coding",
65244
- value: "minimax-coding",
65245
- description: "Coding subscription",
65246
- provider: "minimax-coding"
65247
- },
65248
- { name: "Kimi / Moonshot", value: "kimi", description: "Direct API", provider: "kimi" },
65249
- {
65250
- name: "Kimi Coding",
65251
- value: "kimi-coding",
65252
- description: "Coding subscription",
65253
- provider: "kimi-coding"
65254
- },
65255
- {
65256
- name: "Qwen Plan",
65257
- value: "qwen-cloud",
65258
- description: "Alibaba Model Studio subscription",
65259
- provider: "qwen-cloud"
65260
- },
65261
- { name: "GLM / Zhipu", value: "glm", description: "Direct API", provider: "glm" },
65262
- {
65263
- name: "GLM Coding Plan",
65264
- value: "glm-coding",
65265
- description: "Coding subscription",
65266
- provider: "glm-coding"
65267
- },
65268
- { name: "Z.AI", value: "z-ai", description: "Direct API", provider: "z-ai" },
65269
- {
65270
- name: "OllamaCloud",
65271
- value: "ollamacloud",
65272
- description: "Cloud models",
65273
- provider: "ollamacloud"
65274
- },
65275
- { name: "LiteLLM", value: "litellm", description: "Configured proxy", provider: "litellm" },
65276
- {
65277
- name: "Ollama (local)",
65278
- value: "ollama",
65279
- description: "Local Ollama instance",
65280
- provider: "ollama"
65281
- },
65282
- {
65283
- name: "LM Studio (local)",
65284
- value: "lmstudio",
65285
- description: "Local LM Studio instance",
65286
- provider: "lmstudio"
65287
- },
65288
- {
65289
- name: "Enter custom model",
65290
- value: "custom",
65291
- description: "Type a provider@model specification"
65292
- }
65404
+ zen: "opencode-zen"
65405
+ };
65406
+ PICKER_COPY = {
65407
+ openrouter: { description: "580+ models via unified API" },
65408
+ "opencode-zen": { name: "OpenCode Zen", description: "Free models, no API key needed" },
65409
+ google: { name: "Google Gemini", description: "Direct API" },
65410
+ openai: { description: "Direct API" },
65411
+ "openai-codex": { description: "ChatGPT Plus/Pro subscription (Responses API)" },
65412
+ "x-ai": { name: "xAI / Grok", description: "Direct API" },
65413
+ deepseek: { description: "Direct API" },
65414
+ mistralai: { name: "Mistral", description: "Direct API" },
65415
+ sakana: { name: "Sakana Fugu", description: "Direct API" },
65416
+ "sakana-subscription": { name: "Sakana Fugu Subscription", description: "Subscription plan" },
65417
+ minimax: { description: "Direct API" },
65418
+ "minimax-coding": { name: "MiniMax Coding", description: "Coding subscription" },
65419
+ kimi: { name: "Kimi / Moonshot", description: "Direct API" },
65420
+ "kimi-coding": { name: "Kimi Coding", description: "Coding subscription" },
65421
+ "qwen-cloud": { name: "Qwen Plan", description: "Alibaba Model Studio subscription" },
65422
+ glm: { name: "GLM / Zhipu", description: "Direct API" },
65423
+ "glm-coding": { name: "GLM Coding Plan", description: "Coding subscription" },
65424
+ "z-ai": { name: "Z.AI", description: "Direct API" },
65425
+ ollamacloud: { name: "OllamaCloud", description: "Cloud models" },
65426
+ litellm: { description: "Configured proxy" },
65427
+ ollama: { name: "Ollama (local)", description: "Local Ollama instance" },
65428
+ lmstudio: { name: "LM Studio (local)", description: "Local LM Studio instance" },
65429
+ vllm: { name: "vLLM (local)", description: "Local vLLM server" },
65430
+ mlx: { name: "MLX (local)", description: "Local MLX server" }
65431
+ };
65432
+ PICKER_ORDER = [
65433
+ "openrouter",
65434
+ "opencode-zen",
65435
+ "opencode-zen-go",
65436
+ "google",
65437
+ "antigravity",
65438
+ "openai",
65439
+ "openai-codex",
65440
+ "devin",
65441
+ "x-ai",
65442
+ "deepseek",
65443
+ "mistralai",
65444
+ "sakana",
65445
+ "sakana-subscription",
65446
+ "minimax",
65447
+ "minimax-coding",
65448
+ "kimi",
65449
+ "kimi-coding",
65450
+ "qwen-cloud",
65451
+ "glm",
65452
+ "glm-coding",
65453
+ "z-ai",
65454
+ "ollamacloud",
65455
+ "poe",
65456
+ "vertex",
65457
+ "litellm",
65458
+ "ollama",
65459
+ "lmstudio",
65460
+ "vllm",
65461
+ "mlx"
65293
65462
  ];
65294
- PROVIDER_MODEL_PREFIX = {
65463
+ PROVIDER_MODEL_PREFIX_OVERRIDE = {
65295
65464
  google: "google@",
65296
- antigravity: "ag@",
65297
- openai: "oai@",
65298
- "openai-codex": "cx@",
65299
- "x-ai": "x-ai@",
65300
- deepseek: "ds@",
65301
- mistralai: "mistral@",
65302
- sakana: "sakana@",
65303
- "sakana-subscription": "sc@",
65304
- minimax: "mm@",
65305
- kimi: "kimi@",
65306
- "minimax-coding": "mmc@",
65307
- "kimi-coding": "kc@",
65308
- "qwen-cloud": "qc@",
65309
- glm: "glm@",
65310
- "glm-coding": "gc@",
65311
- "z-ai": "z-ai@",
65312
- ollamacloud: "oc@",
65313
- ollama: "ollama@",
65465
+ openrouter: "openrouter@",
65314
65466
  lmstudio: "lmstudio@",
65315
- zen: "zen@",
65316
- openrouter: "openrouter@"
65467
+ sakana: "sakana@",
65468
+ zen: "zen@"
65317
65469
  };
65318
65470
  });
65319
65471
 
@@ -68112,7 +68264,10 @@ async function parseArgs(args) {
68112
68264
  printAvailableModels();
68113
68265
  process.exit(1);
68114
68266
  }
68115
- config3.model = modelArg;
68267
+ const chain = parseModelChain(modelArg);
68268
+ config3.model = chain[0];
68269
+ if (chain.length > 1)
68270
+ config3.modelChain = chain;
68116
68271
  } else if (arg === "--model-opus") {
68117
68272
  const val = args[++i];
68118
68273
  if (val)
@@ -75451,6 +75606,7 @@ function App({ requestLogin } = {}) {
75451
75606
  setOpFieldCursor(idx < 0 ? 0 : idx);
75452
75607
  }, [opFieldOptionsFiltered, opFieldCursor, mode]);
75453
75608
  const acquireOpAuth = useCallback3(async () => {
75609
+ clearOpSkip();
75454
75610
  const auth = await resolveSdkAuth({
75455
75611
  interactive: true,
75456
75612
  configAccount: readOnepasswordAccount(),
@@ -77566,7 +77722,10 @@ async function runClaudeWithProxy(config3, proxyUrl, onCleanup) {
77566
77722
  setSessionContextWindow2(realWindow);
77567
77723
  } catch {}
77568
77724
  if (contextEnv.notice && !config3.quiet) {
77569
- console.error(contextEnv.notice);
77725
+ if (config3.interactive)
77726
+ console.log(contextEnv.notice);
77727
+ else
77728
+ console.error(contextEnv.notice);
77570
77729
  }
77571
77730
  }
77572
77731
  }
@@ -78701,7 +78860,8 @@ Team Status`);
78701
78860
  quiet: cliConfig.quiet,
78702
78861
  isInteractive: cliConfig.interactive,
78703
78862
  advisorModels: cliConfig.advisorModels,
78704
- advisorCollector: cliConfig.advisorCollector
78863
+ advisorCollector: cliConfig.advisorCollector,
78864
+ modelChain: cliConfig.monitor ? undefined : cliConfig.modelChain
78705
78865
  }));
78706
78866
  const diag = createDiagOutput2({
78707
78867
  interactive: cliConfig.interactive,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudish",
3
- "version": "7.44.0",
3
+ "version": "7.46.0",
4
4
  "description": "Run Claude Code with any model - OpenRouter, Ollama, LM Studio & local models",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -60,10 +60,10 @@
60
60
  "ai"
61
61
  ],
62
62
  "optionalDependencies": {
63
- "@claudish/magmux-darwin-arm64": "7.44.0",
64
- "@claudish/magmux-darwin-x64": "7.44.0",
65
- "@claudish/magmux-linux-arm64": "7.44.0",
66
- "@claudish/magmux-linux-x64": "7.44.0"
63
+ "@claudish/magmux-darwin-arm64": "7.46.0",
64
+ "@claudish/magmux-darwin-x64": "7.46.0",
65
+ "@claudish/magmux-linux-arm64": "7.46.0",
66
+ "@claudish/magmux-linux-x64": "7.46.0"
67
67
  },
68
68
  "author": "Jack Rudenko <i@madappgang.com>",
69
69
  "license": "MIT",