@yhong91/cpac 0.1.12 → 0.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/README.md CHANGED
@@ -157,7 +157,7 @@ Claude Code 的发现协议不携带 context 信息,claude 前缀的未知模
157
157
  注入 CPA 模型目录:
158
158
 
159
159
  ```bash
160
- cpac inject
160
+ cpac inject [--v2_off] [--v2_models] [--config PATH]
161
161
  ```
162
162
 
163
163
  查看状态:
package/dist/cpac.js CHANGED
@@ -734,6 +734,10 @@ export async function createLoopbackProxy(cpaUrl, apiKey, proxyId, port) {
734
734
  response.end(JSON.stringify({ error: "CPA upstream request failed" }));
735
735
  });
736
736
  request.once("aborted", () => upstream.destroy());
737
+ response.once("close", () => {
738
+ if (!response.writableEnded)
739
+ upstream.destroy();
740
+ });
737
741
  request.pipe(upstream);
738
742
  });
739
743
  server.on("upgrade", (_request, socket) => {
@@ -941,6 +945,11 @@ export async function createClaudeProxy(cpaUrl, apiKey) {
941
945
  if (!response.writableEnded)
942
946
  response.end(JSON.stringify({ error: "CPA upstream request failed" }));
943
947
  });
948
+ request.once("aborted", () => upstream.destroy());
949
+ response.once("close", () => {
950
+ if (!response.writableEnded)
951
+ upstream.destroy();
952
+ });
944
953
  upstream.end(body);
945
954
  });
946
955
  });
@@ -1980,7 +1989,8 @@ export async function runUpgrade(checkOnly) {
1980
1989
  function usage() {
1981
1990
  return [
1982
1991
  "Usage: cpac",
1983
- " cpac <inject|status|restore> [--config PATH]",
1992
+ " cpac inject [--v2_off] [--v2_models] [--config PATH]",
1993
+ " cpac <status|restore> [--config PATH]",
1984
1994
  " cpac proxy [--config PATH]",
1985
1995
  " cpac claude [--config PATH] [--] [claude args...]",
1986
1996
  " cpac opencode [--config PATH] [--] [opencode args...]",
@@ -2166,6 +2176,8 @@ function parseArgs(args) {
2166
2176
  }
2167
2177
  let configPath = defaultConfigPath();
2168
2178
  const positional = [];
2179
+ let v2Off = false;
2180
+ let v2Models = false;
2169
2181
  for (let index = 0; index < args.length; index += 1) {
2170
2182
  if (args[index] === "--config") {
2171
2183
  const value = args[++index];
@@ -2173,6 +2185,12 @@ function parseArgs(args) {
2173
2185
  throw new CPACError("--config requires a path");
2174
2186
  configPath = resolve(expandUserPath(value));
2175
2187
  }
2188
+ else if (args[index] === "--v2_off" || args[index] === "--v2-off") {
2189
+ v2Off = true;
2190
+ }
2191
+ else if (args[index] === "--v2_models" || args[index] === "--v2-models") {
2192
+ v2Models = true;
2193
+ }
2176
2194
  else if (args[index].startsWith("-")) {
2177
2195
  throw new CPACError(`unknown option: ${args[index]}`);
2178
2196
  }
@@ -2183,6 +2201,12 @@ function parseArgs(args) {
2183
2201
  !["inject", "status", "restore", "proxy"].includes(positional[0])) {
2184
2202
  throw new CPACError(usage());
2185
2203
  }
2204
+ if (positional[0] !== "inject" && (v2Off || v2Models)) {
2205
+ throw new CPACError("--v2_off / --v2_models is only valid with inject or install");
2206
+ }
2207
+ if (positional[0] === "inject") {
2208
+ return { command: "inject", configPath, v2Off, v2Models };
2209
+ }
2186
2210
  return {
2187
2211
  command: positional[0],
2188
2212
  configPath,
@@ -2244,8 +2268,16 @@ export async function main(args = process.argv.slice(2)) {
2244
2268
  return await runPi(config, parsed.action);
2245
2269
  if (parsed.command === "kimi")
2246
2270
  return await runKimi(config, parsed.action);
2247
- if (parsed.command === "inject")
2248
- await inject(config);
2271
+ if (parsed.command === "inject") {
2272
+ let injectConfig = config;
2273
+ if (parsed.v2Models) {
2274
+ const picked = await pickSpawnModels(config);
2275
+ saveSpawnModels(parsed.configPath, picked);
2276
+ console.log(`spawn_models saved to ${parsed.configPath}: ${picked.join(", ")}`);
2277
+ injectConfig = { ...config, spawn_models: picked };
2278
+ }
2279
+ await inject(injectConfig, parsed.v2Off);
2280
+ }
2249
2281
  else if (parsed.command === "restore")
2250
2282
  await restore(config);
2251
2283
  else
@@ -5,6 +5,7 @@ const CPA = "__CPA_URL__";
5
5
  const BUILTIN = new Set(["openai", "github-copilot", "xai", "deepseek", "anthropic", "google"]);
6
6
  const VENDOR_PREFIXES = [
7
7
  ["gpt-", "openai"], ["o1-", "openai"], ["o3-", "openai"], ["o4-", "openai"],
8
+ ["gpt-image", "openai"],
8
9
  ["claude-", "anthropic"], ["gemini-", "google"], ["grok-", "xai"],
9
10
  ["deepseek-", "deepseek"], ["glm-", "zhipu"], ["kimi-", "moonshot"],
10
11
  ["mimo-", "xiaomi"], ["doubao-", "volcengine"], ["ark-", "volcengine"],
@@ -21,6 +22,9 @@ const MAX_TOKENS = {
21
22
  "doubao-seed-2.0-lite": 128000, "doubao-seed-2.1-turbo": 128000,
22
23
  };
23
24
  const DEFAULT_MAX_TOKENS = 65536;
25
+ const REASONING_EFFORTS = new Set([
26
+ "minimal", "low", "medium", "high", "xhigh", "max", "ultra",
27
+ ]);
24
28
 
25
29
  function vendorFor(slug) {
26
30
  for (const [prefix, vendor] of VENDOR_PREFIXES) {
@@ -33,6 +37,32 @@ function groupName(vendor) {
33
37
  return BUILTIN.has(vendor) ? vendor + "-cpa" : vendor;
34
38
  }
35
39
 
40
+ function effortsFor(row) {
41
+ const levels = row.supported_reasoning_levels;
42
+ if (!Array.isArray(levels)) return undefined;
43
+ const efforts = levels.flatMap(function (level) {
44
+ if (!level || typeof level !== "object") return [];
45
+ const effort = level.effort;
46
+ return typeof effort === "string" && REASONING_EFFORTS.has(effort) ? [effort] : [];
47
+ });
48
+ return efforts.length > 0 ? efforts : undefined;
49
+ }
50
+
51
+ function thinkingLevelMapFor(efforts) {
52
+ if (!efforts || !efforts.some(function (effort) { return REASONING_EFFORTS.has(effort); })) {
53
+ return undefined;
54
+ }
55
+ const available = new Set(efforts);
56
+ return {
57
+ minimal: available.has("minimal") ? "minimal" : available.has("low") ? "low" : null,
58
+ low: available.has("low") ? "low" : null,
59
+ medium: available.has("medium") ? "medium" : null,
60
+ high: available.has("high") ? "high" : null,
61
+ xhigh: available.has("xhigh") ? "xhigh" : null,
62
+ max: available.has("max") || available.has("ultra") ? "max" : null,
63
+ };
64
+ }
65
+
36
66
  function intField(value) {
37
67
  return typeof value === "number" && Number.isFinite(value) && value > 0
38
68
  ? Math.floor(value) : undefined;
@@ -72,7 +102,12 @@ export default async function (pi) {
72
102
  const slug = typeof m.slug === "string" && m.slug.trim()
73
103
  ? m.slug.trim()
74
104
  : typeof m.id === "string" && m.id.trim() ? m.id.trim() : null;
75
- if (slug) rows.push({ slug: slug, display_name: m.display_name, supported_reasoning_levels: m.supported_reasoning_levels, context_window: m.context_window });
105
+ if (slug) rows.push({
106
+ slug: slug,
107
+ display_name: m.display_name,
108
+ supported_reasoning_levels: m.supported_reasoning_levels,
109
+ context_window: m.context_window,
110
+ });
76
111
  }
77
112
  if (rows.length === 0) {
78
113
  console.error("[cpac] CPA models response has no usable models");
@@ -92,15 +127,19 @@ export default async function (pi) {
92
127
  apiKey: apiKey,
93
128
  api: "openai-responses",
94
129
  models: models.map(function (m) {
95
- return {
130
+ const efforts = effortsFor(m);
131
+ const thinkingLevelMap = thinkingLevelMapFor(efforts);
132
+ const modelObj = {
96
133
  id: m.slug,
97
134
  name: typeof m.display_name === "string" ? m.display_name : m.slug,
98
- reasoning: Array.isArray(m.supported_reasoning_levels) && m.supported_reasoning_levels.length > 0,
135
+ reasoning: efforts === undefined || thinkingLevelMap !== undefined,
99
136
  input: ["text", "image"],
100
137
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
101
138
  contextWindow: intField(m.context_window) || 200000,
102
139
  maxTokens: MAX_TOKENS[m.slug] || DEFAULT_MAX_TOKENS,
103
140
  };
141
+ if (thinkingLevelMap) modelObj.thinkingLevelMap = thinkingLevelMap;
142
+ return modelObj;
104
143
  }),
105
144
  });
106
145
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yhong91/cpac",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "Connect Codex and Claude Code to a remote CLIProxyAPI gateway",
5
5
  "type": "module",
6
6
  "bin": {