@webfueler/oc-dash 0.1.4 → 0.1.6

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.
@@ -0,0 +1,67 @@
1
+ import { ocGetJson } from "./opencode.js";
2
+ /**
3
+ * Pure parse of the opencode2 service's GET /api/model payload into the
4
+ * providerID/id -> name map. Entries missing any of the three fields — or
5
+ * carrying an empty or whitespace-only name — drop out, so the client's
6
+ * fallback (the raw id) never receives a blank or wrong label. Accepts both
7
+ * the raw array and the { data: [...] } wrapper the promise client can
8
+ * produce.
9
+ */
10
+ export function parseModelNames(payload) {
11
+ if (payload && typeof payload === "object" && !Array.isArray(payload)) {
12
+ const wrapped = payload;
13
+ if (Array.isArray(wrapped.data))
14
+ payload = wrapped.data;
15
+ }
16
+ if (!Array.isArray(payload))
17
+ return {};
18
+ const names = {};
19
+ for (const entry of payload) {
20
+ if (!entry || typeof entry !== "object")
21
+ continue;
22
+ const { providerID, id, name } = entry;
23
+ if (typeof providerID !== "string" || typeof id !== "string" || typeof name !== "string") {
24
+ continue;
25
+ }
26
+ // Trim before the non-empty check so a whitespace-only name can never
27
+ // reach the label surfaces as a blank; surrounding whitespace on a real
28
+ // name is noise, so the trimmed value is what gets stored.
29
+ const trimmed = name.trim();
30
+ if (!providerID || !id || !trimmed)
31
+ continue;
32
+ names[`${providerID}/${id}`] = trimmed;
33
+ }
34
+ return names;
35
+ }
36
+ // The model catalog is static for the life of a service, so a success lives
37
+ // an hour and a failure retries sooner — the update-check pattern
38
+ // (index.ts:43-44): the caller never waits on a cold fetch more than once
39
+ // per window, and any failure degrades to an empty map (raw-id labels).
40
+ const MODEL_NAMES_TTL_MS = 60 * 60 * 1000;
41
+ const MODEL_NAMES_RETRY_MS = 5 * 60 * 1000;
42
+ let cache = null;
43
+ /**
44
+ * The cached name map. One /api/model fetch per TTL window, never per
45
+ * request; any failure is an empty map so every label surface falls back to
46
+ * today's raw-id behavior.
47
+ */
48
+ export async function modelNames(oc) {
49
+ if (cache && Date.now() < cache.expiresAt)
50
+ return cache.names;
51
+ let names;
52
+ let ttl;
53
+ try {
54
+ names = parseModelNames(await ocGetJson(oc, "/api/model"));
55
+ ttl = MODEL_NAMES_TTL_MS;
56
+ }
57
+ catch {
58
+ names = {};
59
+ ttl = MODEL_NAMES_RETRY_MS;
60
+ }
61
+ cache = { names, expiresAt: Date.now() + ttl };
62
+ return names;
63
+ }
64
+ /** Test seam: forget the module-level cache. */
65
+ export function resetModelNamesCache() {
66
+ cache = null;
67
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webfueler/oc-dash",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Local dashboard for opencode2 session costs, token totals, and subagent spend",
5
5
  "license": "MIT",
6
6
  "type": "module",