@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
@@ -2,9 +2,9 @@
2
2
  "$schema": "https://dsh.community/schemas/dsh-plugin-0.15.json",
3
3
  "id": "com.dsh-tui.dsh-auth",
4
4
  "name": "dsh-auth",
5
- "version": "0.1.0",
5
+ "version": "0.2.0",
6
6
  "manifestVersion": "0.15",
7
- "description": "Subscription OAuth sign-in (ChatGPT/Codex, Claude Pro/Max, SuperGrok) as LLM provider routes for DeepSeek Harness. Registers llm adapter routes through the cordis llm registry; interactive login runs over the user-questions seam and degrades to a clear refusal when no interactive surface is present.",
7
+ "description": "Provider authentication and OpenCode Zen/Go model routes for DeepSeek Harness. Registers llm adapter routes through the cordis llm registry; interactive login runs over the user-questions seam.",
8
8
  "facets": {
9
9
  "host": {
10
10
  "entry": "lib/index.js",
@@ -29,7 +29,7 @@
29
29
  "commands": [
30
30
  {
31
31
  "id": "dsh.auth",
32
- "title": "Sign in with a provider subscription (OAuth)"
32
+ "title": "Manage provider authentication and models"
33
33
  }
34
34
  ]
35
35
  },
@@ -0,0 +1,168 @@
1
+ @ai-sdk/provider@4.0.21 (Apache-2.0)
2
+ Copyright 2023 Vercel, Inc.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+
16
+ ---
17
+
18
+ zod@4.1.8 (MIT)
19
+ MIT License
20
+
21
+ Copyright (c) 2025 Colin McDonnell
22
+
23
+ Permission is hereby granted, free of charge, to any person obtaining a copy
24
+ of this software and associated documentation files (the "Software"), to deal
25
+ in the Software without restriction, including without limitation the rights
26
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27
+ copies of the Software, and to permit persons to whom the Software is
28
+ furnished to do so, subject to the following conditions:
29
+
30
+ The above copyright notice and this permission notice shall be included in all
31
+ copies or substantial portions of the Software.
32
+
33
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39
+ SOFTWARE.
40
+
41
+
42
+ ---
43
+
44
+ eventsource-parser@3.1.1 (MIT)
45
+ MIT License
46
+
47
+ Copyright (c) 2026 Espen Hovlandsdal <espen@hovlandsdal.com>
48
+
49
+ Permission is hereby granted, free of charge, to any person obtaining a copy
50
+ of this software and associated documentation files (the "Software"), to deal
51
+ in the Software without restriction, including without limitation the rights
52
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
53
+ copies of the Software, and to permit persons to whom the Software is
54
+ furnished to do so, subject to the following conditions:
55
+
56
+ The above copyright notice and this permission notice shall be included in all
57
+ copies or substantial portions of the Software.
58
+
59
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
60
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
61
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
62
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
63
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
64
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
65
+ SOFTWARE.
66
+
67
+
68
+ ---
69
+
70
+ @workflow/serde@4.1.0 (Apache-2.0)
71
+ Copyright 2023 Vercel, Inc.
72
+
73
+ Licensed under the Apache License, Version 2.0 (the "License");
74
+ you may not use this file except in compliance with the License.
75
+ You may obtain a copy of the License at
76
+
77
+ http://www.apache.org/licenses/LICENSE-2.0
78
+
79
+ Unless required by applicable law or agreed to in writing, software
80
+ distributed under the License is distributed on an "AS IS" BASIS,
81
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
82
+ See the License for the specific language governing permissions and
83
+ limitations under the License.
84
+
85
+ ---
86
+
87
+ @ai-sdk/provider-utils@5.0.53 (Apache-2.0)
88
+ Copyright 2023 Vercel, Inc.
89
+
90
+ Licensed under the Apache License, Version 2.0 (the "License");
91
+ you may not use this file except in compliance with the License.
92
+ You may obtain a copy of the License at
93
+
94
+ http://www.apache.org/licenses/LICENSE-2.0
95
+
96
+ Unless required by applicable law or agreed to in writing, software
97
+ distributed under the License is distributed on an "AS IS" BASIS,
98
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
99
+ See the License for the specific language governing permissions and
100
+ limitations under the License.
101
+
102
+ ---
103
+
104
+ @ai-sdk/anthropic@4.0.71 (Apache-2.0)
105
+ Copyright 2023 Vercel, Inc.
106
+
107
+ Licensed under the Apache License, Version 2.0 (the "License");
108
+ you may not use this file except in compliance with the License.
109
+ You may obtain a copy of the License at
110
+
111
+ http://www.apache.org/licenses/LICENSE-2.0
112
+
113
+ Unless required by applicable law or agreed to in writing, software
114
+ distributed under the License is distributed on an "AS IS" BASIS,
115
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
116
+ See the License for the specific language governing permissions and
117
+ limitations under the License.
118
+
119
+ ---
120
+
121
+ @ai-sdk/openai@4.0.83 (Apache-2.0)
122
+ Copyright 2023 Vercel, Inc.
123
+
124
+ Licensed under the Apache License, Version 2.0 (the "License");
125
+ you may not use this file except in compliance with the License.
126
+ You may obtain a copy of the License at
127
+
128
+ http://www.apache.org/licenses/LICENSE-2.0
129
+
130
+ Unless required by applicable law or agreed to in writing, software
131
+ distributed under the License is distributed on an "AS IS" BASIS,
132
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133
+ See the License for the specific language governing permissions and
134
+ limitations under the License.
135
+
136
+ ---
137
+
138
+ @ai-sdk/openai-compatible@3.0.62 (Apache-2.0)
139
+ Copyright 2023 Vercel, Inc.
140
+
141
+ Licensed under the Apache License, Version 2.0 (the "License");
142
+ you may not use this file except in compliance with the License.
143
+ You may obtain a copy of the License at
144
+
145
+ http://www.apache.org/licenses/LICENSE-2.0
146
+
147
+ Unless required by applicable law or agreed to in writing, software
148
+ distributed under the License is distributed on an "AS IS" BASIS,
149
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
150
+ See the License for the specific language governing permissions and
151
+ limitations under the License.
152
+
153
+ ---
154
+
155
+ @ai-sdk/google@4.0.87 (Apache-2.0)
156
+ Copyright 2023 Vercel, Inc.
157
+
158
+ Licensed under the Apache License, Version 2.0 (the "License");
159
+ you may not use this file except in compliance with the License.
160
+ You may obtain a copy of the License at
161
+
162
+ http://www.apache.org/licenses/LICENSE-2.0
163
+
164
+ Unless required by applicable law or agreed to in writing, software
165
+ distributed under the License is distributed on an "AS IS" BASIS,
166
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
167
+ See the License for the specific language governing permissions and
168
+ limitations under the License.
@@ -0,0 +1,2 @@
1
+ /** Bound async I/O even when a reader or fixture does not honor fetch cancellation. */
2
+ export declare function abortable<T>(operation: PromiseLike<T>, signal: AbortSignal): Promise<T>;
@@ -0,0 +1,9 @@
1
+ /** Bound async I/O even when a reader or fixture does not honor fetch cancellation. */
2
+ export function abortable(operation, signal) {
3
+ signal.throwIfAborted();
4
+ return new Promise((resolve, reject) => {
5
+ const abort = () => reject(signal.reason);
6
+ signal.addEventListener('abort', abort, { once: true });
7
+ Promise.resolve(operation).then(resolve, reject).finally(() => signal.removeEventListener('abort', abort));
8
+ });
9
+ }
@@ -0,0 +1,66 @@
1
+ /** Auth-owned persistence and interaction contracts; pi routes adapt structurally. */
2
+ export interface StoredOAuthCredential {
3
+ type: 'oauth';
4
+ access: string;
5
+ refresh: string;
6
+ expires: number;
7
+ [key: string]: unknown;
8
+ }
9
+ export interface StoredApiKeyCredential {
10
+ type: 'api_key';
11
+ key?: string;
12
+ env?: Record<string, string>;
13
+ }
14
+ export type StoredCredential = StoredOAuthCredential | StoredApiKeyCredential;
15
+ export interface CredentialInfo {
16
+ providerId: string;
17
+ type: StoredCredential['type'];
18
+ }
19
+ export interface AuthInteraction {
20
+ signal: AbortSignal;
21
+ prompt(prompt: AuthPrompt): Promise<string>;
22
+ notify(event: AuthEvent): void;
23
+ }
24
+ export type AuthPrompt = {
25
+ signal?: AbortSignal;
26
+ } & ({
27
+ type: 'text' | 'secret' | 'manual_code';
28
+ message: string;
29
+ placeholder?: string;
30
+ } | {
31
+ type: 'select';
32
+ message: string;
33
+ options: readonly {
34
+ id: string;
35
+ label: string;
36
+ description?: string;
37
+ }[];
38
+ });
39
+ export type AuthEvent = {
40
+ type: 'info';
41
+ message: string;
42
+ links?: readonly {
43
+ url: string;
44
+ label?: string;
45
+ }[];
46
+ } | {
47
+ type: 'auth_url';
48
+ url: string;
49
+ instructions?: string;
50
+ } | {
51
+ type: 'device_code';
52
+ userCode: string;
53
+ verificationUri: string;
54
+ intervalSeconds?: number;
55
+ expiresInSeconds?: number;
56
+ } | {
57
+ type: 'progress';
58
+ message: string;
59
+ };
60
+ export interface OAuthFlow {
61
+ name: string;
62
+ loginLabel?: string;
63
+ login(interaction: AuthInteraction, options?: {
64
+ getDeviceId?: () => string;
65
+ }): Promise<StoredOAuthCredential>;
66
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * @module dsh-auth/command
6
6
  */
7
- const USAGE = 'Usage: /auth [status] | /auth login [provider] | /auth logout <provider>';
7
+ const USAGE = 'Usage: /auth [status] | /auth login [provider] | /auth logout <provider> | /auth models [provider] | /auth refresh [provider]';
8
8
  function renderStatus(rows) {
9
9
  const lines = rows.map(row => {
10
10
  const state = row.signedIn
@@ -33,6 +33,20 @@ export function createAuthCommandHandler(api) {
33
33
  const { verb, target, extra } = parseArgs(invocation.rawInput);
34
34
  if (extra)
35
35
  return { kind: 'error', text: USAGE };
36
+ if (verb === 'models' || verb === 'refresh') {
37
+ try {
38
+ if (!api.catalogStatus || !api.refreshModels)
39
+ throw new Error('Model catalog operations are unavailable');
40
+ const rows = verb === 'refresh' ? await api.refreshModels(target, invocation.signal) : api.catalogStatus(target);
41
+ if (!rows.length)
42
+ throw new Error('No matching OpenCode catalog is mounted');
43
+ return { kind: rows.some(row => row.warning) && verb === 'refresh' ? 'error' : 'success', text: rows.map(row => `${row.provider}: ${row.available} models (${row.source}); last verified ${new Date(row.fetchedAt).toISOString()}${row.warning ? `; ${row.warning}` : ''}`
44
+ + row.excluded.map(entry => `\n ${entry.id}: ${entry.reason}`).join('')).join('\n') };
45
+ }
46
+ catch (error) {
47
+ return { kind: 'error', text: error instanceof Error ? error.message : 'Model catalog operation failed' };
48
+ }
49
+ }
36
50
  if (verb === undefined || verb === 'status') {
37
51
  if (target !== undefined)
38
52
  return { kind: 'error', text: USAGE };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * File-backed credential persistence: a pi-ai `CredentialStore` over
2
+ * Auth-owned credential persistence, structurally compatible with pi stores, over
3
3
  * one JSON document, one credential per provider id.
4
4
  *
5
5
  * Writes are atomic (temp file + rename) with 0700 directory / 0600 file
@@ -13,16 +13,10 @@
13
13
  *
14
14
  * @module dsh-auth/credentials
15
15
  */
16
- import type { PiAiCredential, PiAiCredentialInfo, PiAiCredentialStore } from './pi-ai.js';
17
- /** The stored credential shapes from pi-ai. */
18
- export type StoredOAuthCredential = Extract<PiAiCredential, {
19
- type: 'oauth';
20
- }>;
21
- export type StoredApiKeyCredential = Extract<PiAiCredential, {
22
- type: 'api_key';
23
- }>;
16
+ import type { StoredCredential, CredentialInfo } from './auth-contract.js';
17
+ export type { StoredOAuthCredential, StoredApiKeyCredential } from './auth-contract.js';
24
18
  /** Narrow an unknown parsed value into a stored credential, or reject it. */
25
- export declare function asStoredCredential(value: unknown): PiAiCredential | undefined;
19
+ export declare function asStoredCredential(value: unknown): StoredCredential | undefined;
26
20
  /** Default credential file location: `$DSH_HOME/dsh-auth/credentials.json` (or `~/.dsh/…`). */
27
21
  export declare function defaultCredentialsFile(): string;
28
22
  /**
@@ -31,22 +25,22 @@ export declare function defaultCredentialsFile(): string;
31
25
  * credential file as "signed out everywhere" would strand every route behind
32
26
  * a fresh login for no reason.
33
27
  */
34
- export declare class CredentialFile implements PiAiCredentialStore {
28
+ export declare class CredentialFile {
35
29
  readonly path: string;
36
30
  /** Serialize all updates in this process; the file lock covers other processes. */
37
31
  private chainTail;
38
32
  constructor(path: string);
39
33
  /** The stored credential for one provider, possibly expired. */
40
- read(providerId: string): Promise<PiAiCredential | undefined>;
34
+ read(providerId: string): Promise<StoredCredential | undefined>;
41
35
  /** Stored credential metadata without resolving or exposing secrets. */
42
- list(): Promise<readonly PiAiCredentialInfo[]>;
36
+ list(): Promise<readonly CredentialInfo[]>;
43
37
  /**
44
38
  * Serialized read-modify-write for one provider. `fn` sees the current
45
39
  * credential; returning a new credential persists it, returning
46
40
  * `undefined` leaves the entry unchanged. Resolves with the post-write
47
41
  * credential. Rejections from `fn` propagate without touching the file.
48
42
  */
49
- modify(providerId: string, fn: (current: PiAiCredential | undefined) => Promise<PiAiCredential | undefined>): Promise<PiAiCredential | undefined>;
43
+ modify(providerId: string, fn: (current: StoredCredential | undefined) => Promise<StoredCredential | undefined>): Promise<StoredCredential | undefined>;
50
44
  /** Remove one provider's credential (logout). */
51
45
  delete(providerId: string): Promise<void>;
52
46
  /** Non-secret metadata for every stored credential, for status surfaces. */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * File-backed credential persistence: a pi-ai `CredentialStore` over
2
+ * Auth-owned credential persistence, structurally compatible with pi stores, over
3
3
  * one JSON document, one credential per provider id.
4
4
  *
5
5
  * Writes are atomic (temp file + rename) with 0700 directory / 0600 file
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * dsh-auth — provider sign-in as LLM provider routes.
3
3
  *
4
- * One cordis plugin mounts pi-ai catalog and custom provider profiles as
4
+ * One cordis plugin mounts owned OpenCode routes and legacy pi profiles as
5
5
  * `llm` registry routes, so
6
- * catalog models appear in every model picker after sign-in. `PiAiAdapter` runs with this
6
+ * catalog models appear in every model picker after sign-in. OpenCode owns
7
+ * its public catalog and bundled SDK transports. Other routes use `PiAiAdapter` with this
7
8
  * plugin's file-backed pi-ai `CredentialStore` injected
8
9
  * (`PiAiAuthInjection`): requests resolve stored credentials through the
9
10
  * provider's own auth or an explicit API key. Refreshes hold the store lock.
@@ -33,10 +34,7 @@
33
34
  */
34
35
  import type { Context } from '@deepseek-ai/cordis';
35
36
  import z from '@deepseek-ai/schemastery';
36
- import type { LlmModelInfo } from '@deepseek-ai/dsh-llm';
37
- import { PiAiAdapter } from '@deepseek-ai/dsh-llm-pi-ai';
38
- import type { PiAiAdapterOptions } from '@deepseek-ai/dsh-llm-pi-ai';
39
- import { type ModelOverride } from './profiles.js';
37
+ import { type ModelOverride } from './routes.js';
40
38
  export declare const name = "dsh-auth";
41
39
  /**
42
40
  * Deliberately empty. A hard code-level inject would deadlock any
@@ -72,25 +70,8 @@ export { QuestionBridge, describeEvent } from './interaction.js';
72
70
  export type { AskFn, QuestionBridgeHelpers } from './interaction.js';
73
71
  export { copyToClipboard, openInBrowser, openerFor } from './opener.js';
74
72
  export { CredentialFile, defaultCredentialsFile } from './credentials.js';
75
- export { OAUTH_PROVIDER_IDS, CATALOG_PROVIDER_IDS, AUTH_PROVIDER_IDS, canonicalProvider, buildOAuthProfile, type ModelOverride } from './profiles.js';
76
- /**
77
- * The adapter the plugin registers: a {@link PiAiAdapter} whose *advisory
78
- * catalog* is credential-gated. A provider with no stored credential
79
- * lists no models — its rows never reach any model picker, which is the
80
- * whole point: picking a model that would only fail with "not signed in"
81
- * is noise. The gate only shapes `listModels`; `resolveModel` and requests
82
- * are untouched, so a model id already saved in a session (or named
83
- * explicitly) keeps resolving exactly as the registry contract promises
84
- * ("advisory and never changes routing").
85
- *
86
- * An expired-but-stored credential still lists: token refresh runs on the
87
- * next request, and a picker that hid a refreshable provider would look
88
- * signed-out when it is not.
89
- */
90
- export declare class CredentialGatedAdapter extends PiAiAdapter {
91
- private readonly hasCredential;
92
- constructor(options: PiAiAdapterOptions, hasCredential: (provider: string) => Promise<boolean>);
93
- listModels(provider: string): Promise<readonly LlmModelInfo[]>;
94
- }
73
+ export { OAUTH_PROVIDER_IDS, CATALOG_PROVIDER_IDS, AUTH_PROVIDER_IDS, canonicalProvider, type ModelOverride } from './routes.js';
74
+ export { OpenCodeAdapter } from './opencode-adapter.js';
75
+ export { OpenCodeCatalog } from './opencode-catalog.js';
95
76
  /** Mount the routes, the service, and the command. */
96
- export declare function apply(ctx: Context, config: Config): void;
77
+ export declare function apply(ctx: Context, config: Config): Promise<void>;
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * dsh-auth — provider sign-in as LLM provider routes.
3
3
  *
4
- * One cordis plugin mounts pi-ai catalog and custom provider profiles as
4
+ * One cordis plugin mounts owned OpenCode routes and legacy pi profiles as
5
5
  * `llm` registry routes, so
6
- * catalog models appear in every model picker after sign-in. `PiAiAdapter` runs with this
6
+ * catalog models appear in every model picker after sign-in. OpenCode owns
7
+ * its public catalog and bundled SDK transports. Other routes use `PiAiAdapter` with this
7
8
  * plugin's file-backed pi-ai `CredentialStore` injected
8
9
  * (`PiAiAuthInjection`): requests resolve stored credentials through the
9
10
  * provider's own auth or an explicit API key. Refreshes hold the store lock.
@@ -31,14 +32,12 @@
31
32
  *
32
33
  * @module dsh-auth
33
34
  */
34
- import { existsSync } from 'node:fs';
35
- import { homedir } from 'node:os';
36
- import { join } from 'node:path';
37
35
  import z from '@deepseek-ai/schemastery';
38
- import { PiAiAdapter } from '@deepseek-ai/dsh-llm-pi-ai';
39
36
  import { CredentialFile, defaultCredentialsFile } from './credentials.js';
40
- import { buildOAuthProfile, AUTH_PROVIDER_IDS, canonicalProvider } from './profiles.js';
41
- import { createCustomProfile, CUSTOM_PROVIDER_IDS } from './custom-profiles.js';
37
+ import { AUTH_PROVIDER_IDS, canonicalProvider } from './routes.js';
38
+ import { OpenCodeAdapter } from './opencode-adapter.js';
39
+ import { OpenCodeCatalog, OPEN_CODE_ROUTES } from './opencode-catalog.js';
40
+ import { OPEN_CODE_SNAPSHOTS } from './opencode-owned.generated.js';
42
41
  import { refreshNous } from './nous-oauth.js';
43
42
  import { createDshAuthApi, DshAuthService } from './service.js';
44
43
  import { createAuthCommandHandler } from './command.js';
@@ -67,48 +66,11 @@ export { createDshAuthApi } from './service.js';
67
66
  export { QuestionBridge, describeEvent } from './interaction.js';
68
67
  export { copyToClipboard, openInBrowser, openerFor } from './opener.js';
69
68
  export { CredentialFile, defaultCredentialsFile } from './credentials.js';
70
- export { OAUTH_PROVIDER_IDS, CATALOG_PROVIDER_IDS, AUTH_PROVIDER_IDS, canonicalProvider, buildOAuthProfile } from './profiles.js';
71
- /**
72
- * The ambient auth context providers may consult while resolving their own
73
- * auth. `env()` answers from the process environment; `fileExists()` answers
74
- * about the host process's filesystem (the paths a provider asks about —
75
- * `~/.aws/credentials` and friends — are facts about where this process
76
- * runs, not about the project under edit).
77
- */
78
- function hostAuthContext() {
79
- return {
80
- env: async (name) => process.env[name],
81
- fileExists: path => Promise.resolve(path.startsWith('~/') ? existsSync(join(homedir(), path.slice(2))) : existsSync(path)),
82
- };
83
- }
84
- /**
85
- * The adapter the plugin registers: a {@link PiAiAdapter} whose *advisory
86
- * catalog* is credential-gated. A provider with no stored credential
87
- * lists no models — its rows never reach any model picker, which is the
88
- * whole point: picking a model that would only fail with "not signed in"
89
- * is noise. The gate only shapes `listModels`; `resolveModel` and requests
90
- * are untouched, so a model id already saved in a session (or named
91
- * explicitly) keeps resolving exactly as the registry contract promises
92
- * ("advisory and never changes routing").
93
- *
94
- * An expired-but-stored credential still lists: token refresh runs on the
95
- * next request, and a picker that hid a refreshable provider would look
96
- * signed-out when it is not.
97
- */
98
- export class CredentialGatedAdapter extends PiAiAdapter {
99
- hasCredential;
100
- constructor(options, hasCredential) {
101
- super(options);
102
- this.hasCredential = hasCredential;
103
- }
104
- async listModels(provider) {
105
- if (!(await this.hasCredential(provider)))
106
- return [];
107
- return super.listModels(provider);
108
- }
109
- }
69
+ export { OAUTH_PROVIDER_IDS, CATALOG_PROVIDER_IDS, AUTH_PROVIDER_IDS, canonicalProvider } from './routes.js';
70
+ export { OpenCodeAdapter } from './opencode-adapter.js';
71
+ export { OpenCodeCatalog } from './opencode-catalog.js';
110
72
  /** Mount the routes, the service, and the command. */
111
- export function apply(ctx, config) {
73
+ export async function apply(ctx, config) {
112
74
  const configured = (config.providers ?? [...AUTH_PROVIDER_IDS]).map(canonicalProvider);
113
75
  const unknown = configured.filter(id => !AUTH_PROVIDER_IDS.includes(id));
114
76
  if (unknown.length > 0 || configured.length === 0) {
@@ -122,14 +84,35 @@ export function apply(ctx, config) {
122
84
  if (unknownOverridden.length > 0) {
123
85
  throw new Error(`dsh-auth: modelOverrides names provider "${unknownOverridden[0]}", which is not among the mounted providers [${configured.join(', ')}]`);
124
86
  }
125
- // Profile construction validates the installed catalog loudly: a pi-ai
126
- // downgrade that dropped a provider fails the boot that asked for it, and
127
- // a per-model miss is refused per route (see buildOAuthProfile).
128
- const custom = new Map(CUSTOM_PROVIDER_IDS.filter(id => configured.includes(id))
129
- .map(id => [id, createCustomProfile(id, overrides[id])]));
130
- const profiles = new Map(configured.map(id => [id,
131
- custom.get(id)?.profile ?? buildOAuthProfile(id, overrides[id]),
132
- ]));
87
+ const piIds = configured.filter(id => !OPEN_CODE_ROUTES.includes(id));
88
+ const custom = new Map();
89
+ const piProfiles = new Map();
90
+ const profiles = new Map();
91
+ let pi;
92
+ if (piIds.length) {
93
+ try {
94
+ pi = await import('./pi-routes.js');
95
+ }
96
+ catch {
97
+ ctx.logger.warn('dsh-auth: pi routes unavailable; OpenCode routes remain available');
98
+ }
99
+ if (pi)
100
+ for (const id of piIds) {
101
+ try {
102
+ const entry = pi.CUSTOM_PROVIDER_IDS.includes(id) ? pi.createCustomProfile(id, overrides[id]) : undefined;
103
+ if (entry)
104
+ custom.set(id, entry);
105
+ const profile = entry?.profile ?? pi.buildOAuthProfile(id, overrides[id]);
106
+ piProfiles.set(id, profile);
107
+ profiles.set(id, { provider: id, displayName: profile.displayName, oauth: profile.piProvider?.auth.oauth });
108
+ }
109
+ catch {
110
+ ctx.logger.warn(`dsh-auth: route ${id} unavailable; remaining routes stay mounted`);
111
+ }
112
+ }
113
+ }
114
+ for (const id of configured.filter(id => OPEN_CODE_ROUTES.includes(id)))
115
+ profiles.set(id, { provider: id, displayName: id === 'opencode' ? 'OpenCode Zen' : 'OpenCode Go' });
133
116
  const store = new CredentialFile(config.credentialsFile ?? defaultCredentialsFile());
134
117
  const credentialKey = async (provider) => {
135
118
  const current = await store.read(provider);
@@ -160,28 +143,34 @@ export function apply(ctx, config) {
160
143
  return false;
161
144
  }
162
145
  };
163
- const adapter = new CredentialGatedAdapter({
146
+ const adapter = pi ? new pi.CredentialGatedAdapter({
164
147
  // One immutable map for the plugin's lifetime: the snapshot memoizes on
165
148
  // identity, and route changes here always mean a plugin remount anyway.
166
- profiles: () => profiles,
149
+ profiles: () => piProfiles,
167
150
  // Explicit API keys cover catalog routes too. OAuth catalog routes still
168
151
  // resolve their grants through pi-ai's injected credential store.
169
152
  resolveApiKey: credentialKey,
170
153
  auth: {
171
154
  credentials: store,
172
- authContext: hostAuthContext(),
155
+ authContext: pi.hostAuthContext(),
173
156
  },
174
157
  resolveAttachments: () => ctx.get('attachments'),
175
158
  onReplayDegrade: ({ provider, model, reason }) => {
176
159
  ctx.logger.warn(`dsh-auth: replay state on assistant history for ${provider}/${model} degraded: ${reason}`);
177
160
  },
178
- }, hasCredential);
161
+ }, hasCredential) : undefined;
162
+ const catalogs = new Map();
163
+ for (const id of OPEN_CODE_ROUTES)
164
+ if (configured.includes(id))
165
+ catalogs.set(id, new OpenCodeCatalog(id, OPEN_CODE_SNAPSHOTS[id], { changed: () => service.api?.notifyModelsChanged?.(id) }));
166
+ const native = new OpenCodeAdapter({ catalogs, store, overrides, attachments: () => ctx.get('attachments'), warn: reason => ctx.logger.warn(reason) });
179
167
  // The service is a thin holder so UIs can find the api without importing
180
168
  // the plugin module; the defensive get-then-create matches how the TUI
181
169
  // mounts userQuestions.
182
170
  const service = ctx.get('dshAuth') ?? new DshAuthService(ctx);
183
171
  const api = createDshAuthApi({
184
172
  profiles,
173
+ catalogs,
185
174
  store,
186
175
  resolveAsk: () => {
187
176
  const questions = ctx.get('userQuestions');
@@ -190,6 +179,14 @@ export function apply(ctx, config) {
190
179
  logger: ctx.logger,
191
180
  nousClientId: config.nous?.clientId,
192
181
  credentialChanged: async (provider, credential) => {
182
+ if (catalogs.has(provider)) {
183
+ if (credential) {
184
+ const status = await catalogs.get(provider).refresh();
185
+ if (status.warning)
186
+ throw new Error(status.warning);
187
+ }
188
+ return;
189
+ }
193
190
  const entry = custom.get(provider);
194
191
  if (entry === undefined)
195
192
  return;
@@ -206,6 +203,8 @@ export function apply(ctx, config) {
206
203
  },
207
204
  });
208
205
  service.api = api;
206
+ for (const catalog of catalogs.values())
207
+ void catalog.start();
209
208
  for (const [provider, entry] of custom) {
210
209
  void credentialKey(provider).then(key => key === undefined ? undefined : entry.refresh(key))
211
210
  .then(count => { if (count !== undefined)
@@ -225,9 +224,11 @@ export function apply(ctx, config) {
225
224
  else {
226
225
  // Individual registrations: one conflicting route must not strand the
227
226
  // rest (the registry keeps the previous owner serving).
228
- for (const id of configured) {
227
+ for (const id of profiles.keys()) {
229
228
  try {
230
- releases.push(llm.registerAdapter([id], adapter));
229
+ const owned = catalogs.has(id) ? native : adapter;
230
+ if (owned)
231
+ releases.push(llm.registerAdapter([id], owned));
231
232
  }
232
233
  catch (error) {
233
234
  ctx.logger.error(`dsh-auth: route "${id}" was not registered: ${error instanceof Error ? error.message : String(error)} `
@@ -243,7 +244,7 @@ export function apply(ctx, config) {
243
244
  const handler = createAuthCommandHandler(api);
244
245
  releases.push(commands.register({
245
246
  name: 'auth',
246
- description: 'Provider authentication: status, login, logout',
247
+ description: 'Provider authentication and catalogs: status, login, logout, models, refresh',
247
248
  handler: invocation => {
248
249
  const operation = handler(invocation);
249
250
  active.add(operation);
@@ -256,6 +257,8 @@ export function apply(ctx, config) {
256
257
  // enter while already-started logins finish their final write.
257
258
  yield async () => { await Promise.allSettled([...active]); };
258
259
  yield () => {
260
+ for (const catalog of catalogs.values())
261
+ catalog.dispose();
259
262
  for (const release of releases)
260
263
  release();
261
264
  };