@lemoncat7/dsh-ssh 0.1.0-alpha.15
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 +105 -0
- package/cordis.patch.yml +10 -0
- package/lib/api.d.ts +23 -0
- package/lib/api.d.ts.map +1 -0
- package/lib/api.js +571 -0
- package/lib/api.js.map +1 -0
- package/lib/client-api.d.ts +174 -0
- package/lib/client-api.d.ts.map +1 -0
- package/lib/client-api.js +88 -0
- package/lib/client-api.js.map +1 -0
- package/lib/client.d.ts +4 -0
- package/lib/client.d.ts.map +1 -0
- package/lib/client.js +8067 -0
- package/lib/client.js.map +7 -0
- package/lib/config.d.ts +14 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +31 -0
- package/lib/config.js.map +1 -0
- package/lib/connector.d.ts +30 -0
- package/lib/connector.d.ts.map +1 -0
- package/lib/connector.js +216 -0
- package/lib/connector.js.map +1 -0
- package/lib/credentials.d.ts +25 -0
- package/lib/credentials.d.ts.map +1 -0
- package/lib/credentials.js +84 -0
- package/lib/credentials.js.map +1 -0
- package/lib/directory.d.ts +8 -0
- package/lib/directory.d.ts.map +1 -0
- package/lib/directory.js +45 -0
- package/lib/directory.js.map +1 -0
- package/lib/domain.d.ts +121 -0
- package/lib/domain.d.ts.map +1 -0
- package/lib/domain.js +136 -0
- package/lib/domain.js.map +1 -0
- package/lib/exec.d.ts +15 -0
- package/lib/exec.d.ts.map +1 -0
- package/lib/exec.js +82 -0
- package/lib/exec.js.map +1 -0
- package/lib/forwards.d.ts +26 -0
- package/lib/forwards.d.ts.map +1 -0
- package/lib/forwards.js +213 -0
- package/lib/forwards.js.map +1 -0
- package/lib/index.d.ts +9 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +53 -0
- package/lib/index.js.map +1 -0
- package/lib/proxy.d.ts +9 -0
- package/lib/proxy.d.ts.map +1 -0
- package/lib/proxy.js +116 -0
- package/lib/proxy.js.map +1 -0
- package/lib/sftp-client.d.ts +12 -0
- package/lib/sftp-client.d.ts.map +1 -0
- package/lib/sftp-client.js +123 -0
- package/lib/sftp-client.js.map +1 -0
- package/lib/sftp.d.ts +43 -0
- package/lib/sftp.d.ts.map +1 -0
- package/lib/sftp.js +251 -0
- package/lib/sftp.js.map +1 -0
- package/lib/sidebar-anchor.d.ts +8 -0
- package/lib/sidebar-anchor.d.ts.map +1 -0
- package/lib/sidebar-anchor.js +50 -0
- package/lib/sidebar-anchor.js.map +1 -0
- package/lib/store.d.ts +20 -0
- package/lib/store.d.ts.map +1 -0
- package/lib/store.js +154 -0
- package/lib/store.js.map +1 -0
- package/lib/terminal.d.ts +108 -0
- package/lib/terminal.d.ts.map +1 -0
- package/lib/terminal.js +384 -0
- package/lib/terminal.js.map +1 -0
- package/lib/tools.d.ts +7 -0
- package/lib/tools.d.ts.map +1 -0
- package/lib/tools.js +237 -0
- package/lib/tools.js.map +1 -0
- package/package.json +107 -0
package/lib/terminal.js
ADDED
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { directoryPrelude } from './exec.js';
|
|
3
|
+
const MAX_SCROLLBACK_CHARS = 256_000;
|
|
4
|
+
export class SshTerminalBackend {
|
|
5
|
+
connector;
|
|
6
|
+
store;
|
|
7
|
+
type = 'ssh';
|
|
8
|
+
constructor(connector, store) {
|
|
9
|
+
this.connector = connector;
|
|
10
|
+
this.store = store;
|
|
11
|
+
}
|
|
12
|
+
async spawn(spec) {
|
|
13
|
+
const profileId = spec.cwd?.trim();
|
|
14
|
+
if (!profileId)
|
|
15
|
+
throw new Error('SSH terminal backend requires the injected profile id as cwd');
|
|
16
|
+
const injection = this.store.injection(spec.owner.session.id);
|
|
17
|
+
if (injection === undefined || !injection.profileIds.includes(profileId))
|
|
18
|
+
throw new Error('This SSH profile is not injected into the current DSH session');
|
|
19
|
+
if (injection.permission !== 'terminal')
|
|
20
|
+
throw new Error('This SSH injection allows one-shot commands only');
|
|
21
|
+
const connection = await this.connector.connect(profileId, spec.signal);
|
|
22
|
+
try {
|
|
23
|
+
const channel = await openShell(connection, 120, 32);
|
|
24
|
+
return new SshTerminalSession(connection, channel, `Connected to ${connection.profile.name} (${connection.profile.username}@${connection.profile.host})`);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
connection.close();
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class SshTerminalSession {
|
|
33
|
+
connection;
|
|
34
|
+
channel;
|
|
35
|
+
motd;
|
|
36
|
+
scrollback = '';
|
|
37
|
+
terminalStatus = { kind: 'running' };
|
|
38
|
+
active;
|
|
39
|
+
closing;
|
|
40
|
+
constructor(connection, channel, motd) {
|
|
41
|
+
this.connection = connection;
|
|
42
|
+
this.channel = channel;
|
|
43
|
+
this.motd = motd;
|
|
44
|
+
channel.setEncoding('utf8');
|
|
45
|
+
channel.on('data', (chunk) => this.receive(String(chunk)));
|
|
46
|
+
channel.stderr?.setEncoding('utf8');
|
|
47
|
+
channel.stderr?.on('data', (chunk) => this.receive(String(chunk)));
|
|
48
|
+
channel.on('exit', (code, signal) => {
|
|
49
|
+
this.terminalStatus = { kind: 'exited', exitCode: code ?? null, signal: normalizeSignal(signal) };
|
|
50
|
+
this.active?.settle('session_exit');
|
|
51
|
+
});
|
|
52
|
+
channel.on('close', () => {
|
|
53
|
+
if (this.terminalStatus.kind === 'running')
|
|
54
|
+
this.terminalStatus = { kind: 'exited', exitCode: null, signal: null };
|
|
55
|
+
this.active?.settle('session_exit');
|
|
56
|
+
connection.close();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
startSend(request) {
|
|
60
|
+
if (this.terminalStatus.kind === 'exited')
|
|
61
|
+
throw new Error('SSH terminal has exited');
|
|
62
|
+
if (this.active !== undefined)
|
|
63
|
+
throw new Error('SSH terminal already has an active send');
|
|
64
|
+
const operation = new SendOperation(this, request);
|
|
65
|
+
this.active = operation;
|
|
66
|
+
operation.done.finally(() => { if (this.active === operation)
|
|
67
|
+
this.active = undefined; }).catch(() => { });
|
|
68
|
+
this.channel.write(request.submit ? `${request.text}\n` : request.text);
|
|
69
|
+
return operation;
|
|
70
|
+
}
|
|
71
|
+
read(request) {
|
|
72
|
+
const lines = this.scrollback.split(/\r?\n/);
|
|
73
|
+
const totalLines = lines.length;
|
|
74
|
+
const offset = clamp(request.offset ?? 0, 0, totalLines);
|
|
75
|
+
const count = clamp(request.count ?? 200, 1, 2000);
|
|
76
|
+
const lineEnd = totalLines - offset;
|
|
77
|
+
const lineBegin = Math.max(0, lineEnd - count);
|
|
78
|
+
return {
|
|
79
|
+
text: lines.slice(lineBegin, lineEnd).join('\n'),
|
|
80
|
+
totalLines,
|
|
81
|
+
lineBegin,
|
|
82
|
+
lineEnd,
|
|
83
|
+
truncated: this.scrollback.length >= MAX_SCROLLBACK_CHARS || lineBegin > 0,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
write(text) {
|
|
87
|
+
if (this.terminalStatus.kind === 'exited')
|
|
88
|
+
throw new Error('SSH terminal has exited');
|
|
89
|
+
this.channel.write(text);
|
|
90
|
+
}
|
|
91
|
+
resize(cols, rows) {
|
|
92
|
+
if (this.terminalStatus.kind === 'exited')
|
|
93
|
+
return;
|
|
94
|
+
this.channel.setWindow(rows, cols, 0, 0);
|
|
95
|
+
}
|
|
96
|
+
async signal(signal) {
|
|
97
|
+
this.channel.signal(signal.replace(/^SIG/, ''));
|
|
98
|
+
return { delivered: true, targetPgid: 0 };
|
|
99
|
+
}
|
|
100
|
+
status() { return this.terminalStatus; }
|
|
101
|
+
close(_reason) {
|
|
102
|
+
if (this.closing !== undefined)
|
|
103
|
+
return this.closing;
|
|
104
|
+
this.closing = new Promise(resolve => {
|
|
105
|
+
if (this.terminalStatus.kind === 'exited') {
|
|
106
|
+
this.connection.close();
|
|
107
|
+
return resolve();
|
|
108
|
+
}
|
|
109
|
+
const timer = setTimeout(() => { this.channel.destroy(); this.connection.close(); resolve(); }, 1500);
|
|
110
|
+
this.channel.once('close', () => { clearTimeout(timer); this.connection.close(); resolve(); });
|
|
111
|
+
this.channel.end();
|
|
112
|
+
});
|
|
113
|
+
return this.closing;
|
|
114
|
+
}
|
|
115
|
+
receive(text) {
|
|
116
|
+
this.scrollback = `${this.scrollback}${text}`.slice(-MAX_SCROLLBACK_CHARS);
|
|
117
|
+
this.active?.push(text);
|
|
118
|
+
}
|
|
119
|
+
snapshotStatus() { return this.terminalStatus; }
|
|
120
|
+
}
|
|
121
|
+
class SendOperation {
|
|
122
|
+
session;
|
|
123
|
+
done;
|
|
124
|
+
resolveDone;
|
|
125
|
+
buffer = '';
|
|
126
|
+
unread = '';
|
|
127
|
+
truncated = false;
|
|
128
|
+
settled = false;
|
|
129
|
+
idleTimer;
|
|
130
|
+
timeoutTimer;
|
|
131
|
+
constructor(session, request) {
|
|
132
|
+
this.session = session;
|
|
133
|
+
this.done = new Promise(resolve => { this.resolveDone = resolve; });
|
|
134
|
+
this.timeoutTimer = setTimeout(() => this.settle('timeout'), 30_000);
|
|
135
|
+
request.signal?.addEventListener('abort', () => { void session.signal('SIGINT').catch(() => { }); this.settle('timeout'); }, { once: true });
|
|
136
|
+
this.armIdle();
|
|
137
|
+
}
|
|
138
|
+
readOutput() {
|
|
139
|
+
const delta = this.unread;
|
|
140
|
+
this.unread = '';
|
|
141
|
+
return { delta, truncated: this.truncated };
|
|
142
|
+
}
|
|
143
|
+
cancel() {
|
|
144
|
+
if (this.settled)
|
|
145
|
+
return false;
|
|
146
|
+
void this.session.signal('SIGINT').catch(() => { });
|
|
147
|
+
this.settle('inferred_idle');
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
push(text) {
|
|
151
|
+
if (this.settled)
|
|
152
|
+
return;
|
|
153
|
+
this.buffer += text;
|
|
154
|
+
this.unread += text;
|
|
155
|
+
if (this.buffer.length > 64_000) {
|
|
156
|
+
this.buffer = this.buffer.slice(-64_000);
|
|
157
|
+
this.truncated = true;
|
|
158
|
+
}
|
|
159
|
+
if (this.unread.length > 64_000) {
|
|
160
|
+
this.unread = this.unread.slice(-64_000);
|
|
161
|
+
this.truncated = true;
|
|
162
|
+
}
|
|
163
|
+
this.armIdle();
|
|
164
|
+
}
|
|
165
|
+
settle(reason) {
|
|
166
|
+
if (this.settled)
|
|
167
|
+
return;
|
|
168
|
+
this.settled = true;
|
|
169
|
+
if (this.idleTimer !== undefined)
|
|
170
|
+
clearTimeout(this.idleTimer);
|
|
171
|
+
if (this.timeoutTimer !== undefined)
|
|
172
|
+
clearTimeout(this.timeoutTimer);
|
|
173
|
+
this.resolveDone({
|
|
174
|
+
viewport: this.buffer,
|
|
175
|
+
waitReason: reason,
|
|
176
|
+
sessionStatus: this.session.snapshotStatus(),
|
|
177
|
+
truncated: this.truncated,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
armIdle() {
|
|
181
|
+
if (this.idleTimer !== undefined)
|
|
182
|
+
clearTimeout(this.idleTimer);
|
|
183
|
+
this.idleTimer = setTimeout(() => this.settle('inferred_idle'), 700);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
export class BrowserTerminalManager {
|
|
187
|
+
connector;
|
|
188
|
+
sessions = new Map();
|
|
189
|
+
constructor(connector) {
|
|
190
|
+
this.connector = connector;
|
|
191
|
+
}
|
|
192
|
+
async create(profileId, cols, rows, signal) {
|
|
193
|
+
const connection = await this.connector.connect(profileId, signal);
|
|
194
|
+
try {
|
|
195
|
+
const channel = await openShell(connection, clamp(cols, 20, 400), clamp(rows, 5, 200));
|
|
196
|
+
const terminal = new BrowserTerminal(randomUUID(), profileId, connection, channel, () => this.sessions.delete(terminal.id));
|
|
197
|
+
this.sessions.set(terminal.id, terminal);
|
|
198
|
+
return { id: terminal.id, profileId };
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
connection.close();
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
get(id) {
|
|
206
|
+
const terminal = this.sessions.get(id);
|
|
207
|
+
if (terminal === undefined)
|
|
208
|
+
throw Object.assign(new Error('browser terminal was not found'), { status: 404 });
|
|
209
|
+
return terminal;
|
|
210
|
+
}
|
|
211
|
+
async closeAll() {
|
|
212
|
+
await Promise.all([...this.sessions.values()].map(terminal => terminal.close()));
|
|
213
|
+
this.sessions.clear();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/** Owner-scoped AI terminals for Web profiles, whose official Terminal service lives inside each Agent preset realm. */
|
|
217
|
+
export class AiTerminalManager {
|
|
218
|
+
connector;
|
|
219
|
+
sessions = new Map();
|
|
220
|
+
constructor(connector) {
|
|
221
|
+
this.connector = connector;
|
|
222
|
+
}
|
|
223
|
+
async create(ownerId, profileId, cwd, name, signal) {
|
|
224
|
+
const connection = await this.connector.connect(profileId, signal);
|
|
225
|
+
try {
|
|
226
|
+
const channel = await openShell(connection, 120, 32);
|
|
227
|
+
const terminalId = randomUUID();
|
|
228
|
+
const session = new SshTerminalSession(connection, channel, `Connected to ${connection.profile.name} (${connection.profile.username}@${connection.profile.host})`);
|
|
229
|
+
const record = {
|
|
230
|
+
terminalId,
|
|
231
|
+
profileId,
|
|
232
|
+
name: name?.trim() || connection.profile.name,
|
|
233
|
+
cwd,
|
|
234
|
+
createdAt: Date.now(),
|
|
235
|
+
session,
|
|
236
|
+
commands: [],
|
|
237
|
+
};
|
|
238
|
+
const owned = this.sessions.get(ownerId) ?? new Map();
|
|
239
|
+
owned.set(terminalId, record);
|
|
240
|
+
this.sessions.set(ownerId, owned);
|
|
241
|
+
channel.write(`${directoryPrelude(cwd)}\n`);
|
|
242
|
+
return { terminalId, profileId, cwd, motd: session.motd };
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
connection.close();
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
get(ownerId, terminalId) {
|
|
250
|
+
const record = this.sessions.get(ownerId)?.get(terminalId);
|
|
251
|
+
if (record === undefined)
|
|
252
|
+
throw new Error('SSH terminal was not found in the current DSH session');
|
|
253
|
+
return record.session;
|
|
254
|
+
}
|
|
255
|
+
list(ownerId) {
|
|
256
|
+
return [...(this.sessions.get(ownerId) ?? new Map()).values()].map(record => ({ terminalId: record.terminalId, status: record.session.status() }));
|
|
257
|
+
}
|
|
258
|
+
async send(ownerId, terminalId, request) {
|
|
259
|
+
const record = this.sessions.get(ownerId)?.get(terminalId);
|
|
260
|
+
if (record === undefined)
|
|
261
|
+
throw new Error('SSH terminal was not found in the current DSH session');
|
|
262
|
+
const startedAt = Date.now();
|
|
263
|
+
const result = await record.session.startSend(request).done;
|
|
264
|
+
record.commands.push({
|
|
265
|
+
id: randomUUID(),
|
|
266
|
+
command: request.text,
|
|
267
|
+
submitted: request.submit !== false,
|
|
268
|
+
startedAt,
|
|
269
|
+
completedAt: Date.now(),
|
|
270
|
+
output: result.viewport.slice(-64_000),
|
|
271
|
+
waitReason: result.waitReason,
|
|
272
|
+
truncated: result.truncated || result.viewport.length > 64_000,
|
|
273
|
+
});
|
|
274
|
+
if (record.commands.length > 80)
|
|
275
|
+
record.commands.splice(0, record.commands.length - 80);
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
write(ownerId, terminalId, text) {
|
|
279
|
+
this.get(ownerId, terminalId).write(text);
|
|
280
|
+
}
|
|
281
|
+
resize(ownerId, terminalId, cols, rows) {
|
|
282
|
+
this.get(ownerId, terminalId).resize(cols, rows);
|
|
283
|
+
}
|
|
284
|
+
activity(ownerId) {
|
|
285
|
+
return [...(this.sessions.get(ownerId) ?? new Map()).values()].map(record => {
|
|
286
|
+
const scrollback = record.session.read({ count: 500 }).text;
|
|
287
|
+
return {
|
|
288
|
+
terminalId: record.terminalId,
|
|
289
|
+
profileId: record.profileId,
|
|
290
|
+
name: record.name,
|
|
291
|
+
cwd: record.cwd,
|
|
292
|
+
createdAt: record.createdAt,
|
|
293
|
+
status: record.session.status(),
|
|
294
|
+
scrollback: scrollback.slice(-96_000),
|
|
295
|
+
commands: structuredClone(record.commands),
|
|
296
|
+
};
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
async close(ownerId, terminalId) {
|
|
300
|
+
const owned = this.sessions.get(ownerId);
|
|
301
|
+
const record = owned?.get(terminalId);
|
|
302
|
+
if (record === undefined)
|
|
303
|
+
return false;
|
|
304
|
+
owned.delete(terminalId);
|
|
305
|
+
if (owned.size === 0)
|
|
306
|
+
this.sessions.delete(ownerId);
|
|
307
|
+
await record.session.close('closed by owner');
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
async closeAll() {
|
|
311
|
+
const sessions = [...this.sessions.values()].flatMap(owned => [...owned.values()].map(record => record.session));
|
|
312
|
+
this.sessions.clear();
|
|
313
|
+
await Promise.all(sessions.map(session => session.close('dsh-ssh disposed').catch(() => { })));
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
export class BrowserTerminal {
|
|
317
|
+
id;
|
|
318
|
+
profileId;
|
|
319
|
+
connection;
|
|
320
|
+
channel;
|
|
321
|
+
onClose;
|
|
322
|
+
output = '';
|
|
323
|
+
baseCursor = 0;
|
|
324
|
+
closed = false;
|
|
325
|
+
idleTimer;
|
|
326
|
+
constructor(id, profileId, connection, channel, onClose) {
|
|
327
|
+
this.id = id;
|
|
328
|
+
this.profileId = profileId;
|
|
329
|
+
this.connection = connection;
|
|
330
|
+
this.channel = channel;
|
|
331
|
+
this.onClose = onClose;
|
|
332
|
+
this.idleTimer = setTimeout(() => { void this.close(); }, 30 * 60_000);
|
|
333
|
+
channel.setEncoding('utf8');
|
|
334
|
+
channel.on('data', (chunk) => this.append(String(chunk)));
|
|
335
|
+
channel.stderr?.setEncoding('utf8');
|
|
336
|
+
channel.stderr?.on('data', (chunk) => this.append(String(chunk)));
|
|
337
|
+
channel.once('close', () => { this.closed = true; connection.close(); onClose(); });
|
|
338
|
+
}
|
|
339
|
+
write(text) { this.touch(); if (this.closed)
|
|
340
|
+
throw new Error('terminal is closed'); this.channel.write(text); }
|
|
341
|
+
resize(cols, rows) { this.touch(); this.channel.setWindow(clamp(rows, 5, 200), clamp(cols, 20, 400), 0, 0); }
|
|
342
|
+
read(cursor) {
|
|
343
|
+
this.touch();
|
|
344
|
+
const safeCursor = Math.max(cursor, this.baseCursor);
|
|
345
|
+
const index = safeCursor - this.baseCursor;
|
|
346
|
+
return { data: this.output.slice(index), cursor: this.baseCursor + this.output.length, truncated: cursor < this.baseCursor, closed: this.closed };
|
|
347
|
+
}
|
|
348
|
+
async close() {
|
|
349
|
+
if (this.closed)
|
|
350
|
+
return;
|
|
351
|
+
this.closed = true;
|
|
352
|
+
clearTimeout(this.idleTimer);
|
|
353
|
+
this.channel.end();
|
|
354
|
+
this.connection.close();
|
|
355
|
+
this.onClose();
|
|
356
|
+
}
|
|
357
|
+
append(text) {
|
|
358
|
+
this.output += text;
|
|
359
|
+
if (this.output.length > 1_000_000) {
|
|
360
|
+
const removed = this.output.length - 750_000;
|
|
361
|
+
this.output = this.output.slice(removed);
|
|
362
|
+
this.baseCursor += removed;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
touch() {
|
|
366
|
+
clearTimeout(this.idleTimer);
|
|
367
|
+
this.idleTimer = setTimeout(() => { void this.close(); }, 30 * 60_000);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function openShell(connection, cols, rows) {
|
|
371
|
+
return new Promise((resolve, reject) => {
|
|
372
|
+
connection.client.shell({ term: connection.profile.terminalType, cols, rows }, (error, channel) => error ? reject(error) : resolve(channel));
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
function normalizeSignal(signal) {
|
|
376
|
+
if (signal === undefined)
|
|
377
|
+
return null;
|
|
378
|
+
const normalized = signal.startsWith('SIG') ? signal : `SIG${signal}`;
|
|
379
|
+
return normalized;
|
|
380
|
+
}
|
|
381
|
+
function clamp(value, min, max) {
|
|
382
|
+
return Math.min(max, Math.max(min, Number.isFinite(value) ? Math.floor(value) : min));
|
|
383
|
+
}
|
|
384
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"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"}
|
package/lib/tools.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import { ForwardManager } from './forwards.js';
|
|
3
|
+
import { SshConnector } from './connector.js';
|
|
4
|
+
import { SshStore } from './store.js';
|
|
5
|
+
import { AiTerminalManager } from './terminal.js';
|
|
6
|
+
export declare function registerSshTools(ctx: Context, store: SshStore, connector: SshConnector, forwards: ForwardManager, terminals: AiTerminalManager): void;
|
|
7
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AASjD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAE,iBAAiB,GAAG,IAAI,CAwBrJ"}
|
package/lib/tools.js
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { executeSshCommand } from './exec.js';
|
|
2
|
+
import { sessionDirectory, setSessionDirectory } from './directory.js';
|
|
3
|
+
const textOutput = {
|
|
4
|
+
schema: { type: 'string' },
|
|
5
|
+
render: (_args, value) => [{ type: 'text', text: String(value) }],
|
|
6
|
+
};
|
|
7
|
+
const APPROVAL_TOOLS = new Set(['ssh_exec', 'ssh_terminal_open', 'ssh_terminal_send', 'ssh_terminal_signal', 'ssh_forward_start', 'ssh_forward_stop']);
|
|
8
|
+
export function registerSshTools(ctx, store, connector, forwards, terminals) {
|
|
9
|
+
const tools = [
|
|
10
|
+
listTool(store),
|
|
11
|
+
cwdTool(store, connector),
|
|
12
|
+
execTool(store, connector),
|
|
13
|
+
terminalOpenTool(store, terminals),
|
|
14
|
+
terminalSendTool(store, terminals),
|
|
15
|
+
terminalReadTool(store, terminals),
|
|
16
|
+
terminalSignalTool(store, terminals),
|
|
17
|
+
terminalCloseTool(store, terminals),
|
|
18
|
+
forwardListTool(store, forwards),
|
|
19
|
+
forwardStartTool(store, forwards),
|
|
20
|
+
forwardStopTool(store, forwards),
|
|
21
|
+
];
|
|
22
|
+
for (const tool of tools)
|
|
23
|
+
ctx.tools.register(tool);
|
|
24
|
+
ctx.on('tools/pre-execute', async (exec, next) => {
|
|
25
|
+
if (!APPROVAL_TOOLS.has(exec.name))
|
|
26
|
+
return next();
|
|
27
|
+
const sessionId = exec.agent?.session.id;
|
|
28
|
+
if (sessionId === undefined)
|
|
29
|
+
return next();
|
|
30
|
+
const injection = store.injection(sessionId);
|
|
31
|
+
if (injection?.requireCommandApproval !== true)
|
|
32
|
+
return next();
|
|
33
|
+
return { kind: 'ask', reason: `SSH access to an injected remote host was requested by ${exec.name}.` };
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function listTool(store) {
|
|
37
|
+
return tool('ssh_list', 'List SSH connections explicitly injected into this DSH conversation. It never reveals credentials or non-injected hosts.', {}, async (_args, exec) => {
|
|
38
|
+
const injection = requireInjection(store, exec);
|
|
39
|
+
const profiles = injection.profileIds.map(id => store.profile(id)).filter(item => item !== undefined).map(profile => ({
|
|
40
|
+
id: profile.id, name: profile.name, host: profile.host, port: profile.port, username: profile.username,
|
|
41
|
+
...(profile.group === undefined ? {} : { group: profile.group }),
|
|
42
|
+
tags: profile.tags, permission: injection.permission, cwd: sessionDirectory(injection, profile.id),
|
|
43
|
+
}));
|
|
44
|
+
return json({ sessionId: injection.sessionId, profiles });
|
|
45
|
+
}, true);
|
|
46
|
+
}
|
|
47
|
+
function cwdTool(store, connector) {
|
|
48
|
+
return tool('ssh_set_cwd', 'Set and verify the working directory used by subsequent ssh_exec and ssh_terminal_open calls for one injected connection in this conversation.', {
|
|
49
|
+
profileId: { type: 'string', required: true, description: 'Exact injected profile id returned by ssh_list.' },
|
|
50
|
+
cwd: { type: 'string', required: true, description: 'Remote directory path. Supports absolute paths and paths under ~.' },
|
|
51
|
+
}, async (raw, exec) => {
|
|
52
|
+
const args = object(raw);
|
|
53
|
+
const profileId = string(args.profileId, 'profileId', 100);
|
|
54
|
+
requireProfile(store, exec, profileId, 'exec');
|
|
55
|
+
const sessionId = exec.agent.session.id;
|
|
56
|
+
const cwd = await setSessionDirectory(store, connector, sessionId, profileId, rawString(args.cwd, 'cwd', 4096), exec.signal);
|
|
57
|
+
return json({ profileId, cwd });
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function execTool(store, connector) {
|
|
61
|
+
return tool('ssh_exec', 'Run one non-interactive command on an SSH connection injected into this conversation. Use ssh_list first and pass an exact profile id. Output is bounded.', {
|
|
62
|
+
profileId: { type: 'string', required: true, description: 'Exact injected profile id returned by ssh_list.' },
|
|
63
|
+
command: { type: 'string', required: true, description: 'Command to execute on the remote host.' },
|
|
64
|
+
timeoutMs: { type: 'integer', description: 'Optional timeout between 1000 and 300000 milliseconds.' },
|
|
65
|
+
}, async (raw, exec) => {
|
|
66
|
+
const args = object(raw);
|
|
67
|
+
const profileId = string(args.profileId, 'profileId', 100);
|
|
68
|
+
const injection = requireInjection(store, exec);
|
|
69
|
+
requireProfile(store, exec, profileId, 'exec');
|
|
70
|
+
const settings = store.settings();
|
|
71
|
+
const timeout = optionalInteger(args.timeoutMs, 'timeoutMs', 1000, 300_000) ?? settings.defaultCommandTimeoutMs;
|
|
72
|
+
return json(await executeSshCommand(connector, profileId, string(args.command, 'command', 100_000), timeout, settings.maxOutputChars, exec.signal, sessionDirectory(injection, profileId)));
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function terminalOpenTool(store, terminals) {
|
|
76
|
+
return tool('ssh_terminal_open', 'Open an interactive terminal on an SSH connection injected with terminal permission. Returns an owner-scoped terminal id.', {
|
|
77
|
+
profileId: { type: 'string', required: true, description: 'Exact injected profile id returned by ssh_list.' },
|
|
78
|
+
name: { type: 'string', description: 'Optional terminal display name.' },
|
|
79
|
+
}, async (raw, exec) => {
|
|
80
|
+
const args = object(raw);
|
|
81
|
+
const profileId = string(args.profileId, 'profileId', 100);
|
|
82
|
+
const injection = requireInjection(store, exec);
|
|
83
|
+
const owner = requireProfile(store, exec, profileId, 'terminal');
|
|
84
|
+
const name = args.name === undefined ? undefined : string(args.name, 'name', 80);
|
|
85
|
+
return json(await terminals.create(owner.session.id, profileId, sessionDirectory(injection, profileId), name, exec.signal));
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
function terminalSendTool(store, terminals) {
|
|
89
|
+
return tool('ssh_terminal_send', 'Send text to an owned SSH terminal and wait until output becomes idle, the command exits, or timeout is reached.', {
|
|
90
|
+
terminalId: { type: 'string', required: true },
|
|
91
|
+
text: { type: 'string', required: true },
|
|
92
|
+
submit: { type: 'boolean', description: 'Append Enter after text. Defaults to true.' },
|
|
93
|
+
}, async (raw, exec) => {
|
|
94
|
+
const args = object(raw);
|
|
95
|
+
const owner = requireAnyInjection(store, exec);
|
|
96
|
+
return json(await terminals.send(owner.session.id, string(args.terminalId, 'terminalId', 200), {
|
|
97
|
+
text: rawString(args.text, 'text', 100_000), submit: args.submit !== false, signal: exec.signal,
|
|
98
|
+
}));
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function terminalReadTool(store, terminals) {
|
|
102
|
+
return tool('ssh_terminal_read', 'Read bounded scrollback from an owned SSH terminal.', {
|
|
103
|
+
terminalId: { type: 'string', required: true },
|
|
104
|
+
offset: { type: 'integer' },
|
|
105
|
+
count: { type: 'integer' },
|
|
106
|
+
}, async (raw, exec) => {
|
|
107
|
+
const args = object(raw);
|
|
108
|
+
const owner = requireAnyInjection(store, exec);
|
|
109
|
+
return json(terminals.get(owner.session.id, string(args.terminalId, 'terminalId', 200)).read({
|
|
110
|
+
...args.offset === undefined ? {} : { offset: integer(args.offset, 'offset', 0, 1_000_000) },
|
|
111
|
+
...args.count === undefined ? {} : { count: integer(args.count, 'count', 1, 2000) },
|
|
112
|
+
}));
|
|
113
|
+
}, true);
|
|
114
|
+
}
|
|
115
|
+
function terminalSignalTool(store, terminals) {
|
|
116
|
+
return tool('ssh_terminal_signal', 'Send an allowed POSIX signal to an owned SSH terminal.', {
|
|
117
|
+
terminalId: { type: 'string', required: true },
|
|
118
|
+
signal: { type: 'string', required: true, enum: ['SIGINT', 'SIGTERM', 'SIGKILL', 'SIGTSTP', 'SIGHUP'] },
|
|
119
|
+
}, async (raw, exec) => {
|
|
120
|
+
const args = object(raw);
|
|
121
|
+
const owner = requireAnyInjection(store, exec);
|
|
122
|
+
const signal = string(args.signal, 'signal', 16);
|
|
123
|
+
return json(await terminals.get(owner.session.id, string(args.terminalId, 'terminalId', 200)).signal(signal));
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function terminalCloseTool(store, terminals) {
|
|
127
|
+
return tool('ssh_terminal_close', 'Close an owned SSH terminal.', { terminalId: { type: 'string', required: true } }, async (raw, exec) => {
|
|
128
|
+
const owner = requireAnyInjection(store, exec);
|
|
129
|
+
const args = object(raw);
|
|
130
|
+
return json({ closed: await terminals.close(owner.session.id, string(args.terminalId, 'terminalId', 200)) });
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function forwardListTool(store, forwards) {
|
|
134
|
+
return tool('ssh_forward_list', 'List port-forward rules belonging to SSH connections injected into this conversation.', {}, async (_raw, exec) => {
|
|
135
|
+
const injection = requireInjection(store, exec);
|
|
136
|
+
const statuses = new Map(forwards.list().map(item => [item.ruleId, item]));
|
|
137
|
+
const rules = store.forwards().filter(rule => injection.profileIds.includes(rule.profileId)).map(rule => ({ ...rule, status: statuses.get(rule.id) }));
|
|
138
|
+
return json({ rules });
|
|
139
|
+
}, true);
|
|
140
|
+
}
|
|
141
|
+
function forwardStartTool(store, forwards) {
|
|
142
|
+
return tool('ssh_forward_start', 'Start an existing port-forward rule whose SSH profile is injected into this conversation. Public binds remain subject to plugin policy.', {
|
|
143
|
+
ruleId: { type: 'string', required: true },
|
|
144
|
+
}, async (raw, exec) => {
|
|
145
|
+
const ruleId = string(object(raw).ruleId, 'ruleId', 100);
|
|
146
|
+
requireForward(store, exec, ruleId);
|
|
147
|
+
return json(await forwards.start(ruleId));
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
function forwardStopTool(store, forwards) {
|
|
151
|
+
return tool('ssh_forward_stop', 'Stop a running port-forward rule whose SSH profile is injected into this conversation.', {
|
|
152
|
+
ruleId: { type: 'string', required: true },
|
|
153
|
+
}, async (raw, exec) => {
|
|
154
|
+
const ruleId = string(object(raw).ruleId, 'ruleId', 100);
|
|
155
|
+
requireForward(store, exec, ruleId);
|
|
156
|
+
return json(await forwards.stop(ruleId));
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
function tool(name, description, properties, execute, concurrencySafe = false) {
|
|
160
|
+
const required = Object.entries(properties).filter(([, value]) => value.required === true).map(([key]) => key);
|
|
161
|
+
const schemaProperties = Object.fromEntries(Object.entries(properties).map(([key, value]) => {
|
|
162
|
+
const { required: _required, ...schema } = value;
|
|
163
|
+
return [key, schema];
|
|
164
|
+
}));
|
|
165
|
+
return {
|
|
166
|
+
name, description,
|
|
167
|
+
parameters: { type: 'object', additionalProperties: false, properties: schemaProperties, required },
|
|
168
|
+
output: textOutput,
|
|
169
|
+
execute,
|
|
170
|
+
isConcurrencySafe: () => concurrencySafe,
|
|
171
|
+
presentCall: args => ({ card: 'terminal', title: presentTitle(name, args) }),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function presentTitle(name, value) {
|
|
175
|
+
const args = typeof value === 'object' && value !== null ? value : {};
|
|
176
|
+
if (name === 'ssh_exec' && typeof args.command === 'string')
|
|
177
|
+
return args.command;
|
|
178
|
+
if (typeof args.profileId === 'string')
|
|
179
|
+
return `${name} · ${args.profileId}`;
|
|
180
|
+
return name;
|
|
181
|
+
}
|
|
182
|
+
function requireInjection(store, exec) {
|
|
183
|
+
const sessionId = exec.agent?.session.id;
|
|
184
|
+
if (sessionId === undefined)
|
|
185
|
+
throw new Error('SSH tools require an owning DSH session');
|
|
186
|
+
const injection = store.injection(sessionId);
|
|
187
|
+
if (injection === undefined || injection.profileIds.length === 0)
|
|
188
|
+
throw new Error('No SSH connection is injected into this DSH session. Ask the user to open 远端 and inject one.');
|
|
189
|
+
return injection;
|
|
190
|
+
}
|
|
191
|
+
function requireAnyInjection(store, exec) {
|
|
192
|
+
requireInjection(store, exec);
|
|
193
|
+
if (exec.agent === undefined)
|
|
194
|
+
throw new Error('SSH tools require an owning agent');
|
|
195
|
+
return exec.agent;
|
|
196
|
+
}
|
|
197
|
+
function requireProfile(store, exec, profileId, permission) {
|
|
198
|
+
const injection = requireInjection(store, exec);
|
|
199
|
+
if (!injection.profileIds.includes(profileId))
|
|
200
|
+
throw new Error('The requested SSH profile is not injected into this DSH session');
|
|
201
|
+
if (permission === 'terminal' && injection.permission !== 'terminal')
|
|
202
|
+
throw new Error('This session injection permits one-shot commands but not interactive terminals');
|
|
203
|
+
if (exec.agent === undefined)
|
|
204
|
+
throw new Error('SSH tools require an owning agent');
|
|
205
|
+
return exec.agent;
|
|
206
|
+
}
|
|
207
|
+
function requireForward(store, exec, ruleId) {
|
|
208
|
+
const rule = store.forward(ruleId);
|
|
209
|
+
if (rule === undefined)
|
|
210
|
+
throw new Error('Port-forward rule was not found');
|
|
211
|
+
requireProfile(store, exec, rule.profileId, 'terminal');
|
|
212
|
+
}
|
|
213
|
+
function object(value) {
|
|
214
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
215
|
+
throw new Error('arguments must be an object');
|
|
216
|
+
return value;
|
|
217
|
+
}
|
|
218
|
+
function string(value, name, max) {
|
|
219
|
+
if (typeof value !== 'string' || value.trim().length === 0 || value.length > max)
|
|
220
|
+
throw new Error(`${name} must be a non-empty string with at most ${max} characters`);
|
|
221
|
+
return value.trim();
|
|
222
|
+
}
|
|
223
|
+
function rawString(value, name, max) {
|
|
224
|
+
if (typeof value !== 'string' || value.length > max)
|
|
225
|
+
throw new Error(`${name} must be a string with at most ${max} characters`);
|
|
226
|
+
return value;
|
|
227
|
+
}
|
|
228
|
+
function integer(value, name, min, max) {
|
|
229
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < min || value > max)
|
|
230
|
+
throw new Error(`${name} must be an integer between ${min} and ${max}`);
|
|
231
|
+
return value;
|
|
232
|
+
}
|
|
233
|
+
function optionalInteger(value, name, min, max) {
|
|
234
|
+
return value === undefined ? undefined : integer(value, name, min, max);
|
|
235
|
+
}
|
|
236
|
+
function json(value) { return JSON.stringify(value, null, 2); }
|
|
237
|
+
//# sourceMappingURL=tools.js.map
|