@jhebe/web-terminal 0.2.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 +86 -0
- package/bin/web-terminal.mjs +309 -0
- package/dist/index.d.ts +3 -0
- package/dist/protocol.d.ts +98 -0
- package/dist/terminal-client.d.ts +62 -0
- package/dist/terminal-view.d.ts +28 -0
- package/dist/web-terminal.css +2 -0
- package/dist/web-terminal.js +352 -0
- package/dist-server/server/history-buffer.d.ts +8 -0
- package/dist-server/server/history-buffer.js +59 -0
- package/dist-server/server/index.d.ts +2 -0
- package/dist-server/server/index.js +2 -0
- package/dist-server/server/session-manager.d.ts +51 -0
- package/dist-server/server/session-manager.js +274 -0
- package/dist-server/server/terminal-server.d.ts +16 -0
- package/dist-server/server/terminal-server.js +250 -0
- package/dist-server/src/protocol.d.ts +98 -0
- package/dist-server/src/protocol.js +199 -0
- package/package.json +59 -0
- package/scripts/prepare-node-pty.mjs +28 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { FitAddon as e } from "@xterm/addon-fit";
|
|
2
|
+
import { Terminal as t } from "@xterm/xterm";
|
|
3
|
+
//#region src/protocol.ts
|
|
4
|
+
function n(e) {
|
|
5
|
+
let t = r(e);
|
|
6
|
+
if (!(!t || typeof t.type != "string")) switch (t.type) {
|
|
7
|
+
case "sessions": return Array.isArray(t.sessions) && t.sessions.every(i) ? {
|
|
8
|
+
type: "sessions",
|
|
9
|
+
sessions: t.sessions
|
|
10
|
+
} : void 0;
|
|
11
|
+
case "opened": return a(t) && i(t.session) ? {
|
|
12
|
+
type: "opened",
|
|
13
|
+
requestId: t.requestId,
|
|
14
|
+
session: t.session
|
|
15
|
+
} : void 0;
|
|
16
|
+
case "closed":
|
|
17
|
+
case "written":
|
|
18
|
+
case "unobserved": return a(t) && typeof t.sessionId == "string" ? {
|
|
19
|
+
type: t.type,
|
|
20
|
+
requestId: t.requestId,
|
|
21
|
+
sessionId: t.sessionId
|
|
22
|
+
} : void 0;
|
|
23
|
+
case "observing": return a(t) && i(t.session) && typeof t.history == "string" ? {
|
|
24
|
+
type: "observing",
|
|
25
|
+
requestId: t.requestId,
|
|
26
|
+
session: t.session,
|
|
27
|
+
history: t.history
|
|
28
|
+
} : void 0;
|
|
29
|
+
case "attached": return a(t) && i(t.session) && typeof t.history == "string" ? {
|
|
30
|
+
type: "attached",
|
|
31
|
+
requestId: t.requestId,
|
|
32
|
+
session: t.session,
|
|
33
|
+
history: t.history
|
|
34
|
+
} : void 0;
|
|
35
|
+
case "detached": return a(t) && typeof t.sessionId == "string" ? {
|
|
36
|
+
type: "detached",
|
|
37
|
+
requestId: t.requestId,
|
|
38
|
+
sessionId: t.sessionId
|
|
39
|
+
} : void 0;
|
|
40
|
+
case "output": return typeof t.sessionId == "string" && typeof t.data == "string" ? {
|
|
41
|
+
type: "output",
|
|
42
|
+
sessionId: t.sessionId,
|
|
43
|
+
data: t.data
|
|
44
|
+
} : void 0;
|
|
45
|
+
case "exit": return typeof t.sessionId == "string" && typeof t.exitCode == "number" && (t.signal === void 0 || typeof t.signal == "number") ? {
|
|
46
|
+
type: "exit",
|
|
47
|
+
sessionId: t.sessionId,
|
|
48
|
+
exitCode: t.exitCode,
|
|
49
|
+
...t.signal === void 0 ? {} : { signal: t.signal }
|
|
50
|
+
} : void 0;
|
|
51
|
+
case "error": return typeof t.message == "string" && (t.requestId === void 0 || typeof t.requestId == "string") ? {
|
|
52
|
+
type: "error",
|
|
53
|
+
message: t.message,
|
|
54
|
+
...t.requestId === void 0 ? {} : { requestId: t.requestId }
|
|
55
|
+
} : void 0;
|
|
56
|
+
default: return;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function r(e) {
|
|
60
|
+
if (typeof e == "string") try {
|
|
61
|
+
let t = JSON.parse(e);
|
|
62
|
+
return o(t) ? t : void 0;
|
|
63
|
+
} catch {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function i(e) {
|
|
68
|
+
return o(e) && typeof e.id == "string" && typeof e.name == "string" && (e.state === "running" || e.state === "exited") && typeof e.attached == "boolean" && typeof e.observers == "number" && typeof e.createdAt == "string" && typeof e.lastActiveAt == "string" && (e.exitCode === void 0 || typeof e.exitCode == "number");
|
|
69
|
+
}
|
|
70
|
+
function a(e) {
|
|
71
|
+
return typeof e.requestId == "string";
|
|
72
|
+
}
|
|
73
|
+
function o(e) {
|
|
74
|
+
return typeof e == "object" && !!e;
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/terminal-view.ts
|
|
78
|
+
var s = class {
|
|
79
|
+
terminal;
|
|
80
|
+
fitAddon = new e();
|
|
81
|
+
target;
|
|
82
|
+
controller;
|
|
83
|
+
resizeObserver;
|
|
84
|
+
disposed = !1;
|
|
85
|
+
constructor(e) {
|
|
86
|
+
this.target = e.target, this.controller = e.controller, this.terminal = new t({
|
|
87
|
+
cursorBlink: !0,
|
|
88
|
+
convertEol: !0,
|
|
89
|
+
fontFamily: "\"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo, monospace",
|
|
90
|
+
fontSize: 14,
|
|
91
|
+
theme: {
|
|
92
|
+
background: "#111318",
|
|
93
|
+
foreground: "#e7e9ee",
|
|
94
|
+
cursor: "#8bd5ca",
|
|
95
|
+
selectionBackground: "#3b4252"
|
|
96
|
+
},
|
|
97
|
+
...e.terminalOptions
|
|
98
|
+
}), this.target.classList.add("web-terminal"), this.terminal.loadAddon(this.fitAddon), this.terminal.open(this.target), this.terminal.onData((e) => this.controller.input(e)), this.terminal.onResize(({ cols: e, rows: t }) => this.controller.resize(e, t)), this.resizeObserver = new ResizeObserver(() => this.fit()), this.resizeObserver.observe(this.target), this.fit();
|
|
99
|
+
}
|
|
100
|
+
write(e) {
|
|
101
|
+
this.disposed || this.terminal.write(e);
|
|
102
|
+
}
|
|
103
|
+
focus() {
|
|
104
|
+
this.assertActive(), this.terminal.focus();
|
|
105
|
+
}
|
|
106
|
+
fit() {
|
|
107
|
+
this.disposed || this.fitAddon.fit();
|
|
108
|
+
}
|
|
109
|
+
dispose() {
|
|
110
|
+
this.disposed || (this.disposeVisuals(), this.controller.release(this));
|
|
111
|
+
}
|
|
112
|
+
disposeVisuals() {
|
|
113
|
+
this.disposed || (this.disposed = !0, this.resizeObserver.disconnect(), this.terminal.dispose(), this.target.classList.remove("web-terminal"));
|
|
114
|
+
}
|
|
115
|
+
assertActive() {
|
|
116
|
+
if (this.disposed) throw Error("This terminal view has been disposed.");
|
|
117
|
+
}
|
|
118
|
+
}, c = class {
|
|
119
|
+
options;
|
|
120
|
+
sessionListeners = /* @__PURE__ */ new Set();
|
|
121
|
+
eventListeners = /* @__PURE__ */ new Set();
|
|
122
|
+
pending = /* @__PURE__ */ new Map();
|
|
123
|
+
socket;
|
|
124
|
+
connectPromise;
|
|
125
|
+
sessionSnapshot = [];
|
|
126
|
+
attachedSessionId;
|
|
127
|
+
view;
|
|
128
|
+
viewSessionId;
|
|
129
|
+
bufferedOutput = [];
|
|
130
|
+
constructor(e) {
|
|
131
|
+
this.options = e, e.autoConnect !== !1 && this.connect().catch((e) => this.reportError(d(e)));
|
|
132
|
+
}
|
|
133
|
+
get sessions() {
|
|
134
|
+
return this.sessionSnapshot;
|
|
135
|
+
}
|
|
136
|
+
get connected() {
|
|
137
|
+
return this.socket?.readyState === WebSocket.OPEN;
|
|
138
|
+
}
|
|
139
|
+
get attachedSession() {
|
|
140
|
+
return this.attachedSessionId;
|
|
141
|
+
}
|
|
142
|
+
connect() {
|
|
143
|
+
return this.connected ? Promise.resolve() : (this.connectPromise ||= new Promise((e, t) => {
|
|
144
|
+
let r = typeof this.options.socketUrl == "function" ? this.options.socketUrl() : this.options.socketUrl, i = new WebSocket(r);
|
|
145
|
+
this.socket = i, i.addEventListener("open", () => {
|
|
146
|
+
this.connectPromise = void 0, this.emitEvent({ type: "connected" }), e();
|
|
147
|
+
}, { once: !0 }), i.addEventListener("message", (e) => {
|
|
148
|
+
this.handleMessage(n(e.data));
|
|
149
|
+
}), i.addEventListener("error", () => {
|
|
150
|
+
let e = /* @__PURE__ */ Error("Terminal WebSocket connection failed.");
|
|
151
|
+
this.connectPromise && (this.connectPromise = void 0, t(e)), this.reportError(e);
|
|
152
|
+
}), i.addEventListener("close", () => {
|
|
153
|
+
this.socket === i && (this.socket = void 0, this.connectPromise = void 0, this.attachedSessionId = void 0, this.rejectPending(/* @__PURE__ */ Error("Terminal connection closed before the operation completed.")), this.emitEvent({ type: "disconnected" }));
|
|
154
|
+
});
|
|
155
|
+
}), this.connectPromise);
|
|
156
|
+
}
|
|
157
|
+
disconnect() {
|
|
158
|
+
this.socket?.close(1e3, "Client disconnected");
|
|
159
|
+
}
|
|
160
|
+
async open(e = {}) {
|
|
161
|
+
let t = await this.request({
|
|
162
|
+
type: "open",
|
|
163
|
+
requestId: u(),
|
|
164
|
+
...e.name === void 0 ? {} : { name: e.name }
|
|
165
|
+
});
|
|
166
|
+
if (t.type !== "opened") throw Error("Unexpected response while opening a terminal session.");
|
|
167
|
+
return this.emitEvent({
|
|
168
|
+
type: "opened",
|
|
169
|
+
sessionId: t.session.id
|
|
170
|
+
}), t.session;
|
|
171
|
+
}
|
|
172
|
+
async close(e) {
|
|
173
|
+
if ((await this.request({
|
|
174
|
+
type: "close",
|
|
175
|
+
requestId: u(),
|
|
176
|
+
sessionId: e
|
|
177
|
+
})).type !== "closed") throw Error("Unexpected response while closing a terminal session.");
|
|
178
|
+
this.attachedSessionId === e && (this.attachedSessionId = void 0, this.disposeView()), this.emitEvent({
|
|
179
|
+
type: "closed",
|
|
180
|
+
sessionId: e
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
async write(e, t) {
|
|
184
|
+
if ((await this.request({
|
|
185
|
+
type: "write",
|
|
186
|
+
requestId: u(),
|
|
187
|
+
sessionId: e,
|
|
188
|
+
data: t
|
|
189
|
+
})).type !== "written") throw Error("Unexpected response while writing to a terminal session.");
|
|
190
|
+
}
|
|
191
|
+
async attach(e) {
|
|
192
|
+
let t = await this.request({
|
|
193
|
+
type: "attach",
|
|
194
|
+
requestId: u(),
|
|
195
|
+
sessionId: e
|
|
196
|
+
});
|
|
197
|
+
if (t.type !== "attached") throw Error("Unexpected response while attaching a terminal session.");
|
|
198
|
+
return this.attachedSessionId = e, this.bufferedOutput = [], this.emitEvent({
|
|
199
|
+
type: "attached",
|
|
200
|
+
sessionId: e
|
|
201
|
+
}), {
|
|
202
|
+
session: t.session,
|
|
203
|
+
history: t.history
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
async detach() {
|
|
207
|
+
let e = this.attachedSessionId;
|
|
208
|
+
if (!e) {
|
|
209
|
+
this.disposeView();
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
await this.detachSession(e);
|
|
213
|
+
}
|
|
214
|
+
async detachSession(e) {
|
|
215
|
+
if ((await this.request({
|
|
216
|
+
type: "detach",
|
|
217
|
+
requestId: u(),
|
|
218
|
+
sessionId: e
|
|
219
|
+
})).type !== "detached") throw Error("Unexpected response while detaching a terminal session.");
|
|
220
|
+
this.attachedSessionId === e && (this.attachedSessionId = void 0), this.viewSessionId === e && this.disposeView(), this.emitEvent({
|
|
221
|
+
type: "detached",
|
|
222
|
+
sessionId: e
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
async display(e, t, n = {}) {
|
|
226
|
+
let r = await this.attach(e);
|
|
227
|
+
this.disposeView();
|
|
228
|
+
let i = new s({
|
|
229
|
+
target: t,
|
|
230
|
+
controller: {
|
|
231
|
+
input: (e) => this.input(e),
|
|
232
|
+
resize: (e, t) => this.resize(e, t),
|
|
233
|
+
release: (e) => this.releaseView(e)
|
|
234
|
+
},
|
|
235
|
+
...n.terminalOptions === void 0 ? {} : { terminalOptions: n.terminalOptions }
|
|
236
|
+
});
|
|
237
|
+
this.view = i, this.viewSessionId = e, i.write(r.history);
|
|
238
|
+
for (let e of this.bufferedOutput) i.write(e);
|
|
239
|
+
return this.bufferedOutput = [], i.focus(), i;
|
|
240
|
+
}
|
|
241
|
+
subscribeSessions(e) {
|
|
242
|
+
return this.sessionListeners.add(e), e(this.sessionSnapshot), () => this.sessionListeners.delete(e);
|
|
243
|
+
}
|
|
244
|
+
subscribeEvents(e) {
|
|
245
|
+
return this.eventListeners.add(e), () => this.eventListeners.delete(e);
|
|
246
|
+
}
|
|
247
|
+
dispose() {
|
|
248
|
+
this.disposeView(), this.disconnect(), this.sessionListeners.clear(), this.eventListeners.clear();
|
|
249
|
+
}
|
|
250
|
+
input(e) {
|
|
251
|
+
this.attachedSessionId && this.send({
|
|
252
|
+
type: "input",
|
|
253
|
+
sessionId: this.attachedSessionId,
|
|
254
|
+
data: e
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
resize(e, t) {
|
|
258
|
+
this.attachedSessionId && this.send({
|
|
259
|
+
type: "resize",
|
|
260
|
+
sessionId: this.attachedSessionId,
|
|
261
|
+
cols: e,
|
|
262
|
+
rows: t
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
releaseView(e) {
|
|
266
|
+
if (this.view !== e) return;
|
|
267
|
+
let t = this.viewSessionId;
|
|
268
|
+
this.view = void 0, this.viewSessionId = void 0, t && this.detachSession(t).catch((e) => this.reportError(d(e)));
|
|
269
|
+
}
|
|
270
|
+
disposeView() {
|
|
271
|
+
let e = this.view;
|
|
272
|
+
this.view = void 0, this.viewSessionId = void 0, e?.disposeVisuals();
|
|
273
|
+
}
|
|
274
|
+
async request(e) {
|
|
275
|
+
if (!("requestId" in e)) throw Error("Request messages require an identity.");
|
|
276
|
+
return await this.connect(), new Promise((t, n) => {
|
|
277
|
+
this.pending.set(e.requestId, {
|
|
278
|
+
resolve: t,
|
|
279
|
+
reject: n
|
|
280
|
+
});
|
|
281
|
+
try {
|
|
282
|
+
this.send(e);
|
|
283
|
+
} catch (t) {
|
|
284
|
+
this.pending.delete(e.requestId), n(d(t));
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
send(e) {
|
|
289
|
+
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) throw Error("Terminal client is not connected.");
|
|
290
|
+
this.socket.send(JSON.stringify(e));
|
|
291
|
+
}
|
|
292
|
+
handleMessage(e) {
|
|
293
|
+
if (!e) {
|
|
294
|
+
this.reportError(/* @__PURE__ */ Error("Received an invalid terminal server message."));
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
if (e.type === "sessions") {
|
|
298
|
+
this.sessionSnapshot = Object.freeze(e.sessions.map((e) => Object.freeze({ ...e })));
|
|
299
|
+
let t = this.attachedSessionId;
|
|
300
|
+
t && !this.sessionSnapshot.some((e) => e.id === t) && (this.attachedSessionId = void 0, this.disposeView(), this.emitEvent({
|
|
301
|
+
type: "detached",
|
|
302
|
+
sessionId: t
|
|
303
|
+
}));
|
|
304
|
+
for (let e of this.sessionListeners) e(this.sessionSnapshot);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
if (e.type === "output") {
|
|
308
|
+
if (e.sessionId !== this.attachedSessionId) return;
|
|
309
|
+
this.view ? this.view.write(e.data) : this.bufferedOutput.push(e.data);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (e.type === "exit") {
|
|
313
|
+
this.emitEvent({
|
|
314
|
+
type: "exit",
|
|
315
|
+
sessionId: e.sessionId,
|
|
316
|
+
exitCode: e.exitCode
|
|
317
|
+
});
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (e.type === "error") {
|
|
321
|
+
let t = Error(e.message);
|
|
322
|
+
if (e.requestId) {
|
|
323
|
+
let n = this.pending.get(e.requestId);
|
|
324
|
+
this.pending.delete(e.requestId), n?.reject(t);
|
|
325
|
+
} else this.reportError(t);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
let t = this.pending.get(e.requestId);
|
|
329
|
+
t && (this.pending.delete(e.requestId), t.resolve(e));
|
|
330
|
+
}
|
|
331
|
+
rejectPending(e) {
|
|
332
|
+
for (let t of this.pending.values()) t.reject(e);
|
|
333
|
+
this.pending.clear();
|
|
334
|
+
}
|
|
335
|
+
emitEvent(e) {
|
|
336
|
+
for (let t of this.eventListeners) t(e);
|
|
337
|
+
}
|
|
338
|
+
reportError(e) {
|
|
339
|
+
this.options.onError ? this.options.onError(e) : console.error(e);
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
function l(e) {
|
|
343
|
+
return new c(e);
|
|
344
|
+
}
|
|
345
|
+
function u() {
|
|
346
|
+
return crypto.randomUUID();
|
|
347
|
+
}
|
|
348
|
+
function d(e) {
|
|
349
|
+
return e instanceof Error ? e : Error(String(e));
|
|
350
|
+
}
|
|
351
|
+
//#endregion
|
|
352
|
+
export { c as TerminalClient, s as TerminalView, l as createTerminalClient };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export class HistoryBuffer {
|
|
2
|
+
limitBytes;
|
|
3
|
+
chunks = [];
|
|
4
|
+
totalBytes = 0;
|
|
5
|
+
constructor(limitBytes) {
|
|
6
|
+
this.limitBytes = limitBytes;
|
|
7
|
+
}
|
|
8
|
+
append(data) {
|
|
9
|
+
if (this.limitBytes === 0 || data.length === 0) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
let normalized = data;
|
|
13
|
+
let bytes = Buffer.byteLength(normalized);
|
|
14
|
+
if (bytes > this.limitBytes) {
|
|
15
|
+
normalized = trimToLastBytes(normalized, this.limitBytes);
|
|
16
|
+
bytes = Buffer.byteLength(normalized);
|
|
17
|
+
this.chunks = [];
|
|
18
|
+
this.totalBytes = 0;
|
|
19
|
+
}
|
|
20
|
+
this.chunks.push({ data: normalized, bytes });
|
|
21
|
+
this.totalBytes += bytes;
|
|
22
|
+
while (this.totalBytes > this.limitBytes &&
|
|
23
|
+
this.chunks.length > 1) {
|
|
24
|
+
const removed = this.chunks.shift();
|
|
25
|
+
if (removed) {
|
|
26
|
+
this.totalBytes -= removed.bytes;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const first = this.chunks[0];
|
|
30
|
+
if (first && this.totalBytes > this.limitBytes) {
|
|
31
|
+
const excess = this.totalBytes - this.limitBytes;
|
|
32
|
+
first.data = trimToLastBytes(first.data, first.bytes - excess);
|
|
33
|
+
const nextBytes = Buffer.byteLength(first.data);
|
|
34
|
+
this.totalBytes += nextBytes - first.bytes;
|
|
35
|
+
first.bytes = nextBytes;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
read() {
|
|
39
|
+
return this.chunks.map(({ data }) => data).join('');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function trimToLastBytes(value, limitBytes) {
|
|
43
|
+
const characters = Array.from(value);
|
|
44
|
+
let bytes = 0;
|
|
45
|
+
let start = characters.length;
|
|
46
|
+
while (start > 0) {
|
|
47
|
+
const character = characters[start - 1];
|
|
48
|
+
if (character === undefined) {
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
const characterBytes = Buffer.byteLength(character);
|
|
52
|
+
if (bytes + characterBytes > limitBytes) {
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
bytes += characterBytes;
|
|
56
|
+
start -= 1;
|
|
57
|
+
}
|
|
58
|
+
return characters.slice(start).join('');
|
|
59
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { createTerminalSessionManager, type OpenSessionOptions, type SessionAttachment, type SessionEvent, type SessionObservation, type SessionSink, type TerminalSessionManager, type TerminalSessionManagerOptions, } from './session-manager.js';
|
|
2
|
+
export { attachTerminalServer, type TerminalAction, type TerminalServer, type TerminalServerOptions, } from './terminal-server.js';
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { SessionSummary } from '../src/protocol.js';
|
|
2
|
+
export interface TerminalSessionManagerOptions {
|
|
3
|
+
shell?: string;
|
|
4
|
+
shellArgs?: string[];
|
|
5
|
+
cwd?: string;
|
|
6
|
+
env?: NodeJS.ProcessEnv;
|
|
7
|
+
historyLimitBytes?: number;
|
|
8
|
+
idleTimeoutMs?: number;
|
|
9
|
+
maxSessions?: number;
|
|
10
|
+
maxObserversPerSession?: number;
|
|
11
|
+
maxWriteBytes?: number;
|
|
12
|
+
maxInputBytes?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface OpenSessionOptions {
|
|
15
|
+
name?: string;
|
|
16
|
+
shell?: string;
|
|
17
|
+
shellArgs?: string[];
|
|
18
|
+
cwd?: string;
|
|
19
|
+
env?: NodeJS.ProcessEnv;
|
|
20
|
+
}
|
|
21
|
+
export interface SessionSink {
|
|
22
|
+
output(data: string): void;
|
|
23
|
+
exit(exitCode: number, signal?: number): void;
|
|
24
|
+
}
|
|
25
|
+
export interface SessionAttachment {
|
|
26
|
+
readonly session: SessionSummary;
|
|
27
|
+
readonly history: string;
|
|
28
|
+
}
|
|
29
|
+
export interface SessionObservation {
|
|
30
|
+
readonly session: SessionSummary;
|
|
31
|
+
readonly history: string;
|
|
32
|
+
}
|
|
33
|
+
export interface SessionEvent {
|
|
34
|
+
readonly type: 'opened' | 'attached' | 'detached' | 'exited' | 'closed' | 'expired';
|
|
35
|
+
readonly session: SessionSummary;
|
|
36
|
+
}
|
|
37
|
+
export interface TerminalSessionManager {
|
|
38
|
+
readonly sessions: readonly SessionSummary[];
|
|
39
|
+
open(options?: OpenSessionOptions): SessionSummary;
|
|
40
|
+
close(sessionId: string): void;
|
|
41
|
+
write(sessionId: string, data: string): void;
|
|
42
|
+
observe(sessionId: string, observerId: string, sink: SessionSink): SessionObservation;
|
|
43
|
+
unobserve(sessionId: string, observerId: string): void;
|
|
44
|
+
attach(sessionId: string, attachmentId: string, sink: SessionSink): SessionAttachment;
|
|
45
|
+
detach(sessionId: string, attachmentId: string): void;
|
|
46
|
+
input(sessionId: string, attachmentId: string, data: string): void;
|
|
47
|
+
resize(sessionId: string, attachmentId: string, cols: number, rows: number): void;
|
|
48
|
+
subscribe(listener: (event: SessionEvent) => void): () => void;
|
|
49
|
+
shutdown(): void;
|
|
50
|
+
}
|
|
51
|
+
export declare function createTerminalSessionManager(options?: TerminalSessionManagerOptions): TerminalSessionManager;
|