@yhong91/cpac 0.2.10 → 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 +1 -1
- package/dist/cpac.js +1 -1
- package/dist/pricing.js +48 -4
- package/package.json +1 -1
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)
|
|
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/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
|
|
211
|
-
if (Object.keys(
|
|
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) {
|