@yhong91/cpac 0.1.36 → 0.1.38
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 -23
- package/dist/agents.js +4 -19
- package/dist/cpac.js +4 -10
- package/dist/targets/pi.js +44 -1
- package/dist/util.js +12 -3
- package/package.json +1 -1
- package/dist/targets/opencode.js +0 -59
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ CPAC 将 Codex 和 Claude Code 接入远端 CLIProxyAPI(CPA):
|
|
|
10
10
|
## 要求
|
|
11
11
|
|
|
12
12
|
- [Node.js](https://nodejs.org/) 20 或更新版本(npm 随 Node 安装)
|
|
13
|
-
- 已安装需要使用的 agent:`codex`、`claude`、`pi`、`kimi` 或 `
|
|
13
|
+
- 已安装需要使用的 agent:`codex`、`claude`、`pi`、`kimi` 或 `grok`
|
|
14
14
|
- 有权访问的 CPA API key
|
|
15
15
|
|
|
16
16
|
## 安装
|
|
@@ -45,7 +45,6 @@ cpac restore codex # 撤掉注入;注入期间的用户改
|
|
|
45
45
|
|
|
46
46
|
# 4b. 临时接管(退出无痕,不写任何配置文件)
|
|
47
47
|
cpac claude # Claude Code 走 CPA
|
|
48
|
-
cpac opencode # opencode 走 CPA(模型选 cpac/<模型名>)
|
|
49
48
|
|
|
50
49
|
# 5. 可选:Agent 配置向导与模型选择(只写配置,不启动)
|
|
51
50
|
cpac setup codex # Codex 配置向导 → 注入
|
|
@@ -62,7 +61,7 @@ cpac upgrade
|
|
|
62
61
|
|
|
63
62
|
本节面向 AI agent:按顺序执行即可完成安装与配置,**全程无交互**。注意:`cpac codex --config` 和 `cpac claude --config` 的交互向导是 TTY-only,agent 必须走下方直接写配置 JSON 的路径。
|
|
64
63
|
|
|
65
|
-
1. **检查前置**:`node --version` ≥ 20;确认用户需要哪些 agent(`codex`/`claude`/`pi`/`kimi`/`
|
|
64
|
+
1. **检查前置**:`node --version` ≥ 20;确认用户需要哪些 agent(`codex`/`claude`/`pi`/`kimi`/`grok` 在 PATH 上)。向用户索取 CPA API key。
|
|
66
65
|
|
|
67
66
|
2. **安装与密钥**:
|
|
68
67
|
|
|
@@ -91,7 +90,7 @@ cpac status # 验证:各 target 显示 ready/injected
|
|
|
91
90
|
```
|
|
92
91
|
|
|
93
92
|
1. **统一启动与配置**:
|
|
94
|
-
- 启动:`cpac <codex|claude|kimi|grok|pi
|
|
93
|
+
- 启动:`cpac <codex|claude|kimi|grok|pi> [args...]`(所有原生参数如 `-c` 等原样透明传递)
|
|
95
94
|
- `setup` / `--setup`:配置 Codex/Claude,然后启动
|
|
96
95
|
- `clear`:还原原生配置
|
|
97
96
|
- 只写配置、不启动:`cpac setup [claude|codex]`;查看当前:`cpac setup`
|
|
@@ -162,7 +161,7 @@ cpac --help
|
|
|
162
161
|
|
|
163
162
|
### 统一管理(detect / install / sync / restore / uninstall / upgrade)
|
|
164
163
|
|
|
165
|
-
参考 vibetime 的目标式命令,对 `codex`、`pi`、`kimi`
|
|
164
|
+
参考 vibetime 的目标式命令,对 `codex`、`pi`、`kimi`、`grok` 四个持久注入目标统一管理(claude 是临时接管,用 `cpac claude` 启动,不属于 install 目标):
|
|
166
165
|
|
|
167
166
|
```bash
|
|
168
167
|
cpac detect [--json]
|
|
@@ -176,7 +175,7 @@ cpac -v | --version
|
|
|
176
175
|
|
|
177
176
|
- `detect` 列出每个目标是否被检测到、是否已接入 CPAC;`--json` 输出机器可读结果。
|
|
178
177
|
- `install` 默认作用于检测到的目标;后面直接写 agent 名(`cpac install codex pi`),`--all` 全部。`codex` 写入 catalog 并拉起 loopback 代理;`pi`/`kimi`/`grok` 写入各自配置。`--dry-run` 只打印将要做的事,`--force` 允许重装已安装目标。
|
|
179
|
-
- `sync` 按当前 `cpa_url` 和 CPA 目录强制刷新**已经接入**的目标(Codex 重新注入、Pi
|
|
178
|
+
- `sync` 按当前 `cpa_url` 和 CPA 目录强制刷新**已经接入**的目标(Codex 重新注入、Pi 按 CLI 版本决定写入 packages / `pi update` / 跳过,并清理旧的 `extensions/pi-cpac.ts`、Kimi 配置重写),用来在 CPA 地址或模型目录变化后一次对齐本地配置。不带参数只动已安装目标;`--all` 会对检测到的目标做同样的强制刷新(尚未安装的会装上)。`--max_context` 仍只对 `cpac install codex` 有效。
|
|
180
179
|
- Codex 注入默认开启 multi-agent v2(写入 `[features.multi_agent_v2] enabled = true`,catalog 行打 `multi_agent_version: "v2"`);`install codex --v2_off` 关掉 toml 并去掉 catalog 戳。v2 开着时会剥掉 `[agents] max_threads`(Codex 否则拒启动)。`cpac status` 对照 config 里的 `v2_off` / `max_context` 和 live toml / catalog。服务端还需在 CPA 配置中开启 `codex.optimize-multi-agent-v2`。
|
|
181
180
|
- `--v2_models` 进入 arrow-key 复选框选择最多 5 个 spawn_agent 模型(勾选顺序即推荐顺序),选择结果存入配置文件的 `spawn_models`,注入时把这些模型排到 `codex-models.json` 最前——Codex 客户端只把 catalog 前 5 个可见模型广告为 spawn 覆盖项。
|
|
182
181
|
- `restore` 必须带 agent 名或 `--all`,卸掉指定(或全部)已注入目标。`--all` 时未安装的目标静默跳过。
|
|
@@ -269,29 +268,16 @@ restore / `<agent> clear` 会删掉该 agent 的备份目录和 state.json 里
|
|
|
269
268
|
|
|
270
269
|
为防止误删或覆盖,`state_dir` 不能是文件系统根目录、用户 home、系统临时目录,也不能包含 Codex 配置文件。
|
|
271
270
|
|
|
272
|
-
### OpenCode(临时接管)
|
|
273
|
-
|
|
274
|
-
```bash
|
|
275
|
-
cpac opencode [--] [opencode args...]
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
通过 `OPENCODE_CONFIG_CONTENT` 内联注入一个 `cpac` OpenAI 兼容 provider(baseURL 直指 CPA,模型列表来自 CPA 目录),不触碰任何 opencode 配置文件,退出即无痕。首次使用时 opencode 会自行安装 `@ai-sdk/openai-compatible` 适配器。模型选择用 `cpac/<模型名>`。
|
|
279
|
-
|
|
280
271
|
### Pi
|
|
281
272
|
|
|
282
|
-
安装 CPA provider 扩展到 Pi:
|
|
283
|
-
|
|
284
273
|
```bash
|
|
285
|
-
cpac pi
|
|
274
|
+
cpac install pi
|
|
275
|
+
cpac pi
|
|
286
276
|
```
|
|
287
277
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
```bash
|
|
291
|
-
cpac restore pi
|
|
292
|
-
```
|
|
278
|
+
未接入时把 `npm:@yhong91/cpac` 写入 `~/.pi/agent/settings.json` 的 `packages`。已接入则比较 `~/.pi/agent/npm/node_modules/@yhong91/cpac` 与当前 CLI 版本:落后则 `pi update npm:@yhong91/cpac`,已是最新(或尚未下载副本)则跳过。`cpac pi` 启动前走同一套判断。Pi 启动时加载 `extensions/pi-cpac.ts` 从 CPA 动态注册模型。旧版 `~/.pi/agent/extensions/pi-cpac.ts` 会在 install / restore 时删掉。
|
|
293
279
|
|
|
294
|
-
|
|
280
|
+
卸载:`cpac restore pi` 或 `cpac pi clear`。尊重 `PI_CODING_AGENT_DIR`。
|
|
295
281
|
|
|
296
282
|
### Kimi Code
|
|
297
283
|
|
package/dist/agents.js
CHANGED
|
@@ -7,8 +7,8 @@ import { MAX_SPAWN_MODELS, loadCatalogSlugs, pickSpawnModels, saveCodexSetup, sa
|
|
|
7
7
|
import { MANAGED_MARKER, inject, restore } from "./targets/codex.js";
|
|
8
8
|
import { grokConfigPath, grokHome, installGrokConfig, isGrokConfigInstalled, uninstallGrokConfig, } from "./targets/grok.js";
|
|
9
9
|
import { installKimiConfig, isKimiConfigInstalled, kimiConfigPath, uninstallKimiConfig, } from "./targets/kimi.js";
|
|
10
|
-
import { PI_PACKAGE,
|
|
11
|
-
import { CPACError, atomicWrite, checkboxPicker, expandUserPath, objectValue, tabWizard, } from "./util.js";
|
|
10
|
+
import { PI_PACKAGE, ensurePiExtension, isPiExtensionInstalled, piAgentDir, piSettingsPath, uninstallPiExtension, } from "./targets/pi.js";
|
|
11
|
+
import { CPACError, atomicWrite, checkboxPicker, compareVersions, expandUserPath, objectValue, tabWizard, } from "./util.js";
|
|
12
12
|
const CODEX_CTX_STD = "Standard (default input budget, e.g. 200K~272K)";
|
|
13
13
|
const CODEX_CTX_MAX = "Max Context Window (lift to full upper bound, e.g. 921K~1M, 90% auto-compact limit)";
|
|
14
14
|
const CODEX_V2_ON = "Enabled (Default)";
|
|
@@ -220,11 +220,7 @@ const TARGETS = [
|
|
|
220
220
|
installed: () => isPiExtensionInstalled(),
|
|
221
221
|
version: () => detectClientVersion("pi"),
|
|
222
222
|
install: async (config, dryRun) => {
|
|
223
|
-
|
|
224
|
-
console.log(`would add ${PI_PACKAGE} to ${piSettingsPath()}`);
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
await installPiExtension(config);
|
|
223
|
+
await ensurePiExtension(config, CPAC_VERSION, dryRun);
|
|
228
224
|
},
|
|
229
225
|
uninstall: async (config, dryRun) => {
|
|
230
226
|
if (dryRun) {
|
|
@@ -321,7 +317,7 @@ export async function runInstall(config, requested, options) {
|
|
|
321
317
|
throw new CPACError("--max_context is only valid with codex");
|
|
322
318
|
}
|
|
323
319
|
for (const target of selected) {
|
|
324
|
-
if (target.installed(config) && !options.force) {
|
|
320
|
+
if (target.installed(config) && !options.force && target.id !== "pi") {
|
|
325
321
|
console.log(`${target.id}: already installed; use --force to reinstall`);
|
|
326
322
|
continue;
|
|
327
323
|
}
|
|
@@ -398,17 +394,6 @@ export async function runUninstall(config, options) {
|
|
|
398
394
|
}
|
|
399
395
|
return 0;
|
|
400
396
|
}
|
|
401
|
-
function compareVersions(a, b) {
|
|
402
|
-
const left = a.split(".").map(Number);
|
|
403
|
-
const right = b.split(".").map(Number);
|
|
404
|
-
for (let index = 0; index < Math.max(left.length, right.length); index++) {
|
|
405
|
-
const l = left[index] ?? 0;
|
|
406
|
-
const r = right[index] ?? 0;
|
|
407
|
-
if (l !== r)
|
|
408
|
-
return l > r ? 1 : -1;
|
|
409
|
-
}
|
|
410
|
-
return 0;
|
|
411
|
-
}
|
|
412
397
|
export async function runUpgrade(config, checkOnly) {
|
|
413
398
|
console.log(`Current version: ${CPAC_VERSION}`);
|
|
414
399
|
let latest;
|
package/dist/cpac.js
CHANGED
|
@@ -8,7 +8,6 @@ import { runClaude, runClaudeConfig } from "./targets/claude.js";
|
|
|
8
8
|
import { multiAgentV2Enabled } from "./targets/codex.js";
|
|
9
9
|
import { isGrokConfigInstalled } from "./targets/grok.js";
|
|
10
10
|
import { isKimiConfigInstalled } from "./targets/kimi.js";
|
|
11
|
-
import { runOpencode } from "./targets/opencode.js";
|
|
12
11
|
import { isPiExtensionInstalled } from "./targets/pi.js";
|
|
13
12
|
import { defaultConfigPath, loadConfig, originalBytes, pickSpawnModels, readState, saveSpawnModels, stateProxy, catalogMaxContextLive, } from "./config.js";
|
|
14
13
|
import { proxyIsHealthy, runProxyChild } from "./proxy.js";
|
|
@@ -21,8 +20,7 @@ export { claudeTierSlots, createClaudeProxy, runClaude, } from "./targets/claude
|
|
|
21
20
|
export { detectClientVersion, detectTargets, runInstall, runRestore, runSync, runTargetLauncher, } from "./agents.js";
|
|
22
21
|
export { installGrokConfig, isGrokConfigInstalled, uninstallGrokConfig, } from "./targets/grok.js";
|
|
23
22
|
export { installKimiConfig, isKimiConfigInstalled, uninstallKimiConfig, } from "./targets/kimi.js";
|
|
24
|
-
export {
|
|
25
|
-
export { installPiExtension, isPiExtensionInstalled, uninstallPiExtension, } from "./targets/pi.js";
|
|
23
|
+
export { ensurePiExtension, installPiExtension, isPiExtensionInstalled, piPackageAction, piPackageVersion, uninstallPiExtension, } from "./targets/pi.js";
|
|
26
24
|
export async function status(config) {
|
|
27
25
|
const apiKey = process.env[config.api_key_env]?.trim();
|
|
28
26
|
const keyConfigured = !!apiKey;
|
|
@@ -82,7 +80,7 @@ export async function status(config) {
|
|
|
82
80
|
return 0;
|
|
83
81
|
}
|
|
84
82
|
async function guide(config) {
|
|
85
|
-
console.log(`CPA Companion\n\nCPA: ${config.cpa_url}\nCPA_API_KEY: ${process.env[config.api_key_env]?.trim() ? "configured" : "not configured"}\n\nCommands:\n cpac <agent> [args...] Launch an agent (codex, kimi, grok, pi, claude
|
|
83
|
+
console.log(`CPA Companion\n\nCPA: ${config.cpa_url}\nCPA_API_KEY: ${process.env[config.api_key_env]?.trim() ? "configured" : "not configured"}\n\nCommands:\n cpac <agent> [args...] Launch an agent (codex, kimi, grok, pi, claude) through CPA\n cpac setup Show current Codex/Claude setup; cpac setup <codex|claude> to change\n cpac status Show agent support status (Codex, Claude, Pi, Kimi, Grok)\n cpac restore <codex|pi|kimi|grok> | --all Detach injected agents
|
|
86
84
|
cpac detect Show supported targets and install status
|
|
87
85
|
cpac install [codex|pi|kimi|grok...] Install CPA into detected targets, or named agents
|
|
88
86
|
cpac sync Refresh installed Codex/Pi/Kimi/Grok injections from CPA
|
|
@@ -96,12 +94,10 @@ async function guide(config) {
|
|
|
96
94
|
console.log(`\nSaved ${config.api_key_env} to ${profile}. Open a new terminal or run:\n source ${profile}`);
|
|
97
95
|
return 0;
|
|
98
96
|
}
|
|
99
|
-
// opencode's config schema rejects a limit block with context but no output;
|
|
100
|
-
// the catalog has no output field, so a safe budget stands in, clamped to the
|
|
101
97
|
function usage() {
|
|
102
98
|
return [
|
|
103
99
|
"Usage: cpac",
|
|
104
|
-
" cpac <codex|kimi|grok|pi|claude
|
|
100
|
+
" cpac <codex|kimi|grok|pi|claude> [args...]",
|
|
105
101
|
" setup|--setup configure Codex/Claude, then launch",
|
|
106
102
|
" clear restore native config",
|
|
107
103
|
" cpac setup Show current Codex/Claude setup",
|
|
@@ -225,7 +221,7 @@ function parseArgs(args) {
|
|
|
225
221
|
}
|
|
226
222
|
return parsed;
|
|
227
223
|
}
|
|
228
|
-
if (["codex", "kimi", "grok", "pi", "claude"
|
|
224
|
+
if (["codex", "kimi", "grok", "pi", "claude"].includes(args[0])) {
|
|
229
225
|
const targetId = args[0];
|
|
230
226
|
let configPath = defaultConfigPath();
|
|
231
227
|
const raw = args.slice(1);
|
|
@@ -610,8 +606,6 @@ export async function main(args = process.argv.slice(2)) {
|
|
|
610
606
|
return code;
|
|
611
607
|
return await runTargetLauncher(loadConfig(parsed.configPath, true), "codex", parsed.args ?? [], "codex", { skipSync: true });
|
|
612
608
|
}
|
|
613
|
-
if (parsed.command === "opencode")
|
|
614
|
-
return await runOpencode(config, parsed.args);
|
|
615
609
|
if (parsed.command === "codex") {
|
|
616
610
|
let launcherConfig = config;
|
|
617
611
|
if (parsed.v2Models) {
|
package/dist/targets/pi.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
1
2
|
import { existsSync, mkdirSync, readFileSync, unlinkSync } from "node:fs";
|
|
2
3
|
import { homedir } from "node:os";
|
|
3
4
|
import { join } from "node:path";
|
|
4
5
|
import { dropAgent, recordAgent } from "../config.js";
|
|
5
6
|
import { stopProxyProcess } from "../proxy.js";
|
|
6
|
-
import { CPACError, atomicWrite, expandUserPath, objectValue, } from "../util.js";
|
|
7
|
+
import { CPACError, atomicWrite, compareVersions, expandUserPath, objectValue, } from "../util.js";
|
|
7
8
|
export const PI_PACKAGE = "npm:@yhong91/cpac";
|
|
8
9
|
const PI_PACKAGE_NAME = "@yhong91/cpac";
|
|
9
10
|
const LEGACY_EXTENSIONS = ["pi-cpac.ts", "cpac.ts", "pi-cpa.ts"];
|
|
@@ -91,6 +92,48 @@ export function isPiExtensionInstalled() {
|
|
|
91
92
|
return false;
|
|
92
93
|
}
|
|
93
94
|
}
|
|
95
|
+
export function piPackageVersion() {
|
|
96
|
+
const pkg = join(piAgentDir(), "npm", "node_modules", "@yhong91", "cpac", "package.json");
|
|
97
|
+
try {
|
|
98
|
+
const version = JSON.parse(readFileSync(pkg, "utf8")).version;
|
|
99
|
+
return typeof version === "string" ? version : undefined;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export function piPackageAction(desiredVersion) {
|
|
106
|
+
if (leftoverExtensions().length > 0 || !isPiExtensionInstalled()) {
|
|
107
|
+
return "install";
|
|
108
|
+
}
|
|
109
|
+
const version = piPackageVersion();
|
|
110
|
+
if (version !== undefined && compareVersions(version, desiredVersion) < 0) {
|
|
111
|
+
return "upgrade";
|
|
112
|
+
}
|
|
113
|
+
return "none";
|
|
114
|
+
}
|
|
115
|
+
export async function ensurePiExtension(config, desiredVersion, dryRun = false) {
|
|
116
|
+
const action = piPackageAction(desiredVersion);
|
|
117
|
+
if (action === "none")
|
|
118
|
+
return;
|
|
119
|
+
if (dryRun) {
|
|
120
|
+
console.log(action === "install"
|
|
121
|
+
? `would add ${PI_PACKAGE} to ${piSettingsPath()}`
|
|
122
|
+
: `would update ${PI_PACKAGE}`);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (action === "install") {
|
|
126
|
+
await installPiExtension(config);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const result = spawnSync("pi", ["update", PI_PACKAGE], {
|
|
130
|
+
stdio: "inherit",
|
|
131
|
+
env: { ...process.env, PI_CODING_AGENT_DIR: piAgentDir() },
|
|
132
|
+
});
|
|
133
|
+
if (result.error || (result.status ?? 1) !== 0) {
|
|
134
|
+
console.warn(`warning: pi update ${PI_PACKAGE} failed`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
94
137
|
export async function installPiExtension(config) {
|
|
95
138
|
const path = piSettingsPath();
|
|
96
139
|
mkdirSync(piAgentDir(), { recursive: true, mode: 0o700 });
|
package/dist/util.js
CHANGED
|
@@ -205,9 +205,7 @@ export async function tabWizard(tabs) {
|
|
|
205
205
|
};
|
|
206
206
|
const resultOf = () => Object.fromEntries(state.map((tab) => [
|
|
207
207
|
tab.id,
|
|
208
|
-
[...tab.selected]
|
|
209
|
-
.sort((a, b) => a - b)
|
|
210
|
-
.map((i) => tab.items[i]),
|
|
208
|
+
[...tab.selected].sort((a, b) => a - b).map((i) => tab.items[i]),
|
|
211
209
|
]));
|
|
212
210
|
const goNext = () => {
|
|
213
211
|
if (index < state.length && state[index].selected.size === 0)
|
|
@@ -385,3 +383,14 @@ export function saveApiKeyExport(profile, name, apiKey) {
|
|
|
385
383
|
const mode = existsSync(profile) ? statSync(profile).mode & 0o7777 : 0o600;
|
|
386
384
|
atomicWrite(profile, Buffer.from(content), mode);
|
|
387
385
|
}
|
|
386
|
+
export function compareVersions(a, b) {
|
|
387
|
+
const left = a.split(".").map(Number);
|
|
388
|
+
const right = b.split(".").map(Number);
|
|
389
|
+
for (let index = 0; index < Math.max(left.length, right.length); index++) {
|
|
390
|
+
const l = left[index] ?? 0;
|
|
391
|
+
const r = right[index] ?? 0;
|
|
392
|
+
if (l !== r)
|
|
393
|
+
return l > r ? 1 : -1;
|
|
394
|
+
}
|
|
395
|
+
return 0;
|
|
396
|
+
}
|
package/package.json
CHANGED
package/dist/targets/opencode.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { apiBase, catalogModelId, fetchCatalog, } from "../config.js";
|
|
3
|
-
import { CPACError } from "../util.js";
|
|
4
|
-
const OPENCODE_OUTPUT_BUDGET = 32_000;
|
|
5
|
-
// Ephemeral takeover: inline a CPA provider via OPENCODE_CONFIG_CONTENT so no
|
|
6
|
-
// opencode config file is touched; the session ends with zero residue.
|
|
7
|
-
export async function runOpencode(config, args, executable = "opencode") {
|
|
8
|
-
const apiKey = process.env[config.api_key_env]?.trim();
|
|
9
|
-
if (!apiKey)
|
|
10
|
-
throw new CPACError(`environment variable ${config.api_key_env} is not set`);
|
|
11
|
-
const catalog = await fetchCatalog(config.cpa_url, apiKey);
|
|
12
|
-
let document;
|
|
13
|
-
try {
|
|
14
|
-
document = JSON.parse(new TextDecoder().decode(catalog.bytes));
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
throw new CPACError("invalid CPA catalog");
|
|
18
|
-
}
|
|
19
|
-
const models = {};
|
|
20
|
-
for (const row of document.models) {
|
|
21
|
-
const id = catalogModelId(row);
|
|
22
|
-
if (!id)
|
|
23
|
-
continue;
|
|
24
|
-
const window = typeof row.context_window === "number" && row.context_window > 0
|
|
25
|
-
? Math.floor(row.context_window)
|
|
26
|
-
: 0;
|
|
27
|
-
models[id] =
|
|
28
|
-
window > 0
|
|
29
|
-
? {
|
|
30
|
-
limit: {
|
|
31
|
-
context: window,
|
|
32
|
-
output: Math.min(OPENCODE_OUTPUT_BUDGET, window),
|
|
33
|
-
},
|
|
34
|
-
}
|
|
35
|
-
: {};
|
|
36
|
-
}
|
|
37
|
-
const content = JSON.stringify({
|
|
38
|
-
provider: {
|
|
39
|
-
cpac: {
|
|
40
|
-
npm: "@ai-sdk/openai-compatible",
|
|
41
|
-
options: { baseURL: apiBase(config.cpa_url), apiKey },
|
|
42
|
-
models,
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
const env = {
|
|
47
|
-
...process.env,
|
|
48
|
-
OPENCODE_CONFIG_CONTENT: content,
|
|
49
|
-
};
|
|
50
|
-
return await new Promise((resolve, reject) => {
|
|
51
|
-
const child = spawn(executable, args, { env, stdio: "inherit" });
|
|
52
|
-
child.once("error", (error) => {
|
|
53
|
-
reject(new CPACError(error instanceof Error && "code" in error && error.code === "ENOENT"
|
|
54
|
-
? `${executable} not found`
|
|
55
|
-
: `cannot start ${executable}: ${error.message}`));
|
|
56
|
-
});
|
|
57
|
-
child.once("close", (code) => resolve(code ?? 1));
|
|
58
|
-
});
|
|
59
|
-
}
|