@yhong91/cpac 0.2.9 → 0.2.11

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
@@ -299,7 +299,7 @@ cpac install pi
299
299
  cpac pi
300
300
  ```
301
301
 
302
- 它在 `~/.pi/agent/models.json` 的 `providers.cpac` 写入 loopback `baseUrl`、占位 `apiKey`、`api: openai-responses`,以及 CPA catalog 的模型列表。每个模型的 `cost` 是 USD / 百万 token(input / output / cacheRead / cacheWrite)。注入时若 `~/.cpac/model-prices.json` 超过 24 小时会从 [models.dev](https://models.dev) 刷新(官方价优先,否则 OpenRouter;失败则用打包快照);未知模型为 0。不改当前默认模型;切换用 `/model`。密钥由 loopback 注入。旧的 `npm:@yhong91/cpac` package 和 `extensions/pi-cpac.ts` 会在 install / restore 时删掉。
302
+ 它在 `~/.pi/agent/models.json` 的 `providers.cpac` 写入 loopback `baseUrl`、占位 `apiKey`、`api: openai-responses`,以及 CPA catalog 的模型列表。每个模型的 `cost` 是 USD / 百万 token(input / output / cacheRead / cacheWrite)。注入时若 `~/.cpac/model-prices.json` 超过 24 小时会从 [models.dev](https://models.dev) 刷新:拉到的价格与缓存对比,有差异才更新,这次没拉到的条目保留(官方价优先,否则 OpenRouter;拉取失败则用已有缓存或打包快照);未知模型为 0。不改当前默认模型;切换用 `/model`。密钥由 loopback 注入。旧的 `npm:@yhong91/cpac` package 和 `extensions/pi-cpac.ts` 会在 install / restore 时删掉。
303
303
 
304
304
  卸载:`cpac restore pi` 或 `cpac pi clear`。尊重 `PI_CODING_AGENT_DIR`。
305
305
 
package/dist/agents.js CHANGED
@@ -451,10 +451,20 @@ function syncTargetIds(config, requested, all) {
451
451
  }
452
452
  return selected.map((target) => target.id);
453
453
  }
454
+ const MODEL_CHANGE_FIELDS = [
455
+ "display_name",
456
+ "context_window",
457
+ "max_context_window",
458
+ "max_tokens",
459
+ "max_output_tokens",
460
+ "input_modalities",
461
+ "supported_reasoning_levels",
462
+ "default_reasoning_level",
463
+ ];
454
464
  function logModelChanges(previous, next) {
455
465
  const { added, removed } = diffModelSlugs(previous.map((model) => model.slug), next.map((model) => model.slug));
456
466
  const addedSet = new Set(added);
457
- const previousJson = new Map(previous.map((model) => [model.slug, JSON.stringify(model)]));
467
+ const previousBySlug = new Map(previous.map((model) => [model.slug, model]));
458
468
  for (const slug of added)
459
469
  console.log(`+ ${slug}`);
460
470
  for (const slug of removed)
@@ -462,9 +472,12 @@ function logModelChanges(previous, next) {
462
472
  for (const model of next) {
463
473
  if (addedSet.has(model.slug))
464
474
  continue;
465
- const before = previousJson.get(model.slug);
466
- if (before !== undefined && before !== JSON.stringify(model))
467
- console.log(`~ ${model.slug}`);
475
+ const before = previousBySlug.get(model.slug);
476
+ if (!before)
477
+ continue;
478
+ const fields = MODEL_CHANGE_FIELDS.filter((key) => JSON.stringify(before[key] ?? null) !== JSON.stringify(model[key] ?? null));
479
+ if (fields.length > 0)
480
+ console.log(`~ ${model.slug} ${fields.join(" ")}`);
468
481
  }
469
482
  }
470
483
  export async function runSync(config, requested, options) {
package/dist/cpac.js CHANGED
@@ -26,7 +26,7 @@ export { detectClientVersion, detectTargets, runInstall, runRestore, runSync, ru
26
26
  export { installGrokConfig, isGrokConfigInstalled, uninstallGrokConfig, } from "./targets/grok.js";
27
27
  export { installKimiConfig, isKimiConfigInstalled, uninstallKimiConfig, } from "./targets/kimi.js";
28
28
  export { installPiConfig, isPiConfigInstalled, uninstallPiConfig, PI_PROVIDER_ID, } from "./targets/pi.js";
29
- export { compactModelPrices, ensureModelPrices, lookupModelCost, resetModelPrices, } from "./pricing.js";
29
+ export { compactModelPrices, ensureModelPrices, lookupModelCost, mergeModelPrices, resetModelPrices, } from "./pricing.js";
30
30
  export { installZedConfig, isZedConfigInstalled, uninstallZedConfig, ZED_PROVIDER_ID, } from "./targets/zed.js";
31
31
  export { installHermesConfig, isHermesConfigInstalled, uninstallHermesConfig, HERMES_PROVIDER_ID, } from "./targets/hermes.js";
32
32
  export { CODEBUDDY_API_KEY_PLACEHOLDER, codebuddyModelsPath, installCodebuddyConfig, isCodebuddyConfigInstalled, uninstallCodebuddyConfig, } from "./targets/codebuddy.js";
package/dist/pricing.js CHANGED
@@ -29,6 +29,9 @@ function cachePath() {
29
29
  return override;
30
30
  return join(homedir(), ".cpac", "model-prices.json");
31
31
  }
32
+ function overlayPath() {
33
+ return `${cachePath()}.overlay`;
34
+ }
32
35
  function parseTable(raw) {
33
36
  const next = new Map();
34
37
  if (!objectValue(raw))
@@ -192,13 +195,53 @@ function cacheIsFresh(path) {
192
195
  return false;
193
196
  }
194
197
  }
198
+ function applyOverlay(base) {
199
+ const extra = readTableFile(overlayPath());
200
+ if (!extra)
201
+ return base;
202
+ for (const [key, cost] of extra)
203
+ base.set(key, cost);
204
+ return base;
205
+ }
195
206
  function applyTable(next) {
196
- table = next;
207
+ table = applyOverlay(next);
197
208
  }
198
209
  function writeCache(path, prices) {
199
210
  mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
200
211
  atomicWrite(path, Buffer.from(`${JSON.stringify(prices)}\n`), 0o600);
201
212
  }
213
+ function readPriceJson(path) {
214
+ if (!existsSync(path))
215
+ return undefined;
216
+ try {
217
+ return JSON.parse(readFileSync(path, "utf8"));
218
+ }
219
+ catch {
220
+ return undefined;
221
+ }
222
+ }
223
+ function costTuple(cost) {
224
+ return [cost.input, cost.output, cost.cacheRead, cost.cacheWrite];
225
+ }
226
+ /** Keep prices the fetch did not return. Update a key only when the fetched tuple differs. */
227
+ export function mergeModelPrices(existing, fetched) {
228
+ const table = {};
229
+ for (const [key, cost] of parseTable(existing))
230
+ table[key] = costTuple(cost);
231
+ for (const [key, cost] of parseTable(fetched)) {
232
+ const next = costTuple(cost);
233
+ const prev = table[key];
234
+ if (prev &&
235
+ prev[0] === next[0] &&
236
+ prev[1] === next[1] &&
237
+ prev[2] === next[2] &&
238
+ prev[3] === next[3]) {
239
+ continue;
240
+ }
241
+ table[key] = next;
242
+ }
243
+ return Object.fromEntries(Object.entries(table).sort(([a], [b]) => a.localeCompare(b)));
244
+ }
202
245
  /** Refresh from models.dev when the local cache is older than 24h; fall back to bundled snapshot. */
203
246
  export async function ensureModelPrices() {
204
247
  const cache = cachePath();
@@ -207,8 +250,9 @@ export async function ensureModelPrices() {
207
250
  return;
208
251
  }
209
252
  try {
210
- const prices = await fetchModelPriceTable();
211
- if (Object.keys(prices).length > 0) {
253
+ const fetched = await fetchModelPriceTable();
254
+ if (Object.keys(fetched).length > 0) {
255
+ const prices = mergeModelPrices(readPriceJson(cache), fetched);
212
256
  writeCache(cache, prices);
213
257
  applyTable(parseTable(prices));
214
258
  return;
@@ -222,7 +266,7 @@ export async function ensureModelPrices() {
222
266
  function loadTable() {
223
267
  if (table)
224
268
  return table;
225
- table = loadBundled();
269
+ table = applyOverlay(readTableFile(cachePath()) ?? loadBundled());
226
270
  return table;
227
271
  }
228
272
  function lookupKeys(slug) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yhong91/cpac",
3
- "version": "0.2.9",
3
+ "version": "0.2.11",
4
4
  "description": "Connect Codex and Claude Code to a remote CLIProxyAPI gateway",
5
5
  "type": "module",
6
6
  "bin": {