@yhong91/cpac 0.2.0 → 0.2.2

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
@@ -363,11 +363,22 @@ cpac restore cline
363
363
 
364
364
  配置目录尊重 `CLINE_DATA_DIR`(否则 `~/.cline/data`,CLI 与 Desktop 共享)。首次写入前若 `providers.json` 已存在,会在 `state_dir/cline/providers.json` 保留原始快照。
365
365
 
366
+ ### 手动打开 5h 窗口
367
+
368
+ 本机手动发一条最小请求,用来激活当前 5h 额度。不定时,也不查配额。定时仍在 VPS 的 `scripts/quota-heartbeat.py`。
369
+
370
+ ```bash
371
+ cpac ping codex # 目录里匹配 gpt-*-luna 的一条
372
+ cpac ping agy # gemini-3.8-flash 和 claude-sonnet-4-6,各一条
373
+ ```
374
+
375
+ 用 `CPA_API_KEY`。日志只打模型、HTTP 状态和不含 key 的 `x-cpa-*` trace。CPA 按账号轮询,一次只会落在一个账号上。
376
+
366
377
  ## 环境变量
367
378
 
368
379
  | 变量 | 默认值 | 作用 |
369
380
  | --- | --- | --- |
370
- | `CPA_API_KEY` | 无 | CPA Bearer key;可由 `cpac` 首次引导写入 shell 启动文件 |
381
+ | `CPA_API_KEY` | 无 | CPA Bearer key;可由 `cpac` 首次引导写入 shell 启动文件;`cpac ping` 也用它 |
371
382
  | `CPA_BASE_URL` | `http://124.223.178.52:8317` | 覆盖内置 CPA 地址 |
372
383
  | `PI_CODING_AGENT_DIR` | `~/.pi/agent` | 覆盖 Pi agent 目录(影响 `models.json` 写入位置) |
373
384
  | `KIMI_CODE_HOME` | `~/.kimi-code` | 覆盖 Kimi Code 目录(影响 `cpac install kimi` 写入位置) |
package/dist/cpac.js CHANGED
@@ -13,6 +13,7 @@ import { isZedConfigInstalled } from "./targets/zed.js";
13
13
  import { isHermesConfigInstalled } from "./targets/hermes.js";
14
14
  import { CLINE_PROVIDER_ID, isClineConfigInstalled } from "./targets/cline.js";
15
15
  import { defaultConfigPath, loadConfig, originalBytes, pickSpawnModels, readState, saveSpawnModels, stateProxy, catalogMaxContextLive, catalogModelId, catalogModelRows, catalogSlugs, fetchCatalog, } from "./config.js";
16
+ import { runPing } from "./ping.js";
16
17
  import { proxyIsHealthy, runProxyChild } from "./proxy.js";
17
18
  import { CPACError, expandUserPath, promptSecret, saveApiKeyExport, shellProfile, } from "./util.js";
18
19
  export { PROVIDER, loadConfig, saveSpawnModels, saveCodexSetup, liftContextWindows, reorderCatalog, sanitizeCatalogModalities, stampMultiAgentVersion, catalogMaxContextLive, readState, stateProxy, } from "./config.js";
@@ -102,7 +103,13 @@ async function listModels(config, json) {
102
103
  console.log(slug);
103
104
  return 0;
104
105
  }
105
- const document = JSON.parse(new TextDecoder().decode(catalog.bytes));
106
+ let document;
107
+ try {
108
+ document = JSON.parse(new TextDecoder().decode(catalog.bytes));
109
+ }
110
+ catch {
111
+ throw new CPACError("CPA catalog is not valid JSON");
112
+ }
106
113
  const models = (catalogModelRows(document) ?? []).flatMap((row) => {
107
114
  const slug = catalogModelId(row);
108
115
  if (!slug)
@@ -127,7 +134,7 @@ async function guide(config) {
127
134
  cpac detect Show supported targets and install status
128
135
  cpac install [codex|pi|kimi|grok|zed|hermes|cline...] Install CPA into detected targets, or named agents
129
136
  cpac sync Refresh installed Codex/Pi/Kimi/Grok/Zed/Hermes/Cline injections from CPA
130
- cpac uninstall Detach all agents, then remove the cpac package (confirms first)\n cpac upgrade Update cpac from npm and sync installed agents\n cpac models List remote CPA catalog models\n cpac --help Show command usage`);
137
+ cpac uninstall Detach all agents, then remove the cpac package (confirms first)\n cpac upgrade Update cpac from npm and sync installed agents\n cpac models List remote CPA catalog models\n cpac ping codex|agy Send one request to open that 5h window\n cpac --help Show command usage`);
131
138
  if (process.env[config.api_key_env]?.trim())
132
139
  return 0;
133
140
  const apiKey = await promptSecret(config.api_key_env);
@@ -164,6 +171,7 @@ function usage() {
164
171
  cmd("cpac restore <agent...>", "detach injected agents"),
165
172
  cmd("cpac uninstall", "detach all agents, then remove the cpac package"),
166
173
  cmd("cpac upgrade", "update cpac from npm and sync installed agents"),
174
+ cmd("cpac ping <codex|agy>", "send one request to open that 5h window"),
167
175
  "",
168
176
  "Positionals:",
169
177
  helpRow("agent", "codex, kimi, grok, pi, zed, hermes, cline, or claude (claude is launch-only)", "", 18),
@@ -227,6 +235,31 @@ function parseArgs(args) {
227
235
  if (args.length === 0) {
228
236
  return { command: "guide", configPath: defaultConfigPath() };
229
237
  }
238
+ if (args[0] === "ping") {
239
+ let configPath = defaultConfigPath();
240
+ let target = "";
241
+ for (let index = 1; index < args.length; index++) {
242
+ const arg = args[index];
243
+ if (arg === "-h" || arg === "--help") {
244
+ console.log(usage());
245
+ return null;
246
+ }
247
+ if (arg === "--config") {
248
+ const value = args[++index];
249
+ if (!value)
250
+ throw new CPACError("--config requires a path");
251
+ configPath = resolve(expandUserPath(value));
252
+ }
253
+ else if (!arg.startsWith("-") && !target)
254
+ target = arg;
255
+ else
256
+ throw new CPACError(`unknown option: ${arg}`);
257
+ }
258
+ if (target !== "codex" && target !== "agy") {
259
+ throw new CPACError("cpac ping requires codex or agy");
260
+ }
261
+ return { command: "ping", configPath, target };
262
+ }
230
263
  if (args[0] === "detect" ||
231
264
  args[0] === "install" ||
232
265
  args[0] === "sync" ||
@@ -663,6 +696,8 @@ export async function main(args = process.argv.slice(2)) {
663
696
  }
664
697
  if (parsed.command === "upgrade")
665
698
  return await runUpgrade(config, parsed.check);
699
+ if (parsed.command === "ping")
700
+ return await runPing(config, parsed.target);
666
701
  if (parsed.command === "claude")
667
702
  return await runClaude(config, parsed.args);
668
703
  if (parsed.command === "claude-config") {
package/dist/ping.js ADDED
@@ -0,0 +1,67 @@
1
+ import { apiBase, catalogSlugs, fetchCatalog } from "./config.js";
2
+ import { CPACError } from "./util.js";
3
+ const LUNA = /^gpt-.+-luna$/;
4
+ export const PING_MODELS = {
5
+ agy: ["gemini-3.8-flash", "claude-sonnet-4-6"],
6
+ };
7
+ export function matchLuna(slugs) {
8
+ const model = slugs.filter((slug) => LUNA.test(slug)).sort().at(-1);
9
+ if (!model)
10
+ throw new CPACError("no catalog model matches gpt-*-luna");
11
+ return model;
12
+ }
13
+ const EMAIL_RE = /[A-Za-z0-9._%+\-]+@/g;
14
+ function redact(value) {
15
+ return String(value).replace(EMAIL_RE, "*@");
16
+ }
17
+ export async function runPing(config, target, deps = {}) {
18
+ const env = deps.env ?? process.env;
19
+ const log = deps.log ?? ((line) => console.log(line));
20
+ const fetchImpl = deps.fetch ?? fetch;
21
+ const apiKey = env[config.api_key_env]?.trim();
22
+ if (!apiKey)
23
+ throw new CPACError(`environment variable ${config.api_key_env} is not set`);
24
+ const url = `${apiBase(config.cpa_url)}/chat/completions`;
25
+ const models = target === "codex"
26
+ ? [matchLuna(catalogSlugs((await fetchCatalog(config.cpa_url, apiKey)).bytes))]
27
+ : PING_MODELS[target];
28
+ let failed = 0;
29
+ for (const model of models) {
30
+ let response;
31
+ try {
32
+ response = await fetchImpl(url, {
33
+ method: "POST",
34
+ headers: {
35
+ Accept: "application/json",
36
+ Authorization: `Bearer ${apiKey}`,
37
+ "Content-Type": "application/json",
38
+ },
39
+ body: JSON.stringify({
40
+ model,
41
+ messages: [{ role: "user", content: "hi" }],
42
+ max_tokens: 16,
43
+ stream: false,
44
+ }),
45
+ redirect: "error",
46
+ signal: AbortSignal.timeout(90_000),
47
+ });
48
+ }
49
+ catch (error) {
50
+ log(`${model} failed: ${error instanceof Error ? error.message : String(error)}`);
51
+ failed += 1;
52
+ continue;
53
+ }
54
+ const text = await response.text();
55
+ const trace = [];
56
+ response.headers.forEach((value, key) => {
57
+ const name = key.toLowerCase();
58
+ if (name.startsWith("x-cpa-") && !name.includes("key"))
59
+ trace.push(`${name}=${redact(value)}`);
60
+ });
61
+ const detail = response.status === 200 ? "" : ` ${redact(text).replaceAll(apiKey, "***").slice(0, 180)}`;
62
+ log(`${model} HTTP ${response.status}${trace.length ? ` ${trace.join(" ")}` : ""}${detail}`);
63
+ if (response.status !== 200)
64
+ failed += 1;
65
+ }
66
+ return failed ? 1 : 0;
67
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yhong91/cpac",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Connect Codex and Claude Code to a remote CLIProxyAPI gateway",
5
5
  "type": "module",
6
6
  "bin": {