@dan-ai-studio/dshopencodego 0.1.51 → 0.1.54
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/README.en.md +1 -1
- package/README.md +1 -1
- package/lib/client.js +5 -0
- package/lib/index.js +258 -31
- package/lib/types/catalog/constants.d.ts +12 -0
- package/lib/types/catalog/go-doc-cache.d.ts +43 -0
- package/lib/types/catalog/index.d.ts +57 -1
- package/lib/types/catalog/reading.d.ts +10 -0
- package/lib/types/catalog/snapshot-cache.d.ts +50 -0
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -247,7 +247,7 @@ npm test # contract tests against a real local HTTP mock gateway
|
|
|
247
247
|
|
|
248
248
|
**Order matters**: `npm run build` must come before `npm test` — `tests/pack-contents.spec.ts` walks every `exports` target, and whether `lib/` exists decides whether that check runs or is silently skipped (it does not fail, it just stops protecting you). `package.json` no longer carries `prepare`, so `npm ci` will not build for you; all three CI workflows run `npm run build` explicitly.
|
|
249
249
|
|
|
250
|
-
The suite covers configuration validation, the protocol ladder, gateway and online metadata, the catalog projection, the adapter on the wire (all three protocols), Remote contracts and services, session-header invariants, usage windows and the meter, the client controller and usage pill, both halves' mounting and registration, the error mapping for stream idle timeouts and aborts, the exit-code contract of the verification scripts, and several **structural invariants** (inject declarations match the source's reads, peer coverage holds both ways, the READMEs match the manifest, and the READMEs' claims about the upstream match the upstream actually installed). That is **
|
|
250
|
+
The suite covers configuration validation, the protocol ladder, gateway and online metadata, the catalog projection, the adapter on the wire (all three protocols), Remote contracts and services, session-header invariants, usage windows and the meter, the client controller and usage pill, both halves' mounting and registration, the error mapping for stream idle timeouts and aborts, the exit-code contract of the verification scripts, and several **structural invariants** (inject declarations match the source's reads, peer coverage holds both ways, the READMEs match the manifest, and the READMEs' claims about the upstream match the upstream actually installed). That is **52 test files** — a number `tests/readme-consistency.spec.ts` checks against the `tests/` directory, so it cannot drift.
|
|
251
251
|
|
|
252
252
|
## Releasing (maintainers)
|
|
253
253
|
|
package/README.md
CHANGED
|
@@ -247,7 +247,7 @@ npm test # 契约测试:真实 HTTP mock 网关,断言请求头/
|
|
|
247
247
|
|
|
248
248
|
**顺序有讲究**:`npm run build` 要排在 `npm test` 之前——`tests/pack-contents.spec.ts` 会遍历 `exports` 的每个目标,`lib/` 存在与否决定这条检查是执行还是被跳过(不报错,但会少一层保护)。`package.json` 已移除 `prepare`,因此 `npm ci` 不会再替你隐式构建;三个 CI workflow 都显式跑了 `npm run build`。
|
|
249
249
|
|
|
250
|
-
测试覆盖配置校验、协议阶梯、网关与在线元数据、目录投影、适配器(三种协议的线上请求)、Remote 契约与服务、会话头不变量、用量窗口与计量、客户端控制器与用量组件、两半的挂载与登记、流空闲超时与中止的错误映射、验证脚本的退出码契约、以及几条**结构性不变量**(inject 声明与源码读取一致、peer 覆盖双向、README 与 manifest 一致、README 对上游的断言与实装的上游一致)。共 **
|
|
250
|
+
测试覆盖配置校验、协议阶梯、网关与在线元数据、目录投影、适配器(三种协议的线上请求)、Remote 契约与服务、会话头不变量、用量窗口与计量、客户端控制器与用量组件、两半的挂载与登记、流空闲超时与中止的错误映射、验证脚本的退出码契约、以及几条**结构性不变量**(inject 声明与源码读取一致、peer 覆盖双向、README 与 manifest 一致、README 对上游的断言与实装的上游一致)。共 **52 个测试文件**——这个数字由 `tests/readme-consistency.spec.ts` 对着 `tests/` 目录校验,不会漂移。
|
|
251
251
|
|
|
252
252
|
## 发布(维护者)
|
|
253
253
|
|
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;
|
|
@@ -129,6 +130,7 @@ function parseCatalogReading(value) {
|
|
|
129
130
|
...typeof row["error"] === "string" ? { error: row["error"] } : {},
|
|
130
131
|
degradations: parseDegradations(row["degradations"]),
|
|
131
132
|
...typeof row["factsCachedAt"] === "number" ? { factsCachedAt: row["factsCachedAt"] } : {},
|
|
133
|
+
...row["cached"] === true ? { cached: true } : {},
|
|
132
134
|
fetchedAtMs: row["fetchedAtMs"],
|
|
133
135
|
quotaSource: quotaSource ?? "seed",
|
|
134
136
|
counts: {
|
|
@@ -337,6 +339,7 @@ var en = {
|
|
|
337
339
|
catalogUnconfigured: "not configurable",
|
|
338
340
|
quotaSeed: "Allowances are the built-in snapshot, transcribed",
|
|
339
341
|
factsCached: "Model metadata is from the last successful reading,",
|
|
342
|
+
catalogRefreshing: "Showing the last known catalog; a refresh is running in the background.",
|
|
340
343
|
refreshLabel: "Catalog refresh (minutes)",
|
|
341
344
|
refreshHint: "How long requests and the model picker reuse one listing.",
|
|
342
345
|
writeRejected: "The settings write was rejected (the namespace was reloaded) \u2014 try again",
|
|
@@ -437,6 +440,7 @@ var zh = {
|
|
|
437
440
|
catalogUnconfigured: "\u65E0\u6CD5\u914D\u7F6E",
|
|
438
441
|
quotaSeed: "\u989D\u5EA6\u6765\u81EA\u5185\u7F6E\u5FEB\u7167\uFF0C\u8F6C\u5199\u4E8E",
|
|
439
442
|
factsCached: "\u6A21\u578B\u5143\u6570\u636E\u6765\u81EA\u4E0A\u6B21\u6210\u529F\u83B7\u53D6\uFF0C",
|
|
443
|
+
catalogRefreshing: "\u663E\u793A\u4E0A\u6B21\u5DF2\u77E5\u7684\u76EE\u5F55\uFF0C\u540E\u53F0\u6B63\u5728\u5237\u65B0\u3002",
|
|
440
444
|
refreshLabel: "\u76EE\u5F55\u5237\u65B0\uFF08\u5206\u949F\uFF09",
|
|
441
445
|
refreshHint: "\u8BF7\u6C42\u4E0E\u6A21\u578B\u9009\u62E9\u5668\u590D\u7528\u540C\u4E00\u4EFD\u5217\u8868\u7684\u65F6\u957F\u3002",
|
|
442
446
|
writeRejected: "\u8BBE\u7F6E\u5199\u5165\u88AB\u62D2\u7EDD\uFF08\u5DF2\u91CD\u65B0\u8F7D\u5165\uFF09\uFF0C\u8BF7\u91CD\u8BD5",
|
|
@@ -1252,6 +1256,7 @@ function Section({ controller, t, getLocale }) {
|
|
|
1252
1256
|
" ",
|
|
1253
1257
|
new Date(reading.factsCachedAt).toISOString().slice(0, 10)
|
|
1254
1258
|
] }),
|
|
1259
|
+
reading !== void 0 && reading.cached === true && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: section_default.hint, children: t("catalogRefreshing") }),
|
|
1255
1260
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: section_default.row, children: [
|
|
1256
1261
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: section_default.hint, htmlFor: "dshopencodego-refresh", children: t("refreshLabel") }),
|
|
1257
1262
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
package/lib/index.js
CHANGED
|
@@ -17788,8 +17788,8 @@ function getBunSandboxEnvValue(name2) {
|
|
|
17788
17788
|
if (procEnvCache === null) {
|
|
17789
17789
|
procEnvCache = /* @__PURE__ */ new Map();
|
|
17790
17790
|
try {
|
|
17791
|
-
const { readFileSync:
|
|
17792
|
-
const data =
|
|
17791
|
+
const { readFileSync: readFileSync5 } = __require("node:fs");
|
|
17792
|
+
const data = readFileSync5("/proc/self/environ", "utf-8");
|
|
17793
17793
|
for (const entry of data.split("\0")) {
|
|
17794
17794
|
const idx = entry.indexOf("=");
|
|
17795
17795
|
if (idx > 0) {
|
|
@@ -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;
|
|
@@ -42700,6 +42701,45 @@ function rememberPaths(cacheDir, discovered) {
|
|
|
42700
42701
|
return merged;
|
|
42701
42702
|
}
|
|
42702
42703
|
|
|
42704
|
+
// src/catalog/go-doc-cache.ts
|
|
42705
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
42706
|
+
import { join as join3 } from "node:path";
|
|
42707
|
+
var CACHE_FILE2 = "go-doc-cache.json";
|
|
42708
|
+
var CACHE_VERSION2 = 1;
|
|
42709
|
+
function readDocCache(cacheDir) {
|
|
42710
|
+
if (cacheDir === void 0) return void 0;
|
|
42711
|
+
try {
|
|
42712
|
+
const parsed = JSON.parse(readFileSync2(join3(cacheDir, CACHE_FILE2), "utf8"));
|
|
42713
|
+
if (parsed.version !== CACHE_VERSION2) return void 0;
|
|
42714
|
+
if (typeof parsed.url !== "string" || typeof parsed.body !== "string") return void 0;
|
|
42715
|
+
if (parsed.body.length === 0) return void 0;
|
|
42716
|
+
return {
|
|
42717
|
+
url: parsed.url,
|
|
42718
|
+
etag: typeof parsed.etag === "string" ? parsed.etag : void 0,
|
|
42719
|
+
body: parsed.body,
|
|
42720
|
+
savedAt: typeof parsed.savedAt === "number" ? parsed.savedAt : 0
|
|
42721
|
+
};
|
|
42722
|
+
} catch {
|
|
42723
|
+
return void 0;
|
|
42724
|
+
}
|
|
42725
|
+
}
|
|
42726
|
+
function writeDocCache(cacheDir, entry) {
|
|
42727
|
+
if (cacheDir === void 0 || entry.body.length === 0) return;
|
|
42728
|
+
try {
|
|
42729
|
+
mkdirSync2(cacheDir, { recursive: true });
|
|
42730
|
+
const file = {
|
|
42731
|
+
version: CACHE_VERSION2,
|
|
42732
|
+
url: entry.url,
|
|
42733
|
+
...entry.etag === void 0 ? {} : { etag: entry.etag },
|
|
42734
|
+
body: entry.body,
|
|
42735
|
+
savedAt: Date.now()
|
|
42736
|
+
};
|
|
42737
|
+
writeFileSync2(join3(cacheDir, CACHE_FILE2), `${JSON.stringify(file)}
|
|
42738
|
+
`, "utf8");
|
|
42739
|
+
} catch {
|
|
42740
|
+
}
|
|
42741
|
+
}
|
|
42742
|
+
|
|
42703
42743
|
// src/catalog/go-doc.ts
|
|
42704
42744
|
var GO_DOC_URLS = [
|
|
42705
42745
|
"https://raw.githubusercontent.com/anomalyco/opencode/dev/services/www/src/docs/content/console/go.mdx",
|
|
@@ -42778,36 +42818,47 @@ function parseGoDocument(markdown) {
|
|
|
42778
42818
|
}
|
|
42779
42819
|
return { quotas, protocols };
|
|
42780
42820
|
}
|
|
42781
|
-
async function readSource(url, signal) {
|
|
42821
|
+
async function readSource(url, signal, etag) {
|
|
42782
42822
|
const timeout = AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS);
|
|
42783
42823
|
try {
|
|
42784
42824
|
const response = await fetch(url, {
|
|
42785
42825
|
redirect: "error",
|
|
42786
|
-
headers: {
|
|
42826
|
+
headers: {
|
|
42827
|
+
...attributionHeaders2(),
|
|
42828
|
+
accept: "text/plain",
|
|
42829
|
+
"cache-control": "no-cache",
|
|
42830
|
+
...etag === void 0 ? {} : { "if-none-match": etag }
|
|
42831
|
+
},
|
|
42787
42832
|
signal: signal === void 0 ? timeout : AbortSignal.any([signal, timeout])
|
|
42788
42833
|
});
|
|
42834
|
+
if (response.status === 304) {
|
|
42835
|
+
await response.body?.cancel().catch(() => {
|
|
42836
|
+
});
|
|
42837
|
+
return { url, notModified: true };
|
|
42838
|
+
}
|
|
42789
42839
|
if (!response.ok) {
|
|
42790
42840
|
await response.body?.cancel().catch(() => {
|
|
42791
42841
|
});
|
|
42792
|
-
return { failure: `${url} answered HTTP ${response.status}` };
|
|
42842
|
+
return { url, failure: `${url} answered HTTP ${response.status}` };
|
|
42793
42843
|
}
|
|
42794
|
-
const
|
|
42844
|
+
const body = await readBoundedText(response, GO_DOC_MAX_BYTES);
|
|
42845
|
+
const document = parseGoDocument(body);
|
|
42795
42846
|
if (document.quotas.size === 0 && document.protocols.size === 0) {
|
|
42796
|
-
return { failure: `${url} carried neither an allowance table nor an endpoint table` };
|
|
42847
|
+
return { url, failure: `${url} carried neither an allowance table nor an endpoint table` };
|
|
42797
42848
|
}
|
|
42798
|
-
return { document };
|
|
42849
|
+
return { url, document, body, etag: response.headers.get("etag") ?? void 0 };
|
|
42799
42850
|
} catch (error) {
|
|
42800
|
-
return { failure: `${url}: ${error instanceof Error ? error.message : String(error)}` };
|
|
42851
|
+
return { url, failure: `${url}: ${error instanceof Error ? error.message : String(error)}` };
|
|
42801
42852
|
}
|
|
42802
42853
|
}
|
|
42803
42854
|
async function fetchGoDocument(signal, options = {}) {
|
|
42804
42855
|
const failures = [];
|
|
42805
|
-
const known = await readSources(GO_DOC_URLS, signal);
|
|
42856
|
+
const known = await readSources(GO_DOC_URLS, signal, options.cacheDir);
|
|
42806
42857
|
if (known.document !== void 0) return known.document;
|
|
42807
42858
|
failures.push(...known.failures);
|
|
42808
42859
|
const remembered = readCachedPaths(options.cacheDir).filter((path5) => !GO_DOC_URLS.some((url) => url.endsWith(path5)));
|
|
42809
42860
|
if (remembered.length > 0) {
|
|
42810
|
-
const cached = await readSources(remembered.flatMap(mirrorsOf), signal);
|
|
42861
|
+
const cached = await readSources(remembered.flatMap(mirrorsOf), signal, options.cacheDir);
|
|
42811
42862
|
if (cached.document !== void 0) return cached.document;
|
|
42812
42863
|
failures.push(...cached.failures);
|
|
42813
42864
|
}
|
|
@@ -42821,7 +42872,7 @@ async function fetchGoDocument(signal, options = {}) {
|
|
|
42821
42872
|
if (fresh.length === 0) {
|
|
42822
42873
|
failures.push("the provider repository named the path that was already tried");
|
|
42823
42874
|
} else {
|
|
42824
|
-
const result = await readSources(fresh, signal);
|
|
42875
|
+
const result = await readSources(fresh, signal, options.cacheDir);
|
|
42825
42876
|
if (result.document !== void 0) return result.document;
|
|
42826
42877
|
failures.push(...result.failures);
|
|
42827
42878
|
}
|
|
@@ -42831,12 +42882,32 @@ async function fetchGoDocument(signal, options = {}) {
|
|
|
42831
42882
|
"DOCUMENT_UNAVAILABLE"
|
|
42832
42883
|
);
|
|
42833
42884
|
}
|
|
42834
|
-
async function readSources(urls, signal) {
|
|
42835
|
-
const attempts = await Promise.all(urls.map((url) => readSource(url, signal)));
|
|
42885
|
+
async function readSources(urls, signal, cacheDir) {
|
|
42836
42886
|
const failures = [];
|
|
42887
|
+
const cached = readDocCache(cacheDir);
|
|
42888
|
+
if (cached !== void 0 && urls.includes(cached.url)) {
|
|
42889
|
+
const attempt = await readSource(cached.url, signal, cached.etag);
|
|
42890
|
+
if ("notModified" in attempt) {
|
|
42891
|
+
const document = parseGoDocument(cached.body);
|
|
42892
|
+
if (document.quotas.size > 0 || document.protocols.size > 0) {
|
|
42893
|
+
return { document, failures };
|
|
42894
|
+
}
|
|
42895
|
+
failures.push(`${attempt.url} answered 304, and the remembered copy carries no table this build can read`);
|
|
42896
|
+
} else if ("document" in attempt) {
|
|
42897
|
+
writeDocCache(cacheDir, { url: attempt.url, etag: attempt.etag, body: attempt.body });
|
|
42898
|
+
return { document: attempt.document, failures };
|
|
42899
|
+
} else {
|
|
42900
|
+
failures.push(attempt.failure);
|
|
42901
|
+
}
|
|
42902
|
+
}
|
|
42903
|
+
const rest = cached === void 0 ? urls : urls.filter((url) => url !== cached.url);
|
|
42904
|
+
const attempts = await Promise.all(rest.map((url) => readSource(url, signal)));
|
|
42837
42905
|
for (const attempt of attempts) {
|
|
42838
|
-
if ("document" in attempt)
|
|
42839
|
-
|
|
42906
|
+
if ("document" in attempt) {
|
|
42907
|
+
writeDocCache(cacheDir, { url: attempt.url, etag: attempt.etag, body: attempt.body });
|
|
42908
|
+
return { document: attempt.document, failures };
|
|
42909
|
+
}
|
|
42910
|
+
if ("failure" in attempt) failures.push(attempt.failure);
|
|
42840
42911
|
}
|
|
42841
42912
|
return { failures };
|
|
42842
42913
|
}
|
|
@@ -43055,15 +43126,15 @@ function toPiModel(facts, baseURL) {
|
|
|
43055
43126
|
}
|
|
43056
43127
|
|
|
43057
43128
|
// src/catalog/metadata-cache.ts
|
|
43058
|
-
import { mkdirSync as
|
|
43059
|
-
import { join as
|
|
43060
|
-
var
|
|
43061
|
-
var
|
|
43129
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
43130
|
+
import { join as join4 } from "node:path";
|
|
43131
|
+
var CACHE_FILE3 = "models-dev-facts.json";
|
|
43132
|
+
var CACHE_VERSION3 = 1;
|
|
43062
43133
|
function readFactsCache(cacheDir) {
|
|
43063
43134
|
if (cacheDir === void 0) return void 0;
|
|
43064
43135
|
try {
|
|
43065
|
-
const parsed = JSON.parse(
|
|
43066
|
-
if (parsed.version !==
|
|
43136
|
+
const parsed = JSON.parse(readFileSync3(join4(cacheDir, CACHE_FILE3), "utf8"));
|
|
43137
|
+
if (parsed.version !== CACHE_VERSION3 || parsed.facts === null || typeof parsed.facts !== "object") return void 0;
|
|
43067
43138
|
const facts = /* @__PURE__ */ new Map();
|
|
43068
43139
|
for (const [id, fact] of Object.entries(parsed.facts)) {
|
|
43069
43140
|
if (fact === null || typeof fact !== "object") continue;
|
|
@@ -43083,14 +43154,69 @@ function readFactsCache(cacheDir) {
|
|
|
43083
43154
|
function writeFactsCache(cacheDir, facts, etag) {
|
|
43084
43155
|
if (cacheDir === void 0 || facts.size === 0) return;
|
|
43085
43156
|
try {
|
|
43086
|
-
|
|
43157
|
+
mkdirSync3(cacheDir, { recursive: true });
|
|
43087
43158
|
const file = {
|
|
43088
|
-
version:
|
|
43159
|
+
version: CACHE_VERSION3,
|
|
43089
43160
|
facts: Object.fromEntries(facts),
|
|
43090
43161
|
updatedAt: Date.now(),
|
|
43091
43162
|
...etag === void 0 ? {} : { etag }
|
|
43092
43163
|
};
|
|
43093
|
-
|
|
43164
|
+
writeFileSync3(join4(cacheDir, CACHE_FILE3), `${JSON.stringify(file)}
|
|
43165
|
+
`, "utf8");
|
|
43166
|
+
} catch {
|
|
43167
|
+
}
|
|
43168
|
+
}
|
|
43169
|
+
|
|
43170
|
+
// src/catalog/snapshot-cache.ts
|
|
43171
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
43172
|
+
import { join as join5 } from "node:path";
|
|
43173
|
+
var CACHE_FILE4 = "catalog-snapshot.json";
|
|
43174
|
+
var CACHE_VERSION4 = 1;
|
|
43175
|
+
function readSnapshotCache(cacheDir) {
|
|
43176
|
+
if (cacheDir === void 0) return void 0;
|
|
43177
|
+
try {
|
|
43178
|
+
const parsed = JSON.parse(readFileSync4(join5(cacheDir, CACHE_FILE4), "utf8"));
|
|
43179
|
+
if (parsed.version !== CACHE_VERSION4) return void 0;
|
|
43180
|
+
if (parsed.facts === null || typeof parsed.facts !== "object") return void 0;
|
|
43181
|
+
const facts = /* @__PURE__ */ new Map();
|
|
43182
|
+
for (const [id, fact] of Object.entries(parsed.facts)) {
|
|
43183
|
+
if (fact === null || typeof fact !== "object") continue;
|
|
43184
|
+
if (typeof fact.id !== "string" || typeof fact.api !== "string") continue;
|
|
43185
|
+
facts.set(id, fact);
|
|
43186
|
+
}
|
|
43187
|
+
if (facts.size === 0) return void 0;
|
|
43188
|
+
const quotas = /* @__PURE__ */ new Map();
|
|
43189
|
+
for (const [id, quota] of Object.entries(parsed.quotas ?? {})) {
|
|
43190
|
+
if (quota === null || typeof quota !== "object") continue;
|
|
43191
|
+
quotas.set(id, quota);
|
|
43192
|
+
}
|
|
43193
|
+
const protocols = /* @__PURE__ */ new Map();
|
|
43194
|
+
for (const [id, protocol] of Object.entries(parsed.protocols ?? {})) {
|
|
43195
|
+
if (typeof protocol !== "string") continue;
|
|
43196
|
+
protocols.set(id, protocol);
|
|
43197
|
+
}
|
|
43198
|
+
return {
|
|
43199
|
+
facts,
|
|
43200
|
+
quotas,
|
|
43201
|
+
protocols,
|
|
43202
|
+
updatedAt: typeof parsed.updatedAt === "number" ? parsed.updatedAt : 0
|
|
43203
|
+
};
|
|
43204
|
+
} catch {
|
|
43205
|
+
return void 0;
|
|
43206
|
+
}
|
|
43207
|
+
}
|
|
43208
|
+
function writeSnapshotCache(cacheDir, facts, quotas, protocols) {
|
|
43209
|
+
if (cacheDir === void 0 || facts.size === 0) return;
|
|
43210
|
+
try {
|
|
43211
|
+
mkdirSync4(cacheDir, { recursive: true });
|
|
43212
|
+
const file = {
|
|
43213
|
+
version: CACHE_VERSION4,
|
|
43214
|
+
facts: Object.fromEntries(facts),
|
|
43215
|
+
...quotas.size === 0 ? {} : { quotas: Object.fromEntries(quotas) },
|
|
43216
|
+
...protocols.size === 0 ? {} : { protocols: Object.fromEntries(protocols) },
|
|
43217
|
+
updatedAt: Date.now()
|
|
43218
|
+
};
|
|
43219
|
+
writeFileSync4(join5(cacheDir, CACHE_FILE4), `${JSON.stringify(file)}
|
|
43094
43220
|
`, "utf8");
|
|
43095
43221
|
} catch {
|
|
43096
43222
|
}
|
|
@@ -43158,6 +43284,10 @@ var OpencodeGoCatalog = class {
|
|
|
43158
43284
|
document;
|
|
43159
43285
|
/** Ids that already forced a revalidation, with the time they did. */
|
|
43160
43286
|
probedAt = /* @__PURE__ */ new Map();
|
|
43287
|
+
/** The background refresh, kept so a caller can wait for it to be over. */
|
|
43288
|
+
backgroundRefresh;
|
|
43289
|
+
/** When the last background refresh was started, for the back-off. */
|
|
43290
|
+
lastRefreshAttemptAt = 0;
|
|
43161
43291
|
constructor(options) {
|
|
43162
43292
|
this.options = options;
|
|
43163
43293
|
const cached = readFactsCache(options.cacheDir);
|
|
@@ -43169,7 +43299,19 @@ var OpencodeGoCatalog = class {
|
|
|
43169
43299
|
}
|
|
43170
43300
|
/**
|
|
43171
43301
|
* The cached snapshot, refreshed when it is older than the configured TTL.
|
|
43172
|
-
*
|
|
43302
|
+
*
|
|
43303
|
+
* A snapshot that is past its TTL is still served — and refreshed behind
|
|
43304
|
+
* it. A caller that asks for a model must not wait for the network to
|
|
43305
|
+
* learn what it already knew, so the answer is the one in hand and the
|
|
43306
|
+
* refresh goes on without anyone watching it. A caller that forces a
|
|
43307
|
+
* refresh — the settings page's button — waits for it, because asking to
|
|
43308
|
+
* refresh and not waiting for the result is asking for nothing.
|
|
43309
|
+
*
|
|
43310
|
+
* A process that has never built a catalog reads the one it wrote down
|
|
43311
|
+
* last time and refreshes that in the background. The first message of a
|
|
43312
|
+
* session therefore names its model at once, instead of after a wait that
|
|
43313
|
+
* belongs to the network rather than to the session.
|
|
43314
|
+
* @param force - revalidate every source, ignoring the cache, and wait.
|
|
43173
43315
|
* @param signal - caller cancellation, forwarded to the documentation read,
|
|
43174
43316
|
* the slowest source and the only one a caller can cancel; a build already
|
|
43175
43317
|
* in flight keeps the signal of the caller that started it, because one
|
|
@@ -43179,6 +43321,19 @@ var OpencodeGoCatalog = class {
|
|
|
43179
43321
|
if (!force && this.served !== void 0 && Date.now() - this.served.fetchedAtMs < this.options.refreshMs) {
|
|
43180
43322
|
return Promise.resolve(this.served);
|
|
43181
43323
|
}
|
|
43324
|
+
if (!force) {
|
|
43325
|
+
if (this.served === void 0) {
|
|
43326
|
+
const remembered = this.readRememberedSnapshot();
|
|
43327
|
+
if (remembered !== void 0) {
|
|
43328
|
+
this.served = remembered;
|
|
43329
|
+
this.refreshInBackground();
|
|
43330
|
+
return Promise.resolve(remembered);
|
|
43331
|
+
}
|
|
43332
|
+
} else {
|
|
43333
|
+
this.refreshInBackground();
|
|
43334
|
+
return Promise.resolve(this.served);
|
|
43335
|
+
}
|
|
43336
|
+
}
|
|
43182
43337
|
this.pending ??= this.build(signal).then((snapshot2) => {
|
|
43183
43338
|
this.served = snapshot2;
|
|
43184
43339
|
return snapshot2;
|
|
@@ -43187,6 +43342,64 @@ var OpencodeGoCatalog = class {
|
|
|
43187
43342
|
});
|
|
43188
43343
|
return this.pending;
|
|
43189
43344
|
}
|
|
43345
|
+
/**
|
|
43346
|
+
* Refresh the catalog behind the snapshot in hand.
|
|
43347
|
+
*
|
|
43348
|
+
* The build is not awaited and its failure is not reported here: the
|
|
43349
|
+
* snapshot in hand stays, and whoever asked for the refresh will see the
|
|
43350
|
+
* next one.
|
|
43351
|
+
*/
|
|
43352
|
+
refreshInBackground() {
|
|
43353
|
+
if (this.pending !== void 0) return;
|
|
43354
|
+
if (Date.now() - this.lastRefreshAttemptAt < BACKGROUND_RETRY_BACKOFF_MS) return;
|
|
43355
|
+
this.lastRefreshAttemptAt = Date.now();
|
|
43356
|
+
const refresh = this.build(void 0, false).then((snapshot2) => {
|
|
43357
|
+
this.served = snapshot2;
|
|
43358
|
+
return snapshot2;
|
|
43359
|
+
}).finally(() => {
|
|
43360
|
+
this.pending = void 0;
|
|
43361
|
+
});
|
|
43362
|
+
this.pending = refresh;
|
|
43363
|
+
this.backgroundRefresh = refresh;
|
|
43364
|
+
void refresh.catch(() => void 0);
|
|
43365
|
+
}
|
|
43366
|
+
/**
|
|
43367
|
+
* Wait for the background refresh, if one is running.
|
|
43368
|
+
*
|
|
43369
|
+
* The refresh belongs to no caller, so nothing else can wait for it. A
|
|
43370
|
+
* host that is shutting down — or a test that must not have a request
|
|
43371
|
+
* outlive it — needs to know when the quiet is over.
|
|
43372
|
+
* @returns a promise that settles when the background refresh has.
|
|
43373
|
+
*/
|
|
43374
|
+
settled() {
|
|
43375
|
+
const refresh = this.backgroundRefresh;
|
|
43376
|
+
return refresh === void 0 ? Promise.resolve() : refresh.then(() => void 0, () => void 0);
|
|
43377
|
+
}
|
|
43378
|
+
/**
|
|
43379
|
+
* The catalog a previous build produced, read from disk.
|
|
43380
|
+
*
|
|
43381
|
+
* It is presented as what it is: a listing from the past, being brought up
|
|
43382
|
+
* to date in the background. The facts it carries are the ones the last
|
|
43383
|
+
* build produced, so the protocols and the thinking levels are the ones
|
|
43384
|
+
* that were true then.
|
|
43385
|
+
* @returns the remembered catalog, or undefined when there is none.
|
|
43386
|
+
*/
|
|
43387
|
+
readRememberedSnapshot() {
|
|
43388
|
+
const remembered = readSnapshotCache(this.options.cacheDir);
|
|
43389
|
+
if (remembered === void 0) return void 0;
|
|
43390
|
+
const facts = new Map(remembered.facts);
|
|
43391
|
+
return {
|
|
43392
|
+
facts,
|
|
43393
|
+
unavailable: /* @__PURE__ */ new Map(),
|
|
43394
|
+
provider: buildProvider2(this.options.baseURL, [...facts.values()].map((fact) => toPiModel(fact, this.options.baseURL))),
|
|
43395
|
+
// The listing is from the past, and the page says so.
|
|
43396
|
+
live: false,
|
|
43397
|
+
// And a refresh of it is already under way.
|
|
43398
|
+
cached: true,
|
|
43399
|
+
fetchedAtMs: remembered.updatedAt,
|
|
43400
|
+
...remembered.quotas.size === 0 ? {} : { documentQuotas: remembered.quotas }
|
|
43401
|
+
};
|
|
43402
|
+
}
|
|
43190
43403
|
/**
|
|
43191
43404
|
* The snapshot that can serve one exact model.
|
|
43192
43405
|
*
|
|
@@ -43232,13 +43445,17 @@ var OpencodeGoCatalog = class {
|
|
|
43232
43445
|
/**
|
|
43233
43446
|
* Revalidate every source, reusing the metadata document when it is unchanged.
|
|
43234
43447
|
* @param signal - caller cancellation, forwarded to the documentation read.
|
|
43448
|
+
* @param retry - whether a failed metadata read is tried again. A caller
|
|
43449
|
+
* that is waiting for the answer wants it tried again; a refresh nobody
|
|
43450
|
+
* is waiting for does not, because the answer it wants is the one it
|
|
43451
|
+
* already has.
|
|
43235
43452
|
*/
|
|
43236
|
-
async build(signal) {
|
|
43453
|
+
async build(signal, retry = true) {
|
|
43237
43454
|
const builtin = builtinModels(this.options.baseURL);
|
|
43238
43455
|
const readDocument = this.options.readDocument ?? ((signal2) => fetchGoDocument(signal2, { cacheDir: this.options.cacheDir }));
|
|
43239
43456
|
const [listing, metadata, documentation] = await Promise.allSettled([
|
|
43240
43457
|
fetchModelIds(this.options.baseURL),
|
|
43241
|
-
this.refreshMetadata(builtin),
|
|
43458
|
+
this.refreshMetadata(builtin, retry),
|
|
43242
43459
|
readDocument(signal)
|
|
43243
43460
|
]);
|
|
43244
43461
|
const degradations = [];
|
|
@@ -43281,13 +43498,20 @@ var OpencodeGoCatalog = class {
|
|
|
43281
43498
|
}
|
|
43282
43499
|
const fromCache = metadata.status !== "fulfilled" && this.lastMetadata === void 0;
|
|
43283
43500
|
const online = metadata.status === "fulfilled" ? metadata.value : this.lastMetadata ?? this.cachedMetadata;
|
|
43284
|
-
|
|
43501
|
+
const snapshot2 = this.assemble(
|
|
43285
43502
|
listing.value,
|
|
43286
43503
|
online,
|
|
43287
43504
|
builtin,
|
|
43288
43505
|
degradations,
|
|
43289
43506
|
fromCache ? this.cachedAt : void 0
|
|
43290
43507
|
);
|
|
43508
|
+
writeSnapshotCache(
|
|
43509
|
+
this.options.cacheDir,
|
|
43510
|
+
snapshot2.facts,
|
|
43511
|
+
this.document?.quotas ?? /* @__PURE__ */ new Map(),
|
|
43512
|
+
this.document?.protocols ?? /* @__PURE__ */ new Map()
|
|
43513
|
+
);
|
|
43514
|
+
return snapshot2;
|
|
43291
43515
|
}
|
|
43292
43516
|
/** Merge the listing with the metadata document and the installed entries. */
|
|
43293
43517
|
assemble(ids, online, builtin, degradations = [], factsCachedAt) {
|
|
@@ -43453,10 +43677,11 @@ var OpencodeGoCatalog = class {
|
|
|
43453
43677
|
* @param builtin - the installed catalog, for the parse.
|
|
43454
43678
|
* @returns the parsed metadata.
|
|
43455
43679
|
*/
|
|
43456
|
-
async refreshMetadata(builtin) {
|
|
43680
|
+
async refreshMetadata(builtin, retry) {
|
|
43457
43681
|
try {
|
|
43458
43682
|
return await this.readMetadataDocument(builtin);
|
|
43459
43683
|
} catch (first) {
|
|
43684
|
+
if (!retry) throw first;
|
|
43460
43685
|
await new Promise((resolve3) => {
|
|
43461
43686
|
setTimeout(resolve3, METADATA_RETRY_DELAY_MS);
|
|
43462
43687
|
});
|
|
@@ -45571,6 +45796,7 @@ function parseCatalogReading(value) {
|
|
|
45571
45796
|
...typeof row["error"] === "string" ? { error: row["error"] } : {},
|
|
45572
45797
|
degradations: parseDegradations(row["degradations"]),
|
|
45573
45798
|
...typeof row["factsCachedAt"] === "number" ? { factsCachedAt: row["factsCachedAt"] } : {},
|
|
45799
|
+
...row["cached"] === true ? { cached: true } : {},
|
|
45574
45800
|
fetchedAtMs: row["fetchedAtMs"],
|
|
45575
45801
|
quotaSource: quotaSource ?? "seed",
|
|
45576
45802
|
counts: {
|
|
@@ -45813,6 +46039,7 @@ function catalogReading(snapshot2, visibility, listingFailure, now) {
|
|
|
45813
46039
|
...!snapshot2.live && listingFailure !== void 0 ? { error: listingFailure } : {},
|
|
45814
46040
|
degradations: snapshot2.degradations ?? [],
|
|
45815
46041
|
...snapshot2.factsCachedAt === void 0 ? {} : { factsCachedAt: snapshot2.factsCachedAt },
|
|
46042
|
+
...snapshot2.cached === void 0 ? {} : { cached: snapshot2.cached },
|
|
45816
46043
|
fetchedAtMs: snapshot2.fetchedAtMs,
|
|
45817
46044
|
quotaSource: documentQuotas === void 0 ? "seed" : "document",
|
|
45818
46045
|
counts: {
|
|
@@ -45981,7 +46208,7 @@ function createUpdateChecker(deps) {
|
|
|
45981
46208
|
}
|
|
45982
46209
|
|
|
45983
46210
|
// src/update/version.ts
|
|
45984
|
-
var PLUGIN_VERSION = true ? "0.1.
|
|
46211
|
+
var PLUGIN_VERSION = true ? "0.1.54" : "0.0.0-dev";
|
|
45985
46212
|
|
|
45986
46213
|
// src/update/service.ts
|
|
45987
46214
|
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;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remembering the provider's document.
|
|
3
|
+
*
|
|
4
|
+
* The document is small — tens of kilobytes — but it is fetched by every
|
|
5
|
+
* process that builds a catalog, and a desktop build fetches over the
|
|
6
|
+
* browser's network stack, where that fetch is measured in hundreds of
|
|
7
|
+
* milliseconds. The registry serves an ETag for it, and a conditional
|
|
8
|
+
* request that comes back `304 Not Modified` carries no body at all, so a
|
|
9
|
+
* process that remembers the ETag can have the document for the price of a
|
|
10
|
+
* round trip.
|
|
11
|
+
*
|
|
12
|
+
* What is remembered is the raw text, not the parsed tables. The parsed
|
|
13
|
+
* form is a reading of a document, and a reading can be taken again; the
|
|
14
|
+
* text is the document.
|
|
15
|
+
*
|
|
16
|
+
* @module @dan-ai-studio/dshopencodego/catalog/go-doc-cache
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* The document a previous read produced.
|
|
20
|
+
*
|
|
21
|
+
* A cache that cannot be read is no cache: the caller then reads the
|
|
22
|
+
* document the ordinary way, which is slower and no worse.
|
|
23
|
+
* @param cacheDir - the plugin's cache directory.
|
|
24
|
+
* @returns the remembered document, or undefined.
|
|
25
|
+
*/
|
|
26
|
+
export declare function readDocCache(cacheDir: string | undefined): {
|
|
27
|
+
readonly url: string;
|
|
28
|
+
readonly etag: string | undefined;
|
|
29
|
+
readonly body: string;
|
|
30
|
+
readonly savedAt: number;
|
|
31
|
+
} | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Remember the document a read produced.
|
|
34
|
+
*
|
|
35
|
+
* A cache directory that cannot be written costs a fetch, and nothing else.
|
|
36
|
+
* @param cacheDir - the plugin's cache directory.
|
|
37
|
+
* @param entry - the document, where it came from, and its ETag.
|
|
38
|
+
*/
|
|
39
|
+
export declare function writeDocCache(cacheDir: string | undefined, entry: {
|
|
40
|
+
readonly url: string;
|
|
41
|
+
readonly etag: string | undefined;
|
|
42
|
+
readonly body: string;
|
|
43
|
+
}): void;
|
|
@@ -73,6 +73,15 @@ export interface CatalogSnapshot {
|
|
|
73
73
|
* a user cannot act on.
|
|
74
74
|
*/
|
|
75
75
|
readonly degradations?: readonly CatalogDegradation[];
|
|
76
|
+
/**
|
|
77
|
+
* True when this snapshot was read from disk and is being brought up to
|
|
78
|
+
* date in the background.
|
|
79
|
+
*
|
|
80
|
+
* It is the difference between "here is the last known catalog" and "here
|
|
81
|
+
* is the catalog, and it is current". A page that shows the first must
|
|
82
|
+
* also say that a refresh is under way.
|
|
83
|
+
*/
|
|
84
|
+
readonly cached?: boolean;
|
|
76
85
|
/**
|
|
77
86
|
* When the facts in this snapshot were written, when they came from disk
|
|
78
87
|
* rather than from a live read.
|
|
@@ -149,16 +158,59 @@ export declare class OpencodeGoCatalog {
|
|
|
149
158
|
private document;
|
|
150
159
|
/** Ids that already forced a revalidation, with the time they did. */
|
|
151
160
|
private readonly probedAt;
|
|
161
|
+
/** The background refresh, kept so a caller can wait for it to be over. */
|
|
162
|
+
private backgroundRefresh;
|
|
163
|
+
/** When the last background refresh was started, for the back-off. */
|
|
164
|
+
private lastRefreshAttemptAt;
|
|
152
165
|
constructor(options: CatalogOptions);
|
|
153
166
|
/**
|
|
154
167
|
* The cached snapshot, refreshed when it is older than the configured TTL.
|
|
155
|
-
*
|
|
168
|
+
*
|
|
169
|
+
* A snapshot that is past its TTL is still served — and refreshed behind
|
|
170
|
+
* it. A caller that asks for a model must not wait for the network to
|
|
171
|
+
* learn what it already knew, so the answer is the one in hand and the
|
|
172
|
+
* refresh goes on without anyone watching it. A caller that forces a
|
|
173
|
+
* refresh — the settings page's button — waits for it, because asking to
|
|
174
|
+
* refresh and not waiting for the result is asking for nothing.
|
|
175
|
+
*
|
|
176
|
+
* A process that has never built a catalog reads the one it wrote down
|
|
177
|
+
* last time and refreshes that in the background. The first message of a
|
|
178
|
+
* session therefore names its model at once, instead of after a wait that
|
|
179
|
+
* belongs to the network rather than to the session.
|
|
180
|
+
* @param force - revalidate every source, ignoring the cache, and wait.
|
|
156
181
|
* @param signal - caller cancellation, forwarded to the documentation read,
|
|
157
182
|
* the slowest source and the only one a caller can cancel; a build already
|
|
158
183
|
* in flight keeps the signal of the caller that started it, because one
|
|
159
184
|
* shared snapshot cannot carry two different cancellations.
|
|
160
185
|
*/
|
|
161
186
|
snapshot(force?: boolean, signal?: AbortSignal): Promise<CatalogSnapshot>;
|
|
187
|
+
/**
|
|
188
|
+
* Refresh the catalog behind the snapshot in hand.
|
|
189
|
+
*
|
|
190
|
+
* The build is not awaited and its failure is not reported here: the
|
|
191
|
+
* snapshot in hand stays, and whoever asked for the refresh will see the
|
|
192
|
+
* next one.
|
|
193
|
+
*/
|
|
194
|
+
private refreshInBackground;
|
|
195
|
+
/**
|
|
196
|
+
* Wait for the background refresh, if one is running.
|
|
197
|
+
*
|
|
198
|
+
* The refresh belongs to no caller, so nothing else can wait for it. A
|
|
199
|
+
* host that is shutting down — or a test that must not have a request
|
|
200
|
+
* outlive it — needs to know when the quiet is over.
|
|
201
|
+
* @returns a promise that settles when the background refresh has.
|
|
202
|
+
*/
|
|
203
|
+
settled(): Promise<void>;
|
|
204
|
+
/**
|
|
205
|
+
* The catalog a previous build produced, read from disk.
|
|
206
|
+
*
|
|
207
|
+
* It is presented as what it is: a listing from the past, being brought up
|
|
208
|
+
* to date in the background. The facts it carries are the ones the last
|
|
209
|
+
* build produced, so the protocols and the thinking levels are the ones
|
|
210
|
+
* that were true then.
|
|
211
|
+
* @returns the remembered catalog, or undefined when there is none.
|
|
212
|
+
*/
|
|
213
|
+
private readRememberedSnapshot;
|
|
162
214
|
/**
|
|
163
215
|
* The snapshot that can serve one exact model.
|
|
164
216
|
*
|
|
@@ -187,6 +239,10 @@ export declare class OpencodeGoCatalog {
|
|
|
187
239
|
/**
|
|
188
240
|
* Revalidate every source, reusing the metadata document when it is unchanged.
|
|
189
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.
|
|
190
246
|
*/
|
|
191
247
|
private build;
|
|
192
248
|
/** Merge the listing with the metadata document and the installed entries. */
|
|
@@ -38,6 +38,16 @@ export interface CatalogReading {
|
|
|
38
38
|
* like a current one.
|
|
39
39
|
*/
|
|
40
40
|
readonly factsCachedAt?: number;
|
|
41
|
+
/**
|
|
42
|
+
* True when the catalog behind this reading was read from disk and is
|
|
43
|
+
* being brought up to date in the background.
|
|
44
|
+
*
|
|
45
|
+
* The reading is usable — the models, the protocols, the levels are all
|
|
46
|
+
* there — but it was true when it was written, and a refresh is under
|
|
47
|
+
* way. The page says so, because a page that looks current and is not is
|
|
48
|
+
* worse than one that admits it.
|
|
49
|
+
*/
|
|
50
|
+
readonly cached?: boolean;
|
|
41
51
|
/** When the underlying snapshot was built. */
|
|
42
52
|
readonly fetchedAtMs: number;
|
|
43
53
|
/**
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remembering the whole catalog.
|
|
3
|
+
*
|
|
4
|
+
* The parsed metadata facts and the provider's document are remembered
|
|
5
|
+
* already, but they are remembered as *evidence* — what to ask about, and
|
|
6
|
+
* what to fall back to when an answer cannot be had. This file remembers
|
|
7
|
+
* the *answer*: the catalog as it stood, so that a process can serve it
|
|
8
|
+
* without waiting for the network at all.
|
|
9
|
+
*
|
|
10
|
+
* That is the difference between a wait and a stale page. A process that
|
|
11
|
+
* starts with nothing must ask the gateway, the metadata document, and the
|
|
12
|
+
* provider's page before it can name a model — and a new session's first
|
|
13
|
+
* message waits for that. A process that starts with a remembered catalog
|
|
14
|
+
* can name a model immediately, and refresh what it remembered afterwards.
|
|
15
|
+
*
|
|
16
|
+
* The listing is the authority on which models exist, so a remembered
|
|
17
|
+
* catalog can only describe the models the gateway advertised when it was
|
|
18
|
+
* written. A model the gateway has since added is missing from it — and a
|
|
19
|
+
* model the gateway has since retired is still in it, which is why the
|
|
20
|
+
* remembered catalog is always presented as being *from* the past.
|
|
21
|
+
*
|
|
22
|
+
* @module @dan-ai-studio/dshopencodego/catalog/snapshot-cache
|
|
23
|
+
*/
|
|
24
|
+
import type { GoQuota } from '../go-limits.ts';
|
|
25
|
+
import type { ModelFacts } from './metadata.ts';
|
|
26
|
+
import type { WireProtocol } from './protocol.ts';
|
|
27
|
+
/**
|
|
28
|
+
* The catalog a previous build produced.
|
|
29
|
+
*
|
|
30
|
+
* A cache that cannot be read is no cache: the caller then builds the
|
|
31
|
+
* catalog the ordinary way, which waits, and no worse.
|
|
32
|
+
* @param cacheDir - the plugin's cache directory.
|
|
33
|
+
* @returns the remembered catalog, or undefined.
|
|
34
|
+
*/
|
|
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;
|
|
41
|
+
/**
|
|
42
|
+
* Remember the catalog a build produced.
|
|
43
|
+
*
|
|
44
|
+
* A cache directory that cannot be written costs a wait, and nothing else.
|
|
45
|
+
* @param cacheDir - the plugin's cache directory.
|
|
46
|
+
* @param facts - the facts the build produced.
|
|
47
|
+
* @param quotas - the provider's allowances.
|
|
48
|
+
* @param protocols - the provider's protocols.
|
|
49
|
+
*/
|
|
50
|
+
export declare function writeSnapshotCache(cacheDir: string | undefined, facts: ReadonlyMap<string, ModelFacts>, quotas: ReadonlyMap<string, GoQuota>, protocols: ReadonlyMap<string, WireProtocol>): void;
|
package/package.json
CHANGED