cc-hub-cli 1.1.6 → 1.1.8
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 +199 -36
- 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
|
}
|
|
@@ -909,7 +997,8 @@ function updateSettingsForProfile(p) {
|
|
|
909
997
|
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
910
998
|
"ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME",
|
|
911
999
|
"ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION",
|
|
912
|
-
"ANTHROPIC_CUSTOM_MODEL_OPTION"
|
|
1000
|
+
"ANTHROPIC_CUSTOM_MODEL_OPTION",
|
|
1001
|
+
"ANTHROPIC_API_KEY"
|
|
913
1002
|
];
|
|
914
1003
|
if (settings2.env) {
|
|
915
1004
|
const env = settings2.env;
|
|
@@ -962,7 +1051,8 @@ function execClaude(profileName, p, extraArgs) {
|
|
|
962
1051
|
p.url || "https://api.openai.com",
|
|
963
1052
|
p.token || "",
|
|
964
1053
|
firstModel || "gpt-4o",
|
|
965
|
-
allModels
|
|
1054
|
+
allModels,
|
|
1055
|
+
{}
|
|
966
1056
|
).then(({ baseUrl, stop }) => {
|
|
967
1057
|
env.ANTHROPIC_BASE_URL = baseUrl;
|
|
968
1058
|
debug(`execClaude: proxy running at ${baseUrl}`);
|
|
@@ -985,6 +1075,22 @@ function execClaude(profileName, p, extraArgs) {
|
|
|
985
1075
|
process.exit(result.status ?? 1);
|
|
986
1076
|
}
|
|
987
1077
|
}
|
|
1078
|
+
function execClaudeBuiltIn(extraArgs) {
|
|
1079
|
+
const binary = resolveClaudeBinary();
|
|
1080
|
+
const cmd = [binary, ...extraArgs];
|
|
1081
|
+
const env = {
|
|
1082
|
+
...process.env,
|
|
1083
|
+
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1",
|
|
1084
|
+
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1"
|
|
1085
|
+
};
|
|
1086
|
+
info(`Launching Claude with built-in official models: binary=${binary}`);
|
|
1087
|
+
const result = spawnSync2(cmd[0], cmd.slice(1), {
|
|
1088
|
+
stdio: "inherit",
|
|
1089
|
+
env,
|
|
1090
|
+
shell: process.platform === "win32"
|
|
1091
|
+
});
|
|
1092
|
+
process.exit(result.status ?? 1);
|
|
1093
|
+
}
|
|
988
1094
|
|
|
989
1095
|
// src/profiles/commands.ts
|
|
990
1096
|
function maskToken(token) {
|
|
@@ -994,10 +1100,10 @@ function maskToken(token) {
|
|
|
994
1100
|
}
|
|
995
1101
|
function formatModels(p) {
|
|
996
1102
|
if (p.models && p.models.length > 0) {
|
|
997
|
-
const nonAnthropicModels = p.models.filter((m) => !
|
|
1103
|
+
const nonAnthropicModels = p.models.filter((m) => !isAnthropicModel3(m));
|
|
998
1104
|
const parts = [];
|
|
999
1105
|
p.models.forEach((m, i) => {
|
|
1000
|
-
if (!
|
|
1106
|
+
if (!isAnthropicModel3(m)) {
|
|
1001
1107
|
const aliasIndex = nonAnthropicModels.indexOf(m);
|
|
1002
1108
|
if (aliasIndex === 0) parts.push(`${m} (sonnet)`);
|
|
1003
1109
|
else if (aliasIndex === 1) parts.push(`${m} (opus)`);
|
|
@@ -1015,20 +1121,20 @@ function formatModels(p) {
|
|
|
1015
1121
|
}
|
|
1016
1122
|
return p.model || "(unset)";
|
|
1017
1123
|
}
|
|
1018
|
-
function
|
|
1124
|
+
function isAnthropicModel3(model) {
|
|
1019
1125
|
const anthropicAliases = ["opus", "sonnet", "haiku", "best", "default", "opusplan", "opus[1m]", "sonnet[1m]"];
|
|
1020
1126
|
const lower = model.toLowerCase();
|
|
1021
1127
|
if (anthropicAliases.includes(lower)) return true;
|
|
1022
1128
|
if (lower.startsWith("claude-")) return true;
|
|
1023
1129
|
return false;
|
|
1024
1130
|
}
|
|
1025
|
-
function
|
|
1131
|
+
function collect2(value, previous) {
|
|
1026
1132
|
return previous.concat([value]);
|
|
1027
1133
|
}
|
|
1028
1134
|
function profileCommand() {
|
|
1029
1135
|
const profile = new Command2("profile").description("Manage Claude CLI profiles");
|
|
1030
1136
|
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)",
|
|
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)", 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
1138
|
const models = opts.model && opts.model.length > 0 ? opts.model : void 0;
|
|
1033
1139
|
if (models && models.length > 3) {
|
|
1034
1140
|
throw new Error("Error: A profile can have at most 3 models.");
|
|
@@ -1050,7 +1156,7 @@ function profileCommand() {
|
|
|
1050
1156
|
debug(`profile add: wrote ${PROFILES_FILE}`);
|
|
1051
1157
|
console.log(`Profile '${name}' saved.`);
|
|
1052
1158
|
}));
|
|
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",
|
|
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", 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
1160
|
ensureProfilesFile();
|
|
1055
1161
|
const data = readJson(PROFILES_FILE);
|
|
1056
1162
|
if (!data.profiles[name]) {
|
|
@@ -1142,6 +1248,9 @@ function profileCommand() {
|
|
|
1142
1248
|
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
1249
|
ensureProfilesFile();
|
|
1144
1250
|
const data = readJson(PROFILES_FILE);
|
|
1251
|
+
if (name === BUILT_IN_DEFAULT) {
|
|
1252
|
+
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.`);
|
|
1253
|
+
}
|
|
1145
1254
|
const p = data.profiles[name];
|
|
1146
1255
|
if (!p) {
|
|
1147
1256
|
throw new Error(`Profile '${name}' not found.`);
|
|
@@ -1153,10 +1262,10 @@ function profileCommand() {
|
|
|
1153
1262
|
console.log(`Name: ${name}`);
|
|
1154
1263
|
console.log(`Model: ${p.model || "(unset)"}`);
|
|
1155
1264
|
if (p.models && p.models.length > 0) {
|
|
1156
|
-
const nonAnthropicModels = p.models.filter((m) => !
|
|
1265
|
+
const nonAnthropicModels = p.models.filter((m) => !isAnthropicModel3(m));
|
|
1157
1266
|
console.log(`Models:`);
|
|
1158
1267
|
for (const m of p.models) {
|
|
1159
|
-
if (!
|
|
1268
|
+
if (!isAnthropicModel3(m)) {
|
|
1160
1269
|
const aliasIndex = nonAnthropicModels.indexOf(m);
|
|
1161
1270
|
let alias = "";
|
|
1162
1271
|
if (aliasIndex === 0) alias = " (sonnet)";
|
|
@@ -1203,9 +1312,19 @@ function profileCommand() {
|
|
|
1203
1312
|
writeJson(PROFILES_FILE, data);
|
|
1204
1313
|
console.log(`Profile '${oldName}' renamed to '${newName}'.`);
|
|
1205
1314
|
}));
|
|
1206
|
-
profile.command("default").description("Set the default profile").argument("
|
|
1315
|
+
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
1316
|
ensureProfilesFile();
|
|
1208
1317
|
const data = readJson(PROFILES_FILE);
|
|
1318
|
+
if (opts.builtIn) {
|
|
1319
|
+
data.default = BUILT_IN_DEFAULT;
|
|
1320
|
+
writeJson(PROFILES_FILE, data);
|
|
1321
|
+
debug(`profile default: wrote ${PROFILES_FILE}`);
|
|
1322
|
+
console.log("Default set to built-in official Anthropic models.");
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
if (!name) {
|
|
1326
|
+
throw new Error("Profile name is required. Use --built-in for official Anthropic models.");
|
|
1327
|
+
}
|
|
1209
1328
|
if (!data.profiles[name]) {
|
|
1210
1329
|
throw new Error(`Profile '${name}' not found.`);
|
|
1211
1330
|
}
|
|
@@ -1228,6 +1347,7 @@ function profileCommand() {
|
|
|
1228
1347
|
return;
|
|
1229
1348
|
}
|
|
1230
1349
|
for (const name of names) {
|
|
1350
|
+
if (name === BUILT_IN_DEFAULT) continue;
|
|
1231
1351
|
const p = data.profiles[name];
|
|
1232
1352
|
debug(`profile sync: syncing '${name}' to desktop`);
|
|
1233
1353
|
syncer.sync(name, p);
|
|
@@ -1240,9 +1360,19 @@ function profileCommand() {
|
|
|
1240
1360
|
}
|
|
1241
1361
|
function useCommand() {
|
|
1242
1362
|
const syncer = createProfileSyncer();
|
|
1243
|
-
return new Command2("use").description("Set a profile as the default").argument("
|
|
1363
|
+
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
1364
|
ensureProfilesFile();
|
|
1245
1365
|
const data = readJson(PROFILES_FILE);
|
|
1366
|
+
if (opts.builtIn) {
|
|
1367
|
+
data.default = BUILT_IN_DEFAULT;
|
|
1368
|
+
writeJson(PROFILES_FILE, data);
|
|
1369
|
+
debug(`use: wrote ${PROFILES_FILE}`);
|
|
1370
|
+
console.log("Default set to built-in official Anthropic models.");
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
if (!name) {
|
|
1374
|
+
throw new Error("Profile name is required. Use --built-in for official Anthropic models.");
|
|
1375
|
+
}
|
|
1246
1376
|
if (!data.profiles[name]) {
|
|
1247
1377
|
throw new Error(`Profile '${name}' not found.`);
|
|
1248
1378
|
}
|
|
@@ -1255,10 +1385,14 @@ function useCommand() {
|
|
|
1255
1385
|
}));
|
|
1256
1386
|
}
|
|
1257
1387
|
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) => {
|
|
1388
|
+
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
1389
|
fixJsonFile(CLAUDE_JSON);
|
|
1260
1390
|
ensureProfilesFile();
|
|
1261
1391
|
const data = readJson(PROFILES_FILE);
|
|
1392
|
+
if (opts.builtIn) {
|
|
1393
|
+
execClaudeBuiltIn(args);
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1262
1396
|
let profileName = "";
|
|
1263
1397
|
let claudeArgs;
|
|
1264
1398
|
if (args.length > 0 && data.profiles[args[0]]) {
|
|
@@ -1268,8 +1402,12 @@ function runCommand() {
|
|
|
1268
1402
|
profileName = data.default || "";
|
|
1269
1403
|
claudeArgs = args;
|
|
1270
1404
|
}
|
|
1405
|
+
if (profileName === BUILT_IN_DEFAULT) {
|
|
1406
|
+
execClaudeBuiltIn(claudeArgs);
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1271
1409
|
if (!profileName) {
|
|
1272
|
-
throw new Error("No default profile set. Use 'cc-hub use <name>' first.");
|
|
1410
|
+
throw new Error("No default profile set. Use 'cc-hub use <name>' or 'cc-hub use --built-in' first.");
|
|
1273
1411
|
}
|
|
1274
1412
|
const p = data.profiles[profileName];
|
|
1275
1413
|
debug(`run: launching claude with profile '${profileName}', args=[${claudeArgs.join(", ")}]`);
|
|
@@ -2025,7 +2163,7 @@ _cc-hub() {
|
|
|
2025
2163
|
local -a commands
|
|
2026
2164
|
commands=(
|
|
2027
2165
|
'profile:Manage Claude CLI profiles'
|
|
2028
|
-
'use:
|
|
2166
|
+
'use:Set a profile as the default'
|
|
2029
2167
|
'run:Launch Claude Code using the default or a specified profile'
|
|
2030
2168
|
'hook:Manage Claude Code hooks in settings.json'
|
|
2031
2169
|
'session:Manage Claude Code sessions'
|
|
@@ -2104,8 +2242,10 @@ _cc-hub() {
|
|
|
2104
2242
|
profile)
|
|
2105
2243
|
if (( CURRENT == 2 )); then
|
|
2106
2244
|
_describe -t profile-subcmds 'profile subcommand' profile_subcmds
|
|
2107
|
-
elif [[ $words[2] == "view" || $words[2] == "remove"
|
|
2245
|
+
elif [[ $words[2] == "view" || $words[2] == "remove" ]]; then
|
|
2108
2246
|
_cc_hub_profiles
|
|
2247
|
+
elif [[ $words[2] == "default" ]]; then
|
|
2248
|
+
_arguments -C -S '--built-in[Use official Anthropic models as default]' '*:profile:_cc_hub_profiles'
|
|
2109
2249
|
elif [[ $words[2] == "rename" ]]; then
|
|
2110
2250
|
if (( CURRENT == 3 )); then
|
|
2111
2251
|
_cc_hub_profiles
|
|
@@ -2137,7 +2277,7 @@ _cc-hub() {
|
|
|
2137
2277
|
fi
|
|
2138
2278
|
;;
|
|
2139
2279
|
use|run)
|
|
2140
|
-
_cc_hub_profiles
|
|
2280
|
+
_arguments -C -S '--built-in[Use official Anthropic models (no custom profile)]' '*:profile:_cc_hub_profiles'
|
|
2141
2281
|
;;
|
|
2142
2282
|
hook)
|
|
2143
2283
|
if (( CURRENT == 2 )); then
|
|
@@ -2160,20 +2300,22 @@ compdef _cc-hub cc-hub
|
|
|
2160
2300
|
`;
|
|
2161
2301
|
|
|
2162
2302
|
// src/complete/bash.ts
|
|
2163
|
-
var BASH_COMPLETION = `_cc-
|
|
2303
|
+
var BASH_COMPLETION = `_cc-hub_profile_names() {
|
|
2164
2304
|
local profiles_file="\${CLAUDE_PROFILES_FILE:-$HOME/.claude/profiles.json}"
|
|
2165
2305
|
if [[ -f "$profiles_file" ]]; then
|
|
2166
|
-
|
|
2167
|
-
names=$(command python3 -c "
|
|
2306
|
+
command python3 -c "
|
|
2168
2307
|
import json
|
|
2169
2308
|
data = json.load(open('$profiles_file'))
|
|
2170
2309
|
for name in data.get('profiles', {}):
|
|
2171
2310
|
print(name)
|
|
2172
|
-
" 2>/dev/null
|
|
2173
|
-
COMPREPLY=($(compgen -W "$names" -- "\${cur}"))
|
|
2311
|
+
" 2>/dev/null
|
|
2174
2312
|
fi
|
|
2175
2313
|
}
|
|
2176
2314
|
|
|
2315
|
+
_cc-hub_profiles() {
|
|
2316
|
+
COMPREPLY=($(compgen -W "$(_cc-hub_profile_names)" -- "\${cur}"))
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2177
2319
|
_cc-hub_models_for_profile() {
|
|
2178
2320
|
local profile_name="$1"
|
|
2179
2321
|
local profiles_file="\${CLAUDE_PROFILES_FILE:-$HOME/.claude/profiles.json}"
|
|
@@ -2222,8 +2364,10 @@ _cc-hub() {
|
|
|
2222
2364
|
profile)
|
|
2223
2365
|
if [[ \${COMP_CWORD} -eq 2 ]]; then
|
|
2224
2366
|
COMPREPLY=($(compgen -W "$profile_subcmds" -- "$cur"))
|
|
2225
|
-
elif [[ "$prev" == "view" || "$prev" == "remove"
|
|
2367
|
+
elif [[ "$prev" == "view" || "$prev" == "remove" ]]; then
|
|
2226
2368
|
_cc-hub_profiles
|
|
2369
|
+
elif [[ "$prev" == "default" ]]; then
|
|
2370
|
+
COMPREPLY=($(compgen -W "--built-in $(_cc-hub_profile_names)" -- "$cur"))
|
|
2227
2371
|
elif [[ "$prev" == "rename" ]]; then
|
|
2228
2372
|
_cc-hub_profiles
|
|
2229
2373
|
elif [[ "$prev" == "profile" ]]; then
|
|
@@ -2249,7 +2393,11 @@ _cc-hub() {
|
|
|
2249
2393
|
fi
|
|
2250
2394
|
;;
|
|
2251
2395
|
use|run)
|
|
2252
|
-
|
|
2396
|
+
if [[ "$prev" == "--built-in" ]]; then
|
|
2397
|
+
:
|
|
2398
|
+
else
|
|
2399
|
+
COMPREPLY=($(compgen -W "--built-in $(_cc-hub_profile_names)" -- "$cur"))
|
|
2400
|
+
fi
|
|
2253
2401
|
;;
|
|
2254
2402
|
hook)
|
|
2255
2403
|
if [[ \${COMP_CWORD} -eq 2 ]]; then
|
|
@@ -2311,6 +2459,10 @@ var POWERSHELL_COMPLETION = `Register-ArgumentCompleter -Native -CommandName cc-
|
|
|
2311
2459
|
$profileSubcmds | ForEach-Object { if ($_ -like "$wordToComplete*") { $_ } }
|
|
2312
2460
|
return
|
|
2313
2461
|
}
|
|
2462
|
+
if ($tokens[2] -eq 'default' -and $tokens.Count -ge 3) {
|
|
2463
|
+
$opts = @('--built-in')
|
|
2464
|
+
$opts | ForEach-Object { if ($_ -like "$wordToComplete*") { $_ } }
|
|
2465
|
+
}
|
|
2314
2466
|
}
|
|
2315
2467
|
'hook' {
|
|
2316
2468
|
if ($tokens.Count -eq 2 -or ($tokens.Count -eq 3 -and $wordToComplete -ne '')) {
|
|
@@ -2330,6 +2482,16 @@ var POWERSHELL_COMPLETION = `Register-ArgumentCompleter -Native -CommandName cc-
|
|
|
2330
2482
|
return
|
|
2331
2483
|
}
|
|
2332
2484
|
}
|
|
2485
|
+
'use' {
|
|
2486
|
+
$opts = @('--built-in')
|
|
2487
|
+
$opts | ForEach-Object { if ($_ -like "$wordToComplete*") { $_ } }
|
|
2488
|
+
return
|
|
2489
|
+
}
|
|
2490
|
+
'run' {
|
|
2491
|
+
$opts = @('--built-in')
|
|
2492
|
+
$opts | ForEach-Object { if ($_ -like "$wordToComplete*") { $_ } }
|
|
2493
|
+
return
|
|
2494
|
+
}
|
|
2333
2495
|
}
|
|
2334
2496
|
}`;
|
|
2335
2497
|
|
|
@@ -2369,6 +2531,7 @@ program.addCommand(hooksCommand());
|
|
|
2369
2531
|
program.addCommand(sessionCommand());
|
|
2370
2532
|
program.addCommand(completionCommand());
|
|
2371
2533
|
program.addCommand(providerCommand());
|
|
2534
|
+
program.addCommand(proxyCommand());
|
|
2372
2535
|
try {
|
|
2373
2536
|
program.parse();
|
|
2374
2537
|
} catch (err) {
|