@agi-fans/dsh-tui 0.20.0 → 0.22.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.
- package/lib/CHANGELOG.md +249 -3
- package/lib/chrome/box.d.ts +47 -16
- package/lib/chrome/box.js +172 -130
- package/lib/chrome/box.js.map +1 -1
- package/lib/chrome/code-highlight.d.ts +15 -0
- package/lib/chrome/code-highlight.js +60 -4
- package/lib/chrome/code-highlight.js.map +1 -1
- package/lib/chrome/command-output.d.ts +29 -2
- package/lib/chrome/command-output.js +100 -4
- package/lib/chrome/command-output.js.map +1 -1
- package/lib/chrome/main-screen-renderer.d.ts +18 -0
- package/lib/chrome/main-screen-renderer.js +58 -5
- package/lib/chrome/main-screen-renderer.js.map +1 -1
- package/lib/chrome/markdown.js +30 -10
- package/lib/chrome/markdown.js.map +1 -1
- package/lib/chrome/relative-time.d.ts +17 -0
- package/lib/chrome/relative-time.js +36 -0
- package/lib/chrome/relative-time.js.map +1 -1
- package/lib/chrome/renderer.d.ts +9 -0
- package/lib/chrome/renderer.js +1 -1
- package/lib/chrome/renderer.js.map +1 -1
- package/lib/chrome/secret-mask.d.ts +35 -0
- package/lib/chrome/secret-mask.js +180 -0
- package/lib/chrome/secret-mask.js.map +1 -0
- package/lib/chrome/status-config.d.ts +161 -51
- package/lib/chrome/status-config.js +237 -126
- package/lib/chrome/status-config.js.map +1 -1
- package/lib/chrome/status-line.d.ts +3 -2
- package/lib/chrome/status-line.js +105 -63
- package/lib/chrome/status-line.js.map +1 -1
- package/lib/chrome/terminal-colors.d.ts +85 -0
- package/lib/chrome/terminal-colors.js +230 -0
- package/lib/chrome/terminal-colors.js.map +1 -0
- package/lib/chrome/terminal-images.d.ts +9 -1
- package/lib/chrome/terminal-images.js +27 -4
- package/lib/chrome/terminal-images.js.map +1 -1
- package/lib/chrome/theme.d.ts +32 -9
- package/lib/chrome/theme.js +616 -520
- package/lib/chrome/theme.js.map +1 -1
- package/lib/chrome/tool-args.d.ts +1 -1
- package/lib/chrome/tool-args.js +3 -1
- package/lib/chrome/tool-args.js.map +1 -1
- package/lib/chrome/tool-renderers.d.ts +2 -0
- package/lib/chrome/tool-renderers.js +69 -3
- package/lib/chrome/tool-renderers.js.map +1 -1
- package/lib/chrome/tools-list.d.ts +1 -4
- package/lib/chrome/tools-list.js +1 -41
- package/lib/chrome/tools-list.js.map +1 -1
- package/lib/chrome/welcome-tips.d.ts +20 -7
- package/lib/chrome/welcome-tips.js +22 -41
- package/lib/chrome/welcome-tips.js.map +1 -1
- package/lib/commands/auth-ids.d.ts +25 -0
- package/lib/commands/auth-ids.js +27 -0
- package/lib/commands/auth-ids.js.map +1 -0
- package/lib/commands/auth-input.d.ts +34 -0
- package/lib/commands/auth-input.js +96 -0
- package/lib/commands/auth-input.js.map +1 -0
- package/lib/commands/auth-providers.d.ts +39 -0
- package/lib/commands/auth-providers.js +125 -0
- package/lib/commands/auth-providers.js.map +1 -0
- package/lib/commands/auth.d.ts +16 -22
- package/lib/commands/auth.js +346 -299
- package/lib/commands/auth.js.map +1 -1
- package/lib/commands/btw.d.ts +27 -0
- package/lib/commands/btw.js +59 -0
- package/lib/commands/btw.js.map +1 -0
- package/lib/commands/diff.js +30 -5
- package/lib/commands/diff.js.map +1 -1
- package/lib/commands/export.d.ts +1 -1
- package/lib/commands/export.js +5 -3
- package/lib/commands/export.js.map +1 -1
- package/lib/commands/files.d.ts +17 -1
- package/lib/commands/files.js +69 -61
- package/lib/commands/files.js.map +1 -1
- package/lib/commands/integrations.js +18 -6
- package/lib/commands/integrations.js.map +1 -1
- package/lib/commands/jobs.d.ts +19 -4
- package/lib/commands/jobs.js +50 -7
- package/lib/commands/jobs.js.map +1 -1
- package/lib/commands/model.js +109 -76
- package/lib/commands/model.js.map +1 -1
- package/lib/commands/permission.js +46 -13
- package/lib/commands/permission.js.map +1 -1
- package/lib/commands/plugins.js +196 -42
- package/lib/commands/plugins.js.map +1 -1
- package/lib/commands/schedule.d.ts +29 -0
- package/lib/commands/schedule.js +228 -0
- package/lib/commands/schedule.js.map +1 -0
- package/lib/commands/session-configuration.d.ts +1 -1
- package/lib/commands/session-configuration.js +29 -28
- package/lib/commands/session-configuration.js.map +1 -1
- package/lib/commands/session.js +75 -80
- package/lib/commands/session.js.map +1 -1
- package/lib/commands/terminal.d.ts +24 -4
- package/lib/commands/terminal.js +38 -94
- package/lib/commands/terminal.js.map +1 -1
- package/lib/commands/trajectory.js +4 -1
- package/lib/commands/trajectory.js.map +1 -1
- package/lib/definition.d.ts +150 -11
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/input/actions.d.ts +51 -0
- package/lib/input/actions.js +120 -0
- package/lib/input/actions.js.map +1 -0
- package/lib/input/editor-discovery.js +2 -2
- package/lib/input/editor-discovery.js.map +1 -1
- package/lib/input/editor.d.ts +0 -10
- package/lib/input/editor.js +11 -9
- package/lib/input/editor.js.map +1 -1
- package/lib/input/image-paste.d.ts +19 -1
- package/lib/input/image-paste.js +82 -1
- package/lib/input/image-paste.js.map +1 -1
- package/lib/input/keybindings-config.d.ts +15 -4
- package/lib/input/keybindings-config.js +57 -35
- package/lib/input/keybindings-config.js.map +1 -1
- package/lib/input/keys.d.ts +18 -0
- package/lib/input/keys.js +48 -3
- package/lib/input/keys.js.map +1 -1
- package/lib/input/wheel-scroll.d.ts +46 -0
- package/lib/input/wheel-scroll.js +69 -0
- package/lib/input/wheel-scroll.js.map +1 -0
- package/lib/prompt-intents.d.ts +182 -0
- package/lib/prompt-intents.js +127 -0
- package/lib/prompt-intents.js.map +1 -0
- package/lib/runtime/agent-profile.d.ts +5 -0
- package/lib/runtime/agent-profile.js +12 -0
- package/lib/runtime/agent-profile.js.map +1 -1
- package/lib/runtime/approval-rules.d.ts +59 -0
- package/lib/runtime/approval-rules.js +179 -0
- package/lib/runtime/approval-rules.js.map +1 -0
- package/lib/runtime/btw-policy.d.ts +7 -0
- package/lib/runtime/btw-policy.js +18 -0
- package/lib/runtime/btw-policy.js.map +1 -0
- package/lib/runtime/btw.d.ts +47 -0
- package/lib/runtime/btw.js +200 -0
- package/lib/runtime/btw.js.map +1 -0
- package/lib/runtime/cancellable.d.ts +75 -0
- package/lib/runtime/cancellable.js +76 -0
- package/lib/runtime/cancellable.js.map +1 -0
- package/lib/runtime/composer-images.js +5 -4
- package/lib/runtime/composer-images.js.map +1 -1
- package/lib/runtime/composer-shell.d.ts +30 -0
- package/lib/runtime/composer-shell.js +150 -0
- package/lib/runtime/composer-shell.js.map +1 -0
- package/lib/runtime/file-recovery-local.d.ts +73 -0
- package/lib/runtime/file-recovery-local.js +351 -0
- package/lib/runtime/file-recovery-local.js.map +1 -0
- package/lib/runtime/file-recovery-recording.d.ts +23 -0
- package/lib/runtime/file-recovery-recording.js +131 -0
- package/lib/runtime/file-recovery-recording.js.map +1 -0
- package/lib/runtime/file-recovery-review.d.ts +3 -0
- package/lib/runtime/file-recovery-review.js +130 -0
- package/lib/runtime/file-recovery-review.js.map +1 -0
- package/lib/runtime/file-recovery-store.d.ts +83 -0
- package/lib/runtime/file-recovery-store.js +329 -0
- package/lib/runtime/file-recovery-store.js.map +1 -0
- package/lib/runtime/file-recovery-worker.d.ts +22 -0
- package/lib/runtime/file-recovery-worker.js +153 -0
- package/lib/runtime/file-recovery-worker.js.map +1 -0
- package/lib/runtime/file-review.d.ts +6 -3
- package/lib/runtime/file-review.js +44 -7
- package/lib/runtime/file-review.js.map +1 -1
- package/lib/runtime/herdr-agent.d.ts +4 -13
- package/lib/runtime/herdr-agent.js +19 -63
- package/lib/runtime/herdr-agent.js.map +1 -1
- package/lib/runtime/plain-tui.d.ts +2 -0
- package/lib/runtime/plain-tui.js +7 -1
- package/lib/runtime/plain-tui.js.map +1 -1
- package/lib/runtime/plugin-settings.d.ts +25 -2
- package/lib/runtime/plugin-settings.js +329 -31
- package/lib/runtime/plugin-settings.js.map +1 -1
- package/lib/runtime/provider-local.d.ts +81 -24
- package/lib/runtime/provider-local.js +1807 -608
- package/lib/runtime/provider-local.js.map +1 -1
- package/lib/runtime/runner.js +52 -12
- package/lib/runtime/runner.js.map +1 -1
- package/lib/runtime/session-archive.d.ts +6 -0
- package/lib/runtime/session-archive.js +129 -3
- package/lib/runtime/session-archive.js.map +1 -1
- package/lib/runtime/session-runtime.js +1 -1
- package/lib/runtime/session-runtime.js.map +1 -1
- package/lib/runtime/settings-preferences.d.ts +5 -0
- package/lib/runtime/settings-preferences.js +34 -0
- package/lib/runtime/settings-preferences.js.map +1 -0
- package/lib/runtime/startup-notices.js +2 -0
- package/lib/runtime/startup-notices.js.map +1 -1
- package/lib/runtime/terminal-modes.d.ts +47 -0
- package/lib/runtime/terminal-modes.js +86 -0
- package/lib/runtime/terminal-modes.js.map +1 -0
- package/lib/runtime/terminal-notifications.d.ts +3 -5
- package/lib/runtime/terminal-notifications.js +4 -8
- package/lib/runtime/terminal-notifications.js.map +1 -1
- package/lib/runtime/terminal-probe.d.ts +36 -0
- package/lib/runtime/terminal-probe.js +37 -0
- package/lib/runtime/terminal-probe.js.map +1 -0
- package/lib/runtime/terminal-session.d.ts +57 -0
- package/lib/runtime/terminal-session.js +164 -0
- package/lib/runtime/terminal-session.js.map +1 -0
- package/lib/runtime/terminal-state.d.ts +29 -0
- package/lib/runtime/terminal-state.js +62 -0
- package/lib/runtime/terminal-state.js.map +1 -0
- package/lib/runtime/tool-presentation.js +50 -14
- package/lib/runtime/tool-presentation.js.map +1 -1
- package/lib/session/external-agents.d.ts +5 -0
- package/lib/session/external-agents.js +27 -0
- package/lib/session/external-agents.js.map +1 -0
- package/lib/session/feature-toggles.d.ts +1 -1
- package/lib/session/feature-toggles.js +1 -1
- package/lib/session/interaction-adapter.d.ts +1 -6
- package/lib/session/interaction-adapter.js +95 -18
- package/lib/session/interaction-adapter.js.map +1 -1
- package/lib/session/message-queue.js +1 -1
- package/lib/session/message-queue.js.map +1 -1
- package/lib/session/model-readiness.d.ts +16 -0
- package/lib/session/model-readiness.js +43 -0
- package/lib/session/model-readiness.js.map +1 -0
- package/lib/session/package-metadata.d.ts +8 -0
- package/lib/session/package-metadata.js +8 -0
- package/lib/session/package-metadata.js.map +1 -1
- package/lib/session/project-context.d.ts +5 -0
- package/lib/session/project-context.js +36 -0
- package/lib/session/project-context.js.map +1 -1
- package/lib/session/session-controller.d.ts +10 -13
- package/lib/session/session-controller.js +183 -98
- package/lib/session/session-controller.js.map +1 -1
- package/lib/session/session-library.js +2 -0
- package/lib/session/session-library.js.map +1 -1
- package/lib/session/session-tree.d.ts +3 -0
- package/lib/session/session-tree.js +3 -1
- package/lib/session/session-tree.js.map +1 -1
- package/lib/session/subagent-roster.d.ts +2 -1
- package/lib/session/subagent-roster.js +24 -3
- package/lib/session/subagent-roster.js.map +1 -1
- package/lib/session/subagent-tracker.d.ts +6 -2
- package/lib/session/subagent-tracker.js +49 -2
- package/lib/session/subagent-tracker.js.map +1 -1
- package/lib/session/tui-settings.d.ts +47 -13
- package/lib/session/tui-settings.js +76 -34
- package/lib/session/tui-settings.js.map +1 -1
- package/lib/session/working-directory.d.ts +4 -0
- package/lib/session/working-directory.js +4 -0
- package/lib/session/working-directory.js.map +1 -0
- package/lib/session/worktrees.d.ts +13 -0
- package/lib/session/worktrees.js +49 -0
- package/lib/session/worktrees.js.map +1 -0
- package/lib/views/approval-rule.d.ts +3 -0
- package/lib/views/approval-rule.js +4 -0
- package/lib/views/approval-rule.js.map +1 -0
- package/lib/views/at-complete.d.ts +8 -0
- package/lib/views/at-complete.js +64 -2
- package/lib/views/at-complete.js.map +1 -1
- package/lib/views/autocomplete.d.ts +22 -2
- package/lib/views/autocomplete.js +70 -40
- package/lib/views/autocomplete.js.map +1 -1
- package/lib/views/copy-targets.d.ts +13 -14
- package/lib/views/copy-targets.js +42 -54
- package/lib/views/copy-targets.js.map +1 -1
- package/lib/views/document-reader.js +18 -5
- package/lib/views/document-reader.js.map +1 -1
- package/lib/views/event-views.d.ts +1 -1
- package/lib/views/event-views.js +60 -6
- package/lib/views/event-views.js.map +1 -1
- package/lib/views/file-actions.d.ts +12 -0
- package/lib/views/file-actions.js +72 -0
- package/lib/views/file-actions.js.map +1 -0
- package/lib/views/history-search.js +15 -21
- package/lib/views/history-search.js.map +1 -1
- package/lib/views/hotkeys.d.ts +8 -1
- package/lib/views/hotkeys.js +53 -26
- package/lib/views/hotkeys.js.map +1 -1
- package/lib/views/list-navigation.d.ts +62 -0
- package/lib/views/list-navigation.js +73 -0
- package/lib/views/list-navigation.js.map +1 -0
- package/lib/views/list-view.d.ts +99 -0
- package/lib/views/list-view.js +165 -0
- package/lib/views/list-view.js.map +1 -0
- package/lib/views/prompt-selector.d.ts +34 -4
- package/lib/views/prompt-selector.js +270 -134
- package/lib/views/prompt-selector.js.map +1 -1
- package/lib/views/session-files.d.ts +1 -2
- package/lib/views/session-files.js +1 -11
- package/lib/views/session-files.js.map +1 -1
- package/lib/views/settings-list.d.ts +51 -10
- package/lib/views/settings-list.js +267 -315
- package/lib/views/settings-list.js.map +1 -1
- package/lib/views/trajectory-timeline.d.ts +72 -0
- package/lib/views/trajectory-timeline.js +133 -0
- package/lib/views/trajectory-timeline.js.map +1 -0
- package/lib/views/trajectory.d.ts +15 -4
- package/lib/views/trajectory.js +557 -123
- package/lib/views/trajectory.js.map +1 -1
- package/lib/views/transcript-export.js +9 -0
- package/lib/views/transcript-export.js.map +1 -1
- package/lib/views/transcript-render.d.ts +97 -25
- package/lib/views/transcript-render.js +542 -170
- package/lib/views/transcript-render.js.map +1 -1
- package/lib/views/transcript-search.d.ts +39 -6
- package/lib/views/transcript-search.js +46 -11
- package/lib/views/transcript-search.js.map +1 -1
- package/lib/views/transcript-types.d.ts +63 -5
- package/lib/views/transcript-types.js +23 -11
- package/lib/views/transcript-types.js.map +1 -1
- package/package.json +95 -65
- package/lib/commands/loop.d.ts +0 -52
- package/lib/commands/loop.js +0 -307
- package/lib/commands/loop.js.map +0 -1
- package/lib/commands/steer.d.ts +0 -5
- package/lib/commands/steer.js +0 -24
- package/lib/commands/steer.js.map +0 -1
- package/lib/runtime/terminal-reader.d.ts +0 -11
- package/lib/runtime/terminal-reader.js +0 -87
- package/lib/runtime/terminal-reader.js.map +0 -1
- package/lib/views/agent-hub.d.ts +0 -32
- package/lib/views/agent-hub.js +0 -206
- package/lib/views/agent-hub.js.map +0 -1
- package/lib/views/copy-selector.d.ts +0 -51
- package/lib/views/copy-selector.js +0 -139
- package/lib/views/copy-selector.js.map +0 -1
package/lib/commands/auth.js
CHANGED
|
@@ -2,98 +2,26 @@
|
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
3
|
import process from 'node:process';
|
|
4
4
|
import { AuthorizationError, } from '@deepseek-ai/dsh-authorization';
|
|
5
|
-
import {
|
|
5
|
+
import { credentialRef, } 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";
|
|
9
|
+
import { authorizationFlows, catalogForRecord, catalogProvidersWithoutFlows, fallbackDeepSeekEntry, flowChoiceValue, isAccountFlow, isDeepSeek, isPiAiDeepSeekFlow, loginAuthorizationFlows, logoutableProviders, parseFlowChoice, profileApiKeyEnv, providerListId, } from "./auth-providers.js";
|
|
10
|
+
import { askPrompt } from "../prompt-intents.js";
|
|
11
|
+
import { CUSTOM_PROTOCOLS, CUSTOM_PROVIDER_VALUE, DEEPSEEK_API_KEYS_URL, DEEPSEEK_MODELS_URL, managedProviderCredentialRef, normalizeApiKey, normalizeDeepSeekApiKey, parseBaseURL, parseModelIds, parseProviderId, validateDeepSeekApiKey, } from "./auth-input.js";
|
|
12
|
+
export { CUSTOM_PROTOCOLS, CUSTOM_PROVIDER_VALUE, DEEPSEEK_API_KEYS_URL, DEEPSEEK_MODELS_URL, managedProviderCredentialRef, normalizeApiKey, normalizeDeepSeekApiKey, parseBaseURL, parseModelIds, parseProviderId, validateDeepSeekApiKey, };
|
|
8
13
|
export const name = 'omdsh-command-auth';
|
|
9
14
|
export const inject = ['commands', 'credentials', 'settings', 'tui', 'llm'];
|
|
10
15
|
export const DEEPSEEK_PROVIDER = 'deepseek-official';
|
|
11
16
|
export const DEEPSEEK_API_KEY = credentialRef('DEEPSEEK_API_KEY');
|
|
12
|
-
export const OMDSH_DEEPSEEK_API_KEY = credentialRef('OMDSH_DEEPSEEK_API_KEY');
|
|
13
17
|
export const DEEPSEEK_SETTINGS = 'llm-deepseek';
|
|
14
18
|
export const PI_AI_SETTINGS = 'llm-pi-ai';
|
|
15
|
-
|
|
16
|
-
export const
|
|
17
|
-
|
|
19
|
+
/** Sentinel the provider picker returns for "sign in to the DeepSeek account". */
|
|
20
|
+
export const DEEPSEEK_ACCOUNT_CHOICE = '__omdsh_deepseek_account__';
|
|
21
|
+
/** Prefix a picker value carries when it names an authorization flow. */
|
|
18
22
|
export const FLOW_VALUE_PREFIX = '__omdsh_flow__:';
|
|
19
|
-
|
|
20
|
-
const FULLSCREEN_CHOICE_THRESHOLD = 8;
|
|
23
|
+
/** Default when the credential store has nothing to say about a provider. */
|
|
21
24
|
const UNCONFIGURED = { configured: false, writable: true };
|
|
22
|
-
const PROVIDER_ID = /^[a-z][a-z0-9-]*$/u;
|
|
23
|
-
const CUSTOM_PROTOCOLS = ['openai-completions', 'openai-responses', 'anthropic-messages'];
|
|
24
|
-
/** Match DeepSeek's own login normalization without accepting an empty Bearer value. */
|
|
25
|
-
export function normalizeDeepSeekApiKey(raw) {
|
|
26
|
-
return normalizeApiKey(raw, 'DeepSeek');
|
|
27
|
-
}
|
|
28
|
-
/** Trim a pasted key and reject an empty Bearer value. */
|
|
29
|
-
export function normalizeApiKey(raw, providerLabel) {
|
|
30
|
-
const normalized = raw.trim().replace(/^bearer\b\s*/iu, '');
|
|
31
|
-
if (normalized === '')
|
|
32
|
-
throw new Error(`Paste a non-empty ${providerLabel} API key.`);
|
|
33
|
-
return normalized;
|
|
34
|
-
}
|
|
35
|
-
/** omdsh-managed credential reference for one catalog provider route. */
|
|
36
|
-
export function managedProviderCredentialRef(provider) {
|
|
37
|
-
return credentialRef(`OMDSH_${provider.toUpperCase().replace(/[^A-Z0-9]+/gu, '_')}_API_KEY`);
|
|
38
|
-
}
|
|
39
|
-
/** Lowercase kebab-case route id used as the permanent provider key. */
|
|
40
|
-
export function parseProviderId(raw) {
|
|
41
|
-
const id = raw.trim().toLowerCase();
|
|
42
|
-
if (!PROVIDER_ID.test(id)) {
|
|
43
|
-
throw new Error('Provider id must start with a letter and use only lowercase letters, digits, and hyphens.');
|
|
44
|
-
}
|
|
45
|
-
if (id === 'custom' || id === CUSTOM_PROVIDER_VALUE) {
|
|
46
|
-
throw new Error('Choose a different provider id.');
|
|
47
|
-
}
|
|
48
|
-
return id;
|
|
49
|
-
}
|
|
50
|
-
/** Absolute http(s) prefix the adapter will call. */
|
|
51
|
-
export function parseBaseURL(raw) {
|
|
52
|
-
const value = raw.trim().replace(/\/+$/u, '');
|
|
53
|
-
let parsed;
|
|
54
|
-
try {
|
|
55
|
-
parsed = new URL(value);
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
throw new Error('Base URL must be an absolute http or https URL.');
|
|
59
|
-
}
|
|
60
|
-
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
61
|
-
throw new Error('Base URL must be an http or https URL.');
|
|
62
|
-
}
|
|
63
|
-
return value;
|
|
64
|
-
}
|
|
65
|
-
/** One or more model ids from a comma- or whitespace-separated list. */
|
|
66
|
-
export function parseModelIds(raw) {
|
|
67
|
-
const ids = [...new Set(raw.split(/[,\s]+/u).map(item => item.trim()).filter(item => item !== ''))];
|
|
68
|
-
if (ids.length === 0)
|
|
69
|
-
throw new Error('Enter at least one model id.');
|
|
70
|
-
return ids;
|
|
71
|
-
}
|
|
72
|
-
/** Validate a candidate without ever reading or returning a response body. */
|
|
73
|
-
export async function validateDeepSeekApiKey(apiKey, signal, fetchImpl = globalThis.fetch) {
|
|
74
|
-
let response;
|
|
75
|
-
try {
|
|
76
|
-
response = await fetchImpl(DEEPSEEK_MODELS_URL, {
|
|
77
|
-
method: 'GET',
|
|
78
|
-
headers: {
|
|
79
|
-
accept: 'application/json',
|
|
80
|
-
authorization: `Bearer ${apiKey}`,
|
|
81
|
-
},
|
|
82
|
-
signal,
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
if (signal.aborted)
|
|
87
|
-
throw new Error('DeepSeek login was cancelled.');
|
|
88
|
-
throw new Error('Could not reach DeepSeek to validate the API key. Check your network and try again.');
|
|
89
|
-
}
|
|
90
|
-
if (response.ok)
|
|
91
|
-
return;
|
|
92
|
-
if (response.status === 401 || response.status === 403) {
|
|
93
|
-
throw new Error('DeepSeek rejected this API key. Copy a valid key from the DeepSeek dashboard and try again.');
|
|
94
|
-
}
|
|
95
|
-
throw new Error(`DeepSeek could not validate the API key (HTTP ${response.status}). Try again later.`);
|
|
96
|
-
}
|
|
97
25
|
function credentialSource(info) {
|
|
98
26
|
if (info.source === 'env')
|
|
99
27
|
return 'the current process environment';
|
|
@@ -115,123 +43,24 @@ function unmanagedCredentialMessage(ref, info, action) {
|
|
|
115
43
|
: 'Update that source directly and restart omdsh.';
|
|
116
44
|
return `${String(ref)} is supplied by ${credentialSource(info)} and /${action === 'replace' ? 'login' : 'logout'} cannot ${action} it. ${instruction}`;
|
|
117
45
|
}
|
|
118
|
-
function
|
|
119
|
-
return entry.provider === DEEPSEEK_PROVIDER;
|
|
120
|
-
}
|
|
121
|
-
function isPiAiLoginable(entry) {
|
|
122
|
-
return entry.settingsNs === 'llm-pi-ai' && entry.provider !== 'deepseek';
|
|
123
|
-
}
|
|
124
|
-
/** Picker labels are route ids; the official adapter keeps the DeepSeek product name. */
|
|
125
|
-
export function providerListId(entry) {
|
|
126
|
-
return isDeepSeek(entry) ? 'DeepSeek' : entry.provider;
|
|
127
|
-
}
|
|
128
|
-
function fallbackDeepSeekEntry() {
|
|
129
|
-
return {
|
|
130
|
-
provider: DEEPSEEK_PROVIDER,
|
|
131
|
-
displayName: 'DeepSeek',
|
|
132
|
-
settingsNs: 'llm-deepseek',
|
|
133
|
-
settingsPath: [],
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
function loginableProviders(ctx) {
|
|
137
|
-
const listed = ctx.llm.listConfigurableProviders();
|
|
138
|
-
const loginable = listed.filter(entry => isDeepSeek(entry) || isPiAiLoginable(entry));
|
|
139
|
-
return loginable.length > 0 ? loginable : [fallbackDeepSeekEntry()];
|
|
140
|
-
}
|
|
141
|
-
function authorizationFlows(ctx) {
|
|
142
|
-
return ctx.get('authorization')?.list() ?? [];
|
|
143
|
-
}
|
|
144
|
-
function isPiAiDeepSeekFlow(flow) {
|
|
145
|
-
return credentialKeyScope(flow.key) === PI_AI_RECORD_SCOPE && credentialKeyId(flow.key) === 'deepseek';
|
|
146
|
-
}
|
|
147
|
-
function loginAuthorizationFlows(ctx) {
|
|
148
|
-
return authorizationFlows(ctx).flatMap(flow => {
|
|
149
|
-
if (!isPiAiDeepSeekFlow(flow))
|
|
150
|
-
return [flow];
|
|
151
|
-
const profile = walkPath(sectionValue(ctx, PI_AI_SETTINGS), ['providers', 'deepseek']);
|
|
152
|
-
if (typeof profile !== 'object' || profile === null || Array.isArray(profile))
|
|
153
|
-
return [];
|
|
154
|
-
return [{ ...flow, label: 'DeepSeek (pi-ai compatibility)' }];
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
function catalogAuthorizationFlow(ctx, provider) {
|
|
158
|
-
const authorization = ctx.get('authorization');
|
|
159
|
-
if (authorization === undefined || !isCredentialKeySegment(provider))
|
|
160
|
-
return undefined;
|
|
161
|
-
return authorization.describe(credentialKey(PI_AI_RECORD_SCOPE, provider));
|
|
162
|
-
}
|
|
163
|
-
function catalogProvidersWithoutFlows(ctx) {
|
|
164
|
-
return loginableProviders(ctx).filter(entry => isDeepSeek(entry) || catalogAuthorizationFlow(ctx, entry.provider) === undefined);
|
|
165
|
-
}
|
|
166
|
-
function flowChoiceValue(key) {
|
|
167
|
-
return FLOW_VALUE_PREFIX + key;
|
|
168
|
-
}
|
|
169
|
-
function parseFlowChoice(raw) {
|
|
170
|
-
if (!raw.startsWith(FLOW_VALUE_PREFIX))
|
|
171
|
-
return undefined;
|
|
172
|
-
try {
|
|
173
|
-
return parseCredentialKey(raw.slice(FLOW_VALUE_PREFIX.length));
|
|
174
|
-
}
|
|
175
|
-
catch {
|
|
176
|
-
return undefined;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
function loginChoiceValues(entries, flows, includeCustom) {
|
|
46
|
+
function loginChoiceValues(entries, flows, includeCustom, includeAccount = false) {
|
|
180
47
|
return [
|
|
48
|
+
...(includeAccount ? [DEEPSEEK_ACCOUNT_CHOICE] : []),
|
|
181
49
|
...entries.map(entry => entry.provider),
|
|
182
50
|
...flows.map(flow => flowChoiceValue(flow.key)),
|
|
183
51
|
...(includeCustom ? [CUSTOM_PROVIDER_VALUE] : []),
|
|
184
52
|
];
|
|
185
53
|
}
|
|
186
|
-
function
|
|
187
|
-
|
|
188
|
-
// profile entry, so the roster reads the same values a form would show.
|
|
189
|
-
return ctx.settings.describe().find(row => String(row.ns) === namespace)?.value;
|
|
190
|
-
}
|
|
191
|
-
function walkPath(root, path) {
|
|
192
|
-
let cursor = root;
|
|
193
|
-
for (const key of path) {
|
|
194
|
-
if (typeof cursor !== 'object' || cursor === null || Array.isArray(cursor))
|
|
195
|
-
return undefined;
|
|
196
|
-
cursor = cursor[key];
|
|
197
|
-
}
|
|
198
|
-
return cursor;
|
|
199
|
-
}
|
|
200
|
-
function profileApiKeyEnv(ctx, entry) {
|
|
201
|
-
const profile = walkPath(sectionValue(ctx, entry.settingsNs), entry.settingsPath);
|
|
202
|
-
if (isDeepSeek(entry)) {
|
|
203
|
-
if (typeof profile !== 'object' || profile === null || Array.isArray(profile))
|
|
204
|
-
return String(DEEPSEEK_API_KEY);
|
|
205
|
-
const apiKeyEnv = profile['apiKeyEnv'];
|
|
206
|
-
return typeof apiKeyEnv === 'string' && apiKeyEnv.trim() !== '' ? apiKeyEnv : String(DEEPSEEK_API_KEY);
|
|
207
|
-
}
|
|
208
|
-
if (typeof profile !== 'object' || profile === null || Array.isArray(profile))
|
|
209
|
-
return undefined;
|
|
210
|
-
const apiKeyEnv = profile['apiKeyEnv'];
|
|
211
|
-
return typeof apiKeyEnv === 'string' && apiKeyEnv.trim() !== '' ? apiKeyEnv : undefined;
|
|
212
|
-
}
|
|
213
|
-
function hasStoredProfile(ctx, entry) {
|
|
214
|
-
if (isDeepSeek(entry))
|
|
215
|
-
return true;
|
|
216
|
-
const profile = walkPath(sectionValue(ctx, entry.settingsNs), entry.settingsPath);
|
|
217
|
-
return typeof profile === 'object' && profile !== null && !Array.isArray(profile);
|
|
218
|
-
}
|
|
219
|
-
function logoutableProviders(ctx) {
|
|
220
|
-
return loginableProviders(ctx).filter(entry => isDeepSeek(entry) || hasStoredProfile(ctx, entry));
|
|
221
|
-
}
|
|
222
|
-
function catalogForRecord(ctx, key) {
|
|
223
|
-
const id = credentialKeyId(key);
|
|
224
|
-
const scope = credentialKeyScope(key);
|
|
225
|
-
return loginableProviders(ctx).find(entry => entry.provider === id && entry.settingsNs === scope);
|
|
226
|
-
}
|
|
227
|
-
function isFlowRecordLogout(choice) {
|
|
228
|
-
return 'key' in choice && 'writable' in choice && !('provider' in choice);
|
|
54
|
+
export function isFlowRecordLogout(choice) {
|
|
55
|
+
return typeof choice !== 'string' && 'key' in choice && 'writable' in choice && !('provider' in choice);
|
|
229
56
|
}
|
|
230
57
|
async function configuredFlowRecords(ctx) {
|
|
231
58
|
const credentials = ctx.credentials;
|
|
232
59
|
const rows = [];
|
|
233
60
|
const claimed = new Set();
|
|
234
61
|
for (const flow of authorizationFlows(ctx)) {
|
|
62
|
+
if (isAccountFlow(flow))
|
|
63
|
+
continue;
|
|
235
64
|
const info = await credentials.describeRecord(flow.key);
|
|
236
65
|
if (!info.configured)
|
|
237
66
|
continue;
|
|
@@ -258,6 +87,9 @@ async function configuredFlowRecords(ctx) {
|
|
|
258
87
|
async function logoutableChoices(ctx) {
|
|
259
88
|
const providers = logoutableProviders(ctx);
|
|
260
89
|
const records = await configuredFlowRecords(ctx);
|
|
90
|
+
const account = accountClient(ctx);
|
|
91
|
+
const accountState = account === undefined ? undefined : await account.getState().catch(() => undefined);
|
|
92
|
+
const accountChoice = accountState?.status === 'credential-stored' ? [DEEPSEEK_ACCOUNT_CHOICE] : [];
|
|
261
93
|
const actionable = [];
|
|
262
94
|
let inactiveDeepSeek;
|
|
263
95
|
for (const entry of providers) {
|
|
@@ -266,19 +98,18 @@ async function logoutableChoices(ctx) {
|
|
|
266
98
|
continue;
|
|
267
99
|
}
|
|
268
100
|
const activeRef = profileApiKeyEnv(ctx, entry) ?? String(DEEPSEEK_API_KEY);
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
if (managed || (activeRef === String(DEEPSEEK_API_KEY) && current.source === 'file')) {
|
|
101
|
+
const current = await ctx.credentials.describe(credentialRef(activeRef));
|
|
102
|
+
if (current.configured && current.source === 'file')
|
|
272
103
|
actionable.push(entry);
|
|
273
|
-
|
|
274
|
-
else {
|
|
104
|
+
else
|
|
275
105
|
inactiveDeepSeek = entry;
|
|
276
|
-
}
|
|
277
106
|
}
|
|
278
|
-
const choices = [...actionable, ...records];
|
|
107
|
+
const choices = [...accountChoice, ...actionable, ...records];
|
|
279
108
|
// Preserve the direct explanatory response when the environment-owned or
|
|
280
109
|
// already-logged-out official route is the only thing the command can see.
|
|
281
|
-
|
|
110
|
+
if (choices.length === 0 && inactiveDeepSeek !== undefined)
|
|
111
|
+
return [inactiveDeepSeek];
|
|
112
|
+
return choices;
|
|
282
113
|
}
|
|
283
114
|
function selected(raw, values) {
|
|
284
115
|
const index = /^\d+$/u.test(raw) ? Number(raw) - 1 : -1;
|
|
@@ -316,15 +147,26 @@ async function pickLoginChoice(ctx, invocation) {
|
|
|
316
147
|
return configured !== 0 ? configured : left.flow.label.localeCompare(right.flow.label);
|
|
317
148
|
});
|
|
318
149
|
const orderedFlows = flowRows.map(row => row.flow);
|
|
319
|
-
const
|
|
320
|
-
const
|
|
150
|
+
const account = ctx.get('deepseekAccount');
|
|
151
|
+
const accountState = account?.getState === undefined ? undefined : await account.getState().catch(() => undefined);
|
|
152
|
+
const includeAccount = account?.getState !== undefined;
|
|
153
|
+
const values = loginChoiceValues(entries, orderedFlows, true, includeAccount);
|
|
321
154
|
const raw = await ctx.tui.prompt({
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
filterable: fullscreen,
|
|
155
|
+
presentation: 'card',
|
|
156
|
+
filterable: true,
|
|
325
157
|
title: 'Provider',
|
|
326
158
|
question: 'Choose a provider to sign in or update',
|
|
327
159
|
options: [
|
|
160
|
+
...(includeAccount ? [{
|
|
161
|
+
label: 'DeepSeek account',
|
|
162
|
+
value: DEEPSEEK_ACCOUNT_CHOICE,
|
|
163
|
+
description: accountState?.status === 'credential-stored'
|
|
164
|
+
? 'signed in · shared with DSH; choose deepseek-account in /model'
|
|
165
|
+
: 'signed out · opens the DeepSeek browser sign-in',
|
|
166
|
+
...(accountState?.status === 'credential-stored'
|
|
167
|
+
? { badge: { label: 'signed in', tone: 'success' } }
|
|
168
|
+
: {}),
|
|
169
|
+
}] : []),
|
|
328
170
|
...entryRows.map(({ entry, badge }) => ({
|
|
329
171
|
label: providerListId(entry),
|
|
330
172
|
value: entry.provider,
|
|
@@ -350,6 +192,7 @@ async function pickLoginChoice(ctx, invocation) {
|
|
|
350
192
|
},
|
|
351
193
|
],
|
|
352
194
|
allowCustom: false,
|
|
195
|
+
notify: false,
|
|
353
196
|
signal: invocation.signal,
|
|
354
197
|
});
|
|
355
198
|
if (raw === null)
|
|
@@ -360,6 +203,8 @@ async function pickLoginChoice(ctx, invocation) {
|
|
|
360
203
|
const chosen = selected(raw, values);
|
|
361
204
|
if (chosen === undefined)
|
|
362
205
|
return undefined;
|
|
206
|
+
if (chosen === DEEPSEEK_ACCOUNT_CHOICE)
|
|
207
|
+
return DEEPSEEK_ACCOUNT_CHOICE;
|
|
363
208
|
const flowKey = parseFlowChoice(chosen);
|
|
364
209
|
if (flowKey !== undefined)
|
|
365
210
|
return orderedFlows.find(flow => flow.key === flowKey);
|
|
@@ -380,6 +225,7 @@ async function pickAuthorizationMethod(ctx, invocation, flow) {
|
|
|
380
225
|
value: method.id,
|
|
381
226
|
})),
|
|
382
227
|
allowCustom: false,
|
|
228
|
+
notify: false,
|
|
383
229
|
signal: invocation.signal,
|
|
384
230
|
});
|
|
385
231
|
if (raw === null)
|
|
@@ -401,7 +247,10 @@ async function loginAuthorization(ctx, invocation, flow) {
|
|
|
401
247
|
const outcome = await authorization.begin({
|
|
402
248
|
key: flow.key,
|
|
403
249
|
method,
|
|
404
|
-
interaction: createAuthorizationInteraction(
|
|
250
|
+
interaction: createAuthorizationInteraction({
|
|
251
|
+
notice: message => { ctx.tui.notice(message); },
|
|
252
|
+
prompt: request => ctx.tui.prompt({ ...request, notify: false }),
|
|
253
|
+
}, `Login to ${flow.label}`),
|
|
405
254
|
signal: invocation.signal,
|
|
406
255
|
});
|
|
407
256
|
if (outcome.status === 'cancelled')
|
|
@@ -423,48 +272,41 @@ async function loginAuthorization(ctx, invocation, flow) {
|
|
|
423
272
|
async function resetDeepSeekCredentialRoute(ctx) {
|
|
424
273
|
await ctx.settings.mutate(DEEPSEEK_SETTINGS, [{ op: 'unset', path: ['apiKeyEnv'] }]);
|
|
425
274
|
}
|
|
426
|
-
|
|
427
|
-
|
|
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() {
|
|
275
|
+
/** Best-effort browser launch; callers always retain the full URL as a fallback. */
|
|
276
|
+
function openExternal(url) {
|
|
438
277
|
const command = process.platform === 'darwin'
|
|
439
|
-
? { file: 'open', args: [
|
|
278
|
+
? { file: 'open', args: [url] }
|
|
440
279
|
: process.platform === 'win32'
|
|
441
|
-
? { file: 'cmd.exe', args: ['/d', '/s', '/c', 'start', '',
|
|
442
|
-
: { file: 'xdg-open', args: [
|
|
280
|
+
? { file: 'cmd.exe', args: ['/d', '/s', '/c', 'start', '', url] }
|
|
281
|
+
: { file: 'xdg-open', args: [url] };
|
|
443
282
|
try {
|
|
444
283
|
const child = spawn(command.file, command.args, { detached: true, stdio: 'ignore' });
|
|
445
284
|
child.once('error', () => undefined);
|
|
446
285
|
child.unref();
|
|
447
286
|
}
|
|
448
287
|
catch {
|
|
449
|
-
// Headless and remote terminals still receive the visible
|
|
288
|
+
// Headless and remote terminals still receive the visible URL.
|
|
450
289
|
}
|
|
451
290
|
}
|
|
291
|
+
/** Best-effort browser launch for the DeepSeek key dashboard. */
|
|
292
|
+
export function openDeepSeekDashboard() {
|
|
293
|
+
openExternal(DEEPSEEK_API_KEYS_URL);
|
|
294
|
+
}
|
|
452
295
|
async function loginDeepSeek(ctx, invocation, config) {
|
|
453
|
-
const current = await ctx.credentials.describe(
|
|
296
|
+
const current = await ctx.credentials.describe(DEEPSEEK_API_KEY);
|
|
454
297
|
if (current.configured && !current.writable) {
|
|
455
|
-
return { kind: 'error', text: unmanagedCredentialMessage(
|
|
298
|
+
return { kind: 'error', text: unmanagedCredentialMessage(DEEPSEEK_API_KEY, current, 'replace') };
|
|
456
299
|
}
|
|
457
300
|
if (config.openDashboard !== false)
|
|
458
301
|
openDeepSeekDashboard();
|
|
459
|
-
const raw = await ctx.tui.prompt({
|
|
302
|
+
const raw = await ctx.tui.prompt(askPrompt({
|
|
460
303
|
title: 'Login to DeepSeek',
|
|
461
304
|
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
|
|
463
|
-
allowCustom: true,
|
|
305
|
+
detail: `Create or copy a key at ${DEEPSEEK_API_KEYS_URL}. It will be validated and stored locally as ${DEEPSEEK_API_KEY}.`,
|
|
464
306
|
secret: true,
|
|
465
307
|
submitLabel: 'validate',
|
|
466
308
|
signal: invocation.signal,
|
|
467
|
-
});
|
|
309
|
+
}));
|
|
468
310
|
if (raw === null)
|
|
469
311
|
return { kind: 'success' };
|
|
470
312
|
let apiKey;
|
|
@@ -478,23 +320,27 @@ async function loginDeepSeek(ctx, invocation, config) {
|
|
|
478
320
|
return { kind: 'error', text: error instanceof Error ? error.message : 'DeepSeek login failed.' };
|
|
479
321
|
}
|
|
480
322
|
try {
|
|
481
|
-
await ctx.credentials.set(
|
|
323
|
+
await ctx.credentials.set(DEEPSEEK_API_KEY, apiKey);
|
|
482
324
|
}
|
|
483
325
|
catch {
|
|
484
326
|
return { kind: 'error', text: 'The API key is valid, but it could not be saved to the Harness credential store.' };
|
|
485
327
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
328
|
+
// A profile may still pin the old product-owned reference. Reset it so the
|
|
329
|
+
// upstream default resolves the key that was just stored.
|
|
330
|
+
if (profileApiKeyEnv(ctx, fallbackDeepSeekEntry()) !== String(DEEPSEEK_API_KEY)) {
|
|
331
|
+
try {
|
|
332
|
+
await resetDeepSeekCredentialRoute(ctx);
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
return {
|
|
336
|
+
kind: 'error',
|
|
337
|
+
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.',
|
|
338
|
+
};
|
|
339
|
+
}
|
|
494
340
|
}
|
|
495
341
|
return {
|
|
496
342
|
kind: 'success',
|
|
497
|
-
text: 'Logged in to DeepSeek.
|
|
343
|
+
text: 'Logged in to DeepSeek. The stored key is used by the deepseek-official route on the next model request.',
|
|
498
344
|
};
|
|
499
345
|
}
|
|
500
346
|
async function loginCatalog(ctx, invocation, entry) {
|
|
@@ -504,15 +350,14 @@ async function loginCatalog(ctx, invocation, entry) {
|
|
|
504
350
|
return { kind: 'error', text: unmanagedCredentialMessage(ref, current, 'replace') };
|
|
505
351
|
}
|
|
506
352
|
const label = entry.displayName;
|
|
507
|
-
const raw = await ctx.tui.prompt({
|
|
353
|
+
const raw = await ctx.tui.prompt(askPrompt({
|
|
508
354
|
title: `Login to ${label}`,
|
|
509
355
|
question: current.configured ? `Paste a new ${label} API key` : `Paste your ${label} API key`,
|
|
510
356
|
detail: `The key is stored locally and activates the ${entry.provider} route for the next model request. Use /model to select it.`,
|
|
511
|
-
allowCustom: true,
|
|
512
357
|
secret: true,
|
|
513
358
|
submitLabel: 'save',
|
|
514
359
|
signal: invocation.signal,
|
|
515
|
-
});
|
|
360
|
+
}));
|
|
516
361
|
if (raw === null)
|
|
517
362
|
return { kind: 'success' };
|
|
518
363
|
let apiKey;
|
|
@@ -563,6 +408,7 @@ async function collectCustomModels(ctx, invocation, draft) {
|
|
|
563
408
|
],
|
|
564
409
|
initialValue: 'enter',
|
|
565
410
|
allowCustom: false,
|
|
411
|
+
notify: false,
|
|
566
412
|
signal: invocation.signal,
|
|
567
413
|
});
|
|
568
414
|
if (source === null)
|
|
@@ -587,8 +433,9 @@ async function collectCustomModels(ctx, invocation, draft) {
|
|
|
587
433
|
})),
|
|
588
434
|
multiSelect: true,
|
|
589
435
|
allowCustom: false,
|
|
590
|
-
|
|
591
|
-
|
|
436
|
+
notify: false,
|
|
437
|
+
filterable: true,
|
|
438
|
+
presentation: 'card',
|
|
592
439
|
signal: invocation.signal,
|
|
593
440
|
});
|
|
594
441
|
if (picked === null)
|
|
@@ -600,37 +447,34 @@ async function collectCustomModels(ctx, invocation, draft) {
|
|
|
600
447
|
if (invocation.signal.aborted)
|
|
601
448
|
return undefined;
|
|
602
449
|
const detail = error instanceof Error ? error.message : 'the endpoint did not list models';
|
|
603
|
-
const fallback = await ctx.tui.prompt({
|
|
450
|
+
const fallback = await ctx.tui.prompt(askPrompt({
|
|
604
451
|
title: 'Custom models',
|
|
605
452
|
question: 'Enter model ids',
|
|
606
453
|
detail: `Could not fetch models (${detail}). Type one or more ids, separated by commas.`,
|
|
607
|
-
allowCustom: true,
|
|
608
454
|
signal: invocation.signal,
|
|
609
|
-
});
|
|
455
|
+
}));
|
|
610
456
|
if (fallback === null)
|
|
611
457
|
return undefined;
|
|
612
458
|
return parseModelIds(fallback);
|
|
613
459
|
}
|
|
614
460
|
}
|
|
615
|
-
const typed = await ctx.tui.prompt({
|
|
461
|
+
const typed = await ctx.tui.prompt(askPrompt({
|
|
616
462
|
title: 'Custom models',
|
|
617
463
|
question: 'Enter model ids',
|
|
618
464
|
detail: 'Separate multiple ids with commas. Example: llama3.1, qwen2.5-coder',
|
|
619
|
-
allowCustom: true,
|
|
620
465
|
signal: invocation.signal,
|
|
621
|
-
});
|
|
466
|
+
}));
|
|
622
467
|
if (typed === null)
|
|
623
468
|
return undefined;
|
|
624
469
|
return parseModelIds(typed);
|
|
625
470
|
}
|
|
626
471
|
async function loginCustom(ctx, invocation) {
|
|
627
|
-
const idRaw = await ctx.tui.prompt({
|
|
472
|
+
const idRaw = await ctx.tui.prompt(askPrompt({
|
|
628
473
|
title: 'Custom provider',
|
|
629
474
|
question: 'Provider id',
|
|
630
475
|
detail: 'Lowercase letters, digits, and hyphens. This id is permanent.',
|
|
631
|
-
allowCustom: true,
|
|
632
476
|
signal: invocation.signal,
|
|
633
|
-
});
|
|
477
|
+
}));
|
|
634
478
|
if (idRaw === null)
|
|
635
479
|
return { kind: 'success' };
|
|
636
480
|
let id;
|
|
@@ -643,13 +487,12 @@ async function loginCustom(ctx, invocation) {
|
|
|
643
487
|
if (reservedProviderIds(ctx).has(id)) {
|
|
644
488
|
return { kind: 'error', text: `${id} is a catalog provider. Use /login and choose it from the list.` };
|
|
645
489
|
}
|
|
646
|
-
const urlRaw = await ctx.tui.prompt({
|
|
490
|
+
const urlRaw = await ctx.tui.prompt(askPrompt({
|
|
647
491
|
title: 'Custom provider',
|
|
648
492
|
question: 'Base URL',
|
|
649
493
|
detail: 'Absolute http(s) prefix, for example http://127.0.0.1:11434/v1',
|
|
650
|
-
allowCustom: true,
|
|
651
494
|
signal: invocation.signal,
|
|
652
|
-
});
|
|
495
|
+
}));
|
|
653
496
|
if (urlRaw === null)
|
|
654
497
|
return { kind: 'success' };
|
|
655
498
|
let baseURL;
|
|
@@ -671,6 +514,7 @@ async function loginCustom(ctx, invocation) {
|
|
|
671
514
|
})),
|
|
672
515
|
initialValue: 'openai-completions',
|
|
673
516
|
allowCustom: false,
|
|
517
|
+
notify: false,
|
|
674
518
|
signal: invocation.signal,
|
|
675
519
|
});
|
|
676
520
|
if (api === null)
|
|
@@ -678,15 +522,14 @@ async function loginCustom(ctx, invocation) {
|
|
|
678
522
|
if (!CUSTOM_PROTOCOLS.includes(api)) {
|
|
679
523
|
return { kind: 'error', text: `Unknown API protocol: ${api}` };
|
|
680
524
|
}
|
|
681
|
-
const keyRaw = await ctx.tui.prompt({
|
|
525
|
+
const keyRaw = await ctx.tui.prompt(askPrompt({
|
|
682
526
|
title: `Login to ${id}`,
|
|
683
527
|
question: `Paste your ${id} API key`,
|
|
684
528
|
detail: 'Leave empty if the endpoint does not need a key.',
|
|
685
|
-
allowCustom: true,
|
|
686
529
|
secret: true,
|
|
687
530
|
submitLabel: 'continue',
|
|
688
531
|
signal: invocation.signal,
|
|
689
|
-
});
|
|
532
|
+
}));
|
|
690
533
|
if (keyRaw === null)
|
|
691
534
|
return { kind: 'success' };
|
|
692
535
|
let apiKey;
|
|
@@ -752,60 +595,88 @@ async function loginCustom(ctx, invocation) {
|
|
|
752
595
|
}
|
|
753
596
|
async function logoutDeepSeek(ctx, invocation) {
|
|
754
597
|
const activeRef = profileApiKeyEnv(ctx, fallbackDeepSeekEntry()) ?? String(DEEPSEEK_API_KEY);
|
|
755
|
-
const
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
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.' };
|
|
598
|
+
const current = await ctx.credentials.describe(credentialRef(activeRef));
|
|
599
|
+
if (!current.configured) {
|
|
600
|
+
if (activeRef !== String(DEEPSEEK_API_KEY)) {
|
|
601
|
+
try {
|
|
602
|
+
await resetDeepSeekCredentialRoute(ctx);
|
|
603
|
+
}
|
|
604
|
+
catch {
|
|
605
|
+
return { kind: 'error', text: 'Could not reset the DeepSeek credential setting.' };
|
|
606
|
+
}
|
|
773
607
|
}
|
|
774
|
-
return
|
|
608
|
+
return { kind: 'success', text: 'DeepSeek is already logged out.' };
|
|
609
|
+
}
|
|
610
|
+
if (current.source !== 'file') {
|
|
611
|
+
return { kind: 'success', text: `DeepSeek is configured by ${credentialSource(current)}; the credential store cannot remove it.` };
|
|
775
612
|
}
|
|
776
|
-
const removesStoredKey = current.source === 'file';
|
|
777
613
|
const answer = await ctx.tui.prompt({
|
|
778
614
|
title: 'Logout from DeepSeek',
|
|
779
|
-
question:
|
|
780
|
-
detail: 'The next model request will
|
|
615
|
+
question: 'Remove the stored DeepSeek API key?',
|
|
616
|
+
detail: 'The next model request will use DEEPSEEK_API_KEY from the launch environment when one is available.',
|
|
781
617
|
options: [
|
|
782
618
|
{ label: 'Cancel', value: 'cancel', description: 'Keep the current credential.' },
|
|
783
619
|
{
|
|
784
620
|
label: 'Log out',
|
|
785
621
|
value: 'logout',
|
|
786
|
-
description:
|
|
787
|
-
? 'Remove the key from the local Harness credential store.'
|
|
788
|
-
: 'Return the provider to its default credential source.',
|
|
622
|
+
description: 'Remove the key from the local Harness credential store.',
|
|
789
623
|
badge: { label: 'removes credential', tone: 'warning' },
|
|
790
624
|
},
|
|
791
625
|
],
|
|
792
626
|
initialValue: 'cancel',
|
|
793
627
|
allowCustom: false,
|
|
628
|
+
notify: false,
|
|
794
629
|
submitLabel: 'apply',
|
|
795
630
|
signal: invocation.signal,
|
|
796
631
|
});
|
|
797
632
|
if (answer !== 'logout')
|
|
798
633
|
return { kind: 'success' };
|
|
799
634
|
try {
|
|
800
|
-
|
|
635
|
+
await ctx.credentials.unset(credentialRef(activeRef));
|
|
636
|
+
if (activeRef !== String(DEEPSEEK_API_KEY))
|
|
801
637
|
await resetDeepSeekCredentialRoute(ctx);
|
|
802
|
-
if (removesStoredKey)
|
|
803
|
-
await ctx.credentials.unset(activeCredential);
|
|
804
638
|
}
|
|
805
639
|
catch {
|
|
806
640
|
return { kind: 'error', text: 'Could not remove the DeepSeek API key from the Harness credential store.' };
|
|
807
641
|
}
|
|
808
|
-
return
|
|
642
|
+
return { kind: 'success', text: 'Logged out from DeepSeek. The next model request falls back to the environment when available.' };
|
|
643
|
+
}
|
|
644
|
+
async function logoutDeepSeekAccount(ctx, invocation) {
|
|
645
|
+
const account = accountClient(ctx);
|
|
646
|
+
if (account === undefined)
|
|
647
|
+
return { kind: 'error', text: 'DeepSeek account integration is not mounted in this build.' };
|
|
648
|
+
let state;
|
|
649
|
+
try {
|
|
650
|
+
state = await account.getState();
|
|
651
|
+
}
|
|
652
|
+
catch {
|
|
653
|
+
return { kind: 'error', text: 'Could not read the shared DeepSeek account state.' };
|
|
654
|
+
}
|
|
655
|
+
if (state.status !== 'credential-stored') {
|
|
656
|
+
return { kind: 'success', text: 'No DeepSeek account grant is stored.' };
|
|
657
|
+
}
|
|
658
|
+
const answer = await ctx.tui.prompt({
|
|
659
|
+
title: 'Logout from DeepSeek account',
|
|
660
|
+
question: 'Sign out of the shared DeepSeek account?',
|
|
661
|
+
detail: 'Removes the local grant and revokes it in the background. The browser session is not signed out.',
|
|
662
|
+
options: [
|
|
663
|
+
{ label: 'Cancel', value: 'cancel', description: 'Keep the account grant.' },
|
|
664
|
+
{ label: 'Sign out', value: 'logout', description: 'Remove the local grant and revoke it in the background.' },
|
|
665
|
+
],
|
|
666
|
+
initialValue: 'cancel',
|
|
667
|
+
allowCustom: false,
|
|
668
|
+
notify: false,
|
|
669
|
+
signal: invocation.signal,
|
|
670
|
+
});
|
|
671
|
+
if (answer !== 'logout')
|
|
672
|
+
return { kind: 'success' };
|
|
673
|
+
try {
|
|
674
|
+
await account.signOut(accountClientMetadata());
|
|
675
|
+
}
|
|
676
|
+
catch {
|
|
677
|
+
return { kind: 'error', text: 'Could not sign out of the DeepSeek account. Try again from DSH Web/Desktop.' };
|
|
678
|
+
}
|
|
679
|
+
return { kind: 'success', text: 'Signed out of the DeepSeek account. The local grant was removed; remote revocation continues in the background.' };
|
|
809
680
|
}
|
|
810
681
|
async function pickLogoutChoice(ctx, invocation) {
|
|
811
682
|
const choices = await logoutableChoices(ctx);
|
|
@@ -813,26 +684,33 @@ async function pickLogoutChoice(ctx, invocation) {
|
|
|
813
684
|
return undefined;
|
|
814
685
|
if (choices.length === 1)
|
|
815
686
|
return choices[0];
|
|
816
|
-
const values = choices.map(choice =>
|
|
817
|
-
|
|
687
|
+
const values = choices.map(choice => choice === DEEPSEEK_ACCOUNT_CHOICE
|
|
688
|
+
? DEEPSEEK_ACCOUNT_CHOICE
|
|
689
|
+
: isFlowRecordLogout(choice) ? flowChoiceValue(choice.key) : choice.provider);
|
|
818
690
|
const raw = await ctx.tui.prompt({
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
filterable: fullscreen,
|
|
691
|
+
presentation: 'card',
|
|
692
|
+
filterable: true,
|
|
822
693
|
title: 'Provider',
|
|
823
694
|
question: 'Choose a provider to log out from',
|
|
824
|
-
options: choices.map(choice =>
|
|
695
|
+
options: choices.map(choice => choice === DEEPSEEK_ACCOUNT_CHOICE
|
|
825
696
|
? {
|
|
826
|
-
label:
|
|
827
|
-
value:
|
|
828
|
-
description:
|
|
697
|
+
label: 'DeepSeek account',
|
|
698
|
+
value: DEEPSEEK_ACCOUNT_CHOICE,
|
|
699
|
+
description: 'shared with DSH · signed in',
|
|
829
700
|
}
|
|
830
|
-
:
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
701
|
+
: isFlowRecordLogout(choice)
|
|
702
|
+
? {
|
|
703
|
+
label: choice.label,
|
|
704
|
+
value: flowChoiceValue(choice.key),
|
|
705
|
+
description: choice.writable ? 'signed in' : 'not removable here',
|
|
706
|
+
}
|
|
707
|
+
: {
|
|
708
|
+
label: providerListId(choice),
|
|
709
|
+
value: choice.provider,
|
|
710
|
+
description: 'signed in',
|
|
711
|
+
}),
|
|
835
712
|
allowCustom: false,
|
|
713
|
+
notify: false,
|
|
836
714
|
signal: invocation.signal,
|
|
837
715
|
});
|
|
838
716
|
if (raw === null)
|
|
@@ -840,10 +718,12 @@ async function pickLogoutChoice(ctx, invocation) {
|
|
|
840
718
|
const chosen = selected(raw, values);
|
|
841
719
|
if (chosen === undefined)
|
|
842
720
|
return undefined;
|
|
721
|
+
if (chosen === DEEPSEEK_ACCOUNT_CHOICE)
|
|
722
|
+
return DEEPSEEK_ACCOUNT_CHOICE;
|
|
843
723
|
const flowKey = parseFlowChoice(chosen);
|
|
844
724
|
if (flowKey !== undefined)
|
|
845
725
|
return choices.find(choice => isFlowRecordLogout(choice) && choice.key === flowKey);
|
|
846
|
-
return choices.find(choice => !isFlowRecordLogout(choice) && choice.provider === chosen);
|
|
726
|
+
return choices.find(choice => typeof choice !== 'string' && !isFlowRecordLogout(choice) && choice.provider === chosen);
|
|
847
727
|
}
|
|
848
728
|
async function logoutRecord(ctx, invocation, record) {
|
|
849
729
|
if (!record.writable) {
|
|
@@ -867,6 +747,7 @@ async function logoutRecord(ctx, invocation, record) {
|
|
|
867
747
|
],
|
|
868
748
|
initialValue: 'cancel',
|
|
869
749
|
allowCustom: false,
|
|
750
|
+
notify: false,
|
|
870
751
|
submitLabel: 'apply',
|
|
871
752
|
signal: invocation.signal,
|
|
872
753
|
});
|
|
@@ -911,6 +792,7 @@ async function logoutCatalog(ctx, invocation, entry) {
|
|
|
911
792
|
],
|
|
912
793
|
initialValue: 'cancel',
|
|
913
794
|
allowCustom: false,
|
|
795
|
+
notify: false,
|
|
914
796
|
submitLabel: 'apply',
|
|
915
797
|
signal: invocation.signal,
|
|
916
798
|
});
|
|
@@ -928,6 +810,156 @@ async function logoutCatalog(ctx, invocation, entry) {
|
|
|
928
810
|
}
|
|
929
811
|
return { kind: 'success', text: `Logged out from ${label}.` };
|
|
930
812
|
}
|
|
813
|
+
function accountClient(ctx) {
|
|
814
|
+
return ctx.get('deepseekAccount');
|
|
815
|
+
}
|
|
816
|
+
function accountClientMetadata() {
|
|
817
|
+
return {
|
|
818
|
+
version: APP_VERSION,
|
|
819
|
+
locale: Intl.DateTimeFormat().resolvedOptions().locale,
|
|
820
|
+
timezoneOffsetSeconds: -new Date().getTimezoneOffset() * 60,
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
function accountCallbackOrigin(ctx) {
|
|
824
|
+
const server = ctx.get('webServer');
|
|
825
|
+
if (server === undefined || typeof server.port !== 'number' || server.port === 0)
|
|
826
|
+
return undefined;
|
|
827
|
+
return `${server.protocol}//${server.host}:${server.port}`;
|
|
828
|
+
}
|
|
829
|
+
function accountFailure(attempt) {
|
|
830
|
+
if (attempt?.phase === 'expired')
|
|
831
|
+
return 'The DeepSeek sign-in expired. Run /login to try again.';
|
|
832
|
+
if (attempt?.phase === 'failed')
|
|
833
|
+
return `DeepSeek sign-in failed${attempt.errorCode === undefined ? '' : ` (${attempt.errorCode})`}. Run /login to try again.`;
|
|
834
|
+
if (attempt?.phase === 'cancelled')
|
|
835
|
+
return 'DeepSeek sign-in was cancelled.';
|
|
836
|
+
return undefined;
|
|
837
|
+
}
|
|
838
|
+
async function waitForAccount(invocation, account, timeoutMs) {
|
|
839
|
+
const deadline = Date.now() + timeoutMs;
|
|
840
|
+
while (Date.now() < deadline) {
|
|
841
|
+
if (invocation.signal.aborted) {
|
|
842
|
+
const state = await account.getState().catch(() => undefined);
|
|
843
|
+
const id = state?.attempt?.id;
|
|
844
|
+
if (id !== undefined)
|
|
845
|
+
await account.cancelSignIn(id).catch(() => undefined);
|
|
846
|
+
return { kind: 'success' };
|
|
847
|
+
}
|
|
848
|
+
const state = await account.getState().catch(() => undefined);
|
|
849
|
+
if (state?.status === 'credential-stored' && state.attempt?.phase === 'succeeded') {
|
|
850
|
+
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.' };
|
|
851
|
+
}
|
|
852
|
+
const failure = accountFailure(state?.attempt);
|
|
853
|
+
if (failure !== undefined)
|
|
854
|
+
return { kind: 'error', text: failure };
|
|
855
|
+
await new Promise(resolve => setTimeout(resolve, 400));
|
|
856
|
+
}
|
|
857
|
+
const state = await account.getState().catch(() => undefined);
|
|
858
|
+
const id = state?.attempt?.id;
|
|
859
|
+
if (id !== undefined)
|
|
860
|
+
await account.cancelSignIn(id).catch(() => undefined);
|
|
861
|
+
return { kind: 'error', text: 'DeepSeek sign-in timed out before the browser completed. Run /login to try again.' };
|
|
862
|
+
}
|
|
863
|
+
async function loginDeepSeekAccount(ctx, invocation, config) {
|
|
864
|
+
const account = accountClient(ctx);
|
|
865
|
+
if (account === undefined)
|
|
866
|
+
return { kind: 'error', text: 'DeepSeek account integration is not mounted in this build.' };
|
|
867
|
+
let state;
|
|
868
|
+
try {
|
|
869
|
+
state = await account.getState();
|
|
870
|
+
}
|
|
871
|
+
catch {
|
|
872
|
+
return { kind: 'error', text: 'Could not read the shared DeepSeek account state.' };
|
|
873
|
+
}
|
|
874
|
+
if (state.status === 'credential-stored') {
|
|
875
|
+
const choice = await ctx.tui.prompt({
|
|
876
|
+
title: 'DeepSeek account',
|
|
877
|
+
question: 'The account is signed in and shared with DSH.',
|
|
878
|
+
detail: 'Use /model to select the deepseek-account route. Signing out removes the local grant; the DSH browser session is revoked in the background.',
|
|
879
|
+
options: [
|
|
880
|
+
{ label: 'Use account route', value: 'use', description: 'Close this prompt; select the route in /model.' },
|
|
881
|
+
{ label: 'Sign out', value: 'out', description: 'Remove the local grant and revoke it in the background.' },
|
|
882
|
+
{ label: 'Cancel', value: 'cancel' },
|
|
883
|
+
],
|
|
884
|
+
initialValue: 'use',
|
|
885
|
+
allowCustom: false,
|
|
886
|
+
notify: false,
|
|
887
|
+
signal: invocation.signal,
|
|
888
|
+
});
|
|
889
|
+
if (choice === null || choice === 'cancel' || choice === 'use') {
|
|
890
|
+
return { kind: 'success', text: 'Use /model to select the deepseek-account route.' };
|
|
891
|
+
}
|
|
892
|
+
try {
|
|
893
|
+
await account.signOut(accountClientMetadata());
|
|
894
|
+
}
|
|
895
|
+
catch {
|
|
896
|
+
return { kind: 'error', text: 'Could not sign out of the DeepSeek account. Try again from DSH Web/Desktop.' };
|
|
897
|
+
}
|
|
898
|
+
return { kind: 'success', text: 'Signed out of the DeepSeek account. The next account request requires sign-in again.' };
|
|
899
|
+
}
|
|
900
|
+
const server = ctx.get('webServer');
|
|
901
|
+
if (server === undefined || ctx.tui.interactive === false) {
|
|
902
|
+
return {
|
|
903
|
+
kind: 'error',
|
|
904
|
+
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.',
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
const origin = accountCallbackOrigin(ctx);
|
|
908
|
+
if (origin === undefined)
|
|
909
|
+
return { kind: 'error', text: 'The loopback sign-in host did not expose a port.' };
|
|
910
|
+
let initial;
|
|
911
|
+
try {
|
|
912
|
+
initial = await account.startSignIn(accountClientMetadata(), origin, 'web');
|
|
913
|
+
}
|
|
914
|
+
catch (error) {
|
|
915
|
+
return { kind: 'error', text: error instanceof Error ? error.message : 'Could not start DeepSeek sign-in.' };
|
|
916
|
+
}
|
|
917
|
+
const attempt = initial.attempt;
|
|
918
|
+
const failure = accountFailure(attempt);
|
|
919
|
+
if (failure !== undefined)
|
|
920
|
+
return { kind: 'error', text: failure };
|
|
921
|
+
let url = attempt?.authorizeUrl;
|
|
922
|
+
for (let i = 0; i < 40 && url === undefined; i += 1) {
|
|
923
|
+
if (invocation.signal.aborted)
|
|
924
|
+
break;
|
|
925
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
926
|
+
const next = await account.getState().catch(() => undefined);
|
|
927
|
+
const nextFailure = accountFailure(next?.attempt);
|
|
928
|
+
if (nextFailure !== undefined)
|
|
929
|
+
return { kind: 'error', text: nextFailure };
|
|
930
|
+
url = next?.attempt?.authorizeUrl;
|
|
931
|
+
}
|
|
932
|
+
if (url === undefined) {
|
|
933
|
+
const id = attempt?.id ?? (await account.getState().catch(() => undefined))?.attempt?.id;
|
|
934
|
+
if (id !== undefined)
|
|
935
|
+
await account.cancelSignIn(id).catch(() => undefined);
|
|
936
|
+
return { kind: 'error', text: 'DeepSeek did not return a browser URL for this sign-in attempt.' };
|
|
937
|
+
}
|
|
938
|
+
if (config.openBrowser !== false)
|
|
939
|
+
openExternal(url);
|
|
940
|
+
const answer = await ctx.tui.prompt({
|
|
941
|
+
title: 'DeepSeek account',
|
|
942
|
+
question: 'Complete sign-in in your browser, then return here.',
|
|
943
|
+
detail: `${url}
|
|
944
|
+
|
|
945
|
+
This terminal is listening on ${origin}/oauth/callback until the attempt ends.`,
|
|
946
|
+
options: [
|
|
947
|
+
{ label: 'I completed sign-in', value: 'done' },
|
|
948
|
+
{ label: 'Cancel', value: 'cancel', description: 'Withdraw this sign-in attempt.' },
|
|
949
|
+
],
|
|
950
|
+
initialValue: 'done',
|
|
951
|
+
allowCustom: false,
|
|
952
|
+
notify: false,
|
|
953
|
+
signal: invocation.signal,
|
|
954
|
+
});
|
|
955
|
+
if (answer !== 'done') {
|
|
956
|
+
const id = attempt?.id ?? (await account.getState().catch(() => undefined))?.attempt?.id;
|
|
957
|
+
if (id !== undefined)
|
|
958
|
+
await account.cancelSignIn(id).catch(() => undefined);
|
|
959
|
+
return { kind: 'success', text: 'DeepSeek sign-in cancelled.' };
|
|
960
|
+
}
|
|
961
|
+
return waitForAccount(invocation, account, 120_000);
|
|
962
|
+
}
|
|
931
963
|
async function login(ctx, invocation, config) {
|
|
932
964
|
if (invocation.rawInput.trim() !== '') {
|
|
933
965
|
return { kind: 'error', text: 'Usage: /login (paste the key only into the protected prompt)' };
|
|
@@ -935,6 +967,8 @@ async function login(ctx, invocation, config) {
|
|
|
935
967
|
const choice = await pickLoginChoice(ctx, invocation);
|
|
936
968
|
if (choice === undefined)
|
|
937
969
|
return { kind: 'success' };
|
|
970
|
+
if (choice === DEEPSEEK_ACCOUNT_CHOICE)
|
|
971
|
+
return loginDeepSeekAccount(ctx, invocation, config);
|
|
938
972
|
if (choice === CUSTOM_PROVIDER_VALUE)
|
|
939
973
|
return loginCustom(ctx, invocation);
|
|
940
974
|
if (isAuthorizationFlow(choice))
|
|
@@ -949,6 +983,8 @@ async function logout(ctx, invocation) {
|
|
|
949
983
|
const choice = await pickLogoutChoice(ctx, invocation);
|
|
950
984
|
if (choice === undefined)
|
|
951
985
|
return { kind: 'success' };
|
|
986
|
+
if (choice === DEEPSEEK_ACCOUNT_CHOICE)
|
|
987
|
+
return logoutDeepSeekAccount(ctx, invocation);
|
|
952
988
|
if (isFlowRecordLogout(choice))
|
|
953
989
|
return logoutRecord(ctx, invocation, choice);
|
|
954
990
|
return isDeepSeek(choice)
|
|
@@ -957,8 +993,19 @@ async function logout(ctx, invocation) {
|
|
|
957
993
|
}
|
|
958
994
|
export function apply(ctx, config = {}) {
|
|
959
995
|
registerCommands(ctx, [
|
|
960
|
-
{
|
|
961
|
-
|
|
996
|
+
{
|
|
997
|
+
name: 'login',
|
|
998
|
+
description: 'Sign in to a provider',
|
|
999
|
+
handler: invocation => login(ctx, invocation, config),
|
|
1000
|
+
},
|
|
1001
|
+
{
|
|
1002
|
+
// Its own command: signing in and signing out are two intentions, and
|
|
1003
|
+
// folding the second into `/login logout` made the reader look for a verb
|
|
1004
|
+
// about leaving inside the one about entering.
|
|
1005
|
+
name: 'logout',
|
|
1006
|
+
description: 'Sign out of a stored provider or account',
|
|
1007
|
+
handler: invocation => logout(ctx, invocation),
|
|
1008
|
+
},
|
|
962
1009
|
], 'omdsh authentication commands');
|
|
963
1010
|
}
|
|
964
1011
|
//# sourceMappingURL=auth.js.map
|