@yhong91/cpac 0.1.44 → 0.1.46
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 +2 -2
- package/dist/agents.js +16 -4
- package/dist/config.js +11 -9
- package/dist/targets/hermes.js +54 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -327,13 +327,13 @@ cpac install hermes
|
|
|
327
327
|
cpac hermes
|
|
328
328
|
```
|
|
329
329
|
|
|
330
|
-
它在 `~/.hermes/config.yaml` 的 `providers.cpac` 写入 loopback `base_url`/`api`、占位 `api_key`、`api_mode: responses`,以及 CPA catalog 的 slug 列表(`discover_models: false`)。不改当前默认模型;切换用 `hermes model` 或 `/model`。密钥由 loopback 注入,不写进 `.env
|
|
330
|
+
它在 default `~/.hermes/config.yaml` 以及每个 `~/.hermes/profiles/<name>/config.yaml` 的 `providers.cpac` 写入 loopback `base_url`/`api`、占位 `api_key`、`api_mode: responses`,以及 CPA catalog 的 slug 列表(`discover_models: false`)。不改当前默认模型;切换用 `hermes model` 或 `/model`。密钥由 loopback 注入,不写进 `.env`。新建 profile 后再跑一次 `cpac install hermes`(或 `cpac hermes`)即可写入。卸载:
|
|
331
331
|
|
|
332
332
|
```bash
|
|
333
333
|
cpac restore hermes
|
|
334
334
|
```
|
|
335
335
|
|
|
336
|
-
配置目录尊重 `HERMES_HOME`(Windows 默认 `%LOCALAPPDATA%\hermes`)。首次写入前若 `config.yaml` 已存在,会在 `state_dir/hermes/config.yaml` 保留原始快照。
|
|
336
|
+
配置目录尊重 `HERMES_HOME`(Windows 默认 `%LOCALAPPDATA%\hermes`)。首次写入前若 default `config.yaml` 已存在,会在 `state_dir/hermes/config.yaml` 保留原始快照。profile 文件不单独备份,restore 只剥 `providers.cpac`。
|
|
337
337
|
|
|
338
338
|
## 环境变量
|
|
339
339
|
|
package/dist/agents.js
CHANGED
|
@@ -9,7 +9,7 @@ import { grokConfigPath, grokHome, installGrokConfig, isGrokConfigInstalled, uni
|
|
|
9
9
|
import { installKimiConfig, isKimiConfigInstalled, kimiConfigPath, uninstallKimiConfig, } from "./targets/kimi.js";
|
|
10
10
|
import { installPiConfig, isPiConfigInstalled, piAgentDir, piModelsPath, uninstallPiConfig, } from "./targets/pi.js";
|
|
11
11
|
import { installZedConfig, isZedConfigInstalled, uninstallZedConfig, zedConfigPath, zedHome, } from "./targets/zed.js";
|
|
12
|
-
import {
|
|
12
|
+
import { hermesConfigPaths, hermesHome, installHermesConfig, isHermesConfigInstalled, uninstallHermesConfig, } from "./targets/hermes.js";
|
|
13
13
|
import { CPACError, atomicWrite, checkboxPicker, compareVersions, expandUserPath, objectValue, tabWizard, } from "./util.js";
|
|
14
14
|
const CODEX_CTX_STD = "Standard (default input budget, e.g. 200K~272K)";
|
|
15
15
|
const CODEX_CTX_MAX = "Max Context Window (lift to full upper bound, e.g. 921K~1M, 90% auto-compact limit)";
|
|
@@ -310,14 +310,16 @@ const TARGETS = [
|
|
|
310
310
|
version: () => detectClientVersion("hermes"),
|
|
311
311
|
install: async (config, dryRun) => {
|
|
312
312
|
if (dryRun) {
|
|
313
|
-
|
|
313
|
+
for (const path of hermesConfigPaths())
|
|
314
|
+
console.log(`would write CPA provider: ${path}`);
|
|
314
315
|
return;
|
|
315
316
|
}
|
|
316
317
|
await installHermesConfig(config);
|
|
317
318
|
},
|
|
318
319
|
uninstall: async (config, dryRun) => {
|
|
319
320
|
if (dryRun) {
|
|
320
|
-
|
|
321
|
+
for (const path of hermesConfigPaths())
|
|
322
|
+
console.log(`would remove CPA provider: ${path}`);
|
|
321
323
|
return;
|
|
322
324
|
}
|
|
323
325
|
await uninstallHermesConfig(config);
|
|
@@ -368,7 +370,10 @@ export async function runInstall(config, requested, options) {
|
|
|
368
370
|
throw new CPACError("--max_context is only valid with codex");
|
|
369
371
|
}
|
|
370
372
|
for (const target of selected) {
|
|
371
|
-
if (target.installed(config) &&
|
|
373
|
+
if (target.installed(config) &&
|
|
374
|
+
!options.force &&
|
|
375
|
+
target.id !== "pi" &&
|
|
376
|
+
target.id !== "hermes") {
|
|
372
377
|
console.log(`${target.id}: already installed; use --force to reinstall`);
|
|
373
378
|
continue;
|
|
374
379
|
}
|
|
@@ -492,5 +497,12 @@ export async function runUpgrade(config, checkOnly) {
|
|
|
492
497
|
return 0;
|
|
493
498
|
}
|
|
494
499
|
console.log(`Syncing ${installed.join(", ")}`);
|
|
500
|
+
if (!upToDate) {
|
|
501
|
+
const cli = fileURLToPath(new URL("./cpac.js", import.meta.url));
|
|
502
|
+
const result = spawnSync(process.execPath, [cli, "sync"], {
|
|
503
|
+
stdio: "inherit",
|
|
504
|
+
});
|
|
505
|
+
return result.status ?? 1;
|
|
506
|
+
}
|
|
495
507
|
return runSync(config, [], { all: false, dryRun: false });
|
|
496
508
|
}
|
package/dist/config.js
CHANGED
|
@@ -315,16 +315,18 @@ function catalogDocument(bytes) {
|
|
|
315
315
|
throw new CPACError("invalid CPA catalog");
|
|
316
316
|
}
|
|
317
317
|
}
|
|
318
|
-
// Codex
|
|
319
|
-
//
|
|
318
|
+
// Codex rejects the catalog on extra input_modalities values or a missing
|
|
319
|
+
// supported_reasoning_levels field. Keep unknown fields; coerce these two.
|
|
320
320
|
function sanitizeModelModalities(model) {
|
|
321
|
-
if (
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
: [];
|
|
327
|
-
|
|
321
|
+
if ("input_modalities" in model) {
|
|
322
|
+
const raw = model.input_modalities;
|
|
323
|
+
const accepted = Array.isArray(raw)
|
|
324
|
+
? raw.filter((value) => value === "text" || value === "image" || value === "audio")
|
|
325
|
+
: [];
|
|
326
|
+
model.input_modalities = accepted.length > 0 ? accepted : ["text"];
|
|
327
|
+
}
|
|
328
|
+
if (!Array.isArray(model.supported_reasoning_levels))
|
|
329
|
+
model.supported_reasoning_levels = [];
|
|
328
330
|
}
|
|
329
331
|
export function sanitizeCatalogModalities(bytes) {
|
|
330
332
|
const document = catalogDocument(bytes);
|
package/dist/targets/hermes.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
-
import { catalogModelId, catalogModelRows, dropAgent, fetchCatalog, recordWrittenConfig, restoreOwnedConfig, } from "../config.js";
|
|
4
|
+
import { catalogModelId, catalogModelRows, dropAgent, fetchCatalog, readState, recordWrittenConfig, restoreOwnedConfig, } from "../config.js";
|
|
5
5
|
import { ensureLoopbackProxy, stopProxyProcess } from "../proxy.js";
|
|
6
6
|
import { CPACError, atomicWrite, ensureCpacBackup, expandUserPath, resolveApiKey, } from "../util.js";
|
|
7
7
|
export const HERMES_PROVIDER_ID = "cpac";
|
|
@@ -18,6 +18,23 @@ export function hermesHome() {
|
|
|
18
18
|
export function hermesConfigPath() {
|
|
19
19
|
return join(hermesHome(), "config.yaml");
|
|
20
20
|
}
|
|
21
|
+
export function hermesConfigPaths() {
|
|
22
|
+
const home = hermesHome();
|
|
23
|
+
const paths = [hermesConfigPath()];
|
|
24
|
+
let entries;
|
|
25
|
+
try {
|
|
26
|
+
entries = readdirSync(join(home, "profiles"), { withFileTypes: true });
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return paths;
|
|
30
|
+
}
|
|
31
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
32
|
+
if (!entry.isDirectory() || entry.name.startsWith("."))
|
|
33
|
+
continue;
|
|
34
|
+
paths.push(join(home, "profiles", entry.name, "config.yaml"));
|
|
35
|
+
}
|
|
36
|
+
return paths;
|
|
37
|
+
}
|
|
21
38
|
function lineIndent(line) {
|
|
22
39
|
const match = line.match(/^( *)/);
|
|
23
40
|
return match ? match[1].length : 0;
|
|
@@ -121,14 +138,17 @@ function hasProvidersCpac(content) {
|
|
|
121
138
|
const indent = childIndent(lines, range.idx, range.end, 0);
|
|
122
139
|
return Boolean(findCpacChild(lines, range.idx, range.end, indent));
|
|
123
140
|
}
|
|
124
|
-
|
|
141
|
+
function pathHasCpac(path) {
|
|
125
142
|
try {
|
|
126
|
-
return hasProvidersCpac(readFileSync(
|
|
143
|
+
return hasProvidersCpac(readFileSync(path, "utf8"));
|
|
127
144
|
}
|
|
128
145
|
catch {
|
|
129
146
|
return false;
|
|
130
147
|
}
|
|
131
148
|
}
|
|
149
|
+
export function isHermesConfigInstalled() {
|
|
150
|
+
return hermesConfigPaths().some(pathHasCpac);
|
|
151
|
+
}
|
|
132
152
|
function stripProvidersCpac(content) {
|
|
133
153
|
const eol = content.includes("\r\n") ? "\r\n" : "\n";
|
|
134
154
|
const lines = content.split(/\r?\n/);
|
|
@@ -194,6 +214,20 @@ function writeHermesProvider(path, port, rows) {
|
|
|
194
214
|
const mode = existsSync(path) ? statSync(path).mode & 0o7777 : 0o600;
|
|
195
215
|
atomicWrite(path, Buffer.from(content), mode);
|
|
196
216
|
}
|
|
217
|
+
function stripHermesFile(path) {
|
|
218
|
+
if (!existsSync(path))
|
|
219
|
+
return;
|
|
220
|
+
const current = readFileSync(path, "utf8");
|
|
221
|
+
if (!hasProvidersCpac(current))
|
|
222
|
+
return;
|
|
223
|
+
const stripped = stripProvidersCpac(current);
|
|
224
|
+
if (stripped.trim() === "") {
|
|
225
|
+
rmSync(path, { force: true });
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const mode = statSync(path).mode & 0o7777;
|
|
229
|
+
atomicWrite(path, Buffer.from(stripped.endsWith("\n") ? stripped : `${stripped}\n`), mode);
|
|
230
|
+
}
|
|
197
231
|
export async function installHermesConfig(config) {
|
|
198
232
|
const apiKey = await resolveApiKey(config.api_key_env);
|
|
199
233
|
const catalog = await fetchCatalog(config.cpa_url, apiKey);
|
|
@@ -208,12 +242,14 @@ export async function installHermesConfig(config) {
|
|
|
208
242
|
if (rows.length === 0)
|
|
209
243
|
throw new CPACError("CPA catalog contains no models");
|
|
210
244
|
const { proxy, fingerprint, started } = await ensureLoopbackProxy(config, apiKey);
|
|
211
|
-
const
|
|
212
|
-
const
|
|
245
|
+
const targets = hermesConfigPaths();
|
|
246
|
+
const primary = targets[0];
|
|
247
|
+
const hadFile = existsSync(primary);
|
|
213
248
|
try {
|
|
214
|
-
ensureCpacBackup(config.state_dir, "hermes",
|
|
215
|
-
|
|
216
|
-
|
|
249
|
+
ensureCpacBackup(config.state_dir, "hermes", primary, !pathHasCpac(primary));
|
|
250
|
+
for (const target of targets)
|
|
251
|
+
writeHermesProvider(target, proxy.port, rows);
|
|
252
|
+
recordWrittenConfig(config.state_dir, "hermes", primary, {
|
|
217
253
|
id: proxy.id,
|
|
218
254
|
pid: proxy.pid,
|
|
219
255
|
port: proxy.port,
|
|
@@ -225,15 +261,20 @@ export async function installHermesConfig(config) {
|
|
|
225
261
|
await stopProxyProcess(proxy);
|
|
226
262
|
throw error;
|
|
227
263
|
}
|
|
228
|
-
console.log(`Installed Hermes provider "${HERMES_PROVIDER_ID}": ${
|
|
264
|
+
console.log(`Installed Hermes provider "${HERMES_PROVIDER_ID}": ${targets.join(", ")}`);
|
|
229
265
|
}
|
|
230
266
|
export async function uninstallHermesConfig(config) {
|
|
231
|
-
const
|
|
267
|
+
const targets = hermesConfigPaths();
|
|
268
|
+
const primary = targets[0];
|
|
232
269
|
if (!isHermesConfigInstalled())
|
|
233
270
|
throw new CPACError("Hermes CPAC provider is not installed");
|
|
234
|
-
|
|
271
|
+
if (readState(config.state_dir)?.agents?.hermes || pathHasCpac(primary)) {
|
|
272
|
+
restoreOwnedConfig(config.state_dir, "hermes", primary, (current) => Buffer.from(stripProvidersCpac(new TextDecoder("utf-8").decode(current))));
|
|
273
|
+
}
|
|
274
|
+
for (const target of targets.slice(1))
|
|
275
|
+
stripHermesFile(target);
|
|
235
276
|
const { stopProxy } = dropAgent(config.state_dir, "hermes");
|
|
236
277
|
if (stopProxy)
|
|
237
278
|
await stopProxyProcess(stopProxy);
|
|
238
|
-
console.log(`Removed Hermes provider "${HERMES_PROVIDER_ID}": ${
|
|
279
|
+
console.log(`Removed Hermes provider "${HERMES_PROVIDER_ID}": ${targets.join(", ")}`);
|
|
239
280
|
}
|