claude-multi 0.6.4 → 0.7.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 (3) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/cli.js +1040 -664
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -7300,15 +7300,31 @@ function getApiKeyPlaceholder(providerName) {
7300
7300
  return prefix ? `${prefix}...` : "sk-...";
7301
7301
  }
7302
7302
  function providerHasRegions(providerName) {
7303
- return providerName === "mimo-token";
7303
+ return providerName in PROVIDER_REGION_MAPS;
7304
+ }
7305
+ function getProviderRegions(providerName) {
7306
+ return PROVIDER_REGION_MAPS[providerName];
7307
+ }
7308
+ function detectRegionFromBaseUrl(baseUrl) {
7309
+ for (const regionMap of Object.values(PROVIDER_REGION_MAPS)) {
7310
+ for (const [regionCode, config] of Object.entries(regionMap)) {
7311
+ const escaped = config.baseUrl.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7312
+ const regex = new RegExp(`^${escaped}/?$`);
7313
+ if (regex.test(baseUrl)) {
7314
+ return regionCode;
7315
+ }
7316
+ }
7317
+ }
7318
+ return null;
7304
7319
  }
7305
7320
  function resolveRegionTemplate(template, region) {
7306
7321
  if (!providerHasRegions(template.name)) {
7307
7322
  return template;
7308
7323
  }
7309
- const regionConfig = MIMO_TOKEN_REGIONS[region];
7324
+ const regionMap = PROVIDER_REGION_MAPS[template.name];
7325
+ const regionConfig = regionMap?.[region];
7310
7326
  if (!regionConfig) {
7311
- throw new Error(`Unknown region '${region}' for ${template.name}. Available: ${Object.keys(MIMO_TOKEN_REGIONS).join(", ")}`);
7327
+ throw new Error(`Unknown region '${region}' for ${template.name}. Available: ${regionMap ? Object.keys(regionMap).join(", ") : "none"}`);
7312
7328
  }
7313
7329
  const resolved = structuredClone(template);
7314
7330
  resolved.settings.env.ANTHROPIC_BASE_URL = regionConfig.baseUrl;
@@ -7338,7 +7354,8 @@ function getProviderByBaseUrl(baseUrl) {
7338
7354
  if (baseUrl === templateUrl)
7339
7355
  return name;
7340
7356
  }
7341
- if (baseUrl.startsWith("https://token-plan-") && baseUrl.endsWith(".xiaomimimo.com/anthropic")) {
7357
+ const regionCode = detectRegionFromBaseUrl(baseUrl);
7358
+ if (regionCode) {
7342
7359
  return "mimo-token";
7343
7360
  }
7344
7361
  return null;
@@ -7357,7 +7374,7 @@ function detectProvider(configDir) {
7357
7374
  return null;
7358
7375
  }
7359
7376
  }
7360
- var PROVIDER_COMMON_ENV, PROVIDER_TEMPLATES, API_KEY_PREFIXES, MIMO_TOKEN_REGIONS;
7377
+ var PROVIDER_COMMON_ENV, PROVIDER_TEMPLATES, API_KEY_PREFIXES, MIMO_TOKEN_REGIONS, PROVIDER_REGION_MAPS;
7361
7378
  var init_templates = __esm(() => {
7362
7379
  PROVIDER_COMMON_ENV = {};
7363
7380
  PROVIDER_TEMPLATES = {
@@ -7379,7 +7396,9 @@ var init_templates = __esm(() => {
7379
7396
  REASONING_EFFORT: "high",
7380
7397
  MAX_THINKING_TOKENS: "8000",
7381
7398
  ENABLE_STREAMING: "true",
7382
- MAX_OUTPUT_TOKENS: "64000"
7399
+ MAX_OUTPUT_TOKENS: "64000",
7400
+ CLAUDE_CODE_AUTO_COMPACT_WINDOW: "131072",
7401
+ CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: "75"
7383
7402
  },
7384
7403
  includeCoAuthoredBy: false,
7385
7404
  alwaysThinkingEnabled: false
@@ -7403,7 +7422,9 @@ var init_templates = __esm(() => {
7403
7422
  ENABLE_THINKING: "true",
7404
7423
  REASONING_EFFORT: "high",
7405
7424
  MAX_OUTPUT_TOKENS: "64000",
7406
- MAX_THINKING_TOKENS: "32000"
7425
+ MAX_THINKING_TOKENS: "32000",
7426
+ CLAUDE_CODE_AUTO_COMPACT_WINDOW: "131072",
7427
+ CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: "75"
7407
7428
  },
7408
7429
  includeCoAuthoredBy: false,
7409
7430
  alwaysThinkingEnabled: true
@@ -7493,7 +7514,9 @@ var init_templates = __esm(() => {
7493
7514
  ENABLE_THINKING: "true",
7494
7515
  REASONING_EFFORT: "high",
7495
7516
  MAX_THINKING_TOKENS: "16000",
7496
- MAX_OUTPUT_TOKENS: "65536"
7517
+ MAX_OUTPUT_TOKENS: "65536",
7518
+ CLAUDE_CODE_AUTO_COMPACT_WINDOW: "131072",
7519
+ CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: "75"
7497
7520
  },
7498
7521
  includeCoAuthoredBy: false,
7499
7522
  alwaysThinkingEnabled: false
@@ -7516,7 +7539,9 @@ var init_templates = __esm(() => {
7516
7539
  ENABLE_THINKING: "true",
7517
7540
  REASONING_EFFORT: "high",
7518
7541
  MAX_THINKING_TOKENS: "16000",
7519
- MAX_OUTPUT_TOKENS: "65536"
7542
+ MAX_OUTPUT_TOKENS: "65536",
7543
+ CLAUDE_CODE_AUTO_COMPACT_WINDOW: "131072",
7544
+ CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: "75"
7520
7545
  },
7521
7546
  includeCoAuthoredBy: false,
7522
7547
  alwaysThinkingEnabled: false
@@ -7539,7 +7564,9 @@ var init_templates = __esm(() => {
7539
7564
  ENABLE_THINKING: "true",
7540
7565
  REASONING_EFFORT: "high",
7541
7566
  MAX_THINKING_TOKENS: "16000",
7542
- MAX_OUTPUT_TOKENS: "65536"
7567
+ MAX_OUTPUT_TOKENS: "65536",
7568
+ CLAUDE_CODE_AUTO_COMPACT_WINDOW: "131072",
7569
+ CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: "75"
7543
7570
  },
7544
7571
  includeCoAuthoredBy: false,
7545
7572
  alwaysThinkingEnabled: false
@@ -7563,6 +7590,9 @@ var init_templates = __esm(() => {
7563
7590
  baseUrl: "https://token-plan-ams.xiaomimimo.com/anthropic"
7564
7591
  }
7565
7592
  };
7593
+ PROVIDER_REGION_MAPS = {
7594
+ "mimo-token": MIMO_TOKEN_REGIONS
7595
+ };
7566
7596
  });
7567
7597
 
7568
7598
  // src/util/json-file.ts
@@ -7665,6 +7695,22 @@ var init_constants = __esm(() => {
7665
7695
  };
7666
7696
  });
7667
7697
 
7698
+ // src/constants/env.ts
7699
+ var TUNABLE_ENV_VARS;
7700
+ var init_env = __esm(() => {
7701
+ TUNABLE_ENV_VARS = new Set([
7702
+ "MAX_OUTPUT_TOKENS",
7703
+ "MAX_THINKING_TOKENS",
7704
+ "REASONING_EFFORT",
7705
+ "ENABLE_THINKING",
7706
+ "ENABLE_STREAMING",
7707
+ "API_TIMEOUT_MS",
7708
+ "CLAUDE_CODE_EFFORT_LEVEL",
7709
+ "CLAUDE_CODE_AUTO_COMPACT_WINDOW",
7710
+ "CLAUDE_AUTOCOMPACT_PCT_OVERRIDE"
7711
+ ]);
7712
+ });
7713
+
7668
7714
  // node_modules/semver/internal/constants.js
7669
7715
  var require_constants = __commonJS((exports, module) => {
7670
7716
  var SEMVER_SPEC_VERSION = "2.0.0";
@@ -9492,22 +9538,11 @@ var require_semver2 = __commonJS((exports, module) => {
9492
9538
  });
9493
9539
 
9494
9540
  // src/paths.ts
9495
- var exports_paths = {};
9496
- __export(exports_paths, {
9497
- getBaseDir: () => getBaseDir,
9498
- PINNED_CLAUDE_BIN: () => PINNED_CLAUDE_BIN,
9499
- PINNED_BIN_DIR: () => PINNED_BIN_DIR
9500
- });
9501
9541
  import { homedir } from "node:os";
9502
- import { join as join2 } from "node:path";
9503
9542
  function getBaseDir() {
9504
9543
  return process.env.CLAUDE_MULTI_HOME || homedir();
9505
9544
  }
9506
- var PINNED_BIN_DIR, PINNED_CLAUDE_BIN;
9507
- var init_paths = __esm(() => {
9508
- PINNED_BIN_DIR = join2(homedir(), ".claude-multi", "bin");
9509
- PINNED_CLAUDE_BIN = process.platform === "win32" ? join2(PINNED_BIN_DIR, "node_modules", ".bin", "claude.cmd") : join2(PINNED_BIN_DIR, "node_modules", ".bin", "claude");
9510
- });
9545
+ var init_paths = () => {};
9511
9546
 
9512
9547
  // src/util/runtime.ts
9513
9548
  function detectPackageManager() {
@@ -9521,27 +9556,12 @@ function detectPackageManager() {
9521
9556
  }
9522
9557
 
9523
9558
  // src/version.ts
9524
- var exports_version = {};
9525
- __export(exports_version, {
9526
- upgradeClaudeMulti: () => upgradeClaudeMulti,
9527
- updateClaudeCode: () => updateClaudeCode,
9528
- isThirdPartyApiBroken: () => isThirdPartyApiBroken,
9529
- installPinnedClaude: () => installPinnedClaude,
9530
- getPinnedBinaryVersion: () => getPinnedBinaryVersion,
9531
- getLatestVersion: () => getLatestVersion,
9532
- getLatestClaudeMultiVersion: () => getLatestClaudeMultiVersion,
9533
- getCurrentVersion: () => getCurrentVersion,
9534
- getClaudeMultiVersion: () => getClaudeMultiVersion,
9535
- checkForUpdates: () => checkForUpdates,
9536
- checkForClaudeMultiUpdates: () => checkForClaudeMultiUpdates,
9537
- COMPATIBLE_CLAUDE_VERSION: () => COMPATIBLE_CLAUDE_VERSION
9538
- });
9539
9559
  import { execSync } from "node:child_process";
9540
- import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync, mkdirSync } from "node:fs";
9541
- import { join as join3, dirname } from "node:path";
9560
+ import { readFileSync as readFileSync2 } from "node:fs";
9561
+ import { join as join2, dirname } from "node:path";
9542
9562
  import { fileURLToPath } from "node:url";
9543
9563
  function getClaudeMultiVersion() {
9544
- const pkgPath = join3(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
9564
+ const pkgPath = join2(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
9545
9565
  const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
9546
9566
  if (typeof pkg !== "object" || pkg === null || typeof pkg.version !== "string") {
9547
9567
  throw new ClaudeMultiError(ErrorCode.CONFIG_CORRUPTED, "Could not read package version");
@@ -9601,40 +9621,6 @@ function getCurrentVersion() {
9601
9621
  return null;
9602
9622
  }
9603
9623
  }
9604
- function isThirdPartyApiBroken(version) {
9605
- return import_semver.default.gte(version, "2.1.154") && import_semver.default.lt(version, "2.1.156");
9606
- }
9607
- function getPinnedBinaryVersion() {
9608
- const pkgJsonPath = join3(PINNED_BIN_DIR, "node_modules", "@anthropic-ai", "claude-code", "package.json");
9609
- try {
9610
- if (!existsSync2(pkgJsonPath))
9611
- return null;
9612
- const pkg = JSON.parse(readFileSync2(pkgJsonPath, "utf-8"));
9613
- return pkg.version ?? null;
9614
- } catch {
9615
- return null;
9616
- }
9617
- }
9618
- function installPinnedClaude() {
9619
- const pm = detectPackageManager();
9620
- mkdirSync(PINNED_BIN_DIR, { recursive: true });
9621
- const pkgJsonPath = join3(PINNED_BIN_DIR, "package.json");
9622
- if (!existsSync2(pkgJsonPath)) {
9623
- writeFileSync(pkgJsonPath, JSON.stringify({ dependencies: {} }, null, 2));
9624
- }
9625
- const pkg = `@anthropic-ai/claude-code@${COMPATIBLE_CLAUDE_VERSION}`;
9626
- const commands = {
9627
- bun: `bun add --cwd ${PINNED_BIN_DIR} ${pkg}`,
9628
- npm: `npm install --prefix ${PINNED_BIN_DIR} ${pkg}`,
9629
- pnpm: `pnpm add --dir ${PINNED_BIN_DIR} ${pkg}`,
9630
- deno: `cd ${PINNED_BIN_DIR} && npm install ${pkg}`
9631
- };
9632
- try {
9633
- execSync(commands[pm], { stdio: "inherit" });
9634
- } catch (err) {
9635
- throw new ClaudeMultiError(ErrorCode.UPDATE_FAILED, `Failed to install pinned Claude v${COMPATIBLE_CLAUDE_VERSION}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
9636
- }
9637
- }
9638
9624
  async function getLatestVersion() {
9639
9625
  try {
9640
9626
  const response = await fetch("https://registry.npmjs.org/@anthropic-ai/claude-code/latest");
@@ -9669,20 +9655,15 @@ function updateClaudeCode() {
9669
9655
  throw new ClaudeMultiError(ErrorCode.UPDATE_FAILED, `Failed to update @anthropic-ai/claude-code: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
9670
9656
  }
9671
9657
  }
9672
- var import_semver, COMPATIBLE_CLAUDE_VERSION = "2.1.156";
9673
9658
  var init_version = __esm(() => {
9674
9659
  init_errors();
9675
- init_paths();
9676
- import_semver = __toESM(require_semver2(), 1);
9677
9660
  });
9678
9661
 
9679
9662
  // src/wrapper.ts
9680
9663
  var exports_wrapper = {};
9681
9664
  __export(exports_wrapper, {
9682
- tryGetGlobalClaudePath: () => tryGetGlobalClaudePath,
9683
9665
  tryGetClaudePath: () => tryGetClaudePath,
9684
9666
  removeWrapper: () => removeWrapper,
9685
- getGlobalClaudePath: () => getGlobalClaudePath,
9686
9667
  getDefaultBinaryPath: () => getDefaultBinaryPath,
9687
9668
  getClaudePath: () => getClaudePath,
9688
9669
  generateWrapperScriptSafe: () => generateWrapperScriptSafe,
@@ -9691,7 +9672,7 @@ __export(exports_wrapper, {
9691
9672
  createWrapper: () => createWrapper,
9692
9673
  buildWrapperScript: () => buildWrapperScript
9693
9674
  });
9694
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, chmodSync, unlinkSync } from "node:fs";
9675
+ import { existsSync as existsSync2, mkdirSync, chmodSync, unlinkSync } from "node:fs";
9695
9676
  import { writeFile as writeFile2 } from "node:fs/promises";
9696
9677
  import { resolve, dirname as dirname2 } from "node:path";
9697
9678
  import { execSync as execSync2 } from "node:child_process";
@@ -9699,19 +9680,15 @@ function getClaudePath() {
9699
9680
  const override = process.env.CLAUDE_MULTI_CLAUDE_PATH;
9700
9681
  if (override) {
9701
9682
  const resolved = resolve(override);
9702
- if (!existsSync3(resolved)) {
9683
+ if (!existsSync2(resolved)) {
9703
9684
  throw new ClaudeMultiError(ErrorCode.CLAUDE_NOT_FOUND, `CLAUDE_MULTI_CLAUDE_PATH points to ${resolved} but that file does not exist`);
9704
9685
  }
9705
9686
  return resolved;
9706
9687
  }
9707
- if (existsSync3(PINNED_CLAUDE_BIN)) {
9708
- return PINNED_CLAUDE_BIN;
9709
- }
9710
9688
  try {
9711
9689
  const command = process.platform === "win32" ? "where claude" : "which claude";
9712
9690
  const claudePath = execSync2(command, { encoding: "utf-8" }).trim();
9713
- const firstPath = claudePath.split(`
9714
- `)[0];
9691
+ const firstPath = claudePath.split(/\r?\n/)[0];
9715
9692
  if (!firstPath) {
9716
9693
  throw new ClaudeMultiError(ErrorCode.CLAUDE_NOT_FOUND, "Could not determine Claude path");
9717
9694
  }
@@ -9729,37 +9706,6 @@ function tryGetClaudePath() {
9729
9706
  return null;
9730
9707
  }
9731
9708
  }
9732
- function getGlobalClaudePath() {
9733
- const override = process.env.CLAUDE_MULTI_CLAUDE_PATH;
9734
- if (override) {
9735
- const resolved = resolve(override);
9736
- if (!existsSync3(resolved)) {
9737
- throw new ClaudeMultiError(ErrorCode.CLAUDE_NOT_FOUND, `CLAUDE_MULTI_CLAUDE_PATH points to ${resolved} but that file does not exist`);
9738
- }
9739
- return resolved;
9740
- }
9741
- try {
9742
- const command = process.platform === "win32" ? "where claude" : "which claude";
9743
- const claudePath = execSync2(command, { encoding: "utf-8" }).trim();
9744
- const firstPath = claudePath.split(`
9745
- `)[0];
9746
- if (!firstPath) {
9747
- throw new ClaudeMultiError(ErrorCode.CLAUDE_NOT_FOUND, "Could not determine Claude path");
9748
- }
9749
- return firstPath.trim();
9750
- } catch (err) {
9751
- if (err instanceof ClaudeMultiError)
9752
- throw err;
9753
- throw new ClaudeMultiError(ErrorCode.CLAUDE_NOT_FOUND, "Claude Code is not installed. Please install @anthropic-ai/claude-code first.", { cause: err });
9754
- }
9755
- }
9756
- function tryGetGlobalClaudePath() {
9757
- try {
9758
- return getGlobalClaudePath();
9759
- } catch {
9760
- return null;
9761
- }
9762
- }
9763
9709
  function generateWrapperScriptSafe(options) {
9764
9710
  const claudePath = tryGetClaudePath();
9765
9711
  if (!claudePath)
@@ -9791,11 +9737,11 @@ set "CLAUDE_CONFIG_DIR=${options.configDir}"
9791
9737
  async function createWrapper(options) {
9792
9738
  const { binaryPath, configDir } = options;
9793
9739
  const binDir = dirname2(binaryPath);
9794
- if (!existsSync3(binDir)) {
9795
- mkdirSync2(binDir, { recursive: true });
9740
+ if (!existsSync2(binDir)) {
9741
+ mkdirSync(binDir, { recursive: true });
9796
9742
  }
9797
- if (!existsSync3(configDir)) {
9798
- mkdirSync2(configDir, { recursive: true });
9743
+ if (!existsSync2(configDir)) {
9744
+ mkdirSync(configDir, { recursive: true });
9799
9745
  }
9800
9746
  if (process.platform === "win32") {
9801
9747
  const script = generateWindowsWrapperScript(options);
@@ -9807,7 +9753,7 @@ async function createWrapper(options) {
9807
9753
  }
9808
9754
  }
9809
9755
  function removeWrapper(binaryPath) {
9810
- if (existsSync3(binaryPath)) {
9756
+ if (existsSync2(binaryPath)) {
9811
9757
  unlinkSync(binaryPath);
9812
9758
  }
9813
9759
  }
@@ -9832,7 +9778,6 @@ function getDefaultBinaryPath(name) {
9832
9778
  }
9833
9779
  var init_wrapper = __esm(() => {
9834
9780
  init_errors();
9835
- init_paths();
9836
9781
  });
9837
9782
 
9838
9783
  // src/migration.ts
@@ -9849,11 +9794,11 @@ __export(exports_migration, {
9849
9794
  LEGACY_INSTANCE_VERSION: () => LEGACY_INSTANCE_VERSION,
9850
9795
  CONFIG_VERSION: () => CONFIG_VERSION
9851
9796
  });
9852
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync, copyFileSync, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync2, chmodSync as chmodSync2 } from "node:fs";
9853
- import { join as join4, dirname as dirname3 } from "node:path";
9797
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, copyFileSync, readFileSync as readFileSync3, rmSync, writeFileSync, chmodSync as chmodSync2 } from "node:fs";
9798
+ import { join as join3, dirname as dirname3 } from "node:path";
9854
9799
  function updateClaudeJson(configDir) {
9855
- const stateFile = join4(configDir, ".claude.json");
9856
- if (!existsSync4(stateFile))
9800
+ const stateFile = join3(configDir, ".claude.json");
9801
+ if (!existsSync3(stateFile))
9857
9802
  return;
9858
9803
  try {
9859
9804
  const raw = JSON.parse(readFileSync3(stateFile, "utf-8"));
@@ -9863,7 +9808,7 @@ function updateClaudeJson(configDir) {
9863
9808
  changed = true;
9864
9809
  }
9865
9810
  if (changed) {
9866
- writeFileSync2(stateFile, JSON.stringify(raw, null, 2), "utf-8");
9811
+ writeFileSync(stateFile, JSON.stringify(raw, null, 2), "utf-8");
9867
9812
  }
9868
9813
  } catch {}
9869
9814
  }
@@ -9872,10 +9817,10 @@ function needsInstanceMigration(config) {
9872
9817
  const stored = config.instanceMigrationVersion;
9873
9818
  if (!stored)
9874
9819
  return INSTANCE_MIGRATIONS.length > 0;
9875
- const storedCoerced = import_semver2.default.coerce(stored);
9820
+ const storedCoerced = import_semver.default.coerce(stored);
9876
9821
  if (!storedCoerced)
9877
9822
  return INSTANCE_MIGRATIONS.length > 0;
9878
- return INSTANCE_MIGRATIONS.some((m) => import_semver2.default.gt(m.version, storedCoerced) && import_semver2.default.lte(m.version, current));
9823
+ return INSTANCE_MIGRATIONS.some((m) => import_semver.default.gt(m.version, storedCoerced) && import_semver.default.lte(m.version, current));
9879
9824
  }
9880
9825
  async function runInstanceMigrations(config) {
9881
9826
  if (!needsInstanceMigration(config))
@@ -9885,8 +9830,8 @@ async function runInstanceMigrations(config) {
9885
9830
  try {
9886
9831
  createBackup(config);
9887
9832
  const currentVersion = getClaudeMultiVersion();
9888
- const stored = import_semver2.default.coerce(config.instanceMigrationVersion || "0.0.0");
9889
- const applicable = INSTANCE_MIGRATIONS.filter((m) => stored ? import_semver2.default.gt(m.version, stored) && import_semver2.default.lte(m.version, currentVersion) : true).toSorted((a, b) => import_semver2.default.compare(a.version, b.version));
9833
+ const stored = import_semver.default.coerce(config.instanceMigrationVersion || "0.0.0");
9834
+ const applicable = INSTANCE_MIGRATIONS.filter((m) => stored ? import_semver.default.gt(m.version, stored) && import_semver.default.lte(m.version, currentVersion) : true).toSorted((a, b) => import_semver.default.compare(a.version, b.version));
9890
9835
  for (const migration of applicable) {
9891
9836
  for (let i = 0;i < config.instances.length; i++) {
9892
9837
  const migrated = await migration.migrate({ ...config.instances[i] }, config);
@@ -9900,26 +9845,31 @@ async function runInstanceMigrations(config) {
9900
9845
  }
9901
9846
  }
9902
9847
  function getBackupDir() {
9903
- return join4(getBaseDir(), ".claude-multi", "backups");
9848
+ return join3(getBaseDir(), ".claude-multi", "backups");
9904
9849
  }
9905
9850
  function getLockFile() {
9906
- return join4(getBaseDir(), ".claude-multi", ".migration.lock");
9851
+ return join3(getBaseDir(), ".claude-multi", ".migration.lock");
9907
9852
  }
9908
9853
  function createLock() {
9909
9854
  const lockFile = getLockFile();
9910
9855
  const lockDir = dirname3(lockFile);
9911
- if (!existsSync4(lockDir))
9912
- mkdirSync3(lockDir, { recursive: true });
9913
- if (existsSync4(lockFile)) {
9856
+ if (!existsSync3(lockDir))
9857
+ mkdirSync2(lockDir, { recursive: true });
9858
+ if (existsSync3(lockFile)) {
9914
9859
  try {
9915
9860
  const raw = JSON.parse(readFileSync3(lockFile, "utf-8"));
9916
9861
  if (typeof raw === "object" && raw !== null && "pid" in raw && typeof raw.pid === "number") {
9917
9862
  const lock = raw;
9918
- try {
9919
- process.kill(lock.pid, 0);
9920
- return false;
9921
- } catch {
9863
+ const lockAge = Date.now() - new Date(lock.startedAt).getTime();
9864
+ if (lockAge > 30 * 60 * 1000) {
9922
9865
  rmSync(lockFile, { force: true });
9866
+ } else {
9867
+ try {
9868
+ process.kill(lock.pid, 0);
9869
+ return false;
9870
+ } catch {
9871
+ rmSync(lockFile, { force: true });
9872
+ }
9923
9873
  }
9924
9874
  } else {
9925
9875
  rmSync(lockFile, { force: true });
@@ -9928,7 +9878,7 @@ function createLock() {
9928
9878
  rmSync(lockFile, { force: true });
9929
9879
  }
9930
9880
  }
9931
- writeFileSync2(lockFile, JSON.stringify({
9881
+ writeFileSync(lockFile, JSON.stringify({
9932
9882
  pid: process.pid,
9933
9883
  startedAt: new Date().toISOString()
9934
9884
  }), "utf-8");
@@ -9953,30 +9903,30 @@ function clearMigrationFailure(config) {
9953
9903
  }
9954
9904
  function createBackup(config) {
9955
9905
  const backupDir = getBackupDir();
9956
- if (!existsSync4(backupDir)) {
9957
- mkdirSync3(backupDir, { recursive: true });
9906
+ if (!existsSync3(backupDir)) {
9907
+ mkdirSync2(backupDir, { recursive: true });
9958
9908
  }
9959
9909
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
9960
9910
  const rand = Math.random().toString(36).slice(2, 6);
9961
9911
  const fromV = config.version || "1.0.0";
9962
- const backupPath = join4(backupDir, `${ts}-${rand}-v${fromV}-to-v${CONFIG_VERSION}`);
9963
- mkdirSync3(backupPath, { recursive: true });
9964
- const configSrc = join4(getBaseDir(), ".claude-multi", "config.json");
9965
- if (existsSync4(configSrc)) {
9966
- copyFileSync(configSrc, join4(backupPath, "config.json"));
9912
+ const backupPath = join3(backupDir, `${ts}-${rand}-v${fromV}-to-v${CONFIG_VERSION}`);
9913
+ mkdirSync2(backupPath, { recursive: true });
9914
+ const configSrc = join3(getBaseDir(), ".claude-multi", "config.json");
9915
+ if (existsSync3(configSrc)) {
9916
+ copyFileSync(configSrc, join3(backupPath, "config.json"));
9967
9917
  }
9968
- const instancesDir = join4(backupPath, "instances");
9918
+ const instancesDir = join3(backupPath, "instances");
9969
9919
  for (const inst of config.instances) {
9970
- const settingsFile = join4(inst.configDir, "settings.json");
9971
- if (existsSync4(settingsFile)) {
9972
- const instDir = join4(instancesDir, inst.name);
9973
- mkdirSync3(instDir, { recursive: true });
9974
- copyFileSync(settingsFile, join4(instDir, "settings.json"));
9920
+ const settingsFile = join3(inst.configDir, "settings.json");
9921
+ if (existsSync3(settingsFile)) {
9922
+ const instDir = join3(instancesDir, inst.name);
9923
+ mkdirSync2(instDir, { recursive: true });
9924
+ copyFileSync(settingsFile, join3(instDir, "settings.json"));
9975
9925
  }
9976
9926
  }
9977
9927
  const backups = readdirSync(backupDir).toSorted();
9978
9928
  for (let i = 0;i < backups.length - 3; i++) {
9979
- rmSync(join4(backupDir, backups[i]), { force: true, recursive: true });
9929
+ rmSync(join3(backupDir, backups[i]), { force: true, recursive: true });
9980
9930
  }
9981
9931
  return backupPath;
9982
9932
  }
@@ -9994,7 +9944,7 @@ function runMigration(config) {
9994
9944
  if (!inst.name || !inst.configDir || !inst.binaryPath) {
9995
9945
  warnings.push(`Instance entry missing required fields, skipping: ${JSON.stringify(inst)}`);
9996
9946
  }
9997
- if (inst.configDir && !existsSync4(inst.configDir)) {
9947
+ if (inst.configDir && !existsSync3(inst.configDir)) {
9998
9948
  warnings.push(`Instance '${inst.name}' configDir does not exist: ${inst.configDir}`);
9999
9949
  }
10000
9950
  }
@@ -10027,18 +9977,19 @@ function getMigrationStatus(config) {
10027
9977
  }
10028
9978
  function listBackups() {
10029
9979
  const backupDir = getBackupDir();
10030
- if (!existsSync4(backupDir))
9980
+ if (!existsSync3(backupDir))
10031
9981
  return [];
10032
9982
  return readdirSync(backupDir).toSorted().toReversed();
10033
9983
  }
10034
- var import_semver2, CONFIG_VERSION = "2.0.0", LEGACY_INSTANCE_VERSION = "0.5", INSTANCE_MIGRATIONS;
9984
+ var import_semver, CONFIG_VERSION = "2.0.0", LEGACY_INSTANCE_VERSION = "0.5", INSTANCE_MIGRATIONS;
10035
9985
  var init_migration = __esm(() => {
10036
9986
  init_paths();
10037
9987
  init_constants();
10038
9988
  init_version();
9989
+ init_env();
10039
9990
  init_wrapper();
10040
9991
  init_templates();
10041
- import_semver2 = __toESM(require_semver2(), 1);
9992
+ import_semver = __toESM(require_semver2(), 1);
10042
9993
  INSTANCE_MIGRATIONS = [
10043
9994
  {
10044
9995
  version: "0.6.2",
@@ -10048,17 +9999,19 @@ var init_migration = __esm(() => {
10048
9999
  if (instance.createdWithVersion && instance.createdWithVersion !== LEGACY_INSTANCE_VERSION && instance.createdWithVersion === currentVersion) {
10049
10000
  return Promise.resolve(instance);
10050
10001
  }
10051
- if (existsSync4(instance.binaryPath)) {
10052
- const claudePath = tryGetGlobalClaudePath();
10002
+ if (existsSync3(instance.binaryPath)) {
10003
+ const claudePath = tryGetClaudePath();
10053
10004
  if (claudePath !== null) {
10054
10005
  const expected = buildWrapperScript(instance, claudePath);
10055
10006
  try {
10056
10007
  const current = readFileSync3(instance.binaryPath, "utf-8");
10057
10008
  if (current !== expected) {
10058
- writeFileSync2(instance.binaryPath, expected, { mode: 493 });
10009
+ writeFileSync(instance.binaryPath, expected, { mode: 493 });
10059
10010
  chmodSync2(instance.binaryPath, 493);
10060
10011
  }
10061
10012
  } catch {}
10013
+ } else {
10014
+ console.warn(`[migration] Could not find claude binary, skipping wrapper regeneration for instance: ${instance.name}`);
10062
10015
  }
10063
10016
  }
10064
10017
  updateClaudeJson(instance.configDir);
@@ -10069,28 +10022,53 @@ var init_migration = __esm(() => {
10069
10022
  version: "0.6.3",
10070
10023
  description: "Sync provider template env vars (model names, thinking/output limits) to latest",
10071
10024
  migrate: (instance) => {
10025
+ const currentVersion = getClaudeMultiVersion();
10026
+ if (instance.createdWithVersion && instance.createdWithVersion !== LEGACY_INSTANCE_VERSION && instance.createdWithVersion === currentVersion) {
10027
+ return Promise.resolve(instance);
10028
+ }
10072
10029
  const providerName = instance.providerTemplate ?? detectProvider(instance.configDir);
10073
10030
  if (!providerName)
10074
10031
  return Promise.resolve(instance);
10075
- const template = getProviderTemplate(providerName);
10032
+ let template = getProviderTemplate(providerName);
10076
10033
  if (!template)
10077
10034
  return Promise.resolve(instance);
10078
- const settingsFile = join4(instance.configDir, "settings.json");
10035
+ const settingsFile = join3(instance.configDir, "settings.json");
10079
10036
  try {
10080
- if (!existsSync4(settingsFile))
10037
+ if (!existsSync3(settingsFile))
10081
10038
  return Promise.resolve(instance);
10082
10039
  const existing = JSON.parse(readFileSync3(settingsFile, "utf-8"));
10083
10040
  const existingEnv = existing.env ?? {};
10084
10041
  const apiKey = existingEnv.ANTHROPIC_AUTH_TOKEN ?? "";
10042
+ const existingBaseUrl = existingEnv.ANTHROPIC_BASE_URL;
10043
+ let resolvedRegional = false;
10044
+ if (providerHasRegions(providerName)) {
10045
+ const providerRegions = getProviderRegions(providerName);
10046
+ const detectedRegion = detectRegionFromBaseUrl(existingBaseUrl ?? "") ?? instance.providerRegion;
10047
+ if (detectedRegion && providerRegions && detectedRegion in providerRegions) {
10048
+ template = resolveRegionTemplate(template, detectedRegion);
10049
+ instance.providerRegion = detectedRegion;
10050
+ resolvedRegional = true;
10051
+ }
10052
+ }
10085
10053
  const templateSettings = structuredClone(template.settings);
10086
10054
  const newEnv = { ...templateSettings.env, ANTHROPIC_AUTH_TOKEN: apiKey };
10055
+ if (providerHasRegions(providerName) && !resolvedRegional && existingBaseUrl) {
10056
+ newEnv.ANTHROPIC_BASE_URL = existingBaseUrl;
10057
+ }
10058
+ for (const key of TUNABLE_ENV_VARS) {
10059
+ if (key in existingEnv && existingEnv[key] !== templateSettings.env[key]) {
10060
+ newEnv[key] = existingEnv[key];
10061
+ }
10062
+ }
10087
10063
  existing.env = { ...existingEnv, ...newEnv };
10088
10064
  existing.includeCoAuthoredBy = template.settings.includeCoAuthoredBy;
10089
10065
  existing.alwaysThinkingEnabled = template.settings.alwaysThinkingEnabled;
10090
- writeFileSync2(settingsFile, JSON.stringify(existing, null, 2), "utf-8");
10091
- } catch {}
10092
- if (!instance.providerTemplate) {
10093
- instance.providerTemplate = providerName;
10066
+ writeFileSync(settingsFile, JSON.stringify(existing, null, 2), "utf-8");
10067
+ if (!instance.providerTemplate) {
10068
+ instance.providerTemplate = providerName;
10069
+ }
10070
+ } catch (err) {
10071
+ console.warn(`[migration] Failed to sync provider template for '${instance.name}': ${err instanceof Error ? err.message : String(err)}`);
10094
10072
  }
10095
10073
  return Promise.resolve(instance);
10096
10074
  }
@@ -10105,6 +10083,7 @@ __export(exports_config, {
10105
10083
  validatePluginOperation: () => validatePluginOperation,
10106
10084
  updateInstanceAutoSync: () => updateInstanceAutoSync,
10107
10085
  unsyncPluginsAndSkills: () => unsyncPluginsAndSkills,
10086
+ syncProviderTemplateForInstance: () => syncProviderTemplateForInstance,
10108
10087
  syncPluginsAndSkills: () => syncPluginsAndSkills,
10109
10088
  setTestDefaultClaudeDir: () => setTestDefaultClaudeDir,
10110
10089
  setTestConfigDir: () => setTestConfigDir,
@@ -10151,11 +10130,11 @@ __export(exports_config, {
10151
10130
  clearTestConfigDir: () => clearTestConfigDir,
10152
10131
  addInstance: () => addInstance
10153
10132
  });
10154
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync2, statSync, readlinkSync, rmSync as rmSync2, lstatSync, readFileSync as readFileSync4, renameSync } from "node:fs";
10133
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync2, statSync, readlinkSync, rmSync as rmSync2, lstatSync, readFileSync as readFileSync4, writeFileSync as writeFileSync2, renameSync } from "node:fs";
10155
10134
  import { readFile as readFile2, writeFile as writeFile3, copyFile, mkdir, symlink } from "node:fs/promises";
10156
10135
  import { homedir as homedir2 } from "node:os";
10157
10136
  import { randomBytes as randomBytes2 } from "node:crypto";
10158
- import { dirname as dirname4, isAbsolute, join as join5, relative, resolve as resolve2 } from "node:path";
10137
+ import { dirname as dirname4, isAbsolute, join as join4, relative, resolve as resolve2 } from "node:path";
10159
10138
  function setTestConfigDir(dir) {
10160
10139
  _testConfigDir = dir;
10161
10140
  }
@@ -10163,19 +10142,19 @@ function clearTestConfigDir() {
10163
10142
  _testConfigDir = undefined;
10164
10143
  }
10165
10144
  function getConfigDir() {
10166
- return _testConfigDir ?? join5(homedir2(), ".claude-multi");
10145
+ return _testConfigDir ?? join4(homedir2(), ".claude-multi");
10167
10146
  }
10168
10147
  function getConfigFile() {
10169
- return join5(getConfigDir(), "config.json");
10148
+ return join4(getConfigDir(), "config.json");
10170
10149
  }
10171
10150
  async function copyDirRecursive(source, target) {
10172
- if (!existsSync5(target)) {
10151
+ if (!existsSync4(target)) {
10173
10152
  await mkdir(target, { recursive: true });
10174
10153
  }
10175
10154
  const entries = readdirSync2(source);
10176
10155
  await Promise.all(entries.map(async (entry) => {
10177
- const src = join5(source, entry);
10178
- const tgt = join5(target, entry);
10156
+ const src = join4(source, entry);
10157
+ const tgt = join4(target, entry);
10179
10158
  const s = statSync(src);
10180
10159
  if (s.isDirectory()) {
10181
10160
  await copyDirRecursive(src, tgt);
@@ -10192,8 +10171,8 @@ function lstatSafe(path) {
10192
10171
  }
10193
10172
  }
10194
10173
  function ensureConfigDir() {
10195
- if (!existsSync5(getConfigDir())) {
10196
- mkdirSync4(getConfigDir(), { recursive: true });
10174
+ if (!existsSync4(getConfigDir())) {
10175
+ mkdirSync3(getConfigDir(), { recursive: true });
10197
10176
  }
10198
10177
  }
10199
10178
  function isConfig(raw) {
@@ -10201,7 +10180,7 @@ function isConfig(raw) {
10201
10180
  }
10202
10181
  async function loadConfig() {
10203
10182
  ensureConfigDir();
10204
- if (!existsSync5(getConfigFile())) {
10183
+ if (!existsSync4(getConfigFile())) {
10205
10184
  const defaultConfig = {
10206
10185
  instances: [],
10207
10186
  version: "2.0.0"
@@ -10297,7 +10276,7 @@ function getDefaultClaudeDir() {
10297
10276
  if (_testDefaultClaudeDir) {
10298
10277
  return _testDefaultClaudeDir;
10299
10278
  }
10300
- return join5(homedir2(), ".claude");
10279
+ return join4(homedir2(), ".claude");
10301
10280
  }
10302
10281
  function isBrokenSymlink(path) {
10303
10282
  try {
@@ -10313,7 +10292,7 @@ function isBrokenSymlink(path) {
10313
10292
  } else {
10314
10293
  targetPath = resolve2(dirname4(path), target);
10315
10294
  }
10316
- return !existsSync5(targetPath);
10295
+ return !existsSync4(targetPath);
10317
10296
  } catch {
10318
10297
  return false;
10319
10298
  }
@@ -10321,7 +10300,7 @@ function isBrokenSymlink(path) {
10321
10300
  function detectBrokenSymlinks(configDir) {
10322
10301
  const result = { broken: [], all: [] };
10323
10302
  for (const dir of SYNC_DIRS) {
10324
- const targetPath = join5(configDir, dir);
10303
+ const targetPath = join4(configDir, dir);
10325
10304
  try {
10326
10305
  const stats = lstatSync(targetPath);
10327
10306
  result.all.push(dir);
@@ -10334,13 +10313,13 @@ function detectBrokenSymlinks(configDir) {
10334
10313
  }
10335
10314
  function hasDefaultClaudeConfig() {
10336
10315
  const defaultDir = getDefaultClaudeDir();
10337
- const settingsFile = join5(defaultDir, "settings.json");
10338
- return existsSync5(defaultDir) && existsSync5(settingsFile);
10316
+ const settingsFile = join4(defaultDir, "settings.json");
10317
+ return existsSync4(defaultDir) && existsSync4(settingsFile);
10339
10318
  }
10340
10319
  async function copySettingsFromDefault(targetConfigDir) {
10341
10320
  const defaultDir = getDefaultClaudeDir();
10342
- const sourceSettings = join5(defaultDir, "settings.json");
10343
- if (!existsSync5(sourceSettings)) {
10321
+ const sourceSettings = join4(defaultDir, "settings.json");
10322
+ if (!existsSync4(sourceSettings)) {
10344
10323
  throw new ClaudeMultiError(ErrorCode.DEFAULT_SETTINGS_NOT_FOUND, "Default Claude settings.json not found");
10345
10324
  }
10346
10325
  const content = await readFile2(sourceSettings, "utf-8");
@@ -10356,18 +10335,18 @@ async function copySettingsFromDefault(targetConfigDir) {
10356
10335
  safeSettings[key] = settings[key];
10357
10336
  }
10358
10337
  }
10359
- if (!existsSync5(targetConfigDir)) {
10338
+ if (!existsSync4(targetConfigDir)) {
10360
10339
  await mkdir(targetConfigDir, { recursive: true });
10361
10340
  }
10362
- const targetSettings = join5(targetConfigDir, "settings.json");
10341
+ const targetSettings = join4(targetConfigDir, "settings.json");
10363
10342
  await writeFile3(targetSettings, JSON.stringify(safeSettings, null, 2), "utf-8");
10364
10343
  }
10365
10344
  async function copyAllFromDefault(targetConfigDir, autoSync = false) {
10366
10345
  const defaultDir = getDefaultClaudeDir();
10367
- if (!existsSync5(defaultDir)) {
10346
+ if (!existsSync4(defaultDir)) {
10368
10347
  throw new ClaudeMultiError(ErrorCode.DEFAULT_DIR_NOT_FOUND, "Default Claude directory not found");
10369
10348
  }
10370
- if (!existsSync5(targetConfigDir)) {
10349
+ if (!existsSync4(targetConfigDir)) {
10371
10350
  await mkdir(targetConfigDir, { recursive: true });
10372
10351
  }
10373
10352
  const excludeFiles = [
@@ -10390,8 +10369,8 @@ async function copyAllFromDefault(targetConfigDir, autoSync = false) {
10390
10369
  const copyRecursive = async (source, target) => {
10391
10370
  const entries = readdirSync2(source);
10392
10371
  for (const entry of entries) {
10393
- const sourcePath = join5(source, entry);
10394
- const targetPath = join5(target, entry);
10372
+ const sourcePath = join4(source, entry);
10373
+ const targetPath = join4(target, entry);
10395
10374
  const stat = statSync(sourcePath);
10396
10375
  if (excludeSet.has(entry)) {
10397
10376
  continue;
@@ -10415,7 +10394,7 @@ async function copyAllFromDefault(targetConfigDir, autoSync = false) {
10415
10394
  await symlink(sourcePath, targetPath, "dir");
10416
10395
  console.log(` Symlinked: ${entry} -> ${sourcePath}`);
10417
10396
  } else {
10418
- if (!existsSync5(targetPath)) {
10397
+ if (!existsSync4(targetPath)) {
10419
10398
  await mkdir(targetPath, { recursive: true });
10420
10399
  }
10421
10400
  await copyRecursive(sourcePath, targetPath);
@@ -10428,11 +10407,11 @@ async function copyAllFromDefault(targetConfigDir, autoSync = false) {
10428
10407
  await copyRecursive(defaultDir, targetConfigDir);
10429
10408
  }
10430
10409
  async function detectMcpConfigurations(configDir) {
10431
- if (!existsSync5(configDir)) {
10410
+ if (!existsSync4(configDir)) {
10432
10411
  return null;
10433
10412
  }
10434
- const settingsFile = join5(configDir, "settings.json");
10435
- if (existsSync5(settingsFile)) {
10413
+ const settingsFile = join4(configDir, "settings.json");
10414
+ if (existsSync4(settingsFile)) {
10436
10415
  try {
10437
10416
  const settingsContent = await readFile2(settingsFile, "utf-8");
10438
10417
  const settings = JSON.parse(settingsContent);
@@ -10443,8 +10422,8 @@ async function detectMcpConfigurations(configDir) {
10443
10422
  }
10444
10423
  const mcpFiles = ["mcp.json", "mcp-servers.json", "claude-mcp.json"];
10445
10424
  for (const mcpFile of mcpFiles) {
10446
- const mcpFilePath = join5(configDir, mcpFile);
10447
- if (existsSync5(mcpFilePath)) {
10425
+ const mcpFilePath = join4(configDir, mcpFile);
10426
+ if (existsSync4(mcpFilePath)) {
10448
10427
  try {
10449
10428
  const mcpContent = await readFile2(mcpFilePath, "utf-8");
10450
10429
  const mcpConfig = JSON.parse(mcpContent);
@@ -10463,12 +10442,12 @@ async function hasDefaultMcpConfig() {
10463
10442
  }
10464
10443
  async function copyMcpServersFromDefault(targetConfigDir) {
10465
10444
  const defaultDir = getDefaultClaudeDir();
10466
- if (!existsSync5(targetConfigDir)) {
10445
+ if (!existsSync4(targetConfigDir)) {
10467
10446
  await mkdir(targetConfigDir, { recursive: true });
10468
10447
  }
10469
- const pluginsDir = join5(targetConfigDir, "plugins");
10470
- const defaultPluginsDir = join5(defaultDir, "plugins");
10471
- if (existsSync5(defaultPluginsDir) && !existsSync5(pluginsDir)) {
10448
+ const pluginsDir = join4(targetConfigDir, "plugins");
10449
+ const defaultPluginsDir = join4(defaultDir, "plugins");
10450
+ if (existsSync4(defaultPluginsDir) && !existsSync4(pluginsDir)) {
10472
10451
  await copyDirRecursive(defaultPluginsDir, pluginsDir);
10473
10452
  }
10474
10453
  const mcpConfig = await detectMcpConfigurations(defaultDir);
@@ -10488,8 +10467,8 @@ async function copyMcpServersFromDefault(targetConfigDir) {
10488
10467
  }
10489
10468
  }
10490
10469
  async function writeMcpConfiguration(targetConfigDir, mcpConfig) {
10491
- const settingsFile = join5(targetConfigDir, "settings.json");
10492
- if (existsSync5(settingsFile)) {
10470
+ const settingsFile = join4(targetConfigDir, "settings.json");
10471
+ if (existsSync4(settingsFile)) {
10493
10472
  try {
10494
10473
  const settingsContent = await readFile2(settingsFile, "utf-8");
10495
10474
  const settings = JSON.parse(settingsContent);
@@ -10498,7 +10477,7 @@ async function writeMcpConfiguration(targetConfigDir, mcpConfig) {
10498
10477
  return;
10499
10478
  } catch {}
10500
10479
  }
10501
- const mcpFile = join5(targetConfigDir, "mcp.json");
10480
+ const mcpFile = join4(targetConfigDir, "mcp.json");
10502
10481
  await writeFile3(mcpFile, JSON.stringify(mcpConfig, null, 2), "utf-8");
10503
10482
  }
10504
10483
  async function copyMcpServersBetweenInstances(sourceInstanceName, targetInstanceName) {
@@ -10527,18 +10506,18 @@ async function listMcpServers(instanceName) {
10527
10506
  return mcpConfig?.mcpServers || null;
10528
10507
  }
10529
10508
  async function createSettingsFromTemplate(targetConfigDir, template, apiKey) {
10530
- if (!existsSync5(targetConfigDir)) {
10509
+ if (!existsSync4(targetConfigDir)) {
10531
10510
  await mkdir(targetConfigDir, { recursive: true });
10532
10511
  }
10533
10512
  const settings = applyProviderTemplate(template, apiKey);
10534
- const settingsFile = join5(targetConfigDir, "settings.json");
10513
+ const settingsFile = join4(targetConfigDir, "settings.json");
10535
10514
  await writeFile3(settingsFile, JSON.stringify(settings, null, 2), "utf-8");
10536
10515
  }
10537
10516
  async function initializeInstanceState(configDir) {
10538
- const stateFile = join5(configDir, ".claude.json");
10539
- if (existsSync5(stateFile))
10517
+ const stateFile = join4(configDir, ".claude.json");
10518
+ if (existsSync4(stateFile))
10540
10519
  return;
10541
- if (!existsSync5(configDir)) {
10520
+ if (!existsSync4(configDir)) {
10542
10521
  await mkdir(configDir, { recursive: true });
10543
10522
  }
10544
10523
  const state = {
@@ -10561,9 +10540,9 @@ async function initializeInstanceState(configDir) {
10561
10540
  await writeFile3(stateFile, JSON.stringify(state, null, 2), "utf-8");
10562
10541
  }
10563
10542
  async function mergeProviderEnv(configDir, template, apiKey) {
10564
- const settingsFile = join5(configDir, "settings.json");
10543
+ const settingsFile = join4(configDir, "settings.json");
10565
10544
  let existing = {};
10566
- if (existsSync5(settingsFile)) {
10545
+ if (existsSync4(settingsFile)) {
10567
10546
  const raw = await readFile2(settingsFile, "utf-8");
10568
10547
  existing = JSON.parse(raw);
10569
10548
  }
@@ -10573,22 +10552,81 @@ async function mergeProviderEnv(configDir, template, apiKey) {
10573
10552
  existing.includeCoAuthoredBy = template.settings.includeCoAuthoredBy;
10574
10553
  await writeFile3(settingsFile, JSON.stringify(existing, null, 2), "utf-8");
10575
10554
  }
10555
+ async function syncProviderTemplateForInstance(instance) {
10556
+ const providerName = instance.providerTemplate ?? detectProvider(instance.configDir);
10557
+ if (!providerName) {
10558
+ throw new ClaudeMultiError(ErrorCode.INSTANCE_NOT_FOUND, `Could not detect provider for '${instance.name}'`);
10559
+ }
10560
+ let template = getProviderTemplate(providerName);
10561
+ if (!template) {
10562
+ throw new ClaudeMultiError(ErrorCode.INSTANCE_NOT_FOUND, `Unknown provider template '${providerName}'`);
10563
+ }
10564
+ const settingsFile = join4(instance.configDir, "settings.json");
10565
+ if (!existsSync4(settingsFile)) {
10566
+ throw new ClaudeMultiError(ErrorCode.INSTANCE_DIR_NOT_FOUND, `No settings.json found for '${instance.name}'`);
10567
+ }
10568
+ const raw = JSON.parse(readFileSync4(settingsFile, "utf-8"));
10569
+ const existingEnv = raw.env ?? {};
10570
+ const apiKey = existingEnv.ANTHROPIC_AUTH_TOKEN ?? "";
10571
+ const existingBaseUrl = existingEnv.ANTHROPIC_BASE_URL;
10572
+ let resolvedRegion = null;
10573
+ if (providerHasRegions(providerName)) {
10574
+ const detectedRegion = existingBaseUrl ? detectRegionFromBaseUrl(existingBaseUrl) ?? instance.providerRegion : instance.providerRegion;
10575
+ if (detectedRegion) {
10576
+ const providerRegions = getProviderRegions(providerName);
10577
+ if (providerRegions && detectedRegion in providerRegions) {
10578
+ template = resolveRegionTemplate(template, detectedRegion);
10579
+ resolvedRegion = detectedRegion;
10580
+ }
10581
+ }
10582
+ }
10583
+ const templateSettings = structuredClone(template.settings);
10584
+ const newEnv = { ...templateSettings.env, ANTHROPIC_AUTH_TOKEN: apiKey };
10585
+ if (providerHasRegions(providerName) && !resolvedRegion && existingBaseUrl) {
10586
+ newEnv.ANTHROPIC_BASE_URL = existingBaseUrl;
10587
+ }
10588
+ for (const key of TUNABLE_ENV_VARS) {
10589
+ if (key in existingEnv && existingEnv[key] !== templateSettings.env[key]) {
10590
+ newEnv[key] = existingEnv[key];
10591
+ }
10592
+ }
10593
+ raw.env = { ...existingEnv, ...newEnv };
10594
+ raw.includeCoAuthoredBy = template.settings.includeCoAuthoredBy;
10595
+ raw.alwaysThinkingEnabled = template.settings.alwaysThinkingEnabled;
10596
+ writeFileSync2(settingsFile, JSON.stringify(raw, null, 2), "utf-8");
10597
+ let needsSave = false;
10598
+ const config = await loadConfig();
10599
+ const inst = config.instances.find((i) => i.name === instance.name);
10600
+ if (inst) {
10601
+ if (!inst.providerTemplate) {
10602
+ inst.providerTemplate = providerName;
10603
+ needsSave = true;
10604
+ }
10605
+ if (providerHasRegions(providerName) && resolvedRegion && !inst.providerRegion) {
10606
+ inst.providerRegion = resolvedRegion;
10607
+ needsSave = true;
10608
+ }
10609
+ if (needsSave) {
10610
+ await saveConfigAtomic(config);
10611
+ }
10612
+ }
10613
+ }
10576
10614
  async function syncPluginsAndSkills(configDir) {
10577
10615
  const defaultDir = getDefaultClaudeDir();
10578
- if (!existsSync5(configDir)) {
10616
+ if (!existsSync4(configDir)) {
10579
10617
  throw new ClaudeMultiError(ErrorCode.INSTANCE_DIR_NOT_FOUND, "Instance config directory does not exist");
10580
10618
  }
10581
10619
  await Promise.all(SYNC_DIRS.map(async (dir) => {
10582
- const targetPath = join5(configDir, dir);
10583
- const sourcePath = join5(defaultDir, dir);
10584
- if (!existsSync5(sourcePath)) {
10620
+ const targetPath = join4(configDir, dir);
10621
+ const sourcePath = join4(defaultDir, dir);
10622
+ if (!existsSync4(sourcePath)) {
10585
10623
  console.log(source_default.yellow(` ⚠ Source ${dir} not found in ${defaultDir}, skipping`));
10586
10624
  return;
10587
10625
  }
10588
- if (existsSync5(targetPath)) {
10626
+ if (existsSync4(targetPath)) {
10589
10627
  try {
10590
10628
  const linkTarget = readlinkSync(targetPath);
10591
- if (linkTarget === sourcePath || linkTarget === join5("..", "..", ".claude", dir)) {
10629
+ if (linkTarget === sourcePath || linkTarget === join4("..", "..", ".claude", dir)) {
10592
10630
  console.log(source_default.gray(` ✓ ${dir} already synced`));
10593
10631
  return;
10594
10632
  }
@@ -10605,8 +10643,8 @@ async function syncPluginsAndSkills(configDir) {
10605
10643
  async function copyFilesRecursive(source, target) {
10606
10644
  const entries = readdirSync2(source);
10607
10645
  await Promise.all(entries.map(async (entry) => {
10608
- const sourceEntry = join5(source, entry);
10609
- const targetEntry = join5(target, entry);
10646
+ const sourceEntry = join4(source, entry);
10647
+ const targetEntry = join4(target, entry);
10610
10648
  const stat = statSync(sourceEntry);
10611
10649
  if (stat.isDirectory()) {
10612
10650
  await mkdir(targetEntry, { recursive: true });
@@ -10618,13 +10656,13 @@ async function copyFilesRecursive(source, target) {
10618
10656
  }
10619
10657
  async function unsyncPluginsAndSkills(configDir) {
10620
10658
  const defaultDir = getDefaultClaudeDir();
10621
- if (!existsSync5(configDir)) {
10659
+ if (!existsSync4(configDir)) {
10622
10660
  throw new ClaudeMultiError(ErrorCode.INSTANCE_DIR_NOT_FOUND, "Instance config directory does not exist");
10623
10661
  }
10624
10662
  await Promise.all(SYNC_DIRS.map(async (dir) => {
10625
- const targetPath = join5(configDir, dir);
10626
- const sourcePath = join5(defaultDir, dir);
10627
- if (!existsSync5(sourcePath)) {
10663
+ const targetPath = join4(configDir, dir);
10664
+ const sourcePath = join4(defaultDir, dir);
10665
+ if (!existsSync4(sourcePath)) {
10628
10666
  console.log(source_default.yellow(` ⚠ Source ${dir} not found in ${defaultDir}, skipping`));
10629
10667
  return;
10630
10668
  }
@@ -10636,7 +10674,7 @@ async function unsyncPluginsAndSkills(configDir) {
10636
10674
  if (isSymlink) {
10637
10675
  rmSync2(targetPath, { force: true });
10638
10676
  console.log(source_default.gray(` ✓ Removed symlink for ${dir}`));
10639
- } else if (!existsSync5(targetPath)) {} else {
10677
+ } else if (!existsSync4(targetPath)) {} else {
10640
10678
  console.log(source_default.yellow(` ⚠ ${dir} is already a regular directory, skipping`));
10641
10679
  return;
10642
10680
  }
@@ -10646,8 +10684,8 @@ async function unsyncPluginsAndSkills(configDir) {
10646
10684
  }));
10647
10685
  }
10648
10686
  async function readClaudeSettings(configDir) {
10649
- const settingsFile = join5(configDir, "settings.json");
10650
- if (!existsSync5(settingsFile)) {
10687
+ const settingsFile = join4(configDir, "settings.json");
10688
+ if (!existsSync4(settingsFile)) {
10651
10689
  return null;
10652
10690
  }
10653
10691
  try {
@@ -10659,10 +10697,10 @@ async function readClaudeSettings(configDir) {
10659
10697
  }
10660
10698
  }
10661
10699
  async function writeClaudeSettings(configDir, settings) {
10662
- if (!existsSync5(configDir)) {
10700
+ if (!existsSync4(configDir)) {
10663
10701
  await mkdir(configDir, { recursive: true });
10664
10702
  }
10665
- await writeJsonFileAtomic(join5(configDir, "settings.json"), settings);
10703
+ await writeJsonFileAtomic(join4(configDir, "settings.json"), settings);
10666
10704
  }
10667
10705
  async function getEnabledPlugins(configDir) {
10668
10706
  const settings = await readClaudeSettings(configDir);
@@ -10712,18 +10750,18 @@ function readMcpJsonFile(mcpPath) {
10712
10750
  }
10713
10751
  function scanPluginDir(baseDir, category, enabledPlugins) {
10714
10752
  const subDir = category === PluginCategory.Internal ? "plugins" : "external_plugins";
10715
- const dir = join5(baseDir, MARKETPLACE_REL, subDir);
10716
- if (!existsSync5(dir))
10753
+ const dir = join4(baseDir, MARKETPLACE_REL, subDir);
10754
+ if (!existsSync4(dir))
10717
10755
  return [];
10718
10756
  const results = [];
10719
10757
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
10720
10758
  if (!entry.isDirectory())
10721
10759
  continue;
10722
- const pluginPath = join5(dir, entry.name);
10760
+ const pluginPath = join4(dir, entry.name);
10723
10761
  let name = entry.name;
10724
10762
  let description = "";
10725
- const metaFile = join5(pluginPath, ".claude-plugin", "plugin.json");
10726
- if (existsSync5(metaFile)) {
10763
+ const metaFile = join4(pluginPath, ".claude-plugin", "plugin.json");
10764
+ if (existsSync4(metaFile)) {
10727
10765
  try {
10728
10766
  const meta = JSON.parse(readFileSync4(metaFile, "utf-8"));
10729
10767
  if (meta.name)
@@ -10734,8 +10772,8 @@ function scanPluginDir(baseDir, category, enabledPlugins) {
10734
10772
  console.error(source_default.yellow(`Warning: Could not parse plugin metadata for ${entry.name}`));
10735
10773
  }
10736
10774
  }
10737
- const mcpFile = join5(pluginPath, ".mcp.json");
10738
- const hasMcp = existsSync5(mcpFile);
10775
+ const mcpFile = join4(pluginPath, ".mcp.json");
10776
+ const hasMcp = existsSync4(mcpFile);
10739
10777
  let mcpServerNames;
10740
10778
  if (hasMcp) {
10741
10779
  const servers = readMcpJsonFile(mcpFile);
@@ -10758,8 +10796,8 @@ function scanPluginDir(baseDir, category, enabledPlugins) {
10758
10796
  }
10759
10797
  function scanPluginsFromDir(baseDir) {
10760
10798
  let enabledPlugins = {};
10761
- const settingsFile = join5(baseDir, "settings.json");
10762
- if (existsSync5(settingsFile)) {
10799
+ const settingsFile = join4(baseDir, "settings.json");
10800
+ if (existsSync4(settingsFile)) {
10763
10801
  try {
10764
10802
  const s = JSON.parse(readFileSync4(settingsFile, "utf-8"));
10765
10803
  if (s.enabledPlugins)
@@ -10779,7 +10817,7 @@ function listInstancePlugins(configDir) {
10779
10817
  return scanPluginsFromDir(configDir);
10780
10818
  }
10781
10819
  function isPluginsSymlinked(configDir) {
10782
- const pluginsPath = join5(configDir, "plugins");
10820
+ const pluginsPath = join4(configDir, "plugins");
10783
10821
  const st = lstatSafe(pluginsPath);
10784
10822
  return st?.isSymbolicLink() ?? false;
10785
10823
  }
@@ -10795,8 +10833,8 @@ function isClaudeCodeRunning(configDir) {
10795
10833
  }
10796
10834
  }
10797
10835
  function readInstalledPlugins(pluginsDir) {
10798
- const filePath = join5(pluginsDir, "installed_plugins.json");
10799
- if (!existsSync5(filePath)) {
10836
+ const filePath = join4(pluginsDir, "installed_plugins.json");
10837
+ if (!existsSync4(filePath)) {
10800
10838
  return { version: 2, plugins: {} };
10801
10839
  }
10802
10840
  try {
@@ -10806,16 +10844,16 @@ function readInstalledPlugins(pluginsDir) {
10806
10844
  }
10807
10845
  }
10808
10846
  async function writeInstalledPlugins(pluginsDir, data) {
10809
- await writeJsonFileAtomic(join5(pluginsDir, "installed_plugins.json"), data);
10847
+ await writeJsonFileAtomic(join4(pluginsDir, "installed_plugins.json"), data);
10810
10848
  }
10811
10849
  async function addPluginToInstalledPlugins(configDir, pluginId) {
10812
- const pluginsDir = join5(configDir, "plugins");
10850
+ const pluginsDir = join4(configDir, "plugins");
10813
10851
  const data = readInstalledPlugins(pluginsDir);
10814
10852
  const key = `${pluginId}@claude-plugins-official`;
10815
10853
  if (!data.plugins[key]) {
10816
10854
  data.plugins[key] = [];
10817
10855
  }
10818
- const cachePath = join5(configDir, MARKETPLACE_REL);
10856
+ const cachePath = join4(configDir, MARKETPLACE_REL);
10819
10857
  const entry = {
10820
10858
  scope: "user",
10821
10859
  installPath: cachePath,
@@ -10827,7 +10865,7 @@ async function addPluginToInstalledPlugins(configDir, pluginId) {
10827
10865
  await writeInstalledPlugins(pluginsDir, data);
10828
10866
  }
10829
10867
  async function removePluginFromInstalledPlugins(configDir, pluginId) {
10830
- const pluginsDir = join5(configDir, "plugins");
10868
+ const pluginsDir = join4(configDir, "plugins");
10831
10869
  const data = readInstalledPlugins(pluginsDir);
10832
10870
  const key = `${pluginId}@claude-plugins-official`;
10833
10871
  delete data.plugins[key];
@@ -10837,7 +10875,7 @@ function estimatePluginSize(pluginPath) {
10837
10875
  let total = 0;
10838
10876
  try {
10839
10877
  for (const entry of readdirSync2(pluginPath, { withFileTypes: true })) {
10840
- const fullPath = join5(pluginPath, entry.name);
10878
+ const fullPath = join4(pluginPath, entry.name);
10841
10879
  if (entry.isDirectory()) {
10842
10880
  total += estimatePluginSize(fullPath);
10843
10881
  } else {
@@ -10855,8 +10893,8 @@ function detectMcpCollisions(configDir, newPluginIds) {
10855
10893
  const defaultDir = getDefaultClaudeDir();
10856
10894
  for (const pluginId of newPluginIds) {
10857
10895
  for (const subDir of ["plugins", "external_plugins"]) {
10858
- const mcpFile = join5(defaultDir, MARKETPLACE_REL, subDir, pluginId, ".mcp.json");
10859
- if (!existsSync5(mcpFile))
10896
+ const mcpFile = join4(defaultDir, MARKETPLACE_REL, subDir, pluginId, ".mcp.json");
10897
+ if (!existsSync4(mcpFile))
10860
10898
  continue;
10861
10899
  const newServers = readMcpJsonFile(mcpFile);
10862
10900
  for (const name of Object.keys(newServers)) {
@@ -10875,10 +10913,10 @@ function detectMcpCollisions(configDir, newPluginIds) {
10875
10913
  function validatePluginOperation(configDir, operation, pluginId) {
10876
10914
  const errors = [];
10877
10915
  const warnings = [];
10878
- if (!existsSync5(configDir)) {
10916
+ if (!existsSync4(configDir)) {
10879
10917
  errors.push("Instance config directory does not exist");
10880
10918
  }
10881
- if (existsSync5(configDir) && isPluginsSymlinked(configDir)) {
10919
+ if (existsSync4(configDir) && isPluginsSymlinked(configDir)) {
10882
10920
  errors.push("Plugins directory is symlinked (auto-sync). Disable auto-sync first.");
10883
10921
  }
10884
10922
  if (operation === PluginAction.Install && pluginId) {
@@ -10893,7 +10931,7 @@ function validatePluginOperation(configDir, operation, pluginId) {
10893
10931
  errors.push(`Plugin '${pluginId}' not installed in this instance`);
10894
10932
  }
10895
10933
  }
10896
- if (operation === PluginAction.Install && pluginId && existsSync5(configDir)) {
10934
+ if (operation === PluginAction.Install && pluginId && existsSync4(configDir)) {
10897
10935
  const collisions = detectMcpCollisions(configDir, [pluginId]);
10898
10936
  for (const c of collisions) {
10899
10937
  warnings.push(`MCP server name collision: '${c.serverName}'`);
@@ -10904,27 +10942,27 @@ function validatePluginOperation(configDir, operation, pluginId) {
10904
10942
  async function copySinglePlugin(targetConfigDir, pluginId, category) {
10905
10943
  const defaultDir = getDefaultClaudeDir();
10906
10944
  const subDir = category === PluginCategory.Internal ? "plugins" : "external_plugins";
10907
- const sourcePlugin = join5(defaultDir, MARKETPLACE_REL, subDir, pluginId);
10908
- const targetPlugin = join5(targetConfigDir, MARKETPLACE_REL, subDir, pluginId);
10909
- if (!existsSync5(sourcePlugin)) {
10945
+ const sourcePlugin = join4(defaultDir, MARKETPLACE_REL, subDir, pluginId);
10946
+ const targetPlugin = join4(targetConfigDir, MARKETPLACE_REL, subDir, pluginId);
10947
+ if (!existsSync4(sourcePlugin)) {
10910
10948
  throw new ClaudeMultiError(ErrorCode.PLUGIN_NOT_FOUND, `Plugin '${pluginId}' not found in default Claude`);
10911
10949
  }
10912
- const scaffoldDir = join5(targetConfigDir, MARKETPLACE_REL, subDir);
10913
- if (!existsSync5(scaffoldDir)) {
10950
+ const scaffoldDir = join4(targetConfigDir, MARKETPLACE_REL, subDir);
10951
+ if (!existsSync4(scaffoldDir)) {
10914
10952
  await mkdir(scaffoldDir, { recursive: true });
10915
10953
  }
10916
- const marketplaceDir = join5(targetConfigDir, MARKETPLACE_REL);
10917
- if (!existsSync5(marketplaceDir)) {
10954
+ const marketplaceDir = join4(targetConfigDir, MARKETPLACE_REL);
10955
+ if (!existsSync4(marketplaceDir)) {
10918
10956
  await mkdir(marketplaceDir, { recursive: true });
10919
10957
  }
10920
10958
  for (const metaFile of ["known_marketplaces.json", "blocklist.json"]) {
10921
- const src = join5(defaultDir, MARKETPLACE_REL, metaFile);
10922
- const tgt = join5(marketplaceDir, metaFile);
10923
- if (existsSync5(src) && !existsSync5(tgt)) {
10959
+ const src = join4(defaultDir, MARKETPLACE_REL, metaFile);
10960
+ const tgt = join4(marketplaceDir, metaFile);
10961
+ if (existsSync4(src) && !existsSync4(tgt)) {
10924
10962
  await copyFile(src, tgt);
10925
10963
  }
10926
10964
  }
10927
- if (existsSync5(targetPlugin)) {
10965
+ if (existsSync4(targetPlugin)) {
10928
10966
  rmSync2(targetPlugin, { force: true, recursive: true });
10929
10967
  }
10930
10968
  await copyDirRecursive(sourcePlugin, targetPlugin);
@@ -10943,15 +10981,15 @@ async function copySelectedPlugins(targetConfigDir, selections) {
10943
10981
  const defaultDir = getDefaultClaudeDir();
10944
10982
  for (const sel of selections) {
10945
10983
  const subDir = sel.category === PluginCategory.Internal ? "plugins" : "external_plugins";
10946
- const source = join5(defaultDir, MARKETPLACE_REL, subDir, sel.id);
10947
- if (!existsSync5(source)) {
10984
+ const source = join4(defaultDir, MARKETPLACE_REL, subDir, sel.id);
10985
+ if (!existsSync4(source)) {
10948
10986
  throw new ClaudeMultiError(ErrorCode.PLUGIN_NOT_FOUND, `Plugin '${sel.id}' not found in default Claude`);
10949
10987
  }
10950
10988
  }
10951
10989
  let totalSize = 0;
10952
10990
  for (const sel of selections) {
10953
10991
  const subDir = sel.category === PluginCategory.Internal ? "plugins" : "external_plugins";
10954
- totalSize += estimatePluginSize(join5(defaultDir, MARKETPLACE_REL, subDir, sel.id));
10992
+ totalSize += estimatePluginSize(join4(defaultDir, MARKETPLACE_REL, subDir, sel.id));
10955
10993
  }
10956
10994
  if (totalSize > 104857600) {
10957
10995
  throw new ClaudeMultiError(ErrorCode.PLUGIN_TOO_LARGE, `Selected plugins total ${(totalSize / 1024 / 1024).toFixed(1)}MB. Ensure sufficient disk space.`);
@@ -10966,8 +11004,8 @@ async function copySelectedPlugins(targetConfigDir, selections) {
10966
11004
  for (const done of completed) {
10967
11005
  try {
10968
11006
  const subDir = done.category === PluginCategory.Internal ? "plugins" : "external_plugins";
10969
- const targetPath = join5(targetConfigDir, MARKETPLACE_REL, subDir, done.id);
10970
- if (existsSync5(targetPath)) {
11007
+ const targetPath = join4(targetConfigDir, MARKETPLACE_REL, subDir, done.id);
11008
+ if (existsSync4(targetPath)) {
10971
11009
  rmSync2(targetPath, { force: true, recursive: true });
10972
11010
  }
10973
11011
  await removePluginFromInstalledPlugins(targetConfigDir, done.id);
@@ -10983,8 +11021,8 @@ async function removeSinglePlugin(configDir, pluginId, category) {
10983
11021
  throw new ClaudeMultiError(ErrorCode.SYMLINK_CONFLICT, "Cannot remove plugins from a symlinked instance. Disable auto-sync first.");
10984
11022
  }
10985
11023
  const subDir = category === PluginCategory.Internal ? "plugins" : "external_plugins";
10986
- const pluginPath = join5(configDir, MARKETPLACE_REL, subDir, pluginId);
10987
- if (!existsSync5(pluginPath)) {
11024
+ const pluginPath = join4(configDir, MARKETPLACE_REL, subDir, pluginId);
11025
+ if (!existsSync4(pluginPath)) {
10988
11026
  throw new ClaudeMultiError(ErrorCode.PLUGIN_NOT_FOUND, `Plugin '${pluginId}' not found in this instance`);
10989
11027
  }
10990
11028
  const backupPath = pluginPath + ".removing";
@@ -11007,16 +11045,16 @@ async function removeSinglePlugin(configDir, pluginId, category) {
11007
11045
  }
11008
11046
  function getMcpServersFromPlugins(configDir) {
11009
11047
  const result = {};
11010
- const basePlugins = join5(configDir, MARKETPLACE_REL);
11048
+ const basePlugins = join4(configDir, MARKETPLACE_REL);
11011
11049
  for (const subDir of ["plugins", "external_plugins"]) {
11012
- const dir = join5(basePlugins, subDir);
11013
- if (!existsSync5(dir))
11050
+ const dir = join4(basePlugins, subDir);
11051
+ if (!existsSync4(dir))
11014
11052
  continue;
11015
11053
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
11016
11054
  if (!entry.isDirectory())
11017
11055
  continue;
11018
- const mcpFile = join5(dir, entry.name, ".mcp.json");
11019
- if (!existsSync5(mcpFile))
11056
+ const mcpFile = join4(dir, entry.name, ".mcp.json");
11057
+ if (!existsSync4(mcpFile))
11020
11058
  continue;
11021
11059
  const servers = readMcpJsonFile(mcpFile);
11022
11060
  Object.assign(result, servers);
@@ -11051,6 +11089,7 @@ var init_config = __esm(() => {
11051
11089
  init_json_file();
11052
11090
  init_errors();
11053
11091
  init_constants();
11092
+ init_env();
11054
11093
  init_source();
11055
11094
  SYNC_DIRS = ["plugins", "skills"];
11056
11095
  });
@@ -33330,7 +33369,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
33330
33369
  return 0;
33331
33370
  };
33332
33371
  var validate2 = function validate3(version) {
33333
- return typeof version === "string" && /^[v\d]/.test(version) && semver3.test(version);
33372
+ return typeof version === "string" && /^[v\d]/.test(version) && semver2.test(version);
33334
33373
  };
33335
33374
  var compare = function compare2(v1, v2, operator) {
33336
33375
  assertValidOperator(operator);
@@ -33364,12 +33403,12 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
33364
33403
  return false;
33365
33404
  return true;
33366
33405
  };
33367
- var semver3 = /^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i;
33406
+ var semver2 = /^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i;
33368
33407
  var validateAndParse = function validateAndParse2(version) {
33369
33408
  if (typeof version !== "string") {
33370
33409
  throw new TypeError("Invalid argument expected string");
33371
33410
  }
33372
- var match = version.match(semver3);
33411
+ var match = version.match(semver2);
33373
33412
  if (!match) {
33374
33413
  throw new Error("Invalid argument not valid semver ('".concat(version, "' received)"));
33375
33414
  }
@@ -56046,6 +56085,18 @@ function useConfig() {
56046
56085
  }
56047
56086
  await reload();
56048
56087
  }, [reload]);
56088
+ const syncTemplateEnv = import_react66.useCallback(async (instance) => {
56089
+ await syncProviderTemplateForInstance(instance);
56090
+ await reload();
56091
+ }, [reload]);
56092
+ const regenerateWrapper = import_react66.useCallback(async (instance) => {
56093
+ await createWrapper({
56094
+ name: instance.name,
56095
+ configDir: instance.configDir,
56096
+ binaryPath: instance.binaryPath
56097
+ });
56098
+ await reload();
56099
+ }, [reload]);
56049
56100
  const migrationStatus = config ? getMigrationStatus(config) : null;
56050
56101
  return {
56051
56102
  config,
@@ -56056,6 +56107,8 @@ function useConfig() {
56056
56107
  addInstance: addInstance2,
56057
56108
  removeInstance: removeInstance2,
56058
56109
  toggleAutoSync,
56110
+ syncTemplateEnv,
56111
+ regenerateWrapper,
56059
56112
  migrationStatus,
56060
56113
  instanceMigrationVersion: config?.instanceMigrationVersion,
56061
56114
  getInstance,
@@ -56125,7 +56178,7 @@ var exports_AddInstance = {};
56125
56178
  __export(exports_AddInstance, {
56126
56179
  AddInstance: () => AddInstance
56127
56180
  });
56128
- import { join as join7 } from "node:path";
56181
+ import { join as join6 } from "node:path";
56129
56182
  import { homedir as homedir3 } from "node:os";
56130
56183
  function stepNumber(step) {
56131
56184
  return STEP_FLOW.indexOf(step) + 1;
@@ -56169,6 +56222,9 @@ function reducer5(state, action) {
56169
56222
  case "SET_ERROR":
56170
56223
  return { ...state, error: action.error };
56171
56224
  case "SELECT_PROVIDER":
56225
+ if (!action.provider || !providerHasRegions(action.provider)) {
56226
+ return { ...state, selectedProvider: action.provider, useProvider: action.useProvider, selectedRegion: null };
56227
+ }
56172
56228
  return { ...state, selectedProvider: action.provider, useProvider: action.useProvider };
56173
56229
  case "SET_REGION":
56174
56230
  return { ...state, selectedRegion: action.region };
@@ -56291,34 +56347,37 @@ var import_react67, jsx_dev_runtime4, AddResult = ({
56291
56347
  onChange: onSelect
56292
56348
  }, undefined, false, undefined, this)
56293
56349
  ]
56294
- }, undefined, true, undefined, this), ProviderRegionStep = ({ state, onSelect }) => /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
56295
- flexDirection: "column",
56296
- gap: 1,
56297
- children: [
56298
- /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
56299
- children: "Select your subscription region:"
56300
- }, undefined, false, undefined, this),
56301
- /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
56302
- borderStyle: "round",
56303
- borderColor: "yellow",
56304
- paddingX: 1,
56305
- children: /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
56306
- bold: true,
56307
- color: "yellow",
56308
- children: "Check your Xiaomi account console to confirm the correct region for your subscription."
56350
+ }, undefined, true, undefined, this), ProviderRegionStep = ({ state, onSelect }) => {
56351
+ const regions = state.selectedProvider ? getProviderRegions(state.selectedProvider) : undefined;
56352
+ return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
56353
+ flexDirection: "column",
56354
+ gap: 1,
56355
+ children: [
56356
+ /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
56357
+ children: "Select your subscription region:"
56358
+ }, undefined, false, undefined, this),
56359
+ /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
56360
+ borderStyle: "round",
56361
+ borderColor: "yellow",
56362
+ paddingX: 1,
56363
+ children: /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
56364
+ bold: true,
56365
+ color: "yellow",
56366
+ children: "Check your account console to confirm the correct region for your subscription."
56367
+ }, undefined, false, undefined, this)
56368
+ }, undefined, false, undefined, this),
56369
+ /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Select, {
56370
+ options: regions ? Object.entries(regions).map(([key, val]) => ({
56371
+ label: `${val.label} — ${val.baseUrl}`,
56372
+ value: key
56373
+ })) : [],
56374
+ visibleOptionCount: 3,
56375
+ defaultValue: state.selectedRegion ?? undefined,
56376
+ onChange: onSelect
56309
56377
  }, undefined, false, undefined, this)
56310
- }, undefined, false, undefined, this),
56311
- /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Select, {
56312
- options: Object.entries(MIMO_TOKEN_REGIONS).map(([key, val]) => ({
56313
- label: `${val.label} — ${val.baseUrl}`,
56314
- value: key
56315
- })),
56316
- visibleOptionCount: 3,
56317
- defaultValue: state.selectedRegion ?? undefined,
56318
- onChange: onSelect
56319
- }, undefined, false, undefined, this)
56320
- ]
56321
- }, undefined, true, undefined, this), ProviderApiKeyStep = ({ state, onSubmit }) => /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
56378
+ ]
56379
+ }, undefined, true, undefined, this);
56380
+ }, ProviderApiKeyStep = ({ state, onSubmit }) => /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
56322
56381
  flexDirection: "column",
56323
56382
  gap: 1,
56324
56383
  children: [
@@ -56345,7 +56404,7 @@ var import_react67, jsx_dev_runtime4, AddResult = ({
56345
56404
  dimColor: true,
56346
56405
  children: [
56347
56406
  "Config: ",
56348
- join7(homedir3(), `.claude-${state.name}`)
56407
+ join6(homedir3(), `.claude-${state.name}`)
56349
56408
  ]
56350
56409
  }, undefined, true, undefined, this),
56351
56410
  /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
@@ -56475,7 +56534,7 @@ var import_react67, jsx_dev_runtime4, AddResult = ({
56475
56534
  dispatch({ type: "SET_STEP", step: "creating" /* Creating */ });
56476
56535
  dispatch({ type: "SET_ERROR", error: "" });
56477
56536
  try {
56478
- const cDir = join7(homedir3(), `.claude-${state.name}`);
56537
+ const cDir = join6(homedir3(), `.claude-${state.name}`);
56479
56538
  const bPath = cfg.getDefaultBinaryPath(state.name);
56480
56539
  const instance = {
56481
56540
  name: state.name,
@@ -56483,7 +56542,9 @@ var import_react67, jsx_dev_runtime4, AddResult = ({
56483
56542
  binaryPath: bPath,
56484
56543
  createdAt: new Date().toISOString(),
56485
56544
  autoSync: sync,
56486
- createdWithVersion: getClaudeMultiVersion()
56545
+ createdWithVersion: getClaudeMultiVersion(),
56546
+ ...state.useProvider && state.selectedProvider ? { providerTemplate: state.selectedProvider } : {},
56547
+ ...state.selectedRegion && state.useProvider && state.selectedProvider && providerHasRegions(state.selectedProvider) ? { providerRegion: state.selectedRegion } : {}
56487
56548
  };
56488
56549
  await cfg.addInstance(instance);
56489
56550
  await Promise.all([
@@ -56717,10 +56778,11 @@ __export(exports_health, {
56717
56778
  dismissIssue: () => dismissIssue,
56718
56779
  dismissAllIssues: () => dismissAllIssues
56719
56780
  });
56720
- import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync5 } from "node:fs";
56721
- import { join as join8 } from "node:path";
56781
+ import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, renameSync as renameSync2, unlinkSync as unlinkSync2 } from "node:fs";
56782
+ import { randomBytes as randomBytes3 } from "node:crypto";
56783
+ import { join as join7 } from "node:path";
56722
56784
  function getHealthFile() {
56723
- return join8(getBaseDir(), ".claude-multi", "health-status.json");
56785
+ return join7(getBaseDir(), ".claude-multi", "health-status.json");
56724
56786
  }
56725
56787
  function runHealthChecks(instances2, migrationStatus, instanceMigrationVersion) {
56726
56788
  const now2 = new Date().toISOString();
@@ -56757,7 +56819,7 @@ function runHealthChecks(instances2, migrationStatus, instanceMigrationVersion)
56757
56819
  });
56758
56820
  }
56759
56821
  for (const inst of instances2) {
56760
- if (!existsSync7(inst.configDir)) {
56822
+ if (!existsSync6(inst.configDir)) {
56761
56823
  issues.push({
56762
56824
  id: `configdir-missing-${inst.name}`,
56763
56825
  severity: "error",
@@ -56773,7 +56835,7 @@ function runHealthChecks(instances2, migrationStatus, instanceMigrationVersion)
56773
56835
  });
56774
56836
  continue;
56775
56837
  }
56776
- if (!existsSync7(inst.binaryPath)) {
56838
+ if (!existsSync6(inst.binaryPath)) {
56777
56839
  issues.push({
56778
56840
  id: `binary-missing-${inst.name}`,
56779
56841
  severity: "warning",
@@ -56788,10 +56850,11 @@ function runHealthChecks(instances2, migrationStatus, instanceMigrationVersion)
56788
56850
  resolutionHint: "Re-create the instance or run 'claude-multi sync'"
56789
56851
  });
56790
56852
  }
56791
- const settingsFile = join8(inst.configDir, "settings.json");
56792
- if (existsSync7(settingsFile)) {
56853
+ const settingsFile = join7(inst.configDir, "settings.json");
56854
+ if (existsSync6(settingsFile)) {
56855
+ let parsed = null;
56793
56856
  try {
56794
- JSON.parse(readFileSync6(settingsFile, "utf-8"));
56857
+ parsed = JSON.parse(readFileSync6(settingsFile, "utf-8"));
56795
56858
  } catch {
56796
56859
  issues.push({
56797
56860
  id: `settings-corrupt-${inst.name}`,
@@ -56807,6 +56870,29 @@ function runHealthChecks(instances2, migrationStatus, instanceMigrationVersion)
56807
56870
  resolutionHint: "Fix or delete the corrupted settings.json"
56808
56871
  });
56809
56872
  }
56873
+ if (parsed && inst.providerTemplate && providerHasRegions(inst.providerTemplate)) {
56874
+ const env3 = parsed.env;
56875
+ const actualUrl = env3?.ANTHROPIC_BASE_URL;
56876
+ const actualRegion = actualUrl ? detectRegionFromBaseUrl(actualUrl) : null;
56877
+ if (inst.providerRegion && actualRegion && inst.providerRegion !== actualRegion) {
56878
+ const providerRegions = getProviderRegions(inst.providerTemplate);
56879
+ const expectedUrl = providerRegions?.[inst.providerRegion]?.baseUrl;
56880
+ issues.push({
56881
+ id: `region-mismatch-${inst.name}`,
56882
+ severity: "warning",
56883
+ category: "settings",
56884
+ title: "Region URL mismatch",
56885
+ message: `Instance '${inst.name}' stored region is ${inst.providerRegion} but base URL points to ${actualRegion}`,
56886
+ detail: `Stored region: ${inst.providerRegion} (${expectedUrl})
56887
+ Actual URL: ${actualUrl}`,
56888
+ instanceName: inst.name,
56889
+ timestamp: now2,
56890
+ dismissed: false,
56891
+ resolved: false,
56892
+ resolutionHint: `Edit settings.json to set ANTHROPIC_BASE_URL to ${expectedUrl}, or run 'claude-multi doctor fix'`
56893
+ });
56894
+ }
56895
+ }
56810
56896
  }
56811
56897
  const symlinks = detectBrokenSymlinks(inst.configDir);
56812
56898
  if (symlinks.broken.length > 0) {
@@ -56824,13 +56910,15 @@ function runHealthChecks(instances2, migrationStatus, instanceMigrationVersion)
56824
56910
  resolutionHint: "Re-sync symlinks from the home screen"
56825
56911
  });
56826
56912
  }
56827
- if (existsSync7(inst.binaryPath) && existsSync7(PINNED_CLAUDE_BIN)) {
56913
+ const expectedClaudePath = tryGetClaudePath();
56914
+ if (existsSync6(inst.binaryPath) && expectedClaudePath) {
56828
56915
  try {
56829
56916
  const wrapperContent = readFileSync6(inst.binaryPath, "utf-8");
56830
56917
  const shellMatch = wrapperContent.match(/exec\s+"([^"]+)"/);
56918
+ const cmdMatch = wrapperContent.match(/"([^"]+)"\s+%\*/);
56831
56919
  const nodeMatch = wrapperContent.match(/spawn\("([^"]+)"/);
56832
- const currentBin = shellMatch?.[1] ?? nodeMatch?.[1];
56833
- if (currentBin && currentBin !== PINNED_CLAUDE_BIN) {
56920
+ const currentBin = shellMatch?.[1] ?? cmdMatch?.[1] ?? nodeMatch?.[1];
56921
+ if (currentBin && currentBin !== expectedClaudePath) {
56834
56922
  issues.push({
56835
56923
  id: `wrong-claude-version-${inst.name}`,
56836
56924
  severity: "error",
@@ -56838,7 +56926,7 @@ function runHealthChecks(instances2, migrationStatus, instanceMigrationVersion)
56838
56926
  title: "Wrong Claude binary",
56839
56927
  message: `Instance '${inst.name}' uses a Claude version that may break 3rd-party providers`,
56840
56928
  detail: `Current: ${currentBin}
56841
- Expected: ${PINNED_CLAUDE_BIN}`,
56929
+ Expected: ${expectedClaudePath}`,
56842
56930
  instanceName: inst.name,
56843
56931
  timestamp: now2,
56844
56932
  dismissed: false,
@@ -56868,7 +56956,7 @@ Expected: ${PINNED_CLAUDE_BIN}`,
56868
56956
  }
56869
56957
  function loadHealthStatus() {
56870
56958
  const healthFile = getHealthFile();
56871
- if (!existsSync7(healthFile)) {
56959
+ if (!existsSync6(healthFile)) {
56872
56960
  return { lastChecked: "", issues: [] };
56873
56961
  }
56874
56962
  try {
@@ -56878,10 +56966,21 @@ function loadHealthStatus() {
56878
56966
  }
56879
56967
  }
56880
56968
  function saveHealthStatus(status) {
56881
- const dir = join8(getBaseDir(), ".claude-multi");
56882
- if (!existsSync7(dir))
56883
- mkdirSync5(dir, { recursive: true });
56884
- writeFileSync3(getHealthFile(), JSON.stringify(status, null, 2), "utf-8");
56969
+ const dir = join7(getBaseDir(), ".claude-multi");
56970
+ if (!existsSync6(dir))
56971
+ mkdirSync4(dir, { recursive: true });
56972
+ const healthFile = getHealthFile();
56973
+ const tmpPath = `${healthFile}.tmp.${randomBytes3(4).toString("hex")}`;
56974
+ try {
56975
+ writeFileSync3(tmpPath, JSON.stringify(status, null, 2), "utf-8");
56976
+ JSON.parse(readFileSync6(tmpPath, "utf-8"));
56977
+ renameSync2(tmpPath, healthFile);
56978
+ } catch (err) {
56979
+ try {
56980
+ unlinkSync2(tmpPath);
56981
+ } catch {}
56982
+ throw err;
56983
+ }
56885
56984
  }
56886
56985
  function dismissIssue(id) {
56887
56986
  const status = loadHealthStatus();
@@ -56899,20 +56998,22 @@ function dismissAllIssues() {
56899
56998
  saveHealthStatus(status);
56900
56999
  }
56901
57000
  function fixWrapperVersions(instances2) {
56902
- if (!existsSync7(PINNED_CLAUDE_BIN))
57001
+ const expectedClaudePath = tryGetClaudePath();
57002
+ if (!expectedClaudePath)
56903
57003
  return [];
56904
57004
  const fixed = [];
56905
57005
  for (const inst of instances2) {
56906
- if (!existsSync7(inst.binaryPath))
57006
+ if (!existsSync6(inst.binaryPath))
56907
57007
  continue;
56908
57008
  try {
56909
57009
  const content = readFileSync6(inst.binaryPath, "utf-8");
56910
57010
  const shellMatch = content.match(/exec\s+"([^"]+)"/);
57011
+ const cmdMatch = content.match(/"([^"]+)"\s+%\*/);
56911
57012
  const nodeMatch = content.match(/spawn\("([^"]+)"/);
56912
- const currentBin = shellMatch?.[1] ?? nodeMatch?.[1];
56913
- if (!currentBin || currentBin === PINNED_CLAUDE_BIN)
57013
+ const currentBin = shellMatch?.[1] ?? cmdMatch?.[1] ?? nodeMatch?.[1];
57014
+ if (!currentBin || currentBin === expectedClaudePath)
56914
57015
  continue;
56915
- const newContent = buildWrapperScript(inst, PINNED_CLAUDE_BIN);
57016
+ const newContent = buildWrapperScript(inst, expectedClaudePath);
56916
57017
  if (content !== newContent) {
56917
57018
  writeFileSync3(inst.binaryPath, newContent, { mode: 493 });
56918
57019
  }
@@ -56928,6 +57029,7 @@ var init_health = __esm(() => {
56928
57029
  init_version();
56929
57030
  init_migration();
56930
57031
  init_wrapper();
57032
+ init_templates();
56931
57033
  });
56932
57034
 
56933
57035
  // src/ink/components/Footer.tsx
@@ -57071,12 +57173,12 @@ var jsx_dev_runtime6, WarningBanner = ({
57071
57173
  children: [
57072
57174
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
57073
57175
  color: "red",
57074
- children: " ⚠ Some instances use a Claude version incompatible with 3rd-party APIs. "
57176
+ children: " ⚠ Some instances have version issues. "
57075
57177
  }, undefined, false, undefined, this),
57076
57178
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
57077
57179
  color: "red",
57078
57180
  bold: true,
57079
- children: "Press ! to auto-fix."
57181
+ children: "Press ! to review and fix."
57080
57182
  }, undefined, false, undefined, this)
57081
57183
  ]
57082
57184
  }, undefined, true, undefined, this)
@@ -57348,7 +57450,110 @@ var init_InstanceSelectMenu = __esm(async () => {
57348
57450
  jsx_dev_runtime9 = __toESM(require_jsx_dev_runtime(), 1);
57349
57451
  });
57350
57452
 
57453
+ // src/ink/util/instance-diagnostics.ts
57454
+ import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
57455
+ import { join as join8 } from "node:path";
57456
+ function tryResolveRegionalTemplate(template2, region) {
57457
+ if (!template2)
57458
+ return;
57459
+ try {
57460
+ const providerRegions = getProviderRegions(template2.name);
57461
+ if (providerRegions && region in providerRegions) {
57462
+ return resolveRegionTemplate(template2, region);
57463
+ }
57464
+ } catch {}
57465
+ return;
57466
+ }
57467
+ function detectTemplateMismatch(instance) {
57468
+ const providerName = instance.providerTemplate ?? detectProvider(instance.configDir);
57469
+ if (!providerName) {
57470
+ return { status: "unknown", providerName: null };
57471
+ }
57472
+ let template2 = getProviderTemplate(providerName);
57473
+ if (!template2) {
57474
+ return { status: "unknown", providerName };
57475
+ }
57476
+ const settingsFile = join8(instance.configDir, "settings.json");
57477
+ if (!existsSync7(settingsFile)) {
57478
+ return { status: "unknown", providerName };
57479
+ }
57480
+ let existingEnv = {};
57481
+ try {
57482
+ const raw = JSON.parse(readFileSync7(settingsFile, "utf-8"));
57483
+ existingEnv = raw.env ?? {};
57484
+ } catch {
57485
+ return { status: "unknown", providerName };
57486
+ }
57487
+ if (providerHasRegions(providerName)) {
57488
+ const existingUrl = existingEnv.ANTHROPIC_BASE_URL;
57489
+ const detectedRegion = existingUrl ? detectRegionFromBaseUrl(existingUrl) ?? instance.providerRegion : instance.providerRegion;
57490
+ if (detectedRegion) {
57491
+ const resolved = tryResolveRegionalTemplate(template2, detectedRegion);
57492
+ if (resolved) {
57493
+ template2 = resolved;
57494
+ }
57495
+ }
57496
+ }
57497
+ const templateEnv = template2.settings.env;
57498
+ for (const [key, expectedValue] of Object.entries(templateEnv)) {
57499
+ if (key === "ANTHROPIC_AUTH_TOKEN")
57500
+ continue;
57501
+ if (TUNABLE_ENV_VARS.has(key))
57502
+ continue;
57503
+ if (existingEnv[key] !== expectedValue) {
57504
+ return { status: "mismatch", providerName };
57505
+ }
57506
+ }
57507
+ return { status: "match", providerName };
57508
+ }
57509
+ function detectWrapperMismatch(instance) {
57510
+ if (!existsSync7(instance.binaryPath)) {
57511
+ return { status: "missing" };
57512
+ }
57513
+ const expected = generateWrapperScriptSafe({
57514
+ name: instance.name,
57515
+ configDir: instance.configDir,
57516
+ binaryPath: instance.binaryPath
57517
+ });
57518
+ if (expected === null) {
57519
+ return { status: "unknown" };
57520
+ }
57521
+ try {
57522
+ const current = readFileSync7(instance.binaryPath, "utf-8");
57523
+ return current === expected ? { status: "match" } : { status: "mismatch" };
57524
+ } catch {
57525
+ return { status: "unknown" };
57526
+ }
57527
+ }
57528
+ var init_instance_diagnostics = __esm(() => {
57529
+ init_templates();
57530
+ init_wrapper();
57531
+ init_env();
57532
+ });
57533
+
57351
57534
  // src/ink/screens/ShowInstanceInfo.tsx
57535
+ function templateStatusLabel(status) {
57536
+ switch (status) {
57537
+ case "match":
57538
+ return "✓ up to date";
57539
+ case "mismatch":
57540
+ return "⚠ mismatch detected";
57541
+ case "unknown":
57542
+ return "? unknown";
57543
+ }
57544
+ }
57545
+ function wrapperStatusLabel(status) {
57546
+ switch (status) {
57547
+ case "match":
57548
+ return "✓ up to date";
57549
+ case "mismatch":
57550
+ return "⚠ mismatch detected";
57551
+ case "missing":
57552
+ return "✗ wrapper missing";
57553
+ case "unknown":
57554
+ return "? unknown";
57555
+ }
57556
+ }
57352
57557
  var import_react69, jsx_dev_runtime10, DetailRow = ({
57353
57558
  label,
57354
57559
  value,
@@ -57382,19 +57587,74 @@ var import_react69, jsx_dev_runtime10, DetailRow = ({
57382
57587
  }, undefined, false, undefined, this)
57383
57588
  ]
57384
57589
  }, undefined, true, undefined, this);
57385
- }, EMPTY_INFO, ShowInstanceInfo = ({ onBack }) => {
57386
- const { instances: instances2, listInstancePlugins: listInstancePlugins2, getInstanceMcpServers: getInstanceMcpServers2 } = useConfig();
57590
+ }, EMPTY_INFO, ACTION_VALUES, ShowInstanceInfo = ({ onBack }) => {
57591
+ const { exit } = use_app_default();
57592
+ const { instances: instances2, listInstancePlugins: listInstancePlugins2, getInstanceMcpServers: getInstanceMcpServers2, syncTemplateEnv, regenerateWrapper } = useConfig();
57387
57593
  const [step, setStep] = import_react69.useState("select");
57388
57594
  const [selected, setSelected] = import_react69.useState(null);
57389
57595
  const [infoData, setInfoData] = import_react69.useState(EMPTY_INFO);
57390
- useNavigation(() => {
57391
- if (step === "info") {
57392
- setStep("select");
57393
- setSelected(null);
57394
- } else {
57395
- onBack();
57596
+ const [templateStatus, setTemplateStatus] = import_react69.useState("unknown");
57597
+ const [wrapperStatus, setWrapperStatus] = import_react69.useState("unknown");
57598
+ const [providerName, setProviderName] = import_react69.useState(null);
57599
+ const [actionResult, setActionResult] = import_react69.useState(null);
57600
+ const [executing, setExecuting] = import_react69.useState(false);
57601
+ const liveSelected = selected ? instances2.find((i) => i.name === selected.name) ?? selected : null;
57602
+ const runDiagnostics = import_react69.useCallback((inst) => {
57603
+ const tmpl = detectTemplateMismatch(inst);
57604
+ const wrap2 = detectWrapperMismatch(inst);
57605
+ setTemplateStatus(tmpl.status);
57606
+ setWrapperStatus(wrap2.status);
57607
+ setProviderName(tmpl.providerName);
57608
+ }, []);
57609
+ const loadInstanceInfo = import_react69.useCallback(async (inst) => {
57610
+ try {
57611
+ const plugins = listInstancePlugins2(inst.configDir);
57612
+ const mcpData = await getInstanceMcpServers2(inst.configDir);
57613
+ setInfoData({
57614
+ pluginCount: plugins.length,
57615
+ enabledCount: plugins.filter((p) => p.enabled).length,
57616
+ mcpPluginCount: Object.keys(mcpData.fromPlugins).length,
57617
+ mcpCustomCount: Object.keys(mcpData.fromSettings).length,
57618
+ mcpCount: Object.keys(mcpData.all).length
57619
+ });
57620
+ } catch {
57621
+ setInfoData(EMPTY_INFO);
57622
+ }
57623
+ }, [listInstancePlugins2, getInstanceMcpServers2]);
57624
+ use_input_default((input, key) => {
57625
+ if (executing)
57626
+ return;
57627
+ if (input === "q") {
57628
+ exit();
57629
+ return;
57630
+ }
57631
+ if (key.escape) {
57632
+ if (step === "info") {
57633
+ setStep("select");
57634
+ setSelected(null);
57635
+ } else if (step === "actions") {
57636
+ setStep("info");
57637
+ } else if (step === "result") {
57638
+ goToInfo();
57639
+ } else {
57640
+ onBack();
57641
+ }
57642
+ } else if (key.return) {
57643
+ if (step === "info") {
57644
+ setStep("actions");
57645
+ } else if (step === "result") {
57646
+ goToInfo();
57647
+ }
57396
57648
  }
57397
57649
  });
57650
+ const goToInfo = () => {
57651
+ const inst = liveSelected;
57652
+ if (inst) {
57653
+ runDiagnostics(inst);
57654
+ loadInstanceInfo(inst);
57655
+ }
57656
+ setStep("info");
57657
+ };
57398
57658
  if (instances2.length === 0) {
57399
57659
  return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
57400
57660
  flexDirection: "column",
@@ -57425,20 +57685,62 @@ var import_react69, jsx_dev_runtime10, DetailRow = ({
57425
57685
  return;
57426
57686
  setSelected(inst);
57427
57687
  setStep("info");
57688
+ runDiagnostics(inst);
57689
+ await loadInstanceInfo(inst);
57690
+ };
57691
+ const handleAction = async (value) => {
57692
+ if (executing)
57693
+ return;
57694
+ if (value === ACTION_VALUES.Back || !selected) {
57695
+ setStep("info");
57696
+ return;
57697
+ }
57698
+ setActionResult(null);
57699
+ setExecuting(true);
57700
+ setStep("executing");
57428
57701
  try {
57429
- const plugins = listInstancePlugins2(inst.configDir);
57430
- const mcpData = await getInstanceMcpServers2(inst.configDir);
57431
- setInfoData({
57432
- pluginCount: plugins.length,
57433
- enabledCount: plugins.filter((p) => p.enabled).length,
57434
- mcpPluginCount: Object.keys(mcpData.fromPlugins).length,
57435
- mcpCustomCount: Object.keys(mcpData.fromSettings).length,
57436
- mcpCount: Object.keys(mcpData.all).length
57702
+ if (value === ACTION_VALUES.SyncTemplate) {
57703
+ await syncTemplateEnv(liveSelected ?? selected);
57704
+ setActionResult({ message: `Settings template updated for '${selected.name}'`, type: "success" });
57705
+ } else if (value === ACTION_VALUES.UpdateWrapper || value === ACTION_VALUES.OverrideWrapper) {
57706
+ await regenerateWrapper(liveSelected ?? selected);
57707
+ setActionResult({ message: `Alias wrapper regenerated for '${selected.name}'`, type: "success" });
57708
+ }
57709
+ } catch (err) {
57710
+ setActionResult({
57711
+ message: err instanceof Error ? err.message : String(err),
57712
+ type: "error"
57713
+ });
57714
+ }
57715
+ setExecuting(false);
57716
+ setStep("result");
57717
+ };
57718
+ const buildActionOptions = () => {
57719
+ const options = [];
57720
+ if (providerName) {
57721
+ options.push({
57722
+ label: `\uD83D\uDD04 Update settings template [${templateStatusLabel(templateStatus)}]`,
57723
+ value: ACTION_VALUES.SyncTemplate
57724
+ });
57725
+ }
57726
+ options.push({
57727
+ label: `\uD83D\uDD27 Update alias wrapper [${wrapperStatusLabel(wrapperStatus)}]`,
57728
+ value: ACTION_VALUES.UpdateWrapper
57729
+ });
57730
+ if (wrapperStatus === "mismatch" || wrapperStatus === "missing") {
57731
+ options.push({
57732
+ label: `\uD83D\uDD28 Override alias to standard`,
57733
+ value: ACTION_VALUES.OverrideWrapper
57437
57734
  });
57438
- } catch {
57439
- setInfoData(EMPTY_INFO);
57440
57735
  }
57736
+ options.push({ label: "← Back", value: ACTION_VALUES.Back });
57737
+ return options;
57441
57738
  };
57739
+ const hasPlugins = infoData.pluginCount !== null;
57740
+ const hasMcp = infoData.mcpCount !== null;
57741
+ const hasProvider = providerName !== null;
57742
+ const lastRow = hasMcp ? "mcp" : hasPlugins ? "plugins" : hasProvider ? "provider" : "version";
57743
+ const displayInstance = liveSelected ?? selected;
57442
57744
  return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
57443
57745
  flexDirection: "column",
57444
57746
  width: "100",
@@ -57452,7 +57754,7 @@ var import_react69, jsx_dev_runtime10, DetailRow = ({
57452
57754
  instances: instances2,
57453
57755
  onSelect: handleInstanceSelect
57454
57756
  }, undefined, false, undefined, this),
57455
- step === "info" && selected && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
57757
+ step === "info" && displayInstance && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
57456
57758
  flexDirection: "column",
57457
57759
  gap: 0,
57458
57760
  children: [
@@ -57466,7 +57768,7 @@ var import_react69, jsx_dev_runtime10, DetailRow = ({
57466
57768
  /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
57467
57769
  bold: true,
57468
57770
  color: "cyan",
57469
- children: selected.name
57771
+ children: displayInstance.name
57470
57772
  }, undefined, false, undefined, this)
57471
57773
  ]
57472
57774
  }, undefined, true, undefined, this),
@@ -57476,53 +57778,186 @@ var import_react69, jsx_dev_runtime10, DetailRow = ({
57476
57778
  children: [
57477
57779
  /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(DetailRow, {
57478
57780
  label: "Binary",
57479
- value: selected.binaryPath,
57781
+ value: displayInstance.binaryPath,
57480
57782
  delay: 50
57481
57783
  }, undefined, false, undefined, this),
57482
57784
  /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(DetailRow, {
57483
57785
  label: "Config",
57484
- value: selected.configDir,
57786
+ value: displayInstance.configDir,
57485
57787
  delay: 100
57486
57788
  }, undefined, false, undefined, this),
57487
57789
  /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(DetailRow, {
57488
57790
  label: "Created",
57489
- value: new Date(selected.createdAt).toLocaleString(),
57791
+ value: new Date(displayInstance.createdAt).toLocaleString(),
57490
57792
  delay: 150
57491
57793
  }, undefined, false, undefined, this),
57492
57794
  /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(DetailRow, {
57493
57795
  label: "Auto-sync",
57494
- value: selected.autoSync !== false ? "✓ Enabled" : "✗ Disabled",
57495
- color: selected.autoSync !== false ? "green" : "red",
57796
+ value: displayInstance.autoSync !== false ? "✓ Enabled" : "✗ Disabled",
57797
+ color: displayInstance.autoSync !== false ? "green" : "red",
57496
57798
  delay: 200
57497
57799
  }, undefined, false, undefined, this),
57498
57800
  /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(DetailRow, {
57499
57801
  label: "Version",
57500
- value: selected.createdWithVersion === LEGACY_INSTANCE_VERSION ? "before version tracking" : selected.createdWithVersion,
57501
- color: selected.createdWithVersion === LEGACY_INSTANCE_VERSION ? "yellow" : undefined,
57802
+ value: displayInstance.createdWithVersion === LEGACY_INSTANCE_VERSION ? "before version tracking" : displayInstance.createdWithVersion,
57803
+ color: displayInstance.createdWithVersion === LEGACY_INSTANCE_VERSION ? "yellow" : undefined,
57804
+ last: lastRow === "version",
57502
57805
  delay: 250
57503
57806
  }, undefined, false, undefined, this),
57504
- infoData.pluginCount !== null && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(DetailRow, {
57807
+ hasProvider && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(DetailRow, {
57808
+ label: "Provider",
57809
+ value: providerName,
57810
+ color: templateStatus === "match" ? undefined : templateStatus === "mismatch" ? "yellow" : undefined,
57811
+ last: lastRow === "provider",
57812
+ delay: 275
57813
+ }, undefined, false, undefined, this),
57814
+ hasPlugins && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(DetailRow, {
57505
57815
  label: "Plugins",
57506
57816
  value: `${infoData.pluginCount} installed, ${infoData.enabledCount} enabled`,
57817
+ last: lastRow === "plugins",
57507
57818
  delay: 300
57508
57819
  }, undefined, false, undefined, this),
57509
- infoData.mcpCount !== null && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(DetailRow, {
57820
+ hasMcp && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(DetailRow, {
57510
57821
  label: "MCP Servers",
57511
57822
  value: `${infoData.mcpCount} total (${infoData.mcpPluginCount} from plugins, ${infoData.mcpCustomCount} custom)`,
57512
- last: true,
57823
+ last: lastRow === "mcp",
57513
57824
  delay: 350
57514
57825
  }, undefined, false, undefined, this)
57515
57826
  ]
57516
57827
  }, undefined, true, undefined, this)
57517
57828
  ]
57518
57829
  }, undefined, true, undefined, this),
57830
+ step === "actions" && displayInstance && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
57831
+ flexDirection: "column",
57832
+ gap: 1,
57833
+ children: [
57834
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
57835
+ gap: 1,
57836
+ children: [
57837
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
57838
+ color: "cyan",
57839
+ children: "●"
57840
+ }, undefined, false, undefined, this),
57841
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
57842
+ bold: true,
57843
+ color: "cyan",
57844
+ children: displayInstance.name
57845
+ }, undefined, false, undefined, this)
57846
+ ]
57847
+ }, undefined, true, undefined, this),
57848
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
57849
+ marginTop: 1,
57850
+ children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
57851
+ bold: true,
57852
+ children: "Actions:"
57853
+ }, undefined, false, undefined, this)
57854
+ }, undefined, false, undefined, this),
57855
+ (() => {
57856
+ const actionOptions = buildActionOptions();
57857
+ return /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Select, {
57858
+ options: actionOptions,
57859
+ visibleOptionCount: actionOptions.length,
57860
+ onChange: handleAction
57861
+ }, undefined, false, undefined, this);
57862
+ })()
57863
+ ]
57864
+ }, undefined, true, undefined, this),
57865
+ step === "executing" && displayInstance && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
57866
+ flexDirection: "column",
57867
+ gap: 1,
57868
+ children: [
57869
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
57870
+ gap: 1,
57871
+ children: [
57872
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
57873
+ color: "cyan",
57874
+ children: "●"
57875
+ }, undefined, false, undefined, this),
57876
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
57877
+ bold: true,
57878
+ color: "cyan",
57879
+ children: displayInstance.name
57880
+ }, undefined, false, undefined, this)
57881
+ ]
57882
+ }, undefined, true, undefined, this),
57883
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
57884
+ marginTop: 1,
57885
+ children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Spinner, {
57886
+ label: "Updating..."
57887
+ }, undefined, false, undefined, this)
57888
+ }, undefined, false, undefined, this)
57889
+ ]
57890
+ }, undefined, true, undefined, this),
57891
+ step === "result" && displayInstance && actionResult && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
57892
+ flexDirection: "column",
57893
+ gap: 1,
57894
+ children: [
57895
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
57896
+ gap: 1,
57897
+ children: [
57898
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
57899
+ color: "cyan",
57900
+ children: "●"
57901
+ }, undefined, false, undefined, this),
57902
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
57903
+ bold: true,
57904
+ color: "cyan",
57905
+ children: displayInstance.name
57906
+ }, undefined, false, undefined, this)
57907
+ ]
57908
+ }, undefined, true, undefined, this),
57909
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
57910
+ marginTop: 1,
57911
+ children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(StatusBar, {
57912
+ message: actionResult.message,
57913
+ type: actionResult.type
57914
+ }, undefined, false, undefined, this)
57915
+ }, undefined, false, undefined, this)
57916
+ ]
57917
+ }, undefined, true, undefined, this),
57519
57918
  /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
57520
57919
  marginTop: 1,
57521
- children: /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
57522
- dimColor: true,
57523
- children: "ESC to go back"
57524
- }, undefined, false, undefined, this)
57525
- }, undefined, false, undefined, this)
57920
+ children: [
57921
+ step === "info" && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
57922
+ gap: 2,
57923
+ children: [
57924
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
57925
+ dimColor: true,
57926
+ children: "Enter for actions"
57927
+ }, undefined, false, undefined, this),
57928
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
57929
+ dimColor: true,
57930
+ children: "ESC to go back"
57931
+ }, undefined, false, undefined, this),
57932
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
57933
+ dimColor: true,
57934
+ children: "q quit"
57935
+ }, undefined, false, undefined, this)
57936
+ ]
57937
+ }, undefined, true, undefined, this),
57938
+ step === "actions" && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
57939
+ dimColor: true,
57940
+ children: "↑↓ navigate │ Enter select │ ESC back │ q quit"
57941
+ }, undefined, false, undefined, this),
57942
+ step === "result" && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Box_default, {
57943
+ gap: 2,
57944
+ children: [
57945
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
57946
+ dimColor: true,
57947
+ children: "Enter/ESC to return"
57948
+ }, undefined, false, undefined, this),
57949
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
57950
+ dimColor: true,
57951
+ children: "q quit"
57952
+ }, undefined, false, undefined, this)
57953
+ ]
57954
+ }, undefined, true, undefined, this),
57955
+ step === "select" && /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(Text, {
57956
+ dimColor: true,
57957
+ children: "ESC to go back │ q quit"
57958
+ }, undefined, false, undefined, this)
57959
+ ]
57960
+ }, undefined, true, undefined, this)
57526
57961
  ]
57527
57962
  }, undefined, true, undefined, this);
57528
57963
  };
@@ -57530,10 +57965,12 @@ var init_ShowInstanceInfo = __esm(async () => {
57530
57965
  init_useConfig();
57531
57966
  init_useAnimations();
57532
57967
  init_migration();
57968
+ init_instance_diagnostics();
57533
57969
  await __promiseAll([
57534
57970
  init_build2(),
57971
+ init_build3(),
57535
57972
  init_Header(),
57536
- init_useNavigation(),
57973
+ init_StatusBar(),
57537
57974
  init_InstanceSelectMenu()
57538
57975
  ]);
57539
57976
  import_react69 = __toESM(require_react(), 1);
@@ -57545,6 +57982,12 @@ var init_ShowInstanceInfo = __esm(async () => {
57545
57982
  mcpPluginCount: null,
57546
57983
  mcpCustomCount: null
57547
57984
  };
57985
+ ACTION_VALUES = {
57986
+ SyncTemplate: "sync-template",
57987
+ UpdateWrapper: "update-wrapper",
57988
+ OverrideWrapper: "override-wrapper",
57989
+ Back: "back"
57990
+ };
57548
57991
  });
57549
57992
 
57550
57993
  // src/ink/screens/RemoveInstance.tsx
@@ -58965,61 +59408,8 @@ var init_ManageMcp = __esm(async () => {
58965
59408
  };
58966
59409
  });
58967
59410
 
58968
- // src/ink/components/IssueCard.tsx
58969
- var jsx_dev_runtime16, SEVERITY_ICONS, SEVERITY_COLORS, IssueCard = ({
58970
- issue,
58971
- index
58972
- }) => {
58973
- const visible = useFadeIn(index * 60 + 50);
58974
- if (!visible)
58975
- return null;
58976
- const icon = SEVERITY_ICONS[issue.severity] ?? "•";
58977
- const color = SEVERITY_COLORS[issue.severity] ?? "white";
58978
- return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
58979
- flexDirection: "column",
58980
- marginLeft: 2,
58981
- children: [
58982
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
58983
- gap: 1,
58984
- children: [
58985
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
58986
- children: icon
58987
- }, undefined, false, undefined, this),
58988
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
58989
- bold: true,
58990
- color,
58991
- children: issue.title
58992
- }, undefined, false, undefined, this)
58993
- ]
58994
- }, undefined, true, undefined, this),
58995
- /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
58996
- marginLeft: 2,
58997
- children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
58998
- dimColor: true,
58999
- children: issue.message
59000
- }, undefined, false, undefined, this)
59001
- }, undefined, false, undefined, this)
59002
- ]
59003
- }, undefined, true, undefined, this);
59004
- };
59005
- var init_IssueCard = __esm(async () => {
59006
- init_useAnimations();
59007
- await init_build2();
59008
- jsx_dev_runtime16 = __toESM(require_jsx_dev_runtime(), 1);
59009
- SEVERITY_ICONS = {
59010
- error: "❌",
59011
- warning: "⚠",
59012
- info: "ℹ️"
59013
- };
59014
- SEVERITY_COLORS = {
59015
- error: "red",
59016
- warning: "yellow",
59017
- info: "gray"
59018
- };
59019
- });
59020
-
59021
59411
  // src/ink/screens/HealthScreen.tsx
59022
- var import_react76, jsx_dev_runtime17, HealthScreen = ({
59412
+ var import_react76, jsx_dev_runtime16, SEVERITY_ICONS, HealthScreen = ({
59023
59413
  issues,
59024
59414
  onDismiss,
59025
59415
  onDismissAll,
@@ -59029,7 +59419,6 @@ var import_react76, jsx_dev_runtime17, HealthScreen = ({
59029
59419
  }) => {
59030
59420
  const [step, setStep] = import_react76.useState("list");
59031
59421
  const [selectedIssue, setSelectedIssue] = import_react76.useState(null);
59032
- const showActions = useFadeIn(100);
59033
59422
  useNavigation(() => {
59034
59423
  if (step === "detail") {
59035
59424
  setStep("list");
@@ -59039,38 +59428,39 @@ var import_react76, jsx_dev_runtime17, HealthScreen = ({
59039
59428
  }
59040
59429
  });
59041
59430
  use_input_default((input) => {
59042
- if (step !== "list")
59043
- return;
59044
- if (input === "d" && selectedIssue) {
59045
- onDismiss(selectedIssue.id);
59046
- setSelectedIssue(null);
59047
- } else if (input === "D") {
59048
- onDismissAll();
59049
- } else if (input === "r") {
59050
- onRetry();
59051
- } else if (input === "f") {
59052
- onFix();
59053
- } else if (input === " " && selectedIssue) {
59054
- setStep("detail");
59431
+ if (step === "list") {
59432
+ if (input === "D") {
59433
+ onDismissAll();
59434
+ } else if (input === "r") {
59435
+ onRetry();
59436
+ } else if (input === "f") {
59437
+ onFix();
59438
+ }
59439
+ } else if (step === "detail" && selectedIssue) {
59440
+ if (input === "d") {
59441
+ onDismiss(selectedIssue.id);
59442
+ setSelectedIssue(null);
59443
+ setStep("list");
59444
+ }
59055
59445
  }
59056
59446
  });
59057
59447
  if (issues.length === 0) {
59058
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59448
+ return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
59059
59449
  flexDirection: "column",
59060
59450
  width: "100",
59061
59451
  paddingX: 2,
59062
59452
  paddingY: 1,
59063
59453
  children: [
59064
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Header, {
59454
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Header, {
59065
59455
  title: "\uD83D\uDC9A System Health"
59066
59456
  }, undefined, false, undefined, this),
59067
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(StatusBar, {
59457
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(StatusBar, {
59068
59458
  message: "All systems healthy!",
59069
59459
  type: "success"
59070
59460
  }, undefined, false, undefined, this),
59071
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59461
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
59072
59462
  marginTop: 1,
59073
- children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59463
+ children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59074
59464
  dimColor: true,
59075
59465
  children: "ESC to go back"
59076
59466
  }, undefined, false, undefined, this)
@@ -59079,137 +59469,167 @@ var import_react76, jsx_dev_runtime17, HealthScreen = ({
59079
59469
  }, undefined, true, undefined, this);
59080
59470
  }
59081
59471
  const hasVersionIssues = issues.some((i) => i.category === "version");
59082
- return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59472
+ const handleIssueSelect = (value) => {
59473
+ const issue = issues.find((i) => i.id === value);
59474
+ if (issue) {
59475
+ setSelectedIssue(issue);
59476
+ setStep("detail");
59477
+ }
59478
+ };
59479
+ const issueOptions = issues.map((issue) => ({
59480
+ label: `${SEVERITY_ICONS[issue.severity] ?? "•"} ${issue.title}`,
59481
+ value: issue.id
59482
+ }));
59483
+ return /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
59083
59484
  flexDirection: "column",
59084
59485
  width: "100",
59085
59486
  paddingX: 2,
59086
59487
  paddingY: 1,
59087
59488
  children: [
59088
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Header, {
59489
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Header, {
59089
59490
  title: "⚠ System Health"
59090
59491
  }, undefined, false, undefined, this),
59091
- step === "list" && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59492
+ step === "list" && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
59092
59493
  flexDirection: "column",
59093
59494
  gap: 1,
59094
59495
  children: [
59095
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59496
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59096
59497
  bold: true,
59097
59498
  children: [
59098
59499
  issues.length,
59099
- " issue(s) found:"
59500
+ " issue(s) found — select one to view:"
59100
59501
  ]
59101
59502
  }, undefined, true, undefined, this),
59102
- issues.map((issue, i) => /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(IssueCard, {
59103
- issue,
59104
- index: i
59105
- }, issue.id, false, undefined, this)),
59106
- showActions && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59503
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Select, {
59504
+ options: issueOptions,
59505
+ visibleOptionCount: issueOptions.length,
59506
+ onChange: handleIssueSelect
59507
+ }, undefined, false, undefined, this),
59508
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
59107
59509
  marginTop: 1,
59108
59510
  flexDirection: "column",
59109
59511
  children: [
59110
- hasVersionIssues && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59512
+ hasVersionIssues && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
59111
59513
  marginBottom: 1,
59112
59514
  children: [
59113
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59515
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59114
59516
  color: "cyan",
59115
59517
  bold: true,
59116
59518
  children: "f"
59117
59519
  }, undefined, false, undefined, this),
59118
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59520
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59119
59521
  color: "cyan",
59120
59522
  children: " fix wrappers "
59121
59523
  }, undefined, false, undefined, this),
59122
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59524
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59123
59525
  dimColor: true,
59124
59526
  children: "│"
59125
59527
  }, undefined, false, undefined, this)
59126
59528
  ]
59127
59529
  }, undefined, true, undefined, this),
59128
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59530
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59129
59531
  dimColor: true,
59130
- children: "Space view detail │ d dismiss │ D dismiss all │ r retry │ ESC back"
59532
+ children: "D dismiss all │ r retry │ ESC back"
59131
59533
  }, undefined, false, undefined, this)
59132
59534
  ]
59133
59535
  }, undefined, true, undefined, this)
59134
59536
  ]
59135
59537
  }, undefined, true, undefined, this),
59136
- step === "detail" && selectedIssue && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59538
+ step === "detail" && selectedIssue && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
59137
59539
  flexDirection: "column",
59138
59540
  gap: 1,
59139
59541
  children: [
59140
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59542
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59141
59543
  bold: true,
59142
59544
  children: selectedIssue.title
59143
59545
  }, undefined, false, undefined, this),
59144
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59546
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
59145
59547
  marginLeft: 2,
59146
59548
  flexDirection: "column",
59147
59549
  children: [
59148
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59550
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
59149
59551
  gap: 1,
59150
59552
  children: [
59151
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59553
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59152
59554
  dimColor: true,
59153
59555
  children: "├─"
59154
59556
  }, undefined, false, undefined, this),
59155
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59557
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59156
59558
  dimColor: true,
59157
59559
  bold: true,
59158
59560
  children: "Severity:"
59159
59561
  }, undefined, false, undefined, this),
59160
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59562
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59161
59563
  color: selectedIssue.severity === "error" ? "red" : "yellow",
59162
59564
  children: selectedIssue.severity
59163
59565
  }, undefined, false, undefined, this)
59164
59566
  ]
59165
59567
  }, undefined, true, undefined, this),
59166
- selectedIssue.instanceName && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59568
+ selectedIssue.instanceName && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
59167
59569
  gap: 1,
59168
59570
  children: [
59169
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59571
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59170
59572
  dimColor: true,
59171
59573
  children: "├─"
59172
59574
  }, undefined, false, undefined, this),
59173
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59575
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59174
59576
  dimColor: true,
59175
59577
  bold: true,
59176
59578
  children: "Instance:"
59177
59579
  }, undefined, false, undefined, this),
59178
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59580
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59179
59581
  children: selectedIssue.instanceName
59180
59582
  }, undefined, false, undefined, this)
59181
59583
  ]
59182
59584
  }, undefined, true, undefined, this),
59183
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59585
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
59184
59586
  gap: 1,
59185
59587
  children: [
59186
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59588
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59187
59589
  dimColor: true,
59188
59590
  children: "├─"
59189
59591
  }, undefined, false, undefined, this),
59190
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59592
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59191
59593
  dimColor: true,
59192
59594
  bold: true,
59193
59595
  children: "Detail:"
59194
59596
  }, undefined, false, undefined, this),
59195
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59597
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59196
59598
  children: selectedIssue.message
59197
59599
  }, undefined, false, undefined, this)
59198
59600
  ]
59199
59601
  }, undefined, true, undefined, this),
59200
- selectedIssue.resolutionHint && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59602
+ selectedIssue.detail && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
59201
59603
  gap: 1,
59202
59604
  children: [
59203
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59605
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59606
+ dimColor: true,
59607
+ children: "├─"
59608
+ }, undefined, false, undefined, this),
59609
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59610
+ dimColor: true,
59611
+ bold: true,
59612
+ children: "Info:"
59613
+ }, undefined, false, undefined, this),
59614
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59615
+ dimColor: true,
59616
+ children: selectedIssue.detail
59617
+ }, undefined, false, undefined, this)
59618
+ ]
59619
+ }, undefined, true, undefined, this),
59620
+ selectedIssue.resolutionHint && /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
59621
+ gap: 1,
59622
+ children: [
59623
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59204
59624
  dimColor: true,
59205
59625
  children: "└─"
59206
59626
  }, undefined, false, undefined, this),
59207
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59627
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59208
59628
  dimColor: true,
59209
59629
  bold: true,
59210
59630
  children: "Fix:"
59211
59631
  }, undefined, false, undefined, this),
59212
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59632
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59213
59633
  dimColor: true,
59214
59634
  children: selectedIssue.resolutionHint
59215
59635
  }, undefined, false, undefined, this)
@@ -59217,18 +59637,18 @@ var import_react76, jsx_dev_runtime17, HealthScreen = ({
59217
59637
  }, undefined, true, undefined, this)
59218
59638
  ]
59219
59639
  }, undefined, true, undefined, this),
59220
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59640
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
59221
59641
  marginTop: 1,
59222
- children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59642
+ children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59223
59643
  dimColor: true,
59224
59644
  children: "d dismiss │ ESC back"
59225
59645
  }, undefined, false, undefined, this)
59226
59646
  }, undefined, false, undefined, this)
59227
59647
  ]
59228
59648
  }, undefined, true, undefined, this),
59229
- /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59649
+ /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Box_default, {
59230
59650
  marginTop: 1,
59231
- children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59651
+ children: /* @__PURE__ */ jsx_dev_runtime16.jsxDEV(Text, {
59232
59652
  dimColor: true,
59233
59653
  children: "ESC to go back"
59234
59654
  }, undefined, false, undefined, this)
@@ -59237,16 +59657,20 @@ var import_react76, jsx_dev_runtime17, HealthScreen = ({
59237
59657
  }, undefined, true, undefined, this);
59238
59658
  };
59239
59659
  var init_HealthScreen = __esm(async () => {
59240
- init_useAnimations();
59241
59660
  await __promiseAll([
59242
59661
  init_build2(),
59662
+ init_build3(),
59243
59663
  init_Header(),
59244
59664
  init_StatusBar(),
59245
- init_IssueCard(),
59246
59665
  init_useNavigation()
59247
59666
  ]);
59248
59667
  import_react76 = __toESM(require_react(), 1);
59249
- jsx_dev_runtime17 = __toESM(require_jsx_dev_runtime(), 1);
59668
+ jsx_dev_runtime16 = __toESM(require_jsx_dev_runtime(), 1);
59669
+ SEVERITY_ICONS = {
59670
+ error: "❌",
59671
+ warning: "⚠",
59672
+ info: "ℹ️"
59673
+ };
59250
59674
  });
59251
59675
 
59252
59676
  // src/ink/screens/UpdateScreen.tsx
@@ -59257,59 +59681,59 @@ function VersionCard({
59257
59681
  hasUpdate,
59258
59682
  failed
59259
59683
  }) {
59260
- return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59684
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59261
59685
  flexDirection: "column",
59262
59686
  marginTop: 1,
59263
59687
  children: [
59264
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59265
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
59688
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59689
+ children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59266
59690
  bold: true,
59267
59691
  children: label
59268
59692
  }, undefined, false, undefined, this)
59269
59693
  }, undefined, false, undefined, this),
59270
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59694
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59271
59695
  marginLeft: 2,
59272
59696
  children: [
59273
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
59697
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59274
59698
  dimColor: true,
59275
59699
  children: "Current: "
59276
59700
  }, undefined, false, undefined, this),
59277
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
59701
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59278
59702
  color: failed ? "red" : hasUpdate ? "yellow" : "green",
59279
59703
  children: current || (failed ? "check failed" : "not installed")
59280
59704
  }, undefined, false, undefined, this)
59281
59705
  ]
59282
59706
  }, undefined, true, undefined, this),
59283
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59707
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59284
59708
  marginLeft: 2,
59285
59709
  children: [
59286
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
59710
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59287
59711
  dimColor: true,
59288
59712
  children: "Latest: "
59289
59713
  }, undefined, false, undefined, this),
59290
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
59714
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59291
59715
  bold: true,
59292
59716
  children: latest || (failed ? "unreachable" : "unknown")
59293
59717
  }, undefined, false, undefined, this)
59294
59718
  ]
59295
59719
  }, undefined, true, undefined, this),
59296
- failed && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59720
+ failed && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59297
59721
  marginLeft: 2,
59298
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
59722
+ children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59299
59723
  color: "red",
59300
59724
  children: "✗ Could not reach npm registry"
59301
59725
  }, undefined, false, undefined, this)
59302
59726
  }, undefined, false, undefined, this),
59303
- !failed && hasUpdate && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59727
+ !failed && hasUpdate && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59304
59728
  marginLeft: 2,
59305
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
59729
+ children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59306
59730
  color: "yellow",
59307
59731
  children: "▲ Update available"
59308
59732
  }, undefined, false, undefined, this)
59309
59733
  }, undefined, false, undefined, this),
59310
- !failed && !hasUpdate && current && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59734
+ !failed && !hasUpdate && current && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59311
59735
  marginLeft: 2,
59312
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
59736
+ children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59313
59737
  color: "green",
59314
59738
  children: "✓ Up to date"
59315
59739
  }, undefined, false, undefined, this)
@@ -59317,7 +59741,7 @@ function VersionCard({
59317
59741
  ]
59318
59742
  }, undefined, true, undefined, this);
59319
59743
  }
59320
- var import_react77, jsx_dev_runtime18, UpdateScreen = ({ onBack }) => {
59744
+ var import_react77, jsx_dev_runtime17, UpdateScreen = ({ onBack }) => {
59321
59745
  const { exit } = use_app_default();
59322
59746
  const fadeIn = useFadeIn();
59323
59747
  const { error, success, setError, setSuccess, clear } = useMessage();
@@ -59390,41 +59814,41 @@ var import_react77, jsx_dev_runtime18, UpdateScreen = ({ onBack }) => {
59390
59814
  return null;
59391
59815
  if (step === "loading" || step === "check-failed") {
59392
59816
  const isFailed = step === "check-failed";
59393
- return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59817
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59394
59818
  flexDirection: "column",
59395
59819
  width: "100",
59396
59820
  paddingX: 2,
59397
59821
  paddingY: 1,
59398
59822
  children: [
59399
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Header, {
59823
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Header, {
59400
59824
  title: "\uD83D\uDCE6 Updates"
59401
59825
  }, undefined, false, undefined, this),
59402
- isFailed ? /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(jsx_dev_runtime18.Fragment, {
59826
+ isFailed ? /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(jsx_dev_runtime17.Fragment, {
59403
59827
  children: [
59404
- multiInfo && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(VersionCard, {
59828
+ multiInfo && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(VersionCard, {
59405
59829
  label: "claude-multi",
59406
59830
  current: multiInfo.current || null,
59407
59831
  latest: multiInfo.latest,
59408
59832
  hasUpdate: multiInfo.updateAvailable,
59409
59833
  failed: !multiInfo.current && !multiInfo.latest
59410
59834
  }, undefined, false, undefined, this),
59411
- claudeInfo && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(VersionCard, {
59835
+ claudeInfo && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(VersionCard, {
59412
59836
  label: "@anthropic-ai/claude-code",
59413
59837
  current: claudeInfo.current,
59414
59838
  latest: claudeInfo.latest,
59415
59839
  hasUpdate: claudeInfo.updateAvailable,
59416
59840
  failed: !claudeInfo.current && !claudeInfo.latest
59417
59841
  }, undefined, false, undefined, this),
59418
- error && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59842
+ error && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59419
59843
  marginTop: 1,
59420
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(StatusBar, {
59844
+ children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(StatusBar, {
59421
59845
  message: error,
59422
59846
  type: "error"
59423
59847
  }, undefined, false, undefined, this)
59424
59848
  }, undefined, false, undefined, this),
59425
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59849
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59426
59850
  marginTop: 1,
59427
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Select, {
59851
+ children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Select, {
59428
59852
  options: [
59429
59853
  { label: "\uD83D\uDD04 Retry", value: "recheck" },
59430
59854
  { label: "↩️ Back to menu", value: "back" }
@@ -59442,9 +59866,9 @@ var import_react77, jsx_dev_runtime18, UpdateScreen = ({ onBack }) => {
59442
59866
  }, undefined, false, undefined, this)
59443
59867
  }, undefined, false, undefined, this)
59444
59868
  ]
59445
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59869
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59446
59870
  marginTop: 1,
59447
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Spinner, {
59871
+ children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Spinner, {
59448
59872
  label: "Checking for updates..."
59449
59873
  }, undefined, false, undefined, this)
59450
59874
  }, undefined, false, undefined, this)
@@ -59452,24 +59876,24 @@ var import_react77, jsx_dev_runtime18, UpdateScreen = ({ onBack }) => {
59452
59876
  }, undefined, true, undefined, this);
59453
59877
  }
59454
59878
  if (step === "updating") {
59455
- return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59879
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59456
59880
  flexDirection: "column",
59457
59881
  width: "100",
59458
59882
  paddingX: 2,
59459
59883
  paddingY: 1,
59460
59884
  children: [
59461
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Header, {
59885
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Header, {
59462
59886
  title: "\uD83D\uDCE6 Updates"
59463
59887
  }, undefined, false, undefined, this),
59464
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59888
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59465
59889
  marginTop: 1,
59466
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Spinner, {
59890
+ children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Spinner, {
59467
59891
  label: `Updating ${updatingTarget === "multi" ? "claude-multi" : "@anthropic-ai/claude-code"}...`
59468
59892
  }, undefined, false, undefined, this)
59469
59893
  }, undefined, false, undefined, this),
59470
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59894
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59471
59895
  marginTop: 1,
59472
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
59896
+ children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
59473
59897
  dimColor: true,
59474
59898
  children: "Do not press ESC: update in progress"
59475
59899
  }, undefined, false, undefined, this)
@@ -59488,44 +59912,44 @@ var import_react77, jsx_dev_runtime18, UpdateScreen = ({ onBack }) => {
59488
59912
  }
59489
59913
  actionOptions.push({ label: "\uD83D\uDD04 Re-check versions", value: "recheck" });
59490
59914
  actionOptions.push({ label: "↩️ Back to menu", value: "back" });
59491
- return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59915
+ return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59492
59916
  flexDirection: "column",
59493
59917
  width: "100",
59494
59918
  paddingX: 2,
59495
59919
  paddingY: 1,
59496
59920
  children: [
59497
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Header, {
59921
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Header, {
59498
59922
  title: "\uD83D\uDCE6 Updates"
59499
59923
  }, undefined, false, undefined, this),
59500
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(VersionCard, {
59924
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(VersionCard, {
59501
59925
  label: "claude-multi",
59502
59926
  current: multiInfo?.current ?? null,
59503
59927
  latest: multiInfo?.latest ?? "",
59504
59928
  hasUpdate: multiUpdate
59505
59929
  }, undefined, false, undefined, this),
59506
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(VersionCard, {
59930
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(VersionCard, {
59507
59931
  label: "@anthropic-ai/claude-code",
59508
59932
  current: claudeInfo?.current ?? null,
59509
59933
  latest: claudeInfo?.latest ?? "",
59510
59934
  hasUpdate: claudeUpdate
59511
59935
  }, undefined, false, undefined, this),
59512
- error && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59936
+ error && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59513
59937
  marginTop: 1,
59514
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(StatusBar, {
59938
+ children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(StatusBar, {
59515
59939
  message: error,
59516
59940
  type: "error"
59517
59941
  }, undefined, false, undefined, this)
59518
59942
  }, undefined, false, undefined, this),
59519
- success && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59943
+ success && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59520
59944
  marginTop: 1,
59521
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(StatusBar, {
59945
+ children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(StatusBar, {
59522
59946
  message: success,
59523
59947
  type: "success"
59524
59948
  }, undefined, false, undefined, this)
59525
59949
  }, undefined, false, undefined, this),
59526
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59950
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
59527
59951
  marginTop: 1,
59528
- children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Select, {
59952
+ children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Select, {
59529
59953
  options: actionOptions,
59530
59954
  onChange: (value) => {
59531
59955
  if (value === "back") {
@@ -59543,7 +59967,7 @@ var import_react77, jsx_dev_runtime18, UpdateScreen = ({ onBack }) => {
59543
59967
  }
59544
59968
  }, undefined, false, undefined, this)
59545
59969
  }, undefined, false, undefined, this),
59546
- /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Footer, {}, undefined, false, undefined, this)
59970
+ /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Footer, {}, undefined, false, undefined, this)
59547
59971
  ]
59548
59972
  }, undefined, true, undefined, this);
59549
59973
  };
@@ -59559,7 +59983,7 @@ var init_UpdateScreen = __esm(async () => {
59559
59983
  init_Footer()
59560
59984
  ]);
59561
59985
  import_react77 = __toESM(require_react(), 1);
59562
- jsx_dev_runtime18 = __toESM(require_jsx_dev_runtime(), 1);
59986
+ jsx_dev_runtime17 = __toESM(require_jsx_dev_runtime(), 1);
59563
59987
  });
59564
59988
 
59565
59989
  // src/ink/App.tsx
@@ -59567,75 +59991,59 @@ var exports_App = {};
59567
59991
  __export(exports_App, {
59568
59992
  App: () => App2
59569
59993
  });
59570
- import { execSync as execSync3 } from "node:child_process";
59571
- import { existsSync as existsSync8 } from "node:fs";
59572
- var import_react78, jsx_dev_runtime19, GoodbyeScreen = () => {
59994
+ var import_react78, jsx_dev_runtime18, GoodbyeScreen = () => {
59573
59995
  const msg = useTypewriter("\uD83D\uDC4B Goodbye!", 40);
59574
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
59996
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59575
59997
  paddingX: 2,
59576
59998
  paddingY: 1,
59577
- children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
59999
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
59578
60000
  dimColor: true,
59579
60001
  children: msg
59580
60002
  }, undefined, false, undefined, this)
59581
60003
  }, undefined, false, undefined, this);
59582
- }, DoctorResultScreen = ({ fixedCount, migratedCount, installFailed, onBack }) => {
60004
+ }, DoctorResultScreen = ({ fixedCount, migratedCount, onBack }) => {
59583
60005
  useNavigation(onBack);
59584
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
60006
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59585
60007
  flexDirection: "column",
59586
60008
  width: "100",
59587
60009
  paddingX: 2,
59588
60010
  paddingY: 1,
59589
60011
  children: [
59590
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Header, {
59591
- title: "\uD83D\uDD27 Doctor Fix"
60012
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Header, {
60013
+ title: "Doctor Fix"
59592
60014
  }, undefined, false, undefined, this),
59593
- installFailed ? /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(jsx_dev_runtime19.Fragment, {
59594
- children: [
59595
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(StatusBar, {
59596
- message: "Failed to install pinned Claude binary",
59597
- type: "error"
59598
- }, undefined, false, undefined, this),
59599
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
59600
- marginTop: 1,
59601
- children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
59602
- dimColor: true,
59603
- children: "Check your network connection and try again."
59604
- }, undefined, false, undefined, this)
59605
- }, undefined, false, undefined, this)
59606
- ]
59607
- }, undefined, true, undefined, this) : fixedCount > 0 || migratedCount > 0 ? /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(jsx_dev_runtime19.Fragment, {
60015
+ fixedCount > 0 || migratedCount > 0 ? /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(jsx_dev_runtime18.Fragment, {
59608
60016
  children: [
59609
- fixedCount > 0 && /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(jsx_dev_runtime19.Fragment, {
60017
+ fixedCount > 0 && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(jsx_dev_runtime18.Fragment, {
59610
60018
  children: [
59611
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(StatusBar, {
59612
- message: `Fixed ${fixedCount} wrapper(s) to use pinned Claude v${COMPATIBLE_CLAUDE_VERSION}!`,
60019
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(StatusBar, {
60020
+ message: `Fixed ${fixedCount} wrapper(s) to use global Claude binary`,
59613
60021
  type: "success"
59614
60022
  }, undefined, false, undefined, this),
59615
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
60023
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59616
60024
  marginTop: 1,
59617
- children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
60025
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
59618
60026
  dimColor: true,
59619
- children: "All 3rd-party API instances now use the correct Claude binary."
60027
+ children: "All instances now use the globally installed Claude binary."
59620
60028
  }, undefined, false, undefined, this)
59621
60029
  }, undefined, false, undefined, this)
59622
60030
  ]
59623
60031
  }, undefined, true, undefined, this),
59624
- migratedCount > 0 && /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
60032
+ migratedCount > 0 && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59625
60033
  marginTop: fixedCount > 0 ? 1 : 0,
59626
- children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(StatusBar, {
60034
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(StatusBar, {
59627
60035
  message: `Migrated ${migratedCount} instance(s) to v${getClaudeMultiVersion()}`,
59628
60036
  type: "success"
59629
60037
  }, undefined, false, undefined, this)
59630
60038
  }, undefined, false, undefined, this)
59631
60039
  ]
59632
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(StatusBar, {
60040
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(StatusBar, {
59633
60041
  message: "All wrappers already use the correct Claude version",
59634
60042
  type: "info"
59635
60043
  }, undefined, false, undefined, this),
59636
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
60044
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59637
60045
  marginTop: 1,
59638
- children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
60046
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
59639
60047
  dimColor: true,
59640
60048
  children: "ESC to go back"
59641
60049
  }, undefined, false, undefined, this)
@@ -59646,9 +60054,9 @@ var import_react78, jsx_dev_runtime19, GoodbyeScreen = () => {
59646
60054
  const visible = useFadeIn(100);
59647
60055
  if (!visible || instances2.length === 0)
59648
60056
  return null;
59649
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
60057
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59650
60058
  marginBottom: 1,
59651
- children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
60059
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
59652
60060
  dimColor: true,
59653
60061
  children: [
59654
60062
  instances2.length,
@@ -59660,23 +60068,15 @@ var import_react78, jsx_dev_runtime19, GoodbyeScreen = () => {
59660
60068
  }, undefined, false, undefined, this);
59661
60069
  }, App2 = () => {
59662
60070
  const { exit } = use_app_default();
59663
- const { instances: instances2, loading, migrationStatus, instanceMigrationVersion } = useConfig();
60071
+ const { instances: instances2, loading, migrationStatus, instanceMigrationVersion, reload } = useConfig();
59664
60072
  const { issues, dismiss, dismissAll, retry } = useHealthCheck(instances2, migrationStatus, instanceMigrationVersion);
59665
60073
  const [screen, setScreen] = import_react78.useState("menu");
59666
60074
  const [menuKey, setMenuKey] = import_react78.useState(0);
59667
- const [ccVersion] = import_react78.useState(() => {
59668
- try {
59669
- const output = execSync3("claude --version 2>/dev/null", { encoding: "utf-8", timeout: 5000 }).trim();
59670
- return output.split(" ")[0] || null;
59671
- } catch {
59672
- return null;
59673
- }
59674
- });
59675
60075
  const [doctorResult, setDoctorResult] = import_react78.useState({
59676
60076
  fixedCount: 0,
59677
- migratedCount: 0,
59678
- installFailed: false
60077
+ migratedCount: 0
59679
60078
  });
60079
+ const [doctorRunning, setDoctorRunning] = import_react78.useState(false);
59680
60080
  use_input_default((input, key) => {
59681
60081
  if (screen !== "menu")
59682
60082
  return;
@@ -59688,10 +60088,10 @@ var import_react78, jsx_dev_runtime19, GoodbyeScreen = () => {
59688
60088
  }
59689
60089
  });
59690
60090
  if (loading) {
59691
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
60091
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59692
60092
  paddingX: 2,
59693
60093
  paddingY: 1,
59694
- children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Spinner, {
60094
+ children: /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Spinner, {
59695
60095
  label: "Loading..."
59696
60096
  }, undefined, false, undefined, this)
59697
60097
  }, undefined, false, undefined, this);
@@ -59701,45 +60101,35 @@ var import_react78, jsx_dev_runtime19, GoodbyeScreen = () => {
59701
60101
  setMenuKey((k) => k + 1);
59702
60102
  };
59703
60103
  const handleDoctorFix = async () => {
59704
- let installFailed = false;
59705
- if (!existsSync8(PINNED_CLAUDE_BIN)) {
60104
+ if (doctorRunning)
60105
+ return;
60106
+ setDoctorRunning(true);
60107
+ try {
60108
+ const fixed = fixWrapperVersions(instances2);
60109
+ let migrated = 0;
59706
60110
  try {
59707
- installPinnedClaude();
59708
- } catch {
59709
- installFailed = true;
59710
- }
59711
- } else {
59712
- const pinnedVer = getPinnedBinaryVersion();
59713
- if (pinnedVer && isThirdPartyApiBroken(pinnedVer)) {
59714
- try {
59715
- installPinnedClaude();
59716
- } catch {
59717
- installFailed = true;
60111
+ const fullConfig = await loadConfig();
60112
+ if (needsInstanceMigration(fullConfig)) {
60113
+ const before2 = fullConfig.instanceMigrationVersion;
60114
+ const migratedConfig = await runInstanceMigrations(fullConfig);
60115
+ if (migratedConfig.instanceMigrationVersion !== before2) {
60116
+ await saveConfigAtomic(migratedConfig);
60117
+ migrated = fullConfig.instances.length;
60118
+ }
59718
60119
  }
59719
- }
60120
+ } catch {}
60121
+ setDoctorResult({ fixedCount: fixed.length, migratedCount: migrated });
60122
+ await reload();
60123
+ setScreen("doctor-result");
60124
+ } finally {
60125
+ setDoctorRunning(false);
59720
60126
  }
59721
- const fixed = installFailed ? [] : fixWrapperVersions(instances2);
59722
- let migrated = 0;
59723
- try {
59724
- const fullConfig = await loadConfig();
59725
- if (needsInstanceMigration(fullConfig)) {
59726
- const before2 = fullConfig.instanceMigrationVersion;
59727
- const migratedConfig = await runInstanceMigrations(fullConfig);
59728
- if (migratedConfig.instanceMigrationVersion !== before2) {
59729
- await saveConfigAtomic(migratedConfig);
59730
- migrated = fullConfig.instances.length;
59731
- }
59732
- }
59733
- } catch {}
59734
- setDoctorResult({ fixedCount: fixed.length, migratedCount: migrated, installFailed });
59735
- retry();
59736
- setScreen("doctor-result");
59737
60127
  };
59738
60128
  if (screen === "goodbye") {
59739
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(GoodbyeScreen, {}, undefined, false, undefined, this);
60129
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(GoodbyeScreen, {}, undefined, false, undefined, this);
59740
60130
  }
59741
60131
  if (screen === "health") {
59742
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(HealthScreen, {
60132
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(HealthScreen, {
59743
60133
  issues,
59744
60134
  onDismiss: dismiss,
59745
60135
  onDismissAll: dismissAll,
@@ -59749,17 +60139,16 @@ var import_react78, jsx_dev_runtime19, GoodbyeScreen = () => {
59749
60139
  }, undefined, false, undefined, this);
59750
60140
  }
59751
60141
  if (screen === "doctor-result") {
59752
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(DoctorResultScreen, {
60142
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(DoctorResultScreen, {
59753
60143
  fixedCount: doctorResult.fixedCount,
59754
60144
  migratedCount: doctorResult.migratedCount,
59755
- installFailed: doctorResult.installFailed,
59756
60145
  onBack: goToMenu
59757
60146
  }, undefined, false, undefined, this);
59758
60147
  }
59759
60148
  if (screen !== "menu") {
59760
60149
  const ScreenComponent = SCREEN_MAP[screen];
59761
60150
  if (ScreenComponent) {
59762
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(ScreenComponent, {
60151
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(ScreenComponent, {
59763
60152
  onBack: goToMenu
59764
60153
  }, undefined, false, undefined, this);
59765
60154
  }
@@ -59790,44 +60179,26 @@ var import_react78, jsx_dev_runtime19, GoodbyeScreen = () => {
59790
60179
  { label: "\uD83D\uDCE6 Check for updates", value: "update" },
59791
60180
  { label: "\uD83D\uDEAA Exit", value: "exit" }
59792
60181
  ];
59793
- return /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
60182
+ return /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Box_default, {
59794
60183
  flexDirection: "column",
59795
60184
  width: "100",
59796
60185
  paddingX: 2,
59797
60186
  paddingY: 1,
59798
60187
  children: [
59799
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Header, {
60188
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Header, {
59800
60189
  title: "\uD83E\uDD16 Claude Multi",
59801
60190
  subtitle: "Interactive Mode"
59802
60191
  }, undefined, false, undefined, this),
59803
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(WarningBanner, {
60192
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(WarningBanner, {
59804
60193
  issueCount: issues.length,
59805
60194
  errorCount,
59806
60195
  warningCount,
59807
60196
  hasVersionIssues
59808
60197
  }, undefined, false, undefined, this),
59809
- ccVersion && isThirdPartyApiBroken(ccVersion) && /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
59810
- marginBottom: 1,
59811
- children: [
59812
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
59813
- color: "red",
59814
- bold: true,
59815
- children: [
59816
- "⚠ Claude Code v",
59817
- ccVersion,
59818
- " does not work with 3rd-party APIs. "
59819
- ]
59820
- }, undefined, true, undefined, this),
59821
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text, {
59822
- color: "red",
59823
- children: "Run 'claude-multi doctor fix' to install a compatible version."
59824
- }, undefined, false, undefined, this)
59825
- ]
59826
- }, undefined, true, undefined, this),
59827
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(InstanceLine, {
60198
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(InstanceLine, {
59828
60199
  instances: instances2
59829
60200
  }, undefined, false, undefined, this),
59830
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Select, {
60201
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Select, {
59831
60202
  options: menuOptions,
59832
60203
  visibleOptionCount: menuOptions.length,
59833
60204
  onChange: async (value) => {
@@ -59843,7 +60214,7 @@ var import_react78, jsx_dev_runtime19, GoodbyeScreen = () => {
59843
60214
  }
59844
60215
  }
59845
60216
  }, menuKey, false, undefined, this),
59846
- /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Footer, {
60217
+ /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Footer, {
59847
60218
  showHealthHint: issues.length > 0
59848
60219
  }, undefined, false, undefined, this)
59849
60220
  ]
@@ -59851,7 +60222,6 @@ var import_react78, jsx_dev_runtime19, GoodbyeScreen = () => {
59851
60222
  }, SCREEN_MAP;
59852
60223
  var init_App2 = __esm(async () => {
59853
60224
  init_version();
59854
- init_paths();
59855
60225
  init_useConfig();
59856
60226
  init_useHealthCheck();
59857
60227
  init_useAnimations();
@@ -59878,7 +60248,7 @@ var init_App2 = __esm(async () => {
59878
60248
  init_UpdateScreen()
59879
60249
  ]);
59880
60250
  import_react78 = __toESM(require_react(), 1);
59881
- jsx_dev_runtime19 = __toESM(require_jsx_dev_runtime(), 1);
60251
+ jsx_dev_runtime18 = __toESM(require_jsx_dev_runtime(), 1);
59882
60252
  SCREEN_MAP = {
59883
60253
  add: AddInstance,
59884
60254
  list: ListInstances,
@@ -59919,7 +60289,7 @@ init_errors();
59919
60289
  init_constants();
59920
60290
  var import_prompts = __toESM(require_prompts3(), 1);
59921
60291
  import { homedir as homedir4 } from "node:os";
59922
- import { join as join9 } from "node:path";
60292
+ import { join as join9, dirname as dirname5, delimiter } from "node:path";
59923
60293
  function exitWithCode(code) {
59924
60294
  process.exit(code);
59925
60295
  }
@@ -59956,6 +60326,7 @@ program2.command("add <name>").description("Add a new Claude Code instance").opt
59956
60326
  let providerTemplate = null;
59957
60327
  let apiKey = "";
59958
60328
  let autoSync = !options.manual;
60329
+ let providerRegion;
59959
60330
  if (options.provider) {
59960
60331
  providerTemplate = getProviderTemplate(options.provider);
59961
60332
  if (!providerTemplate) {
@@ -59970,12 +60341,19 @@ program2.command("add <name>").description("Add a new Claude Code instance").opt
59970
60341
  useProviderTemplate = true;
59971
60342
  if (providerHasRegions(providerTemplate.name)) {
59972
60343
  const region = options.region || "cn";
60344
+ if (!options.region) {
60345
+ const regions = getProviderRegions(providerTemplate.name);
60346
+ console.log(source_default.yellow(`⚠ No --region specified, defaulting to cn. Available: ${regions ? Object.keys(regions).join(", ") : "cn"}`));
60347
+ }
60348
+ providerRegion = region;
59973
60349
  try {
59974
60350
  providerTemplate = resolveRegionTemplate(providerTemplate, region);
59975
60351
  } catch (err) {
59976
60352
  console.error(source_default.red(`✗ ${toMessage(err)}`));
59977
60353
  exitWithCode(1);
59978
60354
  }
60355
+ } else if (options.region) {
60356
+ console.log(source_default.yellow(`⚠ --region is ignored for non-regional provider '${providerTemplate.name}'`));
59979
60357
  }
59980
60358
  }
59981
60359
  if (options.copySettings || options.copyAll || options.copyMcp || options.skipPrompts || options.provider) {
@@ -60040,7 +60418,9 @@ Found existing Claude Code configuration at ~/.claude`));
60040
60418
  binaryPath,
60041
60419
  createdAt: new Date().toISOString(),
60042
60420
  autoSync,
60043
- createdWithVersion: getClaudeMultiVersion()
60421
+ createdWithVersion: getClaudeMultiVersion(),
60422
+ ...useProviderTemplate && providerTemplate ? { providerTemplate: providerTemplate.name } : {},
60423
+ ...providerRegion ? { providerRegion } : {}
60044
60424
  };
60045
60425
  await addInstance(instance);
60046
60426
  try {
@@ -60080,9 +60460,9 @@ Found existing Claude Code configuration at ~/.claude`));
60080
60460
  console.log(source_default.gray(` Binary: ${binaryPath}`));
60081
60461
  console.log(source_default.gray(` Config: ${configDir}`));
60082
60462
  console.log();
60083
- const binDir = binaryPath.substring(0, binaryPath.lastIndexOf("/"));
60463
+ const binDir = dirname5(binaryPath);
60084
60464
  const pathEnv = process.env.PATH || "";
60085
- const isInPath = pathEnv.split(":").some((p) => p === binDir);
60465
+ const isInPath = pathEnv.split(delimiter).some((p) => p === binDir);
60086
60466
  if (!isInPath) {
60087
60467
  console.log(source_default.yellow(`⚠ Warning: ${binDir} is not in your PATH`));
60088
60468
  console.log(source_default.gray(`Add to PATH by running:`));
@@ -60136,6 +60516,10 @@ program2.command("list").alias("ls").description("List all Claude Code instances
60136
60516
  console.log(source_default.gray(` Binary: ${instance.binaryPath}`));
60137
60517
  console.log(source_default.gray(` Config: ${instance.configDir}`));
60138
60518
  console.log(source_default.gray(` Created: ${new Date(instance.createdAt).toLocaleString()}`));
60519
+ if (instance.providerTemplate) {
60520
+ const region = instance.providerRegion ? ` (${instance.providerRegion})` : "";
60521
+ console.log(source_default.gray(` Provider: ${instance.providerTemplate}${region}`));
60522
+ }
60139
60523
  const autoSyncStatus = instance.autoSync !== false ? source_default.green("on") : source_default.yellow("off");
60140
60524
  console.log(source_default.gray(` Auto-sync: ${autoSyncStatus}`));
60141
60525
  console.log(source_default.gray(` Version: ${formatVersionLabel(instance.createdWithVersion)}`));
@@ -60157,6 +60541,10 @@ program2.command("info <name>").description("Show details about a specific insta
60157
60541
  const autoSyncStatus = instance.autoSync !== false ? source_default.green("✓ Enabled") : source_default.yellow("✗ Disabled");
60158
60542
  console.log(`${source_default.gray("Auto-sync:")} ${autoSyncStatus}`);
60159
60543
  console.log(`${source_default.gray("Version:")} ${formatVersionLabel(instance.createdWithVersion)}`);
60544
+ if (instance.providerTemplate) {
60545
+ const region = instance.providerRegion ? ` (${instance.providerRegion})` : "";
60546
+ console.log(`${source_default.gray("Provider:")} ${instance.providerTemplate}${region}`);
60547
+ }
60160
60548
  } catch (error) {
60161
60549
  console.error(source_default.red(`✗ Error: ${toMessage(error)}`));
60162
60550
  exitWithCode(1);
@@ -60232,17 +60620,13 @@ program2.command("doctor").description("Diagnose and fix common issues").argumen
60232
60620
  const [
60233
60621
  instances2,
60234
60622
  { runHealthChecks: runHealthChecks2, fixWrapperVersions: fixWrapperVersions2 },
60235
- { existsSync: existsSync9 },
60236
- { PINNED_CLAUDE_BIN: PINNED_CLAUDE_BIN2 },
60237
- { installPinnedClaude: installPinnedClaude2, getPinnedBinaryVersion: getPinnedBinaryVersion2, isThirdPartyApiBroken: isThirdPartyApiBroken2, COMPATIBLE_CLAUDE_VERSION: COMPATIBLE_CLAUDE_VERSION2 },
60623
+ { tryGetClaudePath: tryGetClaudePath2 },
60238
60624
  { needsInstanceMigration: needsInstanceMigration2, runInstanceMigrations: runInstanceMigrations2 },
60239
60625
  { loadConfig: loadConfig2, saveConfigAtomic: saveConfigAtomic2 }
60240
60626
  ] = await Promise.all([
60241
60627
  listInstances(),
60242
60628
  Promise.resolve().then(() => (init_health(), exports_health)),
60243
- import("node:fs"),
60244
- Promise.resolve().then(() => (init_paths(), exports_paths)),
60245
- Promise.resolve().then(() => (init_version(), exports_version)),
60629
+ Promise.resolve().then(() => (init_wrapper(), exports_wrapper)),
60246
60630
  Promise.resolve().then(() => (init_migration(), exports_migration)),
60247
60631
  Promise.resolve().then(() => (init_config(), exports_config))
60248
60632
  ]);
@@ -60250,23 +60634,15 @@ program2.command("doctor").description("Diagnose and fix common issues").argumen
60250
60634
  console.log(source_default.bold(`
60251
60635
  \uD83D\uDD27 Doctor Fix
60252
60636
  `));
60253
- if (!existsSync9(PINNED_CLAUDE_BIN2)) {
60254
- console.log(source_default.cyan(`Installing compatible Claude v${COMPATIBLE_CLAUDE_VERSION2}...`));
60255
- installPinnedClaude2();
60256
- console.log(source_default.green(`✓ Installed pinned Claude v${COMPATIBLE_CLAUDE_VERSION2}`));
60257
- } else {
60258
- const pinnedVer = getPinnedBinaryVersion2();
60259
- const needsReinstall = !pinnedVer || isThirdPartyApiBroken2(pinnedVer) || pinnedVer !== COMPATIBLE_CLAUDE_VERSION2;
60260
- if (needsReinstall) {
60261
- const reason = !pinnedVer ? "version unknown" : isThirdPartyApiBroken2(pinnedVer) ? "incompatible" : "outdated";
60262
- console.log(source_default.yellow(`Pinned binary is ${pinnedVer ? `v${pinnedVer}` : reason}. Reinstalling v${COMPATIBLE_CLAUDE_VERSION2}...`));
60263
- installPinnedClaude2();
60264
- console.log(source_default.green(`✓ Reinstalled pinned Claude v${COMPATIBLE_CLAUDE_VERSION2}`));
60265
- }
60637
+ const claudePath = tryGetClaudePath2();
60638
+ if (!claudePath) {
60639
+ console.error(source_default.red("Claude Code not found in PATH. Please install it first."));
60640
+ process.exit(1);
60266
60641
  }
60642
+ console.log(source_default.gray(`Using claude at: ${claudePath}`));
60267
60643
  const fixed = fixWrapperVersions2(instances2);
60268
60644
  if (fixed.length > 0) {
60269
- console.log(source_default.green(`✓ Fixed ${fixed.length} wrapper(s) to use pinned Claude version:`));
60645
+ console.log(source_default.green(`✓ Fixed ${fixed.length} wrapper(s) to use resolved Claude version:`));
60270
60646
  for (const name of fixed) {
60271
60647
  console.log(source_default.gray(` • ${name}`));
60272
60648
  }