@yhong91/cpac 0.1.45 → 0.1.47
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 +35 -6
- package/dist/targets/hermes.js +75 -15
- 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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawn, spawnSync } from "node:child_process";
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { dirname, join } from "node:path";
|
|
4
|
+
import { basename, dirname, join } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { MAX_SPAWN_MODELS, loadCatalogSlugs, pickSpawnModels, saveCodexSetup, saveSpawnModels, } from "./config.js";
|
|
7
7
|
import { MANAGED_MARKER, inject, restore } from "./targets/codex.js";
|
|
@@ -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
|
}
|
|
@@ -445,6 +450,19 @@ export async function runUninstall(config, options) {
|
|
|
445
450
|
}
|
|
446
451
|
return 0;
|
|
447
452
|
}
|
|
453
|
+
function runningNpmPrefix() {
|
|
454
|
+
const dist = dirname(fileURLToPath(import.meta.url));
|
|
455
|
+
const pkg = dirname(dist);
|
|
456
|
+
const scoped = dirname(pkg);
|
|
457
|
+
const modules = dirname(scoped);
|
|
458
|
+
if (basename(pkg) !== "cpac" ||
|
|
459
|
+
basename(scoped) !== "@yhong91" ||
|
|
460
|
+
basename(modules) !== "node_modules") {
|
|
461
|
+
return undefined;
|
|
462
|
+
}
|
|
463
|
+
const parent = dirname(modules);
|
|
464
|
+
return basename(parent) === "lib" ? dirname(parent) : parent;
|
|
465
|
+
}
|
|
448
466
|
export async function runUpgrade(config, checkOnly) {
|
|
449
467
|
console.log(`Current version: ${CPAC_VERSION}`);
|
|
450
468
|
let latest;
|
|
@@ -480,7 +498,11 @@ export async function runUpgrade(config, checkOnly) {
|
|
|
480
498
|
}
|
|
481
499
|
else {
|
|
482
500
|
console.log(`Installing @yhong91/cpac@${latest}`);
|
|
483
|
-
const
|
|
501
|
+
const prefix = runningNpmPrefix();
|
|
502
|
+
const args = ["install", "-g", `@yhong91/cpac@${latest}`];
|
|
503
|
+
if (prefix)
|
|
504
|
+
args.splice(2, 0, `--prefix=${prefix}`);
|
|
505
|
+
const result = spawnSync("npm", args, { stdio: "inherit" });
|
|
484
506
|
if (result.status !== 0) {
|
|
485
507
|
console.error("npm install failed");
|
|
486
508
|
return result.status ?? 1;
|
|
@@ -492,5 +514,12 @@ export async function runUpgrade(config, checkOnly) {
|
|
|
492
514
|
return 0;
|
|
493
515
|
}
|
|
494
516
|
console.log(`Syncing ${installed.join(", ")}`);
|
|
517
|
+
if (!upToDate) {
|
|
518
|
+
const cli = join(dirname(fileURLToPath(import.meta.url)), "cpac.js");
|
|
519
|
+
const result = spawnSync(process.execPath, [cli, "sync"], {
|
|
520
|
+
stdio: "inherit",
|
|
521
|
+
});
|
|
522
|
+
return result.status ?? 1;
|
|
523
|
+
}
|
|
495
524
|
return runSync(config, [], { all: false, dryRun: false });
|
|
496
525
|
}
|
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
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
import { catalogModelId, catalogModelRows, dropAgent, fetchCatalog, recordWrittenConfig, restoreOwnedConfig, } from "../config.js";
|
|
3
|
+
import { basename, dirname, join } from "node:path";
|
|
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";
|
|
@@ -15,8 +15,40 @@ export function hermesHome() {
|
|
|
15
15
|
}
|
|
16
16
|
return join(homedir(), ".hermes");
|
|
17
17
|
}
|
|
18
|
+
export function hermesRoot() {
|
|
19
|
+
const home = hermesHome();
|
|
20
|
+
const parent = dirname(home);
|
|
21
|
+
if (basename(parent) === "profiles")
|
|
22
|
+
return dirname(parent);
|
|
23
|
+
return home;
|
|
24
|
+
}
|
|
18
25
|
export function hermesConfigPath() {
|
|
19
|
-
return join(
|
|
26
|
+
return join(hermesRoot(), "config.yaml");
|
|
27
|
+
}
|
|
28
|
+
export function hermesConfigPaths() {
|
|
29
|
+
const root = hermesRoot();
|
|
30
|
+
const paths = [join(root, "config.yaml")];
|
|
31
|
+
let names;
|
|
32
|
+
try {
|
|
33
|
+
names = readdirSync(join(root, "profiles"));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return paths;
|
|
37
|
+
}
|
|
38
|
+
for (const name of names.sort()) {
|
|
39
|
+
if (name.startsWith(".") || name === "default")
|
|
40
|
+
continue;
|
|
41
|
+
const dir = join(root, "profiles", name);
|
|
42
|
+
try {
|
|
43
|
+
if (!statSync(dir).isDirectory())
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
paths.push(join(dir, "config.yaml"));
|
|
50
|
+
}
|
|
51
|
+
return paths;
|
|
20
52
|
}
|
|
21
53
|
function lineIndent(line) {
|
|
22
54
|
const match = line.match(/^( *)/);
|
|
@@ -121,14 +153,17 @@ function hasProvidersCpac(content) {
|
|
|
121
153
|
const indent = childIndent(lines, range.idx, range.end, 0);
|
|
122
154
|
return Boolean(findCpacChild(lines, range.idx, range.end, indent));
|
|
123
155
|
}
|
|
124
|
-
|
|
156
|
+
function pathHasCpac(path) {
|
|
125
157
|
try {
|
|
126
|
-
return hasProvidersCpac(readFileSync(
|
|
158
|
+
return hasProvidersCpac(readFileSync(path, "utf8"));
|
|
127
159
|
}
|
|
128
160
|
catch {
|
|
129
161
|
return false;
|
|
130
162
|
}
|
|
131
163
|
}
|
|
164
|
+
export function isHermesConfigInstalled() {
|
|
165
|
+
return hermesConfigPaths().some(pathHasCpac);
|
|
166
|
+
}
|
|
132
167
|
function stripProvidersCpac(content) {
|
|
133
168
|
const eol = content.includes("\r\n") ? "\r\n" : "\n";
|
|
134
169
|
const lines = content.split(/\r?\n/);
|
|
@@ -194,6 +229,20 @@ function writeHermesProvider(path, port, rows) {
|
|
|
194
229
|
const mode = existsSync(path) ? statSync(path).mode & 0o7777 : 0o600;
|
|
195
230
|
atomicWrite(path, Buffer.from(content), mode);
|
|
196
231
|
}
|
|
232
|
+
function stripHermesFile(path) {
|
|
233
|
+
if (!existsSync(path))
|
|
234
|
+
return;
|
|
235
|
+
const current = readFileSync(path, "utf8");
|
|
236
|
+
if (!hasProvidersCpac(current))
|
|
237
|
+
return;
|
|
238
|
+
const stripped = stripProvidersCpac(current);
|
|
239
|
+
if (stripped.trim() === "") {
|
|
240
|
+
rmSync(path, { force: true });
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const mode = statSync(path).mode & 0o7777;
|
|
244
|
+
atomicWrite(path, Buffer.from(stripped.endsWith("\n") ? stripped : `${stripped}\n`), mode);
|
|
245
|
+
}
|
|
197
246
|
export async function installHermesConfig(config) {
|
|
198
247
|
const apiKey = await resolveApiKey(config.api_key_env);
|
|
199
248
|
const catalog = await fetchCatalog(config.cpa_url, apiKey);
|
|
@@ -208,12 +257,14 @@ export async function installHermesConfig(config) {
|
|
|
208
257
|
if (rows.length === 0)
|
|
209
258
|
throw new CPACError("CPA catalog contains no models");
|
|
210
259
|
const { proxy, fingerprint, started } = await ensureLoopbackProxy(config, apiKey);
|
|
211
|
-
const
|
|
212
|
-
const
|
|
260
|
+
const targets = hermesConfigPaths();
|
|
261
|
+
const primary = targets[0];
|
|
262
|
+
const hadFile = existsSync(primary);
|
|
213
263
|
try {
|
|
214
|
-
ensureCpacBackup(config.state_dir, "hermes",
|
|
215
|
-
|
|
216
|
-
|
|
264
|
+
ensureCpacBackup(config.state_dir, "hermes", primary, !pathHasCpac(primary));
|
|
265
|
+
for (const target of targets)
|
|
266
|
+
writeHermesProvider(target, proxy.port, rows);
|
|
267
|
+
recordWrittenConfig(config.state_dir, "hermes", primary, {
|
|
217
268
|
id: proxy.id,
|
|
218
269
|
pid: proxy.pid,
|
|
219
270
|
port: proxy.port,
|
|
@@ -225,15 +276,24 @@ export async function installHermesConfig(config) {
|
|
|
225
276
|
await stopProxyProcess(proxy);
|
|
226
277
|
throw error;
|
|
227
278
|
}
|
|
228
|
-
console.log(`Installed Hermes provider "${HERMES_PROVIDER_ID}"
|
|
279
|
+
console.log(`Installed Hermes provider "${HERMES_PROVIDER_ID}":`);
|
|
280
|
+
for (const target of targets)
|
|
281
|
+
console.log(` ${target}`);
|
|
229
282
|
}
|
|
230
283
|
export async function uninstallHermesConfig(config) {
|
|
231
|
-
const
|
|
284
|
+
const targets = hermesConfigPaths();
|
|
285
|
+
const primary = targets[0];
|
|
232
286
|
if (!isHermesConfigInstalled())
|
|
233
287
|
throw new CPACError("Hermes CPAC provider is not installed");
|
|
234
|
-
|
|
288
|
+
if (readState(config.state_dir)?.agents?.hermes || pathHasCpac(primary)) {
|
|
289
|
+
restoreOwnedConfig(config.state_dir, "hermes", primary, (current) => Buffer.from(stripProvidersCpac(new TextDecoder("utf-8").decode(current))));
|
|
290
|
+
}
|
|
291
|
+
for (const target of targets.slice(1))
|
|
292
|
+
stripHermesFile(target);
|
|
235
293
|
const { stopProxy } = dropAgent(config.state_dir, "hermes");
|
|
236
294
|
if (stopProxy)
|
|
237
295
|
await stopProxyProcess(stopProxy);
|
|
238
|
-
console.log(`Removed Hermes provider "${HERMES_PROVIDER_ID}"
|
|
296
|
+
console.log(`Removed Hermes provider "${HERMES_PROVIDER_ID}":`);
|
|
297
|
+
for (const target of targets)
|
|
298
|
+
console.log(` ${target}`);
|
|
239
299
|
}
|