@askdkc/dsh-cli 0.12.2 → 0.13.0

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 (161) hide show
  1. package/README.md +6 -1
  2. package/cordis.patch.yml +12 -131
  3. package/lib/types/adapter/channel/session-projection.d.ts.map +1 -1
  4. package/lib/types/adapter/channel/session-projection.js +0 -7
  5. package/lib/types/adapter/ports/channel-view.d.ts +1 -1
  6. package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
  7. package/lib/types/dsh-adapter/bundled-presets.d.ts +3 -8
  8. package/lib/types/dsh-adapter/bundled-presets.d.ts.map +1 -1
  9. package/lib/types/dsh-adapter/bundled-presets.js +5 -20
  10. package/lib/types/dsh-adapter/channel/binding-events.d.ts.map +1 -1
  11. package/lib/types/dsh-adapter/channel/binding-events.js +2 -9
  12. package/lib/types/dsh-adapter/channel/external-commands.d.ts.map +1 -1
  13. package/lib/types/dsh-adapter/channel/external-commands.js +9 -33
  14. package/lib/types/dsh-adapter/channel/local-actions.d.ts +2 -1
  15. package/lib/types/dsh-adapter/channel/local-actions.d.ts.map +1 -1
  16. package/lib/types/dsh-adapter/channel/local-actions.js +6 -2
  17. package/lib/types/dsh-adapter/channel/model-actions.js +3 -3
  18. package/lib/types/dsh-adapter/channel/model-switch.d.ts.map +1 -1
  19. package/lib/types/dsh-adapter/channel/model-switch.js +0 -1
  20. package/lib/types/dsh-adapter/channel/permissions.d.ts +1 -6
  21. package/lib/types/dsh-adapter/channel/permissions.d.ts.map +1 -1
  22. package/lib/types/dsh-adapter/channel/permissions.js +2 -54
  23. package/lib/types/dsh-adapter/channel/projection.d.ts.map +1 -1
  24. package/lib/types/dsh-adapter/channel/projection.js +15 -70
  25. package/lib/types/dsh-adapter/channel/session-fork.d.ts.map +1 -1
  26. package/lib/types/dsh-adapter/channel/session-fork.js +0 -1
  27. package/lib/types/dsh-adapter/channel/session-metadata.d.ts.map +1 -1
  28. package/lib/types/dsh-adapter/channel/session-metadata.js +1 -4
  29. package/lib/types/dsh-adapter/channel/session-resume.js +2 -2
  30. package/lib/types/dsh-adapter/channel/session-rewind.d.ts.map +1 -1
  31. package/lib/types/dsh-adapter/channel/session-rewind.js +0 -1
  32. package/lib/types/dsh-adapter/channel/session-tree-actions.d.ts.map +1 -1
  33. package/lib/types/dsh-adapter/channel/session-tree-actions.js +1 -2
  34. package/lib/types/dsh-adapter/channel/session-tree.d.ts.map +1 -1
  35. package/lib/types/dsh-adapter/channel/session-tree.js +9 -62
  36. package/lib/types/dsh-adapter/channel/skill-catalog.js +1 -1
  37. package/lib/types/dsh-adapter/channel/subagent-projection.d.ts.map +1 -1
  38. package/lib/types/dsh-adapter/channel/subagent-projection.js +2 -8
  39. package/lib/types/dsh-adapter/channel/transcript.d.ts +0 -16
  40. package/lib/types/dsh-adapter/channel/transcript.d.ts.map +1 -1
  41. package/lib/types/dsh-adapter/channel/transcript.js +0 -43
  42. package/lib/types/dsh-adapter/channel.js +2 -2
  43. package/lib/types/dsh-adapter/compat/index.d.ts +5 -25
  44. package/lib/types/dsh-adapter/compat/index.d.ts.map +1 -1
  45. package/lib/types/dsh-adapter/compat/index.js +5 -25
  46. package/lib/types/dsh-adapter/compat/liveSession.d.ts +12 -23
  47. package/lib/types/dsh-adapter/compat/liveSession.d.ts.map +1 -1
  48. package/lib/types/dsh-adapter/compat/liveSession.js +15 -68
  49. package/lib/types/dsh-adapter/compat/messages.d.ts +1 -3
  50. package/lib/types/dsh-adapter/compat/messages.d.ts.map +1 -1
  51. package/lib/types/dsh-adapter/compat/messages.js +2 -10
  52. package/lib/types/dsh-adapter/compat/persistence.d.ts +2 -3
  53. package/lib/types/dsh-adapter/compat/persistence.d.ts.map +1 -1
  54. package/lib/types/dsh-adapter/compat/persistence.js +9 -14
  55. package/lib/types/dsh-adapter/compat/sessionLog.d.ts +10 -144
  56. package/lib/types/dsh-adapter/compat/sessionLog.d.ts.map +1 -1
  57. package/lib/types/dsh-adapter/compat/sessionLog.js +45 -275
  58. package/lib/types/dsh-adapter/compat/settings.d.ts +4 -10
  59. package/lib/types/dsh-adapter/compat/settings.d.ts.map +1 -1
  60. package/lib/types/dsh-adapter/compat/settings.js +6 -23
  61. package/lib/types/dsh-adapter/compat/shell.d.ts +2 -3
  62. package/lib/types/dsh-adapter/compat/shell.d.ts.map +1 -1
  63. package/lib/types/dsh-adapter/compat/shell.js +2 -7
  64. package/lib/types/dsh-adapter/contract.d.ts +4 -34
  65. package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
  66. package/lib/types/dsh-adapter/contract.js +8 -83
  67. package/lib/types/dsh-adapter/packaged-presets.d.ts +0 -19
  68. package/lib/types/dsh-adapter/packaged-presets.d.ts.map +1 -1
  69. package/lib/types/dsh-adapter/packaged-presets.js +1 -83
  70. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  71. package/lib/types/dsh-adapter/plugin.js +25 -168
  72. package/lib/types/dsh-adapter/preset-resolution.d.ts +1 -22
  73. package/lib/types/dsh-adapter/preset-resolution.d.ts.map +1 -1
  74. package/lib/types/dsh-adapter/preset-resolution.js +0 -34
  75. package/lib/types/dsh-adapter/presets.d.ts +1 -4
  76. package/lib/types/dsh-adapter/presets.d.ts.map +1 -1
  77. package/lib/types/dsh-adapter/presets.js +3 -6
  78. package/lib/types/dsh-adapter/questions-answerer.d.ts +5 -43
  79. package/lib/types/dsh-adapter/questions-answerer.d.ts.map +1 -1
  80. package/lib/types/dsh-adapter/questions-answerer.js +7 -63
  81. package/lib/types/dsh-adapter/sessionTree.d.ts +0 -19
  82. package/lib/types/dsh-adapter/sessionTree.d.ts.map +1 -1
  83. package/lib/types/dsh-adapter/sessionTree.js +2 -112
  84. package/lib/types/dsh-adapter/sessions/header.d.ts +2 -2
  85. package/lib/types/dsh-adapter/sessions/header.d.ts.map +1 -1
  86. package/lib/types/dsh-adapter/sessions/header.js +2 -8
  87. package/lib/types/dsh-adapter/sessions/list.d.ts +7 -28
  88. package/lib/types/dsh-adapter/sessions/list.d.ts.map +1 -1
  89. package/lib/types/dsh-adapter/sessions/list.js +48 -103
  90. package/lib/types/dsh-adapter/sessions/store.d.ts +1 -2
  91. package/lib/types/dsh-adapter/sessions/store.d.ts.map +1 -1
  92. package/lib/types/dsh-adapter/sessions/store.js +7 -10
  93. package/lib/types/dsh-adapter/sharp.d.ts.map +1 -1
  94. package/lib/types/dsh-adapter/sharp.js +0 -1
  95. package/lib/types/dsh-adapter/subagents.d.ts +1 -2
  96. package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
  97. package/lib/types/dsh-adapter/subagents.js +1 -12
  98. package/lib/types/dsh-adapter/trajectory/aggregate.d.ts +1 -1
  99. package/lib/types/dsh-adapter/trajectory/aggregate.js +1 -1
  100. package/lib/types/dsh-adapter/trajectory/guards.d.ts +5 -22
  101. package/lib/types/dsh-adapter/trajectory/guards.d.ts.map +1 -1
  102. package/lib/types/dsh-adapter/trajectory/guards.js +5 -23
  103. package/lib/types/dsh-adapter/trajectory/projection.d.ts +5 -21
  104. package/lib/types/dsh-adapter/trajectory/projection.d.ts.map +1 -1
  105. package/lib/types/dsh-adapter/trajectory/projection.js +4 -38
  106. package/lib/types/dsh-adapter/trajectory/types.d.ts +1 -1
  107. package/lib/types/dsh-adapter/trajectory/types.d.ts.map +1 -1
  108. package/lib/types/i18n.d.ts +2 -22
  109. package/lib/types/i18n.d.ts.map +1 -1
  110. package/lib/types/i18n.js +2 -10
  111. package/lib/types/presetPrefs.d.ts +1 -3
  112. package/lib/types/presetPrefs.d.ts.map +1 -1
  113. package/lib/types/presetPrefs.js +2 -4
  114. package/node_modules/@askdkc/dsh-auth/README.ja.md +28 -3
  115. package/node_modules/@askdkc/dsh-auth/README.md +19 -3
  116. package/node_modules/@askdkc/dsh-auth/dsh-plugin.json +3 -3
  117. package/node_modules/@askdkc/dsh-auth/lib/THIRD_PARTY_NOTICES.txt +168 -0
  118. package/node_modules/@askdkc/dsh-auth/lib/abortable.d.ts +2 -0
  119. package/node_modules/@askdkc/dsh-auth/lib/abortable.js +9 -0
  120. package/node_modules/@askdkc/dsh-auth/lib/auth-contract.d.ts +66 -0
  121. package/node_modules/@askdkc/dsh-auth/lib/auth-contract.js +1 -0
  122. package/node_modules/@askdkc/dsh-auth/lib/command.js +15 -1
  123. package/node_modules/@askdkc/dsh-auth/lib/credentials.d.ts +8 -14
  124. package/node_modules/@askdkc/dsh-auth/lib/credentials.js +1 -1
  125. package/node_modules/@askdkc/dsh-auth/lib/index.d.ts +8 -27
  126. package/node_modules/@askdkc/dsh-auth/lib/index.js +67 -64
  127. package/node_modules/@askdkc/dsh-auth/lib/interaction.d.ts +5 -5
  128. package/node_modules/@askdkc/dsh-auth/lib/opencode-adapter.d.ts +37 -0
  129. package/node_modules/@askdkc/dsh-auth/lib/opencode-adapter.js +197 -0
  130. package/node_modules/@askdkc/dsh-auth/lib/opencode-catalog.d.ts +72 -0
  131. package/node_modules/@askdkc/dsh-auth/lib/opencode-catalog.js +252 -0
  132. package/node_modules/@askdkc/dsh-auth/lib/opencode-codec.d.ts +6 -0
  133. package/node_modules/@askdkc/dsh-auth/lib/opencode-codec.js +93 -0
  134. package/node_modules/@askdkc/dsh-auth/lib/opencode-owned.generated.d.ts +113 -0
  135. package/node_modules/@askdkc/dsh-auth/lib/opencode-owned.generated.js +2866 -0
  136. package/node_modules/@askdkc/dsh-auth/lib/opencode-replay.d.ts +15 -0
  137. package/node_modules/@askdkc/dsh-auth/lib/opencode-replay.js +141 -0
  138. package/node_modules/@askdkc/dsh-auth/lib/opencode-sdk.d.ts +5 -0
  139. package/node_modules/@askdkc/dsh-auth/lib/opencode-sdk.js +43465 -0
  140. package/node_modules/@askdkc/dsh-auth/lib/opencode-sdk.js.map +7 -0
  141. package/node_modules/@askdkc/dsh-auth/lib/opencode-transport.d.ts +5 -0
  142. package/node_modules/@askdkc/dsh-auth/lib/opencode-transport.js +69 -0
  143. package/node_modules/@askdkc/dsh-auth/lib/pi-ai.d.ts +2 -1
  144. package/node_modules/@askdkc/dsh-auth/lib/pi-ai.js +2 -1
  145. package/node_modules/@askdkc/dsh-auth/lib/pi-routes.d.ts +32 -0
  146. package/node_modules/@askdkc/dsh-auth/lib/pi-routes.js +45 -0
  147. package/node_modules/@askdkc/dsh-auth/lib/profiles.d.ts +2 -20
  148. package/node_modules/@askdkc/dsh-auth/lib/profiles.js +6 -35
  149. package/node_modules/@askdkc/dsh-auth/lib/routes.d.ts +20 -0
  150. package/node_modules/@askdkc/dsh-auth/lib/routes.js +13 -0
  151. package/node_modules/@askdkc/dsh-auth/lib/service.d.ts +11 -2
  152. package/node_modules/@askdkc/dsh-auth/lib/service.js +26 -16
  153. package/node_modules/@askdkc/dsh-auth/package.json +26 -14
  154. package/package.json +100 -96
  155. package/lib/types/dsh-adapter/providerGuard.d.ts +0 -75
  156. package/lib/types/dsh-adapter/providerGuard.d.ts.map +0 -1
  157. package/lib/types/dsh-adapter/providerGuard.js +0 -104
  158. package/lib/types/migrationPrefs.d.ts +0 -60
  159. package/lib/types/migrationPrefs.d.ts.map +0 -1
  160. package/lib/types/migrationPrefs.js +0 -97
  161. package/presets/liangshen/.dsh-tui-managed.json +0 -5
@@ -0,0 +1,5 @@
1
+ /** Protocol SDKs serialize the wire; the owned descriptor controls model settings. */
2
+ import type { GenerateOptions } from '@deepseek-ai/dsh-llm';
3
+ import type { LanguageModelV4, LanguageModelV4Prompt } from '@ai-sdk/provider';
4
+ import { type OpenCodeModel } from './opencode-catalog.js';
5
+ export declare function createTransport(descriptor: OpenCodeModel, options: GenerateOptions, apiKey: string, headers: Record<string, string>, prompt: LanguageModelV4Prompt, fetcher?: typeof fetch): LanguageModelV4;
@@ -0,0 +1,69 @@
1
+ import { createAnthropic, createOpenAI, createGoogleGenerativeAI, createOpenAICompatible } from './opencode-sdk.js';
2
+ import { OPEN_CODE_URLS } from './opencode-catalog.js';
3
+ export function createTransport(descriptor, options, apiKey, headers, prompt, fetcher = fetch) {
4
+ const baseURL = OPEN_CODE_URLS[options.provider];
5
+ if (!baseURL)
6
+ throw new Error('Unknown OpenCode route');
7
+ const fixedFetch = async (input, init) => {
8
+ const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;
9
+ if (!new URL(url).href.startsWith(`${baseURL}/`))
10
+ throw new Error('OpenCode SDK attempted to leave its fixed route');
11
+ // SDK serializers keep their protocol codecs; all model settings come
12
+ // from our descriptor, overriding any SDK id-based limits or enums.
13
+ if (typeof init?.body === 'string') {
14
+ const body = JSON.parse(init.body);
15
+ const effort = descriptor.reasoning ? options.reasoningEffort : undefined;
16
+ const maxTokens = options.maxTokens ?? descriptor.maxTokens;
17
+ const sampling = descriptor.protocol === 'google' ? (body.generationConfig ??= {}) : body;
18
+ if (descriptor.temperature && options.temperature !== undefined)
19
+ sampling.temperature = options.temperature;
20
+ else
21
+ delete sampling.temperature;
22
+ switch (descriptor.protocol) {
23
+ case 'messages':
24
+ body.max_tokens = maxTokens;
25
+ // Effort works independently of thinking. Do not invent an
26
+ // adaptive mode or budget for models advertising only effort.
27
+ // https://platform.claude.com/docs/en/build-with-claude/effort
28
+ if (effort)
29
+ body.output_config = { ...body.output_config, effort };
30
+ break;
31
+ case 'responses': {
32
+ body.store = false;
33
+ const ids = new Map(prompt.flatMap(message => message.role === 'assistant' ? message.content.flatMap(part => part.type === 'tool-call' && typeof part.providerOptions?.openai?.itemId === 'string' ? [[part.toolCallId, part.providerOptions.openai.itemId]] : []) : []));
34
+ for (const item of body.input ?? [])
35
+ if (item.type === 'function_call' && ids.has(item.call_id))
36
+ item.id = ids.get(item.call_id);
37
+ body.max_output_tokens = maxTokens;
38
+ if (descriptor.reasoning) {
39
+ body.include = [...new Set([...(body.include ?? []), 'reasoning.encrypted_content'])];
40
+ if (effort)
41
+ body.reasoning = { effort };
42
+ }
43
+ else
44
+ delete body.reasoning;
45
+ break;
46
+ }
47
+ case 'chat':
48
+ body.max_tokens = maxTokens;
49
+ if (effort)
50
+ body.reasoning_effort = effort;
51
+ break;
52
+ case 'google':
53
+ sampling.maxOutputTokens = maxTokens;
54
+ if (effort)
55
+ sampling.thinkingConfig = { ...sampling.thinkingConfig, thinkingLevel: effort, includeThoughts: true };
56
+ break;
57
+ }
58
+ init = { ...init, body: JSON.stringify(body) };
59
+ }
60
+ return fetcher(input, { ...init, redirect: 'error' });
61
+ };
62
+ const settings = { baseURL, apiKey, headers, fetch: fixedFetch };
63
+ switch (descriptor.protocol) {
64
+ case 'messages': return createAnthropic(settings)(descriptor.id);
65
+ case 'responses': return createOpenAI(settings).responses(descriptor.id);
66
+ case 'google': return createGoogleGenerativeAI(settings)(descriptor.id);
67
+ case 'chat': return createOpenAICompatible({ ...settings, name: 'opencode' })(descriptor.id);
68
+ }
69
+ }
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * The pi-ai instance owned by the installed dsh-llm-pi-ai adapter.
3
3
  *
4
- * dsh-llm-pi-ai rc.2 depends on pi-ai 0.82.x while alpha.1 depends on 0.84.x.
4
+ * dsh-llm-pi-ai 0.2.0-rc.2 declares pi-ai ^0.87.1; the workspace override
5
+ * pins @earendil-works/pi-ai to the latest verified catalog (0.99.1).
5
6
  * Loading a second catalog from dsh-auth and handing its Provider objects to
6
7
  * the adapter crosses package instances and is neither type- nor runtime-safe.
7
8
  * Resolve the adapter's own dependency instead, and derive every public type
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * The pi-ai instance owned by the installed dsh-llm-pi-ai adapter.
3
3
  *
4
- * dsh-llm-pi-ai rc.2 depends on pi-ai 0.82.x while alpha.1 depends on 0.84.x.
4
+ * dsh-llm-pi-ai 0.2.0-rc.2 declares pi-ai ^0.87.1; the workspace override
5
+ * pins @earendil-works/pi-ai to the latest verified catalog (0.99.1).
5
6
  * Loading a second catalog from dsh-auth and handing its Provider objects to
6
7
  * the adapter crosses package instances and is neither type- nor runtime-safe.
7
8
  * Resolve the adapter's own dependency instead, and derive every public type
@@ -0,0 +1,32 @@
1
+ import { PiAiAdapter, type PiAiAdapterOptions } from '@deepseek-ai/dsh-llm-pi-ai';
2
+ import type { LlmModelInfo } from '@deepseek-ai/dsh-llm';
3
+ import type { PiAiAuthContext } from './pi-ai.js';
4
+ export { buildOAuthProfile } from './profiles.js';
5
+ export { createCustomProfile, CUSTOM_PROVIDER_IDS, type CustomProviderId } from './custom-profiles.js';
6
+ /**
7
+ * The ambient auth context providers may consult while resolving their own
8
+ * auth. `env()` answers from the process environment; `fileExists()` answers
9
+ * about the host process's filesystem (the paths a provider asks about —
10
+ * `~/.aws/credentials` and friends — are facts about where this process
11
+ * runs, not about the project under edit).
12
+ */
13
+ export declare function hostAuthContext(): PiAiAuthContext;
14
+ /**
15
+ * The adapter the plugin registers: a {@link PiAiAdapter} whose *advisory
16
+ * catalog* is credential-gated. A provider with no stored credential
17
+ * lists no models — its rows never reach any model picker, which is the
18
+ * whole point: picking a model that would only fail with "not signed in"
19
+ * is noise. The gate only shapes `listModels`; `resolveModel` and requests
20
+ * are untouched, so a model id already saved in a session (or named
21
+ * explicitly) keeps resolving exactly as the registry contract promises
22
+ * ("advisory and never changes routing").
23
+ *
24
+ * An expired-but-stored credential still lists: token refresh runs on the
25
+ * next request, and a picker that hid a refreshable provider would look
26
+ * signed-out when it is not.
27
+ */
28
+ export declare class CredentialGatedAdapter extends PiAiAdapter {
29
+ private readonly hasCredential;
30
+ constructor(options: PiAiAdapterOptions, hasCredential: (provider: string) => Promise<boolean>);
31
+ listModels(provider: string): Promise<readonly LlmModelInfo[]>;
32
+ }
@@ -0,0 +1,45 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { PiAiAdapter } from '@deepseek-ai/dsh-llm-pi-ai';
5
+ export { buildOAuthProfile } from './profiles.js';
6
+ export { createCustomProfile, CUSTOM_PROVIDER_IDS } from './custom-profiles.js';
7
+ /**
8
+ * The ambient auth context providers may consult while resolving their own
9
+ * auth. `env()` answers from the process environment; `fileExists()` answers
10
+ * about the host process's filesystem (the paths a provider asks about —
11
+ * `~/.aws/credentials` and friends — are facts about where this process
12
+ * runs, not about the project under edit).
13
+ */
14
+ export function hostAuthContext() {
15
+ return {
16
+ env: async (name) => process.env[name],
17
+ fileExists: path => Promise.resolve(path.startsWith('~/') ? existsSync(join(homedir(), path.slice(2))) : existsSync(path)),
18
+ };
19
+ }
20
+ /**
21
+ * The adapter the plugin registers: a {@link PiAiAdapter} whose *advisory
22
+ * catalog* is credential-gated. A provider with no stored credential
23
+ * lists no models — its rows never reach any model picker, which is the
24
+ * whole point: picking a model that would only fail with "not signed in"
25
+ * is noise. The gate only shapes `listModels`; `resolveModel` and requests
26
+ * are untouched, so a model id already saved in a session (or named
27
+ * explicitly) keeps resolving exactly as the registry contract promises
28
+ * ("advisory and never changes routing").
29
+ *
30
+ * An expired-but-stored credential still lists: token refresh runs on the
31
+ * next request, and a picker that hid a refreshable provider would look
32
+ * signed-out when it is not.
33
+ */
34
+ export class CredentialGatedAdapter extends PiAiAdapter {
35
+ hasCredential;
36
+ constructor(options, hasCredential) {
37
+ super(options);
38
+ this.hasCredential = hasCredential;
39
+ }
40
+ async listModels(provider) {
41
+ if (!(await this.hasCredential(provider)))
42
+ return [];
43
+ return super.listModels(provider);
44
+ }
45
+ }
@@ -11,26 +11,8 @@
11
11
  */
12
12
  import type { ResolvedPiAiProviderProfile } from '@deepseek-ai/dsh-llm-pi-ai';
13
13
  import { type PiAiProvider } from './pi-ai.js';
14
- /** Provider routes this build mounts, in picker order. */
15
- export declare const OAUTH_PROVIDER_IDS: readonly ["openai-codex", "anthropic", "xai"];
16
- export declare const CATALOG_PROVIDER_IDS: readonly ["openai-codex", "anthropic", "xai", "opencode", "opencode-go", "openrouter"];
17
- export declare const AUTH_PROVIDER_IDS: readonly ["openai-codex", "anthropic", "xai", "opencode", "opencode-go", "openrouter", "orcarouter", "nous", "infron"];
18
- export declare const PROVIDER_ALIASES: Readonly<Record<string, string>>;
19
- export declare function canonicalProvider(id: string): string;
20
- /** One routable provider id. */
21
- export type OAuthProviderId = (typeof OAUTH_PROVIDER_IDS)[number];
22
- /**
23
- * One per-model catalog override a deployment may name, keyed by model id.
24
- * Every field is optional; an absent field keeps the installed catalog's
25
- * value, so one model's capacity can be tuned without restating the rest.
26
- */
27
- export interface ModelOverride {
28
- /** Override the installed catalog's context window, in tokens. */
29
- contextWindow?: number;
30
- /** Override the installed catalog's max output tokens. */
31
- maxTokens?: number;
32
- }
33
- /** The OAuth flow object for one mounted route (login/refresh/toAuth live here). */
14
+ export { OAUTH_PROVIDER_IDS, CATALOG_PROVIDER_IDS, AUTH_PROVIDER_IDS, canonicalProvider, type ModelOverride } from './routes.js';
15
+ import { type ModelOverride } from './routes.js';
34
16
  export declare function oauthOf(route: PiAiProvider): NonNullable<PiAiProvider['auth']['oauth']>;
35
17
  /**
36
18
  * Build the resolved profile one route registers under. Mirrors the fields
@@ -9,21 +9,10 @@
9
9
  *
10
10
  * @module dsh-auth/profiles
11
11
  */
12
- import { randomUUID } from 'node:crypto';
13
12
  import { resolveRetryPolicy } from '@deepseek-ai/dsh-llm';
14
13
  import { adapterBuiltinProviders } from './pi-ai.js';
15
- /** Provider routes this build mounts, in picker order. */
16
- export const OAUTH_PROVIDER_IDS = ['openai-codex', 'anthropic', 'xai'];
17
- export const CATALOG_PROVIDER_IDS = [...OAUTH_PROVIDER_IDS, 'opencode', 'opencode-go', 'openrouter'];
18
- export const AUTH_PROVIDER_IDS = [...CATALOG_PROVIDER_IDS, 'orcarouter', 'nous', 'infron'];
19
- export const PROVIDER_ALIASES = {
20
- hermes: 'nous',
21
- 'infron.ai': 'infron',
22
- 'opencode-zen': 'opencode',
23
- };
24
- export function canonicalProvider(id) {
25
- return PROVIDER_ALIASES[id] ?? id;
26
- }
14
+ export { OAUTH_PROVIDER_IDS, CATALOG_PROVIDER_IDS, AUTH_PROVIDER_IDS, canonicalProvider } from './routes.js';
15
+ import { CATALOG_PROVIDER_IDS } from './routes.js';
27
16
  /** llm-pi-ai `DEFAULT_MAX_REQUEST_IMAGE_BYTES` (20 MiB, base64 payload bound). */
28
17
  const MAX_REQUEST_IMAGE_BYTES = 20 * 1024 * 1024;
29
18
  /** llm-pi-ai `DEFAULT_REQUEST_IMAGE_PIXEL_BUDGET` (2048×2048 pixels). */
@@ -41,24 +30,6 @@ function catalogProviderOf(id) {
41
30
  }
42
31
  return found;
43
32
  }
44
- /** Add the per-request routing header that the installed pi-ai catalog omits. */
45
- function withOpenCodeSessionHeader(catalog) {
46
- return {
47
- ...catalog,
48
- streamSimple(model, context, options) {
49
- return catalog.streamSimple(model, context, {
50
- ...options,
51
- headers: {
52
- ...options?.headers,
53
- // Agent turns keep their durable id; direct calls without one still
54
- // need a distinct routing identity instead of a gateway 400.
55
- 'x-opencode-session': options?.sessionId || randomUUID(),
56
- },
57
- });
58
- },
59
- };
60
- }
61
- /** The OAuth flow object for one mounted route (login/refresh/toAuth live here). */
62
33
  export function oauthOf(route) {
63
34
  const oauth = route.auth.oauth;
64
35
  if (oauth === undefined) {
@@ -110,7 +81,9 @@ export function buildOAuthProfile(id, modelOverrides) {
110
81
  throw new Error(`dsh-auth: "${id}" is not a catalog provider this build mounts (${CATALOG_PROVIDER_IDS.join(', ')})`);
111
82
  }
112
83
  const provider = catalogProviderOf(id);
113
- const catalog = withModelOverrides(id === 'opencode' || id === 'opencode-go' ? withOpenCodeSessionHeader(provider) : provider, modelOverrides);
84
+ if (id === 'opencode' || id === 'opencode-go')
85
+ throw new Error('OpenCode uses the native adapter');
86
+ const catalog = withModelOverrides(provider, modelOverrides);
114
87
  return {
115
88
  provider: id,
116
89
  displayName: catalog.name,
@@ -118,9 +91,7 @@ export function buildOAuthProfile(id, modelOverrides) {
118
91
  retryPolicy: resolveRetryPolicy(undefined, `dsh-auth: provider "${id}" retryPolicy`),
119
92
  configuredMaxTokens: new Map(),
120
93
  piProvider: catalog,
121
- // Required since 0.1.5 (per-model pre-request diagnostics); this build
122
- // reports none. Older adapters never read the field, so carrying it is
123
- // harmless on a pre-0.1.5 host.
94
+ // Per-model pre-request diagnostics.
124
95
  modelErrors: new Map(),
125
96
  maxRequestImageBytes: MAX_REQUEST_IMAGE_BYTES,
126
97
  requestImagePixelBudget: REQUEST_IMAGE_PIXEL_BUDGET,
@@ -0,0 +1,20 @@
1
+ /** Provider identities and user-owned capacity overrides; no transport imports. */
2
+ /** Provider routes this build mounts, in picker order. */
3
+ export declare const OAUTH_PROVIDER_IDS: readonly ["openai-codex", "anthropic", "xai"];
4
+ export declare const CATALOG_PROVIDER_IDS: readonly ["openai-codex", "anthropic", "xai", "opencode", "opencode-go", "openrouter"];
5
+ export declare const AUTH_PROVIDER_IDS: readonly ["openai-codex", "anthropic", "xai", "opencode", "opencode-go", "openrouter", "orcarouter", "nous", "infron"];
6
+ export declare const PROVIDER_ALIASES: Readonly<Record<string, string>>;
7
+ export declare function canonicalProvider(id: string): string;
8
+ /** One routable provider id. */
9
+ export type OAuthProviderId = (typeof OAUTH_PROVIDER_IDS)[number];
10
+ /**
11
+ * One per-model catalog override a deployment may name, keyed by model id.
12
+ * Every field is optional; an absent field keeps the installed catalog's
13
+ * value, so one model's capacity can be tuned without restating the rest.
14
+ */
15
+ export interface ModelOverride {
16
+ /** Override the installed catalog's context window, in tokens. */
17
+ contextWindow?: number;
18
+ /** Override the installed catalog's max output tokens. */
19
+ maxTokens?: number;
20
+ }
@@ -0,0 +1,13 @@
1
+ /** Provider identities and user-owned capacity overrides; no transport imports. */
2
+ /** Provider routes this build mounts, in picker order. */
3
+ export const OAUTH_PROVIDER_IDS = ['openai-codex', 'anthropic', 'xai'];
4
+ export const CATALOG_PROVIDER_IDS = [...OAUTH_PROVIDER_IDS, 'opencode', 'opencode-go', 'openrouter'];
5
+ export const AUTH_PROVIDER_IDS = [...CATALOG_PROVIDER_IDS, 'orcarouter', 'nous', 'infron'];
6
+ export const PROVIDER_ALIASES = {
7
+ hermes: 'nous',
8
+ 'infron.ai': 'infron',
9
+ 'opencode-zen': 'opencode',
10
+ };
11
+ export function canonicalProvider(id) {
12
+ return PROVIDER_ALIASES[id] ?? id;
13
+ }
@@ -9,9 +9,15 @@
9
9
  */
10
10
  import { Service } from '@deepseek-ai/cordis';
11
11
  import type { Context } from '@deepseek-ai/cordis';
12
- import type { ResolvedPiAiProviderProfile } from '@deepseek-ai/dsh-llm-pi-ai';
13
12
  import { asStoredCredential, CredentialFile } from './credentials.js';
13
+ import type { OpenCodeCatalog, OpenCodeRoute, OpenCodeCatalogStatus } from './opencode-catalog.js';
14
14
  import { type AskFn } from './interaction.js';
15
+ import type { OAuthFlow } from './auth-contract.js';
16
+ export interface AuthRoute {
17
+ provider: string;
18
+ displayName: string;
19
+ oauth?: OAuthFlow;
20
+ }
15
21
  /** One provider's sign-in state; never carries token material. */
16
22
  export interface DshAuthSignInStatus {
17
23
  provider: string;
@@ -41,6 +47,8 @@ export interface DshAuthLoginResult {
41
47
  export interface DshAuthApi {
42
48
  /** Every mounted provider with masked sign-in state. */
43
49
  providers(): Promise<readonly DshAuthSignInStatus[]>;
50
+ catalogStatus?(provider?: string): readonly OpenCodeCatalogStatus[];
51
+ refreshModels?(provider?: string, signal?: AbortSignal): Promise<readonly OpenCodeCatalogStatus[]>;
44
52
  /**
45
53
  * Run one provider's login. `provider` omitted asks the interactive
46
54
  * surface to choose among providers not currently signed in.
@@ -62,7 +70,8 @@ export declare class DshAuthService extends Service {
62
70
  }
63
71
  /** Everything the api factory needs; all cordis surface is injected, so tests run without a host. */
64
72
  export interface DshAuthApiDeps {
65
- profiles: ReadonlyMap<string, ResolvedPiAiProviderProfile>;
73
+ profiles: ReadonlyMap<string, AuthRoute>;
74
+ catalogs?: ReadonlyMap<OpenCodeRoute, OpenCodeCatalog>;
66
75
  store: CredentialFile;
67
76
  /** The interactive ask surface, resolved per call so mounting order never matters. */
68
77
  resolveAsk: () => AskFn | undefined;
@@ -9,21 +9,14 @@
9
9
  */
10
10
  import { Service } from '@deepseek-ai/cordis';
11
11
  import { asStoredCredential } from './credentials.js';
12
- import { canonicalProvider, oauthOf } from './profiles.js';
12
+ import { canonicalProvider } from './routes.js';
13
13
  import { QuestionBridge } from './interaction.js';
14
14
  import { loginNous } from './nous-oauth.js';
15
- /**
16
- * The constructed catalog provider one mounted route carries. 0.1.5 made
17
- * `ResolvedPiAiProviderProfile.piProvider` optional (a stored route that
18
- * cannot be constructed stays editable without one); this plugin mounts only
19
- * constructible routes, so an absent provider here is a mount defect —
20
- * surfaced loudly rather than as an `undefined` dereference mid-flow.
21
- */
22
- function mountedProvider(profile) {
23
- if (profile.piProvider === undefined) {
24
- throw new Error(`dsh-auth: provider "${profile.provider}" mounted without a constructed catalog provider`);
25
- }
26
- return profile.piProvider;
15
+ function oauthOf(profile) {
16
+ const oauth = profile.oauth;
17
+ if (!oauth)
18
+ throw new Error(`dsh-auth: OAuth flow unavailable for ${profile.provider}`);
19
+ return oauth;
27
20
  }
28
21
  /** Cordis service holder; `api` is set by the plugin's apply. */
29
22
  export class DshAuthService extends Service {
@@ -61,7 +54,7 @@ export function createDshAuthApi(deps) {
61
54
  const described = new Map((await deps.store.describe()).map(row => [row.provider, row]));
62
55
  return [...deps.profiles.entries()].map(([id, profile]) => {
63
56
  const methods = authMethodsOf(id);
64
- const oauth = methods.includes('oauth') ? oauthOf(mountedProvider(profile)) : undefined;
57
+ const oauth = methods.includes('oauth') ? oauthOf(profile) : undefined;
65
58
  const row = described.get(id);
66
59
  return {
67
60
  provider: id,
@@ -120,7 +113,7 @@ export function createDshAuthApi(deps) {
120
113
  throw new Error('dsh-auth: sign-in cancelled');
121
114
  let credential;
122
115
  if (method === 'oauth') {
123
- const oauth = oauthOf(mountedProvider(profile));
116
+ const oauth = oauthOf(profile);
124
117
  credential = asStoredCredential(await oauth.login(bridge));
125
118
  if (credential?.type !== 'oauth')
126
119
  throw new Error(`dsh-auth: the ${oauth.name} flow returned an unusable credential`);
@@ -157,7 +150,7 @@ export function createDshAuthApi(deps) {
157
150
  }
158
151
  changed(provider);
159
152
  const expiresAt = credential.type === 'oauth' && provider !== 'openrouter' ? credential.expires : undefined;
160
- return { provider, oauthLabel: method === 'oauth' ? oauthOf(mountedProvider(profile)).name : profile.displayName, authMethods: methods,
153
+ return { provider, oauthLabel: method === 'oauth' ? oauthOf(profile).name : profile.displayName, authMethods: methods,
161
154
  credentialKind: credential.type === 'api_key' || provider === 'openrouter' ? 'api-key' : 'oauth-token',
162
155
  ...(expiresAt === undefined ? {} : { expiresAt }),
163
156
  ...(modelWarning === undefined ? {} : { modelWarning }) };
@@ -168,6 +161,23 @@ export function createDshAuthApi(deps) {
168
161
  };
169
162
  return {
170
163
  providers: statusOf,
164
+ catalogStatus: provider => [...(deps.catalogs?.values() ?? [])].filter(catalog => provider === undefined || catalog.provider === canonicalProvider(provider)).map(catalog => catalog.status()),
165
+ refreshModels: async (provider, signal) => {
166
+ signal?.throwIfAborted();
167
+ const selected = [...(deps.catalogs?.values() ?? [])].filter(catalog => provider === undefined || catalog.provider === canonicalProvider(provider));
168
+ if (!selected.length)
169
+ throw new Error('No matching OpenCode catalog is mounted');
170
+ const refresh = Promise.all(selected.map(catalog => catalog.refresh()));
171
+ if (!signal)
172
+ return refresh;
173
+ return new Promise((resolve, reject) => {
174
+ const abort = () => reject(new Error('Catalog update wait cancelled; shared background refresh may continue'));
175
+ signal.addEventListener('abort', abort, { once: true });
176
+ void refresh.then(resolve, reject).finally(() => signal.removeEventListener('abort', abort));
177
+ if (signal.aborted)
178
+ abort();
179
+ });
180
+ },
171
181
  login: async (provider, signal) => {
172
182
  const ask = deps.resolveAsk();
173
183
  let target = provider === undefined ? undefined : canonicalProvider(provider);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askdkc/dsh-auth",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Provider authentication and model routes for DeepSeek Harness — no dsh source patch, bundled with dsh-cli",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -42,34 +42,46 @@
42
42
  "node": "^22.19 || >=24"
43
43
  },
44
44
  "scripts": {
45
- "build": "tsc -p tsconfig.json",
45
+ "build": "npm run clean && tsc -p tsconfig.json && node scripts/bundle-opencode.mjs",
46
46
  "clean": "node -e \"require('node:fs').rmSync('lib', { recursive: true, force: true })\"",
47
47
  "smoke": "node scripts/smoke.mjs",
48
- "verify": "npm run build && npm run smoke"
48
+ "sync:models": "node --import tsx/esm scripts/sync-owned-opencode.mjs",
49
+ "verify": "npm run build && npm run smoke && node --test scripts/verify-opencode.mjs && node --loader ./scripts/reject-pi-loader.mjs scripts/verify-opencode-isolated.mjs"
49
50
  },
50
51
  "peerDependencies": {
51
52
  "@deepseek-ai/cordis": "^4.0.1",
52
- "@deepseek-ai/dsh-attachment": "^0.1.0-rc.6 || ^0.1.1-rc.1 || 0.1.2-alpha.3 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.3-alpha.2 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.7-rc.1 || 0.2.0-rc.1 || 0.2.0-rc.2",
53
- "@deepseek-ai/dsh-commands": "^0.1.0-rc.6 || ^0.1.1-rc.1 || 0.1.2-alpha.3 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.3-alpha.2 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.7-rc.1 || 0.2.0-rc.1 || 0.2.0-rc.2",
54
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || ^0.1.1-rc.1 || 0.1.2-alpha.3 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.3-alpha.2 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.7-rc.1 || 0.2.0-rc.1 || 0.2.0-rc.2",
55
- "@deepseek-ai/dsh-llm-pi-ai": "^0.1.0-rc.6 || ^0.1.1-rc.1 || 0.1.2-alpha.3 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.3-alpha.2 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.7-rc.1 || 0.2.0-rc.1 || 0.2.0-rc.2",
56
- "@deepseek-ai/dsh-user-questions": "^0.1.0-rc.6 || ^0.1.1-rc.1 || 0.1.2-alpha.3 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.3-alpha.2 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.7-rc.1 || 0.2.0-rc.1 || 0.2.0-rc.2",
53
+ "@deepseek-ai/dsh-attachment": "0.2.0-rc.2",
54
+ "@deepseek-ai/dsh-commands": "0.2.0-rc.2",
55
+ "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
56
+ "@deepseek-ai/dsh-llm-pi-ai": "0.2.0-rc.2",
57
+ "@deepseek-ai/dsh-user-questions": "0.2.0-rc.2",
57
58
  "@deepseek-ai/schemastery": "^3.18.1"
58
59
  },
59
60
  "peerDependenciesMeta": {
60
61
  "@deepseek-ai/dsh-attachment": {
61
62
  "optional": true
63
+ },
64
+ "@deepseek-ai/dsh-llm-pi-ai": {
65
+ "optional": true
62
66
  }
63
67
  },
64
68
  "devDependencies": {
65
69
  "@deepseek-ai/cordis": "^4.0.1",
66
- "@deepseek-ai/dsh-attachment": "^0.1.0-rc.6 || ^0.1.1-rc.1 || 0.1.2-alpha.3 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.3-alpha.2 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.7-rc.1 || 0.2.0-rc.1 || 0.2.0-rc.2",
67
- "@deepseek-ai/dsh-commands": "^0.1.0-rc.6 || ^0.1.1-rc.1 || 0.1.2-alpha.3 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.3-alpha.2 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.7-rc.1 || 0.2.0-rc.1 || 0.2.0-rc.2",
68
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || ^0.1.1-rc.1 || 0.1.2-alpha.3 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.3-alpha.2 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.7-rc.1 || 0.2.0-rc.1 || 0.2.0-rc.2",
69
- "@deepseek-ai/dsh-llm-pi-ai": "^0.1.0-rc.6 || ^0.1.1-rc.1 || 0.1.2-alpha.3 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.3-alpha.2 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.7-rc.1 || 0.2.0-rc.1 || 0.2.0-rc.2",
70
- "@deepseek-ai/dsh-user-questions": "^0.1.0-rc.6 || ^0.1.1-rc.1 || 0.1.2-alpha.3 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.3-alpha.2 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.7-rc.1 || 0.2.0-rc.1 || 0.2.0-rc.2",
70
+ "@deepseek-ai/dsh-attachment": "0.2.0-rc.2",
71
+ "@deepseek-ai/dsh-commands": "0.2.0-rc.2",
72
+ "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
73
+ "@deepseek-ai/dsh-llm-pi-ai": "0.2.0-rc.2",
74
+ "@deepseek-ai/dsh-user-questions": "0.2.0-rc.2",
71
75
  "@deepseek-ai/schemastery": "^3.18.1",
72
76
  "@types/node": "^22.0.0",
73
- "typescript": "^6.0.3"
77
+ "typescript": "^6.0.3",
78
+ "@ai-sdk/anthropic": "4.0.71",
79
+ "@ai-sdk/google": "4.0.87",
80
+ "@ai-sdk/openai": "4.0.83",
81
+ "@ai-sdk/openai-compatible": "3.0.62",
82
+ "@ai-sdk/provider": "4.0.21",
83
+ "esbuild": "0.28.2",
84
+ "zod": "4.1.8",
85
+ "tsx": "4.23.13"
74
86
  }
75
87
  }