cc-hub-cli 1.1.6 → 1.1.7
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 +197 -35
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -254,6 +254,14 @@ 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
|
+
}
|
|
257
265
|
function toDesktopProfile(p) {
|
|
258
266
|
const models = p.models || (p.model ? [p.model] : []);
|
|
259
267
|
const isAnthropic = p.provider === "anthropic" || !p.provider && !p.url;
|
|
@@ -263,13 +271,24 @@ function toDesktopProfile(p) {
|
|
|
263
271
|
inferenceModels: models.map((m) => ({ name: m, supports1m: true }))
|
|
264
272
|
};
|
|
265
273
|
}
|
|
266
|
-
|
|
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 = {
|
|
267
282
|
inferenceProvider: "gateway",
|
|
268
283
|
inferenceGatewayBaseUrl: p.url || void 0,
|
|
269
284
|
inferenceGatewayApiKey: p.token || void 0,
|
|
270
285
|
inferenceGatewayAuthScheme: "bearer",
|
|
271
|
-
inferenceModels:
|
|
286
|
+
inferenceModels: mappedModels.map((m) => ({ name: m, supports1m: true }))
|
|
272
287
|
};
|
|
288
|
+
if (mappings.length > 0) {
|
|
289
|
+
result.inferenceModelMappings = mappings;
|
|
290
|
+
}
|
|
291
|
+
return result;
|
|
273
292
|
}
|
|
274
293
|
var DesktopProfileSyncer = class {
|
|
275
294
|
constructor(app) {
|
|
@@ -471,6 +490,7 @@ function fixJsonFile(filePath, fallback = {}) {
|
|
|
471
490
|
const raw = fs4.readFileSync(filePath, "utf-8");
|
|
472
491
|
try {
|
|
473
492
|
JSON.parse(raw);
|
|
493
|
+
fs4.mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
474
494
|
fs4.copyFileSync(filePath, backupPath);
|
|
475
495
|
return;
|
|
476
496
|
} catch {
|
|
@@ -500,14 +520,24 @@ function fixJsonFile(filePath, fallback = {}) {
|
|
|
500
520
|
warn(`Fixed invalid JSON in ${path4.basename(filePath)}.`);
|
|
501
521
|
console.error(`Fixed invalid JSON in ${path4.basename(filePath)}.`);
|
|
502
522
|
} catch {
|
|
523
|
+
let restored = false;
|
|
503
524
|
if (fs4.existsSync(backupPath)) {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
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) {
|
|
508
538
|
writeJson(filePath, fallback);
|
|
509
|
-
error(`Could not fix ${path4.basename(filePath)}, no backup found, reset to default.`);
|
|
510
|
-
console.error(`Could not fix ${path4.basename(filePath)}, no backup found, reset to default.`);
|
|
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.`);
|
|
511
541
|
}
|
|
512
542
|
}
|
|
513
543
|
}
|
|
@@ -518,6 +548,9 @@ import { spawnSync as spawnSync2, spawn } from "child_process";
|
|
|
518
548
|
// src/provider/index.ts
|
|
519
549
|
import { Command } from "commander";
|
|
520
550
|
|
|
551
|
+
// src/provider/server.ts
|
|
552
|
+
import http from "http";
|
|
553
|
+
|
|
521
554
|
// src/provider/transform.ts
|
|
522
555
|
function sanitizeToolId(id) {
|
|
523
556
|
let sanitized = id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
@@ -675,7 +708,7 @@ function transformOpenAIResponseToAnthropic(openaiResponse, originalModel) {
|
|
|
675
708
|
id: openaiResponse.id ?? `msg_${Date.now()}`,
|
|
676
709
|
type: "message",
|
|
677
710
|
role: "assistant",
|
|
678
|
-
model:
|
|
711
|
+
model: originalModel,
|
|
679
712
|
content,
|
|
680
713
|
stop_reason: finishMap[choice.finish_reason] ?? "end_turn",
|
|
681
714
|
stop_sequence: null,
|
|
@@ -743,8 +776,7 @@ data: ${JSON.stringify(data)}
|
|
|
743
776
|
}
|
|
744
777
|
|
|
745
778
|
// src/provider/server.ts
|
|
746
|
-
|
|
747
|
-
async function startOpenAIProxy(targetUrl, apiKey, model, models = []) {
|
|
779
|
+
async function startOpenAIProxy(targetUrl, apiKey, model, models = [], modelMappings = {}) {
|
|
748
780
|
const base = targetUrl.replace(/\/+$/, "");
|
|
749
781
|
const server = http.createServer(async (req, res) => {
|
|
750
782
|
debug(`Proxy request: ${req.method} ${req.url}`);
|
|
@@ -770,7 +802,9 @@ async function startOpenAIProxy(targetUrl, apiKey, model, models = []) {
|
|
|
770
802
|
return;
|
|
771
803
|
}
|
|
772
804
|
const isStream = !!parsed.stream;
|
|
773
|
-
const
|
|
805
|
+
const requestModel = parsed.model ?? model;
|
|
806
|
+
const actualModel = modelMappings[requestModel] || requestModel;
|
|
807
|
+
const openaiBody = transformAnthropicToOpenAI({ ...parsed, model: actualModel, stream: false });
|
|
774
808
|
if (isStream) {
|
|
775
809
|
res.writeHead(200, {
|
|
776
810
|
"Content-Type": "text/event-stream",
|
|
@@ -875,6 +909,17 @@ var PROVIDERS = [
|
|
|
875
909
|
description: "Embedded proxy \u2014 translates Anthropic requests to OpenAI Chat Completions format"
|
|
876
910
|
}
|
|
877
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
|
+
}
|
|
920
|
+
function collect(value, previous) {
|
|
921
|
+
return previous.concat([value]);
|
|
922
|
+
}
|
|
878
923
|
function providerCommand() {
|
|
879
924
|
const cmd = new Command("provider").description("Manage provider types");
|
|
880
925
|
cmd.command("list").description("List available provider types").action(safeAction(() => {
|
|
@@ -887,8 +932,51 @@ function providerCommand() {
|
|
|
887
932
|
}));
|
|
888
933
|
return cmd;
|
|
889
934
|
}
|
|
935
|
+
function proxyCommand() {
|
|
936
|
+
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) => {
|
|
937
|
+
let targetUrl = opts.url || "https://api.openai.com";
|
|
938
|
+
let apiKey = opts.apiKey || "";
|
|
939
|
+
let defaultModel = opts.model || "gpt-4o";
|
|
940
|
+
let models = [];
|
|
941
|
+
const modelMappings = {};
|
|
942
|
+
if (opts.profile) {
|
|
943
|
+
ensureProfilesFile();
|
|
944
|
+
const data = readJson(PROFILES_FILE);
|
|
945
|
+
const p = data.profiles[opts.profile];
|
|
946
|
+
if (!p) {
|
|
947
|
+
throw new Error(`Profile '${opts.profile}' not found.`);
|
|
948
|
+
}
|
|
949
|
+
targetUrl = p.url || targetUrl;
|
|
950
|
+
apiKey = p.token || apiKey;
|
|
951
|
+
models = p.models || (p.model ? [p.model] : []);
|
|
952
|
+
defaultModel = models[0] || defaultModel;
|
|
953
|
+
models.forEach((m, i) => {
|
|
954
|
+
if (!isAnthropicModel2(m)) {
|
|
955
|
+
const alias = ANTHROPIC_ALIASES2[Math.min(i, ANTHROPIC_ALIASES2.length - 1)];
|
|
956
|
+
modelMappings[alias] = m;
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
} else {
|
|
960
|
+
for (const m of opts.mapping) {
|
|
961
|
+
const [alias, actual] = m.split(":");
|
|
962
|
+
if (alias && actual) {
|
|
963
|
+
modelMappings[alias] = actual;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
models = [defaultModel];
|
|
967
|
+
}
|
|
968
|
+
const { baseUrl, stop } = await startOpenAIProxy(targetUrl, apiKey, defaultModel, models, modelMappings);
|
|
969
|
+
console.log(`Proxy running at ${baseUrl}`);
|
|
970
|
+
console.log("Press Ctrl+C to stop");
|
|
971
|
+
process.on("SIGINT", () => {
|
|
972
|
+
stop();
|
|
973
|
+
process.exit(0);
|
|
974
|
+
});
|
|
975
|
+
}));
|
|
976
|
+
}
|
|
890
977
|
|
|
891
978
|
// src/profiles/runner.ts
|
|
979
|
+
var BUILT_IN_DEFAULT = "__builtin__";
|
|
892
980
|
function resolveClaudeBinary() {
|
|
893
981
|
return createBinaryResolver().resolve();
|
|
894
982
|
}
|
|
@@ -962,7 +1050,8 @@ function execClaude(profileName, p, extraArgs) {
|
|
|
962
1050
|
p.url || "https://api.openai.com",
|
|
963
1051
|
p.token || "",
|
|
964
1052
|
firstModel || "gpt-4o",
|
|
965
|
-
allModels
|
|
1053
|
+
allModels,
|
|
1054
|
+
{}
|
|
966
1055
|
).then(({ baseUrl, stop }) => {
|
|
967
1056
|
env.ANTHROPIC_BASE_URL = baseUrl;
|
|
968
1057
|
debug(`execClaude: proxy running at ${baseUrl}`);
|
|
@@ -985,6 +1074,22 @@ function execClaude(profileName, p, extraArgs) {
|
|
|
985
1074
|
process.exit(result.status ?? 1);
|
|
986
1075
|
}
|
|
987
1076
|
}
|
|
1077
|
+
function execClaudeBuiltIn(extraArgs) {
|
|
1078
|
+
const binary = resolveClaudeBinary();
|
|
1079
|
+
const cmd = [binary, ...extraArgs];
|
|
1080
|
+
const env = {
|
|
1081
|
+
...process.env,
|
|
1082
|
+
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1",
|
|
1083
|
+
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1"
|
|
1084
|
+
};
|
|
1085
|
+
info(`Launching Claude with built-in official models: binary=${binary}`);
|
|
1086
|
+
const result = spawnSync2(cmd[0], cmd.slice(1), {
|
|
1087
|
+
stdio: "inherit",
|
|
1088
|
+
env,
|
|
1089
|
+
shell: process.platform === "win32"
|
|
1090
|
+
});
|
|
1091
|
+
process.exit(result.status ?? 1);
|
|
1092
|
+
}
|
|
988
1093
|
|
|
989
1094
|
// src/profiles/commands.ts
|
|
990
1095
|
function maskToken(token) {
|
|
@@ -994,10 +1099,10 @@ function maskToken(token) {
|
|
|
994
1099
|
}
|
|
995
1100
|
function formatModels(p) {
|
|
996
1101
|
if (p.models && p.models.length > 0) {
|
|
997
|
-
const nonAnthropicModels = p.models.filter((m) => !
|
|
1102
|
+
const nonAnthropicModels = p.models.filter((m) => !isAnthropicModel3(m));
|
|
998
1103
|
const parts = [];
|
|
999
1104
|
p.models.forEach((m, i) => {
|
|
1000
|
-
if (!
|
|
1105
|
+
if (!isAnthropicModel3(m)) {
|
|
1001
1106
|
const aliasIndex = nonAnthropicModels.indexOf(m);
|
|
1002
1107
|
if (aliasIndex === 0) parts.push(`${m} (sonnet)`);
|
|
1003
1108
|
else if (aliasIndex === 1) parts.push(`${m} (opus)`);
|
|
@@ -1015,20 +1120,20 @@ function formatModels(p) {
|
|
|
1015
1120
|
}
|
|
1016
1121
|
return p.model || "(unset)";
|
|
1017
1122
|
}
|
|
1018
|
-
function
|
|
1123
|
+
function isAnthropicModel3(model) {
|
|
1019
1124
|
const anthropicAliases = ["opus", "sonnet", "haiku", "best", "default", "opusplan", "opus[1m]", "sonnet[1m]"];
|
|
1020
1125
|
const lower = model.toLowerCase();
|
|
1021
1126
|
if (anthropicAliases.includes(lower)) return true;
|
|
1022
1127
|
if (lower.startsWith("claude-")) return true;
|
|
1023
1128
|
return false;
|
|
1024
1129
|
}
|
|
1025
|
-
function
|
|
1130
|
+
function collect2(value, previous) {
|
|
1026
1131
|
return previous.concat([value]);
|
|
1027
1132
|
}
|
|
1028
1133
|
function profileCommand() {
|
|
1029
1134
|
const profile = new Command2("profile").description("Manage Claude CLI profiles");
|
|
1030
1135
|
const syncer = createProfileSyncer();
|
|
1031
|
-
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)",
|
|
1136
|
+
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) => {
|
|
1032
1137
|
const models = opts.model && opts.model.length > 0 ? opts.model : void 0;
|
|
1033
1138
|
if (models && models.length > 3) {
|
|
1034
1139
|
throw new Error("Error: A profile can have at most 3 models.");
|
|
@@ -1050,7 +1155,7 @@ function profileCommand() {
|
|
|
1050
1155
|
debug(`profile add: wrote ${PROFILES_FILE}`);
|
|
1051
1156
|
console.log(`Profile '${name}' saved.`);
|
|
1052
1157
|
}));
|
|
1053
|
-
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",
|
|
1158
|
+
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) => {
|
|
1054
1159
|
ensureProfilesFile();
|
|
1055
1160
|
const data = readJson(PROFILES_FILE);
|
|
1056
1161
|
if (!data.profiles[name]) {
|
|
@@ -1142,6 +1247,9 @@ function profileCommand() {
|
|
|
1142
1247
|
profile.command("view").description("View full details of a profile (token unmasked)").argument("<name>", "Profile name").option("-j, --json", "Output as JSON").action(safeAction((name, opts) => {
|
|
1143
1248
|
ensureProfilesFile();
|
|
1144
1249
|
const data = readJson(PROFILES_FILE);
|
|
1250
|
+
if (name === BUILT_IN_DEFAULT) {
|
|
1251
|
+
throw new Error(`'${BUILT_IN_DEFAULT}' is not a stored profile. Use 'cc-hub run --built-in' or 'cc-hub use --built-in' for official Anthropic models.`);
|
|
1252
|
+
}
|
|
1145
1253
|
const p = data.profiles[name];
|
|
1146
1254
|
if (!p) {
|
|
1147
1255
|
throw new Error(`Profile '${name}' not found.`);
|
|
@@ -1153,10 +1261,10 @@ function profileCommand() {
|
|
|
1153
1261
|
console.log(`Name: ${name}`);
|
|
1154
1262
|
console.log(`Model: ${p.model || "(unset)"}`);
|
|
1155
1263
|
if (p.models && p.models.length > 0) {
|
|
1156
|
-
const nonAnthropicModels = p.models.filter((m) => !
|
|
1264
|
+
const nonAnthropicModels = p.models.filter((m) => !isAnthropicModel3(m));
|
|
1157
1265
|
console.log(`Models:`);
|
|
1158
1266
|
for (const m of p.models) {
|
|
1159
|
-
if (!
|
|
1267
|
+
if (!isAnthropicModel3(m)) {
|
|
1160
1268
|
const aliasIndex = nonAnthropicModels.indexOf(m);
|
|
1161
1269
|
let alias = "";
|
|
1162
1270
|
if (aliasIndex === 0) alias = " (sonnet)";
|
|
@@ -1203,9 +1311,19 @@ function profileCommand() {
|
|
|
1203
1311
|
writeJson(PROFILES_FILE, data);
|
|
1204
1312
|
console.log(`Profile '${oldName}' renamed to '${newName}'.`);
|
|
1205
1313
|
}));
|
|
1206
|
-
profile.command("default").description("Set the default profile").argument("
|
|
1314
|
+
profile.command("default").description("Set the default profile").option("--built-in", "Use official Anthropic models as default").argument("[name]", "Profile name to set as default (required unless --built-in)").action(safeAction((name, opts) => {
|
|
1207
1315
|
ensureProfilesFile();
|
|
1208
1316
|
const data = readJson(PROFILES_FILE);
|
|
1317
|
+
if (opts.builtIn) {
|
|
1318
|
+
data.default = BUILT_IN_DEFAULT;
|
|
1319
|
+
writeJson(PROFILES_FILE, data);
|
|
1320
|
+
debug(`profile default: wrote ${PROFILES_FILE}`);
|
|
1321
|
+
console.log("Default set to built-in official Anthropic models.");
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
if (!name) {
|
|
1325
|
+
throw new Error("Profile name is required. Use --built-in for official Anthropic models.");
|
|
1326
|
+
}
|
|
1209
1327
|
if (!data.profiles[name]) {
|
|
1210
1328
|
throw new Error(`Profile '${name}' not found.`);
|
|
1211
1329
|
}
|
|
@@ -1228,6 +1346,7 @@ function profileCommand() {
|
|
|
1228
1346
|
return;
|
|
1229
1347
|
}
|
|
1230
1348
|
for (const name of names) {
|
|
1349
|
+
if (name === BUILT_IN_DEFAULT) continue;
|
|
1231
1350
|
const p = data.profiles[name];
|
|
1232
1351
|
debug(`profile sync: syncing '${name}' to desktop`);
|
|
1233
1352
|
syncer.sync(name, p);
|
|
@@ -1240,9 +1359,19 @@ function profileCommand() {
|
|
|
1240
1359
|
}
|
|
1241
1360
|
function useCommand() {
|
|
1242
1361
|
const syncer = createProfileSyncer();
|
|
1243
|
-
return new Command2("use").description("Set a profile as the default").argument("
|
|
1362
|
+
return new Command2("use").description("Set a profile as the default").option("--built-in", "Use official Anthropic models as default").argument("[name]", "Profile name (required unless --built-in)").action(safeAction((name, opts) => {
|
|
1244
1363
|
ensureProfilesFile();
|
|
1245
1364
|
const data = readJson(PROFILES_FILE);
|
|
1365
|
+
if (opts.builtIn) {
|
|
1366
|
+
data.default = BUILT_IN_DEFAULT;
|
|
1367
|
+
writeJson(PROFILES_FILE, data);
|
|
1368
|
+
debug(`use: wrote ${PROFILES_FILE}`);
|
|
1369
|
+
console.log("Default set to built-in official Anthropic models.");
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
if (!name) {
|
|
1373
|
+
throw new Error("Profile name is required. Use --built-in for official Anthropic models.");
|
|
1374
|
+
}
|
|
1246
1375
|
if (!data.profiles[name]) {
|
|
1247
1376
|
throw new Error(`Profile '${name}' not found.`);
|
|
1248
1377
|
}
|
|
@@ -1255,10 +1384,14 @@ function useCommand() {
|
|
|
1255
1384
|
}));
|
|
1256
1385
|
}
|
|
1257
1386
|
function runCommand() {
|
|
1258
|
-
return new Command2("run").description("Launch Claude Code using the default or a specified profile").allowUnknownOption().argument("[args...]", "Optional profile name followed by extra arguments").action(safeAction((args) => {
|
|
1387
|
+
return new Command2("run").description("Launch Claude Code using the default or a specified profile").option("--built-in", "Use official Anthropic models (no custom profile)").allowUnknownOption().argument("[args...]", "Optional profile name followed by extra arguments").action(safeAction((args, opts) => {
|
|
1259
1388
|
fixJsonFile(CLAUDE_JSON);
|
|
1260
1389
|
ensureProfilesFile();
|
|
1261
1390
|
const data = readJson(PROFILES_FILE);
|
|
1391
|
+
if (opts.builtIn) {
|
|
1392
|
+
execClaudeBuiltIn(args);
|
|
1393
|
+
return;
|
|
1394
|
+
}
|
|
1262
1395
|
let profileName = "";
|
|
1263
1396
|
let claudeArgs;
|
|
1264
1397
|
if (args.length > 0 && data.profiles[args[0]]) {
|
|
@@ -1268,8 +1401,12 @@ function runCommand() {
|
|
|
1268
1401
|
profileName = data.default || "";
|
|
1269
1402
|
claudeArgs = args;
|
|
1270
1403
|
}
|
|
1404
|
+
if (profileName === BUILT_IN_DEFAULT) {
|
|
1405
|
+
execClaudeBuiltIn(claudeArgs);
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1271
1408
|
if (!profileName) {
|
|
1272
|
-
throw new Error("No default profile set. Use 'cc-hub use <name>' first.");
|
|
1409
|
+
throw new Error("No default profile set. Use 'cc-hub use <name>' or 'cc-hub use --built-in' first.");
|
|
1273
1410
|
}
|
|
1274
1411
|
const p = data.profiles[profileName];
|
|
1275
1412
|
debug(`run: launching claude with profile '${profileName}', args=[${claudeArgs.join(", ")}]`);
|
|
@@ -2025,7 +2162,7 @@ _cc-hub() {
|
|
|
2025
2162
|
local -a commands
|
|
2026
2163
|
commands=(
|
|
2027
2164
|
'profile:Manage Claude CLI profiles'
|
|
2028
|
-
'use:
|
|
2165
|
+
'use:Set a profile as the default'
|
|
2029
2166
|
'run:Launch Claude Code using the default or a specified profile'
|
|
2030
2167
|
'hook:Manage Claude Code hooks in settings.json'
|
|
2031
2168
|
'session:Manage Claude Code sessions'
|
|
@@ -2104,8 +2241,10 @@ _cc-hub() {
|
|
|
2104
2241
|
profile)
|
|
2105
2242
|
if (( CURRENT == 2 )); then
|
|
2106
2243
|
_describe -t profile-subcmds 'profile subcommand' profile_subcmds
|
|
2107
|
-
elif [[ $words[2] == "view" || $words[2] == "remove"
|
|
2244
|
+
elif [[ $words[2] == "view" || $words[2] == "remove" ]]; then
|
|
2108
2245
|
_cc_hub_profiles
|
|
2246
|
+
elif [[ $words[2] == "default" ]]; then
|
|
2247
|
+
_arguments -C -S '--built-in[Use official Anthropic models as default]' '*:profile:_cc_hub_profiles'
|
|
2109
2248
|
elif [[ $words[2] == "rename" ]]; then
|
|
2110
2249
|
if (( CURRENT == 3 )); then
|
|
2111
2250
|
_cc_hub_profiles
|
|
@@ -2137,7 +2276,7 @@ _cc-hub() {
|
|
|
2137
2276
|
fi
|
|
2138
2277
|
;;
|
|
2139
2278
|
use|run)
|
|
2140
|
-
_cc_hub_profiles
|
|
2279
|
+
_arguments -C -S '--built-in[Use official Anthropic models (no custom profile)]' '*:profile:_cc_hub_profiles'
|
|
2141
2280
|
;;
|
|
2142
2281
|
hook)
|
|
2143
2282
|
if (( CURRENT == 2 )); then
|
|
@@ -2160,20 +2299,22 @@ compdef _cc-hub cc-hub
|
|
|
2160
2299
|
`;
|
|
2161
2300
|
|
|
2162
2301
|
// src/complete/bash.ts
|
|
2163
|
-
var BASH_COMPLETION = `_cc-
|
|
2302
|
+
var BASH_COMPLETION = `_cc-hub_profile_names() {
|
|
2164
2303
|
local profiles_file="\${CLAUDE_PROFILES_FILE:-$HOME/.claude/profiles.json}"
|
|
2165
2304
|
if [[ -f "$profiles_file" ]]; then
|
|
2166
|
-
|
|
2167
|
-
names=$(command python3 -c "
|
|
2305
|
+
command python3 -c "
|
|
2168
2306
|
import json
|
|
2169
2307
|
data = json.load(open('$profiles_file'))
|
|
2170
2308
|
for name in data.get('profiles', {}):
|
|
2171
2309
|
print(name)
|
|
2172
|
-
" 2>/dev/null
|
|
2173
|
-
COMPREPLY=($(compgen -W "$names" -- "\${cur}"))
|
|
2310
|
+
" 2>/dev/null
|
|
2174
2311
|
fi
|
|
2175
2312
|
}
|
|
2176
2313
|
|
|
2314
|
+
_cc-hub_profiles() {
|
|
2315
|
+
COMPREPLY=($(compgen -W "$(_cc-hub_profile_names)" -- "\${cur}"))
|
|
2316
|
+
}
|
|
2317
|
+
|
|
2177
2318
|
_cc-hub_models_for_profile() {
|
|
2178
2319
|
local profile_name="$1"
|
|
2179
2320
|
local profiles_file="\${CLAUDE_PROFILES_FILE:-$HOME/.claude/profiles.json}"
|
|
@@ -2222,8 +2363,10 @@ _cc-hub() {
|
|
|
2222
2363
|
profile)
|
|
2223
2364
|
if [[ \${COMP_CWORD} -eq 2 ]]; then
|
|
2224
2365
|
COMPREPLY=($(compgen -W "$profile_subcmds" -- "$cur"))
|
|
2225
|
-
elif [[ "$prev" == "view" || "$prev" == "remove"
|
|
2366
|
+
elif [[ "$prev" == "view" || "$prev" == "remove" ]]; then
|
|
2226
2367
|
_cc-hub_profiles
|
|
2368
|
+
elif [[ "$prev" == "default" ]]; then
|
|
2369
|
+
COMPREPLY=($(compgen -W "--built-in $(_cc-hub_profile_names)" -- "$cur"))
|
|
2227
2370
|
elif [[ "$prev" == "rename" ]]; then
|
|
2228
2371
|
_cc-hub_profiles
|
|
2229
2372
|
elif [[ "$prev" == "profile" ]]; then
|
|
@@ -2249,7 +2392,11 @@ _cc-hub() {
|
|
|
2249
2392
|
fi
|
|
2250
2393
|
;;
|
|
2251
2394
|
use|run)
|
|
2252
|
-
|
|
2395
|
+
if [[ "$prev" == "--built-in" ]]; then
|
|
2396
|
+
:
|
|
2397
|
+
else
|
|
2398
|
+
COMPREPLY=($(compgen -W "--built-in $(_cc-hub_profile_names)" -- "$cur"))
|
|
2399
|
+
fi
|
|
2253
2400
|
;;
|
|
2254
2401
|
hook)
|
|
2255
2402
|
if [[ \${COMP_CWORD} -eq 2 ]]; then
|
|
@@ -2311,6 +2458,10 @@ var POWERSHELL_COMPLETION = `Register-ArgumentCompleter -Native -CommandName cc-
|
|
|
2311
2458
|
$profileSubcmds | ForEach-Object { if ($_ -like "$wordToComplete*") { $_ } }
|
|
2312
2459
|
return
|
|
2313
2460
|
}
|
|
2461
|
+
if ($tokens[2] -eq 'default' -and $tokens.Count -ge 3) {
|
|
2462
|
+
$opts = @('--built-in')
|
|
2463
|
+
$opts | ForEach-Object { if ($_ -like "$wordToComplete*") { $_ } }
|
|
2464
|
+
}
|
|
2314
2465
|
}
|
|
2315
2466
|
'hook' {
|
|
2316
2467
|
if ($tokens.Count -eq 2 -or ($tokens.Count -eq 3 -and $wordToComplete -ne '')) {
|
|
@@ -2330,6 +2481,16 @@ var POWERSHELL_COMPLETION = `Register-ArgumentCompleter -Native -CommandName cc-
|
|
|
2330
2481
|
return
|
|
2331
2482
|
}
|
|
2332
2483
|
}
|
|
2484
|
+
'use' {
|
|
2485
|
+
$opts = @('--built-in')
|
|
2486
|
+
$opts | ForEach-Object { if ($_ -like "$wordToComplete*") { $_ } }
|
|
2487
|
+
return
|
|
2488
|
+
}
|
|
2489
|
+
'run' {
|
|
2490
|
+
$opts = @('--built-in')
|
|
2491
|
+
$opts | ForEach-Object { if ($_ -like "$wordToComplete*") { $_ } }
|
|
2492
|
+
return
|
|
2493
|
+
}
|
|
2333
2494
|
}
|
|
2334
2495
|
}`;
|
|
2335
2496
|
|
|
@@ -2369,6 +2530,7 @@ program.addCommand(hooksCommand());
|
|
|
2369
2530
|
program.addCommand(sessionCommand());
|
|
2370
2531
|
program.addCommand(completionCommand());
|
|
2371
2532
|
program.addCommand(providerCommand());
|
|
2533
|
+
program.addCommand(proxyCommand());
|
|
2372
2534
|
try {
|
|
2373
2535
|
program.parse();
|
|
2374
2536
|
} catch (err) {
|