tmuxes 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 +213 -0
- package/bin/tmuxes.js +40 -0
- package/dist/config.js +31 -0
- package/dist/config.js.map +1 -0
- package/dist/exe.js +37 -0
- package/dist/exe.js.map +1 -0
- package/dist/exec.js +43 -0
- package/dist/exec.js.map +1 -0
- package/dist/files.js +250 -0
- package/dist/files.js.map +1 -0
- package/dist/foldersStore.js +101 -0
- package/dist/foldersStore.js.map +1 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.js +16 -0
- package/dist/logger.js.map +1 -0
- package/dist/openBrowser.js +31 -0
- package/dist/openBrowser.js.map +1 -0
- package/dist/platform.js +5 -0
- package/dist/platform.js.map +1 -0
- package/dist/rest/router.js +178 -0
- package/dist/rest/router.js.map +1 -0
- package/dist/targets.js +131 -0
- package/dist/targets.js.map +1 -0
- package/dist/tmux/builder.js +128 -0
- package/dist/tmux/builder.js.map +1 -0
- package/dist/tmux/formats.js +55 -0
- package/dist/tmux/formats.js.map +1 -0
- package/dist/tmux/sessions.js +99 -0
- package/dist/tmux/sessions.js.map +1 -0
- package/dist/validate.js +65 -0
- package/dist/validate.js.map +1 -0
- package/dist/winshell/manager.js +258 -0
- package/dist/winshell/manager.js.map +1 -0
- package/dist/ws/protocol.js +4 -0
- package/dist/ws/protocol.js.map +1 -0
- package/dist/ws/sshState.js +35 -0
- package/dist/ws/sshState.js.map +1 -0
- package/dist/ws/terminalSession.js +204 -0
- package/dist/ws/terminalSession.js.map +1 -0
- package/dist/ws/wsServer.js +151 -0
- package/dist/ws/wsServer.js.map +1 -0
- package/dist/wsl.js +35 -0
- package/dist/wsl.js.map +1 -0
- package/package.json +61 -0
- package/public/assets/index-CfimUdwq.js +44 -0
- package/public/assets/index-DeKxLCiY.css +1 -0
- package/public/index.html +13 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { config } from '../config.js';
|
|
3
|
+
import { sshDestination } from '../targets.js';
|
|
4
|
+
/**
|
|
5
|
+
* Single-quote a string for a POSIX shell. Needed ONLY for the remote ssh path:
|
|
6
|
+
* ssh concatenates the remote command words with spaces and re-parses them
|
|
7
|
+
* through the remote login shell, so each tmux arg must survive that re-parse.
|
|
8
|
+
* Local commands use pure argv (no shell) and need no quoting.
|
|
9
|
+
*/
|
|
10
|
+
export function sshQuote(arg) {
|
|
11
|
+
return `'${arg.replace(/'/g, `'\\''`)}'`;
|
|
12
|
+
}
|
|
13
|
+
/** Local tmux management/attach argv — passed straight to spawn (shell:false). */
|
|
14
|
+
export function localTmux(sub) {
|
|
15
|
+
return ['tmux', ...sub];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Remote tmux argv via the system ssh binary.
|
|
19
|
+
* - tty:false (management) → BatchMode + short ConnectTimeout so it fails fast
|
|
20
|
+
* and never hangs on a prompt. Remote args are sshQuote'd.
|
|
21
|
+
* - tty:true (interactive attach) → -tt forces a remote PTY (and SIGWINCH
|
|
22
|
+
* propagation). BatchMode is left default so host-key/agent prompts surface
|
|
23
|
+
* in the terminal. Remote args are NOT quoted here: this argv is handed to a
|
|
24
|
+
* PTY where the remote command words go to ssh as separate argv elements and
|
|
25
|
+
* our inputs are already allowlist-validated.
|
|
26
|
+
*/
|
|
27
|
+
export function remoteTmux(t, sub, opts) {
|
|
28
|
+
const dest = sshDestination(t);
|
|
29
|
+
const portArgs = t.port ? ['-p', String(t.port)] : [];
|
|
30
|
+
if (opts.tty) {
|
|
31
|
+
return [
|
|
32
|
+
'ssh',
|
|
33
|
+
'-tt',
|
|
34
|
+
'-o',
|
|
35
|
+
`ConnectTimeout=${config.ssh.connectTimeoutTty}`,
|
|
36
|
+
'-o',
|
|
37
|
+
`ServerAliveInterval=${config.ssh.serverAliveInterval}`,
|
|
38
|
+
...portArgs,
|
|
39
|
+
dest,
|
|
40
|
+
'tmux',
|
|
41
|
+
...sub,
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
return [
|
|
45
|
+
'ssh',
|
|
46
|
+
'-o',
|
|
47
|
+
'BatchMode=yes',
|
|
48
|
+
'-o',
|
|
49
|
+
`ConnectTimeout=${config.ssh.connectTimeoutMgmt}`,
|
|
50
|
+
...portArgs,
|
|
51
|
+
dest,
|
|
52
|
+
'tmux',
|
|
53
|
+
...sub.map(sshQuote),
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* tmux inside a WSL distro (Windows host). We use `--exec` (NOT `--`): `--`
|
|
58
|
+
* runs the command through the distro's login shell, which would re-parse tmux
|
|
59
|
+
* format strings (`#{...}` is a comment, `|` a pipe) and mangle them; `--exec`
|
|
60
|
+
* execs the binary directly with no shell, so argv maps straight through with
|
|
61
|
+
* no quoting — like the local path. The interactive case sets TERM via `env`
|
|
62
|
+
* because WSL does not inherit the Windows TERM.
|
|
63
|
+
*/
|
|
64
|
+
export function wslTmux(distro, sub, opts) {
|
|
65
|
+
const prefix = opts.tty ? ['env', 'TERM=xterm-256color', 'tmux'] : ['tmux'];
|
|
66
|
+
return ['wsl.exe', '-d', distro, '--exec', ...prefix, ...sub];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build a non-PTY argv to run an ARBITRARY command on a target (tmux, ls, cat,
|
|
70
|
+
* display-message, …). Same transport rules as management tmux: local runs the
|
|
71
|
+
* argv directly, wsl uses `--exec` (no shell), ssh uses BatchMode + sshQuote.
|
|
72
|
+
*/
|
|
73
|
+
export function commandArgv(t, argv) {
|
|
74
|
+
let full;
|
|
75
|
+
if (t.kind === 'local') {
|
|
76
|
+
full = argv;
|
|
77
|
+
}
|
|
78
|
+
else if (t.kind === 'wsl') {
|
|
79
|
+
full = ['wsl.exe', '-d', t.distro ?? '', '--exec', ...argv];
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
const portArgs = t.port ? ['-p', String(t.port)] : [];
|
|
83
|
+
full = [
|
|
84
|
+
'ssh',
|
|
85
|
+
'-o',
|
|
86
|
+
'BatchMode=yes',
|
|
87
|
+
'-o',
|
|
88
|
+
`ConnectTimeout=${config.ssh.connectTimeoutMgmt}`,
|
|
89
|
+
...portArgs,
|
|
90
|
+
sshDestination(t),
|
|
91
|
+
...argv.map(sshQuote),
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
return { file: full[0], args: full.slice(1) };
|
|
95
|
+
}
|
|
96
|
+
/** Build a management argv (no PTY) for a local / ssh / wsl target. */
|
|
97
|
+
export function managementArgv(t, sub) {
|
|
98
|
+
return commandArgv(t, ['tmux', ...sub]);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Build the create-a-new-session argv, always starting in the user's HOME:
|
|
102
|
+
* - local → tmux `-c <homedir>` (explicit, regardless of the server's cwd),
|
|
103
|
+
* - wsl → `wsl --cd ~` runs the command from the distro's home; tmux inherits,
|
|
104
|
+
* - ssh → the remote command already runs from the remote `$HOME`; tmux inherits.
|
|
105
|
+
* `sub` is the new-session subcommand (named or auto-named `-P` form).
|
|
106
|
+
*/
|
|
107
|
+
export function newSessionArgv(t, sub) {
|
|
108
|
+
if (t.kind === 'local') {
|
|
109
|
+
return { file: 'tmux', args: [...sub, '-c', homedir()] };
|
|
110
|
+
}
|
|
111
|
+
if (t.kind === 'wsl') {
|
|
112
|
+
return { file: 'wsl.exe', args: ['-d', t.distro ?? '', '--cd', '~', '--exec', 'tmux', ...sub] };
|
|
113
|
+
}
|
|
114
|
+
return commandArgv(t, ['tmux', ...sub]);
|
|
115
|
+
}
|
|
116
|
+
/** Build an interactive attach argv (run inside a PTY) for a local / ssh / wsl target. */
|
|
117
|
+
export function attachArgv(t, session) {
|
|
118
|
+
const sub = ['new-session', '-A', '-s', session]; // NO -d: the PTY supplies the terminal
|
|
119
|
+
let argv;
|
|
120
|
+
if (t.kind === 'local')
|
|
121
|
+
argv = localTmux(sub);
|
|
122
|
+
else if (t.kind === 'wsl')
|
|
123
|
+
argv = wslTmux(t.distro ?? '', sub, { tty: true });
|
|
124
|
+
else
|
|
125
|
+
argv = remoteTmux(t, sub, { tty: true });
|
|
126
|
+
return { file: argv[0], args: argv.slice(1) };
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder.js","sourceRoot":"","sources":["../../src/tmux/builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,cAAc,EAAe,MAAM,eAAe,CAAC;AAE5D;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;AAC3C,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,SAAS,CAAC,GAAa;IACrC,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,UAAU,CACxB,CAAS,EACT,GAAa,EACb,IAAsB;IAEtB,MAAM,IAAI,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEtD,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,OAAO;YACL,KAAK;YACL,KAAK;YACL,IAAI;YACJ,kBAAkB,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE;YAChD,IAAI;YACJ,uBAAuB,MAAM,CAAC,GAAG,CAAC,mBAAmB,EAAE;YACvD,GAAG,QAAQ;YACX,IAAI;YACJ,MAAM;YACN,GAAG,GAAG;SACP,CAAC;IACJ,CAAC;IAED,OAAO;QACL,KAAK;QACL,IAAI;QACJ,eAAe;QACf,IAAI;QACJ,kBAAkB,MAAM,CAAC,GAAG,CAAC,kBAAkB,EAAE;QACjD,GAAG,QAAQ;QACX,IAAI;QACJ,MAAM;QACN,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC;KACrB,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,OAAO,CAAC,MAAc,EAAE,GAAa,EAAE,IAAsB;IAC3E,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,qBAAqB,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC5E,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;AAChE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,CAAS,EAAE,IAAc;IACnD,IAAI,IAAc,CAAC;IACnB,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACvB,IAAI,GAAG,IAAI,CAAC;IACd,CAAC;SAAM,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC5B,IAAI,GAAG,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,CAAC;IAC9D,CAAC;SAAM,CAAC;QACN,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,IAAI,GAAG;YACL,KAAK;YACL,IAAI;YACJ,eAAe;YACf,IAAI;YACJ,kBAAkB,MAAM,CAAC,GAAG,CAAC,kBAAkB,EAAE;YACjD,GAAG,QAAQ;YACX,cAAc,CAAC,CAAC,CAAC;YACjB,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;SACtB,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAChD,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,cAAc,CAAC,CAAS,EAAE,GAAa;IACrD,OAAO,WAAW,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,CAAS,EAAE,GAAa;IACrD,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACvB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAC3D,CAAC;IACD,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QACrB,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC;IAClG,CAAC;IACD,OAAO,WAAW,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,UAAU,CAAC,CAAS,EAAE,OAAe;IACnD,MAAM,GAAG,GAAG,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,uCAAuC;IACzF,IAAI,IAAc,CAAC;IACnB,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;QAAE,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;SACzC,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK;QAAE,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;;QACzE,IAAI,GAAG,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** tmux -F format strings + parsers.
|
|
2
|
+
*
|
|
3
|
+
* tmux ESCAPES control characters in -F output (e.g. 0x1f comes back as the
|
|
4
|
+
* literal text "\037"), so we cannot use a control byte as a separator. We use
|
|
5
|
+
* a printable "|" and place the only free-form field (the name) LAST, then
|
|
6
|
+
* rejoin the remainder — robust even if a name itself contains "|", because
|
|
7
|
+
* the leading numeric fields never do. */
|
|
8
|
+
const SEP = '|';
|
|
9
|
+
export const SESSION_FORMAT = [
|
|
10
|
+
'#{session_windows}',
|
|
11
|
+
'#{session_attached}',
|
|
12
|
+
'#{session_created}',
|
|
13
|
+
'#{session_name}', // free-form → must be last
|
|
14
|
+
].join(SEP);
|
|
15
|
+
export const WINDOW_FORMAT = [
|
|
16
|
+
'#{window_index}',
|
|
17
|
+
'#{window_panes}',
|
|
18
|
+
'#{window_active}',
|
|
19
|
+
'#{window_name}', // free-form → must be last
|
|
20
|
+
].join(SEP);
|
|
21
|
+
export function parseSessions(stdout) {
|
|
22
|
+
return stdout
|
|
23
|
+
.split('\n')
|
|
24
|
+
.filter((l) => l.length > 0)
|
|
25
|
+
.map((line) => {
|
|
26
|
+
const parts = line.split(SEP);
|
|
27
|
+
return {
|
|
28
|
+
windows: Number(parts[0]) || 0,
|
|
29
|
+
attached: Number(parts[1]) > 0,
|
|
30
|
+
created: Number(parts[2]) || 0,
|
|
31
|
+
name: parts.slice(3).join(SEP), // name may legitimately contain "|"
|
|
32
|
+
};
|
|
33
|
+
})
|
|
34
|
+
.filter((s) => s.name);
|
|
35
|
+
}
|
|
36
|
+
export function parseWindows(stdout) {
|
|
37
|
+
return stdout
|
|
38
|
+
.split('\n')
|
|
39
|
+
.filter((l) => l.length > 0)
|
|
40
|
+
.map((line) => {
|
|
41
|
+
const parts = line.split(SEP);
|
|
42
|
+
return {
|
|
43
|
+
index: Number(parts[0]) || 0,
|
|
44
|
+
panes: Number(parts[1]) || 0,
|
|
45
|
+
active: Number(parts[2]) > 0,
|
|
46
|
+
name: parts.slice(3).join(SEP),
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/** stderr patterns that mean "no sessions" rather than a real error. */
|
|
51
|
+
const EMPTY_RE = /no server running|no sessions|error connecting to/i;
|
|
52
|
+
export function isEmptySessionsError(stderr) {
|
|
53
|
+
return EMPTY_RE.test(stderr);
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=formats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formats.js","sourceRoot":"","sources":["../../src/tmux/formats.ts"],"names":[],"mappings":"AAAA;;;;;;2CAM2C;AAE3C,MAAM,GAAG,GAAG,GAAG,CAAC;AAEhB,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,oBAAoB;IACpB,qBAAqB;IACrB,oBAAoB;IACpB,iBAAiB,EAAE,2BAA2B;CAC/C,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEZ,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,iBAAiB;IACjB,iBAAiB;IACjB,kBAAkB;IAClB,gBAAgB,EAAE,2BAA2B;CAC9C,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAiBZ,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,MAAM;SACV,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;SAC3B,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC9B,OAAO;YACL,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC9B,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YAC9B,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC9B,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,oCAAoC;SACrE,CAAC;IACJ,CAAC,CAAC;SACD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,OAAO,MAAM;SACV,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;SAC3B,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC9B,OAAO;YACL,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC5B,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC5B,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YAC5B,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;SAC/B,CAAC;IACJ,CAAC,CAAC,CAAC;AACP,CAAC;AAED,wEAAwE;AACxE,MAAM,QAAQ,GAAG,oDAAoD,CAAC;AAEtE,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { runCommand } from '../exec.js';
|
|
2
|
+
import { managementArgv, newSessionArgv } from './builder.js';
|
|
3
|
+
import { SESSION_FORMAT, WINDOW_FORMAT, parseSessions, parseWindows, isEmptySessionsError, } from './formats.js';
|
|
4
|
+
import { isValidSessionName } from '../validate.js';
|
|
5
|
+
/** A management error carrying the HTTP status the router should return. */
|
|
6
|
+
export class TmuxError extends Error {
|
|
7
|
+
status;
|
|
8
|
+
constructor(status, message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.name = 'TmuxError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
// Hard ceiling so a wedged ssh can never hang a request (ssh also has ConnectTimeout).
|
|
15
|
+
const REMOTE_TIMEOUT_MS = 15_000;
|
|
16
|
+
function timeoutFor(target) {
|
|
17
|
+
// Bound ssh (network) and wsl (possible cold start) so a request can't hang.
|
|
18
|
+
return target.kind === 'local' ? undefined : REMOTE_TIMEOUT_MS;
|
|
19
|
+
}
|
|
20
|
+
async function run(target, sub) {
|
|
21
|
+
const { file, args } = managementArgv(target, sub);
|
|
22
|
+
return runCommand(file, args, { timeoutMs: timeoutFor(target) });
|
|
23
|
+
}
|
|
24
|
+
function firstStderrLine(stderr) {
|
|
25
|
+
return stderr.trim().split('\n')[0] || 'command failed';
|
|
26
|
+
}
|
|
27
|
+
export async function listSessions(target) {
|
|
28
|
+
const r = await run(target, ['list-sessions', '-F', SESSION_FORMAT]);
|
|
29
|
+
if (r.code === 0)
|
|
30
|
+
return parseSessions(r.stdout);
|
|
31
|
+
// "no server running" / "no sessions" is the normal empty case.
|
|
32
|
+
if (isEmptySessionsError(r.stderr))
|
|
33
|
+
return [];
|
|
34
|
+
throw new TmuxError(502, firstStderrLine(r.stderr));
|
|
35
|
+
}
|
|
36
|
+
export async function createSession(target, opts) {
|
|
37
|
+
let name = opts.name;
|
|
38
|
+
// New sessions always start in the user's home directory (newSessionArgv).
|
|
39
|
+
if (name) {
|
|
40
|
+
if (!isValidSessionName(name))
|
|
41
|
+
throw new TmuxError(400, 'invalid session name');
|
|
42
|
+
const { file, args } = newSessionArgv(target, ['new-session', '-d', '-s', name]);
|
|
43
|
+
const r = await runCommand(file, args, { timeoutMs: timeoutFor(target) });
|
|
44
|
+
if (r.code !== 0) {
|
|
45
|
+
if (/duplicate session/i.test(r.stderr)) {
|
|
46
|
+
throw new TmuxError(409, `session "${name}" already exists`);
|
|
47
|
+
}
|
|
48
|
+
throw new TmuxError(502, firstStderrLine(r.stderr));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// Let tmux assign a numeric name and report it back.
|
|
53
|
+
const { file, args } = newSessionArgv(target, ['new-session', '-d', '-P', '-F', '#{session_name}']);
|
|
54
|
+
const r = await runCommand(file, args, { timeoutMs: timeoutFor(target) });
|
|
55
|
+
if (r.code !== 0)
|
|
56
|
+
throw new TmuxError(502, firstStderrLine(r.stderr));
|
|
57
|
+
name = r.stdout.trim();
|
|
58
|
+
}
|
|
59
|
+
if (opts.command && opts.command.length > 0) {
|
|
60
|
+
// Type the command literally, then press Enter. Two send-keys calls so the
|
|
61
|
+
// command text can never be misparsed as a key name.
|
|
62
|
+
await run(target, ['send-keys', '-t', name, '-l', opts.command]);
|
|
63
|
+
await run(target, ['send-keys', '-t', name, 'Enter']);
|
|
64
|
+
}
|
|
65
|
+
return { name };
|
|
66
|
+
}
|
|
67
|
+
export async function renameSession(target, name, newName) {
|
|
68
|
+
if (!isValidSessionName(newName))
|
|
69
|
+
throw new TmuxError(400, 'invalid new session name');
|
|
70
|
+
const r = await run(target, ['rename-session', '-t', name, newName]);
|
|
71
|
+
if (r.code === 0)
|
|
72
|
+
return;
|
|
73
|
+
if (/can't find session|session not found/i.test(r.stderr)) {
|
|
74
|
+
throw new TmuxError(404, `session "${name}" not found`);
|
|
75
|
+
}
|
|
76
|
+
if (/duplicate session/i.test(r.stderr)) {
|
|
77
|
+
throw new TmuxError(409, `session "${newName}" already exists`);
|
|
78
|
+
}
|
|
79
|
+
throw new TmuxError(502, firstStderrLine(r.stderr));
|
|
80
|
+
}
|
|
81
|
+
export async function killSession(target, name) {
|
|
82
|
+
const r = await run(target, ['kill-session', '-t', name]);
|
|
83
|
+
if (r.code === 0)
|
|
84
|
+
return;
|
|
85
|
+
if (/can't find session|session not found|no server running/i.test(r.stderr)) {
|
|
86
|
+
throw new TmuxError(404, `session "${name}" not found`);
|
|
87
|
+
}
|
|
88
|
+
throw new TmuxError(502, firstStderrLine(r.stderr));
|
|
89
|
+
}
|
|
90
|
+
export async function listWindows(target, name) {
|
|
91
|
+
const r = await run(target, ['list-windows', '-t', name, '-F', WINDOW_FORMAT]);
|
|
92
|
+
if (r.code === 0)
|
|
93
|
+
return parseWindows(r.stdout);
|
|
94
|
+
if (/can't find session|session not found|no server running/i.test(r.stderr)) {
|
|
95
|
+
throw new TmuxError(404, `session "${name}" not found`);
|
|
96
|
+
}
|
|
97
|
+
throw new TmuxError(502, firstStderrLine(r.stderr));
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../src/tmux/sessions.ts"],"names":[],"mappings":"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"}
|
package/dist/validate.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Allowlist validation. Every value that reaches a spawned tmux/ssh argv is
|
|
3
|
+
* checked here first. We never spawn a shell (argv arrays + shell:false), so
|
|
4
|
+
* this is defense-in-depth, but it also keeps tmux target syntax sane.
|
|
5
|
+
*/
|
|
6
|
+
// '.' and ':' are tmux target delimiters (session:window.pane) — forbidden in names.
|
|
7
|
+
const SESSION_NAME_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
8
|
+
const HOST_RE = /^[A-Za-z0-9._-]{1,255}$/;
|
|
9
|
+
const USER_RE = /^[A-Za-z0-9._-]{1,64}$/;
|
|
10
|
+
// ssh_config Host aliases: word chars, dots, hyphens (no wildcards/spaces).
|
|
11
|
+
const HOST_ALIAS_RE = /^[A-Za-z0-9._-]{1,255}$/;
|
|
12
|
+
export function isValidSessionName(name) {
|
|
13
|
+
return typeof name === 'string' && SESSION_NAME_RE.test(name);
|
|
14
|
+
}
|
|
15
|
+
export function isValidHost(host) {
|
|
16
|
+
return typeof host === 'string' && HOST_RE.test(host);
|
|
17
|
+
}
|
|
18
|
+
export function isValidUser(user) {
|
|
19
|
+
return typeof user === 'string' && USER_RE.test(user);
|
|
20
|
+
}
|
|
21
|
+
export function isValidHostAlias(alias) {
|
|
22
|
+
return typeof alias === 'string' && HOST_ALIAS_RE.test(alias);
|
|
23
|
+
}
|
|
24
|
+
export function isValidPort(port) {
|
|
25
|
+
return (typeof port === 'number' &&
|
|
26
|
+
Number.isInteger(port) &&
|
|
27
|
+
port >= 1 &&
|
|
28
|
+
port <= 65535);
|
|
29
|
+
}
|
|
30
|
+
/** Terminal geometry sent by clients. */
|
|
31
|
+
export function isValidDimension(n) {
|
|
32
|
+
return typeof n === 'number' && Number.isInteger(n) && n >= 1 && n <= 1000;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Parse a "user@host:port" spec (used by the TMUXES_HOSTS env override).
|
|
36
|
+
* Returns null if any component is invalid.
|
|
37
|
+
*/
|
|
38
|
+
export function parseHostSpec(spec) {
|
|
39
|
+
const trimmed = spec.trim();
|
|
40
|
+
if (!trimmed)
|
|
41
|
+
return null;
|
|
42
|
+
let user;
|
|
43
|
+
let rest = trimmed;
|
|
44
|
+
const at = rest.indexOf('@');
|
|
45
|
+
if (at !== -1) {
|
|
46
|
+
user = rest.slice(0, at);
|
|
47
|
+
rest = rest.slice(at + 1);
|
|
48
|
+
if (!isValidUser(user))
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
let port;
|
|
52
|
+
const colon = rest.indexOf(':');
|
|
53
|
+
if (colon !== -1) {
|
|
54
|
+
const portStr = rest.slice(colon + 1);
|
|
55
|
+
rest = rest.slice(0, colon);
|
|
56
|
+
const parsed = Number(portStr);
|
|
57
|
+
if (!isValidPort(parsed))
|
|
58
|
+
return null;
|
|
59
|
+
port = parsed;
|
|
60
|
+
}
|
|
61
|
+
if (!isValidHost(rest))
|
|
62
|
+
return null;
|
|
63
|
+
return { user, host: rest, port };
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,qFAAqF;AACrF,MAAM,eAAe,GAAG,uBAAuB,CAAC;AAChD,MAAM,OAAO,GAAG,yBAAyB,CAAC;AAC1C,MAAM,OAAO,GAAG,wBAAwB,CAAC;AACzC,4EAA4E;AAC5E,MAAM,aAAa,GAAG,yBAAyB,CAAC;AAEhD,MAAM,UAAU,kBAAkB,CAAC,IAAa;IAC9C,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAa;IACvC,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAa;IACvC,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAa;IACvC,OAAO,CACL,OAAO,IAAI,KAAK,QAAQ;QACxB,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;QACtB,IAAI,IAAI,CAAC;QACT,IAAI,IAAI,KAAK,CACd,CAAC;AACJ,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,gBAAgB,CAAC,CAAU;IACzC,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AAC7E,CAAC;AAQD;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE1B,IAAI,IAAwB,CAAC;IAC7B,IAAI,IAAI,GAAG,OAAO,CAAC;IACnB,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;QACd,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACzB,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAC1B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IACtC,CAAC;IAED,IAAI,IAAwB,CAAC;IAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;QACjB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QACtC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAC/B,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC;QACtC,IAAI,GAAG,MAAM,CAAC;IAChB,CAAC;IAED,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import * as pty from 'node-pty';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { isWindows } from '../platform.js';
|
|
6
|
+
import { log } from '../logger.js';
|
|
7
|
+
const SCROLLBACK_CAP = 256 * 1024; // bytes of history replayed to a new client
|
|
8
|
+
function nowEpoch() {
|
|
9
|
+
return Math.floor(Date.now() / 1000);
|
|
10
|
+
}
|
|
11
|
+
/** One persistent native shell (its own pty), shared by 0..N attached clients.
|
|
12
|
+
* Survives client disconnects (the pty lives until killed or it exits), which
|
|
13
|
+
* is what gives "refresh / reconnect / multi-tab" persistence. */
|
|
14
|
+
export class WinShellSession {
|
|
15
|
+
name;
|
|
16
|
+
shell;
|
|
17
|
+
ptyProc;
|
|
18
|
+
onClosed;
|
|
19
|
+
created = nowEpoch();
|
|
20
|
+
clients = new Set();
|
|
21
|
+
scroll = [];
|
|
22
|
+
scrollBytes = 0;
|
|
23
|
+
disposed = false;
|
|
24
|
+
constructor(name, shell, ptyProc, onClosed) {
|
|
25
|
+
this.name = name;
|
|
26
|
+
this.shell = shell;
|
|
27
|
+
this.ptyProc = ptyProc;
|
|
28
|
+
this.onClosed = onClosed;
|
|
29
|
+
this.ptyProc.onData((d) => this.onData(d));
|
|
30
|
+
this.ptyProc.onExit(({ exitCode }) => this.onExit(exitCode));
|
|
31
|
+
}
|
|
32
|
+
get attached() {
|
|
33
|
+
return this.clients.size > 0;
|
|
34
|
+
}
|
|
35
|
+
onData(data) {
|
|
36
|
+
const buf = Buffer.from(data, 'utf8');
|
|
37
|
+
this.scroll.push(buf);
|
|
38
|
+
this.scrollBytes += buf.length;
|
|
39
|
+
while (this.scrollBytes > SCROLLBACK_CAP && this.scroll.length > 1) {
|
|
40
|
+
this.scrollBytes -= this.scroll.shift().length;
|
|
41
|
+
}
|
|
42
|
+
for (const c of this.clients)
|
|
43
|
+
if (c.isOpen())
|
|
44
|
+
c.sendBinary(buf);
|
|
45
|
+
}
|
|
46
|
+
onExit(code) {
|
|
47
|
+
if (this.disposed)
|
|
48
|
+
return;
|
|
49
|
+
this.disposed = true;
|
|
50
|
+
for (const c of this.clients) {
|
|
51
|
+
c.sendControl({ type: 'exit', code });
|
|
52
|
+
c.close(1000, 'shell exited');
|
|
53
|
+
}
|
|
54
|
+
this.clients.clear();
|
|
55
|
+
this.onClosed(this.name);
|
|
56
|
+
}
|
|
57
|
+
/** Add a client: replay history, then it's live. Caller sends `ready` after. */
|
|
58
|
+
attach(client) {
|
|
59
|
+
if (this.scroll.length)
|
|
60
|
+
client.sendBinary(Buffer.concat(this.scroll));
|
|
61
|
+
this.clients.add(client);
|
|
62
|
+
}
|
|
63
|
+
detach(client) {
|
|
64
|
+
this.clients.delete(client); // pty stays alive — persistence across reconnects
|
|
65
|
+
}
|
|
66
|
+
write(data) {
|
|
67
|
+
if (!this.disposed)
|
|
68
|
+
this.ptyProc.write(data);
|
|
69
|
+
}
|
|
70
|
+
resize(cols, rows) {
|
|
71
|
+
try {
|
|
72
|
+
this.ptyProc.resize(cols, rows);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
/* pty may have exited */
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
info() {
|
|
79
|
+
return { name: this.name, windows: 1, attached: this.attached, created: this.created };
|
|
80
|
+
}
|
|
81
|
+
dispose() {
|
|
82
|
+
if (this.disposed) {
|
|
83
|
+
this.onClosed(this.name);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this.disposed = true;
|
|
87
|
+
try {
|
|
88
|
+
this.ptyProc.kill();
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
/* already gone */
|
|
92
|
+
}
|
|
93
|
+
for (const c of this.clients)
|
|
94
|
+
c.close(1000, 'killed');
|
|
95
|
+
this.clients.clear();
|
|
96
|
+
this.onClosed(this.name);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const NAME_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
100
|
+
/** Owns all native shell sessions for the local Windows machine. */
|
|
101
|
+
export class WinShellManager {
|
|
102
|
+
sessions = new Map();
|
|
103
|
+
shells;
|
|
104
|
+
constructor(shells) {
|
|
105
|
+
this.shells = shells ?? detectShells();
|
|
106
|
+
}
|
|
107
|
+
listShells() {
|
|
108
|
+
return this.shells.map((s) => ({ id: s.id, label: s.label }));
|
|
109
|
+
}
|
|
110
|
+
resolveShell(shellId) {
|
|
111
|
+
if (shellId) {
|
|
112
|
+
const found = this.shells.find((s) => s.id === shellId);
|
|
113
|
+
if (found)
|
|
114
|
+
return found;
|
|
115
|
+
}
|
|
116
|
+
if (this.shells.length === 0)
|
|
117
|
+
throw new Error('no shell available');
|
|
118
|
+
return this.shells[0];
|
|
119
|
+
}
|
|
120
|
+
list() {
|
|
121
|
+
return [...this.sessions.values()]
|
|
122
|
+
.map((s) => s.info())
|
|
123
|
+
.sort((a, b) => a.created - b.created);
|
|
124
|
+
}
|
|
125
|
+
windows(name) {
|
|
126
|
+
const s = this.sessions.get(name);
|
|
127
|
+
if (!s)
|
|
128
|
+
return [];
|
|
129
|
+
return [{ index: 0, name: s.shell.label, panes: 1, active: true }];
|
|
130
|
+
}
|
|
131
|
+
has(name) {
|
|
132
|
+
return this.sessions.has(name);
|
|
133
|
+
}
|
|
134
|
+
autoName(shell) {
|
|
135
|
+
for (let i = 1; i < 10000; i++) {
|
|
136
|
+
const candidate = `${shell.id}-${i}`;
|
|
137
|
+
if (!this.sessions.has(candidate))
|
|
138
|
+
return candidate;
|
|
139
|
+
}
|
|
140
|
+
return `${shell.id}-${nowEpoch()}`;
|
|
141
|
+
}
|
|
142
|
+
/** Create a new shell session. Returns the (possibly auto-assigned) name. */
|
|
143
|
+
create(opts) {
|
|
144
|
+
const shell = this.resolveShell(opts.shellId);
|
|
145
|
+
const name = opts.name ?? this.autoName(shell);
|
|
146
|
+
if (!NAME_RE.test(name))
|
|
147
|
+
throw new ManagerError(400, 'invalid session name');
|
|
148
|
+
if (this.sessions.has(name))
|
|
149
|
+
throw new ManagerError(409, `session "${name}" already exists`);
|
|
150
|
+
const cols = opts.cols ?? 80;
|
|
151
|
+
const rows = opts.rows ?? 24;
|
|
152
|
+
let proc;
|
|
153
|
+
try {
|
|
154
|
+
proc = pty.spawn(shell.file, shell.args, {
|
|
155
|
+
name: 'xterm-256color',
|
|
156
|
+
cols,
|
|
157
|
+
rows,
|
|
158
|
+
cwd: homedir(),
|
|
159
|
+
env: { ...process.env, TERM: 'xterm-256color' },
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
catch (e) {
|
|
163
|
+
throw new ManagerError(502, `failed to start ${shell.label}: ${e.message}`);
|
|
164
|
+
}
|
|
165
|
+
const session = new WinShellSession(name, shell, proc, (n) => this.sessions.delete(n));
|
|
166
|
+
this.sessions.set(name, session);
|
|
167
|
+
if (opts.command && opts.command.length > 0)
|
|
168
|
+
session.write(`${opts.command}\r`);
|
|
169
|
+
log.info(`winshell created ${name} (${shell.label})`);
|
|
170
|
+
return { name };
|
|
171
|
+
}
|
|
172
|
+
rename(oldName, newName) {
|
|
173
|
+
if (!NAME_RE.test(newName))
|
|
174
|
+
throw new ManagerError(400, 'invalid new session name');
|
|
175
|
+
const s = this.sessions.get(oldName);
|
|
176
|
+
if (!s)
|
|
177
|
+
throw new ManagerError(404, `session "${oldName}" not found`);
|
|
178
|
+
if (this.sessions.has(newName))
|
|
179
|
+
throw new ManagerError(409, `session "${newName}" already exists`);
|
|
180
|
+
// Re-key the same live instance (its pty handlers read `this.name`).
|
|
181
|
+
this.sessions.delete(oldName);
|
|
182
|
+
s.name = newName;
|
|
183
|
+
this.sessions.set(newName, s);
|
|
184
|
+
}
|
|
185
|
+
kill(name) {
|
|
186
|
+
const s = this.sessions.get(name);
|
|
187
|
+
if (!s)
|
|
188
|
+
throw new ManagerError(404, `session "${name}" not found`);
|
|
189
|
+
s.dispose();
|
|
190
|
+
}
|
|
191
|
+
/** Attach a client to an existing session, creating it (default shell) if missing. */
|
|
192
|
+
attachOrCreate(name, cols, rows, client) {
|
|
193
|
+
let s = this.sessions.get(name);
|
|
194
|
+
if (!s) {
|
|
195
|
+
this.create({ name, cols, rows });
|
|
196
|
+
s = this.sessions.get(name);
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
s.resize(cols, rows);
|
|
200
|
+
}
|
|
201
|
+
s.attach(client);
|
|
202
|
+
return s;
|
|
203
|
+
}
|
|
204
|
+
disposeAll() {
|
|
205
|
+
for (const s of [...this.sessions.values()])
|
|
206
|
+
s.dispose();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/** Manager error carrying an HTTP status for the router. */
|
|
210
|
+
export class ManagerError extends Error {
|
|
211
|
+
status;
|
|
212
|
+
constructor(status, message) {
|
|
213
|
+
super(message);
|
|
214
|
+
this.status = status;
|
|
215
|
+
this.name = 'ManagerError';
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/** Detect launchable shells for this machine. Windows: PowerShell 7 (if on
|
|
219
|
+
* PATH) → Windows PowerShell → cmd → Git Bash. Otherwise (test/fake): the
|
|
220
|
+
* user's shell, so the whole path can be exercised on Linux. */
|
|
221
|
+
export function detectShells() {
|
|
222
|
+
if (!isWindows) {
|
|
223
|
+
const file = process.env.SHELL || '/bin/bash';
|
|
224
|
+
return [{ id: 'shell', label: file.split('/').pop() || 'shell', file, args: ['-i'] }];
|
|
225
|
+
}
|
|
226
|
+
const shells = [];
|
|
227
|
+
const sysRoot = process.env.SystemRoot || process.env.windir || 'C:\\Windows';
|
|
228
|
+
const pwsh = findOnPath('pwsh.exe');
|
|
229
|
+
if (pwsh)
|
|
230
|
+
shells.push({ id: 'pwsh', label: 'PowerShell 7', file: pwsh, args: ['-NoLogo'] });
|
|
231
|
+
const winPs = join(sysRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
|
232
|
+
if (existsSync(winPs))
|
|
233
|
+
shells.push({ id: 'powershell', label: 'Windows PowerShell', file: winPs, args: ['-NoLogo'] });
|
|
234
|
+
const cmd = join(sysRoot, 'System32', 'cmd.exe');
|
|
235
|
+
if (existsSync(cmd))
|
|
236
|
+
shells.push({ id: 'cmd', label: 'Command Prompt', file: cmd, args: [] });
|
|
237
|
+
const gitBash = 'C:\\Program Files\\Git\\bin\\bash.exe';
|
|
238
|
+
if (existsSync(gitBash))
|
|
239
|
+
shells.push({ id: 'gitbash', label: 'Git Bash', file: gitBash, args: ['-i', '-l'] });
|
|
240
|
+
// Last-resort fallback so the target is never shell-less.
|
|
241
|
+
if (shells.length === 0)
|
|
242
|
+
shells.push({ id: 'cmd', label: 'Command Prompt', file: 'cmd.exe', args: [] });
|
|
243
|
+
return shells;
|
|
244
|
+
}
|
|
245
|
+
function findOnPath(exe) {
|
|
246
|
+
const dirs = (process.env.PATH || '').split(isWindows ? ';' : ':');
|
|
247
|
+
for (const dir of dirs) {
|
|
248
|
+
if (!dir)
|
|
249
|
+
continue;
|
|
250
|
+
const candidate = join(dir, exe);
|
|
251
|
+
if (existsSync(candidate))
|
|
252
|
+
return candidate;
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
/** Singleton used by the REST and WS layers. */
|
|
257
|
+
export const winShell = new WinShellManager();
|
|
258
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/winshell/manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAsBnC,MAAM,cAAc,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC,4CAA4C;AAE/E,SAAS,QAAQ;IACf,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;AACvC,CAAC;AAED;;mEAEmE;AACnE,MAAM,OAAO,eAAe;IAQjB;IACE;IACQ;IACA;IAVV,OAAO,GAAG,QAAQ,EAAE,CAAC;IACtB,OAAO,GAAG,IAAI,GAAG,EAAe,CAAC;IACjC,MAAM,GAAa,EAAE,CAAC;IACtB,WAAW,GAAG,CAAC,CAAC;IAChB,QAAQ,GAAG,KAAK,CAAC;IAEzB,YACS,IAAY,EACV,KAAe,EACP,OAAiB,EACjB,QAAgC;QAH1C,SAAI,GAAJ,IAAI,CAAQ;QACV,UAAK,GAAL,KAAK,CAAU;QACP,YAAO,GAAP,OAAO,CAAU;QACjB,aAAQ,GAAR,QAAQ,CAAwB;QAEjD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/D,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC;IAC/B,CAAC;IAEO,MAAM,CAAC,IAAY;QACzB,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,WAAW,IAAI,GAAG,CAAC,MAAM,CAAC;QAC/B,OAAO,IAAI,CAAC,WAAW,GAAG,cAAc,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnE,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAG,CAAC,MAAM,CAAC;QAClD,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO;YAAE,IAAI,CAAC,CAAC,MAAM,EAAE;gBAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAClE,CAAC;IAEO,MAAM,CAAC,IAAmB;QAChC,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7B,CAAC,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YACtC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;QAChC,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,gFAAgF;IAChF,MAAM,CAAC,MAAmB;QACxB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM;YAAE,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QACtE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IAED,MAAM,CAAC,MAAmB;QACxB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,kDAAkD;IACjF,CAAC;IAED,KAAK,CAAC,IAAY;QAChB,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,CAAC,IAAY,EAAE,IAAY;QAC/B,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAClC,CAAC;QAAC,MAAM,CAAC;YACP,yBAAyB;QAC3B,CAAC;IACH,CAAC;IAED,IAAI;QACF,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;IACzF,CAAC;IAED,OAAO;QACL,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QACtB,CAAC;QAAC,MAAM,CAAC;YACP,kBAAkB;QACpB,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO;YAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACtD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;CACF;AAED,MAAM,OAAO,GAAG,uBAAuB,CAAC;AAExC,oEAAoE;AACpE,MAAM,OAAO,eAAe;IAClB,QAAQ,GAAG,IAAI,GAAG,EAA2B,CAAC;IAC9C,MAAM,CAAa;IAE3B,YAAY,MAAmB;QAC7B,IAAI,CAAC,MAAM,GAAG,MAAM,IAAI,YAAY,EAAE,CAAC;IACzC,CAAC;IAED,UAAU;QACR,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAChE,CAAC;IAEO,YAAY,CAAC,OAAgB;QACnC,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC;YACxD,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAC;QAC1B,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;QACpE,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACxB,CAAC;IAED,IAAI;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;aAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACpB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,CAAC,IAAY;QAClB,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,CAAC;YAAE,OAAO,EAAE,CAAC;QAClB,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAEO,QAAQ,CAAC,KAAe;QAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,MAAM,SAAS,GAAG,GAAG,KAAK,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC;gBAAE,OAAO,SAAS,CAAC;QACtD,CAAC;QACD,OAAO,GAAG,KAAK,CAAC,EAAE,IAAI,QAAQ,EAAE,EAAE,CAAC;IACrC,CAAC;IAED,6EAA6E;IAC7E,MAAM,CAAC,IAAyF;QAG9F,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC/C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,YAAY,CAAC,GAAG,EAAE,sBAAsB,CAAC,CAAC;QAC7E,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,YAAY,CAAC,GAAG,EAAE,YAAY,IAAI,kBAAkB,CAAC,CAAC;QAE7F,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;QAC7B,IAAI,IAAc,CAAC;QACnB,IAAI,CAAC;YACH,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE;gBACvC,IAAI,EAAE,gBAAgB;gBACtB,IAAI;gBACJ,IAAI;gBACJ,GAAG,EAAE,OAAO,EAAE;gBACd,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,gBAAgB,EAAE;aAChD,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,IAAI,YAAY,CAAC,GAAG,EAAE,mBAAmB,KAAK,CAAC,KAAK,KAAM,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;QACzF,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QACvF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACjC,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;QAChF,GAAG,CAAC,IAAI,CAAC,oBAAoB,IAAI,KAAK,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC;QACtD,OAAO,EAAE,IAAI,EAAE,CAAC;IAClB,CAAC;IAED,MAAM,CAAC,OAAe,EAAE,OAAe;QACrC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,MAAM,IAAI,YAAY,CAAC,GAAG,EAAE,0BAA0B,CAAC,CAAC;QACpF,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,YAAY,CAAC,GAAG,EAAE,YAAY,OAAO,aAAa,CAAC,CAAC;QACtE,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,MAAM,IAAI,YAAY,CAAC,GAAG,EAAE,YAAY,OAAO,kBAAkB,CAAC,CAAC;QACnG,qEAAqE;QACrE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC,CAAC,IAAI,GAAG,OAAO,CAAC;QACjB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAChC,CAAC;IAED,IAAI,CAAC,IAAY;QACf,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,YAAY,CAAC,GAAG,EAAE,YAAY,IAAI,aAAa,CAAC,CAAC;QACnE,CAAC,CAAC,OAAO,EAAE,CAAC;IACd,CAAC;IAED,sFAAsF;IACtF,cAAc,CAAC,IAAY,EAAE,IAAY,EAAE,IAAY,EAAE,MAAmB;QAC1E,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,CAAC,CAAC,EAAE,CAAC;YACP,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YAClC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACvB,CAAC;QACD,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACjB,OAAO,CAAC,CAAC;IACX,CAAC;IAED,UAAU;QACR,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;YAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IAC3D,CAAC;CACF;AAED,4DAA4D;AAC5D,MAAM,OAAO,YAAa,SAAQ,KAAK;IAE5B;IADT,YACS,MAAc,EACrB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHR,WAAM,GAAN,MAAM,CAAQ;QAIrB,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED;;iEAEiE;AACjE,MAAM,UAAU,YAAY;IAC1B,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,WAAW,CAAC;QAC9C,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACxF,CAAC;IAED,MAAM,MAAM,GAAe,EAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,aAAa,CAAC;IAE9E,MAAM,IAAI,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IACpC,IAAI,IAAI;QAAE,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IAE5F,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACvF,IAAI,UAAU,CAAC,KAAK,CAAC;QAAE,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,oBAAoB,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IAEtH,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;IACjD,IAAI,UAAU,CAAC,GAAG,CAAC;QAAE,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAE9F,MAAM,OAAO,GAAG,uCAAuC,CAAC;IACxD,IAAI,UAAU,CAAC,OAAO,CAAC;QAAE,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAE9G,0DAA0D;IAC1D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IACxG,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,UAAU,CAAC,GAAW;IAC7B,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACnE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC,GAAG;YAAE,SAAS;QACnB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACjC,IAAI,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;IAC9C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gDAAgD;AAChD,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,eAAe,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/ws/protocol.ts"],"names":[],"mappings":"AAAA;8DAC8D"}
|