@agi-fans/dsh-tui 0.20.0 → 0.21.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 (202) hide show
  1. package/lib/CHANGELOG.md +92 -3
  2. package/lib/chrome/main-screen-renderer.d.ts +2 -0
  3. package/lib/chrome/main-screen-renderer.js +5 -0
  4. package/lib/chrome/main-screen-renderer.js.map +1 -1
  5. package/lib/chrome/renderer.d.ts +9 -0
  6. package/lib/chrome/renderer.js +1 -1
  7. package/lib/chrome/renderer.js.map +1 -1
  8. package/lib/chrome/theme.js +32 -33
  9. package/lib/chrome/theme.js.map +1 -1
  10. package/lib/chrome/tool-args.d.ts +1 -1
  11. package/lib/chrome/tool-args.js +3 -1
  12. package/lib/chrome/tool-args.js.map +1 -1
  13. package/lib/chrome/tool-renderers.d.ts +2 -0
  14. package/lib/chrome/tool-renderers.js +10 -0
  15. package/lib/chrome/tool-renderers.js.map +1 -1
  16. package/lib/chrome/welcome-tips.d.ts +1 -0
  17. package/lib/chrome/welcome-tips.js +3 -2
  18. package/lib/chrome/welcome-tips.js.map +1 -1
  19. package/lib/commands/advisor.d.ts +15 -0
  20. package/lib/commands/advisor.js +200 -0
  21. package/lib/commands/advisor.js.map +1 -0
  22. package/lib/commands/auth.d.ts +4 -2
  23. package/lib/commands/auth.js +317 -83
  24. package/lib/commands/auth.js.map +1 -1
  25. package/lib/commands/btw.d.ts +15 -0
  26. package/lib/commands/btw.js +126 -0
  27. package/lib/commands/btw.js.map +1 -0
  28. package/lib/commands/diff.js +125 -5
  29. package/lib/commands/diff.js.map +1 -1
  30. package/lib/commands/export.d.ts +1 -1
  31. package/lib/commands/export.js +31 -5
  32. package/lib/commands/export.js.map +1 -1
  33. package/lib/commands/files.js +55 -7
  34. package/lib/commands/files.js.map +1 -1
  35. package/lib/commands/integrations.js +18 -6
  36. package/lib/commands/integrations.js.map +1 -1
  37. package/lib/commands/model.js +1 -0
  38. package/lib/commands/model.js.map +1 -1
  39. package/lib/commands/permission.js +35 -1
  40. package/lib/commands/permission.js.map +1 -1
  41. package/lib/commands/plugins.js +76 -39
  42. package/lib/commands/plugins.js.map +1 -1
  43. package/lib/commands/rate.d.ts +33 -0
  44. package/lib/commands/rate.js +212 -0
  45. package/lib/commands/rate.js.map +1 -0
  46. package/lib/commands/schedule.d.ts +19 -0
  47. package/lib/commands/schedule.js +197 -0
  48. package/lib/commands/schedule.js.map +1 -0
  49. package/lib/commands/session-configuration.d.ts +1 -1
  50. package/lib/commands/session-configuration.js +37 -28
  51. package/lib/commands/session-configuration.js.map +1 -1
  52. package/lib/commands/session.js +67 -48
  53. package/lib/commands/session.js.map +1 -1
  54. package/lib/commands/terminal.d.ts +15 -1
  55. package/lib/commands/terminal.js +119 -16
  56. package/lib/commands/terminal.js.map +1 -1
  57. package/lib/commands/worktree.d.ts +5 -0
  58. package/lib/commands/worktree.js +103 -0
  59. package/lib/commands/worktree.js.map +1 -0
  60. package/lib/definition.d.ts +45 -6
  61. package/lib/definition.js.map +1 -1
  62. package/lib/input/editor-discovery.js +2 -2
  63. package/lib/input/editor-discovery.js.map +1 -1
  64. package/lib/input/keybindings-config.d.ts +6 -2
  65. package/lib/input/keybindings-config.js +55 -6
  66. package/lib/input/keybindings-config.js.map +1 -1
  67. package/lib/input/keys.d.ts +2 -0
  68. package/lib/input/keys.js +10 -0
  69. package/lib/input/keys.js.map +1 -1
  70. package/lib/runtime/agent-profile.d.ts +5 -0
  71. package/lib/runtime/agent-profile.js +12 -0
  72. package/lib/runtime/agent-profile.js.map +1 -1
  73. package/lib/runtime/approval-rules.d.ts +59 -0
  74. package/lib/runtime/approval-rules.js +179 -0
  75. package/lib/runtime/approval-rules.js.map +1 -0
  76. package/lib/runtime/btw-policy.d.ts +7 -0
  77. package/lib/runtime/btw-policy.js +18 -0
  78. package/lib/runtime/btw-policy.js.map +1 -0
  79. package/lib/runtime/btw.d.ts +47 -0
  80. package/lib/runtime/btw.js +200 -0
  81. package/lib/runtime/btw.js.map +1 -0
  82. package/lib/runtime/composer-shell.d.ts +15 -0
  83. package/lib/runtime/composer-shell.js +84 -0
  84. package/lib/runtime/composer-shell.js.map +1 -0
  85. package/lib/runtime/file-recovery-local.d.ts +73 -0
  86. package/lib/runtime/file-recovery-local.js +351 -0
  87. package/lib/runtime/file-recovery-local.js.map +1 -0
  88. package/lib/runtime/file-recovery-recording.d.ts +23 -0
  89. package/lib/runtime/file-recovery-recording.js +131 -0
  90. package/lib/runtime/file-recovery-recording.js.map +1 -0
  91. package/lib/runtime/file-recovery-review.d.ts +4 -0
  92. package/lib/runtime/file-recovery-review.js +131 -0
  93. package/lib/runtime/file-recovery-review.js.map +1 -0
  94. package/lib/runtime/file-recovery-store.d.ts +83 -0
  95. package/lib/runtime/file-recovery-store.js +325 -0
  96. package/lib/runtime/file-recovery-store.js.map +1 -0
  97. package/lib/runtime/file-recovery-worker.d.ts +22 -0
  98. package/lib/runtime/file-recovery-worker.js +153 -0
  99. package/lib/runtime/file-recovery-worker.js.map +1 -0
  100. package/lib/runtime/file-review.d.ts +6 -3
  101. package/lib/runtime/file-review.js +43 -6
  102. package/lib/runtime/file-review.js.map +1 -1
  103. package/lib/runtime/herdr-agent.d.ts +4 -13
  104. package/lib/runtime/herdr-agent.js +19 -63
  105. package/lib/runtime/herdr-agent.js.map +1 -1
  106. package/lib/runtime/hook-settings.d.ts +2 -2
  107. package/lib/runtime/plugin-settings.d.ts +25 -2
  108. package/lib/runtime/plugin-settings.js +327 -31
  109. package/lib/runtime/plugin-settings.js.map +1 -1
  110. package/lib/runtime/provider-local.d.ts +24 -9
  111. package/lib/runtime/provider-local.js +714 -156
  112. package/lib/runtime/provider-local.js.map +1 -1
  113. package/lib/runtime/runner.js +48 -2
  114. package/lib/runtime/runner.js.map +1 -1
  115. package/lib/runtime/session-archive.d.ts +6 -0
  116. package/lib/runtime/session-archive.js +129 -3
  117. package/lib/runtime/session-archive.js.map +1 -1
  118. package/lib/runtime/session-runtime.js +1 -1
  119. package/lib/runtime/session-runtime.js.map +1 -1
  120. package/lib/runtime/settings-preferences.d.ts +5 -0
  121. package/lib/runtime/settings-preferences.js +34 -0
  122. package/lib/runtime/settings-preferences.js.map +1 -0
  123. package/lib/runtime/terminal-state.d.ts +29 -0
  124. package/lib/runtime/terminal-state.js +62 -0
  125. package/lib/runtime/terminal-state.js.map +1 -0
  126. package/lib/runtime/tool-presentation.js +50 -14
  127. package/lib/runtime/tool-presentation.js.map +1 -1
  128. package/lib/session/external-agents.d.ts +5 -0
  129. package/lib/session/external-agents.js +27 -0
  130. package/lib/session/external-agents.js.map +1 -0
  131. package/lib/session/feature-toggles.d.ts +1 -1
  132. package/lib/session/feature-toggles.js +1 -1
  133. package/lib/session/interaction-adapter.d.ts +1 -6
  134. package/lib/session/interaction-adapter.js +92 -14
  135. package/lib/session/interaction-adapter.js.map +1 -1
  136. package/lib/session/model-readiness.d.ts +16 -0
  137. package/lib/session/model-readiness.js +43 -0
  138. package/lib/session/model-readiness.js.map +1 -0
  139. package/lib/session/project-context.d.ts +5 -0
  140. package/lib/session/project-context.js +36 -0
  141. package/lib/session/project-context.js.map +1 -1
  142. package/lib/session/session-controller.d.ts +0 -2
  143. package/lib/session/session-controller.js +92 -89
  144. package/lib/session/session-controller.js.map +1 -1
  145. package/lib/session/session-library.js +2 -0
  146. package/lib/session/session-library.js.map +1 -1
  147. package/lib/session/session-tree.d.ts +1 -0
  148. package/lib/session/session-tree.js +1 -1
  149. package/lib/session/session-tree.js.map +1 -1
  150. package/lib/session/subagent-roster.d.ts +2 -1
  151. package/lib/session/subagent-roster.js +24 -3
  152. package/lib/session/subagent-roster.js.map +1 -1
  153. package/lib/session/subagent-tracker.d.ts +6 -2
  154. package/lib/session/subagent-tracker.js +49 -2
  155. package/lib/session/subagent-tracker.js.map +1 -1
  156. package/lib/session/tui-settings.d.ts +7 -2
  157. package/lib/session/tui-settings.js +3 -0
  158. package/lib/session/tui-settings.js.map +1 -1
  159. package/lib/session/working-directory.d.ts +4 -0
  160. package/lib/session/working-directory.js +4 -0
  161. package/lib/session/working-directory.js.map +1 -0
  162. package/lib/session/worktrees.d.ts +13 -0
  163. package/lib/session/worktrees.js +49 -0
  164. package/lib/session/worktrees.js.map +1 -0
  165. package/lib/views/agent-hub.js +3 -0
  166. package/lib/views/agent-hub.js.map +1 -1
  167. package/lib/views/approval-rule.d.ts +3 -0
  168. package/lib/views/approval-rule.js +4 -0
  169. package/lib/views/approval-rule.js.map +1 -0
  170. package/lib/views/at-complete.d.ts +8 -0
  171. package/lib/views/at-complete.js +64 -2
  172. package/lib/views/at-complete.js.map +1 -1
  173. package/lib/views/autocomplete.d.ts +4 -0
  174. package/lib/views/autocomplete.js +28 -11
  175. package/lib/views/autocomplete.js.map +1 -1
  176. package/lib/views/copy-selector.d.ts +3 -0
  177. package/lib/views/copy-selector.js +46 -15
  178. package/lib/views/copy-selector.js.map +1 -1
  179. package/lib/views/copy-targets.d.ts +2 -2
  180. package/lib/views/copy-targets.js +31 -13
  181. package/lib/views/copy-targets.js.map +1 -1
  182. package/lib/views/event-views.d.ts +1 -1
  183. package/lib/views/event-views.js +18 -0
  184. package/lib/views/event-views.js.map +1 -1
  185. package/lib/views/file-actions.d.ts +12 -0
  186. package/lib/views/file-actions.js +72 -0
  187. package/lib/views/file-actions.js.map +1 -0
  188. package/lib/views/hotkeys.d.ts +1 -0
  189. package/lib/views/hotkeys.js +6 -3
  190. package/lib/views/hotkeys.js.map +1 -1
  191. package/lib/views/prompt-selector.d.ts +3 -1
  192. package/lib/views/prompt-selector.js +33 -21
  193. package/lib/views/prompt-selector.js.map +1 -1
  194. package/lib/views/settings-list.d.ts +41 -2
  195. package/lib/views/settings-list.js +176 -57
  196. package/lib/views/settings-list.js.map +1 -1
  197. package/lib/views/transcript-export.js +9 -0
  198. package/lib/views/transcript-export.js.map +1 -1
  199. package/lib/views/transcript-render.d.ts +2 -0
  200. package/lib/views/transcript-render.js +46 -8
  201. package/lib/views/transcript-render.js.map +1 -1
  202. package/package.json +97 -48
@@ -4,17 +4,18 @@ import process from 'node:process';
4
4
  import { AuthorizationError, } from '@deepseek-ai/dsh-authorization';
5
5
  import { credentialKey, credentialKeyId, credentialKeyScope, credentialRef, isCredentialKeySegment, parseCredentialKey, } from '@deepseek-ai/dsh-credentials';
6
6
  import { createAuthorizationInteraction } from "../session/authorization-interaction.js";
7
+ import { APP_VERSION } from "../session/package-metadata.js";
7
8
  import { registerCommands } from "./registration.js";
8
9
  export const name = 'omdsh-command-auth';
9
10
  export const inject = ['commands', 'credentials', 'settings', 'tui', 'llm'];
10
11
  export const DEEPSEEK_PROVIDER = 'deepseek-official';
11
12
  export const DEEPSEEK_API_KEY = credentialRef('DEEPSEEK_API_KEY');
12
- export const OMDSH_DEEPSEEK_API_KEY = credentialRef('OMDSH_DEEPSEEK_API_KEY');
13
13
  export const DEEPSEEK_SETTINGS = 'llm-deepseek';
14
14
  export const PI_AI_SETTINGS = 'llm-pi-ai';
15
15
  export const DEEPSEEK_API_KEYS_URL = 'https://platform.deepseek.com/api_keys';
16
16
  export const DEEPSEEK_MODELS_URL = 'https://api.deepseek.com/v1/models';
17
17
  export const CUSTOM_PROVIDER_VALUE = '__omdsh_custom__';
18
+ export const DEEPSEEK_ACCOUNT_CHOICE = '__omdsh_deepseek_account__';
18
19
  export const FLOW_VALUE_PREFIX = '__omdsh_flow__:';
19
20
  const PI_AI_RECORD_SCOPE = 'llm-pi-ai';
20
21
  const FULLSCREEN_CHOICE_THRESHOLD = 8;
@@ -144,8 +145,13 @@ function authorizationFlows(ctx) {
144
145
  function isPiAiDeepSeekFlow(flow) {
145
146
  return credentialKeyScope(flow.key) === PI_AI_RECORD_SCOPE && credentialKeyId(flow.key) === 'deepseek';
146
147
  }
148
+ function isAccountFlow(flow) {
149
+ return credentialKeyScope(flow.key) === 'deepseek-account-platform';
150
+ }
147
151
  function loginAuthorizationFlows(ctx) {
148
152
  return authorizationFlows(ctx).flatMap(flow => {
153
+ if (isAccountFlow(flow))
154
+ return [];
149
155
  if (!isPiAiDeepSeekFlow(flow))
150
156
  return [flow];
151
157
  const profile = walkPath(sectionValue(ctx, PI_AI_SETTINGS), ['providers', 'deepseek']);
@@ -176,8 +182,9 @@ function parseFlowChoice(raw) {
176
182
  return undefined;
177
183
  }
178
184
  }
179
- function loginChoiceValues(entries, flows, includeCustom) {
185
+ function loginChoiceValues(entries, flows, includeCustom, includeAccount = false) {
180
186
  return [
187
+ ...(includeAccount ? [DEEPSEEK_ACCOUNT_CHOICE] : []),
181
188
  ...entries.map(entry => entry.provider),
182
189
  ...flows.map(flow => flowChoiceValue(flow.key)),
183
190
  ...(includeCustom ? [CUSTOM_PROVIDER_VALUE] : []),
@@ -225,13 +232,15 @@ function catalogForRecord(ctx, key) {
225
232
  return loginableProviders(ctx).find(entry => entry.provider === id && entry.settingsNs === scope);
226
233
  }
227
234
  function isFlowRecordLogout(choice) {
228
- return 'key' in choice && 'writable' in choice && !('provider' in choice);
235
+ return typeof choice !== 'string' && 'key' in choice && 'writable' in choice && !('provider' in choice);
229
236
  }
230
237
  async function configuredFlowRecords(ctx) {
231
238
  const credentials = ctx.credentials;
232
239
  const rows = [];
233
240
  const claimed = new Set();
234
241
  for (const flow of authorizationFlows(ctx)) {
242
+ if (isAccountFlow(flow))
243
+ continue;
235
244
  const info = await credentials.describeRecord(flow.key);
236
245
  if (!info.configured)
237
246
  continue;
@@ -258,6 +267,9 @@ async function configuredFlowRecords(ctx) {
258
267
  async function logoutableChoices(ctx) {
259
268
  const providers = logoutableProviders(ctx);
260
269
  const records = await configuredFlowRecords(ctx);
270
+ const account = accountClient(ctx);
271
+ const accountState = account === undefined ? undefined : await account.getState().catch(() => undefined);
272
+ const accountChoice = accountState?.status === 'credential-stored' ? [DEEPSEEK_ACCOUNT_CHOICE] : [];
261
273
  const actionable = [];
262
274
  let inactiveDeepSeek;
263
275
  for (const entry of providers) {
@@ -266,19 +278,18 @@ async function logoutableChoices(ctx) {
266
278
  continue;
267
279
  }
268
280
  const activeRef = profileApiKeyEnv(ctx, entry) ?? String(DEEPSEEK_API_KEY);
269
- const managed = activeRef === String(OMDSH_DEEPSEEK_API_KEY);
270
- const current = await ctx.credentials.describe(managed ? OMDSH_DEEPSEEK_API_KEY : DEEPSEEK_API_KEY);
271
- if (managed || (activeRef === String(DEEPSEEK_API_KEY) && current.source === 'file')) {
281
+ const current = await ctx.credentials.describe(credentialRef(activeRef));
282
+ if (current.configured && current.source === 'file')
272
283
  actionable.push(entry);
273
- }
274
- else {
284
+ else
275
285
  inactiveDeepSeek = entry;
276
- }
277
286
  }
278
- const choices = [...actionable, ...records];
287
+ const choices = [...accountChoice, ...actionable, ...records];
279
288
  // Preserve the direct explanatory response when the environment-owned or
280
289
  // already-logged-out official route is the only thing the command can see.
281
- return choices.length === 0 && inactiveDeepSeek !== undefined ? [inactiveDeepSeek] : choices;
290
+ if (choices.length === 0 && inactiveDeepSeek !== undefined)
291
+ return [inactiveDeepSeek];
292
+ return choices;
282
293
  }
283
294
  function selected(raw, values) {
284
295
  const index = /^\d+$/u.test(raw) ? Number(raw) - 1 : -1;
@@ -316,7 +327,10 @@ async function pickLoginChoice(ctx, invocation) {
316
327
  return configured !== 0 ? configured : left.flow.label.localeCompare(right.flow.label);
317
328
  });
318
329
  const orderedFlows = flowRows.map(row => row.flow);
319
- const values = loginChoiceValues(entries, orderedFlows, true);
330
+ const account = ctx.get('deepseekAccount');
331
+ const accountState = account?.getState === undefined ? undefined : await account.getState().catch(() => undefined);
332
+ const includeAccount = account?.getState !== undefined;
333
+ const values = loginChoiceValues(entries, orderedFlows, true, includeAccount);
320
334
  const fullscreen = values.length > FULLSCREEN_CHOICE_THRESHOLD;
321
335
  const raw = await ctx.tui.prompt({
322
336
  ...(fullscreen ? { presentation: 'fullscreen-list' } : {}),
@@ -325,6 +339,16 @@ async function pickLoginChoice(ctx, invocation) {
325
339
  title: 'Provider',
326
340
  question: 'Choose a provider to sign in or update',
327
341
  options: [
342
+ ...(includeAccount ? [{
343
+ label: 'DeepSeek account',
344
+ value: DEEPSEEK_ACCOUNT_CHOICE,
345
+ description: accountState?.status === 'credential-stored'
346
+ ? 'signed in · shared with DSH; choose deepseek-account in /model'
347
+ : 'signed out · opens the DeepSeek browser sign-in',
348
+ ...(accountState?.status === 'credential-stored'
349
+ ? { badge: { label: 'signed in', tone: 'success' } }
350
+ : {}),
351
+ }] : []),
328
352
  ...entryRows.map(({ entry, badge }) => ({
329
353
  label: providerListId(entry),
330
354
  value: entry.provider,
@@ -350,6 +374,7 @@ async function pickLoginChoice(ctx, invocation) {
350
374
  },
351
375
  ],
352
376
  allowCustom: false,
377
+ notify: false,
353
378
  signal: invocation.signal,
354
379
  });
355
380
  if (raw === null)
@@ -360,6 +385,8 @@ async function pickLoginChoice(ctx, invocation) {
360
385
  const chosen = selected(raw, values);
361
386
  if (chosen === undefined)
362
387
  return undefined;
388
+ if (chosen === DEEPSEEK_ACCOUNT_CHOICE)
389
+ return DEEPSEEK_ACCOUNT_CHOICE;
363
390
  const flowKey = parseFlowChoice(chosen);
364
391
  if (flowKey !== undefined)
365
392
  return orderedFlows.find(flow => flow.key === flowKey);
@@ -380,6 +407,7 @@ async function pickAuthorizationMethod(ctx, invocation, flow) {
380
407
  value: method.id,
381
408
  })),
382
409
  allowCustom: false,
410
+ notify: false,
383
411
  signal: invocation.signal,
384
412
  });
385
413
  if (raw === null)
@@ -401,7 +429,10 @@ async function loginAuthorization(ctx, invocation, flow) {
401
429
  const outcome = await authorization.begin({
402
430
  key: flow.key,
403
431
  method,
404
- interaction: createAuthorizationInteraction(ctx.tui, `Login to ${flow.label}`),
432
+ interaction: createAuthorizationInteraction({
433
+ notice: message => { ctx.tui.notice(message); },
434
+ prompt: request => ctx.tui.prompt({ ...request, notify: false }),
435
+ }, `Login to ${flow.label}`),
405
436
  signal: invocation.signal,
406
437
  });
407
438
  if (outcome.status === 'cancelled')
@@ -423,45 +454,40 @@ async function loginAuthorization(ctx, invocation, flow) {
423
454
  async function resetDeepSeekCredentialRoute(ctx) {
424
455
  await ctx.settings.mutate(DEEPSEEK_SETTINGS, [{ op: 'unset', path: ['apiKeyEnv'] }]);
425
456
  }
426
- async function logoutSuccess(ctx, managed) {
427
- const fallback = await ctx.credentials.describe(DEEPSEEK_API_KEY);
428
- if (fallback.configured) {
429
- return {
430
- kind: 'success',
431
- text: `${managed ? 'Logged out from the omdsh-managed DeepSeek credential.' : 'Logged out from DeepSeek.'} Falling back to DEEPSEEK_API_KEY from ${credentialSource(fallback)}.`,
432
- };
433
- }
434
- return { kind: 'success', text: 'Logged out from DeepSeek.' };
435
- }
436
- /** Best-effort browser launch; the prompt always retains the full URL as a fallback. */
437
- export function openDeepSeekDashboard() {
457
+ /** Best-effort browser launch; callers always retain the full URL as a fallback. */
458
+ function openExternal(url) {
438
459
  const command = process.platform === 'darwin'
439
- ? { file: 'open', args: [DEEPSEEK_API_KEYS_URL] }
460
+ ? { file: 'open', args: [url] }
440
461
  : process.platform === 'win32'
441
- ? { file: 'cmd.exe', args: ['/d', '/s', '/c', 'start', '', DEEPSEEK_API_KEYS_URL] }
442
- : { file: 'xdg-open', args: [DEEPSEEK_API_KEYS_URL] };
462
+ ? { file: 'cmd.exe', args: ['/d', '/s', '/c', 'start', '', url] }
463
+ : { file: 'xdg-open', args: [url] };
443
464
  try {
444
465
  const child = spawn(command.file, command.args, { detached: true, stdio: 'ignore' });
445
466
  child.once('error', () => undefined);
446
467
  child.unref();
447
468
  }
448
469
  catch {
449
- // Headless and remote terminals still receive the visible dashboard URL.
470
+ // Headless and remote terminals still receive the visible URL.
450
471
  }
451
472
  }
473
+ /** Best-effort browser launch for the DeepSeek key dashboard. */
474
+ export function openDeepSeekDashboard() {
475
+ openExternal(DEEPSEEK_API_KEYS_URL);
476
+ }
452
477
  async function loginDeepSeek(ctx, invocation, config) {
453
- const current = await ctx.credentials.describe(OMDSH_DEEPSEEK_API_KEY);
478
+ const current = await ctx.credentials.describe(DEEPSEEK_API_KEY);
454
479
  if (current.configured && !current.writable) {
455
- return { kind: 'error', text: unmanagedCredentialMessage(OMDSH_DEEPSEEK_API_KEY, current, 'replace') };
480
+ return { kind: 'error', text: unmanagedCredentialMessage(DEEPSEEK_API_KEY, current, 'replace') };
456
481
  }
457
482
  if (config.openDashboard !== false)
458
483
  openDeepSeekDashboard();
459
484
  const raw = await ctx.tui.prompt({
460
485
  title: 'Login to DeepSeek',
461
486
  question: current.configured ? 'Paste a new DeepSeek API key' : 'Paste your DeepSeek API key',
462
- detail: `Create or copy a key at ${DEEPSEEK_API_KEYS_URL}. It will be validated, stored locally, and preferred over DEEPSEEK_API_KEY.`,
487
+ detail: `Create or copy a key at ${DEEPSEEK_API_KEYS_URL}. It will be validated and stored locally as ${DEEPSEEK_API_KEY}.`,
463
488
  allowCustom: true,
464
489
  secret: true,
490
+ notify: false,
465
491
  submitLabel: 'validate',
466
492
  signal: invocation.signal,
467
493
  });
@@ -478,23 +504,27 @@ async function loginDeepSeek(ctx, invocation, config) {
478
504
  return { kind: 'error', text: error instanceof Error ? error.message : 'DeepSeek login failed.' };
479
505
  }
480
506
  try {
481
- await ctx.credentials.set(OMDSH_DEEPSEEK_API_KEY, apiKey);
507
+ await ctx.credentials.set(DEEPSEEK_API_KEY, apiKey);
482
508
  }
483
509
  catch {
484
510
  return { kind: 'error', text: 'The API key is valid, but it could not be saved to the Harness credential store.' };
485
511
  }
486
- try {
487
- await ctx.settings.update(DEEPSEEK_SETTINGS, { apiKeyEnv: String(OMDSH_DEEPSEEK_API_KEY) });
488
- }
489
- catch {
490
- return {
491
- kind: 'error',
492
- text: 'The API key is valid and saved, but omdsh could not activate it for the DeepSeek provider. Run /login again after checking your settings file.',
493
- };
512
+ // A profile may still pin the old product-owned reference. Reset it so the
513
+ // upstream default resolves the key that was just stored.
514
+ if (profileApiKeyEnv(ctx, fallbackDeepSeekEntry()) !== String(DEEPSEEK_API_KEY)) {
515
+ try {
516
+ await resetDeepSeekCredentialRoute(ctx);
517
+ }
518
+ catch {
519
+ return {
520
+ kind: 'error',
521
+ text: 'The API key is valid and saved, but the DeepSeek credential reference could not be reset. Run /login again after checking your settings file.',
522
+ };
523
+ }
494
524
  }
495
525
  return {
496
526
  kind: 'success',
497
- text: 'Logged in to DeepSeek. Your omdsh credential takes priority on the next model request.',
527
+ text: 'Logged in to DeepSeek. The stored key is used by the deepseek-official route on the next model request.',
498
528
  };
499
529
  }
500
530
  async function loginCatalog(ctx, invocation, entry) {
@@ -510,6 +540,7 @@ async function loginCatalog(ctx, invocation, entry) {
510
540
  detail: `The key is stored locally and activates the ${entry.provider} route for the next model request. Use /model to select it.`,
511
541
  allowCustom: true,
512
542
  secret: true,
543
+ notify: false,
513
544
  submitLabel: 'save',
514
545
  signal: invocation.signal,
515
546
  });
@@ -563,6 +594,7 @@ async function collectCustomModels(ctx, invocation, draft) {
563
594
  ],
564
595
  initialValue: 'enter',
565
596
  allowCustom: false,
597
+ notify: false,
566
598
  signal: invocation.signal,
567
599
  });
568
600
  if (source === null)
@@ -587,6 +619,7 @@ async function collectCustomModels(ctx, invocation, draft) {
587
619
  })),
588
620
  multiSelect: true,
589
621
  allowCustom: false,
622
+ notify: false,
590
623
  filterable: discovered.length > FULLSCREEN_CHOICE_THRESHOLD,
591
624
  ...(discovered.length > FULLSCREEN_CHOICE_THRESHOLD ? { presentation: 'fullscreen-list' } : {}),
592
625
  signal: invocation.signal,
@@ -605,6 +638,7 @@ async function collectCustomModels(ctx, invocation, draft) {
605
638
  question: 'Enter model ids',
606
639
  detail: `Could not fetch models (${detail}). Type one or more ids, separated by commas.`,
607
640
  allowCustom: true,
641
+ notify: false,
608
642
  signal: invocation.signal,
609
643
  });
610
644
  if (fallback === null)
@@ -617,6 +651,7 @@ async function collectCustomModels(ctx, invocation, draft) {
617
651
  question: 'Enter model ids',
618
652
  detail: 'Separate multiple ids with commas. Example: llama3.1, qwen2.5-coder',
619
653
  allowCustom: true,
654
+ notify: false,
620
655
  signal: invocation.signal,
621
656
  });
622
657
  if (typed === null)
@@ -629,6 +664,7 @@ async function loginCustom(ctx, invocation) {
629
664
  question: 'Provider id',
630
665
  detail: 'Lowercase letters, digits, and hyphens. This id is permanent.',
631
666
  allowCustom: true,
667
+ notify: false,
632
668
  signal: invocation.signal,
633
669
  });
634
670
  if (idRaw === null)
@@ -648,6 +684,7 @@ async function loginCustom(ctx, invocation) {
648
684
  question: 'Base URL',
649
685
  detail: 'Absolute http(s) prefix, for example http://127.0.0.1:11434/v1',
650
686
  allowCustom: true,
687
+ notify: false,
651
688
  signal: invocation.signal,
652
689
  });
653
690
  if (urlRaw === null)
@@ -671,6 +708,7 @@ async function loginCustom(ctx, invocation) {
671
708
  })),
672
709
  initialValue: 'openai-completions',
673
710
  allowCustom: false,
711
+ notify: false,
674
712
  signal: invocation.signal,
675
713
  });
676
714
  if (api === null)
@@ -684,6 +722,7 @@ async function loginCustom(ctx, invocation) {
684
722
  detail: 'Leave empty if the endpoint does not need a key.',
685
723
  allowCustom: true,
686
724
  secret: true,
725
+ notify: false,
687
726
  submitLabel: 'continue',
688
727
  signal: invocation.signal,
689
728
  });
@@ -752,60 +791,88 @@ async function loginCustom(ctx, invocation) {
752
791
  }
753
792
  async function logoutDeepSeek(ctx, invocation) {
754
793
  const activeRef = profileApiKeyEnv(ctx, fallbackDeepSeekEntry()) ?? String(DEEPSEEK_API_KEY);
755
- const usesOmdshCredential = activeRef === String(OMDSH_DEEPSEEK_API_KEY);
756
- const activeCredential = usesOmdshCredential ? OMDSH_DEEPSEEK_API_KEY : DEEPSEEK_API_KEY;
757
- const current = await ctx.credentials.describe(activeCredential);
758
- const legacyManagedDefault = activeRef === String(DEEPSEEK_API_KEY) && current.source === 'file';
759
- if (!usesOmdshCredential && !legacyManagedDefault) {
760
- if (!current.configured)
761
- return { kind: 'success', text: 'DeepSeek is already logged out.' };
762
- return {
763
- kind: 'success',
764
- text: `No omdsh-managed DeepSeek login is active. DeepSeek is configured by ${credentialSource(current)}.`,
765
- };
766
- }
767
- if (usesOmdshCredential && !current.configured) {
768
- try {
769
- await resetDeepSeekCredentialRoute(ctx);
770
- }
771
- catch {
772
- return { kind: 'error', text: 'Could not reset the DeepSeek credential setting.' };
794
+ const current = await ctx.credentials.describe(credentialRef(activeRef));
795
+ if (!current.configured) {
796
+ if (activeRef !== String(DEEPSEEK_API_KEY)) {
797
+ try {
798
+ await resetDeepSeekCredentialRoute(ctx);
799
+ }
800
+ catch {
801
+ return { kind: 'error', text: 'Could not reset the DeepSeek credential setting.' };
802
+ }
773
803
  }
774
- return logoutSuccess(ctx, true);
804
+ return { kind: 'success', text: 'DeepSeek is already logged out.' };
805
+ }
806
+ if (current.source !== 'file') {
807
+ return { kind: 'success', text: `DeepSeek is configured by ${credentialSource(current)}; the credential store cannot remove it.` };
775
808
  }
776
- const removesStoredKey = current.source === 'file';
777
809
  const answer = await ctx.tui.prompt({
778
810
  title: 'Logout from DeepSeek',
779
- question: removesStoredKey ? 'Remove the stored DeepSeek API key?' : 'Stop using the omdsh DeepSeek credential?',
780
- detail: 'The next model request will fall back to DEEPSEEK_API_KEY when one is available.',
811
+ question: 'Remove the stored DeepSeek API key?',
812
+ detail: 'The next model request will use DEEPSEEK_API_KEY from the launch environment when one is available.',
781
813
  options: [
782
814
  { label: 'Cancel', value: 'cancel', description: 'Keep the current credential.' },
783
815
  {
784
816
  label: 'Log out',
785
817
  value: 'logout',
786
- description: removesStoredKey
787
- ? 'Remove the key from the local Harness credential store.'
788
- : 'Return the provider to its default credential source.',
818
+ description: 'Remove the key from the local Harness credential store.',
789
819
  badge: { label: 'removes credential', tone: 'warning' },
790
820
  },
791
821
  ],
792
822
  initialValue: 'cancel',
793
823
  allowCustom: false,
824
+ notify: false,
794
825
  submitLabel: 'apply',
795
826
  signal: invocation.signal,
796
827
  });
797
828
  if (answer !== 'logout')
798
829
  return { kind: 'success' };
799
830
  try {
800
- if (usesOmdshCredential)
831
+ await ctx.credentials.unset(credentialRef(activeRef));
832
+ if (activeRef !== String(DEEPSEEK_API_KEY))
801
833
  await resetDeepSeekCredentialRoute(ctx);
802
- if (removesStoredKey)
803
- await ctx.credentials.unset(activeCredential);
804
834
  }
805
835
  catch {
806
836
  return { kind: 'error', text: 'Could not remove the DeepSeek API key from the Harness credential store.' };
807
837
  }
808
- return logoutSuccess(ctx, usesOmdshCredential);
838
+ return { kind: 'success', text: 'Logged out from DeepSeek. The next model request falls back to the environment when available.' };
839
+ }
840
+ async function logoutDeepSeekAccount(ctx, invocation) {
841
+ const account = accountClient(ctx);
842
+ if (account === undefined)
843
+ return { kind: 'error', text: 'DeepSeek account integration is not mounted in this build.' };
844
+ let state;
845
+ try {
846
+ state = await account.getState();
847
+ }
848
+ catch {
849
+ return { kind: 'error', text: 'Could not read the shared DeepSeek account state.' };
850
+ }
851
+ if (state.status !== 'credential-stored') {
852
+ return { kind: 'success', text: 'No DeepSeek account grant is stored.' };
853
+ }
854
+ const answer = await ctx.tui.prompt({
855
+ title: 'Logout from DeepSeek account',
856
+ question: 'Sign out of the shared DeepSeek account?',
857
+ detail: 'Removes the local grant and revokes it in the background. The browser session is not signed out.',
858
+ options: [
859
+ { label: 'Cancel', value: 'cancel', description: 'Keep the account grant.' },
860
+ { label: 'Sign out', value: 'logout', description: 'Remove the local grant and revoke it in the background.' },
861
+ ],
862
+ initialValue: 'cancel',
863
+ allowCustom: false,
864
+ notify: false,
865
+ signal: invocation.signal,
866
+ });
867
+ if (answer !== 'logout')
868
+ return { kind: 'success' };
869
+ try {
870
+ await account.signOut(accountClientMetadata());
871
+ }
872
+ catch {
873
+ return { kind: 'error', text: 'Could not sign out of the DeepSeek account. Try again from DSH Web/Desktop.' };
874
+ }
875
+ return { kind: 'success', text: 'Signed out of the DeepSeek account. The local grant was removed; remote revocation continues in the background.' };
809
876
  }
810
877
  async function pickLogoutChoice(ctx, invocation) {
811
878
  const choices = await logoutableChoices(ctx);
@@ -813,7 +880,9 @@ async function pickLogoutChoice(ctx, invocation) {
813
880
  return undefined;
814
881
  if (choices.length === 1)
815
882
  return choices[0];
816
- const values = choices.map(choice => isFlowRecordLogout(choice) ? flowChoiceValue(choice.key) : choice.provider);
883
+ const values = choices.map(choice => choice === DEEPSEEK_ACCOUNT_CHOICE
884
+ ? DEEPSEEK_ACCOUNT_CHOICE
885
+ : isFlowRecordLogout(choice) ? flowChoiceValue(choice.key) : choice.provider);
817
886
  const fullscreen = values.length > FULLSCREEN_CHOICE_THRESHOLD;
818
887
  const raw = await ctx.tui.prompt({
819
888
  ...(fullscreen ? { presentation: 'fullscreen-list' } : {}),
@@ -821,18 +890,25 @@ async function pickLogoutChoice(ctx, invocation) {
821
890
  filterable: fullscreen,
822
891
  title: 'Provider',
823
892
  question: 'Choose a provider to log out from',
824
- options: choices.map(choice => isFlowRecordLogout(choice)
893
+ options: choices.map(choice => choice === DEEPSEEK_ACCOUNT_CHOICE
825
894
  ? {
826
- label: choice.label,
827
- value: flowChoiceValue(choice.key),
828
- description: choice.writable ? 'signed in' : 'not removable here',
895
+ label: 'DeepSeek account',
896
+ value: DEEPSEEK_ACCOUNT_CHOICE,
897
+ description: 'shared with DSH · signed in',
829
898
  }
830
- : {
831
- label: providerListId(choice),
832
- value: choice.provider,
833
- description: 'signed in',
834
- }),
899
+ : isFlowRecordLogout(choice)
900
+ ? {
901
+ label: choice.label,
902
+ value: flowChoiceValue(choice.key),
903
+ description: choice.writable ? 'signed in' : 'not removable here',
904
+ }
905
+ : {
906
+ label: providerListId(choice),
907
+ value: choice.provider,
908
+ description: 'signed in',
909
+ }),
835
910
  allowCustom: false,
911
+ notify: false,
836
912
  signal: invocation.signal,
837
913
  });
838
914
  if (raw === null)
@@ -840,10 +916,12 @@ async function pickLogoutChoice(ctx, invocation) {
840
916
  const chosen = selected(raw, values);
841
917
  if (chosen === undefined)
842
918
  return undefined;
919
+ if (chosen === DEEPSEEK_ACCOUNT_CHOICE)
920
+ return DEEPSEEK_ACCOUNT_CHOICE;
843
921
  const flowKey = parseFlowChoice(chosen);
844
922
  if (flowKey !== undefined)
845
923
  return choices.find(choice => isFlowRecordLogout(choice) && choice.key === flowKey);
846
- return choices.find(choice => !isFlowRecordLogout(choice) && choice.provider === chosen);
924
+ return choices.find(choice => typeof choice !== 'string' && !isFlowRecordLogout(choice) && choice.provider === chosen);
847
925
  }
848
926
  async function logoutRecord(ctx, invocation, record) {
849
927
  if (!record.writable) {
@@ -867,6 +945,7 @@ async function logoutRecord(ctx, invocation, record) {
867
945
  ],
868
946
  initialValue: 'cancel',
869
947
  allowCustom: false,
948
+ notify: false,
870
949
  submitLabel: 'apply',
871
950
  signal: invocation.signal,
872
951
  });
@@ -911,6 +990,7 @@ async function logoutCatalog(ctx, invocation, entry) {
911
990
  ],
912
991
  initialValue: 'cancel',
913
992
  allowCustom: false,
993
+ notify: false,
914
994
  submitLabel: 'apply',
915
995
  signal: invocation.signal,
916
996
  });
@@ -928,6 +1008,156 @@ async function logoutCatalog(ctx, invocation, entry) {
928
1008
  }
929
1009
  return { kind: 'success', text: `Logged out from ${label}.` };
930
1010
  }
1011
+ function accountClient(ctx) {
1012
+ return ctx.get('deepseekAccount');
1013
+ }
1014
+ function accountClientMetadata() {
1015
+ return {
1016
+ version: APP_VERSION,
1017
+ locale: Intl.DateTimeFormat().resolvedOptions().locale,
1018
+ timezoneOffsetSeconds: -new Date().getTimezoneOffset() * 60,
1019
+ };
1020
+ }
1021
+ function accountCallbackOrigin(ctx) {
1022
+ const server = ctx.get('webServer');
1023
+ if (server === undefined || typeof server.port !== 'number' || server.port === 0)
1024
+ return undefined;
1025
+ return `${server.protocol}//${server.host}:${server.port}`;
1026
+ }
1027
+ function accountFailure(attempt) {
1028
+ if (attempt?.phase === 'expired')
1029
+ return 'The DeepSeek sign-in expired. Run /login to try again.';
1030
+ if (attempt?.phase === 'failed')
1031
+ return `DeepSeek sign-in failed${attempt.errorCode === undefined ? '' : ` (${attempt.errorCode})`}. Run /login to try again.`;
1032
+ if (attempt?.phase === 'cancelled')
1033
+ return 'DeepSeek sign-in was cancelled.';
1034
+ return undefined;
1035
+ }
1036
+ async function waitForAccount(invocation, account, timeoutMs) {
1037
+ const deadline = Date.now() + timeoutMs;
1038
+ while (Date.now() < deadline) {
1039
+ if (invocation.signal.aborted) {
1040
+ const state = await account.getState().catch(() => undefined);
1041
+ const id = state?.attempt?.id;
1042
+ if (id !== undefined)
1043
+ await account.cancelSignIn(id).catch(() => undefined);
1044
+ return { kind: 'success' };
1045
+ }
1046
+ const state = await account.getState().catch(() => undefined);
1047
+ if (state?.status === 'credential-stored' && state.attempt?.phase === 'succeeded') {
1048
+ return { kind: 'success', text: 'Signed in to the DeepSeek account. Use /model to select the deepseek-account route; web search and fetch use the account token there.' };
1049
+ }
1050
+ const failure = accountFailure(state?.attempt);
1051
+ if (failure !== undefined)
1052
+ return { kind: 'error', text: failure };
1053
+ await new Promise(resolve => setTimeout(resolve, 400));
1054
+ }
1055
+ const state = await account.getState().catch(() => undefined);
1056
+ const id = state?.attempt?.id;
1057
+ if (id !== undefined)
1058
+ await account.cancelSignIn(id).catch(() => undefined);
1059
+ return { kind: 'error', text: 'DeepSeek sign-in timed out before the browser completed. Run /login to try again.' };
1060
+ }
1061
+ async function loginDeepSeekAccount(ctx, invocation, config) {
1062
+ const account = accountClient(ctx);
1063
+ if (account === undefined)
1064
+ return { kind: 'error', text: 'DeepSeek account integration is not mounted in this build.' };
1065
+ let state;
1066
+ try {
1067
+ state = await account.getState();
1068
+ }
1069
+ catch {
1070
+ return { kind: 'error', text: 'Could not read the shared DeepSeek account state.' };
1071
+ }
1072
+ if (state.status === 'credential-stored') {
1073
+ const choice = await ctx.tui.prompt({
1074
+ title: 'DeepSeek account',
1075
+ question: 'The account is signed in and shared with DSH.',
1076
+ detail: 'Use /model to select the deepseek-account route. Signing out removes the local grant; the DSH browser session is revoked in the background.',
1077
+ options: [
1078
+ { label: 'Use account route', value: 'use', description: 'Close this prompt; select the route in /model.' },
1079
+ { label: 'Sign out', value: 'out', description: 'Remove the local grant and revoke it in the background.' },
1080
+ { label: 'Cancel', value: 'cancel' },
1081
+ ],
1082
+ initialValue: 'use',
1083
+ allowCustom: false,
1084
+ notify: false,
1085
+ signal: invocation.signal,
1086
+ });
1087
+ if (choice === null || choice === 'cancel' || choice === 'use') {
1088
+ return { kind: 'success', text: 'Use /model to select the deepseek-account route.' };
1089
+ }
1090
+ try {
1091
+ await account.signOut(accountClientMetadata());
1092
+ }
1093
+ catch {
1094
+ return { kind: 'error', text: 'Could not sign out of the DeepSeek account. Try again from DSH Web/Desktop.' };
1095
+ }
1096
+ return { kind: 'success', text: 'Signed out of the DeepSeek account. The next account request requires sign-in again.' };
1097
+ }
1098
+ const server = ctx.get('webServer');
1099
+ if (server === undefined || ctx.tui.interactive === false) {
1100
+ return {
1101
+ kind: 'error',
1102
+ text: 'The loopback sign-in host is unavailable. Start omdsh with its normal terminal host, or sign in through DSH Web/Desktop and rerun /login.',
1103
+ };
1104
+ }
1105
+ const origin = accountCallbackOrigin(ctx);
1106
+ if (origin === undefined)
1107
+ return { kind: 'error', text: 'The loopback sign-in host did not expose a port.' };
1108
+ let initial;
1109
+ try {
1110
+ initial = await account.startSignIn(accountClientMetadata(), origin, 'web');
1111
+ }
1112
+ catch (error) {
1113
+ return { kind: 'error', text: error instanceof Error ? error.message : 'Could not start DeepSeek sign-in.' };
1114
+ }
1115
+ const attempt = initial.attempt;
1116
+ const failure = accountFailure(attempt);
1117
+ if (failure !== undefined)
1118
+ return { kind: 'error', text: failure };
1119
+ let url = attempt?.authorizeUrl;
1120
+ for (let i = 0; i < 40 && url === undefined; i += 1) {
1121
+ if (invocation.signal.aborted)
1122
+ break;
1123
+ await new Promise(resolve => setTimeout(resolve, 100));
1124
+ const next = await account.getState().catch(() => undefined);
1125
+ const nextFailure = accountFailure(next?.attempt);
1126
+ if (nextFailure !== undefined)
1127
+ return { kind: 'error', text: nextFailure };
1128
+ url = next?.attempt?.authorizeUrl;
1129
+ }
1130
+ if (url === undefined) {
1131
+ const id = attempt?.id ?? (await account.getState().catch(() => undefined))?.attempt?.id;
1132
+ if (id !== undefined)
1133
+ await account.cancelSignIn(id).catch(() => undefined);
1134
+ return { kind: 'error', text: 'DeepSeek did not return a browser URL for this sign-in attempt.' };
1135
+ }
1136
+ if (config.openBrowser !== false)
1137
+ openExternal(url);
1138
+ const answer = await ctx.tui.prompt({
1139
+ title: 'DeepSeek account',
1140
+ question: 'Complete sign-in in your browser, then return here.',
1141
+ detail: `${url}
1142
+
1143
+ This terminal is listening on ${origin}/oauth/callback until the attempt ends.`,
1144
+ options: [
1145
+ { label: 'I completed sign-in', value: 'done' },
1146
+ { label: 'Cancel', value: 'cancel', description: 'Withdraw this sign-in attempt.' },
1147
+ ],
1148
+ initialValue: 'done',
1149
+ allowCustom: false,
1150
+ notify: false,
1151
+ signal: invocation.signal,
1152
+ });
1153
+ if (answer !== 'done') {
1154
+ const id = attempt?.id ?? (await account.getState().catch(() => undefined))?.attempt?.id;
1155
+ if (id !== undefined)
1156
+ await account.cancelSignIn(id).catch(() => undefined);
1157
+ return { kind: 'success', text: 'DeepSeek sign-in cancelled.' };
1158
+ }
1159
+ return waitForAccount(invocation, account, 120_000);
1160
+ }
931
1161
  async function login(ctx, invocation, config) {
932
1162
  if (invocation.rawInput.trim() !== '') {
933
1163
  return { kind: 'error', text: 'Usage: /login (paste the key only into the protected prompt)' };
@@ -935,6 +1165,8 @@ async function login(ctx, invocation, config) {
935
1165
  const choice = await pickLoginChoice(ctx, invocation);
936
1166
  if (choice === undefined)
937
1167
  return { kind: 'success' };
1168
+ if (choice === DEEPSEEK_ACCOUNT_CHOICE)
1169
+ return loginDeepSeekAccount(ctx, invocation, config);
938
1170
  if (choice === CUSTOM_PROVIDER_VALUE)
939
1171
  return loginCustom(ctx, invocation);
940
1172
  if (isAuthorizationFlow(choice))
@@ -949,6 +1181,8 @@ async function logout(ctx, invocation) {
949
1181
  const choice = await pickLogoutChoice(ctx, invocation);
950
1182
  if (choice === undefined)
951
1183
  return { kind: 'success' };
1184
+ if (choice === DEEPSEEK_ACCOUNT_CHOICE)
1185
+ return logoutDeepSeekAccount(ctx, invocation);
952
1186
  if (isFlowRecordLogout(choice))
953
1187
  return logoutRecord(ctx, invocation, choice);
954
1188
  return isDeepSeek(choice)
@@ -958,7 +1192,7 @@ async function logout(ctx, invocation) {
958
1192
  export function apply(ctx, config = {}) {
959
1193
  registerCommands(ctx, [
960
1194
  { name: 'login', description: 'Sign in to a provider', handler: invocation => login(ctx, invocation, config) },
961
- { name: 'logout', description: 'Sign out of a stored provider', handler: invocation => logout(ctx, invocation) },
1195
+ { name: 'logout', description: 'Sign out of a stored provider or account', handler: invocation => logout(ctx, invocation) },
962
1196
  ], 'omdsh authentication commands');
963
1197
  }
964
1198
  //# sourceMappingURL=auth.js.map