@deepseek-harness-tui/dsh-tui 0.11.2 → 0.12.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/README.md +21 -5
- package/bin/dsh-tui.js +30 -3
- package/cordis.patch.yml +24 -5
- package/guide/dsh-tui-guide/SKILL.md +37 -0
- package/guide/dsh-tui-guide/configuration.en.md +410 -0
- package/guide/dsh-tui-guide/configuration.md +349 -0
- package/guide/dsh-tui-guide/getting-started.en.md +398 -0
- package/guide/dsh-tui-guide/getting-started.md +397 -0
- package/guide/dsh-tui-guide/interaction.en.md +674 -0
- package/guide/dsh-tui-guide/interaction.md +747 -0
- package/guide/dsh-tui-guide/migrate.en.md +166 -0
- package/guide/dsh-tui-guide/migrate.md +132 -0
- package/guide/dsh-tui-guide/plugins.en.md +68 -0
- package/guide/dsh-tui-guide/plugins.md +56 -0
- package/guide/dsh-tui-guide/themes.en.md +192 -0
- package/guide/dsh-tui-guide/themes.md +185 -0
- package/guide/dsh-tui-guide/user-guide.en.md +608 -0
- package/guide/dsh-tui-guide/user-guide.md +586 -0
- package/guide/dsh-tui-guide/vscode.en.md +301 -0
- package/guide/dsh-tui-guide/vscode.md +272 -0
- package/lib/settings.json +5 -5
- package/lib/types/adapter/channel/ui-policy.d.ts +3 -2
- package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -1
- package/lib/types/adapter/channel/ui-policy.js +4 -0
- package/lib/types/adapter/ports/channel-settings.d.ts +2 -1
- package/lib/types/adapter/ports/channel-settings.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-ui.d.ts +29 -2
- package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-view.d.ts +43 -9
- package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
- package/lib/types/components/Chat/JobCard.d.ts +9 -0
- package/lib/types/components/Chat/JobCard.d.ts.map +1 -1
- package/lib/types/components/Chat/JobCard.js +42 -15
- package/lib/types/components/Chat/SubagentMessage.js +6 -6
- package/lib/types/components/JobsPanel.d.ts.map +1 -1
- package/lib/types/components/JobsPanel.js +80 -33
- package/lib/types/components/MessageList.js +3 -3
- package/lib/types/components/PresetPicker.d.ts +4 -0
- package/lib/types/components/PresetPicker.d.ts.map +1 -1
- package/lib/types/components/PresetPicker.js +5 -1
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +12 -1
- package/lib/types/components/StarPrompt.d.ts +6 -0
- package/lib/types/components/StarPrompt.d.ts.map +1 -1
- package/lib/types/components/StarPrompt.js +45 -0
- package/lib/types/components/SubagentCard.d.ts.map +1 -1
- package/lib/types/components/SubagentCard.js +8 -8
- package/lib/types/components/SubagentDetailScene.d.ts.map +1 -1
- package/lib/types/components/SubagentDetailScene.js +108 -10
- package/lib/types/components/WhaleGirl.d.ts +6 -0
- package/lib/types/components/WhaleGirl.d.ts.map +1 -1
- package/lib/types/components/WhaleGirl.js +18 -0
- package/lib/types/components/messages/AssistantThinkingMessage.js +4 -4
- package/lib/types/components/trajectory/MiniWake.d.ts +11 -1
- package/lib/types/components/trajectory/MiniWake.d.ts.map +1 -1
- package/lib/types/components/trajectory/MiniWake.js +9 -3
- package/lib/types/dsh-adapter/channel/external-commands.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/external-commands.js +8 -2
- package/lib/types/dsh-adapter/channel/job-projection.d.ts +1 -0
- package/lib/types/dsh-adapter/channel/job-projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/job-projection.js +88 -3
- package/lib/types/dsh-adapter/channel/mode-actions.d.ts +13 -0
- package/lib/types/dsh-adapter/channel/mode-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/mode-actions.js +48 -7
- package/lib/types/dsh-adapter/channel/mode-permission-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/mode-permission-actions.js +25 -9
- package/lib/types/dsh-adapter/channel/preferences.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/preferences.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/preferences.js +17 -7
- package/lib/types/dsh-adapter/channel/state.d.ts +4 -2
- package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/state.js +1 -1
- package/lib/types/dsh-adapter/channel/subagent-projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/subagent-projection.js +5 -0
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +16 -1
- package/lib/types/dsh-adapter/contract.d.ts +5 -4
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +9 -3
- package/lib/types/dsh-adapter/jobs.d.ts +82 -16
- package/lib/types/dsh-adapter/jobs.d.ts.map +1 -1
- package/lib/types/dsh-adapter/jobs.js +104 -9
- package/lib/types/dsh-adapter/oauth/bonus.d.ts +27 -0
- package/lib/types/dsh-adapter/oauth/bonus.d.ts.map +1 -0
- package/lib/types/dsh-adapter/oauth/bonus.js +69 -0
- package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/command.d.ts +2 -1
- package/lib/types/dsh-adapter/oauth/command.d.ts.map +1 -0
- package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/command.js +7 -4
- package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/credentials.d.ts +13 -10
- package/lib/types/dsh-adapter/oauth/credentials.d.ts.map +1 -0
- package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/credentials.js +61 -23
- package/lib/types/dsh-adapter/oauth/deepseek.d.ts +39 -0
- package/lib/types/dsh-adapter/oauth/deepseek.d.ts.map +1 -0
- package/lib/types/dsh-adapter/oauth/deepseek.js +189 -0
- package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/index.d.ts +24 -16
- package/lib/types/dsh-adapter/oauth/index.d.ts.map +1 -0
- package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/index.js +34 -20
- package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/interaction.d.ts +14 -12
- package/lib/types/dsh-adapter/oauth/interaction.d.ts.map +1 -0
- package/lib/types/dsh-adapter/oauth/interaction.js +283 -0
- package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/opener.d.ts +2 -1
- package/lib/types/dsh-adapter/oauth/opener.d.ts.map +1 -0
- package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/opener.js +1 -1
- package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/pi-ai.d.ts +10 -3
- package/lib/types/dsh-adapter/oauth/pi-ai.d.ts.map +1 -0
- package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/pi-ai.js +8 -3
- package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/profiles.d.ts +6 -3
- package/lib/types/dsh-adapter/oauth/profiles.d.ts.map +1 -0
- package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/profiles.js +11 -3
- package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/service.d.ts +14 -7
- package/lib/types/dsh-adapter/oauth/service.d.ts.map +1 -0
- package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/service.js +82 -17
- package/lib/types/dsh-adapter/plugin.d.ts +8 -1
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +72 -12
- package/lib/types/dsh-adapter/presets.d.ts +6 -4
- package/lib/types/dsh-adapter/presets.d.ts.map +1 -1
- package/lib/types/dsh-adapter/presets.js +6 -4
- package/lib/types/dsh-adapter/providerWizard.d.ts.map +1 -1
- package/lib/types/dsh-adapter/providerWizard.js +6 -2
- package/lib/types/dsh-adapter/subagents.d.ts +1 -0
- package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
- package/lib/types/dsh-adapter/subagents.js +10 -1
- package/lib/types/i18n.d.ts +174 -18
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +53 -11
- package/lib/types/ink/components/App.d.ts.map +1 -1
- package/lib/types/ink/components/App.js +10 -1
- package/lib/types/ink/parse-keypress.d.ts +35 -0
- package/lib/types/ink/parse-keypress.d.ts.map +1 -1
- package/lib/types/ink/parse-keypress.js +611 -83
- package/lib/types/ink/terminal-querier.d.ts +25 -0
- package/lib/types/ink/terminal-querier.d.ts.map +1 -1
- package/lib/types/ink/terminal-querier.js +42 -0
- package/lib/types/ink/update-overflow-guard.d.ts +24 -0
- package/lib/types/ink/update-overflow-guard.d.ts.map +1 -1
- package/lib/types/ink/update-overflow-guard.js +25 -3
- package/lib/types/minimalUiMode.d.ts +19 -0
- package/lib/types/minimalUiMode.d.ts.map +1 -0
- package/lib/types/minimalUiMode.js +23 -0
- package/lib/types/oauth.d.ts +5 -10
- package/lib/types/oauth.d.ts.map +1 -1
- package/lib/types/oauth.js +5 -10
- package/lib/types/screens/Chat.d.ts +4 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +27 -7
- package/lib/types/screens/StatusLine.d.ts +4 -0
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +12 -12
- package/lib/types/settings/definitions.js +4 -4
- package/lib/types/tips.d.ts.map +1 -1
- package/lib/types/tips.js +10 -4
- package/lib/types/update.js +2 -2
- package/lib/types/utils/pastedImagePath.d.ts +8 -0
- package/lib/types/utils/pastedImagePath.d.ts.map +1 -1
- package/lib/types/utils/pastedImagePath.js +29 -5
- package/package.json +111 -100
- package/presets/liangshen/agent.cordis.yml +4 -1
- package/presets/liangshen/preset.yml +1 -1
- package/lib/types/minimalMode.d.ts +0 -13
- package/lib/types/minimalMode.d.ts.map +0 -1
- package/lib/types/minimalMode.js +0 -17
- package/node_modules/@deepseek-harness-tui/dsh-auth/LICENSE +0 -21
- package/node_modules/@deepseek-harness-tui/dsh-auth/README.md +0 -143
- package/node_modules/@deepseek-harness-tui/dsh-auth/cordis.patch.yml +0 -11
- package/node_modules/@deepseek-harness-tui/dsh-auth/dsh-plugin.json +0 -44
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/interaction.js +0 -193
- package/node_modules/@deepseek-harness-tui/dsh-auth/package.json +0 -69
|
@@ -8,17 +8,11 @@ const STATUS_SUFFIX_PATTERN = /^\s*\[status:\s/;
|
|
|
8
8
|
function isTerminal(status) {
|
|
9
9
|
return status === 'completed' || status === 'killed' || status === 'failed';
|
|
10
10
|
}
|
|
11
|
-
/**
|
|
12
|
-
* Ordered store of the current conversation's background jobs. Fed by
|
|
13
|
-
* {@link BackgroundJobStore.replace} with a fresh `list()` after every
|
|
14
|
-
* `onJobsChanged` commit, and by {@link BackgroundJobStore.onOutputSeen}
|
|
15
|
-
* with the tail of every `job_output` tool result. Emits no React state of
|
|
16
|
-
* its own — the channel wires the events into its version bump, exactly
|
|
17
|
-
* like the subagent projection.
|
|
18
|
-
*/
|
|
19
11
|
export class BackgroundJobStore {
|
|
20
12
|
events;
|
|
21
13
|
jobs = new Map();
|
|
14
|
+
/** Kernel-ring read state per job id (own cursor — never the model's). */
|
|
15
|
+
kernelReads = new Map();
|
|
22
16
|
/** Commands captured from start acks that arrived before the registry
|
|
23
17
|
* registered the job (the tool/result stream and the registry commit can
|
|
24
18
|
* race); consumed on registration. */
|
|
@@ -52,16 +46,24 @@ export class BackgroundJobStore {
|
|
|
52
46
|
...(command === undefined ? {} : { command }),
|
|
53
47
|
status: snap.status,
|
|
54
48
|
...(snap.detail === undefined ? {} : { detail: snap.detail }),
|
|
49
|
+
...(snap.progress === undefined ? {} : { progress: snap.progress }),
|
|
55
50
|
startedAt: snap.startedAt,
|
|
56
51
|
...(snap.finishedAt === undefined ? {} : { finishedAt: snap.finishedAt }),
|
|
57
52
|
outputLines: [],
|
|
53
|
+
...(snap.output === undefined ? {} : {
|
|
54
|
+
outputTotalBytes: snap.output.total,
|
|
55
|
+
...(snap.output.spillPaths === undefined || snap.output.spillPaths.length === 0 ? {} : { spillPaths: [...snap.output.spillPaths] }),
|
|
56
|
+
}),
|
|
58
57
|
});
|
|
58
|
+
if (snap.output !== undefined)
|
|
59
|
+
this.seedKernelRead(snap.id, snap.output.earliest);
|
|
59
60
|
changed = true;
|
|
60
61
|
continue;
|
|
61
62
|
}
|
|
62
63
|
if (prev.status === snap.status &&
|
|
63
64
|
prev.detail === snap.detail &&
|
|
64
65
|
prev.label === snap.label &&
|
|
66
|
+
prev.progress === snap.progress &&
|
|
65
67
|
prev.finishedAt === snap.finishedAt)
|
|
66
68
|
continue;
|
|
67
69
|
const wasLive = !isTerminal(prev.status);
|
|
@@ -71,10 +73,20 @@ export class BackgroundJobStore {
|
|
|
71
73
|
delete prev.detail;
|
|
72
74
|
else
|
|
73
75
|
prev.detail = snap.detail;
|
|
76
|
+
if (snap.progress === undefined)
|
|
77
|
+
delete prev.progress;
|
|
78
|
+
else
|
|
79
|
+
prev.progress = snap.progress;
|
|
74
80
|
if (snap.finishedAt === undefined)
|
|
75
81
|
delete prev.finishedAt;
|
|
76
82
|
else
|
|
77
83
|
prev.finishedAt = snap.finishedAt;
|
|
84
|
+
if (snap.output !== undefined) {
|
|
85
|
+
prev.outputTotalBytes = snap.output.total;
|
|
86
|
+
if (snap.output.spillPaths !== undefined && snap.output.spillPaths.length > 0)
|
|
87
|
+
prev.spillPaths = [...snap.output.spillPaths];
|
|
88
|
+
this.seedKernelRead(snap.id, snap.output.earliest);
|
|
89
|
+
}
|
|
78
90
|
changed = true;
|
|
79
91
|
if (wasLive && isTerminal(snap.status))
|
|
80
92
|
this.events.onSettled?.(prev);
|
|
@@ -132,13 +144,95 @@ export class BackgroundJobStore {
|
|
|
132
144
|
const lines = text
|
|
133
145
|
.split(/\r?\n/)
|
|
134
146
|
.map(line => line.replace(/\s+$/, ''))
|
|
135
|
-
.filter(line => line !== '' && !STATUS_SUFFIX_PATTERN.test(line))
|
|
147
|
+
.filter(line => line !== '' && !STATUS_SUFFIX_PATTERN.test(line))
|
|
148
|
+
.map(text => ({ text }));
|
|
136
149
|
if (lines.length === 0)
|
|
137
150
|
return;
|
|
138
151
|
job.outputLines = [...job.outputLines, ...lines].slice(-JOBS_MAX_OUTPUT_LINES);
|
|
139
152
|
job.lastOutputAt = at;
|
|
140
153
|
this.events.onChanged?.();
|
|
141
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Seed the kernel-ring cursor for one job at its retained head. Called from
|
|
157
|
+
* replace() whenever a snapshot carries ring facts — before any output
|
|
158
|
+
* pull — so the first readAt starts at `earliest`, never at 0 (bytes
|
|
159
|
+
* before `earliest` are already evicted).
|
|
160
|
+
*/
|
|
161
|
+
seedKernelRead(id, earliest) {
|
|
162
|
+
const read = this.kernelReads.get(id);
|
|
163
|
+
if (read === undefined)
|
|
164
|
+
this.kernelReads.set(id, { cursor: earliest, partial: '' });
|
|
165
|
+
else if (read.cursor < earliest) {
|
|
166
|
+
// Ring evicted past our cursor: resync and mark the discontinuity.
|
|
167
|
+
read.cursor = earliest;
|
|
168
|
+
read.partial = '';
|
|
169
|
+
const job = this.jobs.get(id);
|
|
170
|
+
if (job !== undefined)
|
|
171
|
+
job.outputDropped = true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Pull one non-consuming increment from the kernel ring after an `output`
|
|
176
|
+
* event. Chunks split into lines; a chunk without a trailing newline parks
|
|
177
|
+
* its tail until the next pull (producers append streaming bytes, so line
|
|
178
|
+
* boundaries routinely straddle chunks). `gapBefore`/`lossy` mark bytes
|
|
179
|
+
* lost before the retained tail — surfaced as the dropped banner.
|
|
180
|
+
*/
|
|
181
|
+
onKernelOutput(id, read, at = Date.now()) {
|
|
182
|
+
const job = this.jobs.get(id);
|
|
183
|
+
if (job === undefined)
|
|
184
|
+
return;
|
|
185
|
+
const state = this.kernelReads.get(id) ?? { cursor: 0, partial: '' };
|
|
186
|
+
this.kernelReads.set(id, state);
|
|
187
|
+
const appended = [];
|
|
188
|
+
let gapPending = read.lossy === true;
|
|
189
|
+
let gapSeen = read.lossy === true;
|
|
190
|
+
for (const chunk of read.chunks) {
|
|
191
|
+
if (chunk.gapBefore === true) {
|
|
192
|
+
gapPending = true;
|
|
193
|
+
gapSeen = true;
|
|
194
|
+
}
|
|
195
|
+
let text = state.partial + chunk.text;
|
|
196
|
+
state.partial = '';
|
|
197
|
+
// A \r not followed by \n is a same-line rewrite (progress spinners);
|
|
198
|
+
// keep the tail after the last one so the panel shows the newest form.
|
|
199
|
+
const lines = text.split(/\r?\n/);
|
|
200
|
+
if (!text.endsWith('\n')) {
|
|
201
|
+
state.partial = lines.pop() ?? '';
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
lines.pop();
|
|
205
|
+
}
|
|
206
|
+
for (const raw of lines) {
|
|
207
|
+
const trimmed = raw.replace(/\s+$/, '');
|
|
208
|
+
if (trimmed === '')
|
|
209
|
+
continue;
|
|
210
|
+
appended.push({ text: trimmed, ...(chunk.channel === undefined ? {} : { channel: chunk.channel }), ...(gapPending ? { gapBefore: true } : {}) });
|
|
211
|
+
gapPending = false;
|
|
212
|
+
}
|
|
213
|
+
if (state.partial !== '') {
|
|
214
|
+
// Re-split parked tail the same way so an embedded \r refreshes it.
|
|
215
|
+
const parts = state.partial.split(/\r(?!\n)/);
|
|
216
|
+
state.partial = parts[parts.length - 1];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
state.cursor = read.next;
|
|
220
|
+
if (gapSeen)
|
|
221
|
+
job.outputDropped = true;
|
|
222
|
+
if (appended.length > 0) {
|
|
223
|
+
job.outputLines = [...job.outputLines, ...appended].slice(-JOBS_MAX_OUTPUT_LINES);
|
|
224
|
+
job.lastOutputAt = at;
|
|
225
|
+
this.events.onChanged?.();
|
|
226
|
+
}
|
|
227
|
+
else if (job.outputTotalBytes !== read.next) {
|
|
228
|
+
this.events.onChanged?.();
|
|
229
|
+
}
|
|
230
|
+
job.outputTotalBytes = Math.max(job.outputTotalBytes ?? 0, read.next);
|
|
231
|
+
}
|
|
232
|
+
/** Kernel read state accessor for tests and cursor diagnostics. */
|
|
233
|
+
kernelCursorOf(id) {
|
|
234
|
+
return this.kernelReads.get(id)?.cursor;
|
|
235
|
+
}
|
|
142
236
|
/** A fresh snapshot in registration order (newest tracked job last). */
|
|
143
237
|
snapshot() {
|
|
144
238
|
return [...this.jobs.values()];
|
|
@@ -160,6 +254,7 @@ export class BackgroundJobStore {
|
|
|
160
254
|
if (this.jobs.size === 0)
|
|
161
255
|
return;
|
|
162
256
|
this.jobs.clear();
|
|
257
|
+
this.kernelReads.clear();
|
|
163
258
|
this.events.onChanged?.();
|
|
164
259
|
}
|
|
165
260
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { DeepSeekAccount, AccountBonusOrderId, AccountUserId } from '@deepseek-ai/dsh-deepseek-account';
|
|
2
|
+
export interface WhaleCouponNotice {
|
|
3
|
+
readonly accountId: AccountUserId;
|
|
4
|
+
readonly orderId: AccountBonusOrderId;
|
|
5
|
+
readonly amount: string;
|
|
6
|
+
readonly currency: 'CNY' | 'USD';
|
|
7
|
+
readonly expiresAt: string;
|
|
8
|
+
}
|
|
9
|
+
type BonusAccount = Pick<DeepSeekAccount, 'getUnnotifiedBonuses' | 'ackBonusNotified'>;
|
|
10
|
+
/** TUI-only presentation of a Platform-confirmed login grant. */
|
|
11
|
+
export declare class WhaleCouponStore {
|
|
12
|
+
private readonly listeners;
|
|
13
|
+
private readonly seen;
|
|
14
|
+
private notice;
|
|
15
|
+
private account;
|
|
16
|
+
private generation;
|
|
17
|
+
readonly subscribe: (listener: () => void) => (() => void);
|
|
18
|
+
readonly getSnapshot: () => WhaleCouponNotice | null;
|
|
19
|
+
refresh(account: BonusAccount): Promise<void>;
|
|
20
|
+
/** Called only after the modal has painted; a read alone never acknowledges. */
|
|
21
|
+
shown(orderId: AccountBonusOrderId): void;
|
|
22
|
+
dismiss(orderId: AccountBonusOrderId): void;
|
|
23
|
+
clear(): void;
|
|
24
|
+
private emit;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=bonus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bonus.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/oauth/bonus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAA;AAG5G,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAA;IACjC,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAA;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,QAAQ,EAAE,KAAK,GAAG,KAAK,CAAA;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,KAAK,YAAY,GAAG,IAAI,CAAC,eAAe,EAAE,sBAAsB,GAAG,kBAAkB,CAAC,CAAA;AAEtF,iEAAiE;AACjE,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwB;IAClD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;IACzC,OAAO,CAAC,MAAM,CAAiC;IAC/C,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,UAAU,CAAI;IAEtB,QAAQ,CAAC,SAAS,GAAI,UAAU,MAAM,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC,CAGxD;IAED,QAAQ,CAAC,WAAW,QAAO,iBAAiB,GAAG,IAAI,CAAe;IAE5D,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBnD,gFAAgF;IAChF,KAAK,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI;IASzC,OAAO,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI;IAS3C,KAAK,IAAI,IAAI;IAQb,OAAO,CAAC,IAAI;CACb"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { deepSeekClientMetadata } from './deepseek.js';
|
|
2
|
+
/** TUI-only presentation of a Platform-confirmed login grant. */
|
|
3
|
+
export class WhaleCouponStore {
|
|
4
|
+
listeners = new Set();
|
|
5
|
+
seen = new Set();
|
|
6
|
+
notice = null;
|
|
7
|
+
account;
|
|
8
|
+
generation = 0;
|
|
9
|
+
subscribe = (listener) => {
|
|
10
|
+
this.listeners.add(listener);
|
|
11
|
+
return () => { this.listeners.delete(listener); };
|
|
12
|
+
};
|
|
13
|
+
getSnapshot = () => this.notice;
|
|
14
|
+
async refresh(account) {
|
|
15
|
+
const generation = ++this.generation;
|
|
16
|
+
if (this.listeners.size === 0)
|
|
17
|
+
return;
|
|
18
|
+
let batch;
|
|
19
|
+
try {
|
|
20
|
+
batch = await account.getUnnotifiedBonuses(deepSeekClientMetadata());
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (generation !== this.generation || batch === null)
|
|
26
|
+
return;
|
|
27
|
+
const granted = batch.bonuses.find(bonus => bonus.campaign === 'dsh_login_bonus'
|
|
28
|
+
&& !this.seen.has(`${batch.accountId}:${bonus.orderId}`)
|
|
29
|
+
&& (Number.isNaN(Date.parse(bonus.expiresAt)) || Date.parse(bonus.expiresAt) > Date.now()));
|
|
30
|
+
if (granted === undefined)
|
|
31
|
+
return;
|
|
32
|
+
this.account = account;
|
|
33
|
+
this.notice = {
|
|
34
|
+
accountId: batch.accountId, orderId: granted.orderId,
|
|
35
|
+
amount: granted.amount, currency: granted.currency, expiresAt: granted.expiresAt,
|
|
36
|
+
};
|
|
37
|
+
this.emit();
|
|
38
|
+
}
|
|
39
|
+
/** Called only after the modal has painted; a read alone never acknowledges. */
|
|
40
|
+
shown(orderId) {
|
|
41
|
+
const notice = this.notice;
|
|
42
|
+
if (notice?.orderId !== orderId || this.account === undefined)
|
|
43
|
+
return;
|
|
44
|
+
const key = `${notice.accountId}:${notice.orderId}`;
|
|
45
|
+
if (this.seen.has(key))
|
|
46
|
+
return;
|
|
47
|
+
this.seen.add(key);
|
|
48
|
+
void this.account.ackBonusNotified(notice.accountId, orderId, deepSeekClientMetadata()).catch(() => false);
|
|
49
|
+
}
|
|
50
|
+
dismiss(orderId) {
|
|
51
|
+
if (this.notice?.orderId !== orderId)
|
|
52
|
+
return;
|
|
53
|
+
this.shown(orderId);
|
|
54
|
+
const account = this.account;
|
|
55
|
+
this.notice = null;
|
|
56
|
+
this.emit();
|
|
57
|
+
if (account !== undefined)
|
|
58
|
+
void this.refresh(account);
|
|
59
|
+
}
|
|
60
|
+
clear() {
|
|
61
|
+
this.generation++;
|
|
62
|
+
this.notice = null;
|
|
63
|
+
this.account = undefined;
|
|
64
|
+
this.seen.clear();
|
|
65
|
+
this.emit();
|
|
66
|
+
}
|
|
67
|
+
emit() { for (const listener of this.listeners)
|
|
68
|
+
listener(); }
|
|
69
|
+
}
|
package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/command.d.ts
RENAMED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The `/auth` command: a textual veneer over the {@link DshAuthApi} — status
|
|
3
3
|
* listing, login (optionally picking the provider interactively), logout.
|
|
4
4
|
*
|
|
5
|
-
* @module dsh-
|
|
5
|
+
* @module @deepseek-harness-tui/dsh-tui/oauth/command
|
|
6
6
|
*/
|
|
7
7
|
import type { CommandInvocation, CommandResult } from '@deepseek-ai/dsh-commands';
|
|
8
8
|
import type { DshAuthApi } from './service.js';
|
|
@@ -12,3 +12,4 @@ import type { DshAuthApi } from './service.js';
|
|
|
12
12
|
* propagate for the host's own error surface.
|
|
13
13
|
*/
|
|
14
14
|
export declare function createAuthCommandHandler(api: DshAuthApi): (invocation: CommandInvocation) => Promise<CommandResult>;
|
|
15
|
+
//# sourceMappingURL=command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/oauth/command.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AACjF,OAAO,KAAK,EAAE,UAAU,EAAuB,MAAM,cAAc,CAAA;AA0BnE;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,UAAU,GAAG,CAAC,UAAU,EAAE,iBAAiB,KAAK,OAAO,CAAC,aAAa,CAAC,CAiCnH"}
|
package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/command.js
RENAMED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
* The `/auth` command: a textual veneer over the {@link DshAuthApi} — status
|
|
3
3
|
* listing, login (optionally picking the provider interactively), logout.
|
|
4
4
|
*
|
|
5
|
-
* @module dsh-
|
|
5
|
+
* @module @deepseek-harness-tui/dsh-tui/oauth/command
|
|
6
6
|
*/
|
|
7
7
|
const USAGE = 'Usage: /auth [status] | /auth login [provider] | /auth logout <provider>';
|
|
8
8
|
function renderStatus(rows) {
|
|
9
9
|
const lines = rows.map(row => {
|
|
10
10
|
const state = row.signedIn
|
|
11
|
-
?
|
|
11
|
+
? row.expiresAt === undefined
|
|
12
|
+
? 'signed in'
|
|
13
|
+
: `signed in — token expires ${new Date(row.expiresAt).toISOString()}`
|
|
12
14
|
: row.expired
|
|
13
15
|
? 'signed in, token expired — /auth login to refresh'
|
|
14
16
|
: 'not signed in';
|
|
@@ -41,8 +43,9 @@ export function createAuthCommandHandler(api) {
|
|
|
41
43
|
const result = await api.login(target, invocation.signal);
|
|
42
44
|
return {
|
|
43
45
|
kind: 'success',
|
|
44
|
-
text: `Signed in to ${result.oauthLabel} (${result.provider})
|
|
45
|
-
+ `${new Date(result.expiresAt).toISOString()}.
|
|
46
|
+
text: `Signed in to ${result.oauthLabel} (${result.provider})`
|
|
47
|
+
+ `${result.expiresAt === undefined ? '' : `; token expires ${new Date(result.expiresAt).toISOString()}`}. `
|
|
48
|
+
+ 'Its models are selectable via /model.',
|
|
46
49
|
};
|
|
47
50
|
}
|
|
48
51
|
catch (error) {
|
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
* one JSON document, one credential per provider id.
|
|
4
4
|
*
|
|
5
5
|
* Writes are atomic (temp file + rename) with 0700 directory / 0600 file
|
|
6
|
-
* permissions best-effort on every platform.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* permissions best-effort on every platform. Mutations share an in-process
|
|
7
|
+
* queue and a cross-process file lock; pi-ai runs its OAuth refresh *inside*
|
|
8
|
+
* {@link CredentialFile.modify}, so the exclusion also keeps concurrent
|
|
9
|
+
* requests from double-refreshing a rotated token. The file is the single
|
|
10
|
+
* source of truth;
|
|
11
11
|
* nothing here ever logs token material, and {@link CredentialFile.describe}
|
|
12
12
|
* reports only non-secret metadata for status surfaces.
|
|
13
13
|
*
|
|
14
|
-
* @module dsh-
|
|
14
|
+
* @module @deepseek-harness-tui/dsh-tui/oauth/credentials
|
|
15
15
|
*/
|
|
16
16
|
import type { PiAiCredential, PiAiCredentialInfo, PiAiCredentialStore } from './pi-ai.js';
|
|
17
17
|
/** The stored credential shape: a pi-ai `OAuthCredential`. */
|
|
@@ -30,10 +30,12 @@ export declare function defaultCredentialsFile(): string;
|
|
|
30
30
|
*/
|
|
31
31
|
export declare class CredentialFile implements PiAiCredentialStore {
|
|
32
32
|
readonly path: string;
|
|
33
|
-
/**
|
|
34
|
-
private
|
|
35
|
-
private
|
|
33
|
+
/** One document needs one read-modify-write queue, regardless of provider. */
|
|
34
|
+
private pending;
|
|
35
|
+
private deviceId;
|
|
36
36
|
constructor(path: string);
|
|
37
|
+
/** Stable per-store UUID for pi-ai's OpenAI ChatGPT agent-host identity. */
|
|
38
|
+
getOrCreateDeviceId(): string;
|
|
37
39
|
/** The stored credential for one provider, possibly expired. */
|
|
38
40
|
read(providerId: string): Promise<PiAiCredential | undefined>;
|
|
39
41
|
/** Stored credential metadata without resolving or exposing secrets. */
|
|
@@ -53,8 +55,9 @@ export declare class CredentialFile implements PiAiCredentialStore {
|
|
|
53
55
|
expiresAt: number;
|
|
54
56
|
expired: boolean;
|
|
55
57
|
}[]>;
|
|
56
|
-
/**
|
|
58
|
+
/** Keep the entire read-modify-rename cycle exclusive across processes. */
|
|
57
59
|
private chain;
|
|
58
60
|
private load;
|
|
59
61
|
private save;
|
|
60
62
|
}
|
|
63
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/oauth/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAEzF,8DAA8D;AAC9D,MAAM,MAAM,qBAAqB,GAAG,OAAO,CAAC,cAAc,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CAAA;AAQ9E,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,qBAAqB,GAAG,SAAS,CAOpF;AAED,+FAA+F;AAC/F,wBAAgB,sBAAsB,IAAI,MAAM,CAK/C;AAKD;;;;;GAKG;AACH,qBAAa,cAAe,YAAW,mBAAmB;IACxD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,8EAA8E;IAC9E,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,QAAQ,CAAoB;gBAExB,IAAI,EAAE,MAAM;IAIxB,4EAA4E;IAC5E,mBAAmB,IAAI,MAAM;IAiC7B,gEAAgE;IAC1D,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IAInE,wEAAwE;IAClE,IAAI,IAAI,OAAO,CAAC,SAAS,kBAAkB,EAAE,CAAC;IAQpD;;;;;OAKG;IACG,MAAM,CACV,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,KAAK,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,GAC/E,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IActC,iDAAiD;IAC3C,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU/C,4EAA4E;IACtE,QAAQ,IAAI,OAAO,CAAC,SAAS;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAY/F,2EAA2E;IAC3E,OAAO,CAAC,KAAK;YAUC,IAAI;YAuCJ,IAAI;CAWnB"}
|
|
@@ -3,19 +3,21 @@
|
|
|
3
3
|
* one JSON document, one credential per provider id.
|
|
4
4
|
*
|
|
5
5
|
* Writes are atomic (temp file + rename) with 0700 directory / 0600 file
|
|
6
|
-
* permissions best-effort on every platform.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* permissions best-effort on every platform. Mutations share an in-process
|
|
7
|
+
* queue and a cross-process file lock; pi-ai runs its OAuth refresh *inside*
|
|
8
|
+
* {@link CredentialFile.modify}, so the exclusion also keeps concurrent
|
|
9
|
+
* requests from double-refreshing a rotated token. The file is the single
|
|
10
|
+
* source of truth;
|
|
11
11
|
* nothing here ever logs token material, and {@link CredentialFile.describe}
|
|
12
12
|
* reports only non-secret metadata for status surfaces.
|
|
13
13
|
*
|
|
14
|
-
* @module dsh-
|
|
14
|
+
* @module @deepseek-harness-tui/dsh-tui/oauth/credentials
|
|
15
15
|
*/
|
|
16
|
+
import { randomUUID } from 'node:crypto';
|
|
16
17
|
import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
17
18
|
import { dirname, join } from 'node:path';
|
|
18
19
|
import { homedir } from 'node:os';
|
|
20
|
+
import { withFileLock } from '@deepseek-ai/dsh-atomic-write';
|
|
19
21
|
/** Narrow an unknown parsed value into a stored credential, or reject it. */
|
|
20
22
|
export function asStoredCredential(value) {
|
|
21
23
|
if (typeof value !== 'object' || value === null)
|
|
@@ -38,6 +40,7 @@ export function defaultCredentialsFile() {
|
|
|
38
40
|
return join(root, 'dsh-auth', 'credentials.json');
|
|
39
41
|
}
|
|
40
42
|
const EMPTY_DOCUMENT = { version: 1, providers: {} };
|
|
43
|
+
const DEVICE_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
|
|
41
44
|
/**
|
|
42
45
|
* The credential file. IO failures throw (loud, naming the path) rather than
|
|
43
46
|
* degrading to an empty store: silently treating a corrupt or unreadable
|
|
@@ -46,12 +49,49 @@ const EMPTY_DOCUMENT = { version: 1, providers: {} };
|
|
|
46
49
|
*/
|
|
47
50
|
export class CredentialFile {
|
|
48
51
|
path;
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
/** One document needs one read-modify-write queue, regardless of provider. */
|
|
53
|
+
pending = Promise.resolve();
|
|
54
|
+
deviceId;
|
|
52
55
|
constructor(path) {
|
|
53
56
|
this.path = path;
|
|
54
57
|
}
|
|
58
|
+
/** Stable per-store UUID for pi-ai's OpenAI ChatGPT agent-host identity. */
|
|
59
|
+
getOrCreateDeviceId() {
|
|
60
|
+
if (this.deviceId !== undefined)
|
|
61
|
+
return this.deviceId;
|
|
62
|
+
const path = join(dirname(this.path), 'device-id');
|
|
63
|
+
let value;
|
|
64
|
+
try {
|
|
65
|
+
value = readFileSync(path, 'utf8');
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (error?.code !== 'ENOENT') {
|
|
69
|
+
throw new Error(`dsh-auth: cannot read device ID file ${path}: ${String(error)}`);
|
|
70
|
+
}
|
|
71
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
72
|
+
try {
|
|
73
|
+
writeFileSync(path, `${randomUUID()}\n`, { flag: 'wx', mode: 0o600 });
|
|
74
|
+
}
|
|
75
|
+
catch (writeError) {
|
|
76
|
+
// A second process may have created the same installation ID first.
|
|
77
|
+
if (writeError?.code !== 'EEXIST') {
|
|
78
|
+
throw new Error(`dsh-auth: cannot write device ID file ${path}: ${String(writeError)}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
value = readFileSync(path, 'utf8');
|
|
83
|
+
}
|
|
84
|
+
catch (readError) {
|
|
85
|
+
throw new Error(`dsh-auth: cannot read device ID file ${path}: ${String(readError)}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const id = value.trim();
|
|
89
|
+
if (!DEVICE_ID_PATTERN.test(id)) {
|
|
90
|
+
throw new Error(`dsh-auth: device ID file ${path} is not a UUID; fix or remove it by hand`);
|
|
91
|
+
}
|
|
92
|
+
this.deviceId = id;
|
|
93
|
+
return id;
|
|
94
|
+
}
|
|
55
95
|
/** The stored credential for one provider, possibly expired. */
|
|
56
96
|
async read(providerId) {
|
|
57
97
|
return (await this.load()).providers[providerId];
|
|
@@ -71,7 +111,7 @@ export class CredentialFile {
|
|
|
71
111
|
* credential. Rejections from `fn` propagate without touching the file.
|
|
72
112
|
*/
|
|
73
113
|
async modify(providerId, fn) {
|
|
74
|
-
return this.chain(
|
|
114
|
+
return this.chain(async () => {
|
|
75
115
|
const document = await this.load();
|
|
76
116
|
const current = document.providers[providerId];
|
|
77
117
|
const replacement = await fn(current);
|
|
@@ -86,7 +126,7 @@ export class CredentialFile {
|
|
|
86
126
|
}
|
|
87
127
|
/** Remove one provider's credential (logout). */
|
|
88
128
|
async delete(providerId) {
|
|
89
|
-
await this.chain(
|
|
129
|
+
await this.chain(async () => {
|
|
90
130
|
const document = await this.load();
|
|
91
131
|
if (!(providerId in document.providers))
|
|
92
132
|
return;
|
|
@@ -107,23 +147,24 @@ export class CredentialFile {
|
|
|
107
147
|
expired: credential.expires <= now,
|
|
108
148
|
}));
|
|
109
149
|
}
|
|
110
|
-
/**
|
|
111
|
-
chain(
|
|
112
|
-
const run =
|
|
113
|
-
|
|
150
|
+
/** Keep the entire read-modify-rename cycle exclusive across processes. */
|
|
151
|
+
chain(operation) {
|
|
152
|
+
const run = this.pending.then(() => {
|
|
153
|
+
mkdirSync(dirname(this.path), { recursive: true, mode: 0o700 });
|
|
154
|
+
// pi-ai may refresh a token over the network while holding this lock.
|
|
155
|
+
return withFileLock(this.path, operation, { waitMs: 120_000 });
|
|
156
|
+
});
|
|
157
|
+
this.pending = run.then(() => undefined, () => undefined);
|
|
114
158
|
return run;
|
|
115
159
|
}
|
|
116
160
|
async load() {
|
|
117
|
-
if (this.cache !== undefined)
|
|
118
|
-
return this.cache;
|
|
119
161
|
let text;
|
|
120
162
|
try {
|
|
121
163
|
text = readFileSync(this.path, 'utf8');
|
|
122
164
|
}
|
|
123
165
|
catch (error) {
|
|
124
166
|
if (error?.code === 'ENOENT') {
|
|
125
|
-
|
|
126
|
-
return this.cache;
|
|
167
|
+
return EMPTY_DOCUMENT;
|
|
127
168
|
}
|
|
128
169
|
throw new Error(`dsh-auth: cannot read credential file ${this.path}: ${String(error)}`);
|
|
129
170
|
}
|
|
@@ -150,13 +191,11 @@ export class CredentialFile {
|
|
|
150
191
|
}
|
|
151
192
|
providers[provider] = credential;
|
|
152
193
|
}
|
|
153
|
-
|
|
154
|
-
return this.cache;
|
|
194
|
+
return { version: 1, providers };
|
|
155
195
|
}
|
|
156
196
|
async save(document) {
|
|
157
197
|
const text = JSON.stringify(document, null, 2) + '\n';
|
|
158
198
|
const directory = dirname(this.path);
|
|
159
|
-
mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
160
199
|
const temporary = join(directory, `.${Math.random().toString(36).slice(2)}.tmp`);
|
|
161
200
|
try {
|
|
162
201
|
writeFileSync(temporary, text, { mode: 0o600 });
|
|
@@ -165,6 +204,5 @@ export class CredentialFile {
|
|
|
165
204
|
catch (error) {
|
|
166
205
|
throw new Error(`dsh-auth: cannot write credential file ${this.path}: ${String(error)}`);
|
|
167
206
|
}
|
|
168
|
-
this.cache = document;
|
|
169
207
|
}
|
|
170
208
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI interaction for the Host-owned DeepSeek account authorization flow.
|
|
3
|
+
*
|
|
4
|
+
* The Host owns PKCE, its callback route, credential persistence, and the
|
|
5
|
+
* `deepseek-account` LLM route. This module supplies a loopback callback
|
|
6
|
+
* origin, mounts the official Host listener when an older bundle patch omits
|
|
7
|
+
* it, and displays the Host's safe attempt state through the existing question
|
|
8
|
+
* panel. It never reads or writes the account grant.
|
|
9
|
+
*/
|
|
10
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
11
|
+
import type { AccountClientMetadata, DeepSeekAccount } from '@deepseek-ai/dsh-deepseek-account';
|
|
12
|
+
import { type AskFn, type QuestionBridgeHelpers } from './interaction.js';
|
|
13
|
+
export declare const DEEPSEEK_ACCOUNT_PROVIDER = "deepseek-account";
|
|
14
|
+
/** Only the account operations this interaction surface consumes. */
|
|
15
|
+
export type DeepSeekAccountAuth = Pick<DeepSeekAccount, 'getState' | 'startSignIn' | 'cancelSignIn' | 'signOut' | 'watch'> & Partial<Pick<DeepSeekAccount, 'getUnnotifiedBonuses' | 'ackBonusNotified'>>;
|
|
16
|
+
/** Resolve late: the account provider is absent on older DSH hosts. */
|
|
17
|
+
export declare function deepSeekAccountFrom(ctx: Context): DeepSeekAccountAuth | undefined;
|
|
18
|
+
/** The Host webServer may be shared, supplied by the TUI row, or mounted as a fallback. */
|
|
19
|
+
export declare function deepSeekCallbackOrigin(ctx: Context): string;
|
|
20
|
+
/**
|
|
21
|
+
* Keep DeepSeek login usable after a profile-only update from a global TUI
|
|
22
|
+
* whose older bundle patch still mounts `dsh-tui-auth` but has no webserver
|
|
23
|
+
* row. The official Host listener is mounted only on the first login that
|
|
24
|
+
* needs it; an existing Web or user-owned listener is never replaced.
|
|
25
|
+
*/
|
|
26
|
+
export declare function createDeepSeekCallbackOriginResolver(ctx: Context): {
|
|
27
|
+
resolve(): Promise<string>;
|
|
28
|
+
dispose(): Promise<void>;
|
|
29
|
+
};
|
|
30
|
+
/** Platform request identity is supplied by the current TUI, not cached by the Host. */
|
|
31
|
+
export declare function deepSeekClientMetadata(): AccountClientMetadata;
|
|
32
|
+
/**
|
|
33
|
+
* Start (or join) the upstream browser attempt, display its URL, and wait for
|
|
34
|
+
* the upstream state stream. Esc/external abort cancels the exact attempt;
|
|
35
|
+
* a commit that won that race is reported as success instead of a false
|
|
36
|
+
* cancellation. The question panel is always retired before returning.
|
|
37
|
+
*/
|
|
38
|
+
export declare function loginDeepSeekAccount(account: DeepSeekAccountAuth, callbackOrigin: string, ask: AskFn, signal?: AbortSignal, helpers?: QuestionBridgeHelpers): Promise<void>;
|
|
39
|
+
//# sourceMappingURL=deepseek.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deepseek.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/oauth/deepseek.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,qBAAqB,EAAe,eAAe,EAAmB,MAAM,mCAAmC,CAAA;AAG7H,OAAO,EAAkB,KAAK,KAAK,EAAE,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAEzF,eAAO,MAAM,yBAAyB,qBAAqB,CAAA;AAE3D,qEAAqE;AACrE,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,eAAe,EAAE,UAAU,GAAG,aAAa,GAAG,cAAc,GAAG,SAAS,GAAG,OAAO,CAAC,GACtH,OAAO,CAAC,IAAI,CAAC,eAAe,EAAE,sBAAsB,GAAG,kBAAkB,CAAC,CAAC,CAAA;AAE/E,uEAAuE;AACvE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,mBAAmB,GAAG,SAAS,CAEjF;AAED,2FAA2F;AAC3F,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAO3D;AAED;;;;;GAKG;AACH,wBAAgB,oCAAoC,CAAC,GAAG,EAAE,OAAO,GAAG;IAClE,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;IAC1B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB,CA0DA;AAWD,wFAAwF;AACxF,wBAAgB,sBAAsB,IAAI,qBAAqB,CAM9D;AAMD;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,mBAAmB,EAC5B,cAAc,EAAE,MAAM,EACtB,GAAG,EAAE,KAAK,EACV,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,IAAI,CAAC,CAyDf"}
|