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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +213 -0
  3. package/bin/tmuxes.js +40 -0
  4. package/dist/config.js +31 -0
  5. package/dist/config.js.map +1 -0
  6. package/dist/exe.js +37 -0
  7. package/dist/exe.js.map +1 -0
  8. package/dist/exec.js +43 -0
  9. package/dist/exec.js.map +1 -0
  10. package/dist/files.js +250 -0
  11. package/dist/files.js.map +1 -0
  12. package/dist/foldersStore.js +101 -0
  13. package/dist/foldersStore.js.map +1 -0
  14. package/dist/index.js +73 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/logger.js +16 -0
  17. package/dist/logger.js.map +1 -0
  18. package/dist/openBrowser.js +31 -0
  19. package/dist/openBrowser.js.map +1 -0
  20. package/dist/platform.js +5 -0
  21. package/dist/platform.js.map +1 -0
  22. package/dist/rest/router.js +178 -0
  23. package/dist/rest/router.js.map +1 -0
  24. package/dist/targets.js +131 -0
  25. package/dist/targets.js.map +1 -0
  26. package/dist/tmux/builder.js +128 -0
  27. package/dist/tmux/builder.js.map +1 -0
  28. package/dist/tmux/formats.js +55 -0
  29. package/dist/tmux/formats.js.map +1 -0
  30. package/dist/tmux/sessions.js +99 -0
  31. package/dist/tmux/sessions.js.map +1 -0
  32. package/dist/validate.js +65 -0
  33. package/dist/validate.js.map +1 -0
  34. package/dist/winshell/manager.js +258 -0
  35. package/dist/winshell/manager.js.map +1 -0
  36. package/dist/ws/protocol.js +4 -0
  37. package/dist/ws/protocol.js.map +1 -0
  38. package/dist/ws/sshState.js +35 -0
  39. package/dist/ws/sshState.js.map +1 -0
  40. package/dist/ws/terminalSession.js +204 -0
  41. package/dist/ws/terminalSession.js.map +1 -0
  42. package/dist/ws/wsServer.js +151 -0
  43. package/dist/ws/wsServer.js.map +1 -0
  44. package/dist/wsl.js +35 -0
  45. package/dist/wsl.js.map +1 -0
  46. package/package.json +61 -0
  47. package/public/assets/index-CfimUdwq.js +44 -0
  48. package/public/assets/index-DeKxLCiY.css +1 -0
  49. package/public/index.html +13 -0
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Classify ssh failure/prompt states from PTY output so the UI can show
3
+ * something better than a black screen. Best-effort string matching on the
4
+ * raw ssh client output.
5
+ */
6
+ const MATCHERS = [
7
+ {
8
+ state: 'hostkey',
9
+ re: /authenticity of host|fingerprint|known_hosts|REMOTE HOST IDENTIFICATION HAS CHANGED/i,
10
+ message: 'Unknown or changed host key — verify the host in a regular terminal first.',
11
+ },
12
+ {
13
+ state: 'authfail',
14
+ re: /permission denied|too many authentication failures|no such identity|authentication failed/i,
15
+ message: 'SSH authentication failed — check your keys / ssh-agent.',
16
+ },
17
+ {
18
+ state: 'refused',
19
+ re: /connection refused|could not resolve hostname|name or service not known|no route to host/i,
20
+ message: 'Could not connect to the host (refused / unresolved).',
21
+ },
22
+ {
23
+ state: 'timeout',
24
+ re: /connection timed out|operation timed out|timed out waiting/i,
25
+ message: 'Connection timed out.',
26
+ },
27
+ ];
28
+ export function classifySsh(text) {
29
+ for (const m of MATCHERS) {
30
+ if (m.re.test(text))
31
+ return { state: m.state, message: m.message };
32
+ }
33
+ return null;
34
+ }
35
+ //# sourceMappingURL=sshState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sshState.js","sourceRoot":"","sources":["../../src/ws/sshState.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,QAAQ,GAAuD;IACnE;QACE,KAAK,EAAE,SAAS;QAChB,EAAE,EAAE,sFAAsF;QAC1F,OAAO,EAAE,4EAA4E;KACtF;IACD;QACE,KAAK,EAAE,UAAU;QACjB,EAAE,EAAE,4FAA4F;QAChG,OAAO,EAAE,0DAA0D;KACpE;IACD;QACE,KAAK,EAAE,SAAS;QAChB,EAAE,EAAE,2FAA2F;QAC/F,OAAO,EAAE,uDAAuD;KACjE;IACD;QACE,KAAK,EAAE,SAAS;QAChB,EAAE,EAAE,6DAA6D;QACjE,OAAO,EAAE,uBAAuB;KACjC;CACF,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IACrE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,204 @@
1
+ import * as pty from 'node-pty';
2
+ import { homedir } from 'node:os';
3
+ import { attachArgv } from '../tmux/builder.js';
4
+ import { resolveExecutable } from '../exe.js';
5
+ import { classifySsh } from './sshState.js';
6
+ import { log } from '../logger.js';
7
+ const HEARTBEAT_MS = 30_000;
8
+ const HIGH_WATER = 1 << 20; // 1 MiB buffered → pause the PTY
9
+ const LOW_WATER = 1 << 18; // 256 KiB → resume
10
+ const KILL_GRACE_MS = 2_000;
11
+ /** Owns exactly one PTY for one WebSocket. dispose() is idempotent. */
12
+ export class TerminalSession {
13
+ ws;
14
+ target;
15
+ session;
16
+ ptyProc;
17
+ disposed = false;
18
+ alive = true;
19
+ paused = false;
20
+ heartbeat;
21
+ drainTimer;
22
+ killTimer;
23
+ /** Scan ssh output for failure/prompt states until the link looks healthy. */
24
+ sshScanBudget;
25
+ constructor(ws, target, session, cols, rows) {
26
+ this.ws = ws;
27
+ this.target = target;
28
+ this.session = session;
29
+ this.sshScanBudget = target.kind === 'ssh' ? 8192 : 0;
30
+ const { file, args } = attachArgv(target, session);
31
+ // node-pty on Windows needs a full exe path (no PATH/.exe resolution).
32
+ this.ptyProc = pty.spawn(resolveExecutable(file), args, {
33
+ name: 'xterm-256color',
34
+ cols,
35
+ rows,
36
+ cwd: homedir(),
37
+ env: { ...process.env, TERM: 'xterm-256color' },
38
+ });
39
+ this.ptyProc.onData((data) => this.onPtyData(data));
40
+ this.ptyProc.onExit(({ exitCode }) => this.onPtyExit(exitCode));
41
+ this.ws.on('message', (data, isBinary) => this.onClientMessage(data, isBinary));
42
+ this.ws.on('close', () => this.dispose());
43
+ this.ws.on('error', () => this.dispose());
44
+ this.ws.on('pong', () => {
45
+ this.alive = true;
46
+ });
47
+ this.heartbeat = setInterval(() => this.tick(), HEARTBEAT_MS);
48
+ this.sendControl({ type: 'ready', target: target.id, session });
49
+ }
50
+ onPtyData(data) {
51
+ if (this.sshScanBudget > 0) {
52
+ this.sshScanBudget -= data.length;
53
+ const ssh = classifySsh(data);
54
+ if (ssh)
55
+ this.sendControl({ type: 'ssh', state: ssh.state, message: ssh.message });
56
+ }
57
+ this.sendBinary(Buffer.from(data, 'utf8'));
58
+ }
59
+ onPtyExit(exitCode) {
60
+ this.sendControl({ type: 'exit', code: exitCode });
61
+ this.closeWs(1000, 'pty exited');
62
+ this.dispose();
63
+ }
64
+ onClientMessage(data, isBinary) {
65
+ if (this.disposed)
66
+ return;
67
+ if (isBinary) {
68
+ // Raw keystrokes → straight into the PTY.
69
+ this.ptyProc.write(toBufferString(data));
70
+ return;
71
+ }
72
+ let msg;
73
+ try {
74
+ msg = JSON.parse(toBufferString(data));
75
+ }
76
+ catch {
77
+ return; // ignore malformed control frames
78
+ }
79
+ if (msg.type === 'resize') {
80
+ const cols = clampDim(msg.cols);
81
+ const rows = clampDim(msg.rows);
82
+ if (cols && rows) {
83
+ try {
84
+ this.ptyProc.resize(cols, rows);
85
+ }
86
+ catch {
87
+ /* pty may have exited */
88
+ }
89
+ }
90
+ }
91
+ else if (msg.type === 'ping') {
92
+ this.sendControl({ type: 'pong' });
93
+ }
94
+ }
95
+ sendBinary(buf) {
96
+ if (this.disposed || this.ws.readyState !== this.ws.OPEN)
97
+ return;
98
+ this.ws.send(buf, { binary: true });
99
+ if (!this.paused && this.ws.bufferedAmount > HIGH_WATER) {
100
+ this.paused = true;
101
+ this.ptyProc.pause();
102
+ this.drainTimer = setInterval(() => this.checkDrain(), 50);
103
+ }
104
+ }
105
+ checkDrain() {
106
+ if (this.disposed)
107
+ return;
108
+ if (this.ws.bufferedAmount < LOW_WATER) {
109
+ this.paused = false;
110
+ if (this.drainTimer)
111
+ clearInterval(this.drainTimer);
112
+ this.drainTimer = undefined;
113
+ this.ptyProc.resume();
114
+ }
115
+ }
116
+ sendControl(msg) {
117
+ if (this.disposed || this.ws.readyState !== this.ws.OPEN)
118
+ return;
119
+ this.ws.send(JSON.stringify(msg), { binary: false });
120
+ }
121
+ tick() {
122
+ if (!this.alive) {
123
+ log.warn(`heartbeat lost for ${this.target.id}/${this.session}, terminating`);
124
+ try {
125
+ this.ws.terminate();
126
+ }
127
+ catch {
128
+ /* ignore */
129
+ }
130
+ this.dispose();
131
+ return;
132
+ }
133
+ this.alive = false;
134
+ try {
135
+ this.ws.ping();
136
+ }
137
+ catch {
138
+ /* ignore */
139
+ }
140
+ }
141
+ closeWs(code, reason) {
142
+ try {
143
+ if (this.ws.readyState === this.ws.OPEN)
144
+ this.ws.close(code, reason);
145
+ }
146
+ catch {
147
+ /* ignore */
148
+ }
149
+ }
150
+ /** Idempotent teardown — called from pty exit, ws close, and shutdown. */
151
+ dispose() {
152
+ if (this.disposed)
153
+ return;
154
+ this.disposed = true;
155
+ if (this.heartbeat)
156
+ clearInterval(this.heartbeat);
157
+ if (this.drainTimer)
158
+ clearInterval(this.drainTimer);
159
+ try {
160
+ this.ptyProc.kill(); // SIGHUP → tmux client detaches; session keeps running
161
+ }
162
+ catch {
163
+ /* already gone */
164
+ }
165
+ // Force-kill if it lingers.
166
+ this.killTimer = setTimeout(() => {
167
+ try {
168
+ this.ptyProc.kill('SIGKILL');
169
+ }
170
+ catch {
171
+ /* already gone */
172
+ }
173
+ }, KILL_GRACE_MS);
174
+ this.killTimer.unref?.();
175
+ this.closeWs(1000, 'disposed');
176
+ registry.delete(this);
177
+ }
178
+ }
179
+ function toBufferString(data) {
180
+ if (typeof data === 'string')
181
+ return data;
182
+ if (Buffer.isBuffer(data))
183
+ return data.toString('utf8');
184
+ if (Array.isArray(data))
185
+ return Buffer.concat(data).toString('utf8');
186
+ if (data instanceof ArrayBuffer)
187
+ return Buffer.from(data).toString('utf8');
188
+ return String(data);
189
+ }
190
+ function clampDim(n) {
191
+ if (typeof n !== 'number' || !Number.isInteger(n) || n < 1 || n > 1000)
192
+ return null;
193
+ return n;
194
+ }
195
+ /** All live sessions, so the process can tear them down on shutdown. */
196
+ export const registry = new Set();
197
+ export function track(s) {
198
+ registry.add(s);
199
+ }
200
+ export function disposeAll() {
201
+ for (const s of [...registry])
202
+ s.dispose();
203
+ }
204
+ //# sourceMappingURL=terminalSession.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminalSession.js","sourceRoot":"","sources":["../../src/ws/terminalSession.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,151 @@
1
+ import { WebSocketServer } from 'ws';
2
+ import { config } from '../config.js';
3
+ import { getTarget, isValidTargetId } from '../targets.js';
4
+ import { isValidSessionName, isValidDimension } from '../validate.js';
5
+ import { TerminalSession, track } from './terminalSession.js';
6
+ import { winShell } from '../winshell/manager.js';
7
+ import { log } from '../logger.js';
8
+ const HEARTBEAT_MS = 30_000;
9
+ function reject(socket, status, message) {
10
+ socket.write(`HTTP/1.1 ${status} ${message}\r\nConnection: close\r\n\r\n`);
11
+ socket.destroy();
12
+ }
13
+ function rawToString(data) {
14
+ if (typeof data === 'string')
15
+ return data;
16
+ if (Buffer.isBuffer(data))
17
+ return data.toString('utf8');
18
+ if (Array.isArray(data))
19
+ return Buffer.concat(data).toString('utf8');
20
+ if (data instanceof ArrayBuffer)
21
+ return Buffer.from(data).toString('utf8');
22
+ return String(data);
23
+ }
24
+ let nextClientId = 1;
25
+ /** Attach a WS to a native shell session (one persistent pty, many clients). */
26
+ function attachWinShell(ws, target, session, cols, rows) {
27
+ const client = {
28
+ id: nextClientId++,
29
+ sendBinary: (buf) => {
30
+ if (ws.readyState === ws.OPEN)
31
+ ws.send(buf, { binary: true });
32
+ },
33
+ sendControl: (msg) => {
34
+ if (ws.readyState === ws.OPEN)
35
+ ws.send(JSON.stringify(msg), { binary: false });
36
+ },
37
+ isOpen: () => ws.readyState === ws.OPEN,
38
+ close: (code, reason) => {
39
+ try {
40
+ ws.close(code, reason);
41
+ }
42
+ catch {
43
+ /* ignore */
44
+ }
45
+ },
46
+ };
47
+ let shellSession;
48
+ try {
49
+ shellSession = winShell.attachOrCreate(session, cols, rows, client);
50
+ }
51
+ catch (e) {
52
+ client.sendControl({ type: 'error', message: e instanceof Error ? e.message : 'failed to start shell' });
53
+ client.close(1011, 'shell error');
54
+ return;
55
+ }
56
+ client.sendControl({ type: 'ready', target: target.id, session });
57
+ ws.on('message', (data, isBinary) => {
58
+ if (isBinary) {
59
+ shellSession.write(rawToString(data));
60
+ return;
61
+ }
62
+ let msg;
63
+ try {
64
+ msg = JSON.parse(rawToString(data));
65
+ }
66
+ catch {
67
+ return;
68
+ }
69
+ if (msg.type === 'resize' && isValidDimension(msg.cols) && isValidDimension(msg.rows)) {
70
+ shellSession.resize(msg.cols, msg.rows);
71
+ }
72
+ else if (msg.type === 'ping') {
73
+ client.sendControl({ type: 'pong' });
74
+ }
75
+ });
76
+ let alive = true;
77
+ ws.on('pong', () => {
78
+ alive = true;
79
+ });
80
+ const hb = setInterval(() => {
81
+ if (!alive) {
82
+ try {
83
+ ws.terminate();
84
+ }
85
+ catch {
86
+ /* ignore */
87
+ }
88
+ return;
89
+ }
90
+ alive = false;
91
+ try {
92
+ ws.ping();
93
+ }
94
+ catch {
95
+ /* ignore */
96
+ }
97
+ }, HEARTBEAT_MS);
98
+ const cleanup = () => {
99
+ clearInterval(hb);
100
+ shellSession.detach(client); // pty stays alive — persistence across reconnects
101
+ };
102
+ ws.on('close', cleanup);
103
+ ws.on('error', cleanup);
104
+ }
105
+ /** Attach the single /ws interactive-attach endpoint to the HTTP server. */
106
+ export function attachWebSocket(server) {
107
+ const wss = new WebSocketServer({ noServer: true });
108
+ server.on('upgrade', (req, socket, head) => {
109
+ let url;
110
+ try {
111
+ url = new URL(req.url ?? '', `http://${req.headers.host ?? 'localhost'}`);
112
+ }
113
+ catch {
114
+ reject(socket, 400, 'Bad Request');
115
+ return;
116
+ }
117
+ if (url.pathname !== '/ws') {
118
+ reject(socket, 404, 'Not Found');
119
+ return;
120
+ }
121
+ // The WS upgrade bypasses Express middleware — enforce Origin here.
122
+ if (!config.isAllowedOrigin(req.headers.origin)) {
123
+ log.warn(`rejected WS upgrade from disallowed origin: ${req.headers.origin}`);
124
+ reject(socket, 403, 'Forbidden');
125
+ return;
126
+ }
127
+ const targetId = url.searchParams.get('target') ?? '';
128
+ const session = url.searchParams.get('session') ?? '';
129
+ if (!isValidTargetId(targetId))
130
+ return reject(socket, 400, 'Bad Request');
131
+ const target = getTarget(targetId);
132
+ if (!target)
133
+ return reject(socket, 404, 'Not Found');
134
+ if (!isValidSessionName(session))
135
+ return reject(socket, 400, 'Bad Request');
136
+ const colsRaw = Number(url.searchParams.get('cols'));
137
+ const rowsRaw = Number(url.searchParams.get('rows'));
138
+ const cols = isValidDimension(colsRaw) ? colsRaw : 80;
139
+ const rows = isValidDimension(rowsRaw) ? rowsRaw : 24;
140
+ wss.handleUpgrade(req, socket, head, (ws) => {
141
+ log.info(`attach ${target.id}/${session} (${cols}x${rows})`);
142
+ if (target.kind === 'winlocal') {
143
+ attachWinShell(ws, target, session, cols, rows);
144
+ return;
145
+ }
146
+ const ts = new TerminalSession(ws, target, session, cols, rows);
147
+ track(ts);
148
+ });
149
+ });
150
+ }
151
+ //# sourceMappingURL=wsServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wsServer.js","sourceRoot":"","sources":["../../src/ws/wsServer.ts"],"names":[],"mappings":"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"}
package/dist/wsl.js ADDED
@@ -0,0 +1,35 @@
1
+ import { runCommand } from './exec.js';
2
+ import { log } from './logger.js';
3
+ /** Distros that exist for the container runtime, never for interactive use. */
4
+ const SYSTEM_DISTROS = /^docker-desktop(-data)?$/i;
5
+ const NUL = 0;
6
+ const BOM = 0xfeff;
7
+ /**
8
+ * Enumerate installed WSL distros (Windows only). `wsl.exe -l -q` prints names
9
+ * one per line in UTF-16LE (with a BOM), so we decode accordingly.
10
+ */
11
+ export async function listWslDistros() {
12
+ const r = await runCommand('wsl.exe', ['-l', '-q'], { encoding: 'utf16le', timeoutMs: 8000 });
13
+ if (r.code !== 0) {
14
+ if (r.stdout.trim() || r.stderr.trim()) {
15
+ log.warn(`wsl.exe -l -q failed: ${(r.stderr || r.stdout).trim().split('\n')[0]}`);
16
+ }
17
+ return [];
18
+ }
19
+ return parseWslList(r.stdout);
20
+ }
21
+ /** Parse `wsl.exe -l -q` decoded output into clean distro names. */
22
+ export function parseWslList(stdout) {
23
+ // Drop NULs / BOM via code point so the source stays pure ASCII.
24
+ const cleaned = Array.from(stdout)
25
+ .filter((ch) => {
26
+ const c = ch.charCodeAt(0);
27
+ return c !== NUL && c !== BOM;
28
+ })
29
+ .join('');
30
+ return cleaned
31
+ .split(/\r?\n/)
32
+ .map((line) => line.trim())
33
+ .filter((name) => name.length > 0 && !SYSTEM_DISTROS.test(name));
34
+ }
35
+ //# sourceMappingURL=wsl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wsl.js","sourceRoot":"","sources":["../src/wsl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAElC,+EAA+E;AAC/E,MAAM,cAAc,GAAG,2BAA2B,CAAC;AAEnD,MAAM,GAAG,GAAG,CAAC,CAAC;AACd,MAAM,GAAG,GAAG,MAAM,CAAC;AAEnB;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9F,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACjB,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;YACvC,GAAG,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACpF,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,iEAAiE;IACjE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;SAC/B,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE;QACb,MAAM,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC3B,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC;IAChC,CAAC,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAC;IACZ,OAAO,OAAO;SACX,KAAK,CAAC,OAAO,CAAC;SACd,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AACrE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "tmuxes",
3
+ "version": "0.1.0",
4
+ "description": "Web UI to run and supervise many CLI coding agents in tmux — local, SSH, and WSL.",
5
+ "keywords": [
6
+ "tmux",
7
+ "terminal",
8
+ "xterm",
9
+ "ssh",
10
+ "wsl",
11
+ "web-terminal",
12
+ "node-pty",
13
+ "cli-agents"
14
+ ],
15
+ "license": "MIT",
16
+ "author": "f1974939505 (https://github.com/f1974939505)",
17
+ "homepage": "https://github.com/f1974939505/tmuxes#readme",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/f1974939505/tmuxes.git",
21
+ "directory": "server"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/f1974939505/tmuxes/issues"
25
+ },
26
+ "type": "module",
27
+ "main": "dist/index.js",
28
+ "bin": {
29
+ "tmuxes": "bin/tmuxes.js"
30
+ },
31
+ "engines": {
32
+ "node": ">=18"
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "public",
37
+ "bin",
38
+ "README.md",
39
+ "LICENSE"
40
+ ],
41
+ "scripts": {
42
+ "dev": "tsx watch src/index.ts",
43
+ "build": "tsc -p tsconfig.json",
44
+ "start": "node dist/index.js",
45
+ "test": "vitest run",
46
+ "prepack": "node ../scripts/prepack.mjs"
47
+ },
48
+ "dependencies": {
49
+ "express": "^5.2.1",
50
+ "node-pty": "1.1.0",
51
+ "ws": "^8.21.0"
52
+ },
53
+ "devDependencies": {
54
+ "@types/express": "^5.0.0",
55
+ "@types/node": "^22.10.0",
56
+ "@types/ws": "^8.5.13",
57
+ "tsx": "^4.19.2",
58
+ "typescript": "^5.7.2",
59
+ "vitest": "^2.1.8"
60
+ }
61
+ }