dsh-opencode-go 0.1.12 → 0.1.13
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 +20 -6
- package/README.md +20 -7
- package/THIRD_PARTY_NOTICES.md +2 -0
- package/docs/verification.md +38 -0
- package/lib/client.js +286 -53
- package/lib/index.js +120 -20
- package/lib/types/adapter.d.ts +3 -3
- package/lib/types/catalog.d.ts +10 -2
- package/lib/types/client/SessionCache.d.ts +6 -0
- package/lib/types/client/UsagePill.d.ts +2 -0
- package/lib/types/client/locales.d.ts +22 -0
- package/lib/types/client/section-controller.d.ts +2 -0
- package/lib/types/client/session-cache.d.ts +18 -0
- package/lib/types/config.d.ts +1 -1
- package/lib/types/models-contract.d.ts +11 -1
- package/package.json +23 -22
package/lib/index.js
CHANGED
|
@@ -749,9 +749,27 @@ function parseGoModelCatalog(value) {
|
|
|
749
749
|
return {
|
|
750
750
|
models: parseGoModels(catalog.models),
|
|
751
751
|
stale: catalog.stale,
|
|
752
|
-
...catalog.error === void 0 ? {} : { error: catalog.error }
|
|
752
|
+
...catalog.error === void 0 ? {} : { error: catalog.error },
|
|
753
|
+
...catalog.sources === void 0 ? {} : { sources: parseSources(catalog.sources) }
|
|
753
754
|
};
|
|
754
755
|
}
|
|
756
|
+
function parseSources(value) {
|
|
757
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Invalid OpenCode Go catalog sources");
|
|
758
|
+
const sources = value;
|
|
759
|
+
const parse = (value2) => {
|
|
760
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) throw new Error("Invalid OpenCode Go catalog source status");
|
|
761
|
+
const source = value2;
|
|
762
|
+
if (source.updatedAt !== void 0 && (typeof source.updatedAt !== "number" || !Number.isSafeInteger(source.updatedAt) || source.updatedAt < 0 || source.updatedAt > 864e13)) {
|
|
763
|
+
throw new Error("Invalid OpenCode Go catalog source timestamp");
|
|
764
|
+
}
|
|
765
|
+
if (source.error !== void 0 && typeof source.error !== "string") throw new Error("Invalid OpenCode Go catalog source error");
|
|
766
|
+
return {
|
|
767
|
+
...source.updatedAt === void 0 ? {} : { updatedAt: source.updatedAt },
|
|
768
|
+
...source.error === void 0 ? {} : { error: source.error }
|
|
769
|
+
};
|
|
770
|
+
};
|
|
771
|
+
return { listing: parse(sources.listing), metadata: parse(sources.metadata) };
|
|
772
|
+
}
|
|
755
773
|
var codec = {
|
|
756
774
|
mode: "strict",
|
|
757
775
|
typeSymbol: "dsh-opencode-go#GoModelCatalog",
|
|
@@ -973,8 +991,30 @@ var PROVIDER_ID = "opencode-go";
|
|
|
973
991
|
var DISPLAY_NAME = "OpenCode Go";
|
|
974
992
|
var DEFAULT_BASE_URL = "https://opencode.ai/zen/go/v1";
|
|
975
993
|
var MODELS_FETCH_TIMEOUT_MS = 1e4;
|
|
994
|
+
var METADATA_FETCH_TIMEOUT_MS = 3e4;
|
|
995
|
+
var STALE_WHILE_REVALIDATE_MS = 5 * 6e4;
|
|
976
996
|
var MODEL_LISTING_MAX_BYTES = 1024 * 1024;
|
|
977
997
|
var MODEL_METADATA_MAX_BYTES = 16 * 1024 * 1024;
|
|
998
|
+
var RETRY_MIN_MS = 5e3;
|
|
999
|
+
var RETRY_MAX_MS = 6e4;
|
|
1000
|
+
function waitForSnapshot(pending, signal) {
|
|
1001
|
+
if (signal === void 0) return pending;
|
|
1002
|
+
return new Promise((resolve, reject) => {
|
|
1003
|
+
const abort = () => {
|
|
1004
|
+
signal.removeEventListener("abort", abort);
|
|
1005
|
+
reject(new LlmError5("opencode-go catalog request aborted by caller", "ABORTED", { cause: signal.reason }));
|
|
1006
|
+
};
|
|
1007
|
+
pending.then((value) => {
|
|
1008
|
+
signal.removeEventListener("abort", abort);
|
|
1009
|
+
resolve(value);
|
|
1010
|
+
}, (error) => {
|
|
1011
|
+
signal.removeEventListener("abort", abort);
|
|
1012
|
+
reject(error);
|
|
1013
|
+
});
|
|
1014
|
+
if (signal.aborted) abort();
|
|
1015
|
+
else signal.addEventListener("abort", abort, { once: true });
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
978
1018
|
function builtinModels(baseURL) {
|
|
979
1019
|
return new Map(getBuiltinModels("opencode-go").map((model) => [model.id, {
|
|
980
1020
|
...model,
|
|
@@ -1047,17 +1087,26 @@ var OpencodeGoCatalog = class {
|
|
|
1047
1087
|
pending;
|
|
1048
1088
|
metadata;
|
|
1049
1089
|
metadataETag;
|
|
1050
|
-
|
|
1051
|
-
|
|
1090
|
+
metadataUpdatedAtMs;
|
|
1091
|
+
failures = 0;
|
|
1092
|
+
refreshAtMs = 0;
|
|
1093
|
+
snapshot(force = false, signal) {
|
|
1094
|
+
if (signal?.aborted) {
|
|
1095
|
+
return Promise.reject(new LlmError5("opencode-go catalog request aborted by caller", "ABORTED", { cause: signal.reason }));
|
|
1096
|
+
}
|
|
1097
|
+
if (!force && this.served !== void 0 && Date.now() < this.refreshAtMs) {
|
|
1052
1098
|
return Promise.resolve(this.served);
|
|
1053
1099
|
}
|
|
1054
1100
|
this.pending ??= this.build().then((snapshot) => {
|
|
1055
1101
|
this.served = snapshot;
|
|
1102
|
+
this.failures = snapshot.live && snapshot.metadataLive ? 0 : Math.min(this.failures + 1, 5);
|
|
1103
|
+
const lifetime = this.failures === 0 ? this.refreshMs : Math.min(this.refreshMs, RETRY_MIN_MS * 2 ** (this.failures - 1), RETRY_MAX_MS);
|
|
1104
|
+
this.refreshAtMs = snapshot.fetchedAtMs + lifetime;
|
|
1056
1105
|
return snapshot;
|
|
1057
1106
|
}).finally(() => {
|
|
1058
1107
|
this.pending = void 0;
|
|
1059
1108
|
});
|
|
1060
|
-
return this.pending;
|
|
1109
|
+
return waitForSnapshot(this.pending, signal);
|
|
1061
1110
|
}
|
|
1062
1111
|
/** Conditional HTTP requests save bandwidth while still checking for updated metadata. */
|
|
1063
1112
|
async refreshMetadata(builtin) {
|
|
@@ -1068,23 +1117,37 @@ var OpencodeGoCatalog = class {
|
|
|
1068
1117
|
...this.metadataETag === void 0 ? {} : { "if-none-match": this.metadataETag }
|
|
1069
1118
|
},
|
|
1070
1119
|
cache: "no-cache",
|
|
1071
|
-
signal: AbortSignal.timeout(
|
|
1120
|
+
signal: AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS)
|
|
1072
1121
|
}, MODEL_METADATA_MAX_BYTES);
|
|
1073
|
-
if (response.status === 304 && this.metadata !== void 0)
|
|
1074
|
-
|
|
1075
|
-
|
|
1122
|
+
if (response.status === 304 && this.metadata !== void 0) {
|
|
1123
|
+
this.metadataUpdatedAtMs = Date.now();
|
|
1124
|
+
return this.metadata;
|
|
1125
|
+
}
|
|
1126
|
+
if (!response.ok) throw new LlmError5(`${MODEL_METADATA_URL} answered HTTP ${response.status}`, "DISCOVERY_FAILED");
|
|
1127
|
+
let metadata;
|
|
1128
|
+
try {
|
|
1129
|
+
metadata = readModelMetadata(body, this.baseURL, builtin);
|
|
1130
|
+
} catch (error) {
|
|
1131
|
+
throw new LlmError5(`${MODEL_METADATA_URL} returned invalid model configuration`, "DISCOVERY_FAILED", { cause: error });
|
|
1132
|
+
}
|
|
1076
1133
|
this.metadata = metadata;
|
|
1077
1134
|
this.metadataETag = response.headers.get("etag") ?? void 0;
|
|
1135
|
+
this.metadataUpdatedAtMs = Date.now();
|
|
1078
1136
|
return metadata;
|
|
1079
1137
|
}
|
|
1080
1138
|
/** Gateway ids decide membership; online metadata decides how to call each model. */
|
|
1081
1139
|
async build() {
|
|
1082
1140
|
const builtin = builtinModels(this.baseURL);
|
|
1083
1141
|
const [listing, metadataResult] = await Promise.allSettled([
|
|
1084
|
-
fetchLiveModelIds(this.baseURL),
|
|
1142
|
+
fetchLiveModelIds(this.baseURL).then((ids) => ({ ids, updatedAtMs: Date.now() })),
|
|
1085
1143
|
this.refreshMetadata(builtin)
|
|
1086
1144
|
]);
|
|
1087
1145
|
const metadata = metadataResult.status === "fulfilled" ? metadataResult.value : this.metadata;
|
|
1146
|
+
const metadataStatus = {
|
|
1147
|
+
metadataLive: metadataResult.status === "fulfilled",
|
|
1148
|
+
...this.metadataUpdatedAtMs === void 0 ? {} : { metadataUpdatedAtMs: this.metadataUpdatedAtMs },
|
|
1149
|
+
...metadataResult.status === "rejected" ? { metadataFailure: metadataResult.reason } : {}
|
|
1150
|
+
};
|
|
1088
1151
|
const known = new Map([...builtin, ...this.served?.models ?? [], ...metadata?.models ?? []]);
|
|
1089
1152
|
if (metadataResult.status === "rejected") {
|
|
1090
1153
|
this.onFallback({ url: MODEL_METADATA_URL, error: metadataResult.reason, kept: known.size });
|
|
@@ -1099,12 +1162,14 @@ var OpencodeGoCatalog = class {
|
|
|
1099
1162
|
provider: buildProvider(this.baseURL, [...models2.values()]),
|
|
1100
1163
|
live: false,
|
|
1101
1164
|
fetchedAtMs: Date.now(),
|
|
1165
|
+
...metadataStatus,
|
|
1166
|
+
...this.served?.listingUpdatedAtMs === void 0 ? {} : { listingUpdatedAtMs: this.served.listingUpdatedAtMs },
|
|
1102
1167
|
listingFailure: listing.reason
|
|
1103
1168
|
};
|
|
1104
1169
|
}
|
|
1105
1170
|
const models = /* @__PURE__ */ new Map();
|
|
1106
1171
|
const unavailable = /* @__PURE__ */ new Map();
|
|
1107
|
-
for (const id of listing.value) {
|
|
1172
|
+
for (const id of listing.value.ids) {
|
|
1108
1173
|
const error = metadata?.errors.get(id);
|
|
1109
1174
|
const model = known.get(id);
|
|
1110
1175
|
if (error !== void 0 || model === void 0) {
|
|
@@ -1115,19 +1180,28 @@ var OpencodeGoCatalog = class {
|
|
|
1115
1180
|
}
|
|
1116
1181
|
if (unavailable.size > 0) this.onOmitted([...unavailable.keys()]);
|
|
1117
1182
|
return {
|
|
1118
|
-
details: new Map(listing.value.map((id) => [id, metadata?.details.get(id) ?? {}])),
|
|
1183
|
+
details: new Map(listing.value.ids.map((id) => [id, metadata?.details.get(id) ?? {}])),
|
|
1119
1184
|
models,
|
|
1120
1185
|
unavailable,
|
|
1121
1186
|
provider: buildProvider(this.baseURL, [...models.values()]),
|
|
1122
1187
|
live: true,
|
|
1123
|
-
fetchedAtMs: Date.now()
|
|
1188
|
+
fetchedAtMs: Date.now(),
|
|
1189
|
+
listingUpdatedAtMs: listing.value.updatedAtMs,
|
|
1190
|
+
...metadataStatus
|
|
1124
1191
|
};
|
|
1125
1192
|
}
|
|
1126
1193
|
/** New or previously unconfigured ids get a fresh lookup even during the runtime TTL. */
|
|
1127
|
-
async forModel(id) {
|
|
1194
|
+
async forModel(id, signal) {
|
|
1128
1195
|
const cached = this.served;
|
|
1129
|
-
|
|
1130
|
-
if (
|
|
1196
|
+
if (signal?.aborted) throw new LlmError5("opencode-go catalog request aborted by caller", "ABORTED", { cause: signal.reason });
|
|
1197
|
+
if (cached?.models.has(id) && cached.listingUpdatedAtMs !== void 0 && cached.metadataUpdatedAtMs !== void 0 && Date.now() < Math.min(cached.listingUpdatedAtMs, cached.metadataUpdatedAtMs) + this.refreshMs + STALE_WHILE_REVALIDATE_MS) {
|
|
1198
|
+
void this.snapshot().catch(() => {
|
|
1199
|
+
});
|
|
1200
|
+
return cached;
|
|
1201
|
+
}
|
|
1202
|
+
let snapshot = await this.snapshot(false, signal);
|
|
1203
|
+
if (!snapshot.models.has(id) && snapshot === cached) snapshot = await this.snapshot(true, signal);
|
|
1204
|
+
if (signal?.aborted) throw new LlmError5("opencode-go catalog request aborted by caller", "ABORTED", { cause: signal.reason });
|
|
1131
1205
|
if (snapshot.unavailable.has(id)) {
|
|
1132
1206
|
throw new LlmError5(
|
|
1133
1207
|
`opencode-go model "${id}" is advertised but cannot be configured: ${snapshot.unavailable.get(id)}; refresh the model list to retry`,
|
|
@@ -1166,15 +1240,31 @@ function describeCatalog(snapshot) {
|
|
|
1166
1240
|
}
|
|
1167
1241
|
async function discoverSettingsModels(catalog) {
|
|
1168
1242
|
const snapshot = await catalog.snapshot(true);
|
|
1243
|
+
const metadataError = snapshot.metadataLive ? void 0 : metadataFailureMessage(snapshot.metadataFailure);
|
|
1244
|
+
const errors = [snapshot.live ? void 0 : listingError(snapshot).message, metadataError].filter((message) => message !== void 0);
|
|
1169
1245
|
return {
|
|
1170
1246
|
models: sortModels([
|
|
1171
1247
|
...describeConfiguredModels(snapshot),
|
|
1172
1248
|
...[...snapshot.unavailable.keys()].map((id) => ({ id, name: id, configurationMissing: true }))
|
|
1173
1249
|
].map((model) => ({ ...model, ...snapshot.details.get(model.id) }))),
|
|
1174
|
-
stale:
|
|
1175
|
-
...
|
|
1250
|
+
stale: errors.length > 0,
|
|
1251
|
+
...errors.length === 0 ? {} : { error: errors.join("; ") },
|
|
1252
|
+
sources: {
|
|
1253
|
+
listing: {
|
|
1254
|
+
...snapshot.listingUpdatedAtMs === void 0 ? {} : { updatedAt: snapshot.listingUpdatedAtMs },
|
|
1255
|
+
...snapshot.live ? {} : { error: listingError(snapshot).message }
|
|
1256
|
+
},
|
|
1257
|
+
metadata: {
|
|
1258
|
+
...snapshot.metadataUpdatedAtMs === void 0 ? {} : { updatedAt: snapshot.metadataUpdatedAtMs },
|
|
1259
|
+
...metadataError === void 0 ? {} : { error: metadataError }
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1176
1262
|
};
|
|
1177
1263
|
}
|
|
1264
|
+
function metadataFailureMessage(error) {
|
|
1265
|
+
const detail = error instanceof LlmError5 ? error.message : `${MODEL_METADATA_URL}: ${error instanceof SyntaxError ? "invalid JSON response" : error instanceof RangeError ? `response exceeds the ${MODEL_METADATA_MAX_BYTES} byte limit` : transportFailure(error)}`;
|
|
1266
|
+
return `could not refresh model configuration (${detail}); using previously fetched or built-in configuration where available`;
|
|
1267
|
+
}
|
|
1178
1268
|
|
|
1179
1269
|
// src/config.ts
|
|
1180
1270
|
import { MAX_TIMER_DELAY_MS } from "@deepseek-ai/dsh-timeout";
|
|
@@ -1288,9 +1378,9 @@ var OpencodeGoAdapter = class extends LlmAdapter {
|
|
|
1288
1378
|
inputModalities: [...model.input]
|
|
1289
1379
|
}));
|
|
1290
1380
|
}
|
|
1291
|
-
async resolveModel(_provider, model,
|
|
1381
|
+
async resolveModel(_provider, model, signal) {
|
|
1292
1382
|
const config = this.options.config();
|
|
1293
|
-
const snapshot = await this.catalogOf(config).forModel(model);
|
|
1383
|
+
const snapshot = await this.catalogOf(config).forModel(model, signal);
|
|
1294
1384
|
const resolved = snapshot.models.get(model);
|
|
1295
1385
|
if (resolved === void 0) {
|
|
1296
1386
|
throw new LlmError6(`opencode-go has no model "${model}"`, "UNKNOWN_MODEL");
|
|
@@ -1338,7 +1428,17 @@ var OpencodeGoAdapter = class extends LlmAdapter {
|
|
|
1338
1428
|
throw new LlmError6("llm-opencode-go does not support GenerateOptions.stop", "UNSUPPORTED_OPTION");
|
|
1339
1429
|
}
|
|
1340
1430
|
const config = this.options.config();
|
|
1341
|
-
|
|
1431
|
+
let snapshot;
|
|
1432
|
+
try {
|
|
1433
|
+
snapshot = await this.catalogOf(config).forModel(options.model, options.signal);
|
|
1434
|
+
} catch (error) {
|
|
1435
|
+
if (!options.signal?.aborted) throw error;
|
|
1436
|
+
yield { type: "finish", reason: {
|
|
1437
|
+
kind: "aborted",
|
|
1438
|
+
failure: { code: "ABORTED", message: "opencode-go request aborted by caller" }
|
|
1439
|
+
} };
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1342
1442
|
const advertised = snapshot.models.get(options.model);
|
|
1343
1443
|
if (advertised === void 0) {
|
|
1344
1444
|
throw new LlmError6(`opencode-go has no model "${options.model}"`, "UNKNOWN_MODEL");
|
package/lib/types/adapter.d.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* attribution User-Agent (`deepseek-harness/<version>`), which pi-ai's client
|
|
6
6
|
* lets request headers override, and `x-opencode-session`, which the gateway
|
|
7
7
|
* requires and uses to route a conversation and share its prompt cache. The
|
|
8
|
-
* header value is the request's session id
|
|
9
|
-
*
|
|
8
|
+
* header value is the request's session id, stable across continuation and
|
|
9
|
+
* restore. Actual cache reads remain an upstream usage fact. A request with no
|
|
10
10
|
* session id gets a fresh random value rather than a shared constant, because
|
|
11
11
|
* a constant would merge unrelated traffic into one cache bucket.
|
|
12
12
|
*
|
|
@@ -91,7 +91,7 @@ export declare class OpencodeGoAdapter extends LlmAdapter {
|
|
|
91
91
|
name: string;
|
|
92
92
|
};
|
|
93
93
|
listModels(_provider: string): Promise<readonly LlmModelInfo[]>;
|
|
94
|
-
resolveModel(_provider: string, model: string,
|
|
94
|
+
resolveModel(_provider: string, model: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
|
|
95
95
|
/** Describe one model: capacities plus the reasoning levels it actually offers. */
|
|
96
96
|
private modelInfo;
|
|
97
97
|
/** Validate an explicit effort against the model's own levels, without clamping. */
|
package/lib/types/catalog.d.ts
CHANGED
|
@@ -12,8 +12,13 @@ export interface CatalogSnapshot {
|
|
|
12
12
|
readonly unavailable: ReadonlyMap<string, string>;
|
|
13
13
|
readonly provider: Provider;
|
|
14
14
|
readonly live: boolean;
|
|
15
|
+
readonly metadataLive: boolean;
|
|
15
16
|
/** Retained for explicit discovery; runtime callers may still use the last catalog. */
|
|
16
17
|
readonly listingFailure?: unknown;
|
|
18
|
+
readonly metadataFailure?: unknown;
|
|
19
|
+
/** Last successful fetch or revalidation, not the time of a failed attempt. */
|
|
20
|
+
readonly listingUpdatedAtMs?: number;
|
|
21
|
+
readonly metadataUpdatedAtMs?: number;
|
|
17
22
|
readonly fetchedAtMs: number;
|
|
18
23
|
}
|
|
19
24
|
/** A valid empty listing means the gateway serves nothing; malformed replies are failures. */
|
|
@@ -29,6 +34,9 @@ export declare class OpencodeGoCatalog {
|
|
|
29
34
|
private pending;
|
|
30
35
|
private metadata;
|
|
31
36
|
private metadataETag;
|
|
37
|
+
private metadataUpdatedAtMs;
|
|
38
|
+
private failures;
|
|
39
|
+
private refreshAtMs;
|
|
32
40
|
constructor(baseURL: string, refreshMs: number, onFallback: (detail: {
|
|
33
41
|
url: string;
|
|
34
42
|
error: unknown;
|
|
@@ -36,13 +44,13 @@ export declare class OpencodeGoCatalog {
|
|
|
36
44
|
}) => void,
|
|
37
45
|
/** Kept for API compatibility; now reports unconfigured ids rather than hiding them. */
|
|
38
46
|
onOmitted: (ids: readonly string[]) => void);
|
|
39
|
-
snapshot(force?: boolean): Promise<CatalogSnapshot>;
|
|
47
|
+
snapshot(force?: boolean, signal?: AbortSignal): Promise<CatalogSnapshot>;
|
|
40
48
|
/** Conditional HTTP requests save bandwidth while still checking for updated metadata. */
|
|
41
49
|
private refreshMetadata;
|
|
42
50
|
/** Gateway ids decide membership; online metadata decides how to call each model. */
|
|
43
51
|
private build;
|
|
44
52
|
/** New or previously unconfigured ids get a fresh lookup even during the runtime TTL. */
|
|
45
|
-
forModel(id: string): Promise<CatalogSnapshot>;
|
|
53
|
+
forModel(id: string, signal?: AbortSignal): Promise<CatalogSnapshot>;
|
|
46
54
|
}
|
|
47
55
|
/** Explicit discovery always revalidates both sources, including during the runtime TTL. */
|
|
48
56
|
export declare function discoverCatalogModels(catalog: OpencodeGoCatalog): Promise<readonly LlmDiscoveredModel[]>;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
2
2
|
import type { ModelDirectoryState } from '@deepseek-ai/dsh-client-ui-model-selection/client';
|
|
3
3
|
import type { GoUsage } from '../usage-contract.ts';
|
|
4
|
+
import type { SessionEventSource } from './session-cache.ts';
|
|
4
5
|
export interface UsagePillProps {
|
|
5
6
|
directory: SnapshotStore<ModelDirectoryState>;
|
|
6
7
|
readUsage: () => Promise<GoUsage>;
|
|
7
8
|
t: (key: string) => string;
|
|
8
9
|
getLocale?: () => string;
|
|
10
|
+
sessionEvents?: SessionEventSource;
|
|
9
11
|
}
|
|
10
12
|
/** Only the selected Go provider mounts a poller, so other models send no usage traffic. */
|
|
11
13
|
export declare function UsagePill({ directory, ...props }: UsagePillProps): import("react").JSX.Element | null;
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
/** Copy dictionaries for the OpenCode Go settings page. English is the key-set source of truth. */
|
|
2
2
|
export declare const en: {
|
|
3
|
+
cacheTitle: string;
|
|
4
|
+
cacheReadRatio: string;
|
|
5
|
+
cacheReadTokens: string;
|
|
6
|
+
cacheInputTokens: string;
|
|
7
|
+
cacheWriteTokens: string;
|
|
8
|
+
cacheResponses: string;
|
|
9
|
+
cacheEmpty: string;
|
|
10
|
+
cacheExcluded: string;
|
|
11
|
+
cacheScope: string;
|
|
12
|
+
cacheMore: string;
|
|
13
|
+
cacheRoutingHint: string;
|
|
3
14
|
usageTitle: string;
|
|
4
15
|
usageRollingShort: string;
|
|
5
16
|
usageHint: string;
|
|
@@ -20,6 +31,8 @@ export declare const en: {
|
|
|
20
31
|
nav: string;
|
|
21
32
|
title: string;
|
|
22
33
|
intro: string;
|
|
34
|
+
starProject: string;
|
|
35
|
+
starProjectTitle: string;
|
|
23
36
|
enabledLabel: string;
|
|
24
37
|
enabledHint: string;
|
|
25
38
|
enabledOff: string;
|
|
@@ -34,6 +47,13 @@ export declare const en: {
|
|
|
34
47
|
modelsLoading: string;
|
|
35
48
|
modelsFailed: string;
|
|
36
49
|
modelsStale: string;
|
|
50
|
+
modelsMetadataFailed: string;
|
|
51
|
+
modelsListingSource: string;
|
|
52
|
+
modelsMetadataSource: string;
|
|
53
|
+
modelsSourceFailed: string;
|
|
54
|
+
modelsSourceCurrent: string;
|
|
55
|
+
modelsSourceNeverFetched: string;
|
|
56
|
+
modelsSourceUpdated: string;
|
|
37
57
|
modelsRefreshing: string;
|
|
38
58
|
modelsEmpty: string;
|
|
39
59
|
modelsCount: string;
|
|
@@ -51,6 +71,8 @@ export declare const en: {
|
|
|
51
71
|
modelVisibleLabel: string;
|
|
52
72
|
configurationMissing: string;
|
|
53
73
|
configurationMissingHint: string;
|
|
74
|
+
configurationUnavailable: string;
|
|
75
|
+
configurationUnavailableHint: string;
|
|
54
76
|
pickerFailed: string;
|
|
55
77
|
deprecatedBadge: string;
|
|
56
78
|
deprecatedHint: string;
|
|
@@ -67,11 +67,13 @@ export type OpencodeGoModels =
|
|
|
67
67
|
readonly stale?: boolean;
|
|
68
68
|
readonly message?: string;
|
|
69
69
|
readonly refreshing?: boolean;
|
|
70
|
+
readonly sources?: GoModelCatalog['sources'];
|
|
70
71
|
}
|
|
71
72
|
/** The listing could not be read; `message` is the Host's own diagnostic. */
|
|
72
73
|
| {
|
|
73
74
|
readonly status: 'failed';
|
|
74
75
|
readonly message?: string;
|
|
76
|
+
readonly sources?: GoModelCatalog['sources'];
|
|
75
77
|
};
|
|
76
78
|
/** What the settings page renders. */
|
|
77
79
|
export interface OpencodeGoSectionState extends FormShell {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store';
|
|
2
|
+
/** Small structural face of the optional Host session event feed, across Host versions. */
|
|
3
|
+
export type SessionEventSource = ObservableSnapshot<unknown>;
|
|
4
|
+
export interface SessionCacheStats {
|
|
5
|
+
responses: number;
|
|
6
|
+
missingUsage: number;
|
|
7
|
+
unattributed: number;
|
|
8
|
+
inputTokens: number;
|
|
9
|
+
cacheReadTokens: number;
|
|
10
|
+
cacheWriteTokens: number;
|
|
11
|
+
hasMore: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Use only already-loaded history; never open a session or fetch more pages for diagnostics. */
|
|
14
|
+
export declare function sessionEventSource(sessions: unknown, sessionId: string): SessionEventSource | undefined;
|
|
15
|
+
/** Aggregate settled Go responses. Pi-ai's input excludes cache reads AND writes. */
|
|
16
|
+
export declare function summarizeSessionCache(window: unknown): SessionCacheStats;
|
|
17
|
+
/** Stable snapshots avoid rendering on unrelated live chunks; history replacement recomputes. */
|
|
18
|
+
export declare function sessionCacheSource(events: SessionEventSource): ObservableSnapshot<SessionCacheStats>;
|
package/lib/types/config.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import z from '@deepseek-ai/schemastery';
|
|
12
12
|
/** Environment variable resolving the OpenCode API key. */
|
|
13
13
|
export declare const DEFAULT_API_KEY_ENV = "OPENCODE_API_KEY";
|
|
14
|
-
/**
|
|
14
|
+
/** Successful refresh lifetime; failed refreshes retry sooner and explicit discovery revalidates immediately. */
|
|
15
15
|
export declare const DEFAULT_REFRESH_MINUTES = 60;
|
|
16
16
|
/** Default maximum idle interval while a stream read is outstanding. */
|
|
17
17
|
export declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 300000;
|
|
@@ -10,11 +10,21 @@ export interface GoModel {
|
|
|
10
10
|
/** Advertised by the gateway but lacking a usable protocol and capability configuration. */
|
|
11
11
|
configurationMissing?: boolean;
|
|
12
12
|
}
|
|
13
|
-
/**
|
|
13
|
+
/** Last successful check of one catalog source, plus any current refresh failure. */
|
|
14
|
+
export interface GoCatalogSourceStatus {
|
|
15
|
+
readonly updatedAt?: number;
|
|
16
|
+
readonly error?: string;
|
|
17
|
+
}
|
|
18
|
+
/** A failed refresh retains the Host's usable data with an explicit diagnostic. */
|
|
14
19
|
export interface GoModelCatalog {
|
|
15
20
|
readonly models: readonly GoModel[];
|
|
16
21
|
readonly stale: boolean;
|
|
17
22
|
readonly error?: string;
|
|
23
|
+
/** Optional for clients reading an older Host response. Timestamps are Unix milliseconds. */
|
|
24
|
+
readonly sources?: {
|
|
25
|
+
readonly listing: GoCatalogSourceStatus;
|
|
26
|
+
readonly metadata: GoCatalogSourceStatus;
|
|
27
|
+
};
|
|
18
28
|
}
|
|
19
29
|
/** Missing configuration cannot be enabled; configured models follow explicit switches or lifecycle defaults. */
|
|
20
30
|
export declare function isModelEnabled(model: Pick<GoModel, 'id' | 'deprecated' | 'configurationMissing'>, modelVisibility?: Readonly<Record<string, boolean>>): boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-opencode-go",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "OpenCode Go model provider and settings UI for DeepSeek Harness",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"engines": {
|
|
16
16
|
"node": "^22.19.0 || >=24.0.0",
|
|
17
|
-
"dsh": ">=0.1.5-rc.1
|
|
17
|
+
"dsh": ">=0.1.5-rc.1"
|
|
18
18
|
},
|
|
19
19
|
"main": "./lib/index.js",
|
|
20
20
|
"types": "./lib/types/index.d.ts",
|
|
@@ -60,26 +60,26 @@
|
|
|
60
60
|
"verify:headless": "node scripts/headless-smoke.mjs"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
|
-
"@deepseek-ai/cordis": "4.0.2
|
|
64
|
-
"@deepseek-ai/dsh-api-gateway": ">=0.1.5-rc.1
|
|
65
|
-
"@deepseek-ai/dsh-api-remotes": ">=0.1.5-rc.1
|
|
66
|
-
"@deepseek-ai/dsh-api-settings-controller": ">=0.1.5-rc.1
|
|
67
|
-
"@deepseek-ai/dsh-attachment": ">=0.1.5-rc.1
|
|
68
|
-
"@deepseek-ai/dsh-client-locale": ">=0.1.5-rc.1
|
|
69
|
-
"@deepseek-ai/dsh-client-store": ">=0.1.5-rc.1
|
|
70
|
-
"@deepseek-ai/dsh-client-ui-conversation": ">=0.1.5-rc.1
|
|
71
|
-
"@deepseek-ai/dsh-client-ui-model-selection": ">=0.1.5-rc.1
|
|
72
|
-
"@deepseek-ai/dsh-client-ui-renderer": ">=0.1.5-rc.1
|
|
73
|
-
"@deepseek-ai/dsh-client-ui-settings": ">=0.1.5-rc.1
|
|
74
|
-
"@deepseek-ai/dsh-client-ui-slots": ">=0.1.5-rc.1
|
|
75
|
-
"@deepseek-ai/dsh-credentials": ">=0.1.5-rc.1
|
|
76
|
-
"@deepseek-ai/dsh-fs": ">=0.1.5-rc.1
|
|
77
|
-
"@deepseek-ai/dsh-launch-environment": ">=0.1.5-rc.1
|
|
78
|
-
"@deepseek-ai/dsh-llm": ">=0.1.5-rc.1
|
|
79
|
-
"@deepseek-ai/dsh-settings": ">=0.1.5-rc.1
|
|
80
|
-
"@deepseek-ai/dsh-timeout": ">=0.1.5-rc.1
|
|
81
|
-
"@deepseek-ai/dsh-typert-protocol": ">=0.1.5-rc.1
|
|
82
|
-
"@deepseek-ai/dsh-typert-registry": ">=0.1.5-rc.1
|
|
63
|
+
"@deepseek-ai/cordis": ">=4.0.2",
|
|
64
|
+
"@deepseek-ai/dsh-api-gateway": ">=0.1.5-rc.1",
|
|
65
|
+
"@deepseek-ai/dsh-api-remotes": ">=0.1.5-rc.1",
|
|
66
|
+
"@deepseek-ai/dsh-api-settings-controller": ">=0.1.5-rc.1",
|
|
67
|
+
"@deepseek-ai/dsh-attachment": ">=0.1.5-rc.1",
|
|
68
|
+
"@deepseek-ai/dsh-client-locale": ">=0.1.5-rc.1",
|
|
69
|
+
"@deepseek-ai/dsh-client-store": ">=0.1.5-rc.1",
|
|
70
|
+
"@deepseek-ai/dsh-client-ui-conversation": ">=0.1.5-rc.1",
|
|
71
|
+
"@deepseek-ai/dsh-client-ui-model-selection": ">=0.1.5-rc.1",
|
|
72
|
+
"@deepseek-ai/dsh-client-ui-renderer": ">=0.1.5-rc.1",
|
|
73
|
+
"@deepseek-ai/dsh-client-ui-settings": ">=0.1.5-rc.1",
|
|
74
|
+
"@deepseek-ai/dsh-client-ui-slots": ">=0.1.5-rc.1",
|
|
75
|
+
"@deepseek-ai/dsh-credentials": ">=0.1.5-rc.1",
|
|
76
|
+
"@deepseek-ai/dsh-fs": ">=0.1.5-rc.1",
|
|
77
|
+
"@deepseek-ai/dsh-launch-environment": ">=0.1.5-rc.1",
|
|
78
|
+
"@deepseek-ai/dsh-llm": ">=0.1.5-rc.1",
|
|
79
|
+
"@deepseek-ai/dsh-settings": ">=0.1.5-rc.1",
|
|
80
|
+
"@deepseek-ai/dsh-timeout": ">=0.1.5-rc.1",
|
|
81
|
+
"@deepseek-ai/dsh-typert-protocol": ">=0.1.5-rc.1",
|
|
82
|
+
"@deepseek-ai/dsh-typert-registry": ">=0.1.5-rc.1"
|
|
83
83
|
},
|
|
84
84
|
"dependencies": {
|
|
85
85
|
"@deepseek-ai/dsh-brand": "0.1.6-alpha.1",
|
|
@@ -110,6 +110,7 @@
|
|
|
110
110
|
"@deepseek-ai/dsh-home-paths": "0.1.6-alpha.1",
|
|
111
111
|
"@deepseek-ai/dsh-launch-environment": "0.1.6-alpha.1",
|
|
112
112
|
"@deepseek-ai/dsh-llm": "0.1.6-alpha.1",
|
|
113
|
+
"@deepseek-ai/dsh-session": "0.1.6-alpha.1",
|
|
113
114
|
"@deepseek-ai/dsh-settings": "0.1.6-alpha.1",
|
|
114
115
|
"@deepseek-ai/dsh-settings-file": "0.1.6-alpha.1",
|
|
115
116
|
"@deepseek-ai/dsh-timeout": "0.1.6-alpha.1",
|