@dan-ai-studio/dshopencodego 0.1.53 → 0.1.55

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/lib/client.js CHANGED
@@ -27,6 +27,7 @@ __export(index_exports, {
27
27
  module.exports = __toCommonJS(index_exports);
28
28
 
29
29
  // src/catalog/constants.ts
30
+ var BACKGROUND_RETRY_BACKOFF_MS = 5 * 6e4;
30
31
  var MODEL_LISTING_MAX_BYTES = 1024 * 1024;
31
32
  var MODEL_METADATA_MAX_BYTES = 16 * 1024 * 1024;
32
33
  var UNKNOWN_MODEL_PROBE_COOLDOWN_MS = 5 * 6e4;
@@ -1411,7 +1412,7 @@ function failureOf(error) {
1411
1412
  }
1412
1413
  return { message: "", retainPrevious: false };
1413
1414
  }
1414
- var POLL_MS = 6e4;
1415
+ var POLL_MS = 5 * 6e4;
1415
1416
  var MAX_UNPRICED_IDS = 3;
1416
1417
  function formatUSD(value) {
1417
1418
  if (value === 0) return "$0.0000";
package/lib/index.js CHANGED
@@ -42530,6 +42530,7 @@ var MODEL_METADATA_PROVIDER = "opencode-go";
42530
42530
  var METADATA_FETCH_TIMEOUT_MS = 1e4;
42531
42531
  var MODEL_METADATA_TIMEOUT_MS = 3e4;
42532
42532
  var METADATA_RETRY_DELAY_MS = 1500;
42533
+ var BACKGROUND_RETRY_BACKOFF_MS = 5 * 6e4;
42533
42534
  var MODEL_LISTING_MAX_BYTES = 1024 * 1024;
42534
42535
  var MODEL_METADATA_MAX_BYTES = 16 * 1024 * 1024;
42535
42536
  var UNKNOWN_MODEL_PROBE_COOLDOWN_MS = 5 * 6e4;
@@ -42639,6 +42640,7 @@ var DISCOVERY_MAX_BYTES = 8 * 1024 * 1024;
42639
42640
  var DISCOVERY_TIMEOUT_MS = 1e4;
42640
42641
  var CACHE_FILE = "go-doc-paths.json";
42641
42642
  var CACHE_VERSION = 1;
42643
+ var DISCOVERY_COOLDOWN_MS = 60 * 6e4;
42642
42644
  function selectDocumentPaths(paths) {
42643
42645
  const selected = [];
42644
42646
  for (const path5 of paths) {
@@ -42699,6 +42701,31 @@ function rememberPaths(cacheDir, discovered) {
42699
42701
  }
42700
42702
  return merged;
42701
42703
  }
42704
+ function discoveryAllowed(cacheDir) {
42705
+ if (cacheDir === void 0) return true;
42706
+ try {
42707
+ const parsed = JSON.parse(readFileSync(join2(cacheDir, CACHE_FILE), "utf8"));
42708
+ if (typeof parsed.lastAttemptAt !== "number") return true;
42709
+ return Date.now() - parsed.lastAttemptAt >= DISCOVERY_COOLDOWN_MS;
42710
+ } catch {
42711
+ return true;
42712
+ }
42713
+ }
42714
+ function rememberAttempt(cacheDir) {
42715
+ if (cacheDir === void 0) return;
42716
+ try {
42717
+ mkdirSync(cacheDir, { recursive: true });
42718
+ const file = {
42719
+ version: CACHE_VERSION,
42720
+ paths: readCachedPaths(cacheDir),
42721
+ updatedAt: Date.now(),
42722
+ lastAttemptAt: Date.now()
42723
+ };
42724
+ writeFileSync(join2(cacheDir, CACHE_FILE), `${JSON.stringify(file, null, 2)}
42725
+ `, "utf8");
42726
+ } catch {
42727
+ }
42728
+ }
42702
42729
 
42703
42730
  // src/catalog/go-doc-cache.ts
42704
42731
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
@@ -42775,11 +42802,11 @@ function protocolOfEndpoint(endpoint) {
42775
42802
  return void 0;
42776
42803
  }
42777
42804
  function tableRows(lines, required) {
42778
- const start = lines.findIndex((line) => line.startsWith("|") && line.includes(required));
42805
+ const start = lines.findIndex((line) => line.trimStart().startsWith("|") && line.includes(required));
42779
42806
  if (start < 0) return [];
42780
42807
  const rows = [];
42781
42808
  for (let index = start + 2; index < lines.length; index++) {
42782
- const line = lines[index] ?? "";
42809
+ const line = (lines[index] ?? "").trimStart();
42783
42810
  if (!line.startsWith("|")) break;
42784
42811
  rows.push(line.split("|").slice(1, -1).map((entry) => entry.trim()));
42785
42812
  }
@@ -42862,18 +42889,23 @@ async function fetchGoDocument(signal, options = {}) {
42862
42889
  failures.push(...cached.failures);
42863
42890
  }
42864
42891
  const discover = options.discover ?? discoverDocumentPaths;
42865
- const discovered = await discover(signal);
42866
- if (discovered.length === 0) {
42867
- failures.push("the provider repository named no document");
42892
+ if (options.discover === void 0 && !discoveryAllowed(options.cacheDir)) {
42893
+ failures.push("the provider repository was not asked again; it was asked within the hour");
42868
42894
  } else {
42869
- const merged = rememberPaths(options.cacheDir, discovered);
42870
- const fresh = merged.filter((path5) => !GO_DOC_URLS.some((url) => url.endsWith(path5))).flatMap(mirrorsOf);
42871
- if (fresh.length === 0) {
42872
- failures.push("the provider repository named the path that was already tried");
42895
+ if (options.discover === void 0) rememberAttempt(options.cacheDir);
42896
+ const discovered = await discover(signal);
42897
+ if (discovered.length === 0) {
42898
+ failures.push("the provider repository named no document");
42873
42899
  } else {
42874
- const result = await readSources(fresh, signal, options.cacheDir);
42875
- if (result.document !== void 0) return result.document;
42876
- failures.push(...result.failures);
42900
+ const merged = rememberPaths(options.cacheDir, discovered);
42901
+ const fresh = merged.filter((path5) => !GO_DOC_URLS.some((url) => url.endsWith(path5))).flatMap(mirrorsOf);
42902
+ if (fresh.length === 0) {
42903
+ failures.push("the provider repository named the path that was already tried");
42904
+ } else {
42905
+ const result = await readSources(fresh, signal, options.cacheDir);
42906
+ if (result.document !== void 0) return result.document;
42907
+ failures.push(...result.failures);
42908
+ }
42877
42909
  }
42878
42910
  }
42879
42911
  throw new LlmError3(
@@ -43285,6 +43317,8 @@ var OpencodeGoCatalog = class {
43285
43317
  probedAt = /* @__PURE__ */ new Map();
43286
43318
  /** The background refresh, kept so a caller can wait for it to be over. */
43287
43319
  backgroundRefresh;
43320
+ /** When the last background refresh was started, for the back-off. */
43321
+ lastRefreshAttemptAt = 0;
43288
43322
  constructor(options) {
43289
43323
  this.options = options;
43290
43324
  const cached = readFactsCache(options.cacheDir);
@@ -43348,7 +43382,9 @@ var OpencodeGoCatalog = class {
43348
43382
  */
43349
43383
  refreshInBackground() {
43350
43384
  if (this.pending !== void 0) return;
43351
- const refresh = this.build().then((snapshot2) => {
43385
+ if (Date.now() - this.lastRefreshAttemptAt < BACKGROUND_RETRY_BACKOFF_MS) return;
43386
+ this.lastRefreshAttemptAt = Date.now();
43387
+ const refresh = this.build(void 0, false).then((snapshot2) => {
43352
43388
  this.served = snapshot2;
43353
43389
  return snapshot2;
43354
43390
  }).finally(() => {
@@ -43440,13 +43476,17 @@ var OpencodeGoCatalog = class {
43440
43476
  /**
43441
43477
  * Revalidate every source, reusing the metadata document when it is unchanged.
43442
43478
  * @param signal - caller cancellation, forwarded to the documentation read.
43479
+ * @param retry - whether a failed metadata read is tried again. A caller
43480
+ * that is waiting for the answer wants it tried again; a refresh nobody
43481
+ * is waiting for does not, because the answer it wants is the one it
43482
+ * already has.
43443
43483
  */
43444
- async build(signal) {
43484
+ async build(signal, retry = true) {
43445
43485
  const builtin = builtinModels(this.options.baseURL);
43446
43486
  const readDocument = this.options.readDocument ?? ((signal2) => fetchGoDocument(signal2, { cacheDir: this.options.cacheDir }));
43447
43487
  const [listing, metadata, documentation] = await Promise.allSettled([
43448
43488
  fetchModelIds(this.options.baseURL),
43449
- this.refreshMetadata(builtin),
43489
+ this.refreshMetadata(builtin, retry),
43450
43490
  readDocument(signal)
43451
43491
  ]);
43452
43492
  const degradations = [];
@@ -43668,10 +43708,11 @@ var OpencodeGoCatalog = class {
43668
43708
  * @param builtin - the installed catalog, for the parse.
43669
43709
  * @returns the parsed metadata.
43670
43710
  */
43671
- async refreshMetadata(builtin) {
43711
+ async refreshMetadata(builtin, retry) {
43672
43712
  try {
43673
43713
  return await this.readMetadataDocument(builtin);
43674
43714
  } catch (first) {
43715
+ if (!retry) throw first;
43675
43716
  await new Promise((resolve3) => {
43676
43717
  setTimeout(resolve3, METADATA_RETRY_DELAY_MS);
43677
43718
  });
@@ -43688,8 +43729,8 @@ function inferFamily(id) {
43688
43729
  return ["grok", "gpt", "muse"].some((family) => lower.startsWith(family)) ? "openai-responses" : "openai-completions";
43689
43730
  }
43690
43731
  async function discoverCatalogModels(catalog, signal) {
43691
- const snapshot2 = await catalog.snapshot(true, signal);
43692
- if (!snapshot2.live) {
43732
+ const snapshot2 = await catalog.snapshot(false, signal);
43733
+ if (snapshot2.listingFailure !== void 0) {
43693
43734
  const detail = snapshot2.listingFailure instanceof LlmError4 ? snapshot2.listingFailure.message : "the live model listing is unreachable";
43694
43735
  throw new LlmError4(`dshopencodego: ${detail}; refresh the model list to retry`, "DISCOVERY_FAILED", {
43695
43736
  cause: snapshot2.listingFailure
@@ -46198,7 +46239,7 @@ function createUpdateChecker(deps) {
46198
46239
  }
46199
46240
 
46200
46241
  // src/update/version.ts
46201
- var PLUGIN_VERSION = true ? "0.1.53" : "0.0.0-dev";
46242
+ var PLUGIN_VERSION = true ? "0.1.55" : "0.0.0-dev";
46202
46243
 
46203
46244
  // src/update/service.ts
46204
46245
  import { TypertRemoteService as TypertRemoteService2 } from "@deepseek-ai/dsh-typert-protocol";
@@ -33,6 +33,18 @@ export declare const MODEL_METADATA_TIMEOUT_MS = 30000;
33
33
  * document still fails inside the catalog build.
34
34
  */
35
35
  export declare const METADATA_RETRY_DELAY_MS = 1500;
36
+ /**
37
+ * How long a failed background refresh waits before it is tried again.
38
+ *
39
+ * A refresh that fails leaves the catalog's age where it was, so every call
40
+ * sees it as past its TTL and starts another. Without a back-off, a service
41
+ * that is down turns every model request into a full download attempt.
42
+ *
43
+ * Five minutes is short enough that a service that comes back is picked up
44
+ * soon, and long enough that a service that stays down is asked once, not
45
+ * once per request.
46
+ */
47
+ export declare const BACKGROUND_RETRY_BACKOFF_MS: number;
36
48
  /** Response caps: the listing is tiny, the metadata document is not. */
37
49
  export declare const MODEL_LISTING_MAX_BYTES: number;
38
50
  export declare const MODEL_METADATA_MAX_BYTES: number;
@@ -24,6 +24,16 @@
24
24
  */
25
25
  /** A repository listing is large; the cap keeps a hostile response bounded. */
26
26
  export declare const DISCOVERY_MAX_BYTES: number;
27
+ /**
28
+ * How long the repository listing waits before it is asked again.
29
+ *
30
+ * The listing is unauthenticated, and an unauthenticated GitHub API allows
31
+ * sixty requests an hour. The listing is also large — the provider's whole
32
+ * tree, thousands of entries. Asking it on every build would spend that
33
+ * budget on a provider that is merely unreachable, and would make every
34
+ * build slower for a question with the same answer.
35
+ */
36
+ export declare const DISCOVERY_COOLDOWN_MS: number;
27
37
  /**
28
38
  * The repository paths a discovery run found.
29
39
  *
@@ -76,3 +86,21 @@ export declare function readCachedPaths(cacheDir: string | undefined): readonly
76
86
  * @returns the merged set, in the order it should be tried.
77
87
  */
78
88
  export declare function rememberPaths(cacheDir: string | undefined, discovered: readonly string[]): readonly string[];
89
+ /**
90
+ * Whether the repository may be asked again.
91
+ *
92
+ * The question is worth at most an hour: a provider whose document has
93
+ * moved is found within the hour, and one that is merely down is not asked
94
+ * again every minute while it is down.
95
+ * @param cacheDir - the plugin's cache directory.
96
+ * @returns true when the repository may be asked.
97
+ */
98
+ export declare function discoveryAllowed(cacheDir: string | undefined): boolean;
99
+ /**
100
+ * Record that the repository was asked, whether or not it answered.
101
+ *
102
+ * The attempt is what the budget is spent on, so a question that came back
103
+ * empty counts as a question.
104
+ * @param cacheDir - the plugin's cache directory.
105
+ */
106
+ export declare function rememberAttempt(cacheDir: string | undefined): void;
@@ -160,6 +160,8 @@ export declare class OpencodeGoCatalog {
160
160
  private readonly probedAt;
161
161
  /** The background refresh, kept so a caller can wait for it to be over. */
162
162
  private backgroundRefresh;
163
+ /** When the last background refresh was started, for the back-off. */
164
+ private lastRefreshAttemptAt;
163
165
  constructor(options: CatalogOptions);
164
166
  /**
165
167
  * The cached snapshot, refreshed when it is older than the configured TTL.
@@ -237,6 +239,10 @@ export declare class OpencodeGoCatalog {
237
239
  /**
238
240
  * Revalidate every source, reusing the metadata document when it is unchanged.
239
241
  * @param signal - caller cancellation, forwarded to the documentation read.
242
+ * @param retry - whether a failed metadata read is tried again. A caller
243
+ * that is waiting for the answer wants it tried again; a refresh nobody
244
+ * is waiting for does not, because the answer it wants is the one it
245
+ * already has.
240
246
  */
241
247
  private build;
242
248
  /** Merge the listing with the metadata document and the installed entries. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dan-ai-studio/dshopencodego",
3
- "version": "0.1.53",
3
+ "version": "0.1.55",
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",