agentsw 0.7.2 → 0.8.0

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/modelsdev.js CHANGED
@@ -5,39 +5,57 @@ import { readJsonIfExists, writeFileAtomic } from "./fsutil.js";
5
5
  const API_URL = "https://models.dev/api.json";
6
6
  const CACHE_FILE = path.join(configDir, "models-dev.json");
7
7
  const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
8
+ const catalogFetchedAt = new WeakMap();
9
+ /** Fetch time is kept outside the provider dictionary and never changes its public shape. */
10
+ export function getCatalogFetchedAt(catalog) {
11
+ return catalogFetchedAt.get(catalog);
12
+ }
13
+ function readCachedCatalog() {
14
+ const catalog = readJsonIfExists(CACHE_FILE);
15
+ if (catalog) {
16
+ try {
17
+ catalogFetchedAt.set(catalog, fs.statSync(CACHE_FILE).mtime.toISOString());
18
+ }
19
+ catch {
20
+ // A concurrently removed cache still supplies useful metadata without a fetch time.
21
+ }
22
+ }
23
+ return catalog;
24
+ }
8
25
  export async function loadCatalog(opts = {}) {
9
26
  if (!opts.refresh) {
10
27
  try {
11
28
  const stat = fs.statSync(CACHE_FILE);
12
29
  const fresh = Date.now() - stat.mtimeMs < CACHE_TTL_MS;
13
30
  if (fresh || opts.offline)
14
- return readJsonIfExists(CACHE_FILE);
31
+ return readCachedCatalog();
15
32
  }
16
33
  catch {
17
34
  /* no cache */
18
35
  }
19
36
  }
20
37
  if (opts.offline)
21
- return readJsonIfExists(CACHE_FILE);
38
+ return readCachedCatalog();
22
39
  try {
23
40
  const res = await fetch(API_URL, { signal: AbortSignal.timeout(15000) });
24
41
  if (!res.ok)
25
42
  throw new Error(`models.dev responded ${res.status}`);
26
43
  const text = await res.text();
27
- JSON.parse(text); // validate before caching
44
+ const catalog = JSON.parse(text); // validate before caching
28
45
  writeFileAtomic(CACHE_FILE, text);
29
- return JSON.parse(text);
46
+ catalogFetchedAt.set(catalog, new Date().toISOString());
47
+ return catalog;
30
48
  }
31
49
  catch (err) {
32
50
  // network failure: fall back to stale cache if present
33
- const cached = readJsonIfExists(CACHE_FILE);
51
+ const cached = readCachedCatalog();
34
52
  if (cached)
35
53
  return cached;
36
54
  process.stderr.write(`warning: could not fetch models.dev catalog: ${err.message}\n`);
37
55
  return undefined;
38
56
  }
39
57
  }
40
- function toModelSpec(id, m) {
58
+ export function toModelSpec(id, m) {
41
59
  const efforts = m.reasoning_options?.find((o) => o.type === "effort")?.values;
42
60
  return {
43
61
  id,
@@ -46,8 +64,10 @@ function toModelSpec(id, m) {
46
64
  maxInput: m.limit?.input,
47
65
  maxOutput: m.limit?.output,
48
66
  reasoning: m.reasoning,
49
- reasoningEfforts: efforts?.length ? efforts : undefined,
50
- imageInput: m.attachment || m.modalities?.input?.includes("image") || undefined,
67
+ reasoningEfforts: efforts,
68
+ imageInput: m.attachment !== undefined || m.modalities?.input !== undefined
69
+ ? m.attachment === true || m.modalities?.input?.includes("image") === true
70
+ : undefined,
51
71
  cost: m.cost
52
72
  ? { input: m.cost.input, output: m.cost.output, cacheRead: m.cost.cache_read, cacheWrite: m.cost.cache_write }
53
73
  : undefined,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentsw",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "CLI provider switcher for coding agents: manage OpenAI/Anthropic-protocol providers in one place and sync them, with models.dev metadata, into Claude Code, Codex, omp, pi, prime-agent, opencode, Hermes, WorkBuddy and DeepSeek Harness",
5
5
  "type": "module",
6
6
  "repository": {