@dan-ai-studio/dshopencodego 0.1.83 → 0.1.84

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.
@@ -69,3 +69,16 @@ export declare const MODEL_METADATA_MAX_BYTES: number;
69
69
  * without a restart, and long enough that a bad id costs one fetch.
70
70
  */
71
71
  export declare const UNKNOWN_MODEL_PROBE_COOLDOWN_MS: number;
72
+ /**
73
+ * How old a catalog may be before it counts as stale, no matter the
74
+ * configured TTL.
75
+ *
76
+ * `refreshMinutes` answers "how long may requests reuse one listing", and a
77
+ * deployment may set it as high as seven days. Staleness answers a different
78
+ * question — "how long may the plugin go without asking what changed" — and
79
+ * that one is capped at a day: past it, the next read refreshes in the
80
+ * background even when the TTL has not expired, so a gateway that adds or
81
+ * retires models is noticed within a day. A lower TTL keeps working exactly
82
+ * as before; the cap only shortens longer ones.
83
+ */
84
+ export declare const MAX_CATALOG_STALENESS_MS: number;
@@ -23,12 +23,24 @@ import type { LlmDiscoveredModel } from '@deepseek-ai/dsh-llm';
23
23
  import type { GoDoc } from './go-doc.ts';
24
24
  import type { ModelDefaults, ModelFacts } from './metadata.ts';
25
25
  import type { GoQuota } from '../go-limits.ts';
26
- export { PROVIDER_ID, DISPLAY_NAME, DEFAULT_BASE_URL, CONSOLE_BASE_URL } from './constants.ts';
26
+ export { PROVIDER_ID, DISPLAY_NAME, DEFAULT_BASE_URL, CONSOLE_BASE_URL, MAX_CATALOG_STALENESS_MS } from './constants.ts';
27
27
  export { readModelIds, fetchModelIds } from './gateway.ts';
28
28
  export { readOnlineMetadata, toPiModel, modelBaseURL } from './metadata.ts';
29
29
  export type { ModelFacts, OnlineMetadata, ModelDefaults } from './metadata.ts';
30
30
  export { decideProtocol, inferProtocol, protocolOfNpm } from './protocol.ts';
31
31
  export type { ProtocolDecision, ProtocolSource, WireProtocol } from './protocol.ts';
32
+ /**
33
+ * Whether a snapshot still answers without a refresh.
34
+ *
35
+ * Freshness has a hard ceiling on top of the configured TTL: past a day, a
36
+ * catalog counts as stale no matter how long the TTL runs, and the next read
37
+ * refreshes it in the background. A shorter TTL behaves exactly as before;
38
+ * the ceiling only shortens longer ones. A snapshot that has never been
39
+ * built (age zero or less) is never fresh.
40
+ *
41
+ * Exported so tests can pin the ceiling without waiting a day.
42
+ */
43
+ export declare function snapshotIsFresh(fetchedAtMs: number, nowMs: number, refreshMs: number): boolean;
32
44
  /**
33
45
  * One source the catalog could not read.
34
46
  *
@@ -142,6 +154,16 @@ export interface CatalogOptions {
142
154
  * nothing else.
143
155
  */
144
156
  readonly cacheDir?: string;
157
+ /**
158
+ * A snapshot baked in at publish time: the floor when the disk cache is
159
+ * empty (fresh installs, wiped caches, a first run on a new machine).
160
+ *
161
+ * The disk always wins when it has anything to say, and the endpoint must
162
+ * match — a seed built for Go never serves Console and vice versa. Like a
163
+ * remembered disk reading it is served stale with an immediate background
164
+ * refresh, never presented as current.
165
+ */
166
+ readonly seed?: unknown;
145
167
  /**
146
168
  * The fetch the gateway listing is read through.
147
169
  *
@@ -220,12 +242,22 @@ export declare class OpencodeGoCatalog {
220
242
  */
221
243
  settled(): Promise<void>;
222
244
  /**
223
- * The catalog a previous build produced, read from disk.
245
+ * Present a past listing as a stale-but-serving snapshot.
224
246
  *
225
247
  * It is presented as what it is: a listing from the past, being brought up
226
248
  * to date in the background. The facts it carries are the ones the last
227
249
  * build produced, so the protocols and the thinking levels are the ones
228
250
  * that were true then.
251
+ */
252
+ private presentRemembered;
253
+ /**
254
+ * The catalog a previous build produced, read from disk — or, when the
255
+ * disk holds nothing, the snapshot baked in at publish time.
256
+ *
257
+ * The bundled seed is the floor, not a peer: the disk always wins when it
258
+ * has anything to say, and the endpoint must match — a seed built for Go
259
+ * never serves Console and vice versa. Either way the reading is served
260
+ * stale with an immediate background refresh, never presented as current.
229
261
  * @returns the remembered catalog, or undefined when there is none.
230
262
  */
231
263
  private readRememberedSnapshot;
@@ -24,6 +24,43 @@
24
24
  import type { GoQuota } from '../go-limits.ts';
25
25
  import type { ModelFacts } from './metadata.ts';
26
26
  import type { WireProtocol } from './protocol.ts';
27
+ /** What the cache file holds. */
28
+ export interface SnapshotFile {
29
+ readonly version: number;
30
+ /** The facts, by model id. */
31
+ readonly facts: Record<string, ModelFacts>;
32
+ /** The provider's allowances, by model id. */
33
+ readonly quotas?: Record<string, GoQuota>;
34
+ /** The provider's protocols, by model id. */
35
+ readonly protocols?: Record<string, WireProtocol>;
36
+ /**
37
+ * The endpoint the snapshot was built against. Disk readings omit it —
38
+ * they were built for the endpoint in force when they were written — but
39
+ * a bundled seed must name its endpoint, or it could serve one gateway's
40
+ * listing to another.
41
+ */
42
+ readonly baseURL?: string;
43
+ /** When the catalog was built. */
44
+ readonly updatedAt: number;
45
+ }
46
+ /** A validated snapshot file, ready to serve. */
47
+ export interface ParsedSnapshotFile {
48
+ readonly facts: ReadonlyMap<string, ModelFacts>;
49
+ readonly quotas: ReadonlyMap<string, GoQuota>;
50
+ readonly protocols: ReadonlyMap<string, WireProtocol>;
51
+ readonly baseURL: string | undefined;
52
+ readonly updatedAt: number;
53
+ }
54
+ /**
55
+ * Validate a snapshot document, wherever it came from.
56
+ *
57
+ * One validator serves both readers: the disk cache and the bundled seed
58
+ * share the format on purpose, so a seed the format outgrows is refused
59
+ * rather than half-read.
60
+ * @param value - the parsed JSON document.
61
+ * @returns the validated reading, or undefined.
62
+ */
63
+ export declare function parseSnapshotFile(value: unknown): ParsedSnapshotFile | undefined;
27
64
  /**
28
65
  * The catalog a previous build produced.
29
66
  *
@@ -32,12 +69,7 @@ import type { WireProtocol } from './protocol.ts';
32
69
  * @param cacheDir - the plugin's cache directory.
33
70
  * @returns the remembered catalog, or undefined.
34
71
  */
35
- export declare function readSnapshotCache(cacheDir: string | undefined): {
36
- readonly facts: ReadonlyMap<string, ModelFacts>;
37
- readonly quotas: ReadonlyMap<string, GoQuota>;
38
- readonly protocols: ReadonlyMap<string, WireProtocol>;
39
- readonly updatedAt: number;
40
- } | undefined;
72
+ export declare function readSnapshotCache(cacheDir: string | undefined): ParsedSnapshotFile | undefined;
41
73
  /**
42
74
  * Remember the catalog a build produced.
43
75
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dan-ai-studio/dshopencodego",
3
- "version": "0.1.83",
3
+ "version": "0.1.84",
4
4
  "description": "OpenCode Go provider for DeepSeek Harness: live gateway catalog, per-conversation session header, usage",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -63,6 +63,8 @@
63
63
  "lint": "oxlint",
64
64
  "lint:fix": "oxlint --fix",
65
65
  "build": "node scripts/build.mjs",
66
+ "seed": "node scripts/seed-catalog.mjs",
67
+ "seed": "node scripts/seed-catalog.mjs",
66
68
  "typecheck": "tsc -p tsconfig.host.json --noEmit && tsc -p tsconfig.client.json --noEmit && tsc -p tsconfig.tests.json --noEmit",
67
69
  "test": "vitest run",
68
70
  "test:coverage": "vitest run --coverage",