@dan-ai-studio/dshopencodego 0.1.5

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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +156 -0
  3. package/README.md +156 -0
  4. package/cordis.patch.yml +3 -0
  5. package/lib/build-info.json +11 -0
  6. package/lib/client.js +1215 -0
  7. package/lib/index.js +2036 -0
  8. package/lib/types/adapter.d.ts +84 -0
  9. package/lib/types/adapter.js +311 -0
  10. package/lib/types/catalog/constants.d.ts +16 -0
  11. package/lib/types/catalog/constants.js +16 -0
  12. package/lib/types/catalog/contract.d.ts +26 -0
  13. package/lib/types/catalog/contract.js +131 -0
  14. package/lib/types/catalog/gateway.d.ts +20 -0
  15. package/lib/types/catalog/gateway.js +59 -0
  16. package/lib/types/catalog/index.d.ts +108 -0
  17. package/lib/types/catalog/index.js +288 -0
  18. package/lib/types/catalog/json-response.d.ts +19 -0
  19. package/lib/types/catalog/json-response.js +72 -0
  20. package/lib/types/catalog/metadata.d.ts +73 -0
  21. package/lib/types/catalog/metadata.js +259 -0
  22. package/lib/types/catalog/protocol.d.ts +65 -0
  23. package/lib/types/catalog/protocol.js +87 -0
  24. package/lib/types/catalog/reading.d.ts +41 -0
  25. package/lib/types/catalog/reading.js +68 -0
  26. package/lib/types/catalog/service.d.ts +32 -0
  27. package/lib/types/catalog/service.js +45 -0
  28. package/lib/types/config.d.ts +93 -0
  29. package/lib/types/config.js +76 -0
  30. package/lib/types/conversion/context.d.ts +55 -0
  31. package/lib/types/conversion/context.js +202 -0
  32. package/lib/types/conversion/index.d.ts +9 -0
  33. package/lib/types/conversion/index.js +7 -0
  34. package/lib/types/conversion/replay.d.ts +56 -0
  35. package/lib/types/conversion/replay.js +242 -0
  36. package/lib/types/conversion/stream.d.ts +46 -0
  37. package/lib/types/conversion/stream.js +203 -0
  38. package/lib/types/go-limits.d.ts +41 -0
  39. package/lib/types/go-limits.js +79 -0
  40. package/lib/types/index.d.ts +54 -0
  41. package/lib/types/index.js +195 -0
  42. package/lib/types/models.d.ts +90 -0
  43. package/lib/types/models.js +86 -0
  44. package/lib/types/remotes.d.ts +12 -0
  45. package/lib/types/remotes.js +28 -0
  46. package/lib/types/session-header.d.ts +36 -0
  47. package/lib/types/session-header.js +45 -0
  48. package/lib/types/usage/contract.d.ts +39 -0
  49. package/lib/types/usage/contract.js +106 -0
  50. package/lib/types/usage/index.d.ts +11 -0
  51. package/lib/types/usage/index.js +8 -0
  52. package/lib/types/usage/meter.d.ts +53 -0
  53. package/lib/types/usage/meter.js +65 -0
  54. package/lib/types/usage/service.d.ts +48 -0
  55. package/lib/types/usage/service.js +74 -0
  56. package/lib/types/usage/windows.d.ts +51 -0
  57. package/lib/types/usage/windows.js +84 -0
  58. package/package.json +147 -0
@@ -0,0 +1,53 @@
1
+ /**
2
+ * The since-boot usage meter.
3
+ *
4
+ * The gateway's `/usage` endpoint reports account percentages, not tokens, so
5
+ * the only honest source of per-model token counts is the usage the provider
6
+ * itself returns on each completed call. This meter accumulates exactly that,
7
+ * per model and in total, for the lifetime of the process — and says so: the
8
+ * number is labelled "since this Harness started", never "today".
9
+ *
10
+ * A conversation's own totals are durable and are folded from the session log
11
+ * by the client, which is why nothing here persists.
12
+ *
13
+ * @module @dan-ai-studio/dshopencodego/usage/meter
14
+ */
15
+ import type { TokenUsage } from '@deepseek-ai/dsh-llm';
16
+ /** Token counts and call count for one model, or for every model. */
17
+ export interface MeterTotals {
18
+ readonly calls: number;
19
+ readonly inputTokens: number;
20
+ readonly outputTokens: number;
21
+ readonly cacheReadTokens: number;
22
+ readonly cacheWriteTokens: number;
23
+ /** Sum of the four token fields, which is what a provider bills. */
24
+ readonly totalTokens: number;
25
+ }
26
+ /** One model's share of the since-boot meter. */
27
+ export interface MeterModelEntry extends MeterTotals {
28
+ readonly model: string;
29
+ }
30
+ /** The whole since-boot reading. */
31
+ export interface GoMeter {
32
+ /** When this process started counting, in epoch milliseconds. */
33
+ readonly sinceMs: number;
34
+ /** When this reading was taken, in epoch milliseconds. */
35
+ readonly atMs: number;
36
+ readonly totals: MeterTotals;
37
+ /** Per-model entries, busiest first. */
38
+ readonly models: readonly MeterModelEntry[];
39
+ }
40
+ /** Accumulates provider-reported usage for the lifetime of one Host process. */
41
+ export declare class UsageMeter {
42
+ private readonly sinceMs;
43
+ private totals;
44
+ private readonly byModel;
45
+ /**
46
+ * Record one completed call.
47
+ * @param model - the model id the call was made with.
48
+ * @param usage - the provider's own usage for that call.
49
+ */
50
+ record(model: string, usage: TokenUsage): void;
51
+ /** The current reading, busiest model first. */
52
+ snapshot(now?: number): GoMeter;
53
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The since-boot usage meter.
3
+ *
4
+ * The gateway's `/usage` endpoint reports account percentages, not tokens, so
5
+ * the only honest source of per-model token counts is the usage the provider
6
+ * itself returns on each completed call. This meter accumulates exactly that,
7
+ * per model and in total, for the lifetime of the process — and says so: the
8
+ * number is labelled "since this Harness started", never "today".
9
+ *
10
+ * A conversation's own totals are durable and are folded from the session log
11
+ * by the client, which is why nothing here persists.
12
+ *
13
+ * @module @dan-ai-studio/dshopencodego/usage/meter
14
+ */
15
+ const EMPTY = {
16
+ calls: 0,
17
+ inputTokens: 0,
18
+ outputTokens: 0,
19
+ cacheReadTokens: 0,
20
+ cacheWriteTokens: 0,
21
+ totalTokens: 0,
22
+ };
23
+ function add(totals, usage) {
24
+ const inputTokens = totals.inputTokens + usage.inputTokens;
25
+ const outputTokens = totals.outputTokens + usage.outputTokens;
26
+ const cacheReadTokens = totals.cacheReadTokens + (usage.cacheReadTokens ?? 0);
27
+ const cacheWriteTokens = totals.cacheWriteTokens + (usage.cacheWriteTokens ?? 0);
28
+ return {
29
+ calls: totals.calls + 1,
30
+ inputTokens,
31
+ outputTokens,
32
+ cacheReadTokens,
33
+ cacheWriteTokens,
34
+ // The four counts are disjoint (uncached input, cached input, output), so
35
+ // their sum is the billed total. The provider's own `totalTokens` is not
36
+ // used: it is optional, and mixing the two would double-count a cache read.
37
+ totalTokens: inputTokens + outputTokens + cacheReadTokens + cacheWriteTokens,
38
+ };
39
+ }
40
+ /** Accumulates provider-reported usage for the lifetime of one Host process. */
41
+ export class UsageMeter {
42
+ sinceMs = Date.now();
43
+ totals = EMPTY;
44
+ byModel = new Map();
45
+ /**
46
+ * Record one completed call.
47
+ * @param model - the model id the call was made with.
48
+ * @param usage - the provider's own usage for that call.
49
+ */
50
+ record(model, usage) {
51
+ this.totals = add(this.totals, usage);
52
+ this.byModel.set(model, add(this.byModel.get(model) ?? EMPTY, usage));
53
+ }
54
+ /** The current reading, busiest model first. */
55
+ snapshot(now = Date.now()) {
56
+ return {
57
+ sinceMs: this.sinceMs,
58
+ atMs: now,
59
+ totals: this.totals,
60
+ models: [...this.byModel.entries()]
61
+ .map(([model, totals]) => ({ model, ...totals }))
62
+ .sort((left, right) => right.totalTokens - left.totalTokens || left.model.localeCompare(right.model)),
63
+ };
64
+ }
65
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Host half of the usage Remote.
3
+ *
4
+ * The credential never leaves the Host: the browser asks for a reading and gets
5
+ * percentages, and the Host decides whether a failed read may be shown as the
6
+ * previous one. A read that fails while the account is unchanged keeps the last
7
+ * good value and says it is stale; a read for a different endpoint or key
8
+ * invalidates it, because the old numbers describe a different account.
9
+ *
10
+ * @module @dan-ai-studio/dshopencodego/usage/service
11
+ */
12
+ import type { Context } from '@deepseek-ai/cordis';
13
+ import { RemoteError, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
14
+ import type { UsageMeter } from './meter.ts';
15
+ import type { GoMeter } from './meter.ts';
16
+ import type { GoUsageWindows } from './windows.ts';
17
+ /** Host inputs for the usage service. */
18
+ export interface UsageServiceOptions {
19
+ /** Current gateway base URL. */
20
+ readonly baseURL: () => string;
21
+ /** Resolve the route credential per read. */
22
+ readonly resolveApiKey: () => Promise<string | undefined>;
23
+ /** The process-lifetime meter fed by the adapter. */
24
+ readonly meter: UsageMeter;
25
+ }
26
+ /**
27
+ * Decide what one failed read means for the client.
28
+ *
29
+ * A missing credential is a configuration fact, not a transient one: retrying
30
+ * cannot help and the reading a client holds was produced by a *different*
31
+ * account, so it must not be shown as this one's. Any other failure describes
32
+ * the account the client already has a reading for, so that reading stays valid
33
+ * and is marked stale.
34
+ * @param error - the failure raised by credential resolution or the read.
35
+ * @param source - identity of the account the failed read was for.
36
+ * @returns the domain error to send across the wire.
37
+ */
38
+ export declare function usageFailure(error: unknown, source: string | undefined): RemoteError;
39
+ /** Usage Remote: account windows from the gateway, spend from this process. */
40
+ export declare class OpencodeGoUsageService extends TypertRemoteService {
41
+ private identity;
42
+ private readonly options;
43
+ constructor(ctx: Context, options: UsageServiceOptions);
44
+ /** The account's three windows, or a domain failure the client can render. */
45
+ readWindows(): Promise<GoUsageWindows>;
46
+ /** What this process has spent through the route since it started. */
47
+ readMeter(): GoMeter;
48
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Host half of the usage Remote.
3
+ *
4
+ * The credential never leaves the Host: the browser asks for a reading and gets
5
+ * percentages, and the Host decides whether a failed read may be shown as the
6
+ * previous one. A read that fails while the account is unchanged keeps the last
7
+ * good value and says it is stale; a read for a different endpoint or key
8
+ * invalidates it, because the old numbers describe a different account.
9
+ *
10
+ * @module @dan-ai-studio/dshopencodego/usage/service
11
+ */
12
+ import { randomUUID } from 'node:crypto';
13
+ import { RemoteError, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
14
+ import { LlmError } from '@deepseek-ai/dsh-llm';
15
+ import { assertBaseURL } from "../config.js";
16
+ import { readUsageWindows } from "./windows.js";
17
+ /**
18
+ * Decide what one failed read means for the client.
19
+ *
20
+ * A missing credential is a configuration fact, not a transient one: retrying
21
+ * cannot help and the reading a client holds was produced by a *different*
22
+ * account, so it must not be shown as this one's. Any other failure describes
23
+ * the account the client already has a reading for, so that reading stays valid
24
+ * and is marked stale.
25
+ * @param error - the failure raised by credential resolution or the read.
26
+ * @param source - identity of the account the failed read was for.
27
+ * @returns the domain error to send across the wire.
28
+ */
29
+ export function usageFailure(error, source) {
30
+ const missing = error instanceof LlmError && error.code === 'MISSING_CREDENTIAL';
31
+ return new RemoteError('dshopencodego/usage-unavailable', error instanceof Error ? error.message : 'OpenCode Go usage is unavailable', missing
32
+ ? { retryable: false, retainPrevious: false }
33
+ : { retryable: true, retainPrevious: true, ...source === undefined ? {} : { source } }, { cause: error });
34
+ }
35
+ /** Usage Remote: account windows from the gateway, spend from this process. */
36
+ export class OpencodeGoUsageService extends TypertRemoteService {
37
+ identity;
38
+ options;
39
+ constructor(ctx, options) {
40
+ super(ctx, 'opencodeGoUsage');
41
+ this.options = options;
42
+ }
43
+ /** The account's three windows, or a domain failure the client can render. */
44
+ async readWindows() {
45
+ const baseURL = assertBaseURL(this.options.baseURL());
46
+ let key;
47
+ try {
48
+ key = await this.options.resolveApiKey();
49
+ }
50
+ catch (error) {
51
+ this.identity = undefined;
52
+ throw usageFailure(error, undefined);
53
+ }
54
+ if (key === undefined || key.length === 0) {
55
+ this.identity = undefined;
56
+ throw usageFailure(new LlmError('No OpenCode Go API key is configured', 'MISSING_CREDENTIAL'), undefined);
57
+ }
58
+ if (this.identity?.baseURL !== baseURL || this.identity.key !== key) {
59
+ // A new account or endpoint invalidates any reading the client holds.
60
+ this.identity = { baseURL, key, source: randomUUID() };
61
+ }
62
+ const { source } = this.identity;
63
+ try {
64
+ return await readUsageWindows({ baseURL, apiKey: key, source });
65
+ }
66
+ catch (error) {
67
+ throw usageFailure(error, source);
68
+ }
69
+ }
70
+ /** What this process has spent through the route since it started. */
71
+ readMeter() {
72
+ return this.options.meter.snapshot();
73
+ }
74
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The account's usage windows, read from the gateway.
3
+ *
4
+ * `/usage` reports three percentages — a rolling five-hour window, a week, and
5
+ * a month — each with a reset time and a rate-limited flag. It reports *no*
6
+ * per-model breakdown and no token counts, so this module never pretends to:
7
+ * per-model and per-session numbers are derived locally from real provider
8
+ * usage instead (see `./meter.ts` and the session fold in the client).
9
+ *
10
+ * @module @dan-ai-studio/dshopencodego/usage/windows
11
+ */
12
+ /** One quota window as the gateway reports it. */
13
+ export interface UsageWindow {
14
+ readonly status: 'ok' | 'rate-limited';
15
+ readonly percent: number;
16
+ readonly resetsAt: string;
17
+ }
18
+ /** The account's three windows plus an opaque identity for the reading. */
19
+ export interface GoUsageWindows {
20
+ /** Opaque Host identity for the endpoint and credential that produced this reading. */
21
+ readonly source?: string;
22
+ readonly rolling: UsageWindow;
23
+ readonly weekly: UsageWindow;
24
+ readonly monthly: UsageWindow;
25
+ }
26
+ /**
27
+ * Parse a usage response body.
28
+ * @param value - the `usage` member of the response.
29
+ * @returns the three windows.
30
+ * @throws {Error} when a window is missing or malformed; an unavailable reading
31
+ * must never be displayed as a zero percentage.
32
+ */
33
+ export declare function parseGoUsage(value: unknown): Omit<GoUsageWindows, 'source'>;
34
+ /** Inputs one usage read needs. */
35
+ export interface UsageReadOptions {
36
+ /** Normalized gateway base URL. */
37
+ readonly baseURL: string;
38
+ /** Resolved credential; absence is reported as an unconfigured route. */
39
+ readonly apiKey: string | undefined;
40
+ /** Opaque identity of the endpoint and credential pair, when known. */
41
+ readonly source?: string;
42
+ readonly signal?: AbortSignal;
43
+ }
44
+ /**
45
+ * Read the account's usage windows.
46
+ * @param options - endpoint, credential, and identity.
47
+ * @returns the three windows, tagged with the identity that produced them.
48
+ * @throws {LlmError} `USAGE_UNAVAILABLE` when the credential is missing, the
49
+ * endpoint fails, or the body is not a usage document.
50
+ */
51
+ export declare function readUsageWindows(options: UsageReadOptions): Promise<GoUsageWindows>;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The account's usage windows, read from the gateway.
3
+ *
4
+ * `/usage` reports three percentages — a rolling five-hour window, a week, and
5
+ * a month — each with a reset time and a rate-limited flag. It reports *no*
6
+ * per-model breakdown and no token counts, so this module never pretends to:
7
+ * per-model and per-session numbers are derived locally from real provider
8
+ * usage instead (see `./meter.ts` and the session fold in the client).
9
+ *
10
+ * @module @dan-ai-studio/dshopencodego/usage/windows
11
+ */
12
+ import { attributionHeaders, LlmError } from '@deepseek-ai/dsh-llm';
13
+ import { readBoundedJson } from "../catalog/json-response.js";
14
+ /** Response cap: this document is a few hundred bytes. */
15
+ const USAGE_MAX_BYTES = 1024 * 1024;
16
+ /** Timeout for one usage read. */
17
+ const USAGE_TIMEOUT_MS = 10_000;
18
+ /**
19
+ * Parse a usage response body.
20
+ * @param value - the `usage` member of the response.
21
+ * @returns the three windows.
22
+ * @throws {Error} when a window is missing or malformed; an unavailable reading
23
+ * must never be displayed as a zero percentage.
24
+ */
25
+ export function parseGoUsage(value) {
26
+ if (value === null || typeof value !== 'object')
27
+ throw new Error('invalid usage response');
28
+ const source = value;
29
+ const result = {};
30
+ for (const key of ['rolling', 'weekly', 'monthly']) {
31
+ const row = source[key];
32
+ if (row === null || row === undefined
33
+ || (row.status !== 'ok' && row.status !== 'rate-limited')
34
+ || typeof row.percent !== 'number' || !Number.isFinite(row.percent) || row.percent < 0
35
+ || typeof row.resetsAt !== 'string' || !Number.isFinite(Date.parse(row.resetsAt))) {
36
+ throw new Error(`invalid usage response: window "${key}" is missing or malformed`);
37
+ }
38
+ result[key] = { status: row.status, percent: row.percent, resetsAt: row.resetsAt };
39
+ }
40
+ return result;
41
+ }
42
+ /**
43
+ * Read the account's usage windows.
44
+ * @param options - endpoint, credential, and identity.
45
+ * @returns the three windows, tagged with the identity that produced them.
46
+ * @throws {LlmError} `USAGE_UNAVAILABLE` when the credential is missing, the
47
+ * endpoint fails, or the body is not a usage document.
48
+ */
49
+ export async function readUsageWindows(options) {
50
+ if (options.apiKey === undefined || options.apiKey.length === 0) {
51
+ throw new LlmError('dshopencodego: no credential is configured for the opencode-go route', 'USAGE_UNAVAILABLE');
52
+ }
53
+ const url = `${options.baseURL.replace(/\/+$/, '')}/usage`;
54
+ const timeout = AbortSignal.timeout(USAGE_TIMEOUT_MS);
55
+ let response;
56
+ try {
57
+ response = await fetch(url, {
58
+ redirect: 'error',
59
+ headers: {
60
+ ...attributionHeaders(),
61
+ accept: 'application/json',
62
+ authorization: `Bearer ${options.apiKey}`,
63
+ },
64
+ signal: options.signal === undefined ? timeout : AbortSignal.any([options.signal, timeout]),
65
+ });
66
+ }
67
+ catch (error) {
68
+ throw new LlmError(`could not reach ${url}`, 'USAGE_UNAVAILABLE', { cause: error });
69
+ }
70
+ if (!response.ok) {
71
+ await response.body?.cancel().catch(() => { });
72
+ throw new LlmError(`${url} answered HTTP ${response.status}`, 'USAGE_UNAVAILABLE');
73
+ }
74
+ const body = await readBoundedJson(response, url, USAGE_MAX_BYTES);
75
+ try {
76
+ return {
77
+ ...parseGoUsage(body?.usage),
78
+ ...options.source === undefined ? {} : { source: options.source },
79
+ };
80
+ }
81
+ catch (error) {
82
+ throw new LlmError(`${url} returned an invalid usage document`, 'USAGE_UNAVAILABLE', { cause: error });
83
+ }
84
+ }
package/package.json ADDED
@@ -0,0 +1,147 @@
1
+ {
2
+ "name": "@dan-ai-studio/dshopencodego",
3
+ "version": "0.1.5",
4
+ "description": "OpenCode Go provider for DeepSeek Harness: live gateway catalog, per-conversation session header, usage",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/dan-ai-studio/dshopencodego.git"
10
+ },
11
+ "homepage": "https://github.com/dan-ai-studio/dshopencodego#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/dan-ai-studio/dshopencodego/issues"
14
+ },
15
+ "engines": {
16
+ "node": "^22.19.0 || >=24.0.0",
17
+ "dsh": ">=0.1.7-alpha.1 <0.1.8"
18
+ },
19
+ "main": "./lib/index.js",
20
+ "types": "./lib/types/index.d.ts",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./lib/types/index.d.ts",
24
+ "default": "./lib/index.js"
25
+ },
26
+ "./client": {
27
+ "types": "./lib/types/client/index.d.ts",
28
+ "default": "./lib/client.js"
29
+ },
30
+ "./package.json": "./package.json"
31
+ },
32
+ "files": [
33
+ "lib",
34
+ "cordis.patch.yml",
35
+ "README.md",
36
+ "README.en.md",
37
+ "LICENSE"
38
+ ],
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "dsh": {
43
+ "bundle": {
44
+ "patch": "./cordis.patch.yml"
45
+ },
46
+ "client": {
47
+ "inject": [
48
+ "@deepseek-ai/dsh-client-locale",
49
+ "@deepseek-ai/dsh-client-ui-settings",
50
+ "@deepseek-ai/dsh-api-remotes",
51
+ "@deepseek-ai/dsh-client-ui-model-selection"
52
+ ],
53
+ "platform": "web"
54
+ }
55
+ },
56
+ "scripts": {
57
+ "build": "node scripts/build.mjs",
58
+ "typecheck": "tsc -p tsconfig.host.json --noEmit && tsc -p tsconfig.client.json --noEmit",
59
+ "test": "vitest run",
60
+ "test:watch": "vitest",
61
+ "prepare": "npm run build",
62
+ "prepack": "npm run build"
63
+ },
64
+ "dependencies": {
65
+ "@deepseek-ai/schemastery": "^3.18.3",
66
+ "@earendil-works/pi-ai": "0.87.1"
67
+ },
68
+ "peerDependencies": {
69
+ "@deepseek-ai/cordis": "4.0.2 || 4.0.3 || 4.0.4",
70
+ "@deepseek-ai/dsh-api-remotes": ">=0.1.7-alpha.1 <0.1.8",
71
+ "@deepseek-ai/dsh-attachment": ">=0.1.7-alpha.1 <0.1.8",
72
+ "@deepseek-ai/dsh-brand": ">=0.1.7-alpha.1 <0.1.8",
73
+ "@deepseek-ai/dsh-client-locale": ">=0.1.7-alpha.1 <0.1.8",
74
+ "@deepseek-ai/dsh-client-store": ">=0.1.7-alpha.1 <0.1.8",
75
+ "@deepseek-ai/dsh-client-ui-model-selection": ">=0.1.7-alpha.1 <0.1.8",
76
+ "@deepseek-ai/dsh-client-ui-settings": ">=0.1.7-alpha.1 <0.1.8",
77
+ "@deepseek-ai/dsh-client-ui-slots": ">=0.1.7-alpha.1 <0.1.8",
78
+ "@deepseek-ai/dsh-credentials": ">=0.1.7-alpha.1 <0.1.8",
79
+ "@deepseek-ai/dsh-fs": ">=0.1.7-alpha.1 <0.1.8",
80
+ "@deepseek-ai/dsh-launch-environment": ">=0.1.7-alpha.1 <0.1.8",
81
+ "@deepseek-ai/dsh-llm": ">=0.1.7-alpha.1 <0.1.8",
82
+ "@deepseek-ai/dsh-settings": ">=0.1.7-alpha.1 <0.1.8",
83
+ "@deepseek-ai/dsh-timeout": ">=0.1.7-alpha.1 <0.1.8",
84
+ "@deepseek-ai/dsh-typert-protocol": ">=0.1.7-alpha.1 <0.1.8"
85
+ },
86
+ "peerDependenciesMeta": {
87
+ "@deepseek-ai/dsh-api-remotes": {
88
+ "optional": true
89
+ },
90
+ "@deepseek-ai/dsh-client-locale": {
91
+ "optional": true
92
+ },
93
+ "@deepseek-ai/dsh-client-store": {
94
+ "optional": true
95
+ },
96
+ "@deepseek-ai/dsh-client-ui-model-selection": {
97
+ "optional": true
98
+ },
99
+ "@deepseek-ai/dsh-client-ui-settings": {
100
+ "optional": true
101
+ },
102
+ "@deepseek-ai/dsh-client-ui-slots": {
103
+ "optional": true
104
+ }
105
+ },
106
+ "devDependencies": {
107
+ "@deepseek-ai/cordis": "4.0.4",
108
+ "@deepseek-ai/dsh-api-remotes": "0.1.7-rc.1",
109
+ "@deepseek-ai/dsh-attachment": "0.1.7-rc.1",
110
+ "@deepseek-ai/dsh-brand": "0.1.7-rc.1",
111
+ "@deepseek-ai/dsh-client-locale": "0.1.7-rc.1",
112
+ "@deepseek-ai/dsh-client-store": "0.1.7-rc.1",
113
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.7-rc.1",
114
+ "@deepseek-ai/dsh-client-ui-model-selection": "0.1.7-rc.1",
115
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-rc.1",
116
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.1",
117
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.1",
118
+ "@deepseek-ai/dsh-credentials": "0.1.7-rc.1",
119
+ "@deepseek-ai/dsh-fs": "0.1.7-rc.1",
120
+ "@deepseek-ai/dsh-launch-environment": "0.1.7-rc.1",
121
+ "@deepseek-ai/dsh-llm": "0.1.7-rc.1",
122
+ "@deepseek-ai/dsh-settings": "0.1.7-rc.1",
123
+ "@deepseek-ai/dsh-timeout": "0.1.7-rc.1",
124
+ "@deepseek-ai/dsh-typert-protocol": "0.1.7-rc.1",
125
+ "@deepseek-ai/dsh-typert-registry": "^0.1.7-rc.1",
126
+ "@testing-library/dom": "^10.4.1",
127
+ "@testing-library/react": "^16.3.0",
128
+ "@types/node": "^22.20.0",
129
+ "@types/react": "~18.3.1",
130
+ "@types/react-dom": "~18.3.0",
131
+ "esbuild": "^0.28.2",
132
+ "jsdom": "^29.0.0",
133
+ "lightningcss": "^1.32.0",
134
+ "react": "^18.3.1",
135
+ "react-dom": "^18.3.1",
136
+ "typescript": "^6.0.3",
137
+ "vitest": "^4.1.8"
138
+ },
139
+ "keywords": [
140
+ "deepseek-harness",
141
+ "dsh-plugin",
142
+ "opencode",
143
+ "opencode-go",
144
+ "llm",
145
+ "cordis"
146
+ ]
147
+ }