-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcreate-command.ts
More file actions
107 lines (85 loc) · 3.06 KB
/
Copy pathcreate-command.ts
File metadata and controls
107 lines (85 loc) · 3.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import { IPty, spawn } from '@homebridge/node-pty-prebuilt-multiarch';
import chalk from 'chalk';
import { Router } from 'express';
import WebSocket from 'ws';
import { Session, SocketServer } from '../socket-server.js';
import { ShellUtils } from '../../utils/shell.js';
export enum ConnectCommand {
TERMINAL = 'start terminal',
APPLY = 'apply',
PLAN = 'plan',
IMPORT = 'import',
REFRESH = 'refresh',
DESTROY = 'destroy',
INIT = 'init',
TEST = 'test',
}
interface Params {
name: ConnectCommand;
command?: string[];
spawnCommand?: (body: Record<string, unknown>, ws: WebSocket, session: Session) => IPty | Promise<IPty>;
onExit?: (exitCode: number, ws: WebSocket, session: Session) => Promise<Record<string, unknown> | undefined | void>;
}
export function createCommandHandler({ name, command, spawnCommand, onExit }: Params): Router {
if (!Object.values(ConnectCommand).includes(name)) {
throw new Error(`Unknown command ${name}. Please check code`);
}
if (!command && !spawnCommand) {
throw new Error('One of command or spawnCommand must be provided to createCommandHandler');
}
const router = Router({
mergeParams: true,
});
router.post('/:sessionId/start', async (req, res) => {
const { sessionId } = req.params;
console.log(`Received request to ${name}, sessionId: ${sessionId}`)
if (!sessionId) {
return res.status(400).json({ error: 'SessionId must be provided' });
}
const manager = SocketServer.get();
const session = manager.getSession(sessionId);
if (!session) {
return res.status(400).json({ error: 'SessionId does not exist' });
}
const { ws, server, clientId } = session;
if (!ws) {
return res.status(400).json({ error: 'SessionId not open' });
}
if (session.pty) {
return res.status(304).json({ status: 'Already started' })
}
console.log(req.body);
const pty = spawnCommand ? await spawnCommand(req.body, ws, session) : spawn(ShellUtils.getDefaultShell(), command!, {
name: 'xterm-color',
cols: 80,
rows: 30,
cwd: process.env.HOME,
env: process.env
});
session.pty = pty;
pty.onData((data) => {
ws.send(Buffer.from(data, 'utf8'));
});
ws.on('message', (message) => {
pty.write(message.toString('utf8'));
})
pty.onExit(async ({ exitCode, signal }) => {
console.log(`Command ${name} exited with exit code`, exitCode);
ws.send(Buffer.from(chalk.blue(`Session ended exit code ${exitCode}`), 'utf8'))
const mainWs = SocketServer.get().getMainConnection(clientId);
if (mainWs) {
const data = await onExit?.(exitCode, ws, session) ?? {}
mainWs.send(JSON.stringify({ key: `finish:${sessionId}`, success: exitCode === 0, data })) // Send finish command only if client connection is still open
}
ws.terminate();
server.close();
})
ws.on('close', () => {
console.log('Session ws closed. Shutting down pty');
pty.kill();
manager.removeSession(sessionId)
})
return res.status(204).json({});
});
return router;
}