dsh-ssh-tui 0.1.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/LICENSE +21 -0
- package/README.md +262 -0
- package/README.zh-CN.md +206 -0
- package/cordis.patch.yml +62 -0
- package/lib/index.js +179 -0
- package/lib/index.js.map +1 -0
- package/lib/picker.js +88 -0
- package/lib/picker.js.map +1 -0
- package/lib/reasoning.js +36 -0
- package/lib/reasoning.js.map +1 -0
- package/lib/session-list.js +76 -0
- package/lib/session-list.js.map +1 -0
- package/lib/startup.js +75 -0
- package/lib/startup.js.map +1 -0
- package/lib/tui.js +3685 -0
- package/lib/tui.js.map +1 -0
- package/lib/types/index.d.ts +33 -0
- package/lib/types/picker.d.ts +21 -0
- package/lib/types/reasoning.d.ts +20 -0
- package/lib/types/session-list.d.ts +26 -0
- package/lib/types/startup.d.ts +41 -0
- package/lib/types/tui.d.ts +365 -0
- package/package.json +119 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-ssh-tui — an SSH-friendly interactive terminal front door for DeepSeek
|
|
3
|
+
* Harness. The bundle rides over @deepseek-ai/dsh-base and drives one
|
|
4
|
+
* configured agent, so the whole plugin ecosystem (shell, filesystem, skills,
|
|
5
|
+
* subagents, sandbox approvals) is the same one the web surface uses.
|
|
6
|
+
*/
|
|
7
|
+
import { installModelSelection } from '@deepseek-ai/dsh-agent';
|
|
8
|
+
import { errorChain } from '@deepseek-ai/dsh-llm';
|
|
9
|
+
import { SessionId } from '@deepseek-ai/dsh-session';
|
|
10
|
+
import { randomUUID } from 'node:crypto';
|
|
11
|
+
import { showSessionPicker } from './picker.js';
|
|
12
|
+
import { mountTui } from './tui.js';
|
|
13
|
+
import { defaultReasoningEffort } from './reasoning.js';
|
|
14
|
+
export const name = 'ssh-tui';
|
|
15
|
+
/** Core services required before the terminal channel can drive an agent. */
|
|
16
|
+
export const inject = ['agents', 'agentDefaultModel'];
|
|
17
|
+
/**
|
|
18
|
+
* Mount the SSH TUI. The `main` agent is created here after the loader
|
|
19
|
+
* settles, reading the saved default provider/model from
|
|
20
|
+
* `agent-default-model` (the same settings memory the official Models page
|
|
21
|
+
* uses). Launch flags still win when supplied.
|
|
22
|
+
*/
|
|
23
|
+
export function apply(ctx, config) {
|
|
24
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
25
|
+
throw new Error('dsh-ssh-tui: both stdin and stdout must be TTYs; use a terminal/SSH session');
|
|
26
|
+
}
|
|
27
|
+
ctx.effect(() => {
|
|
28
|
+
let disposed = false;
|
|
29
|
+
let switching = false;
|
|
30
|
+
let handle;
|
|
31
|
+
let controller;
|
|
32
|
+
// An explicit in-process change (/setup or /model) wins over launch-time
|
|
33
|
+
// CLI overrides for every session created or resumed later in this process.
|
|
34
|
+
let liveSelection;
|
|
35
|
+
const start = async (sessionId, resume) => {
|
|
36
|
+
await ctx.get('loader')?.await();
|
|
37
|
+
if (disposed)
|
|
38
|
+
return;
|
|
39
|
+
const agents = ctx.get('agents');
|
|
40
|
+
if (agents === undefined)
|
|
41
|
+
throw new Error('dsh-ssh-tui: agents service is unavailable');
|
|
42
|
+
const defaultModel = ctx.get('agentDefaultModel');
|
|
43
|
+
const selection = liveSelection ?? defaultModel?.currentSelection();
|
|
44
|
+
const provider = liveSelection !== undefined
|
|
45
|
+
? liveSelection.provider
|
|
46
|
+
: config.provider ?? selection?.provider ?? 'deepseek-official';
|
|
47
|
+
const model = liveSelection !== undefined
|
|
48
|
+
? liveSelection.model
|
|
49
|
+
: config.model ?? selection?.model ?? 'deepseek-v4-flash';
|
|
50
|
+
// OpenCode / third-party (llm-pi-ai) routes carry no adapter-level
|
|
51
|
+
// reasoning default, so default a supported effort ourselves when none is
|
|
52
|
+
// selected. Without it the model streams thinking as plain text and the
|
|
53
|
+
// foldable `思考中` block never has data to render.
|
|
54
|
+
let reasoningEffort = selection?.reasoningEffort;
|
|
55
|
+
if (reasoningEffort === undefined && provider !== 'deepseek-official') {
|
|
56
|
+
const llm = ctx.get('llm');
|
|
57
|
+
if (llm !== undefined) {
|
|
58
|
+
reasoningEffort = await defaultReasoningEffort(llm, provider, model);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const selectionRef = {
|
|
62
|
+
current: selection !== undefined
|
|
63
|
+
? { ...selection, ...(reasoningEffort === undefined ? {} : { reasoningEffort }) }
|
|
64
|
+
: { provider, model, ...(reasoningEffort === undefined ? {} : { reasoningEffort }) },
|
|
65
|
+
assembled: undefined,
|
|
66
|
+
};
|
|
67
|
+
const agentPresets = ctx.get('agentPresets');
|
|
68
|
+
const agentOptions = {
|
|
69
|
+
provider,
|
|
70
|
+
model,
|
|
71
|
+
...(reasoningEffort === undefined ? {} : { reasoningEffort }),
|
|
72
|
+
};
|
|
73
|
+
const setup = async (agentCtx) => {
|
|
74
|
+
installModelSelection(agentCtx, selectionRef);
|
|
75
|
+
await agentPresets?.mount(agentCtx);
|
|
76
|
+
};
|
|
77
|
+
handle = resume
|
|
78
|
+
? await agents.resume({ resumeSessionId: sessionId, agentOptions, setup })
|
|
79
|
+
: await agents.create({ sessionId, meta: { cwd: process.cwd() }, agentOptions, setup });
|
|
80
|
+
if (disposed) {
|
|
81
|
+
await handle.dispose();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const presetId = agentPresets?.composedPreset(handle.agent.ctx) ?? agentPresets?.defaultId;
|
|
85
|
+
let presetName = presetId;
|
|
86
|
+
if (presetId !== undefined) {
|
|
87
|
+
try {
|
|
88
|
+
const preset = await agentPresets?.resolve(presetId);
|
|
89
|
+
if (preset?.name !== undefined)
|
|
90
|
+
presetName = preset.name;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Fall back to the id.
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
controller = mountTui(ctx, {
|
|
97
|
+
...config,
|
|
98
|
+
resumePicker: false,
|
|
99
|
+
sessionId: String(sessionId),
|
|
100
|
+
resume,
|
|
101
|
+
provider,
|
|
102
|
+
model,
|
|
103
|
+
selectionRef,
|
|
104
|
+
presetId,
|
|
105
|
+
presetName,
|
|
106
|
+
onSwitchSession: switchTo,
|
|
107
|
+
onSelectionChanged: (next) => {
|
|
108
|
+
liveSelection = next;
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
/** Tear down the current channel and resume another session in its place. */
|
|
113
|
+
const switchTo = async (target) => {
|
|
114
|
+
if (switching || disposed)
|
|
115
|
+
return;
|
|
116
|
+
switching = true;
|
|
117
|
+
try {
|
|
118
|
+
if (controller !== undefined) {
|
|
119
|
+
await controller.dispose();
|
|
120
|
+
controller = undefined;
|
|
121
|
+
}
|
|
122
|
+
if (handle !== undefined) {
|
|
123
|
+
await handle.dispose();
|
|
124
|
+
handle = undefined;
|
|
125
|
+
}
|
|
126
|
+
await start(SessionId(target), true);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
process.stderr.write(`dsh-ssh-tui: failed to switch to session "${target}": ${errorChain(error)}\n`);
|
|
130
|
+
const exit = ctx.get('appExit');
|
|
131
|
+
if (exit !== undefined)
|
|
132
|
+
exit(1);
|
|
133
|
+
else
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
switching = false;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
const boot = async () => {
|
|
141
|
+
if (config.resumePicker === true) {
|
|
142
|
+
await ctx.get('loader')?.await();
|
|
143
|
+
const picked = await showSessionPicker(ctx, config.color !== false);
|
|
144
|
+
if (disposed)
|
|
145
|
+
return;
|
|
146
|
+
if (picked === null) {
|
|
147
|
+
const exit = ctx.get('appExit');
|
|
148
|
+
if (exit !== undefined)
|
|
149
|
+
exit(0);
|
|
150
|
+
else
|
|
151
|
+
process.exit(0);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (picked.kind === 'resume') {
|
|
155
|
+
await start(SessionId(picked.id), true);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
await start(SessionId(`main-session-${randomUUID()}`), false);
|
|
159
|
+
}
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
await start(SessionId(config.sessionId), config.resume === true);
|
|
163
|
+
};
|
|
164
|
+
void boot().catch((error) => {
|
|
165
|
+
process.stderr.write(`dsh-ssh-tui: session "${config.sessionId}" failed to start: ${errorChain(error)}\n`);
|
|
166
|
+
const exit = ctx.get('appExit');
|
|
167
|
+
if (exit !== undefined)
|
|
168
|
+
exit(1);
|
|
169
|
+
else
|
|
170
|
+
process.exit(1);
|
|
171
|
+
});
|
|
172
|
+
return async () => {
|
|
173
|
+
disposed = true;
|
|
174
|
+
await controller?.dispose();
|
|
175
|
+
await handle?.dispose();
|
|
176
|
+
};
|
|
177
|
+
}, 'ssh-tui');
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/lib/picker.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Startup history-session picker: a small raw-mode selector shown BEFORE the
|
|
3
|
+
* main TUI mounts, so launching without an explicit session id lands on a
|
|
4
|
+
* choice instead of a fresh main screen.
|
|
5
|
+
*/
|
|
6
|
+
import { StringDecoder } from 'node:string_decoder';
|
|
7
|
+
import { formatSessionTime, listResumableSessions } from './session-list.js';
|
|
8
|
+
/**
|
|
9
|
+
* Show the picker and wait for one digit, Enter (new session), or Esc/Ctrl+C
|
|
10
|
+
* (cancel). Restores the terminal before resolving.
|
|
11
|
+
* @param ctx - boot context supplying sessionPersistence.
|
|
12
|
+
* @param color - whether to apply ANSI colors.
|
|
13
|
+
* @returns the selection, or null when cancelled.
|
|
14
|
+
*/
|
|
15
|
+
export async function showSessionPicker(ctx, color) {
|
|
16
|
+
const persistence = ctx.get('sessionPersistence');
|
|
17
|
+
if (persistence === undefined) {
|
|
18
|
+
process.stderr.write('dsh-ssh-tui: sessionPersistence service is unavailable\n');
|
|
19
|
+
return { kind: 'new' };
|
|
20
|
+
}
|
|
21
|
+
const sessions = await listResumableSessions(persistence, '');
|
|
22
|
+
if (sessions.length === 0) {
|
|
23
|
+
process.stdout.write('dsh-ssh-tui: no resumable history sessions; starting a fresh session.\n');
|
|
24
|
+
return { kind: 'new' };
|
|
25
|
+
}
|
|
26
|
+
const useAltScreen = process.env.DSH_TUI_NO_ALT_SCREEN !== '1'
|
|
27
|
+
&& process.env.DSH_TUI_NO_ALT_SCREEN !== 'true';
|
|
28
|
+
const decoder = new StringDecoder('utf8');
|
|
29
|
+
process.stdin.setRawMode(true);
|
|
30
|
+
process.stdin.resume();
|
|
31
|
+
process.stdout.write(`${useAltScreen ? '\x1b[?1049h' : ''}\x1b[?25l`);
|
|
32
|
+
const style = (text, code) => color ? `\x1b[${code}m${text}\x1b[0m` : text;
|
|
33
|
+
const render = () => {
|
|
34
|
+
const width = Math.max(20, process.stdout.columns || 80);
|
|
35
|
+
const lines = [
|
|
36
|
+
style('DeepSeek Harness — 选择要恢复的历史会话', '1'),
|
|
37
|
+
'─'.repeat(width),
|
|
38
|
+
];
|
|
39
|
+
sessions.forEach((session, index) => {
|
|
40
|
+
lines.push(`${index + 1} ${session.label}`);
|
|
41
|
+
lines.push(` ${style(formatSessionTime(session.updatedAt), '90')} · ${style(session.cwd, '90')}`);
|
|
42
|
+
});
|
|
43
|
+
lines.push('');
|
|
44
|
+
lines.push(`${style('0', '36')} / ${style('Enter', '36')} 新建会话`);
|
|
45
|
+
lines.push(`${style('Esc', '36')} 取消`);
|
|
46
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
47
|
+
for (const line of lines) {
|
|
48
|
+
const wrapped = line.length > width ? `${line.slice(0, width - 1)}…` : line;
|
|
49
|
+
process.stdout.write(`${wrapped}\x1b[0m\x1b[K\n`);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
return new Promise((resolve) => {
|
|
53
|
+
let done = false;
|
|
54
|
+
const cleanup = (result) => {
|
|
55
|
+
if (done)
|
|
56
|
+
return;
|
|
57
|
+
done = true;
|
|
58
|
+
process.stdin.removeListener('data', onData);
|
|
59
|
+
process.stdin.setRawMode(false);
|
|
60
|
+
process.stdin.pause();
|
|
61
|
+
process.stdout.write(`\x1b[0m\x1b[?25h${useAltScreen ? '\x1b[?1049l' : ''}\n`);
|
|
62
|
+
resolve(result);
|
|
63
|
+
};
|
|
64
|
+
const onData = (chunk) => {
|
|
65
|
+
const text = decoder.write(chunk);
|
|
66
|
+
for (const char of text) {
|
|
67
|
+
if (char === '\x1b' || char === '\x03') {
|
|
68
|
+
cleanup(null);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (char === '\r' || char === '\n' || char === '0') {
|
|
72
|
+
cleanup({ kind: 'new' });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (char >= '1' && char <= '9') {
|
|
76
|
+
const session = sessions[Number(char) - 1];
|
|
77
|
+
if (session !== undefined) {
|
|
78
|
+
cleanup({ kind: 'resume', id: session.id });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
process.stdin.on('data', onData);
|
|
85
|
+
render();
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=picker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"picker.js","sourceRoot":"","sources":["../src/picker.ts"],"names":[],"mappings":"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"}
|
package/lib/reasoning.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared reasoning-effort defaults for the SSH TUI.
|
|
3
|
+
*
|
|
4
|
+
* OpenCode / third-party (llm-pi-ai) routes carry no adapter-level reasoning
|
|
5
|
+
* default (unlike `llm-deepseek`, whose row declares `reasoningEffort: max`).
|
|
6
|
+
* Without an explicit effort the model streams its thinking as plain `text`
|
|
7
|
+
* chunks instead of `reasoning` blocks, so the TUI never receives the data
|
|
8
|
+
* needed to render the collapsible `思考中` block. This helper picks a
|
|
9
|
+
* supported default so the fold has data to show.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Pick a default reasoning effort for a provider/model when none is selected.
|
|
13
|
+
*
|
|
14
|
+
* Prefers the model's own `defaultEffort`, then `max` (matching the
|
|
15
|
+
* `llm-deepseek` default), then the adapter's first supported non-`off` level.
|
|
16
|
+
* Returns `undefined` when the route is unknown or the model reports no
|
|
17
|
+
* reasoning support, preserving current behavior in those cases.
|
|
18
|
+
*/
|
|
19
|
+
export async function defaultReasoningEffort(llm, provider, model) {
|
|
20
|
+
try {
|
|
21
|
+
const info = await llm.resolveModelInfo(provider, model);
|
|
22
|
+
const efforts = info?.reasoning?.efforts ?? [];
|
|
23
|
+
if (info?.reasoning?.defaultEffort !== undefined)
|
|
24
|
+
return info.reasoning.defaultEffort;
|
|
25
|
+
const nonOff = efforts.filter(effort => effort.id !== 'off');
|
|
26
|
+
if (nonOff.length === 0)
|
|
27
|
+
return undefined;
|
|
28
|
+
const max = nonOff.find(effort => effort.id === 'max');
|
|
29
|
+
const chosen = max ?? nonOff[0];
|
|
30
|
+
return chosen?.id;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=reasoning.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reasoning.js","sourceRoot":"","sources":["../src/reasoning.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,GAAe,EACf,QAAgB,EAChB,KAAa;IAEb,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;QACxD,MAAM,OAAO,GAAG,IAAI,EAAE,SAAS,EAAE,OAAO,IAAI,EAAE,CAAA;QAC9C,IAAI,IAAI,EAAE,SAAS,EAAE,aAAa,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC,aAAa,CAAA;QACrF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,KAAK,CAAC,CAAA;QAC5D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAA;QACzC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,KAAK,CAAC,CAAA;QACtD,MAAM,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC,CAAC,CAAC,CAAA;QAC/B,OAAO,MAAM,EAAE,EAAE,CAAA;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared history-session listing for the SSH TUI: the launch picker and the
|
|
3
|
+
* in-app `/resume` command use the same candidates, labels, and ordering, so
|
|
4
|
+
* both surfaces offer the same sessions.
|
|
5
|
+
*/
|
|
6
|
+
/** `MM-DD HH:mm` local-time label for session lists. */
|
|
7
|
+
export function formatSessionTime(timestamp) {
|
|
8
|
+
const date = new Date(timestamp);
|
|
9
|
+
const pad = (value) => String(value).padStart(2, '0');
|
|
10
|
+
return `${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* List the most recent resumable top-level sessions, newest first.
|
|
14
|
+
*
|
|
15
|
+
* Subagent-owned sessions, the current session, and sessions with no user
|
|
16
|
+
* input (empty/uuid-only rows) are excluded; the label is the user's first
|
|
17
|
+
* input, falling back to the session title and then the id.
|
|
18
|
+
* @param persistence - the session persistence service.
|
|
19
|
+
* @param currentId - the live session to exclude (empty at launch).
|
|
20
|
+
* @returns up to nine candidates in display order, all carrying user input.
|
|
21
|
+
*/
|
|
22
|
+
export async function listResumableSessions(persistence, currentId) {
|
|
23
|
+
const headers = await persistence.list();
|
|
24
|
+
const candidates = headers
|
|
25
|
+
.filter(meta => meta.id !== currentId
|
|
26
|
+
&& meta.cwd !== undefined
|
|
27
|
+
&& meta.origin !== 'subagent'
|
|
28
|
+
&& (meta.delegationDepth ?? 0) === 0)
|
|
29
|
+
.sort((a, b) => b.createdAt - a.createdAt)
|
|
30
|
+
.slice(0, 30);
|
|
31
|
+
const inspected = await Promise.all(candidates.map(async (meta) => {
|
|
32
|
+
try {
|
|
33
|
+
const inspection = await persistence.inspect(meta.id);
|
|
34
|
+
const firstUserMessage = inspection.events.find((event) => event.type === 'user/message'
|
|
35
|
+
&& event.data.source.kind === 'user');
|
|
36
|
+
const firstUserText = firstUserMessage === undefined
|
|
37
|
+
? undefined
|
|
38
|
+
: firstUserMessage.data.content
|
|
39
|
+
.map((block) => {
|
|
40
|
+
const candidate = block;
|
|
41
|
+
return candidate.type === 'text' && typeof candidate.text === 'string' ? candidate.text : '';
|
|
42
|
+
})
|
|
43
|
+
.join(' ')
|
|
44
|
+
.replace(/\s+/gu, ' ')
|
|
45
|
+
.trim()
|
|
46
|
+
.slice(0, 80);
|
|
47
|
+
const titleEvent = [...inspection.events].reverse()
|
|
48
|
+
.find(event => event.type === 'session/title');
|
|
49
|
+
const title = titleEvent === undefined
|
|
50
|
+
? undefined
|
|
51
|
+
: titleEvent.data.title;
|
|
52
|
+
const updatedAt = inspection.events.at(-1)?.time ?? meta.createdAt;
|
|
53
|
+
return {
|
|
54
|
+
id: meta.id,
|
|
55
|
+
label: firstUserText ?? title ?? meta.id,
|
|
56
|
+
updatedAt,
|
|
57
|
+
cwd: meta.cwd ?? '',
|
|
58
|
+
hasUserInput: firstUserMessage !== undefined,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return {
|
|
63
|
+
id: meta.id,
|
|
64
|
+
label: meta.id,
|
|
65
|
+
updatedAt: meta.createdAt,
|
|
66
|
+
cwd: meta.cwd ?? '',
|
|
67
|
+
hasUserInput: false,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}));
|
|
71
|
+
return inspected
|
|
72
|
+
.filter(item => item.hasUserInput)
|
|
73
|
+
.slice(0, 9)
|
|
74
|
+
.map(({ hasUserInput: _hasUserInput, ...rest }) => rest);
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=session-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-list.js","sourceRoot":"","sources":["../src/session-list.ts"],"names":[],"mappings":"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"}
|
package/lib/startup.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command-line intake for the SSH TUI. Parses the app arguments handed over
|
|
3
|
+
* by the dsh launcher, mints or resumes the `main` agent identity, and
|
|
4
|
+
* provides the `sshTuiStartup` service consumed by the agent-loop row and the
|
|
5
|
+
* TUI row.
|
|
6
|
+
*/
|
|
7
|
+
import { randomUUID } from 'node:crypto';
|
|
8
|
+
import { Command } from 'commander';
|
|
9
|
+
import { parseCmdline } from '@deepseek-ai/dsh-cmdline';
|
|
10
|
+
import { CONFIGURED_AGENT_IDENTITIES_KEY } from '@deepseek-ai/dsh-agent-loop';
|
|
11
|
+
import { SessionId } from '@deepseek-ai/dsh-session';
|
|
12
|
+
/** Service key under which the parsed TUI launch options are provided. */
|
|
13
|
+
export const SSH_TUI_STARTUP_SERVICE = 'sshTuiStartup';
|
|
14
|
+
/** Config `id` of the agent-loop entry the TUI drives. */
|
|
15
|
+
export const MAIN_AGENT_ID = 'main';
|
|
16
|
+
export const name = 'ssh-tui-startup';
|
|
17
|
+
export const inject = ['cmdlineArgs'];
|
|
18
|
+
/**
|
|
19
|
+
* Build the TUI command grammar and provide the session identity for the
|
|
20
|
+
* agent-loop row plus the {@link SshTuiStartup} service. On `--help` or a
|
|
21
|
+
* usage error nothing is provided, so dependent rows never activate and the
|
|
22
|
+
* process exits through the cmdline exit seam.
|
|
23
|
+
*/
|
|
24
|
+
export function apply(ctx) {
|
|
25
|
+
const program = new Command()
|
|
26
|
+
.name('dsh --profile tui')
|
|
27
|
+
.description('SSH-friendly interactive terminal session over DeepSeek Harness')
|
|
28
|
+
.helpOption('-h, --help', 'show this help')
|
|
29
|
+
.argument('[mode]', 'resume — open the history-session picker')
|
|
30
|
+
.argument('[session]', 'session id to resume (with the resume mode)')
|
|
31
|
+
.option('--resume [session]', 'resume a persisted session (empty = session picker)')
|
|
32
|
+
.option('--new', 'start a fresh session without the history picker')
|
|
33
|
+
.option('--model <model>', 'override the default model id')
|
|
34
|
+
.option('--provider <provider>', 'override the default provider route')
|
|
35
|
+
.option('--no-color', 'disable ANSI colors');
|
|
36
|
+
program.action((mode, session, options) => {
|
|
37
|
+
if (mode !== undefined && mode !== 'resume') {
|
|
38
|
+
program.error(`dsh --profile tui: unknown argument "${mode}" (expected "resume")`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const flagValue = options.resume;
|
|
42
|
+
const flagId = typeof flagValue === 'string' ? flagValue.trim() : '';
|
|
43
|
+
const positionalId = session?.trim() ?? '';
|
|
44
|
+
if (positionalId !== '' && flagId !== '' && positionalId !== flagId) {
|
|
45
|
+
program.error('dsh --profile tui: session id given twice with different values (--resume and positional)');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (positionalId !== '' && mode === undefined && flagValue === undefined) {
|
|
49
|
+
program.error('dsh --profile tui: a session id requires resume mode or --resume');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (options.new === true && (flagValue !== undefined || positionalId !== '')) {
|
|
53
|
+
program.error('dsh --profile tui: --new cannot be combined with a resume session id');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const resumeId = flagId !== '' ? flagId : positionalId;
|
|
57
|
+
const picker = resumeId === '' && (mode === 'resume' || flagValue !== undefined);
|
|
58
|
+
const resume = resumeId !== '';
|
|
59
|
+
const identity = resume
|
|
60
|
+
? { id: SessionId(resumeId), resume: true }
|
|
61
|
+
: { id: SessionId(`main-session-${randomUUID()}`), resume: false };
|
|
62
|
+
ctx.provide(CONFIGURED_AGENT_IDENTITIES_KEY, { [MAIN_AGENT_ID]: identity });
|
|
63
|
+
ctx.provide(SSH_TUI_STARTUP_SERVICE, {
|
|
64
|
+
sessionId: identity.id,
|
|
65
|
+
resume: identity.resume,
|
|
66
|
+
resumePicker: picker,
|
|
67
|
+
model: options.model?.trim() || undefined,
|
|
68
|
+
provider: options.provider?.trim() || undefined,
|
|
69
|
+
noColor: options.color === false,
|
|
70
|
+
});
|
|
71
|
+
ctx.provide('tuiGoodbyeMessage', `To resume this session: dsh --profile tui --resume=${identity.id}`);
|
|
72
|
+
});
|
|
73
|
+
parseCmdline(ctx, program);
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=startup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup.js","sourceRoot":"","sources":["../src/startup.ts"],"names":[],"mappings":"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"}
|