cc-hub-cli 1.1.12 → 1.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +130 -83
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command7 } from "commander";
4
+ import { Command as Command8 } from "commander";
5
5
  import { createRequire } from "module";
6
6
 
7
7
  // src/profiles/commands.ts
@@ -258,9 +258,6 @@ import { randomUUID } from "crypto";
258
258
  // src/provider/index.ts
259
259
  import { Command } from "commander";
260
260
 
261
- // src/provider/server.ts
262
- import http from "http";
263
-
264
261
  // src/provider/transform.ts
265
262
  function sanitizeToolId(id) {
266
263
  let sanitized = id.replace(/[^a-zA-Z0-9_-]/g, "_");
@@ -486,6 +483,7 @@ data: ${JSON.stringify(data)}
486
483
  }
487
484
 
488
485
  // src/provider/server.ts
486
+ import http from "http";
489
487
  async function startOpenAIProxy(targetUrl, apiKey, model, models = [], modelMappings = {}) {
490
488
  const base = targetUrl.replace(/\/+$/, "");
491
489
  const server = http.createServer(async (req, res) => {
@@ -631,9 +629,6 @@ function isAnthropicModel(model) {
631
629
  }
632
630
  return false;
633
631
  }
634
- function collect(value, previous) {
635
- return previous.concat([value]);
636
- }
637
632
  function providerCommand() {
638
633
  const cmd = new Command("provider").description("Manage provider types");
639
634
  cmd.command("list").description("List available provider types").action(safeAction(() => {
@@ -646,48 +641,6 @@ function providerCommand() {
646
641
  }));
647
642
  return cmd;
648
643
  }
649
- function proxyCommand() {
650
- return new Command("proxy").description("Start a standalone OpenAI proxy for the desktop app").option("--profile <name>", "Use configuration from a saved profile").option("-u, --url <url>", "Upstream base URL (e.g., https://api.openai.com)").option("-k, --api-key <key>", "Upstream API key").option("-m, --model <model>", "Default model", "gpt-4o").option("--mapping <mapping>", "Model alias mapping (format: alias:actual, can be used multiple times)", collect, []).action(safeAction(async (opts) => {
651
- let targetUrl = opts.url || "https://api.openai.com";
652
- let apiKey = opts.apiKey || "";
653
- let defaultModel = opts.model || "gpt-4o";
654
- let models = [];
655
- const modelMappings = {};
656
- if (opts.profile) {
657
- ensureProfilesFile();
658
- const data = readJson(PROFILES_FILE);
659
- const p = data.profiles[opts.profile];
660
- if (!p) {
661
- throw new Error(`Profile '${opts.profile}' not found.`);
662
- }
663
- targetUrl = p.url || targetUrl;
664
- apiKey = p.token || apiKey;
665
- models = p.models || (p.model ? [p.model] : []);
666
- defaultModel = models[0] || defaultModel;
667
- models.forEach((m, i) => {
668
- if (!isAnthropicModel(m)) {
669
- const alias = ANTHROPIC_ALIASES[Math.min(i, ANTHROPIC_ALIASES.length - 1)];
670
- modelMappings[alias] = m;
671
- }
672
- });
673
- } else {
674
- for (const m of opts.mapping) {
675
- const [alias, actual] = m.split(":");
676
- if (alias && actual) {
677
- modelMappings[alias] = actual;
678
- }
679
- }
680
- models = [defaultModel];
681
- }
682
- const { baseUrl, stop } = await startOpenAIProxy(targetUrl, apiKey, defaultModel, models, modelMappings);
683
- console.log(`Proxy running at ${baseUrl}`);
684
- console.log("Press Ctrl+C to stop");
685
- process.on("SIGINT", () => {
686
- stop();
687
- process.exit(0);
688
- });
689
- }));
690
- }
691
644
 
692
645
  // src/platform/profile-syncer.ts
693
646
  function toDesktopProfile(p) {
@@ -970,6 +923,9 @@ function fixJsonFile(filePath, fallback = {}) {
970
923
  }
971
924
  }
972
925
 
926
+ // src/profiles/commands.ts
927
+ import path5 from "path";
928
+
973
929
  // src/profiles/runner.ts
974
930
  import { spawnSync as spawnSync2, spawn } from "child_process";
975
931
  var BUILT_IN_DEFAULT = "__builtin__";
@@ -1117,13 +1073,13 @@ function formatModels(p) {
1117
1073
  }
1118
1074
  return p.model || "(unset)";
1119
1075
  }
1120
- function collect2(value, previous) {
1076
+ function collect(value, previous) {
1121
1077
  return previous.concat([value]);
1122
1078
  }
1123
1079
  function profileCommand() {
1124
1080
  const profile = new Command2("profile").description("Manage Claude CLI profiles");
1125
1081
  const syncer = createProfileSyncer();
1126
- profile.command("add").description("Add or update a profile").argument("<name>", "Profile name").option("-m, --model <model>", "Model ID - can be used multiple times (max 3)", collect2, []).option("-t, --token <token>", "API key / token").option("-u, --url <url>", "Base URL").option("-p, --provider <provider>", "Provider type: anthropic (default) or openai").action(safeAction((name, opts) => {
1082
+ profile.command("add").description("Add or update a profile").argument("<name>", "Profile name").option("-m, --model <model>", "Model ID - can be used multiple times (max 3)", collect, []).option("-t, --token <token>", "API key / token").option("-u, --url <url>", "Base URL").option("-p, --provider <provider>", "Provider type: anthropic (default) or openai").action(safeAction((name, opts) => {
1127
1083
  const models = opts.model && opts.model.length > 0 ? opts.model : void 0;
1128
1084
  if (models && models.length > 3) {
1129
1085
  throw new Error("Error: A profile can have at most 3 models.");
@@ -1145,7 +1101,7 @@ function profileCommand() {
1145
1101
  debug(`profile add: wrote ${PROFILES_FILE}`);
1146
1102
  console.log(`Profile '${name}' saved.`);
1147
1103
  }));
1148
- profile.command("update").description("Update fields of an existing profile").argument("<name>", "Profile name (must already exist)").option("-m, --model <model>", "Model ID - can be used multiple times", collect2, []).option("-d, --delete-model <model>", "Remove model ID - can be used multiple times", collect2, []).option("-t, --token <token>", "API key / token").option("-u, --url <url>", "Base URL").option("-p, --provider <provider>", "Provider type").action(safeAction((name, opts) => {
1104
+ profile.command("update").description("Update fields of an existing profile").argument("<name>", "Profile name (must already exist)").option("-m, --model <model>", "Model ID - can be used multiple times", collect, []).option("-d, --delete-model <model>", "Remove model ID - can be used multiple times", collect, []).option("-t, --token <token>", "API key / token").option("-u, --url <url>", "Base URL").option("-p, --provider <provider>", "Provider type").action(safeAction((name, opts) => {
1149
1105
  ensureProfilesFile();
1150
1106
  const data = readJson(PROFILES_FILE);
1151
1107
  if (!data.profiles[name]) {
@@ -1345,6 +1301,48 @@ function profileCommand() {
1345
1301
  debug(`profile sync: wrote ${PROFILES_FILE}`);
1346
1302
  console.log(`Synced ${names.length} profile(s) to the desktop app.`);
1347
1303
  }));
1304
+ profile.command("export").description("Export a profile to a settings file").argument("<name>", "Profile name").action(safeAction((name) => {
1305
+ ensureProfilesFile();
1306
+ const data = readJson(PROFILES_FILE);
1307
+ const p = data.profiles[name];
1308
+ if (!p) {
1309
+ throw new Error(`Profile '${name}' not found.`);
1310
+ }
1311
+ ensureSettingsFile();
1312
+ const settings2 = readJson(SETTINGS_FILE);
1313
+ const exported = Object.fromEntries(
1314
+ Object.entries(settings2).filter(([key]) => !key.startsWith("_"))
1315
+ );
1316
+ const env = {
1317
+ ...typeof exported.env === "object" && exported.env !== null ? exported.env : {}
1318
+ };
1319
+ if (p.token) env.ANTHROPIC_AUTH_TOKEN = p.token;
1320
+ if (p.url) env.ANTHROPIC_BASE_URL = p.url;
1321
+ const models = p.models || (p.model ? [p.model] : []);
1322
+ if (models.length > 0) {
1323
+ if (models[0]) {
1324
+ env.ANTHROPIC_DEFAULT_SONNET_MODEL = models[0];
1325
+ env.ANTHROPIC_DEFAULT_SONNET_MODEL_NAME = models[0];
1326
+ env.ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION = `Custom: ${models[0]}`;
1327
+ }
1328
+ if (models[1]) {
1329
+ env.ANTHROPIC_DEFAULT_OPUS_MODEL = models[1];
1330
+ env.ANTHROPIC_DEFAULT_OPUS_MODEL_NAME = models[1];
1331
+ env.ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION = `Custom: ${models[1]}`;
1332
+ }
1333
+ if (models[2]) {
1334
+ env.ANTHROPIC_DEFAULT_HAIKU_MODEL = models[2];
1335
+ env.ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME = models[2];
1336
+ env.ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION = `Custom: ${models[2]}`;
1337
+ }
1338
+ }
1339
+ env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = "1";
1340
+ env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = "1";
1341
+ exported.env = env;
1342
+ const exportPath = path5.join(CLAUDE_DIR, `settings.${name}.json`);
1343
+ writeJson(exportPath, exported);
1344
+ console.log(`Profile '${name}' exported to ${exportPath}`);
1345
+ }));
1348
1346
  return profile;
1349
1347
  }
1350
1348
  function useCommand() {
@@ -1624,12 +1622,12 @@ function decodePath2(encoded) {
1624
1622
 
1625
1623
  // src/sessions/stats.ts
1626
1624
  import fs5 from "fs";
1627
- import path5 from "path";
1625
+ import path6 from "path";
1628
1626
  function getDirSize(dir) {
1629
1627
  let total = 0;
1630
1628
  try {
1631
1629
  for (const entry of fs5.readdirSync(dir, { withFileTypes: true })) {
1632
- const fullPath = path5.join(dir, entry.name);
1630
+ const fullPath = path6.join(dir, entry.name);
1633
1631
  if (entry.isDirectory()) {
1634
1632
  total += getDirSize(fullPath);
1635
1633
  } else {
@@ -1653,7 +1651,7 @@ function formatSize(bytes) {
1653
1651
 
1654
1652
  // src/sessions/utils.ts
1655
1653
  import fs6 from "fs";
1656
- import path6 from "path";
1654
+ import path7 from "path";
1657
1655
  function formatTimestamp(ms) {
1658
1656
  const d = new Date(ms);
1659
1657
  const pad = (n) => String(n).padStart(2, "0");
@@ -1662,7 +1660,7 @@ function formatTimestamp(ms) {
1662
1660
  function findProjectDir(query) {
1663
1661
  debug(`sessions: findProjectDir query="${query}"`);
1664
1662
  const encoded = encodePath(query);
1665
- if (fs6.existsSync(path6.join(PROJECTS_DIR, encoded))) {
1663
+ if (fs6.existsSync(path7.join(PROJECTS_DIR, encoded))) {
1666
1664
  debug(`sessions: findProjectDir exact match ${encoded}`);
1667
1665
  return encoded;
1668
1666
  }
@@ -1740,10 +1738,10 @@ function findSessionFile(sessionQuery, projectQuery) {
1740
1738
  if (!projDir) {
1741
1739
  throw new Error(`No project matched: ${projectQuery}`);
1742
1740
  }
1743
- searchDirs.push(path6.join(PROJECTS_DIR, projDir));
1741
+ searchDirs.push(path7.join(PROJECTS_DIR, projDir));
1744
1742
  } else {
1745
1743
  try {
1746
- searchDirs = fs6.readdirSync(PROJECTS_DIR).map((d) => path6.join(PROJECTS_DIR, d));
1744
+ searchDirs = fs6.readdirSync(PROJECTS_DIR).map((d) => path7.join(PROJECTS_DIR, d));
1747
1745
  } catch {
1748
1746
  throw new Error(`No projects directory found at ${PROJECTS_DIR}`);
1749
1747
  }
@@ -1759,7 +1757,7 @@ function findSessionFile(sessionQuery, projectQuery) {
1759
1757
  for (const file of files) {
1760
1758
  const sessionId = file.replace(/\.jsonl$/, "");
1761
1759
  if (sessionId.toLowerCase().includes(sessionQuery.toLowerCase())) {
1762
- matches.push({ filePath: path6.join(dir, file), project: path6.basename(dir) });
1760
+ matches.push({ filePath: path7.join(dir, file), project: path7.basename(dir) });
1763
1761
  }
1764
1762
  }
1765
1763
  }
@@ -1767,7 +1765,7 @@ function findSessionFile(sessionQuery, projectQuery) {
1767
1765
  return null;
1768
1766
  }
1769
1767
  if (matches.length > 1) {
1770
- const lines = matches.map((m) => ` ${path6.basename(m.filePath)} in ${decodePath(m.project)}`).join("\n");
1768
+ const lines = matches.map((m) => ` ${path7.basename(m.filePath)} in ${decodePath(m.project)}`).join("\n");
1771
1769
  throw new Error(`Multiple sessions matched '${sessionQuery}':
1772
1770
  ${lines}
1773
1771
  Use --project to disambiguate.`);
@@ -1778,7 +1776,7 @@ Use --project to disambiguate.`);
1778
1776
  // src/sessions/commands.ts
1779
1777
  import { Command as Command4 } from "commander";
1780
1778
  import fs7 from "fs";
1781
- import path7 from "path";
1779
+ import path8 from "path";
1782
1780
  import { spawnSync as spawnSync3 } from "child_process";
1783
1781
  function sessionCommand() {
1784
1782
  const session = new Command4("session").description("Manage Claude Code sessions");
@@ -1795,14 +1793,14 @@ function sessionCommand() {
1795
1793
  return;
1796
1794
  }
1797
1795
  dirs.sort((a, b) => {
1798
- const statA = fs7.statSync(path7.join(PROJECTS_DIR, a));
1799
- const statB = fs7.statSync(path7.join(PROJECTS_DIR, b));
1796
+ const statA = fs7.statSync(path8.join(PROJECTS_DIR, a));
1797
+ const statB = fs7.statSync(path8.join(PROJECTS_DIR, b));
1800
1798
  return statB.mtimeMs - statA.mtimeMs;
1801
1799
  });
1802
1800
  let count = 0;
1803
1801
  for (const projDir of dirs) {
1804
1802
  if (count >= limit) break;
1805
- const fullPath = path7.join(PROJECTS_DIR, projDir);
1803
+ const fullPath = path8.join(PROJECTS_DIR, projDir);
1806
1804
  let nSessions = 0;
1807
1805
  try {
1808
1806
  nSessions = fs7.readdirSync(fullPath).filter((f) => f.endsWith(".jsonl")).length;
@@ -1826,7 +1824,7 @@ function sessionCommand() {
1826
1824
  if (!projDir) {
1827
1825
  throw new Error(`No project matched: ${project}`);
1828
1826
  }
1829
- const fullPath = path7.join(PROJECTS_DIR, projDir);
1827
+ const fullPath = path8.join(PROJECTS_DIR, projDir);
1830
1828
  console.log(`Project: ${decodePath2(projDir)}`);
1831
1829
  console.log(`Dir: ${fullPath}`);
1832
1830
  console.log("");
@@ -1840,7 +1838,7 @@ function sessionCommand() {
1840
1838
  return;
1841
1839
  }
1842
1840
  for (const file of files) {
1843
- const filePath = path7.join(fullPath, file);
1841
+ const filePath = path8.join(fullPath, file);
1844
1842
  const sessionId = file.replace(/\.jsonl$/, "");
1845
1843
  let msgCount = 0;
1846
1844
  try {
@@ -1890,7 +1888,7 @@ function sessionCommand() {
1890
1888
  if (!projDir) {
1891
1889
  throw new Error(`No project matched: ${opts.project}`);
1892
1890
  }
1893
- searchRoots = [{ root: path7.join(PROJECTS_DIR, projDir), label: "" }];
1891
+ searchRoots = [{ root: path8.join(PROJECTS_DIR, projDir), label: "" }];
1894
1892
  }
1895
1893
  const limit = parseInt(opts.limit, 10);
1896
1894
  let count = 0;
@@ -1904,7 +1902,7 @@ function sessionCommand() {
1904
1902
  }
1905
1903
  for (const entry of entries) {
1906
1904
  if (count >= limit) break;
1907
- const fullPath = path7.join(dir, entry.name);
1905
+ const fullPath = path8.join(dir, entry.name);
1908
1906
  if (entry.isDirectory()) {
1909
1907
  searchDir(fullPath, label, baseDir);
1910
1908
  } else if (entry.name.endsWith(".jsonl")) {
@@ -1918,9 +1916,9 @@ function sessionCommand() {
1918
1916
  const match = opts.ignoreCase ? line.toLowerCase().includes(query.toLowerCase()) : line.includes(query);
1919
1917
  if (match) {
1920
1918
  if (!found) {
1921
- const relPath = path7.relative(baseDir, fullPath);
1922
- const projEnc = relPath.split(path7.sep)[0];
1923
- const sessionId = path7.basename(fullPath, ".jsonl");
1919
+ const relPath = path8.relative(baseDir, fullPath);
1920
+ const projEnc = relPath.split(path8.sep)[0];
1921
+ const sessionId = path8.basename(fullPath, ".jsonl");
1924
1922
  const projName = label ? projEnc : decodePath2(projEnc);
1925
1923
  console.log(`${label}[${projName} \u2192 ${sessionId}]`);
1926
1924
  found = true;
@@ -1972,7 +1970,7 @@ function sessionCommand() {
1972
1970
  console.log(fmt("---", "----------", "-------", "---", ""));
1973
1971
  for (const file of files) {
1974
1972
  try {
1975
- const data = JSON.parse(fs7.readFileSync(path7.join(SESSIONS_DIR, file), "utf-8"));
1973
+ const data = JSON.parse(fs7.readFileSync(path8.join(SESSIONS_DIR, file), "utf-8"));
1976
1974
  const pid = String(data.pid || "?");
1977
1975
  const sessionId = data.sessionId || "?";
1978
1976
  const cwd = data.cwd || "?";
@@ -2000,7 +1998,7 @@ function sessionCommand() {
2000
1998
  const results = [];
2001
1999
  try {
2002
2000
  for (const entry of fs7.readdirSync(dir, { withFileTypes: true })) {
2003
- const fullPath = path7.join(dir, entry.name);
2001
+ const fullPath = path8.join(dir, entry.name);
2004
2002
  if (entry.isDirectory()) results.push(...walk(fullPath));
2005
2003
  else if (entry.name.endsWith(".jsonl")) results.push(fullPath);
2006
2004
  }
@@ -2077,7 +2075,7 @@ function sessionCommand() {
2077
2075
  return;
2078
2076
  }
2079
2077
  for (const entry of entries) {
2080
- const fullPath = path7.join(dir, entry.name);
2078
+ const fullPath = path8.join(dir, entry.name);
2081
2079
  if (entry.isDirectory()) {
2082
2080
  walk(fullPath);
2083
2081
  } else if (entry.name.endsWith(".jsonl")) {
@@ -2183,6 +2181,7 @@ _cc-hub() {
2183
2181
  'rename:Rename a profile'
2184
2182
  'default:Set the default profile'
2185
2183
  'sync:Synchronize all CLI profiles to the desktop app'
2184
+ 'export:Export a profile to a settings file'
2186
2185
  )
2187
2186
 
2188
2187
  local -a hooks_subcmds
@@ -2237,7 +2236,7 @@ _cc-hub() {
2237
2236
  profile)
2238
2237
  if (( CURRENT == 2 )); then
2239
2238
  _describe -t profile-subcmds 'profile subcommand' profile_subcmds
2240
- elif [[ $words[2] == "view" || $words[2] == "remove" ]]; then
2239
+ elif [[ $words[2] == "view" || $words[2] == "remove" || $words[2] == "export" ]]; then
2241
2240
  _cc_hub_profiles
2242
2241
  elif [[ $words[2] == "default" ]]; then
2243
2242
  _arguments -C -S '--built-in[Use official Anthropic models as default]' '*:profile:_cc_hub_profiles'
@@ -2346,7 +2345,7 @@ _cc-hub() {
2346
2345
  prev="\${COMP_WORDS[COMP_CWORD-1]}"
2347
2346
  commands="profile use run hook session provider cache completion help"
2348
2347
 
2349
- local profile_subcmds="add update list view remove rename default sync"
2348
+ local profile_subcmds="add update list view remove rename default sync export"
2350
2349
  local provider_subcmds="list"
2351
2350
  local provider_types="anthropic openai"
2352
2351
  local hooks_subcmds="list add remove enable disable"
@@ -2365,7 +2364,7 @@ _cc-hub() {
2365
2364
  profile)
2366
2365
  if [[ \${COMP_CWORD} -eq 2 ]]; then
2367
2366
  COMPREPLY=($(compgen -W "$profile_subcmds" -- "$cur"))
2368
- elif [[ "$prev" == "view" || "$prev" == "remove" ]]; then
2367
+ elif [[ "$prev" == "view" || "$prev" == "remove" || "$prev" == "export" ]]; then
2369
2368
  _cc-hub_profiles
2370
2369
  elif [[ "$prev" == "default" ]]; then
2371
2370
  COMPREPLY=($(compgen -W "--built-in $(_cc-hub_profile_names)" -- "$cur"))
@@ -2446,7 +2445,7 @@ var POWERSHELL_COMPLETION = `Register-ArgumentCompleter -Native -CommandName cc-
2446
2445
  'help:Display help for a command'
2447
2446
  )
2448
2447
 
2449
- $profileSubcmds = @('add', 'update', 'list', 'view', 'remove', 'rename', 'default', 'sync')
2448
+ $profileSubcmds = @('add', 'update', 'list', 'view', 'remove', 'rename', 'default', 'sync', 'export')
2450
2449
  $hookSubcmds = @('list', 'add', 'remove', 'enable', 'disable')
2451
2450
  $sessionSubcmds = @('list', 'show', 'search', 'ps', 'stats', 'clean', 'troubleshoot')
2452
2451
  $cacheSubcmds = @('restore')
@@ -2529,10 +2528,58 @@ function completionCommand() {
2529
2528
  }));
2530
2529
  }
2531
2530
 
2532
- // src/cache/commands.ts
2531
+ // src/proxy/commands.ts
2533
2532
  import { Command as Command6 } from "commander";
2533
+ function collect2(value, previous) {
2534
+ return previous.concat([value]);
2535
+ }
2536
+ function proxyCommand() {
2537
+ return new Command6("proxy").description("Start a standalone OpenAI proxy for the desktop app").option("--profile <name>", "Use configuration from a saved profile").option("-u, --url <url>", "Upstream base URL (e.g., https://api.openai.com)").option("-k, --api-key <key>", "Upstream API key").option("-m, --model <model>", "Default model", "gpt-4o").option("--mapping <mapping>", "Model alias mapping (format: alias:actual, can be used multiple times)", collect2, []).action(safeAction(async (opts) => {
2538
+ let targetUrl = opts.url || "https://api.openai.com";
2539
+ let apiKey = opts.apiKey || "";
2540
+ let defaultModel = opts.model || "gpt-4o";
2541
+ let models = [];
2542
+ const modelMappings = {};
2543
+ if (opts.profile) {
2544
+ ensureProfilesFile();
2545
+ const data = readJson(PROFILES_FILE);
2546
+ const p = data.profiles[opts.profile];
2547
+ if (!p) {
2548
+ throw new Error(`Profile '${opts.profile}' not found.`);
2549
+ }
2550
+ targetUrl = p.url || targetUrl;
2551
+ apiKey = p.token || apiKey;
2552
+ models = p.models || (p.model ? [p.model] : []);
2553
+ defaultModel = models[0] || defaultModel;
2554
+ models.forEach((m, i) => {
2555
+ if (!isAnthropicModel(m)) {
2556
+ const alias = ANTHROPIC_ALIASES[Math.min(i, ANTHROPIC_ALIASES.length - 1)];
2557
+ modelMappings[alias] = m;
2558
+ }
2559
+ });
2560
+ } else {
2561
+ for (const m of opts.mapping) {
2562
+ const [alias, actual] = m.split(":");
2563
+ if (alias && actual) {
2564
+ modelMappings[alias] = actual;
2565
+ }
2566
+ }
2567
+ models = [defaultModel];
2568
+ }
2569
+ const { baseUrl, stop } = await startOpenAIProxy(targetUrl, apiKey, defaultModel, models, modelMappings);
2570
+ console.log(`Proxy running at ${baseUrl}`);
2571
+ console.log("Press Ctrl+C to stop");
2572
+ process.on("SIGINT", () => {
2573
+ stop();
2574
+ process.exit(0);
2575
+ });
2576
+ }));
2577
+ }
2578
+
2579
+ // src/cache/commands.ts
2580
+ import { Command as Command7 } from "commander";
2534
2581
  import fs8 from "fs";
2535
- import path8 from "path";
2582
+ import path9 from "path";
2536
2583
  import { spawnSync as spawnSync4 } from "child_process";
2537
2584
  import { createInterface } from "readline/promises";
2538
2585
  async function confirmPrompt(message) {
@@ -2605,9 +2652,9 @@ function killProcesses(pids) {
2605
2652
  }
2606
2653
  }
2607
2654
  function cacheCommand() {
2608
- const cache = new Command6("cache").description("Manage Claude Code cache and backup files");
2655
+ const cache = new Command7("cache").description("Manage Claude Code cache and backup files");
2609
2656
  cache.command("restore").description("Restore ~/.claude/.claude.json.backup to ~/.claude.json").action(safeAction(async () => {
2610
- const backupPath = path8.join(CLAUDE_DIR, ".claude.json.backup");
2657
+ const backupPath = path9.join(CLAUDE_DIR, ".claude.json.backup");
2611
2658
  const targetPath = CLAUDE_JSON;
2612
2659
  if (!fs8.existsSync(backupPath)) {
2613
2660
  throw new Error(`Backup not found: ${backupPath}`);
@@ -2638,7 +2685,7 @@ ensureSettingsFile();
2638
2685
  var settings = readJson(SETTINGS_FILE);
2639
2686
  setLogLevel(settings._cc_hub_logLevel || "INFO");
2640
2687
  installGlobalExceptionHandlers();
2641
- var program = new Command7();
2688
+ var program = new Command8();
2642
2689
  program.name("cc-hub").description("Manage Claude CLI profiles, hooks, and sessions").version(version);
2643
2690
  program.addCommand(profileCommand());
2644
2691
  program.addCommand(useCommand());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-hub-cli",
3
- "version": "1.1.12",
3
+ "version": "1.1.14",
4
4
  "description": "Manage Claude CLI profiles, hooks, and sessions",
5
5
  "type": "module",
6
6
  "bin": {