cc-hub-cli 1.1.11 → 1.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +393 -403
- 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
|
|
4
|
+
import { Command as Command8 } from "commander";
|
|
5
5
|
import { createRequire } from "module";
|
|
6
6
|
|
|
7
7
|
// src/profiles/commands.ts
|
|
@@ -254,303 +254,10 @@ var NoOpDesktopApp = class {
|
|
|
254
254
|
import fs3 from "fs";
|
|
255
255
|
import path3 from "path";
|
|
256
256
|
import { randomUUID } from "crypto";
|
|
257
|
-
var ANTHROPIC_ALIASES = ["claude-sonnet-4-5", "claude-opus-4-7", "claude-haiku-4-5-20251001"];
|
|
258
|
-
function isAnthropicModel(model) {
|
|
259
|
-
const anthropicAliases = ["opus", "sonnet", "haiku", "best", "default", "opusplan", "opus[1m]", "sonnet[1m]"];
|
|
260
|
-
const lower = model.toLowerCase();
|
|
261
|
-
if (anthropicAliases.includes(lower)) return true;
|
|
262
|
-
if (lower.startsWith("claude-")) return true;
|
|
263
|
-
return false;
|
|
264
|
-
}
|
|
265
|
-
function toDesktopProfile(p) {
|
|
266
|
-
const models = p.models || (p.model ? [p.model] : []);
|
|
267
|
-
const isAnthropic = p.provider === "anthropic" || !p.provider && !p.url;
|
|
268
|
-
if (isAnthropic && !p.url) {
|
|
269
|
-
return {
|
|
270
|
-
inferenceProvider: "1p",
|
|
271
|
-
inferenceModels: models.map((m) => ({ name: m, supports1m: true }))
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
const mappings = [];
|
|
275
|
-
const mappedModels = models.map((m, index) => {
|
|
276
|
-
if (isAnthropicModel(m)) return m;
|
|
277
|
-
const alias = ANTHROPIC_ALIASES[Math.min(index, ANTHROPIC_ALIASES.length - 1)];
|
|
278
|
-
mappings.push({ alias, actual: m });
|
|
279
|
-
return alias;
|
|
280
|
-
});
|
|
281
|
-
const result = {
|
|
282
|
-
inferenceProvider: "gateway",
|
|
283
|
-
inferenceGatewayBaseUrl: p.url || void 0,
|
|
284
|
-
inferenceGatewayApiKey: p.token || void 0,
|
|
285
|
-
inferenceGatewayAuthScheme: "bearer",
|
|
286
|
-
inferenceModels: mappedModels.map((m) => ({ name: m, supports1m: true }))
|
|
287
|
-
};
|
|
288
|
-
if (mappings.length > 0) {
|
|
289
|
-
result.inferenceModelMappings = mappings;
|
|
290
|
-
}
|
|
291
|
-
return result;
|
|
292
|
-
}
|
|
293
|
-
var DesktopProfileSyncer = class {
|
|
294
|
-
constructor(app) {
|
|
295
|
-
this.app = app;
|
|
296
|
-
}
|
|
297
|
-
app;
|
|
298
|
-
isSupported() {
|
|
299
|
-
return this.app.isInstalled();
|
|
300
|
-
}
|
|
301
|
-
sync(name, p) {
|
|
302
|
-
const configLib = this.app.getConfigLibrary();
|
|
303
|
-
debug(`profile-syncer: sync '${name}' to ${configLib || "(none)"}`);
|
|
304
|
-
if (!configLib) return;
|
|
305
|
-
if (!fs3.existsSync(configLib)) {
|
|
306
|
-
fs3.mkdirSync(configLib, { recursive: true });
|
|
307
|
-
}
|
|
308
|
-
const meta = this.readMeta();
|
|
309
|
-
const entries = meta.entries || [];
|
|
310
|
-
let id = p.desktopId;
|
|
311
|
-
if (!id) {
|
|
312
|
-
const existingByName = entries.find((e) => e.name === name);
|
|
313
|
-
if (existingByName) {
|
|
314
|
-
id = existingByName.id;
|
|
315
|
-
} else {
|
|
316
|
-
id = randomUUID();
|
|
317
|
-
}
|
|
318
|
-
p.desktopId = id;
|
|
319
|
-
}
|
|
320
|
-
const existingIndex = entries.findIndex((e) => e.id === id);
|
|
321
|
-
if (existingIndex !== -1) {
|
|
322
|
-
entries[existingIndex].name = name;
|
|
323
|
-
} else {
|
|
324
|
-
entries.push({ id, name });
|
|
325
|
-
}
|
|
326
|
-
meta.entries = entries;
|
|
327
|
-
this.writeMeta(meta);
|
|
328
|
-
this.writeProfile(id, configLib, toDesktopProfile(p));
|
|
329
|
-
debug(`profile-syncer: synced '${name}' id=${id}`);
|
|
330
|
-
}
|
|
331
|
-
remove(name, p) {
|
|
332
|
-
const configLib = this.app.getConfigLibrary();
|
|
333
|
-
debug(`profile-syncer: remove '${name}' id=${p.desktopId || "(none)"} from ${configLib || "(none)"}`);
|
|
334
|
-
if (!configLib || !p.desktopId) return;
|
|
335
|
-
const meta = this.readMeta();
|
|
336
|
-
if (meta.entries) {
|
|
337
|
-
meta.entries = meta.entries.filter((e) => e.id !== p.desktopId);
|
|
338
|
-
}
|
|
339
|
-
if (meta.appliedId === p.desktopId) {
|
|
340
|
-
delete meta.appliedId;
|
|
341
|
-
}
|
|
342
|
-
this.writeMeta(meta);
|
|
343
|
-
const filePath = path3.join(configLib, `${p.desktopId}.json`);
|
|
344
|
-
if (fs3.existsSync(filePath)) {
|
|
345
|
-
fs3.unlinkSync(filePath);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
setActive(p) {
|
|
349
|
-
const configLib = this.app.getConfigLibrary();
|
|
350
|
-
debug(`profile-syncer: setActive id=${p.desktopId || "(none)"} in ${configLib || "(none)"}`);
|
|
351
|
-
if (!configLib || !p.desktopId) return;
|
|
352
|
-
const meta = this.readMeta();
|
|
353
|
-
meta.appliedId = p.desktopId;
|
|
354
|
-
const entries = meta.entries || [];
|
|
355
|
-
if (!entries.some((e) => e.id === p.desktopId)) {
|
|
356
|
-
entries.push({ id: p.desktopId, name: "unknown" });
|
|
357
|
-
meta.entries = entries;
|
|
358
|
-
}
|
|
359
|
-
this.writeMeta(meta);
|
|
360
|
-
}
|
|
361
|
-
metaFile() {
|
|
362
|
-
const configLib = this.app.getConfigLibrary();
|
|
363
|
-
return configLib ? path3.join(configLib, "_meta.json") : void 0;
|
|
364
|
-
}
|
|
365
|
-
readMeta() {
|
|
366
|
-
const file = this.metaFile();
|
|
367
|
-
if (!file || !fs3.existsSync(file)) return {};
|
|
368
|
-
try {
|
|
369
|
-
return readJson(file);
|
|
370
|
-
} catch {
|
|
371
|
-
return {};
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
writeMeta(meta) {
|
|
375
|
-
const file = this.metaFile();
|
|
376
|
-
if (file) writeJson(file, meta);
|
|
377
|
-
}
|
|
378
|
-
writeProfile(id, configLib, data) {
|
|
379
|
-
writeJson(path3.join(configLib, `${id}.json`), data);
|
|
380
|
-
}
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
// src/platform/binary-resolver.ts
|
|
384
|
-
import { spawnSync } from "child_process";
|
|
385
|
-
var SystemBinaryResolver = class {
|
|
386
|
-
constructor(app) {
|
|
387
|
-
this.app = app;
|
|
388
|
-
}
|
|
389
|
-
app;
|
|
390
|
-
resolve() {
|
|
391
|
-
debug("binary-resolver: trying global 'claude' command");
|
|
392
|
-
try {
|
|
393
|
-
const result = spawnSync("claude", ["--version"], {
|
|
394
|
-
shell: process.platform === "win32",
|
|
395
|
-
encoding: "utf-8"
|
|
396
|
-
});
|
|
397
|
-
if (result.status === 0) {
|
|
398
|
-
debug("binary-resolver: found global 'claude'");
|
|
399
|
-
return "claude";
|
|
400
|
-
}
|
|
401
|
-
} catch {
|
|
402
|
-
}
|
|
403
|
-
debug("binary-resolver: trying desktop app binary");
|
|
404
|
-
const desktopBinary = this.app.findBinary();
|
|
405
|
-
if (desktopBinary) {
|
|
406
|
-
debug(`binary-resolver: found desktop binary at ${desktopBinary}`);
|
|
407
|
-
return desktopBinary;
|
|
408
|
-
}
|
|
409
|
-
throw new Error("Could not find Claude Code CLI. Install it globally or install the Claude Code desktop app.");
|
|
410
|
-
}
|
|
411
|
-
};
|
|
412
|
-
|
|
413
|
-
// src/platform/path-codec.ts
|
|
414
|
-
var UnixPathCodec = class {
|
|
415
|
-
encode(p) {
|
|
416
|
-
return p.replace(/[\\/]/g, "-").replace(/\./g, "-").replace(/:/g, "");
|
|
417
|
-
}
|
|
418
|
-
decode(encoded) {
|
|
419
|
-
return encoded.replace(/--/g, "/.").replace(/-/g, "/");
|
|
420
|
-
}
|
|
421
|
-
};
|
|
422
|
-
var WindowsPathCodec = class {
|
|
423
|
-
encode(p) {
|
|
424
|
-
return p.replace(/[\\/]/g, "-").replace(/\./g, "-").replace(/:/g, "");
|
|
425
|
-
}
|
|
426
|
-
decode(encoded) {
|
|
427
|
-
const decoded = encoded.replace(/--/g, "\\.").replace(/-/g, "\\");
|
|
428
|
-
if (/^[A-Za-z]\\/.test(decoded)) {
|
|
429
|
-
return decoded[0] + ":" + decoded.slice(1);
|
|
430
|
-
}
|
|
431
|
-
return decoded;
|
|
432
|
-
}
|
|
433
|
-
};
|
|
434
|
-
|
|
435
|
-
// src/platform/index.ts
|
|
436
|
-
function createDesktopApp() {
|
|
437
|
-
if (process.platform === "darwin") return new MacOSDesktopApp();
|
|
438
|
-
if (process.platform === "win32") return new WindowsDesktopApp();
|
|
439
|
-
return new NoOpDesktopApp();
|
|
440
|
-
}
|
|
441
|
-
function createProfileSyncer() {
|
|
442
|
-
return new DesktopProfileSyncer(createDesktopApp());
|
|
443
|
-
}
|
|
444
|
-
function createBinaryResolver() {
|
|
445
|
-
return new SystemBinaryResolver(createDesktopApp());
|
|
446
|
-
}
|
|
447
|
-
function createPathCodec() {
|
|
448
|
-
if (process.platform === "win32") return new WindowsPathCodec();
|
|
449
|
-
return new UnixPathCodec();
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// src/config.ts
|
|
453
|
-
var CLAUDE_DIR = process.env.CLAUDE_DIR || path4.join(os3.homedir(), ".claude");
|
|
454
|
-
var PROFILES_FILE = process.env.CLAUDE_PROFILES_FILE || path4.join(CLAUDE_DIR, "profiles.json");
|
|
455
|
-
var SETTINGS_FILE = process.env.CLAUDE_SETTINGS_FILE || path4.join(CLAUDE_DIR, "settings.json");
|
|
456
|
-
var CLAUDE_JSON = path4.join(os3.homedir(), ".claude.json");
|
|
457
|
-
var PROJECTS_DIR = path4.join(CLAUDE_DIR, "projects");
|
|
458
|
-
var SESSIONS_DIR = path4.join(CLAUDE_DIR, "sessions");
|
|
459
|
-
var desktopApp = createDesktopApp();
|
|
460
|
-
var DESKTOP_CONFIG_LIBRARY = desktopApp.getConfigLibrary() || "";
|
|
461
|
-
var DESKTOP_META_FILE = DESKTOP_CONFIG_LIBRARY ? path4.join(DESKTOP_CONFIG_LIBRARY, "_meta.json") : "";
|
|
462
|
-
var DESKTOP_SESSIONS_DIR = desktopApp.getSessionsDir() || "";
|
|
463
|
-
function isDesktopAppInstalled() {
|
|
464
|
-
return desktopApp.isInstalled();
|
|
465
|
-
}
|
|
466
|
-
function ensureFile(filePath, defaultContent) {
|
|
467
|
-
if (!fs4.existsSync(filePath)) {
|
|
468
|
-
debug(`ensureFile: creating ${filePath}`);
|
|
469
|
-
fs4.mkdirSync(path4.dirname(filePath), { recursive: true });
|
|
470
|
-
fs4.writeFileSync(filePath, defaultContent, "utf-8");
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
function readJson(filePath) {
|
|
474
|
-
debug(`readJson: ${filePath}`);
|
|
475
|
-
return JSON.parse(fs4.readFileSync(filePath, "utf-8"));
|
|
476
|
-
}
|
|
477
|
-
function writeJson(filePath, data) {
|
|
478
|
-
debug(`writeJson: ${filePath}`);
|
|
479
|
-
fs4.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
480
|
-
}
|
|
481
|
-
function ensureProfilesFile() {
|
|
482
|
-
ensureFile(PROFILES_FILE, '{"profiles":{}}\n');
|
|
483
|
-
}
|
|
484
|
-
function ensureSettingsFile() {
|
|
485
|
-
ensureFile(SETTINGS_FILE, "{}\n");
|
|
486
|
-
}
|
|
487
|
-
function fixJsonFile(filePath, fallback = {}) {
|
|
488
|
-
if (!fs4.existsSync(filePath)) return;
|
|
489
|
-
const backupPath = path4.join(CLAUDE_DIR, path4.basename(filePath) + ".backup");
|
|
490
|
-
const raw = fs4.readFileSync(filePath, "utf-8");
|
|
491
|
-
try {
|
|
492
|
-
JSON.parse(raw);
|
|
493
|
-
fs4.mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
494
|
-
fs4.copyFileSync(filePath, backupPath);
|
|
495
|
-
return;
|
|
496
|
-
} catch {
|
|
497
|
-
}
|
|
498
|
-
let text = raw.trim();
|
|
499
|
-
if (text.charCodeAt(0) === 65279) {
|
|
500
|
-
text = text.slice(1).trim();
|
|
501
|
-
}
|
|
502
|
-
text = text.replace(/,\s*$/, "");
|
|
503
|
-
text = text.replace(/,\s*([}\]])/g, "$1");
|
|
504
|
-
const lastBrace = Math.max(text.lastIndexOf("}"), text.lastIndexOf("]"));
|
|
505
|
-
if (lastBrace !== -1 && lastBrace < text.length - 1) {
|
|
506
|
-
text = text.slice(0, lastBrace + 1);
|
|
507
|
-
}
|
|
508
|
-
let openCurly = 0, openSquare = 0;
|
|
509
|
-
for (const ch of text) {
|
|
510
|
-
if (ch === "{") openCurly++;
|
|
511
|
-
else if (ch === "}") openCurly--;
|
|
512
|
-
else if (ch === "[") openSquare++;
|
|
513
|
-
else if (ch === "]") openSquare--;
|
|
514
|
-
}
|
|
515
|
-
if (openSquare > 0) text += "]".repeat(openSquare);
|
|
516
|
-
if (openCurly > 0) text += "}".repeat(openCurly);
|
|
517
|
-
try {
|
|
518
|
-
JSON.parse(text);
|
|
519
|
-
fs4.writeFileSync(filePath, text + "\n", "utf-8");
|
|
520
|
-
warn(`Fixed invalid JSON in ${path4.basename(filePath)}.`);
|
|
521
|
-
console.error(`Fixed invalid JSON in ${path4.basename(filePath)}.`);
|
|
522
|
-
} catch {
|
|
523
|
-
let restored = false;
|
|
524
|
-
if (fs4.existsSync(backupPath)) {
|
|
525
|
-
try {
|
|
526
|
-
const backupRaw = fs4.readFileSync(backupPath, "utf-8");
|
|
527
|
-
JSON.parse(backupRaw);
|
|
528
|
-
fs4.copyFileSync(backupPath, filePath);
|
|
529
|
-
restored = true;
|
|
530
|
-
warn(`Restored ${path4.basename(filePath)} from backup.`);
|
|
531
|
-
console.error(`Restored ${path4.basename(filePath)} from backup.`);
|
|
532
|
-
} catch {
|
|
533
|
-
error(`Backup ${path4.basename(backupPath)} is also corrupt; using fallback.`);
|
|
534
|
-
console.error(`Backup ${path4.basename(backupPath)} is also corrupt; using fallback.`);
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
if (!restored) {
|
|
538
|
-
writeJson(filePath, fallback);
|
|
539
|
-
error(`Could not fix ${path4.basename(filePath)}, no valid backup found, reset to default.`);
|
|
540
|
-
console.error(`Could not fix ${path4.basename(filePath)}, no valid backup found, reset to default.`);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// src/profiles/runner.ts
|
|
546
|
-
import { spawnSync as spawnSync2, spawn } from "child_process";
|
|
547
257
|
|
|
548
258
|
// src/provider/index.ts
|
|
549
259
|
import { Command } from "commander";
|
|
550
260
|
|
|
551
|
-
// src/provider/server.ts
|
|
552
|
-
import http from "http";
|
|
553
|
-
|
|
554
261
|
// src/provider/transform.ts
|
|
555
262
|
function sanitizeToolId(id) {
|
|
556
263
|
let sanitized = id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
@@ -776,6 +483,7 @@ data: ${JSON.stringify(data)}
|
|
|
776
483
|
}
|
|
777
484
|
|
|
778
485
|
// src/provider/server.ts
|
|
486
|
+
import http from "http";
|
|
779
487
|
async function startOpenAIProxy(targetUrl, apiKey, model, models = [], modelMappings = {}) {
|
|
780
488
|
const base = targetUrl.replace(/\/+$/, "");
|
|
781
489
|
const server = http.createServer(async (req, res) => {
|
|
@@ -872,110 +580,351 @@ data: ${errText}
|
|
|
872
580
|
res.end(JSON.stringify({ error: { type: "internal_error", message: String(err) } }));
|
|
873
581
|
}
|
|
874
582
|
}
|
|
875
|
-
});
|
|
876
|
-
return new Promise((resolve, reject) => {
|
|
877
|
-
server.listen(0, "127.0.0.1", () => {
|
|
878
|
-
const addr = server.address();
|
|
879
|
-
const baseUrl = `http://127.0.0.1:${addr.port}`;
|
|
880
|
-
debug(`OpenAI proxy listening on ${baseUrl}`);
|
|
881
|
-
resolve({
|
|
882
|
-
baseUrl,
|
|
883
|
-
stop: () => {
|
|
884
|
-
debug("OpenAI proxy stopped");
|
|
885
|
-
server.close();
|
|
886
|
-
}
|
|
887
|
-
});
|
|
888
|
-
});
|
|
889
|
-
server.on("error", reject);
|
|
890
|
-
});
|
|
583
|
+
});
|
|
584
|
+
return new Promise((resolve, reject) => {
|
|
585
|
+
server.listen(0, "127.0.0.1", () => {
|
|
586
|
+
const addr = server.address();
|
|
587
|
+
const baseUrl = `http://127.0.0.1:${addr.port}`;
|
|
588
|
+
debug(`OpenAI proxy listening on ${baseUrl}`);
|
|
589
|
+
resolve({
|
|
590
|
+
baseUrl,
|
|
591
|
+
stop: () => {
|
|
592
|
+
debug("OpenAI proxy stopped");
|
|
593
|
+
server.close();
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
});
|
|
597
|
+
server.on("error", reject);
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
function readBody(req) {
|
|
601
|
+
return new Promise((resolve, reject) => {
|
|
602
|
+
const chunks = [];
|
|
603
|
+
req.on("data", (c) => chunks.push(c));
|
|
604
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
605
|
+
req.on("error", reject);
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// src/provider/index.ts
|
|
610
|
+
var PROVIDERS = [
|
|
611
|
+
{
|
|
612
|
+
name: "anthropic",
|
|
613
|
+
description: "Default \u2014 sends Anthropic-format requests directly to the configured URL"
|
|
614
|
+
},
|
|
615
|
+
{
|
|
616
|
+
name: "openai",
|
|
617
|
+
description: "Embedded proxy \u2014 translates Anthropic requests to OpenAI Chat Completions format"
|
|
618
|
+
}
|
|
619
|
+
];
|
|
620
|
+
var ANTHROPIC_ALIASES = ["claude-sonnet-4-6", "claude-opus-4-7", "claude-haiku-4-5"];
|
|
621
|
+
function isAnthropicModel(model) {
|
|
622
|
+
const anthropicAliases = ["opus", "sonnet", "haiku", "best", "default", "opusplan", "opus[1m]", "sonnet[1m]"];
|
|
623
|
+
const lower = model.toLowerCase();
|
|
624
|
+
if (anthropicAliases.includes(lower)) return true;
|
|
625
|
+
if (lower.startsWith("claude-")) return true;
|
|
626
|
+
for (let index = 0; index < anthropicAliases.length; index++) {
|
|
627
|
+
const element = anthropicAliases[index];
|
|
628
|
+
if (lower.includes(element)) return true;
|
|
629
|
+
}
|
|
630
|
+
return false;
|
|
631
|
+
}
|
|
632
|
+
function providerCommand() {
|
|
633
|
+
const cmd = new Command("provider").description("Manage provider types");
|
|
634
|
+
cmd.command("list").description("List available provider types").action(safeAction(() => {
|
|
635
|
+
const fmt = (name, desc) => `${name.padEnd(12)} ${desc}`;
|
|
636
|
+
console.log(fmt("NAME", "DESCRIPTION"));
|
|
637
|
+
console.log(fmt("----", "-----------"));
|
|
638
|
+
for (const p of PROVIDERS) {
|
|
639
|
+
console.log(fmt(p.name, p.description));
|
|
640
|
+
}
|
|
641
|
+
}));
|
|
642
|
+
return cmd;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// src/platform/profile-syncer.ts
|
|
646
|
+
function toDesktopProfile(p) {
|
|
647
|
+
const models = p.models || (p.model ? [p.model] : []);
|
|
648
|
+
const isAnthropic = p.provider === "anthropic" || !p.provider && !p.url;
|
|
649
|
+
if (isAnthropic && !p.url) {
|
|
650
|
+
return {
|
|
651
|
+
inferenceProvider: "1p",
|
|
652
|
+
inferenceModels: models.map((m) => ({ name: m, supports1m: true }))
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
const mappings = [];
|
|
656
|
+
const mappedModels = models.map((m, index) => {
|
|
657
|
+
if (isAnthropicModel(m)) return m;
|
|
658
|
+
const alias = ANTHROPIC_ALIASES[Math.min(index, ANTHROPIC_ALIASES.length - 1)];
|
|
659
|
+
mappings.push({ alias, actual: m });
|
|
660
|
+
return alias;
|
|
661
|
+
});
|
|
662
|
+
const result = {
|
|
663
|
+
inferenceProvider: "gateway",
|
|
664
|
+
inferenceGatewayBaseUrl: p.url || void 0,
|
|
665
|
+
inferenceGatewayApiKey: p.token || void 0,
|
|
666
|
+
inferenceGatewayAuthScheme: "bearer",
|
|
667
|
+
inferenceModels: mappedModels.map((m) => ({ name: m, supports1m: true }))
|
|
668
|
+
};
|
|
669
|
+
if (mappings.length > 0) {
|
|
670
|
+
result.inferenceModelMappings = mappings;
|
|
671
|
+
}
|
|
672
|
+
return result;
|
|
673
|
+
}
|
|
674
|
+
var DesktopProfileSyncer = class {
|
|
675
|
+
constructor(app) {
|
|
676
|
+
this.app = app;
|
|
677
|
+
}
|
|
678
|
+
app;
|
|
679
|
+
isSupported() {
|
|
680
|
+
return this.app.isInstalled();
|
|
681
|
+
}
|
|
682
|
+
sync(name, p) {
|
|
683
|
+
const configLib = this.app.getConfigLibrary();
|
|
684
|
+
debug(`profile-syncer: sync '${name}' to ${configLib || "(none)"}`);
|
|
685
|
+
if (!configLib) return;
|
|
686
|
+
if (!fs3.existsSync(configLib)) {
|
|
687
|
+
fs3.mkdirSync(configLib, { recursive: true });
|
|
688
|
+
}
|
|
689
|
+
const meta = this.readMeta();
|
|
690
|
+
const entries = meta.entries || [];
|
|
691
|
+
let id = p.desktopId;
|
|
692
|
+
if (!id) {
|
|
693
|
+
const existingByName = entries.find((e) => e.name === name);
|
|
694
|
+
if (existingByName) {
|
|
695
|
+
id = existingByName.id;
|
|
696
|
+
} else {
|
|
697
|
+
id = randomUUID();
|
|
698
|
+
}
|
|
699
|
+
p.desktopId = id;
|
|
700
|
+
}
|
|
701
|
+
const existingIndex = entries.findIndex((e) => e.id === id);
|
|
702
|
+
if (existingIndex !== -1) {
|
|
703
|
+
entries[existingIndex].name = name;
|
|
704
|
+
} else {
|
|
705
|
+
entries.push({ id, name });
|
|
706
|
+
}
|
|
707
|
+
meta.entries = entries;
|
|
708
|
+
this.writeMeta(meta);
|
|
709
|
+
this.writeProfile(id, configLib, toDesktopProfile(p));
|
|
710
|
+
debug(`profile-syncer: synced '${name}' id=${id}`);
|
|
711
|
+
}
|
|
712
|
+
remove(name, p) {
|
|
713
|
+
const configLib = this.app.getConfigLibrary();
|
|
714
|
+
debug(`profile-syncer: remove '${name}' id=${p.desktopId || "(none)"} from ${configLib || "(none)"}`);
|
|
715
|
+
if (!configLib || !p.desktopId) return;
|
|
716
|
+
const meta = this.readMeta();
|
|
717
|
+
if (meta.entries) {
|
|
718
|
+
meta.entries = meta.entries.filter((e) => e.id !== p.desktopId);
|
|
719
|
+
}
|
|
720
|
+
if (meta.appliedId === p.desktopId) {
|
|
721
|
+
delete meta.appliedId;
|
|
722
|
+
}
|
|
723
|
+
this.writeMeta(meta);
|
|
724
|
+
const filePath = path3.join(configLib, `${p.desktopId}.json`);
|
|
725
|
+
if (fs3.existsSync(filePath)) {
|
|
726
|
+
fs3.unlinkSync(filePath);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
setActive(p) {
|
|
730
|
+
const configLib = this.app.getConfigLibrary();
|
|
731
|
+
debug(`profile-syncer: setActive id=${p.desktopId || "(none)"} in ${configLib || "(none)"}`);
|
|
732
|
+
if (!configLib || !p.desktopId) return;
|
|
733
|
+
const meta = this.readMeta();
|
|
734
|
+
meta.appliedId = p.desktopId;
|
|
735
|
+
const entries = meta.entries || [];
|
|
736
|
+
if (!entries.some((e) => e.id === p.desktopId)) {
|
|
737
|
+
entries.push({ id: p.desktopId, name: "unknown" });
|
|
738
|
+
meta.entries = entries;
|
|
739
|
+
}
|
|
740
|
+
this.writeMeta(meta);
|
|
741
|
+
}
|
|
742
|
+
metaFile() {
|
|
743
|
+
const configLib = this.app.getConfigLibrary();
|
|
744
|
+
return configLib ? path3.join(configLib, "_meta.json") : void 0;
|
|
745
|
+
}
|
|
746
|
+
readMeta() {
|
|
747
|
+
const file = this.metaFile();
|
|
748
|
+
if (!file || !fs3.existsSync(file)) return {};
|
|
749
|
+
try {
|
|
750
|
+
return readJson(file);
|
|
751
|
+
} catch {
|
|
752
|
+
return {};
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
writeMeta(meta) {
|
|
756
|
+
const file = this.metaFile();
|
|
757
|
+
if (file) writeJson(file, meta);
|
|
758
|
+
}
|
|
759
|
+
writeProfile(id, configLib, data) {
|
|
760
|
+
writeJson(path3.join(configLib, `${id}.json`), data);
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
// src/platform/binary-resolver.ts
|
|
765
|
+
import { spawnSync } from "child_process";
|
|
766
|
+
var SystemBinaryResolver = class {
|
|
767
|
+
constructor(app) {
|
|
768
|
+
this.app = app;
|
|
769
|
+
}
|
|
770
|
+
app;
|
|
771
|
+
resolve() {
|
|
772
|
+
debug("binary-resolver: trying global 'claude' command");
|
|
773
|
+
try {
|
|
774
|
+
const result = spawnSync("claude", ["--version"], {
|
|
775
|
+
shell: process.platform === "win32",
|
|
776
|
+
encoding: "utf-8"
|
|
777
|
+
});
|
|
778
|
+
if (result.status === 0) {
|
|
779
|
+
debug("binary-resolver: found global 'claude'");
|
|
780
|
+
return "claude";
|
|
781
|
+
}
|
|
782
|
+
} catch {
|
|
783
|
+
}
|
|
784
|
+
debug("binary-resolver: trying desktop app binary");
|
|
785
|
+
const desktopBinary = this.app.findBinary();
|
|
786
|
+
if (desktopBinary) {
|
|
787
|
+
debug(`binary-resolver: found desktop binary at ${desktopBinary}`);
|
|
788
|
+
return desktopBinary;
|
|
789
|
+
}
|
|
790
|
+
throw new Error("Could not find Claude Code CLI. Install it globally or install the Claude Code desktop app.");
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
// src/platform/path-codec.ts
|
|
795
|
+
var UnixPathCodec = class {
|
|
796
|
+
encode(p) {
|
|
797
|
+
return p.replace(/[\\/]/g, "-").replace(/\./g, "-").replace(/:/g, "");
|
|
798
|
+
}
|
|
799
|
+
decode(encoded) {
|
|
800
|
+
return encoded.replace(/--/g, "/.").replace(/-/g, "/");
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
var WindowsPathCodec = class {
|
|
804
|
+
encode(p) {
|
|
805
|
+
return p.replace(/[\\/]/g, "-").replace(/\./g, "-").replace(/:/g, "");
|
|
806
|
+
}
|
|
807
|
+
decode(encoded) {
|
|
808
|
+
const decoded = encoded.replace(/--/g, "\\.").replace(/-/g, "\\");
|
|
809
|
+
if (/^[A-Za-z]\\/.test(decoded)) {
|
|
810
|
+
return decoded[0] + ":" + decoded.slice(1);
|
|
811
|
+
}
|
|
812
|
+
return decoded;
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
|
|
816
|
+
// src/platform/index.ts
|
|
817
|
+
function createDesktopApp() {
|
|
818
|
+
if (process.platform === "darwin") return new MacOSDesktopApp();
|
|
819
|
+
if (process.platform === "win32") return new WindowsDesktopApp();
|
|
820
|
+
return new NoOpDesktopApp();
|
|
891
821
|
}
|
|
892
|
-
function
|
|
893
|
-
return new
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
822
|
+
function createProfileSyncer() {
|
|
823
|
+
return new DesktopProfileSyncer(createDesktopApp());
|
|
824
|
+
}
|
|
825
|
+
function createBinaryResolver() {
|
|
826
|
+
return new SystemBinaryResolver(createDesktopApp());
|
|
827
|
+
}
|
|
828
|
+
function createPathCodec() {
|
|
829
|
+
if (process.platform === "win32") return new WindowsPathCodec();
|
|
830
|
+
return new UnixPathCodec();
|
|
899
831
|
}
|
|
900
832
|
|
|
901
|
-
// src/
|
|
902
|
-
var
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
833
|
+
// src/config.ts
|
|
834
|
+
var CLAUDE_DIR = process.env.CLAUDE_DIR || path4.join(os3.homedir(), ".claude");
|
|
835
|
+
var PROFILES_FILE = process.env.CLAUDE_PROFILES_FILE || path4.join(CLAUDE_DIR, "profiles.json");
|
|
836
|
+
var SETTINGS_FILE = process.env.CLAUDE_SETTINGS_FILE || path4.join(CLAUDE_DIR, "settings.json");
|
|
837
|
+
var CLAUDE_JSON = path4.join(os3.homedir(), ".claude.json");
|
|
838
|
+
var PROJECTS_DIR = path4.join(CLAUDE_DIR, "projects");
|
|
839
|
+
var SESSIONS_DIR = path4.join(CLAUDE_DIR, "sessions");
|
|
840
|
+
var desktopApp = createDesktopApp();
|
|
841
|
+
var DESKTOP_CONFIG_LIBRARY = desktopApp.getConfigLibrary() || "";
|
|
842
|
+
var DESKTOP_META_FILE = DESKTOP_CONFIG_LIBRARY ? path4.join(DESKTOP_CONFIG_LIBRARY, "_meta.json") : "";
|
|
843
|
+
var DESKTOP_SESSIONS_DIR = desktopApp.getSessionsDir() || "";
|
|
844
|
+
function isDesktopAppInstalled() {
|
|
845
|
+
return desktopApp.isInstalled();
|
|
846
|
+
}
|
|
847
|
+
function ensureFile(filePath, defaultContent) {
|
|
848
|
+
if (!fs4.existsSync(filePath)) {
|
|
849
|
+
debug(`ensureFile: creating ${filePath}`);
|
|
850
|
+
fs4.mkdirSync(path4.dirname(filePath), { recursive: true });
|
|
851
|
+
fs4.writeFileSync(filePath, defaultContent, "utf-8");
|
|
910
852
|
}
|
|
911
|
-
];
|
|
912
|
-
var ANTHROPIC_ALIASES2 = ["claude-sonnet-4-5", "claude-opus-4-7", "claude-haiku-4-5-20251001"];
|
|
913
|
-
function isAnthropicModel2(model) {
|
|
914
|
-
const anthropicAliases = ["opus", "sonnet", "haiku", "best", "default", "opusplan", "opus[1m]", "sonnet[1m]"];
|
|
915
|
-
const lower = model.toLowerCase();
|
|
916
|
-
if (anthropicAliases.includes(lower)) return true;
|
|
917
|
-
if (lower.startsWith("claude-")) return true;
|
|
918
|
-
return false;
|
|
919
853
|
}
|
|
920
|
-
function
|
|
921
|
-
|
|
854
|
+
function readJson(filePath) {
|
|
855
|
+
debug(`readJson: ${filePath}`);
|
|
856
|
+
return JSON.parse(fs4.readFileSync(filePath, "utf-8"));
|
|
922
857
|
}
|
|
923
|
-
function
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
const fmt = (name, desc) => `${name.padEnd(12)} ${desc}`;
|
|
927
|
-
console.log(fmt("NAME", "DESCRIPTION"));
|
|
928
|
-
console.log(fmt("----", "-----------"));
|
|
929
|
-
for (const p of PROVIDERS) {
|
|
930
|
-
console.log(fmt(p.name, p.description));
|
|
931
|
-
}
|
|
932
|
-
}));
|
|
933
|
-
return cmd;
|
|
858
|
+
function writeJson(filePath, data) {
|
|
859
|
+
debug(`writeJson: ${filePath}`);
|
|
860
|
+
fs4.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
934
861
|
}
|
|
935
|
-
function
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
862
|
+
function ensureProfilesFile() {
|
|
863
|
+
ensureFile(PROFILES_FILE, '{"profiles":{}}\n');
|
|
864
|
+
}
|
|
865
|
+
function ensureSettingsFile() {
|
|
866
|
+
ensureFile(SETTINGS_FILE, "{}\n");
|
|
867
|
+
}
|
|
868
|
+
function fixJsonFile(filePath, fallback = {}) {
|
|
869
|
+
if (!fs4.existsSync(filePath)) return;
|
|
870
|
+
const backupPath = path4.join(CLAUDE_DIR, path4.basename(filePath) + ".backup");
|
|
871
|
+
const raw = fs4.readFileSync(filePath, "utf-8");
|
|
872
|
+
try {
|
|
873
|
+
JSON.parse(raw);
|
|
874
|
+
fs4.mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
875
|
+
fs4.copyFileSync(filePath, backupPath);
|
|
876
|
+
return;
|
|
877
|
+
} catch {
|
|
878
|
+
}
|
|
879
|
+
let text = raw.trim();
|
|
880
|
+
if (text.charCodeAt(0) === 65279) {
|
|
881
|
+
text = text.slice(1).trim();
|
|
882
|
+
}
|
|
883
|
+
text = text.replace(/,\s*$/, "");
|
|
884
|
+
text = text.replace(/,\s*([}\]])/g, "$1");
|
|
885
|
+
const lastBrace = Math.max(text.lastIndexOf("}"), text.lastIndexOf("]"));
|
|
886
|
+
if (lastBrace !== -1 && lastBrace < text.length - 1) {
|
|
887
|
+
text = text.slice(0, lastBrace + 1);
|
|
888
|
+
}
|
|
889
|
+
let openCurly = 0, openSquare = 0;
|
|
890
|
+
for (const ch of text) {
|
|
891
|
+
if (ch === "{") openCurly++;
|
|
892
|
+
else if (ch === "}") openCurly--;
|
|
893
|
+
else if (ch === "[") openSquare++;
|
|
894
|
+
else if (ch === "]") openSquare--;
|
|
895
|
+
}
|
|
896
|
+
if (openSquare > 0) text += "]".repeat(openSquare);
|
|
897
|
+
if (openCurly > 0) text += "}".repeat(openCurly);
|
|
898
|
+
try {
|
|
899
|
+
JSON.parse(text);
|
|
900
|
+
fs4.writeFileSync(filePath, text + "\n", "utf-8");
|
|
901
|
+
warn(`Fixed invalid JSON in ${path4.basename(filePath)}.`);
|
|
902
|
+
console.error(`Fixed invalid JSON in ${path4.basename(filePath)}.`);
|
|
903
|
+
} catch {
|
|
904
|
+
let restored = false;
|
|
905
|
+
if (fs4.existsSync(backupPath)) {
|
|
906
|
+
try {
|
|
907
|
+
const backupRaw = fs4.readFileSync(backupPath, "utf-8");
|
|
908
|
+
JSON.parse(backupRaw);
|
|
909
|
+
fs4.copyFileSync(backupPath, filePath);
|
|
910
|
+
restored = true;
|
|
911
|
+
warn(`Restored ${path4.basename(filePath)} from backup.`);
|
|
912
|
+
console.error(`Restored ${path4.basename(filePath)} from backup.`);
|
|
913
|
+
} catch {
|
|
914
|
+
error(`Backup ${path4.basename(backupPath)} is also corrupt; using fallback.`);
|
|
915
|
+
console.error(`Backup ${path4.basename(backupPath)} is also corrupt; using fallback.`);
|
|
965
916
|
}
|
|
966
|
-
models = [defaultModel];
|
|
967
917
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
});
|
|
975
|
-
}));
|
|
918
|
+
if (!restored) {
|
|
919
|
+
writeJson(filePath, fallback);
|
|
920
|
+
error(`Could not fix ${path4.basename(filePath)}, no valid backup found, reset to default.`);
|
|
921
|
+
console.error(`Could not fix ${path4.basename(filePath)}, no valid backup found, reset to default.`);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
976
924
|
}
|
|
977
925
|
|
|
978
926
|
// src/profiles/runner.ts
|
|
927
|
+
import { spawnSync as spawnSync2, spawn } from "child_process";
|
|
979
928
|
var BUILT_IN_DEFAULT = "__builtin__";
|
|
980
929
|
function resolveClaudeBinary() {
|
|
981
930
|
return createBinaryResolver().resolve();
|
|
@@ -1100,10 +1049,10 @@ function maskToken(token) {
|
|
|
1100
1049
|
}
|
|
1101
1050
|
function formatModels(p) {
|
|
1102
1051
|
if (p.models && p.models.length > 0) {
|
|
1103
|
-
const nonAnthropicModels = p.models.filter((m) => !
|
|
1052
|
+
const nonAnthropicModels = p.models.filter((m) => !isAnthropicModel(m));
|
|
1104
1053
|
const parts = [];
|
|
1105
1054
|
p.models.forEach((m, i) => {
|
|
1106
|
-
if (!
|
|
1055
|
+
if (!isAnthropicModel(m)) {
|
|
1107
1056
|
const aliasIndex = nonAnthropicModels.indexOf(m);
|
|
1108
1057
|
if (aliasIndex === 0) parts.push(`${m} (sonnet)`);
|
|
1109
1058
|
else if (aliasIndex === 1) parts.push(`${m} (opus)`);
|
|
@@ -1121,20 +1070,13 @@ function formatModels(p) {
|
|
|
1121
1070
|
}
|
|
1122
1071
|
return p.model || "(unset)";
|
|
1123
1072
|
}
|
|
1124
|
-
function
|
|
1125
|
-
const anthropicAliases = ["opus", "sonnet", "haiku", "best", "default", "opusplan", "opus[1m]", "sonnet[1m]"];
|
|
1126
|
-
const lower = model.toLowerCase();
|
|
1127
|
-
if (anthropicAliases.includes(lower)) return true;
|
|
1128
|
-
if (lower.startsWith("claude-")) return true;
|
|
1129
|
-
return false;
|
|
1130
|
-
}
|
|
1131
|
-
function collect2(value, previous) {
|
|
1073
|
+
function collect(value, previous) {
|
|
1132
1074
|
return previous.concat([value]);
|
|
1133
1075
|
}
|
|
1134
1076
|
function profileCommand() {
|
|
1135
1077
|
const profile = new Command2("profile").description("Manage Claude CLI profiles");
|
|
1136
1078
|
const syncer = createProfileSyncer();
|
|
1137
|
-
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)",
|
|
1079
|
+
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) => {
|
|
1138
1080
|
const models = opts.model && opts.model.length > 0 ? opts.model : void 0;
|
|
1139
1081
|
if (models && models.length > 3) {
|
|
1140
1082
|
throw new Error("Error: A profile can have at most 3 models.");
|
|
@@ -1156,7 +1098,7 @@ function profileCommand() {
|
|
|
1156
1098
|
debug(`profile add: wrote ${PROFILES_FILE}`);
|
|
1157
1099
|
console.log(`Profile '${name}' saved.`);
|
|
1158
1100
|
}));
|
|
1159
|
-
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",
|
|
1101
|
+
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) => {
|
|
1160
1102
|
ensureProfilesFile();
|
|
1161
1103
|
const data = readJson(PROFILES_FILE);
|
|
1162
1104
|
if (!data.profiles[name]) {
|
|
@@ -1262,10 +1204,10 @@ function profileCommand() {
|
|
|
1262
1204
|
console.log(`Name: ${name}`);
|
|
1263
1205
|
console.log(`Model: ${p.model || "(unset)"}`);
|
|
1264
1206
|
if (p.models && p.models.length > 0) {
|
|
1265
|
-
const nonAnthropicModels = p.models.filter((m) => !
|
|
1207
|
+
const nonAnthropicModels = p.models.filter((m) => !isAnthropicModel(m));
|
|
1266
1208
|
console.log(`Models:`);
|
|
1267
1209
|
for (const m of p.models) {
|
|
1268
|
-
if (!
|
|
1210
|
+
if (!isAnthropicModel(m)) {
|
|
1269
1211
|
const aliasIndex = nonAnthropicModels.indexOf(m);
|
|
1270
1212
|
let alias = "";
|
|
1271
1213
|
if (aliasIndex === 0) alias = " (sonnet)";
|
|
@@ -2540,8 +2482,56 @@ function completionCommand() {
|
|
|
2540
2482
|
}));
|
|
2541
2483
|
}
|
|
2542
2484
|
|
|
2543
|
-
// src/
|
|
2485
|
+
// src/proxy/commands.ts
|
|
2544
2486
|
import { Command as Command6 } from "commander";
|
|
2487
|
+
function collect2(value, previous) {
|
|
2488
|
+
return previous.concat([value]);
|
|
2489
|
+
}
|
|
2490
|
+
function proxyCommand() {
|
|
2491
|
+
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) => {
|
|
2492
|
+
let targetUrl = opts.url || "https://api.openai.com";
|
|
2493
|
+
let apiKey = opts.apiKey || "";
|
|
2494
|
+
let defaultModel = opts.model || "gpt-4o";
|
|
2495
|
+
let models = [];
|
|
2496
|
+
const modelMappings = {};
|
|
2497
|
+
if (opts.profile) {
|
|
2498
|
+
ensureProfilesFile();
|
|
2499
|
+
const data = readJson(PROFILES_FILE);
|
|
2500
|
+
const p = data.profiles[opts.profile];
|
|
2501
|
+
if (!p) {
|
|
2502
|
+
throw new Error(`Profile '${opts.profile}' not found.`);
|
|
2503
|
+
}
|
|
2504
|
+
targetUrl = p.url || targetUrl;
|
|
2505
|
+
apiKey = p.token || apiKey;
|
|
2506
|
+
models = p.models || (p.model ? [p.model] : []);
|
|
2507
|
+
defaultModel = models[0] || defaultModel;
|
|
2508
|
+
models.forEach((m, i) => {
|
|
2509
|
+
if (!isAnthropicModel(m)) {
|
|
2510
|
+
const alias = ANTHROPIC_ALIASES[Math.min(i, ANTHROPIC_ALIASES.length - 1)];
|
|
2511
|
+
modelMappings[alias] = m;
|
|
2512
|
+
}
|
|
2513
|
+
});
|
|
2514
|
+
} else {
|
|
2515
|
+
for (const m of opts.mapping) {
|
|
2516
|
+
const [alias, actual] = m.split(":");
|
|
2517
|
+
if (alias && actual) {
|
|
2518
|
+
modelMappings[alias] = actual;
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
models = [defaultModel];
|
|
2522
|
+
}
|
|
2523
|
+
const { baseUrl, stop } = await startOpenAIProxy(targetUrl, apiKey, defaultModel, models, modelMappings);
|
|
2524
|
+
console.log(`Proxy running at ${baseUrl}`);
|
|
2525
|
+
console.log("Press Ctrl+C to stop");
|
|
2526
|
+
process.on("SIGINT", () => {
|
|
2527
|
+
stop();
|
|
2528
|
+
process.exit(0);
|
|
2529
|
+
});
|
|
2530
|
+
}));
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2533
|
+
// src/cache/commands.ts
|
|
2534
|
+
import { Command as Command7 } from "commander";
|
|
2545
2535
|
import fs8 from "fs";
|
|
2546
2536
|
import path8 from "path";
|
|
2547
2537
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
@@ -2616,7 +2606,7 @@ function killProcesses(pids) {
|
|
|
2616
2606
|
}
|
|
2617
2607
|
}
|
|
2618
2608
|
function cacheCommand() {
|
|
2619
|
-
const cache = new
|
|
2609
|
+
const cache = new Command7("cache").description("Manage Claude Code cache and backup files");
|
|
2620
2610
|
cache.command("restore").description("Restore ~/.claude/.claude.json.backup to ~/.claude.json").action(safeAction(async () => {
|
|
2621
2611
|
const backupPath = path8.join(CLAUDE_DIR, ".claude.json.backup");
|
|
2622
2612
|
const targetPath = CLAUDE_JSON;
|
|
@@ -2649,7 +2639,7 @@ ensureSettingsFile();
|
|
|
2649
2639
|
var settings = readJson(SETTINGS_FILE);
|
|
2650
2640
|
setLogLevel(settings._cc_hub_logLevel || "INFO");
|
|
2651
2641
|
installGlobalExceptionHandlers();
|
|
2652
|
-
var program = new
|
|
2642
|
+
var program = new Command8();
|
|
2653
2643
|
program.name("cc-hub").description("Manage Claude CLI profiles, hooks, and sessions").version(version);
|
|
2654
2644
|
program.addCommand(profileCommand());
|
|
2655
2645
|
program.addCommand(useCommand());
|