tmuxes 0.1.9 → 0.1.10

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 (140) hide show
  1. package/dist/agentHooks.js +91 -0
  2. package/dist/agentHooks.js.map +1 -0
  3. package/dist/agentOutput.js +30 -0
  4. package/dist/agentOutput.js.map +1 -0
  5. package/dist/agentState.js +45 -0
  6. package/dist/agentState.js.map +1 -0
  7. package/dist/config.js +32 -0
  8. package/dist/config.js.map +1 -0
  9. package/dist/exe.js +37 -0
  10. package/dist/exe.js.map +1 -0
  11. package/dist/exec.js +43 -0
  12. package/dist/exec.js.map +1 -0
  13. package/dist/files.js +308 -0
  14. package/dist/files.js.map +1 -0
  15. package/dist/foldersStore.js +103 -0
  16. package/dist/foldersStore.js.map +1 -0
  17. package/dist/index.js +117 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/logger.js +16 -0
  20. package/dist/logger.js.map +1 -0
  21. package/{server/src/monitor.ts → dist/monitor.js} +9 -10
  22. package/dist/monitor.js.map +1 -0
  23. package/dist/openBrowser.js +31 -0
  24. package/dist/openBrowser.js.map +1 -0
  25. package/{server/src/platform.ts → dist/platform.js} +5 -4
  26. package/dist/platform.js.map +1 -0
  27. package/dist/rest/router.js +198 -0
  28. package/dist/rest/router.js.map +1 -0
  29. package/dist/targetCommand.js +60 -0
  30. package/dist/targetCommand.js.map +1 -0
  31. package/dist/targets.js +131 -0
  32. package/dist/targets.js.map +1 -0
  33. package/dist/tmux/builder.js +174 -0
  34. package/dist/tmux/builder.js.map +1 -0
  35. package/dist/tmux/formats.js +61 -0
  36. package/dist/tmux/formats.js.map +1 -0
  37. package/dist/tmux/sessions.js +157 -0
  38. package/dist/tmux/sessions.js.map +1 -0
  39. package/dist/validate.js +65 -0
  40. package/dist/validate.js.map +1 -0
  41. package/dist/windowsSsh.js +209 -0
  42. package/dist/windowsSsh.js.map +1 -0
  43. package/dist/winshell/manager.js +267 -0
  44. package/dist/winshell/manager.js.map +1 -0
  45. package/dist/ws/protocol.js +4 -0
  46. package/dist/ws/protocol.js.map +1 -0
  47. package/dist/ws/sshState.js +35 -0
  48. package/dist/ws/sshState.js.map +1 -0
  49. package/dist/ws/terminalSession.js +204 -0
  50. package/dist/ws/terminalSession.js.map +1 -0
  51. package/dist/ws/wsServer.js +151 -0
  52. package/dist/ws/wsServer.js.map +1 -0
  53. package/dist/wsl.js +35 -0
  54. package/dist/wsl.js.map +1 -0
  55. package/package.json +49 -16
  56. package/public/assets/index-D_X5SnGx.css +1 -0
  57. package/public/assets/index-Dl69CPyt.js +44 -0
  58. package/{client → public}/index.html +3 -2
  59. package/.node-version +0 -1
  60. package/.nvmrc +0 -1
  61. package/.tmp-npm-cache/_cacache/content-v2/sha512/43/27/5e000b8b9c56a6ccc66f709485499f4304e2cb1982582ba571321c07b3ef56fcabd2c671898cc8003365a0485b6fd8e73e7b17b073cec0f7d1628c1a99df +0 -0
  62. package/.tmp-npm-cache/_cacache/content-v2/sha512/51/cf/4301295d74559ed494bae160d54d8741077f89faebb311882ac065019246951e7b53f3dcb913793c42b331e14c7070c4810c3cdc27a427d103a7db4614e0 +0 -0
  63. package/.tmp-npm-cache/_cacache/content-v2/sha512/c3/4d/d68a454a916e74c2617f586fbf770981b33811d667c2547eb0e9fc21938f4ee7e98f1ceee4bde8ad8815b5f6efe21b60eee798837d68f51a3340d7e5bb7a +0 -0
  64. package/.tmp-npm-cache/_cacache/content-v2/sha512/fe/40/2abfbefc96299e8bf714aa91d62607190ae299e102cf5933db2e2904640d65d25d67dbbb6fa2ddc92a17f00b9dbfdf2e37487f67d96ec36c64a285b59a7d +0 -0
  65. package/.tmp-npm-cache/_cacache/index-v5/27/fe/81a3de6ce7ae3d1e41a3421de20c5629998c4ee5d0ffe2037630f03b03b2 +0 -4
  66. package/.tmp-npm-cache/_cacache/index-v5/65/22/dd66711f62681fce09aabb2357a2907b4a0c778ac5227c4baf9603fd86e8 +0 -4
  67. package/.tmp-npm-cache/_update-notifier-last-checked +0 -0
  68. package/AGENTS.md +0 -15
  69. package/CLAUDE.md +0 -3
  70. package/README.en.md +0 -304
  71. package/SECURITY.md +0 -31
  72. package/client/package.json +0 -29
  73. package/client/src/App.tsx +0 -123
  74. package/client/src/activity.ts +0 -5
  75. package/client/src/api.ts +0 -130
  76. package/client/src/attention.tsx +0 -157
  77. package/client/src/components/FileExplorer.tsx +0 -156
  78. package/client/src/components/FileViewer.tsx +0 -194
  79. package/client/src/components/SessionRow.tsx +0 -108
  80. package/client/src/components/SessionTree.tsx +0 -197
  81. package/client/src/components/SettingsButton.tsx +0 -122
  82. package/client/src/components/Sidebar.tsx +0 -96
  83. package/client/src/components/StatusBanner.tsx +0 -31
  84. package/client/src/components/TargetGroup.tsx +0 -275
  85. package/client/src/components/TerminalPanel.tsx +0 -192
  86. package/client/src/folders.ts +0 -245
  87. package/client/src/hooks/useTerminal.ts +0 -67
  88. package/client/src/hooks/useTmuxSocket.ts +0 -65
  89. package/client/src/i18n.ts +0 -213
  90. package/client/src/main.tsx +0 -17
  91. package/client/src/settings.tsx +0 -87
  92. package/client/src/styles.css +0 -723
  93. package/client/src/types.ts +0 -93
  94. package/client/src/util.ts +0 -65
  95. package/client/tsconfig.json +0 -13
  96. package/client/vite.config.ts +0 -15
  97. package/fig/fig1.png +0 -0
  98. package/scripts/prepack.mjs +0 -35
  99. package/server/package.json +0 -61
  100. package/server/src/agentHooks.ts +0 -120
  101. package/server/src/agentOutput.ts +0 -36
  102. package/server/src/agentState.ts +0 -70
  103. package/server/src/config.ts +0 -31
  104. package/server/src/exe.ts +0 -34
  105. package/server/src/exec.ts +0 -61
  106. package/server/src/files.ts +0 -330
  107. package/server/src/foldersStore.ts +0 -114
  108. package/server/src/index.ts +0 -114
  109. package/server/src/logger.ts +0 -16
  110. package/server/src/openBrowser.ts +0 -28
  111. package/server/src/rest/router.ts +0 -290
  112. package/server/src/targetCommand.ts +0 -79
  113. package/server/src/targets.ts +0 -152
  114. package/server/src/tmux/builder.ts +0 -198
  115. package/server/src/tmux/formats.ts +0 -95
  116. package/server/src/tmux/sessions.ts +0 -204
  117. package/server/src/validate.ts +0 -79
  118. package/server/src/windowsSsh.ts +0 -239
  119. package/server/src/winshell/manager.ts +0 -296
  120. package/server/src/ws/protocol.ts +0 -15
  121. package/server/src/ws/sshState.ts +0 -36
  122. package/server/src/ws/terminalSession.ts +0 -207
  123. package/server/src/ws/wsServer.ts +0 -153
  124. package/server/src/wsl.ts +0 -38
  125. package/server/test/agentHooks.test.ts +0 -66
  126. package/server/test/agentOutput.test.ts +0 -26
  127. package/server/test/agentState.test.ts +0 -24
  128. package/server/test/builder.test.ts +0 -162
  129. package/server/test/files.test.ts +0 -81
  130. package/server/test/formats.test.ts +0 -123
  131. package/server/test/monitor.test.ts +0 -25
  132. package/server/test/validate.test.ts +0 -71
  133. package/server/test/wsl.test.ts +0 -18
  134. package/server/tsconfig.json +0 -9
  135. package/server/vitest.config.ts +0 -12
  136. package/start.cmd +0 -30
  137. package/start.command +0 -20
  138. package/start.sh +0 -20
  139. package/tsconfig.base.json +0 -19
  140. /package/{server/bin → bin}/tmuxes.js +0 -0
@@ -0,0 +1,131 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { isValidHostAlias, parseHostSpec } from './validate.js';
5
+ import { isWindows } from './platform.js';
6
+ import { listWslDistros } from './wsl.js';
7
+ import { winShell } from './winshell/manager.js';
8
+ import { log } from './logger.js';
9
+ /** Native Windows shells (PowerShell/cmd via ConPTY). Enabled on Windows, or
10
+ * anywhere via TMUXES_FAKE_WINSHELL for testing the path on Linux/macOS. */
11
+ const WINSHELL_ENABLED = isWindows || !!process.env.TMUXES_FAKE_WINSHELL;
12
+ function winlocalTarget() {
13
+ return {
14
+ id: 'winlocal',
15
+ kind: 'winlocal',
16
+ label: isWindows ? 'Windows (local)' : 'Local shell (fake)',
17
+ shells: winShell.listShells(),
18
+ };
19
+ }
20
+ const ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
21
+ export function isValidTargetId(id) {
22
+ return typeof id === 'string' && ID_RE.test(id);
23
+ }
24
+ function slug(s) {
25
+ return s.replace(/[^A-Za-z0-9._-]/g, '-');
26
+ }
27
+ const LOCAL = { id: 'local', kind: 'local', label: 'Local' };
28
+ /** Best-effort, read-only parse of ~/.ssh/config Host aliases (no wildcards). */
29
+ function parseSshConfig() {
30
+ const path = join(homedir(), '.ssh', 'config');
31
+ let text;
32
+ try {
33
+ text = readFileSync(path, 'utf8');
34
+ }
35
+ catch {
36
+ return []; // no config file is normal
37
+ }
38
+ const targets = [];
39
+ const seen = new Set();
40
+ for (const rawLine of text.split('\n')) {
41
+ const line = rawLine.trim();
42
+ if (!line || line.startsWith('#'))
43
+ continue;
44
+ const m = /^Host\s+(.+)$/i.exec(line);
45
+ if (!m)
46
+ continue;
47
+ for (const token of m[1].split(/\s+/)) {
48
+ // Skip wildcard / negated patterns — they aren't concrete hosts.
49
+ if (token.includes('*') || token.includes('?') || token.startsWith('!'))
50
+ continue;
51
+ if (!isValidHostAlias(token) || seen.has(token))
52
+ continue;
53
+ seen.add(token);
54
+ targets.push({ id: `cfg-${slug(token)}`, kind: 'ssh', label: token, host: token });
55
+ }
56
+ }
57
+ return targets;
58
+ }
59
+ /** Parse the TMUXES_HOSTS="alice@web1,bob@db2:2222" override. */
60
+ function parseEnvHosts() {
61
+ const env = process.env.TMUXES_HOSTS;
62
+ if (!env)
63
+ return [];
64
+ const targets = [];
65
+ for (const part of env.split(',')) {
66
+ const spec = parseHostSpec(part);
67
+ if (!spec) {
68
+ log.warn(`ignoring invalid TMUXES_HOSTS entry: ${part.trim()}`);
69
+ continue;
70
+ }
71
+ const label = `${spec.user ? `${spec.user}@` : ''}${spec.host}${spec.port ? `:${spec.port}` : ''}`;
72
+ targets.push({
73
+ id: `env-${slug(label)}`,
74
+ kind: 'ssh',
75
+ label,
76
+ host: spec.host,
77
+ user: spec.user,
78
+ port: spec.port,
79
+ });
80
+ }
81
+ return targets;
82
+ }
83
+ function sshTargets() {
84
+ const all = [...parseEnvHosts(), ...parseSshConfig()];
85
+ const byId = new Map();
86
+ for (const t of all)
87
+ if (!byId.has(t.id))
88
+ byId.set(t.id, t);
89
+ return [...byId.values()];
90
+ }
91
+ /** The synchronous part of the target list (everything except WSL discovery). */
92
+ function baseTargets() {
93
+ const base = [];
94
+ if (WINSHELL_ENABLED)
95
+ base.push(winlocalTarget());
96
+ // Windows has no native tmux; its "local" machine is reached through WSL.
97
+ if (!isWindows)
98
+ base.push(LOCAL);
99
+ return [...base, ...sshTargets()];
100
+ }
101
+ // Cache so the (async, Windows-only) WSL discovery doesn't run on the WS
102
+ // upgrade path. The client always GETs /api/targets first, which refreshes it.
103
+ let cachedTargets = baseTargets();
104
+ /** Recompute the full target list, including WSL distros on Windows. */
105
+ export async function refreshTargets() {
106
+ if (isWindows) {
107
+ const distros = await listWslDistros();
108
+ const wsl = distros.map((name) => ({
109
+ id: `wsl-${slug(name)}`,
110
+ kind: 'wsl',
111
+ label: name,
112
+ distro: name,
113
+ }));
114
+ cachedTargets = [winlocalTarget(), ...wsl, ...sshTargets()];
115
+ }
116
+ else {
117
+ cachedTargets = baseTargets();
118
+ }
119
+ return cachedTargets;
120
+ }
121
+ export function listTargets() {
122
+ return cachedTargets;
123
+ }
124
+ export function getTarget(id) {
125
+ return cachedTargets.find((t) => t.id === id);
126
+ }
127
+ /** ssh destination string: "user@host" or just the config alias / host. */
128
+ export function sshDestination(t) {
129
+ return t.user ? `${t.user}@${t.host}` : `${t.host}`;
130
+ }
131
+ //# sourceMappingURL=targets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"targets.js","sourceRoot":"","sources":["../src/targets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,174 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdirSync } from 'node:fs';
3
+ import { homedir, tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { config } from '../config.js';
6
+ import { sshDestination } from '../targets.js';
7
+ import { isWindows } from '../platform.js';
8
+ /**
9
+ * Single-quote a string for a POSIX shell. Needed ONLY for the remote ssh path:
10
+ * ssh concatenates the remote command words with spaces and re-parses them
11
+ * through the remote login shell, so each tmux arg must survive that re-parse.
12
+ * Local commands use pure argv (no shell) and need no quoting.
13
+ */
14
+ export function sshQuote(arg) {
15
+ return `'${arg.replace(/'/g, `'\\''`)}'`;
16
+ }
17
+ /** Local tmux management/attach argv — passed straight to spawn (shell:false). */
18
+ export function localTmux(sub) {
19
+ return ['tmux', ...sub];
20
+ }
21
+ function sshControlDir() {
22
+ // OpenSSH connection sharing avoids repeated TCP/auth handshakes for sidebar
23
+ // polling and file operations. Windows OpenSSH accepts the config but fails
24
+ // at runtime on mux sockets, so Windows management uses an app-owned SSH
25
+ // session instead of ControlMaster.
26
+ if (isWindows)
27
+ return null;
28
+ const uid = process.getuid?.() ?? 'user';
29
+ const dir = join(tmpdir(), `tmuxes-ssh-${uid}`);
30
+ try {
31
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ return dir;
37
+ }
38
+ export function sshControlPath(t) {
39
+ const dir = sshControlDir();
40
+ if (!dir)
41
+ return null;
42
+ const key = `${sshDestination(t)}:${t.port ?? 22}:${t.id}`;
43
+ return join(dir, createHash('sha1').update(key).digest('hex'));
44
+ }
45
+ function sshMultiplexArgs(t) {
46
+ const path = sshControlPath(t);
47
+ if (!path)
48
+ return [];
49
+ return [
50
+ '-o',
51
+ 'ControlMaster=auto',
52
+ '-o',
53
+ `ControlPath=${path}`,
54
+ '-o',
55
+ `ControlPersist=${config.ssh.controlPersist}`,
56
+ ];
57
+ }
58
+ export function sshClientArgs(t, opts) {
59
+ const portArgs = t.port ? ['-p', String(t.port)] : [];
60
+ return [
61
+ 'ssh',
62
+ ...(opts.tty ? ['-tt'] : []),
63
+ ...(opts.batchMode ? ['-o', 'BatchMode=yes'] : []),
64
+ '-o',
65
+ `ConnectTimeout=${opts.connectTimeout}`,
66
+ ...(opts.multiplex === false ? ['-o', 'ControlMaster=no'] : sshMultiplexArgs(t)),
67
+ ...portArgs,
68
+ sshDestination(t),
69
+ ];
70
+ }
71
+ /**
72
+ * Remote tmux argv via the system ssh binary.
73
+ * - tty:false (management) → BatchMode + short ConnectTimeout so it fails fast
74
+ * and never hangs on a prompt. Remote args are sshQuote'd.
75
+ * - tty:true (interactive attach) → -tt forces a remote PTY (and SIGWINCH
76
+ * propagation). BatchMode is left default so host-key/agent prompts surface
77
+ * in the terminal. tmuxes does not force ServerAliveInterval; users can set
78
+ * keepalives in ~/.ssh/config if their site allows them. Remote args are NOT
79
+ * quoted here: this argv is handed to a PTY where the remote command words go
80
+ * to ssh as separate argv elements and our inputs are already allowlist-
81
+ * validated.
82
+ */
83
+ export function remoteTmux(t, sub, opts) {
84
+ if (opts.tty) {
85
+ return [
86
+ ...sshClientArgs(t, {
87
+ tty: true,
88
+ connectTimeout: config.ssh.connectTimeoutTty,
89
+ multiplex: opts.multiplex,
90
+ }),
91
+ 'tmux',
92
+ ...sub,
93
+ ];
94
+ }
95
+ return [
96
+ ...sshClientArgs(t, {
97
+ batchMode: true,
98
+ connectTimeout: config.ssh.connectTimeoutMgmt,
99
+ multiplex: opts.multiplex,
100
+ }),
101
+ 'tmux',
102
+ ...sub.map(sshQuote),
103
+ ];
104
+ }
105
+ /**
106
+ * tmux inside a WSL distro (Windows host). We use `--exec` (NOT `--`): `--`
107
+ * runs the command through the distro's login shell, which would re-parse tmux
108
+ * format strings (`#{...}` is a comment, `|` a pipe) and mangle them; `--exec`
109
+ * execs the binary directly with no shell, so argv maps straight through with
110
+ * no quoting — like the local path. The interactive case sets TERM via `env`
111
+ * because WSL does not inherit the Windows TERM.
112
+ */
113
+ export function wslTmux(distro, sub, opts) {
114
+ const prefix = opts.tty ? ['env', 'TERM=xterm-256color', 'tmux'] : ['tmux'];
115
+ return ['wsl.exe', '-d', distro, '--exec', ...prefix, ...sub];
116
+ }
117
+ /**
118
+ * Build a non-PTY argv to run an ARBITRARY command on a target (tmux, ls, cat,
119
+ * display-message, …). Same transport rules as management tmux: local runs the
120
+ * argv directly, wsl uses `--exec` (no shell), ssh uses BatchMode + sshQuote.
121
+ */
122
+ export function commandArgv(t, argv, opts = {}) {
123
+ let full;
124
+ if (t.kind === 'local') {
125
+ full = argv;
126
+ }
127
+ else if (t.kind === 'wsl') {
128
+ full = ['wsl.exe', '-d', t.distro ?? '', '--exec', ...argv];
129
+ }
130
+ else {
131
+ full = [
132
+ ...sshClientArgs(t, {
133
+ batchMode: true,
134
+ connectTimeout: config.ssh.connectTimeoutMgmt,
135
+ multiplex: opts.multiplex,
136
+ }),
137
+ ...argv.map(sshQuote),
138
+ ];
139
+ }
140
+ return { file: full[0], args: full.slice(1) };
141
+ }
142
+ /** Build a management argv (no PTY) for a local / ssh / wsl target. */
143
+ export function managementArgv(t, sub, opts = {}) {
144
+ return commandArgv(t, ['tmux', ...sub], opts);
145
+ }
146
+ /**
147
+ * Build the create-a-new-session argv, always starting in the user's HOME:
148
+ * - local → tmux `-c <homedir>` (explicit, regardless of the server's cwd),
149
+ * - wsl → `wsl --cd ~` runs the command from the distro's home; tmux inherits,
150
+ * - ssh → the remote command already runs from the remote `$HOME`; tmux inherits.
151
+ * `sub` is the new-session subcommand (named or auto-named `-P` form).
152
+ */
153
+ export function newSessionArgv(t, sub, opts = {}) {
154
+ if (t.kind === 'local') {
155
+ return { file: 'tmux', args: [...sub, '-c', homedir()] };
156
+ }
157
+ if (t.kind === 'wsl') {
158
+ return { file: 'wsl.exe', args: ['-d', t.distro ?? '', '--cd', '~', '--exec', 'tmux', ...sub] };
159
+ }
160
+ return commandArgv(t, ['tmux', ...sub], opts);
161
+ }
162
+ /** Build an interactive attach argv (run inside a PTY) for a local / ssh / wsl target. */
163
+ export function attachArgv(t, session) {
164
+ const sub = ['new-session', '-A', '-s', session]; // NO -d: the PTY supplies the terminal
165
+ let argv;
166
+ if (t.kind === 'local')
167
+ argv = localTmux(sub);
168
+ else if (t.kind === 'wsl')
169
+ argv = wslTmux(t.distro ?? '', sub, { tty: true });
170
+ else
171
+ argv = remoteTmux(t, sub, { tty: true });
172
+ return { file: argv[0], args: argv.slice(1) };
173
+ }
174
+ //# sourceMappingURL=builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.js","sourceRoot":"","sources":["../../src/tmux/builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
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
+ import { AGENT_OPTION, parseAgentValue } from '../agentState.js';
9
+ const SEP = '|';
10
+ export const SESSION_FORMAT = [
11
+ '#{session_windows}',
12
+ '#{session_attached}',
13
+ '#{session_created}',
14
+ '#{session_activity}', // epoch from tmux; kept for display/debug
15
+ `#{${AGENT_OPTION}}`, // agent hook state: "<kind>:<state>:<reason>:<event>:<nonce>"
16
+ '#{session_name}', // free-form → must be last
17
+ ].join(SEP);
18
+ export const WINDOW_FORMAT = [
19
+ '#{window_index}',
20
+ '#{window_panes}',
21
+ '#{window_active}',
22
+ '#{window_name}', // free-form → must be last
23
+ ].join(SEP);
24
+ export function parseSessions(stdout) {
25
+ return stdout
26
+ .split('\n')
27
+ .filter((l) => l.length > 0)
28
+ .map((line) => {
29
+ const parts = line.split(SEP);
30
+ const agent = parseAgentValue(parts[4] || '');
31
+ return {
32
+ windows: Number(parts[0]) || 0,
33
+ attached: Number(parts[1]) > 0,
34
+ created: Number(parts[2]) || 0,
35
+ lastActivity: Number(parts[3]) || 0,
36
+ ...agent,
37
+ name: parts.slice(5).join(SEP), // name may legitimately contain "|"
38
+ };
39
+ })
40
+ .filter((s) => s.name);
41
+ }
42
+ export function parseWindows(stdout) {
43
+ return stdout
44
+ .split('\n')
45
+ .filter((l) => l.length > 0)
46
+ .map((line) => {
47
+ const parts = line.split(SEP);
48
+ return {
49
+ index: Number(parts[0]) || 0,
50
+ panes: Number(parts[1]) || 0,
51
+ active: Number(parts[2]) > 0,
52
+ name: parts.slice(3).join(SEP),
53
+ };
54
+ });
55
+ }
56
+ /** stderr patterns that mean "no sessions" rather than a real error. */
57
+ const EMPTY_RE = /no server running|no sessions|error connecting to/i;
58
+ export function isEmptySessionsError(stderr) {
59
+ return EMPTY_RE.test(stderr);
60
+ }
61
+ //# sourceMappingURL=formats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formats.js","sourceRoot":"","sources":["../../src/tmux/formats.ts"],"names":[],"mappings":"AAAA;;;;;;2CAM2C;AAE3C,OAAO,EAAE,YAAY,EAAE,eAAe,EAAyD,MAAM,kBAAkB,CAAC;AAExH,MAAM,GAAG,GAAG,GAAG,CAAC;AAEhB,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,oBAAoB;IACpB,qBAAqB;IACrB,oBAAoB;IACpB,qBAAqB,EAAE,0CAA0C;IACjE,KAAK,YAAY,GAAG,EAAE,8DAA8D;IACpF,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;AA6BZ,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,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9C,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,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACnC,GAAG,KAAK;YACR,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,157 @@
1
+ import { managementArgv, newSessionArgv } from './builder.js';
2
+ import { runTargetCommand } from '../targetCommand.js';
3
+ import { AGENT_OPTION, agentValue, agentInitialValue, parseAgentValue, } from '../agentState.js';
4
+ import { augmentAgentCommand } from '../agentHooks.js';
5
+ import { classifyAgentTerminalError } from '../agentOutput.js';
6
+ import { SESSION_FORMAT, WINDOW_FORMAT, parseSessions, parseWindows, isEmptySessionsError, } from './formats.js';
7
+ import { isValidSessionName } from '../validate.js';
8
+ /** A management error carrying the HTTP status the router should return. */
9
+ export class TmuxError extends Error {
10
+ status;
11
+ constructor(status, message) {
12
+ super(message);
13
+ this.status = status;
14
+ this.name = 'TmuxError';
15
+ }
16
+ }
17
+ // Hard ceiling so a wedged ssh can never hang a request (ssh also has ConnectTimeout).
18
+ const REMOTE_TIMEOUT_MS = 15_000;
19
+ function timeoutFor(target) {
20
+ // Bound ssh (network) and wsl (possible cold start) so a request can't hang.
21
+ return target.kind === 'local' ? undefined : REMOTE_TIMEOUT_MS;
22
+ }
23
+ async function run(target, sub) {
24
+ return runTargetCommand(target, (opts) => managementArgv(target, sub, opts), {
25
+ timeoutMs: timeoutFor(target),
26
+ });
27
+ }
28
+ function firstStderrLine(stderr) {
29
+ return stderr.trim().split('\n')[0] || 'command failed';
30
+ }
31
+ export async function listSessions(target) {
32
+ const r = await run(target, ['list-sessions', '-F', SESSION_FORMAT]);
33
+ if (r.code === 0)
34
+ return reconcileAgentTerminalErrors(target, parseSessions(r.stdout));
35
+ // "no server running" / "no sessions" is the normal empty case.
36
+ if (isEmptySessionsError(r.stderr))
37
+ return [];
38
+ throw new TmuxError(502, firstStderrLine(r.stderr));
39
+ }
40
+ async function reconcileAgentTerminalErrors(target, sessions) {
41
+ return Promise.all(sessions.map((session) => reconcileAgentTerminalError(target, session)));
42
+ }
43
+ async function reconcileAgentTerminalError(target, session) {
44
+ if (!session.agentKind || session.agentState !== 'running')
45
+ return session;
46
+ const pane = await run(target, ['capture-pane', '-t', session.name, '-p', '-S', '-20']);
47
+ if (pane.code !== 0)
48
+ return session;
49
+ const event = classifyAgentTerminalError(pane.stdout, session.agentKind);
50
+ if (!event)
51
+ return session;
52
+ const value = agentValue(session.agentKind, 'idle', 'error', event, String(Date.now()));
53
+ const set = await run(target, ['set-option', '-t', session.name, '-q', AGENT_OPTION, value]);
54
+ if (set.code !== 0)
55
+ return session;
56
+ const snapshot = parseAgentValue(value);
57
+ return snapshot ? { ...session, ...snapshot } : session;
58
+ }
59
+ export async function createSession(target, opts) {
60
+ let name = opts.name;
61
+ // New sessions always start in the user's home directory (newSessionArgv).
62
+ if (name) {
63
+ if (!isValidSessionName(name))
64
+ throw new TmuxError(400, 'invalid session name');
65
+ const sessionName = name;
66
+ const r = await runTargetCommand(target, (opts) => newSessionArgv(target, ['new-session', '-d', '-s', sessionName], opts), { timeoutMs: timeoutFor(target) });
67
+ if (r.code !== 0) {
68
+ if (/duplicate session/i.test(r.stderr)) {
69
+ throw new TmuxError(409, `session "${name}" already exists`);
70
+ }
71
+ throw new TmuxError(502, firstStderrLine(r.stderr));
72
+ }
73
+ }
74
+ else {
75
+ // Let tmux assign a numeric name and report it back.
76
+ const r = await runTargetCommand(target, (opts) => newSessionArgv(target, ['new-session', '-d', '-P', '-F', '#{session_name}'], opts), { timeoutMs: timeoutFor(target) });
77
+ if (r.code !== 0)
78
+ throw new TmuxError(502, firstStderrLine(r.stderr));
79
+ name = r.stdout.trim();
80
+ }
81
+ if (opts.command && opts.command.length > 0) {
82
+ const augmented = augmentAgentCommand(opts.command);
83
+ if (augmented.kind) {
84
+ await run(target, [
85
+ 'set-option',
86
+ '-t',
87
+ name,
88
+ '-q',
89
+ AGENT_OPTION,
90
+ agentInitialValue(augmented.kind),
91
+ ]);
92
+ }
93
+ // Type the command literally, then press Enter. Two send-keys calls so the
94
+ // command text can never be misparsed as a key name.
95
+ await run(target, ['send-keys', '-t', name, '-l', augmented.command]);
96
+ await run(target, ['send-keys', '-t', name, 'Enter']);
97
+ }
98
+ return { name };
99
+ }
100
+ export async function launchAgentInSession(target, name, agent) {
101
+ const augmented = augmentAgentCommand(agent);
102
+ if (!augmented.kind)
103
+ throw new TmuxError(400, 'unsupported agent');
104
+ const set = await run(target, [
105
+ 'set-option',
106
+ '-t',
107
+ name,
108
+ '-q',
109
+ AGENT_OPTION,
110
+ agentInitialValue(augmented.kind),
111
+ ]);
112
+ if (set.code !== 0) {
113
+ if (/can't find session|session not found|no server running/i.test(set.stderr)) {
114
+ throw new TmuxError(404, `session "${name}" not found`);
115
+ }
116
+ throw new TmuxError(502, firstStderrLine(set.stderr));
117
+ }
118
+ const send = await run(target, ['send-keys', '-t', name, '-l', augmented.command]);
119
+ if (send.code !== 0)
120
+ throw new TmuxError(502, firstStderrLine(send.stderr));
121
+ const enter = await run(target, ['send-keys', '-t', name, 'Enter']);
122
+ if (enter.code !== 0)
123
+ throw new TmuxError(502, firstStderrLine(enter.stderr));
124
+ }
125
+ export async function renameSession(target, name, newName) {
126
+ if (!isValidSessionName(newName))
127
+ throw new TmuxError(400, 'invalid new session name');
128
+ const r = await run(target, ['rename-session', '-t', name, newName]);
129
+ if (r.code === 0)
130
+ return;
131
+ if (/can't find session|session not found/i.test(r.stderr)) {
132
+ throw new TmuxError(404, `session "${name}" not found`);
133
+ }
134
+ if (/duplicate session/i.test(r.stderr)) {
135
+ throw new TmuxError(409, `session "${newName}" already exists`);
136
+ }
137
+ throw new TmuxError(502, firstStderrLine(r.stderr));
138
+ }
139
+ export async function killSession(target, name) {
140
+ const r = await run(target, ['kill-session', '-t', name]);
141
+ if (r.code === 0)
142
+ return;
143
+ if (/can't find session|session not found|no server running/i.test(r.stderr)) {
144
+ throw new TmuxError(404, `session "${name}" not found`);
145
+ }
146
+ throw new TmuxError(502, firstStderrLine(r.stderr));
147
+ }
148
+ export async function listWindows(target, name) {
149
+ const r = await run(target, ['list-windows', '-t', name, '-F', WINDOW_FORMAT]);
150
+ if (r.code === 0)
151
+ return parseWindows(r.stdout);
152
+ if (/can't find session|session not found|no server running/i.test(r.stderr)) {
153
+ throw new TmuxError(404, `session "${name}" not found`);
154
+ }
155
+ throw new TmuxError(502, firstStderrLine(r.stderr));
156
+ }
157
+ //# 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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"}
@@ -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"}