@yhong91/cpac 0.1.28 → 0.1.30
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 +9 -4
- package/dist/agents.js +2 -2
- package/dist/config.js +12 -1
- package/dist/pi-extension.template +6 -6
- package/dist/targets/codex.js +3 -2
- package/dist/targets/grok.js +1 -1
- package/dist/targets/kimi.js +1 -1
- package/dist/targets/pi.js +25 -7
- package/dist/util.js +23 -8
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -248,7 +248,7 @@ Codex App 的长驻 `app-server` 可能缓存旧目录。`inject` 会删除 `mod
|
|
|
248
248
|
|
|
249
249
|
注入命令可选 `--max_context`(`cpac inject --max_context` 或 `cpac install --target codex --max_context`,默认不带):把 catalog 中 `max_context_window` 高于 `context_window` 的模型提升到上限,并把 `auto_compact_token_limit` 设为上限的 90%。Codex 把 `context_window` 当输入预算而非展示标签,上游默认保留保守运营值(如 GPT-5.6 家族 272k),带上该参数后可用到实测上限(约 921k,opencodex 实测);90% 压缩线确保在硬上限前触发 auto-compact。不带参数注入的仍是原本上下文的目录。该参数仅限 codex,与其他 target 组合使用会直接报错。
|
|
250
250
|
|
|
251
|
-
第一次注入时,原文件按原始 bytes 备份到 `state_dir/config.toml
|
|
251
|
+
第一次注入时,原文件按原始 bytes 备份到 `state_dir/codex/config.toml`。重复注入不会覆盖首次备份,并会保留 CPAC 管理字段之外的用户编辑。`restore`:若 `config.toml` 相对首次注入未改,按原 mode 逐字节还原备份;若用户在注入期间改过文件,只撤掉 CPAC 写入的 catalog、url 和标记。原先没有 `config.toml` 且撤掉后为空则删除文件。关闭 CPAC loopback 代理。
|
|
252
252
|
|
|
253
253
|
loopback 代理是 detached 用户进程,不安装系统服务。机器重启或进程意外退出后,`cpac status` 会报告 `loopback proxy stopped`;重新执行 `cpac inject` 即可恢复。
|
|
254
254
|
|
|
@@ -265,6 +265,11 @@ cpac proxy
|
|
|
265
265
|
```text
|
|
266
266
|
Codex 配置:$CODEX_HOME/config.toml,未设置 CODEX_HOME 时为 ~/.codex/config.toml
|
|
267
267
|
CPAC state:~/.cpac
|
|
268
|
+
各 agent 配置备份:~/.cpac/<agent>/<原文件名>
|
|
269
|
+
Codex ~/.cpac/codex/config.toml
|
|
270
|
+
Pi ~/.cpac/pi/pi-cpac.ts
|
|
271
|
+
Kimi ~/.cpac/kimi/config.toml
|
|
272
|
+
Grok ~/.cpac/grok/config.toml
|
|
268
273
|
```
|
|
269
274
|
|
|
270
275
|
为防止误删或覆盖,`state_dir` 不能是文件系统根目录、用户 home、系统临时目录,也不能包含 Codex 配置文件。
|
|
@@ -285,7 +290,7 @@ cpac opencode [--] [opencode args...]
|
|
|
285
290
|
cpac pi install
|
|
286
291
|
```
|
|
287
292
|
|
|
288
|
-
它将生成 `cpac.ts` 写入 `~/.pi/agent/extensions/`,Pi 启动时自动加载并从 CPA 动态注册模型目录。卸载:
|
|
293
|
+
它将生成 `pi-cpac.ts` 写入 `~/.pi/agent/extensions/`,Pi 启动时自动加载并从 CPA 动态注册模型目录。卸载:
|
|
289
294
|
|
|
290
295
|
```bash
|
|
291
296
|
cpac restore --target pi
|
|
@@ -307,7 +312,7 @@ cpac kimi install
|
|
|
307
312
|
cpac restore --target kimi
|
|
308
313
|
```
|
|
309
314
|
|
|
310
|
-
Kimi Code 配置目录尊重 `KIMI_CODE_HOME` 环境变量。首次写入前若 `config.toml`
|
|
315
|
+
Kimi Code 配置目录尊重 `KIMI_CODE_HOME` 环境变量。首次写入前若 `config.toml` 已存在,会在 `state_dir/kimi/config.toml` 保留一份原始快照(只存最早版本),万一文件损坏可手动复制回去。
|
|
311
316
|
|
|
312
317
|
## 环境变量
|
|
313
318
|
|
|
@@ -326,7 +331,7 @@ CPAC CLI 可临时覆盖 CPA 地址:
|
|
|
326
331
|
CPA_BASE_URL='https://cpa.example.com' cpac claude
|
|
327
332
|
```
|
|
328
333
|
|
|
329
|
-
`pi-
|
|
334
|
+
`pi-cpac.ts` 插件固定连接配置指定的 CPA 地址,并且只从 Pi 进程环境读取 `CPA_API_KEY`;不读取用户 home 中的私有文件。
|
|
330
335
|
|
|
331
336
|
## 可选 JSON 配置
|
|
332
337
|
|
package/dist/agents.js
CHANGED
|
@@ -209,14 +209,14 @@ const TARGETS = [
|
|
|
209
209
|
version: () => detectClientVersion("pi"),
|
|
210
210
|
install: async (config, dryRun) => {
|
|
211
211
|
if (dryRun) {
|
|
212
|
-
console.log(`would write Pi extension: ${join(piExtensionsDir(), "cpac.ts")}`);
|
|
212
|
+
console.log(`would write Pi extension: ${join(piExtensionsDir(), "pi-cpac.ts")}`);
|
|
213
213
|
return;
|
|
214
214
|
}
|
|
215
215
|
await installPiExtension(config);
|
|
216
216
|
},
|
|
217
217
|
uninstall: async (_config, dryRun) => {
|
|
218
218
|
if (dryRun) {
|
|
219
|
-
console.log(`would remove Pi extension: ${join(piExtensionsDir(), "cpac.ts")}`);
|
|
219
|
+
console.log(`would remove Pi extension: ${join(piExtensionsDir(), "pi-cpac.ts")}`);
|
|
220
220
|
return;
|
|
221
221
|
}
|
|
222
222
|
await uninstallPiExtension();
|
package/dist/config.js
CHANGED
|
@@ -452,7 +452,9 @@ export function originalBytes(stateDir, state, expectedPath) {
|
|
|
452
452
|
if (!state.config_existed)
|
|
453
453
|
return new Uint8Array();
|
|
454
454
|
try {
|
|
455
|
-
|
|
455
|
+
const next = join(stateDir, "codex", "config.toml");
|
|
456
|
+
const prev = join(stateDir, "config.toml.backup");
|
|
457
|
+
return readFileSync(existsSync(next) ? next : prev);
|
|
456
458
|
}
|
|
457
459
|
catch (error) {
|
|
458
460
|
throw new CPACError(`cannot read original config backup: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -461,6 +463,15 @@ export function originalBytes(stateDir, state, expectedPath) {
|
|
|
461
463
|
export function cleanupStateFiles(stateDir) {
|
|
462
464
|
for (const name of STATE_FILES)
|
|
463
465
|
rmSync(join(stateDir, name), { force: true });
|
|
466
|
+
rmSync(join(stateDir, "codex", "config.toml"), { force: true });
|
|
467
|
+
try {
|
|
468
|
+
rmdirSync(join(stateDir, "codex"));
|
|
469
|
+
}
|
|
470
|
+
catch (error) {
|
|
471
|
+
const code = error.code;
|
|
472
|
+
if (code !== "ENOENT" && code !== "ENOTEMPTY" && code !== "EEXIST")
|
|
473
|
+
throw error;
|
|
474
|
+
}
|
|
464
475
|
try {
|
|
465
476
|
rmdirSync(stateDir);
|
|
466
477
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// CPAC
|
|
1
|
+
// Pi CPAC extension: register CPA models as Pi providers
|
|
2
2
|
// Installed by: cpac pi install
|
|
3
3
|
|
|
4
4
|
const CPA = "__CPA_URL__";
|
|
@@ -71,7 +71,7 @@ function intField(value) {
|
|
|
71
71
|
export default async function (pi) {
|
|
72
72
|
const apiKey = (process.env.CPA_API_KEY || "").trim();
|
|
73
73
|
if (!apiKey) {
|
|
74
|
-
console.error("[cpac] CPA_API_KEY not set; skipping CPA providers");
|
|
74
|
+
console.error("[pi-cpac] CPA_API_KEY not set; skipping CPA providers");
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
let payload;
|
|
@@ -81,19 +81,19 @@ export default async function (pi) {
|
|
|
81
81
|
signal: AbortSignal.timeout(10000),
|
|
82
82
|
});
|
|
83
83
|
if (!res.ok) {
|
|
84
|
-
console.error("[cpac] CPA models request failed: HTTP " + res.status);
|
|
84
|
+
console.error("[pi-cpac] CPA models request failed: HTTP " + res.status);
|
|
85
85
|
return;
|
|
86
86
|
}
|
|
87
87
|
payload = await res.json();
|
|
88
88
|
} catch (error) {
|
|
89
|
-
console.error("[cpac] CPA models request failed: " + (error && error.message || error));
|
|
89
|
+
console.error("[pi-cpac] CPA models request failed: " + (error && error.message || error));
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
92
92
|
const source = payload && Array.isArray(payload.models)
|
|
93
93
|
? payload.models
|
|
94
94
|
: payload && Array.isArray(payload.data) ? payload.data : null;
|
|
95
95
|
if (!source) {
|
|
96
|
-
console.error("[cpac] CPA models response has no models list");
|
|
96
|
+
console.error("[pi-cpac] CPA models response has no models list");
|
|
97
97
|
return;
|
|
98
98
|
}
|
|
99
99
|
const rows = [];
|
|
@@ -110,7 +110,7 @@ export default async function (pi) {
|
|
|
110
110
|
});
|
|
111
111
|
}
|
|
112
112
|
if (rows.length === 0) {
|
|
113
|
-
console.error("[cpac] CPA models response has no usable models");
|
|
113
|
+
console.error("[pi-cpac] CPA models response has no usable models");
|
|
114
114
|
return;
|
|
115
115
|
}
|
|
116
116
|
const groups = new Map();
|
package/dist/targets/codex.js
CHANGED
|
@@ -286,7 +286,8 @@ export async function inject(config, v2Off = false, maxContext = false) {
|
|
|
286
286
|
catalog.bytes = liftContextWindows(catalog.bytes);
|
|
287
287
|
const stateDirExisted = existsSync(config.state_dir);
|
|
288
288
|
if (!state &&
|
|
289
|
-
STATE_FILES.some((name) => existsSync(join(config.state_dir, name)))
|
|
289
|
+
(STATE_FILES.some((name) => existsSync(join(config.state_dir, name))) ||
|
|
290
|
+
existsSync(join(config.state_dir, "codex", "config.toml")))) {
|
|
290
291
|
throw new CPACError("state_dir contains CPAC files without a valid state; refusing to overwrite them");
|
|
291
292
|
}
|
|
292
293
|
const fingerprint = proxyFingerprint(config, apiKey);
|
|
@@ -350,7 +351,7 @@ export async function inject(config, v2Off = false, maxContext = false) {
|
|
|
350
351
|
let configWritten = false;
|
|
351
352
|
try {
|
|
352
353
|
if (existed)
|
|
353
|
-
atomicWrite(join(config.state_dir, "config.toml
|
|
354
|
+
atomicWrite(join(config.state_dir, "codex", "config.toml"), original);
|
|
354
355
|
atomicWrite(catalogPath, catalog.bytes);
|
|
355
356
|
atomicWrite(config.codex_config, injected, originalMode);
|
|
356
357
|
configWritten = true;
|
package/dist/targets/grok.js
CHANGED
|
@@ -93,7 +93,7 @@ export async function installGrokConfig(config) {
|
|
|
93
93
|
lines.pop();
|
|
94
94
|
lines.push(GROK_BLOCK_END);
|
|
95
95
|
const target = grokConfigPath();
|
|
96
|
-
ensureCpacBackup(target);
|
|
96
|
+
ensureCpacBackup(config.state_dir, "grok", target);
|
|
97
97
|
writeGrokBlock(target, lines.join("\n"));
|
|
98
98
|
console.log(`Installed Grok Build provider config: ${target}`);
|
|
99
99
|
if (!recorded || !(await proxyIsHealthy(recorded))) {
|
package/dist/targets/kimi.js
CHANGED
|
@@ -159,7 +159,7 @@ export async function installKimiConfig(config) {
|
|
|
159
159
|
}
|
|
160
160
|
lines.push(KIMI_BLOCK_END);
|
|
161
161
|
const target = kimiConfigPath();
|
|
162
|
-
ensureCpacBackup(target);
|
|
162
|
+
ensureCpacBackup(config.state_dir, "kimi", target);
|
|
163
163
|
writeKimiBlock(target, lines.join("\n"));
|
|
164
164
|
console.log(`Installed Kimi Code provider config: ${target}`);
|
|
165
165
|
if (!recorded || !(await proxyIsHealthy(recorded))) {
|
package/dist/targets/pi.js
CHANGED
|
@@ -7,7 +7,8 @@ export function piExtensionsDir() {
|
|
|
7
7
|
return join(expandUserPath(process.env.PI_CODING_AGENT_DIR?.trim() || join(homedir(), ".pi", "agent")), "extensions");
|
|
8
8
|
}
|
|
9
9
|
export function isPiExtensionInstalled() {
|
|
10
|
-
|
|
10
|
+
const dir = piExtensionsDir();
|
|
11
|
+
return existsSync(join(dir, "pi-cpac.ts")) || existsSync(join(dir, "cpac.ts"));
|
|
11
12
|
}
|
|
12
13
|
function piTemplatePath() {
|
|
13
14
|
return join(dirname(fileURLToPath(import.meta.url)), "..", "pi-extension.template");
|
|
@@ -18,15 +19,32 @@ function piExtensionContent(cpaUrl) {
|
|
|
18
19
|
export async function installPiExtension(config) {
|
|
19
20
|
const dir = piExtensionsDir();
|
|
20
21
|
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
21
|
-
const target = join(dir, "cpac.ts");
|
|
22
|
-
ensureCpacBackup(target);
|
|
22
|
+
const target = join(dir, "pi-cpac.ts");
|
|
23
|
+
ensureCpacBackup(config.state_dir, "pi", target);
|
|
23
24
|
atomicWrite(target, Buffer.from(piExtensionContent(config.cpa_url)), 0o644);
|
|
25
|
+
for (const legacy of ["cpac.ts", "pi-cpa.ts"]) {
|
|
26
|
+
const legacyPath = join(dir, legacy);
|
|
27
|
+
if (existsSync(legacyPath) && legacyPath !== target) {
|
|
28
|
+
try {
|
|
29
|
+
unlinkSync(legacyPath);
|
|
30
|
+
}
|
|
31
|
+
catch { }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
24
34
|
console.log(`Installed Pi extension: ${target}`);
|
|
25
35
|
}
|
|
26
36
|
export async function uninstallPiExtension() {
|
|
27
|
-
const
|
|
28
|
-
|
|
37
|
+
const dir = piExtensionsDir();
|
|
38
|
+
const target = join(dir, "pi-cpac.ts");
|
|
39
|
+
const legacyTargets = [join(dir, "cpac.ts"), join(dir, "pi-cpa.ts")];
|
|
40
|
+
let removed = false;
|
|
41
|
+
for (const file of [target, ...legacyTargets]) {
|
|
42
|
+
if (existsSync(file)) {
|
|
43
|
+
unlinkSync(file);
|
|
44
|
+
console.log(`Removed Pi extension: ${file}`);
|
|
45
|
+
removed = true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (!removed)
|
|
29
49
|
throw new CPACError("Pi extension is not installed");
|
|
30
|
-
unlinkSync(target);
|
|
31
|
-
console.log(`Removed Pi extension: ${target}`);
|
|
32
50
|
}
|
package/dist/util.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { chmodSync, closeSync, copyFileSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
import { dirname, join, parse } from "node:path";
|
|
3
|
+
import { basename, dirname, join, parse } from "node:path";
|
|
4
4
|
import { createInterface } from "node:readline/promises";
|
|
5
5
|
import { Writable } from "node:stream";
|
|
6
6
|
let atomicSequence = 0;
|
|
@@ -9,13 +9,28 @@ export class CPACError extends Error {
|
|
|
9
9
|
export function objectValue(value) {
|
|
10
10
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11
11
|
}
|
|
12
|
-
export function ensureCpacBackup(target) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
export function ensureCpacBackup(stateDir, agent, target) {
|
|
13
|
+
const dest = join(stateDir, agent, basename(target));
|
|
14
|
+
const sidecar = `${target}.cpac-backup`;
|
|
15
|
+
if (!existsSync(dest)) {
|
|
16
|
+
const source = existsSync(sidecar)
|
|
17
|
+
? sidecar
|
|
18
|
+
: existsSync(target)
|
|
19
|
+
? target
|
|
20
|
+
: null;
|
|
21
|
+
if (!source)
|
|
22
|
+
return;
|
|
23
|
+
mkdirSync(dirname(dest), { recursive: true, mode: 0o700 });
|
|
24
|
+
try {
|
|
25
|
+
chmodSync(stateDir, 0o700);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Directory may already have the desired mode.
|
|
29
|
+
}
|
|
30
|
+
copyFileSync(source, dest);
|
|
31
|
+
}
|
|
32
|
+
if (existsSync(sidecar))
|
|
33
|
+
unlinkSync(sidecar);
|
|
19
34
|
}
|
|
20
35
|
export function expandUserPath(value) {
|
|
21
36
|
if (value === "~")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yhong91/cpac",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"description": "Connect Codex and Claude Code to a remote CLIProxyAPI gateway",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
"clean": "node -e \"for (const d of ['dist','dist-test']) require('fs').rmSync(d,{recursive:true,force:true})\"",
|
|
31
31
|
"build": "npm run clean --if-present && tsc -p tsconfig.build.json && cp src/pi-extension.template dist/ && node -e \"const fs=require('fs');const p='dist/cpac.js';const s=fs.readFileSync(p,'utf8');if(!s.startsWith('#!'))fs.writeFileSync(p,'#!/usr/bin/env node\\n'+s);try{fs.chmodSync(p,0o755)}catch{}\"",
|
|
32
32
|
"check": "tsc -p tsconfig.json --noEmit",
|
|
33
|
-
"check:pi": "tsc -p tsconfig.pi.json --noEmit",
|
|
34
33
|
"test": "npm run clean && tsc -p tsconfig.test.json && cp src/pi-extension.template dist-test/src/ && node --test --test-reporter=spec dist-test/cpac.test.js",
|
|
35
34
|
"pack:check": "npm pack --dry-run"
|
|
36
35
|
},
|