@yhong91/cpac 0.1.8 → 0.1.9
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/README.md +5 -1
- package/cpac.example.json +2 -1
- package/dist/cpac.js +74 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -134,14 +134,18 @@ CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST=1
|
|
|
134
134
|
|
|
135
135
|
代理开启 Claude Code 的 gateway model discovery:`/model` 选择器会列出 CPA 目录里的全部模型。claude 系模型直接使用原 id;其它模型以 `claude-cpac--<模型名>` 别名出现(例如 `claude-cpac--gpt-5.6-sol`),请求发出时由代理还原为真实模型名。
|
|
136
136
|
|
|
137
|
-
启动时还会把模型槽位(`ANTHROPIC_DEFAULT_OPUS/SONNET/HAIKU_MODEL` 及 `ANTHROPIC_SMALL_FAST_MODEL`)指向目录里的模型,避免 haiku/subagent 任务打到网关不认识的默认 id。默认自动选择(claude
|
|
137
|
+
启动时还会把模型槽位(`ANTHROPIC_DEFAULT_OPUS/SONNET/HAIKU_MODEL` 及 `ANTHROPIC_SMALL_FAST_MODEL`)指向目录里的模型,避免 haiku/subagent 任务打到网关不认识的默认 id。默认自动选择(claude 系优先:按家族名匹配 opus/sonnet/haiku,否则最大窗口给 opus/sonnet、最小窗口给 haiku),可用命令覆盖:
|
|
138
138
|
|
|
139
139
|
```sh
|
|
140
140
|
cpac claude-models # 查看当前覆盖
|
|
141
141
|
cpac claude-models --opus claude-opus-4-8 --haiku glm-5.3 # 设置/合并
|
|
142
|
+
cpac claude-models --haiku # 不带值:arrow-key 下拉选择
|
|
143
|
+
cpac claude-models --classifier claude-sonnet-4-6 # Auto mode 审批分类器模型
|
|
142
144
|
cpac claude-models --reset # 恢复自动选择
|
|
143
145
|
```
|
|
144
146
|
|
|
147
|
+
所有设定写入配置文件持久缓存,每次 `cpac claude` 启动自动生效。`--classifier` 对应 `CLAUDE_CODE_AUTO_MODE_MODEL`(Auto mode 的审批分类器,默认跟随 Claude Code 内置选择);不设置则不注入。
|
|
148
|
+
|
|
145
149
|
你自己 export 同名环境变量时以你为准。
|
|
146
150
|
|
|
147
151
|
Claude Code 的发现协议不携带 context 信息,claude 前缀的未知模型默认按 200K 记账,且 `CLAUDE_CODE_MAX_CONTEXT_TOKENS` 对 claude 前缀的名字不生效。cpac 采用与 opencodex 相同的机制:目录里窗口足够大的别名模型会带 `[1m]` 标记出现在选择器中(Claude Code 按 1M 记账,发请求前自己剥掉标记),同时 `cpac claude` 注入 `CLAUDE_CODE_AUTO_COMPACT_WINDOW`(默认 350K,与 opencodex 一致,接受范围 100K–1M)作为压缩阈值。你自己 export 该变量时以你为准。claude 系模型如需 1M 窗口,在模型名后加 `[1m]`(如 `cpac claude --model claude-sonnet-4-6[1m]`)。
|
package/cpac.example.json
CHANGED
package/dist/cpac.js
CHANGED
|
@@ -25,7 +25,7 @@ const CONFIG_KEYS = new Set([
|
|
|
25
25
|
// Codex advertises only the first 5 picker-visible catalog models as
|
|
26
26
|
// spawn_agent overrides (codex-rs MAX_SPAWN_AGENT_MODEL_OVERRIDES).
|
|
27
27
|
const MAX_SPAWN_MODELS = 5;
|
|
28
|
-
const CLAUDE_SLOT_KEYS = ["opus", "sonnet", "haiku"];
|
|
28
|
+
const CLAUDE_SLOT_KEYS = ["opus", "sonnet", "haiku", "classifier"];
|
|
29
29
|
const REQUIRED_STATE_KEYS = new Set([
|
|
30
30
|
"config_path",
|
|
31
31
|
"config_existed",
|
|
@@ -343,6 +343,12 @@ async function checkboxPicker(title, items, max) {
|
|
|
343
343
|
else if (key === "\x1b[B")
|
|
344
344
|
cursor = (cursor + 1) % items.length;
|
|
345
345
|
else if (key === " ") {
|
|
346
|
+
if (max === 1) {
|
|
347
|
+
selected.clear();
|
|
348
|
+
selected.add(cursor);
|
|
349
|
+
finish();
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
346
352
|
if (selected.has(cursor))
|
|
347
353
|
selected.delete(cursor);
|
|
348
354
|
else if (selected.size < max)
|
|
@@ -768,8 +774,10 @@ function shouldMarkOneMillion(id, window, compactWindow) {
|
|
|
768
774
|
return true;
|
|
769
775
|
return compactWindow !== undefined && window > 200_000 && window >= compactWindow;
|
|
770
776
|
}
|
|
771
|
-
// Auto tier defaults: prefer claude-family models;
|
|
772
|
-
// opus/sonnet
|
|
777
|
+
// Auto tier defaults: prefer claude-family models; within the pool match by
|
|
778
|
+
// family keyword first (opus/sonnet/haiku) so equal context windows don't
|
|
779
|
+
// collapse every slot onto the first catalog entry, then fall back to
|
|
780
|
+
// biggest-window (opus/sonnet) and smallest-window (haiku).
|
|
773
781
|
function defaultClaudeSlots(models) {
|
|
774
782
|
if (!models.length)
|
|
775
783
|
return undefined;
|
|
@@ -779,10 +787,11 @@ function defaultClaudeSlots(models) {
|
|
|
779
787
|
const smallest = pool
|
|
780
788
|
.filter((model) => model.window > 0)
|
|
781
789
|
.sort((a, b) => a.window - b.window)[0];
|
|
790
|
+
const family = (keyword) => pool.find((model) => model.id.includes(keyword));
|
|
782
791
|
return {
|
|
783
|
-
opus: biggest.id,
|
|
784
|
-
sonnet: biggest.id,
|
|
785
|
-
haiku: (smallest ?? biggest).id,
|
|
792
|
+
opus: (family("opus") ?? biggest).id,
|
|
793
|
+
sonnet: (family("sonnet") ?? biggest).id,
|
|
794
|
+
haiku: (family("haiku") ?? smallest ?? biggest).id,
|
|
786
795
|
};
|
|
787
796
|
}
|
|
788
797
|
// Resolve the tier-model env values: config overrides win over catalog
|
|
@@ -1418,7 +1427,22 @@ async function guide(config) {
|
|
|
1418
1427
|
console.log(`\nSaved ${config.api_key_env} to ${profile}. Open a new terminal or run:\n source ${profile}`);
|
|
1419
1428
|
return 0;
|
|
1420
1429
|
}
|
|
1421
|
-
export function runClaudeModels(parsed) {
|
|
1430
|
+
export async function runClaudeModels(parsed) {
|
|
1431
|
+
if (parsed.pick.length) {
|
|
1432
|
+
const config = loadConfig(parsed.configPath);
|
|
1433
|
+
const apiKey = process.env[config.api_key_env]?.trim();
|
|
1434
|
+
if (!apiKey)
|
|
1435
|
+
throw new CPACError(`environment variable ${config.api_key_env} is not set`);
|
|
1436
|
+
const catalog = await fetchCatalog(config.cpa_url, apiKey);
|
|
1437
|
+
const document = JSON.parse(new TextDecoder().decode(catalog.bytes));
|
|
1438
|
+
const slugs = document.models.map((model) => model.slug);
|
|
1439
|
+
for (const slot of parsed.pick) {
|
|
1440
|
+
const [picked] = await checkboxPicker(`Select ${slot} model`, slugs, 1);
|
|
1441
|
+
if (!picked)
|
|
1442
|
+
throw new CPACError("no model selected");
|
|
1443
|
+
parsed.models[slot] = picked;
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1422
1446
|
if (!parsed.reset && !Object.keys(parsed.models).length) {
|
|
1423
1447
|
const config = loadConfig(parsed.configPath);
|
|
1424
1448
|
console.log(config.claude_models
|
|
@@ -1499,18 +1523,20 @@ export async function runClaude(config, args, executable = "claude") {
|
|
|
1499
1523
|
if (compactWindow !== undefined && !(Number(process.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW) > 0)) {
|
|
1500
1524
|
env.CLAUDE_CODE_AUTO_COMPACT_WINDOW = String(compactWindow);
|
|
1501
1525
|
}
|
|
1526
|
+
const setSlot = (name, value) => {
|
|
1527
|
+
if (process.env[name] === undefined)
|
|
1528
|
+
env[name] = value;
|
|
1529
|
+
};
|
|
1502
1530
|
if (slots) {
|
|
1503
1531
|
// Route haiku/subagent tier traffic at catalog models instead of the
|
|
1504
1532
|
// stock claude-haiku-* ids the gateway does not carry.
|
|
1505
|
-
const setSlot = (name, value) => {
|
|
1506
|
-
if (process.env[name] === undefined)
|
|
1507
|
-
env[name] = value;
|
|
1508
|
-
};
|
|
1509
1533
|
setSlot("ANTHROPIC_DEFAULT_OPUS_MODEL", slots.opus);
|
|
1510
1534
|
setSlot("ANTHROPIC_DEFAULT_SONNET_MODEL", slots.sonnet);
|
|
1511
1535
|
setSlot("ANTHROPIC_DEFAULT_HAIKU_MODEL", slots.haiku);
|
|
1512
1536
|
setSlot("ANTHROPIC_SMALL_FAST_MODEL", slots.haiku);
|
|
1513
1537
|
}
|
|
1538
|
+
if (config.claude_models?.classifier)
|
|
1539
|
+
setSlot("CLAUDE_CODE_AUTO_MODE_MODEL", config.claude_models.classifier);
|
|
1514
1540
|
delete env.ANTHROPIC_API_KEY;
|
|
1515
1541
|
delete env.CLAUDE_CODE_USE_ANTHROPIC_AWS;
|
|
1516
1542
|
delete env.CLAUDE_CODE_USE_BEDROCK;
|
|
@@ -1919,7 +1945,7 @@ function usage() {
|
|
|
1919
1945
|
" cpac <inject|status|restore> [--config PATH]",
|
|
1920
1946
|
" cpac proxy [--config PATH]",
|
|
1921
1947
|
" cpac claude [--config PATH] [--] [claude args...]",
|
|
1922
|
-
" cpac claude-models [--opus M] [--sonnet M] [--haiku M] [--reset] [--config PATH]",
|
|
1948
|
+
" cpac claude-models [--opus [M]] [--sonnet [M]] [--haiku [M]] [--classifier [M]] [--reset] [--config PATH]",
|
|
1923
1949
|
" cpac pi <install|uninstall|status> [--config PATH]",
|
|
1924
1950
|
" cpac kimi <install|uninstall|status> [--config PATH]",
|
|
1925
1951
|
" cpac detect [--json] [--home PATH]",
|
|
@@ -2009,8 +2035,27 @@ function parseArgs(args) {
|
|
|
2009
2035
|
return { command: args[0], configPath, action };
|
|
2010
2036
|
}
|
|
2011
2037
|
if (args[0] === "claude-models") {
|
|
2038
|
+
if (args.includes("-h") || args.includes("--help")) {
|
|
2039
|
+
console.log([
|
|
2040
|
+
"Usage: cpac claude-models [--opus [M]] [--sonnet [M]] [--haiku [M]] [--classifier [M]] [--reset] [--config PATH]",
|
|
2041
|
+
"",
|
|
2042
|
+
"Sets the model Claude Code uses per task tier. Saved to the config file",
|
|
2043
|
+
"and applied on every `cpac claude` launch:",
|
|
2044
|
+
" --opus heavy reasoning / planning fallback tier (ANTHROPIC_DEFAULT_OPUS_MODEL)",
|
|
2045
|
+
" --sonnet main conversation default tier (ANTHROPIC_DEFAULT_SONNET_MODEL)",
|
|
2046
|
+
" --haiku high-frequency small tasks: titles, summaries, background",
|
|
2047
|
+
" (ANTHROPIC_DEFAULT_HAIKU_MODEL + ANTHROPIC_SMALL_FAST_MODEL)",
|
|
2048
|
+
" --classifier auto-mode approval classifier (CLAUDE_CODE_AUTO_MODE_MODEL);",
|
|
2049
|
+
" unset follows Claude Code's built-in default",
|
|
2050
|
+
"",
|
|
2051
|
+
"A slot without a value opens an arrow-key picker over the CPA catalog;",
|
|
2052
|
+
"--reset clears all overrides back to catalog auto-selection.",
|
|
2053
|
+
].join("\n"));
|
|
2054
|
+
return null;
|
|
2055
|
+
}
|
|
2012
2056
|
let configPath = defaultConfigPath();
|
|
2013
2057
|
const models = {};
|
|
2058
|
+
const pick = [];
|
|
2014
2059
|
let reset = false;
|
|
2015
2060
|
for (let index = 1; index < args.length; index += 1) {
|
|
2016
2061
|
const arg = args[index];
|
|
@@ -2023,17 +2068,27 @@ function parseArgs(args) {
|
|
|
2023
2068
|
else if (arg === "--reset") {
|
|
2024
2069
|
reset = true;
|
|
2025
2070
|
}
|
|
2026
|
-
else if (arg === "--opus" ||
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2071
|
+
else if (arg === "--opus" ||
|
|
2072
|
+
arg === "--sonnet" ||
|
|
2073
|
+
arg === "--haiku" ||
|
|
2074
|
+
arg === "--classifier") {
|
|
2075
|
+
const slot = arg.slice(2);
|
|
2076
|
+
const value = args[index + 1];
|
|
2077
|
+
if (value !== undefined && !value.startsWith("-")) {
|
|
2078
|
+
index += 1;
|
|
2079
|
+
if (!value.trim())
|
|
2080
|
+
throw new CPACError(`${arg} requires a model id`);
|
|
2081
|
+
models[slot] = value.trim();
|
|
2082
|
+
}
|
|
2083
|
+
else {
|
|
2084
|
+
pick.push(slot);
|
|
2085
|
+
}
|
|
2031
2086
|
}
|
|
2032
2087
|
else {
|
|
2033
2088
|
throw new CPACError(`unknown option: ${arg}`);
|
|
2034
2089
|
}
|
|
2035
2090
|
}
|
|
2036
|
-
return { command: "claude-models", configPath, models, reset };
|
|
2091
|
+
return { command: "claude-models", configPath, models, pick, reset };
|
|
2037
2092
|
}
|
|
2038
2093
|
if (args[0] === "claude") {
|
|
2039
2094
|
let configPath = defaultConfigPath();
|
|
@@ -2124,7 +2179,7 @@ export async function main(args = process.argv.slice(2)) {
|
|
|
2124
2179
|
if (parsed.command === "claude")
|
|
2125
2180
|
return await runClaude(config, parsed.args);
|
|
2126
2181
|
if (parsed.command === "claude-models")
|
|
2127
|
-
return runClaudeModels(parsed);
|
|
2182
|
+
return await runClaudeModels(parsed);
|
|
2128
2183
|
if (parsed.command === "proxy")
|
|
2129
2184
|
return await runProxy(config);
|
|
2130
2185
|
if (parsed.command === "pi")
|