@yhong91/cpac 0.2.8 → 0.2.9

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/dist/agents.js CHANGED
@@ -3,8 +3,9 @@ import { existsSync, readFileSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
4
  import { basename, dirname, join } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
- import { MAX_SPAWN_MODELS, fetchCatalog, loadCatalogSlugs, pickSpawnModels, saveCodexSetup, saveSpawnModels, } from "./config.js";
7
- import { markModelsApplied, refreshModelIndex } from "./models.js";
6
+ import { MAX_SPAWN_MODELS, fetchCatalog, loadCatalogSlugs, pickSpawnModels, readState, saveCodexSetup, saveSpawnModels, saveState, stateProxy, } from "./config.js";
7
+ import { ensureLoopbackProxy } from "./proxy.js";
8
+ import { diffModelSlugs, markModelsApplied, readModelIndex, refreshModelIndex, } from "./models.js";
8
9
  import { MANAGED_MARKER, inject, restore } from "./targets/codex.js";
9
10
  import { grokConfigPath, grokHome, installGrokConfig, isGrokConfigInstalled, uninstallGrokConfig, } from "./targets/grok.js";
10
11
  import { installKimiConfig, isKimiConfigInstalled, kimiConfigPath, uninstallKimiConfig, } from "./targets/kimi.js";
@@ -135,6 +136,7 @@ export async function runTargetLauncher(config, targetId, args, executable = tar
135
136
  await target.install(config, false, options.v2Off, options.maxContext);
136
137
  }
137
138
  }
139
+ await ensureLauncherProxy(config, target, options);
138
140
  return await new Promise((resolvePromise, rejectPromise) => {
139
141
  const env = targetId === "zed"
140
142
  ? { ...process.env, CPAC_API_KEY: process.env.CPAC_API_KEY || "cpac" }
@@ -178,6 +180,28 @@ export function detectClientVersion(binary) {
178
180
  }
179
181
  return undefined;
180
182
  }
183
+ async function ensureLauncherProxy(config, target, options) {
184
+ const apiKey = await resolveApiKey(config.api_key_env);
185
+ const before = readState(config.state_dir);
186
+ const previous = before ? stateProxy(before) : null;
187
+ if (!previous) {
188
+ await target.install(config, false, options.v2Off, options.maxContext);
189
+ return;
190
+ }
191
+ const { proxy, fingerprint, started } = await ensureLoopbackProxy(config, apiKey);
192
+ if (!started)
193
+ return;
194
+ saveState(config.state_dir, {
195
+ ...before,
196
+ proxy_id: proxy.id,
197
+ proxy_pid: proxy.pid,
198
+ proxy_port: proxy.port,
199
+ proxy_fingerprint: fingerprint,
200
+ });
201
+ if (previous.port === proxy.port)
202
+ return;
203
+ await target.install(config, false, options.v2Off, options.maxContext);
204
+ }
181
205
  function binaryOnPath(name) {
182
206
  if (!/^[\w.-]+$/.test(name))
183
207
  return false;
@@ -427,17 +451,21 @@ function syncTargetIds(config, requested, all) {
427
451
  }
428
452
  return selected.map((target) => target.id);
429
453
  }
430
- function formatModelVersion(iso) {
431
- const date = new Date(iso);
432
- if (Number.isNaN(date.getTime()))
433
- return iso;
434
- const pad = (value) => String(value).padStart(2, "0");
435
- const clock = `${pad(date.getHours())}:${pad(date.getMinutes())}`;
436
- // ponytail: minute display; the stored ISO still separates same-minute updates
437
- const monthDay = `${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
438
- return date.getFullYear() === new Date().getFullYear()
439
- ? `${monthDay} ${clock}`
440
- : `${date.getFullYear()}-${monthDay} ${clock}`;
454
+ function logModelChanges(previous, next) {
455
+ const { added, removed } = diffModelSlugs(previous.map((model) => model.slug), next.map((model) => model.slug));
456
+ const addedSet = new Set(added);
457
+ const previousJson = new Map(previous.map((model) => [model.slug, JSON.stringify(model)]));
458
+ for (const slug of added)
459
+ console.log(`+ ${slug}`);
460
+ for (const slug of removed)
461
+ console.log(`- ${slug}`);
462
+ for (const model of next) {
463
+ if (addedSet.has(model.slug))
464
+ continue;
465
+ const before = previousJson.get(model.slug);
466
+ if (before !== undefined && before !== JSON.stringify(model))
467
+ console.log(`~ ${model.slug}`);
468
+ }
441
469
  }
442
470
  export async function runSync(config, requested, options) {
443
471
  const ids = syncTargetIds(config, requested, options.all);
@@ -446,16 +474,11 @@ export async function runSync(config, requested, options) {
446
474
  }
447
475
  const apiKey = await resolveApiKey(config.api_key_env);
448
476
  const catalog = await fetchCatalog(config.cpa_url, apiKey);
477
+ const previous = readModelIndex(config.state_dir);
449
478
  const { index, changed } = refreshModelIndex(config.state_dir, catalog.bytes, new Date().toISOString(), !options.dryRun);
450
- console.log(`models ${formatModelVersion(index.updated_at)}${changed ? (options.dryRun ? " would update" : " updated") : " unchanged"}`);
479
+ if (changed)
480
+ logModelChanges(previous?.models ?? [], index.models);
451
481
  const pending = ids.filter((id) => index.applied[id] !== index.updated_at);
452
- for (const id of ids) {
453
- const applied = index.applied[id];
454
- if (applied === index.updated_at)
455
- console.log(` ${id}: unchanged`);
456
- else if (applied)
457
- console.log(` ${id}: ${formatModelVersion(applied)}`);
458
- }
459
482
  if (pending.length === 0)
460
483
  return 0;
461
484
  // ponytail: version is the model-list timestamp only. spawn_models, v2_off,
package/dist/proxy.js CHANGED
@@ -346,6 +346,9 @@ export async function ensureLoopbackProxy(config, apiKey) {
346
346
  if (existing)
347
347
  await stopProxyProcess(existing);
348
348
  const proxy = await startProxyProcess(config, apiKey);
349
+ if (!existing) {
350
+ console.log(`loopback proxy listening on http://127.0.0.1:${proxy.port}/v1`);
351
+ }
349
352
  return { proxy, fingerprint, started: true };
350
353
  }
351
354
  export async function runProxy(config) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yhong91/cpac",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Connect Codex and Claude Code to a remote CLIProxyAPI gateway",
5
5
  "type": "module",
6
6
  "bin": {