tmuxes 0.1.7 → 0.1.9

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/.node-version +1 -0
  2. package/.nvmrc +1 -0
  3. package/.tmp-npm-cache/_cacache/content-v2/sha512/43/27/5e000b8b9c56a6ccc66f709485499f4304e2cb1982582ba571321c07b3ef56fcabd2c671898cc8003365a0485b6fd8e73e7b17b073cec0f7d1628c1a99df +0 -0
  4. package/.tmp-npm-cache/_cacache/content-v2/sha512/51/cf/4301295d74559ed494bae160d54d8741077f89faebb311882ac065019246951e7b53f3dcb913793c42b331e14c7070c4810c3cdc27a427d103a7db4614e0 +0 -0
  5. package/.tmp-npm-cache/_cacache/content-v2/sha512/c3/4d/d68a454a916e74c2617f586fbf770981b33811d667c2547eb0e9fc21938f4ee7e98f1ceee4bde8ad8815b5f6efe21b60eee798837d68f51a3340d7e5bb7a +0 -0
  6. package/.tmp-npm-cache/_cacache/content-v2/sha512/fe/40/2abfbefc96299e8bf714aa91d62607190ae299e102cf5933db2e2904640d65d25d67dbbb6fa2ddc92a17f00b9dbfdf2e37487f67d96ec36c64a285b59a7d +0 -0
  7. package/.tmp-npm-cache/_cacache/index-v5/27/fe/81a3de6ce7ae3d1e41a3421de20c5629998c4ee5d0ffe2037630f03b03b2 +4 -0
  8. package/.tmp-npm-cache/_cacache/index-v5/65/22/dd66711f62681fce09aabb2357a2907b4a0c778ac5227c4baf9603fd86e8 +4 -0
  9. package/.tmp-npm-cache/_update-notifier-last-checked +0 -0
  10. package/AGENTS.md +15 -0
  11. package/CLAUDE.md +3 -0
  12. package/LICENSE +21 -21
  13. package/README.en.md +304 -0
  14. package/README.md +301 -289
  15. package/SECURITY.md +31 -0
  16. package/{public → client}/index.html +12 -13
  17. package/client/package.json +29 -0
  18. package/client/src/App.tsx +123 -0
  19. package/client/src/activity.ts +5 -0
  20. package/client/src/api.ts +130 -0
  21. package/client/src/attention.tsx +157 -0
  22. package/client/src/components/FileExplorer.tsx +156 -0
  23. package/client/src/components/FileViewer.tsx +194 -0
  24. package/client/src/components/SessionRow.tsx +108 -0
  25. package/client/src/components/SessionTree.tsx +197 -0
  26. package/client/src/components/SettingsButton.tsx +122 -0
  27. package/client/src/components/Sidebar.tsx +96 -0
  28. package/client/src/components/StatusBanner.tsx +31 -0
  29. package/client/src/components/TargetGroup.tsx +275 -0
  30. package/client/src/components/TerminalPanel.tsx +192 -0
  31. package/client/src/folders.ts +245 -0
  32. package/client/src/hooks/useTerminal.ts +67 -0
  33. package/client/src/hooks/useTmuxSocket.ts +65 -0
  34. package/client/src/i18n.ts +213 -0
  35. package/client/src/main.tsx +17 -0
  36. package/client/src/settings.tsx +87 -0
  37. package/client/src/styles.css +723 -0
  38. package/client/src/types.ts +93 -0
  39. package/client/src/util.ts +65 -0
  40. package/client/tsconfig.json +13 -0
  41. package/client/vite.config.ts +15 -0
  42. package/fig/fig1.png +0 -0
  43. package/package.json +28 -61
  44. package/scripts/prepack.mjs +35 -0
  45. package/{bin → server/bin}/tmuxes.js +36 -36
  46. package/server/package.json +61 -0
  47. package/server/src/agentHooks.ts +120 -0
  48. package/server/src/agentOutput.ts +36 -0
  49. package/server/src/agentState.ts +70 -0
  50. package/server/src/config.ts +31 -0
  51. package/server/src/exe.ts +34 -0
  52. package/server/src/exec.ts +61 -0
  53. package/server/src/files.ts +330 -0
  54. package/server/src/foldersStore.ts +114 -0
  55. package/server/src/index.ts +114 -0
  56. package/server/src/logger.ts +16 -0
  57. package/{dist/monitor.js → server/src/monitor.ts} +10 -9
  58. package/server/src/openBrowser.ts +28 -0
  59. package/{dist/platform.js → server/src/platform.ts} +4 -5
  60. package/server/src/rest/router.ts +290 -0
  61. package/server/src/targetCommand.ts +79 -0
  62. package/server/src/targets.ts +152 -0
  63. package/server/src/tmux/builder.ts +198 -0
  64. package/server/src/tmux/formats.ts +95 -0
  65. package/server/src/tmux/sessions.ts +204 -0
  66. package/server/src/validate.ts +79 -0
  67. package/server/src/windowsSsh.ts +239 -0
  68. package/server/src/winshell/manager.ts +296 -0
  69. package/server/src/ws/protocol.ts +15 -0
  70. package/server/src/ws/sshState.ts +36 -0
  71. package/server/src/ws/terminalSession.ts +207 -0
  72. package/server/src/ws/wsServer.ts +153 -0
  73. package/server/src/wsl.ts +38 -0
  74. package/server/test/agentHooks.test.ts +66 -0
  75. package/server/test/agentOutput.test.ts +26 -0
  76. package/server/test/agentState.test.ts +24 -0
  77. package/server/test/builder.test.ts +162 -0
  78. package/server/test/files.test.ts +81 -0
  79. package/server/test/formats.test.ts +123 -0
  80. package/server/test/monitor.test.ts +25 -0
  81. package/server/test/validate.test.ts +71 -0
  82. package/server/test/wsl.test.ts +18 -0
  83. package/server/tsconfig.json +9 -0
  84. package/server/vitest.config.ts +12 -0
  85. package/start.cmd +30 -0
  86. package/start.command +20 -0
  87. package/start.sh +20 -0
  88. package/tsconfig.base.json +19 -0
  89. package/dist/agentHooks.js +0 -91
  90. package/dist/agentHooks.js.map +0 -1
  91. package/dist/agentOutput.js +0 -30
  92. package/dist/agentOutput.js.map +0 -1
  93. package/dist/agentState.js +0 -45
  94. package/dist/agentState.js.map +0 -1
  95. package/dist/config.js +0 -32
  96. package/dist/config.js.map +0 -1
  97. package/dist/exe.js +0 -37
  98. package/dist/exe.js.map +0 -1
  99. package/dist/exec.js +0 -43
  100. package/dist/exec.js.map +0 -1
  101. package/dist/files.js +0 -243
  102. package/dist/files.js.map +0 -1
  103. package/dist/foldersStore.js +0 -103
  104. package/dist/foldersStore.js.map +0 -1
  105. package/dist/index.js +0 -117
  106. package/dist/index.js.map +0 -1
  107. package/dist/logger.js +0 -16
  108. package/dist/logger.js.map +0 -1
  109. package/dist/monitor.js.map +0 -1
  110. package/dist/openBrowser.js +0 -31
  111. package/dist/openBrowser.js.map +0 -1
  112. package/dist/platform.js.map +0 -1
  113. package/dist/rest/router.js +0 -190
  114. package/dist/rest/router.js.map +0 -1
  115. package/dist/targetCommand.js +0 -41
  116. package/dist/targetCommand.js.map +0 -1
  117. package/dist/targets.js +0 -131
  118. package/dist/targets.js.map +0 -1
  119. package/dist/tmux/builder.js +0 -173
  120. package/dist/tmux/builder.js.map +0 -1
  121. package/dist/tmux/formats.js +0 -61
  122. package/dist/tmux/formats.js.map +0 -1
  123. package/dist/tmux/sessions.js +0 -157
  124. package/dist/tmux/sessions.js.map +0 -1
  125. package/dist/validate.js +0 -65
  126. package/dist/validate.js.map +0 -1
  127. package/dist/winshell/manager.js +0 -267
  128. package/dist/winshell/manager.js.map +0 -1
  129. package/dist/ws/protocol.js +0 -4
  130. package/dist/ws/protocol.js.map +0 -1
  131. package/dist/ws/sshState.js +0 -35
  132. package/dist/ws/sshState.js.map +0 -1
  133. package/dist/ws/terminalSession.js +0 -204
  134. package/dist/ws/terminalSession.js.map +0 -1
  135. package/dist/ws/wsServer.js +0 -151
  136. package/dist/ws/wsServer.js.map +0 -1
  137. package/dist/wsl.js +0 -35
  138. package/dist/wsl.js.map +0 -1
  139. package/public/assets/index-BpVrfoZw.js +0 -44
  140. package/public/assets/index-D_X5SnGx.css +0 -1
@@ -0,0 +1,81 @@
1
+ import { mkdtemp, mkdir, realpath, rm, symlink, writeFile } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { isInsideRoot, resolveScopedPath } from '../src/files.js';
6
+ import type { Target } from '../src/targets.js';
7
+
8
+ const local: Target = { id: 'local', kind: 'local', label: 'Local' };
9
+ const describePosixLocal = process.platform === 'win32' ? describe.skip : describe;
10
+
11
+ async function withTemp<T>(fn: (dir: string) => Promise<T>): Promise<T> {
12
+ const dir = await mkdtemp(join(tmpdir(), 'tmuxes-files-'));
13
+ try {
14
+ return await fn(dir);
15
+ } finally {
16
+ await rm(dir, { recursive: true, force: true });
17
+ }
18
+ }
19
+
20
+ describe('isInsideRoot', () => {
21
+ it('matches root exactly or descendants only', () => {
22
+ expect(isInsideRoot('/home/me/proj', '/home/me/proj')).toBe(true);
23
+ expect(isInsideRoot('/home/me/proj', '/home/me/proj/src/a.ts')).toBe(true);
24
+ expect(isInsideRoot('/home/me/proj', '/home/me/project2/a.ts')).toBe(false);
25
+ expect(isInsideRoot('/', '/etc/passwd')).toBe(true);
26
+ });
27
+ });
28
+
29
+ describePosixLocal('resolveScopedPath', () => {
30
+ it('allows existing files inside the session root', async () => {
31
+ await withTemp(async (dir) => {
32
+ const root = join(dir, 'root');
33
+ const src = join(root, 'src');
34
+ const file = join(src, 'a.txt');
35
+ await mkdir(src, { recursive: true });
36
+ await writeFile(file, 'ok');
37
+
38
+ await expect(resolveScopedPath(local, root, file)).resolves.toBe(await realpath(file));
39
+ });
40
+ });
41
+
42
+ it('allows creating a new file inside the session root', async () => {
43
+ await withTemp(async (dir) => {
44
+ const root = join(dir, 'root');
45
+ await mkdir(root);
46
+
47
+ await expect(resolveScopedPath(local, root, join(root, 'new.txt'), { forWrite: true })).resolves.toBe(
48
+ join(await realpath(root), 'new.txt'),
49
+ );
50
+ });
51
+ });
52
+
53
+ it('rejects existing paths outside the session root', async () => {
54
+ await withTemp(async (dir) => {
55
+ const root = join(dir, 'root');
56
+ const outside = join(dir, 'outside');
57
+ const file = join(outside, 'secret.txt');
58
+ await mkdir(root);
59
+ await mkdir(outside);
60
+ await writeFile(file, 'secret');
61
+
62
+ await expect(resolveScopedPath(local, root, file)).rejects.toMatchObject({
63
+ status: 403,
64
+ });
65
+ });
66
+ });
67
+
68
+ it('rejects symlink escapes from the session root', async () => {
69
+ await withTemp(async (dir) => {
70
+ const root = join(dir, 'root');
71
+ const outside = join(dir, 'outside');
72
+ await mkdir(root);
73
+ await mkdir(outside);
74
+ await symlink(outside, join(root, 'link'));
75
+
76
+ await expect(resolveScopedPath(local, root, join(root, 'link', 'new.txt'), { forWrite: true })).rejects.toMatchObject({
77
+ status: 403,
78
+ });
79
+ });
80
+ });
81
+ });
@@ -0,0 +1,123 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ SESSION_FORMAT,
4
+ WINDOW_FORMAT,
5
+ parseSessions,
6
+ parseWindows,
7
+ isEmptySessionsError,
8
+ } from '../src/tmux/formats.js';
9
+
10
+ const SEP = '|';
11
+
12
+ describe('parseSessions', () => {
13
+ it('parses delimited session lines (name field last)', () => {
14
+ const out = [
15
+ ['3', '1', '1700000000', '1700000050', '', 'work'].join(SEP),
16
+ ['1', '0', '1700000100', '1700000150', '', 'my proj'].join(SEP), // name with a space survives
17
+ ].join('\n');
18
+ expect(parseSessions(out)).toEqual([
19
+ { name: 'work', windows: 3, attached: true, created: 1700000000, lastActivity: 1700000050 },
20
+ { name: 'my proj', windows: 1, attached: false, created: 1700000100, lastActivity: 1700000150 },
21
+ ]);
22
+ });
23
+ it('parses hook-derived agent state', () => {
24
+ const out = [
25
+ '1',
26
+ '0',
27
+ '1700000100',
28
+ '1700000150',
29
+ 'codex:waiting:decision:PermissionRequest:1700000200.42',
30
+ 'agent',
31
+ ].join(SEP);
32
+ expect(parseSessions(out)).toEqual([
33
+ {
34
+ name: 'agent',
35
+ windows: 1,
36
+ attached: false,
37
+ created: 1700000100,
38
+ lastActivity: 1700000150,
39
+ agentKind: 'codex',
40
+ agentState: 'waiting',
41
+ attentionReason: 'decision',
42
+ agentEvent: 'PermissionRequest',
43
+ agentNonce: '1700000200.42',
44
+ },
45
+ ]);
46
+ });
47
+ it('keeps a separator that appears inside a name', () => {
48
+ const out = ['2', '0', '1700000200', '1700000250', 'claude:idle:done:Stop:1', 'a|b'].join(SEP);
49
+ expect(parseSessions(out)).toEqual([
50
+ {
51
+ name: 'a|b',
52
+ windows: 2,
53
+ attached: false,
54
+ created: 1700000200,
55
+ lastActivity: 1700000250,
56
+ agentKind: 'claude',
57
+ agentState: 'idle',
58
+ attentionReason: 'done',
59
+ agentEvent: 'Stop',
60
+ agentNonce: '1',
61
+ },
62
+ ]);
63
+ });
64
+ it('parses hook-derived error state', () => {
65
+ const out = ['1', '0', '1700000200', '1700000250', 'codex:idle:error:CodexStreamDisconnected:1', 'agent'].join(SEP);
66
+ expect(parseSessions(out)).toEqual([
67
+ {
68
+ name: 'agent',
69
+ windows: 1,
70
+ attached: false,
71
+ created: 1700000200,
72
+ lastActivity: 1700000250,
73
+ agentKind: 'codex',
74
+ agentState: 'idle',
75
+ attentionReason: 'error',
76
+ agentEvent: 'CodexStreamDisconnected',
77
+ agentNonce: '1',
78
+ },
79
+ ]);
80
+ });
81
+ it('returns [] for empty output', () => {
82
+ expect(parseSessions('')).toEqual([]);
83
+ expect(parseSessions('\n')).toEqual([]);
84
+ });
85
+ it('places the free-form name token last', () => {
86
+ expect(SESSION_FORMAT.split(SEP)).toEqual([
87
+ '#{session_windows}',
88
+ '#{session_attached}',
89
+ '#{session_created}',
90
+ '#{session_activity}',
91
+ '#{@tmuxes_agent}',
92
+ '#{session_name}',
93
+ ]);
94
+ });
95
+ });
96
+
97
+ describe('parseWindows', () => {
98
+ it('parses window lines', () => {
99
+ const out = [
100
+ ['0', '2', '1', 'bash'].join(SEP),
101
+ ['1', '1', '0', 'logs'].join(SEP),
102
+ ].join('\n');
103
+ expect(parseWindows(out)).toEqual([
104
+ { index: 0, name: 'bash', panes: 2, active: true },
105
+ { index: 1, name: 'logs', panes: 1, active: false },
106
+ ]);
107
+ expect(WINDOW_FORMAT.split(SEP)[0]).toBe('#{window_index}');
108
+ });
109
+ });
110
+
111
+ describe('isEmptySessionsError', () => {
112
+ it('treats no-server / no-sessions as empty', () => {
113
+ expect(isEmptySessionsError('no server running on /tmp/tmux-1000/default')).toBe(true);
114
+ expect(isEmptySessionsError('no sessions')).toBe(true);
115
+ expect(isEmptySessionsError('error connecting to /tmp/tmux-1000/default (No such file)')).toBe(
116
+ true,
117
+ );
118
+ });
119
+ it('does not swallow real ssh errors', () => {
120
+ expect(isEmptySessionsError('Permission denied (publickey).')).toBe(false);
121
+ expect(isEmptySessionsError('ssh: connect to host x port 22: Connection timed out')).toBe(false);
122
+ });
123
+ });
@@ -0,0 +1,25 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { annotate } from '../src/monitor.js';
3
+ import type { SessionInfo } from '../src/tmux/formats.js';
4
+
5
+ function session(name: string): SessionInfo {
6
+ return {
7
+ name,
8
+ windows: 1,
9
+ attached: false,
10
+ created: 1_700_000_000,
11
+ lastActivity: 10,
12
+ agentKind: 'codex',
13
+ agentState: 'idle',
14
+ attentionReason: 'done',
15
+ agentEvent: 'Stop',
16
+ agentNonce: '123',
17
+ };
18
+ }
19
+
20
+ describe('annotate', () => {
21
+ it('passes through hook-derived agent state without terminal heuristics', () => {
22
+ const sessions = [session('work')];
23
+ expect(annotate('local', sessions)).toBe(sessions);
24
+ });
25
+ });
@@ -0,0 +1,71 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ isValidSessionName,
4
+ isValidHost,
5
+ isValidUser,
6
+ isValidPort,
7
+ isValidDimension,
8
+ parseHostSpec,
9
+ } from '../src/validate.js';
10
+
11
+ describe('isValidSessionName', () => {
12
+ it('accepts plain names', () => {
13
+ expect(isValidSessionName('work')).toBe(true);
14
+ expect(isValidSessionName('my-session_2')).toBe(true);
15
+ expect(isValidSessionName('a'.repeat(64))).toBe(true);
16
+ });
17
+ it('rejects tmux delimiters and metachars', () => {
18
+ expect(isValidSessionName('has.dot')).toBe(false); // '.' is a target delimiter
19
+ expect(isValidSessionName('has:colon')).toBe(false); // ':' is a target delimiter
20
+ expect(isValidSessionName('with space')).toBe(false);
21
+ expect(isValidSessionName('rm -rf /')).toBe(false);
22
+ expect(isValidSessionName('$(whoami)')).toBe(false);
23
+ expect(isValidSessionName('')).toBe(false);
24
+ expect(isValidSessionName('a'.repeat(65))).toBe(false);
25
+ expect(isValidSessionName(42 as unknown)).toBe(false);
26
+ });
27
+ });
28
+
29
+ describe('host / user / port', () => {
30
+ it('validates hosts', () => {
31
+ expect(isValidHost('example.com')).toBe(true);
32
+ expect(isValidHost('10.0.0.1')).toBe(true);
33
+ expect(isValidHost('bad host')).toBe(false);
34
+ expect(isValidHost('a;b')).toBe(false);
35
+ });
36
+ it('validates users', () => {
37
+ expect(isValidUser('alice')).toBe(true);
38
+ expect(isValidUser('al ice')).toBe(false);
39
+ });
40
+ it('validates ports', () => {
41
+ expect(isValidPort(22)).toBe(true);
42
+ expect(isValidPort(65535)).toBe(true);
43
+ expect(isValidPort(0)).toBe(false);
44
+ expect(isValidPort(70000)).toBe(false);
45
+ expect(isValidPort(22.5)).toBe(false);
46
+ });
47
+ it('validates dimensions', () => {
48
+ expect(isValidDimension(80)).toBe(true);
49
+ expect(isValidDimension(1000)).toBe(true);
50
+ expect(isValidDimension(0)).toBe(false);
51
+ expect(isValidDimension(1001)).toBe(false);
52
+ });
53
+ });
54
+
55
+ describe('parseHostSpec', () => {
56
+ it('parses user@host:port', () => {
57
+ expect(parseHostSpec('alice@web1:2222')).toEqual({ user: 'alice', host: 'web1', port: 2222 });
58
+ });
59
+ it('parses host only', () => {
60
+ expect(parseHostSpec('db2')).toEqual({ user: undefined, host: 'db2', port: undefined });
61
+ });
62
+ it('parses user@host', () => {
63
+ expect(parseHostSpec('bob@db2')).toEqual({ user: 'bob', host: 'db2', port: undefined });
64
+ });
65
+ it('rejects garbage', () => {
66
+ expect(parseHostSpec('')).toBeNull();
67
+ expect(parseHostSpec('a b@c')).toBeNull();
68
+ expect(parseHostSpec('host:notaport')).toBeNull();
69
+ expect(parseHostSpec('host:99999')).toBeNull();
70
+ });
71
+ });
@@ -0,0 +1,18 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parseWslList } from '../src/wsl.js';
3
+
4
+ const BOM = String.fromCharCode(0xfeff);
5
+
6
+ describe('parseWslList', () => {
7
+ it('parses CRLF list, strips BOM, filters docker system distros', () => {
8
+ const out = `${BOM}Ubuntu\r\nDebian\r\ndocker-desktop\r\ndocker-desktop-data\r\nkali-linux\r\n`;
9
+ expect(parseWslList(out)).toEqual(['Ubuntu', 'Debian', 'kali-linux']);
10
+ });
11
+ it('handles names with version suffixes and blank lines', () => {
12
+ expect(parseWslList('Ubuntu-22.04\n\nUbuntu-24.04\n')).toEqual(['Ubuntu-22.04', 'Ubuntu-24.04']);
13
+ });
14
+ it('returns [] for empty output', () => {
15
+ expect(parseWslList('')).toEqual([]);
16
+ expect(parseWslList(BOM + '\r\n')).toEqual([]);
17
+ });
18
+ });
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src",
6
+ "types": ["node"]
7
+ },
8
+ "include": ["src"]
9
+ }
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ // Source uses NodeNext-style ".js" import specifiers; map them to ".ts".
5
+ resolve: {
6
+ extensionAlias: { '.js': ['.ts', '.js'] },
7
+ },
8
+ test: {
9
+ include: ['test/**/*.test.ts'],
10
+ environment: 'node',
11
+ },
12
+ });
package/start.cmd ADDED
@@ -0,0 +1,30 @@
1
+ @echo off
2
+ REM One-click launcher for Windows. Double-click in Explorer, or run in
3
+ REM Windows Terminal. Builds if needed, starts the server, opens the browser.
4
+ setlocal
5
+ cd /d "%~dp0"
6
+
7
+ where npm >nul 2>nul
8
+ if errorlevel 1 (
9
+ echo [tmuxes] Node.js / npm not found. Install Node 18+ from https://nodejs.org and retry.
10
+ pause
11
+ exit /b 1
12
+ )
13
+
14
+ if not exist node_modules (
15
+ echo [tmuxes] Installing dependencies...
16
+ call npm install
17
+ if errorlevel 1 ( echo [tmuxes] npm install failed. & pause & exit /b 1 )
18
+ )
19
+
20
+ echo [tmuxes] Building...
21
+ call npm run build
22
+ if errorlevel 1 ( echo [tmuxes] build failed. & pause & exit /b 1 )
23
+
24
+ echo [tmuxes] Starting on http://127.0.0.1:7420 (a browser window will open)...
25
+ REM Run node DIRECTLY (not via npm) so the server is the console's own child:
26
+ REM closing this window (or Ctrl+C) reaches it, it shuts down, and the port is
27
+ REM released. Going through `npm start` can orphan the node process, leaving
28
+ REM 7420 held until the next reboot.
29
+ set TMUXES_OPEN=1
30
+ node "%~dp0server\dist\index.js"
package/start.command ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env bash
2
+ # One-click launcher for macOS / Linux. Builds if needed, starts the server,
3
+ # and opens the browser. (macOS users: the double-clickable copy is start.command.)
4
+ set -e
5
+ cd "$(dirname "$0")"
6
+
7
+ if ! command -v npm >/dev/null 2>&1; then
8
+ echo "[tmuxes] Node.js / npm not found. Install Node 18+ (e.g. 'brew install node') and retry."
9
+ exit 1
10
+ fi
11
+ if ! command -v tmux >/dev/null 2>&1; then
12
+ echo "[tmuxes] tmux not found. Install it (macOS: 'brew install tmux', Debian/Ubuntu: 'sudo apt install tmux')."
13
+ exit 1
14
+ fi
15
+
16
+ [ -d node_modules ] || { echo "[tmuxes] Installing dependencies..."; npm install; }
17
+ echo "[tmuxes] Building..."
18
+ npm run build
19
+ echo "[tmuxes] Starting on http://127.0.0.1:7420 (a browser window will open)..."
20
+ TMUXES_OPEN=1 npm start
package/start.sh ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env bash
2
+ # One-click launcher for macOS / Linux. Builds if needed, starts the server,
3
+ # and opens the browser. (macOS users: the double-clickable copy is start.command.)
4
+ set -e
5
+ cd "$(dirname "$0")"
6
+
7
+ if ! command -v npm >/dev/null 2>&1; then
8
+ echo "[tmuxes] Node.js / npm not found. Install Node 18+ (e.g. 'brew install node') and retry."
9
+ exit 1
10
+ fi
11
+ if ! command -v tmux >/dev/null 2>&1; then
12
+ echo "[tmuxes] tmux not found. Install it (macOS: 'brew install tmux', Debian/Ubuntu: 'sudo apt install tmux')."
13
+ exit 1
14
+ fi
15
+
16
+ [ -d node_modules ] || { echo "[tmuxes] Installing dependencies..."; npm install; }
17
+ echo "[tmuxes] Building..."
18
+ npm run build
19
+ echo "[tmuxes] Starting on http://127.0.0.1:7420 (a browser window will open)..."
20
+ TMUXES_OPEN=1 npm start
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["ES2022"],
5
+ "module": "NodeNext",
6
+ "moduleResolution": "NodeNext",
7
+ "strict": true,
8
+ "noUnusedLocals": true,
9
+ "noUnusedParameters": true,
10
+ "noFallthroughCasesInSwitch": true,
11
+ "noImplicitReturns": true,
12
+ "esModuleInterop": true,
13
+ "forceConsistentCasingInFileNames": true,
14
+ "skipLibCheck": true,
15
+ "resolveJsonModule": true,
16
+ "declaration": false,
17
+ "sourceMap": true
18
+ }
19
+ }
@@ -1,91 +0,0 @@
1
- import { agentHookCommand, } from './agentState.js';
2
- function isDisabled() {
3
- const v = process.env.TMUXES_NO_AUTOHOOK;
4
- return v === '1' || v === 'true';
5
- }
6
- function baseName(token) {
7
- return (token.split(/[\\/]/).pop() || '').toLowerCase().replace(/\.(exe|cmd|bat)$/, '');
8
- }
9
- export function detectAgentKind(command) {
10
- if (isDisabled())
11
- return undefined;
12
- const trimmed = command.trim();
13
- if (!trimmed)
14
- return undefined;
15
- const m = /^(\S+)(\s+[\s\S]*)?$/.exec(trimmed);
16
- if (!m)
17
- return undefined;
18
- switch (baseName(m[1])) {
19
- case 'claude':
20
- return 'claude';
21
- case 'codex':
22
- return 'codex';
23
- default:
24
- return undefined;
25
- }
26
- }
27
- function hook(kind, state, reason, event) {
28
- return { type: 'command', command: agentHookCommand(kind, state, reason, event) };
29
- }
30
- function claudeSettings() {
31
- return JSON.stringify({
32
- hooks: {
33
- UserPromptSubmit: [{ hooks: [hook('claude', 'running', '', 'UserPromptSubmit')] }],
34
- PreToolUse: [{ matcher: '', hooks: [hook('claude', 'running', '', 'PreToolUse')] }],
35
- PostToolUse: [{ matcher: '', hooks: [hook('claude', 'running', '', 'PostToolUse')] }],
36
- PermissionRequest: [
37
- { matcher: '', hooks: [hook('claude', 'waiting', 'decision', 'PermissionRequest')] },
38
- ],
39
- Notification: [
40
- {
41
- matcher: 'permission_prompt',
42
- hooks: [hook('claude', 'waiting', 'decision', 'Notification.permission_prompt')],
43
- },
44
- {
45
- matcher: 'elicitation_dialog',
46
- hooks: [hook('claude', 'waiting', 'decision', 'Notification.elicitation_dialog')],
47
- },
48
- ],
49
- Stop: [{ hooks: [hook('claude', 'idle', 'done', 'Stop')] }],
50
- StopFailure: [{ hooks: [hook('claude', 'idle', 'error', 'StopFailure')] }],
51
- SessionEnd: [{ hooks: [hook('claude', 'idle', 'done', 'SessionEnd')] }],
52
- },
53
- });
54
- }
55
- function tomlString(v) {
56
- return `"${v.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
57
- }
58
- function codexHookConfig(event, cmd, opts = {}) {
59
- const matcher = opts.matcher === undefined ? '' : `matcher=${tomlString(opts.matcher)},`;
60
- return `hooks.${event}=[{${matcher}hooks=[{type="command",command=${tomlString(cmd)}}]}]`;
61
- }
62
- function codexConfigArgs() {
63
- const configs = [
64
- codexHookConfig('UserPromptSubmit', agentHookCommand('codex', 'running', '', 'UserPromptSubmit')),
65
- codexHookConfig('PreToolUse', agentHookCommand('codex', 'running', '', 'PreToolUse'), {
66
- matcher: '',
67
- }),
68
- codexHookConfig('PostToolUse', agentHookCommand('codex', 'running', '', 'PostToolUse'), {
69
- matcher: '',
70
- }),
71
- codexHookConfig('PermissionRequest', agentHookCommand('codex', 'waiting', 'decision', 'PermissionRequest'), { matcher: '' }),
72
- codexHookConfig('Stop', agentHookCommand('codex', 'idle', 'done', 'Stop')),
73
- ];
74
- return configs.map((c) => `-c '${c}'`).join(' ');
75
- }
76
- export function augmentAgentCommand(command) {
77
- const kind = detectAgentKind(command);
78
- if (!kind)
79
- return { command };
80
- const trimmed = command.trim();
81
- const m = /^(\S+)(\s+[\s\S]*)?$/.exec(trimmed);
82
- if (!m)
83
- return { command };
84
- const prog = m[1];
85
- const rest = m[2] ?? '';
86
- if (kind === 'claude') {
87
- return { kind, command: `${prog} --settings '${claudeSettings()}'${rest}` };
88
- }
89
- return { kind, command: `${prog} ${codexConfigArgs()}${rest}` };
90
- }
91
- //# sourceMappingURL=agentHooks.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"agentHooks.js","sourceRoot":"","sources":["../src/agentHooks.ts"],"names":[],"mappings":"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"}
@@ -1,30 +0,0 @@
1
- const ERROR_PATTERNS = [
2
- {
3
- event: 'CodexStreamDisconnected',
4
- kinds: ['codex'],
5
- re: /stream disconnected before completion.*error sending request for url.*\/codex\/responses/i,
6
- },
7
- {
8
- event: 'StreamDisconnected',
9
- re: /stream disconnected before completion/i,
10
- },
11
- ];
12
- function normalizeTerminalText(text) {
13
- return text
14
- .replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, '')
15
- .replace(/\s+/g, ' ')
16
- .trim();
17
- }
18
- export function classifyAgentTerminalError(text, kind) {
19
- const normalized = normalizeTerminalText(text);
20
- if (!normalized)
21
- return undefined;
22
- for (const pattern of ERROR_PATTERNS) {
23
- if (pattern.kinds && !pattern.kinds.includes(kind))
24
- continue;
25
- if (pattern.re.test(normalized))
26
- return pattern.event;
27
- }
28
- return undefined;
29
- }
30
- //# sourceMappingURL=agentOutput.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"agentOutput.js","sourceRoot":"","sources":["../src/agentOutput.ts"],"names":[],"mappings":"AAQA,MAAM,cAAc,GAA2B;IAC7C;QACE,KAAK,EAAE,yBAAyB;QAChC,KAAK,EAAE,CAAC,OAAO,CAAC;QAChB,EAAE,EAAE,2FAA2F;KAChG;IACD;QACE,KAAK,EAAE,oBAAoB;QAC3B,EAAE,EAAE,wCAAwC;KAC7C;CACF,CAAC;AAEF,SAAS,qBAAqB,CAAC,IAAY;IACzC,OAAO,IAAI;SACR,OAAO,CAAC,4BAA4B,EAAE,EAAE,CAAC;SACzC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,IAAY,EAAE,IAAe;IACtE,MAAM,UAAU,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAC/C,IAAI,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IAClC,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;QACrC,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,SAAS;QAC7D,IAAI,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC;YAAE,OAAO,OAAO,CAAC,KAAK,CAAC;IACxD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -1,45 +0,0 @@
1
- export const AGENT_OPTION = '@tmuxes_agent';
2
- function isAgentKind(v) {
3
- return v === 'claude' || v === 'codex';
4
- }
5
- function isAgentState(v) {
6
- return v === 'running' || v === 'waiting' || v === 'idle';
7
- }
8
- function isAttentionReason(v) {
9
- return v === 'decision' || v === 'done' || v === 'error';
10
- }
11
- function cleanToken(v) {
12
- return v.replace(/[^A-Za-z0-9_.-]/g, '_');
13
- }
14
- export function agentValue(kind, state, reason, event, nonce) {
15
- return [
16
- kind,
17
- state,
18
- reason,
19
- cleanToken(event),
20
- nonce.replace(/:/g, '_'),
21
- ].join(':');
22
- }
23
- export function agentInitialValue(kind) {
24
- return agentValue(kind, 'idle', '', 'launch', String(Date.now()));
25
- }
26
- export function agentHookCommand(kind, state, reason, event) {
27
- return `tmux set-option -q ${AGENT_OPTION} ${agentValue(kind, state, reason, event, '$(date +%s).$$')}`;
28
- }
29
- export function parseAgentValue(raw) {
30
- if (!raw)
31
- return undefined;
32
- const [kind, state, reason, event, ...nonceParts] = raw.split(':');
33
- if (!isAgentKind(kind) || !isAgentState(state))
34
- return undefined;
35
- const snap = { agentKind: kind, agentState: state };
36
- if (isAttentionReason(reason))
37
- snap.attentionReason = reason;
38
- if (event)
39
- snap.agentEvent = event;
40
- const nonce = nonceParts.join(':');
41
- if (nonce)
42
- snap.agentNonce = nonce;
43
- return snap;
44
- }
45
- //# sourceMappingURL=agentState.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"agentState.js","sourceRoot":"","sources":["../src/agentState.ts"],"names":[],"mappings":"AAYA,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC;AAE5C,SAAS,WAAW,CAAC,CAAS;IAC5B,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,OAAO,CAAC;AACzC,CAAC;AAED,SAAS,YAAY,CAAC,CAAS;IAC7B,OAAO,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,MAAM,CAAC;AAC5D,CAAC;AAED,SAAS,iBAAiB,CAAC,CAAS;IAClC,OAAO,CAAC,KAAK,UAAU,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,OAAO,CAAC;AAC3D,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,UAAU,CACxB,IAAe,EACf,KAAiB,EACjB,MAA4B,EAC5B,KAAa,EACb,KAAa;IAEb,OAAO;QACL,IAAI;QACJ,KAAK;QACL,MAAM;QACN,UAAU,CAAC,KAAK,CAAC;QACjB,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC;KACzB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAe;IAC/C,OAAO,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,IAAe,EACf,KAAiB,EACjB,MAA4B,EAC5B,KAAa;IAEb,OAAO,sBAAsB,YAAY,IAAI,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,CAAC,EAAE,CAAC;AAC1G,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAuB;IACrD,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACjE,MAAM,IAAI,GAAkB,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IACnE,IAAI,iBAAiB,CAAC,MAAM,CAAC;QAAE,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC;IAC7D,IAAI,KAAK;QAAE,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IACnC,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,KAAK;QAAE,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IACnC,OAAO,IAAI,CAAC;AACd,CAAC"}
package/dist/config.js DELETED
@@ -1,32 +0,0 @@
1
- /** Central configuration. Localhost-only by design — see README/SECURITY. */
2
- export const config = {
3
- /** Bind address. Intentionally not configurable: this is a no-auth local shell UI. */
4
- host: '127.0.0.1',
5
- port: Number(process.env.TMUXES_PORT ?? 7420),
6
- /** ssh timeouts (seconds). */
7
- ssh: {
8
- /** Management calls fail fast so the UI never hangs. */
9
- connectTimeoutMgmt: 8,
10
- /** Interactive attach is allowed a little longer. */
11
- connectTimeoutTty: 10,
12
- /** Keep the OpenSSH control master alive for long-lived SSH reuse. */
13
- controlPersist: 'yes',
14
- },
15
- /**
16
- * Allowed WebSocket Origins. The WS upgrade bypasses Express middleware, so
17
- * we enforce this in the upgrade handler to block DNS-rebind / cross-site WS
18
- * hijack. Empty/absent Origin (non-browser clients) is allowed.
19
- */
20
- isAllowedOrigin(origin) {
21
- if (!origin)
22
- return true;
23
- try {
24
- const { hostname } = new URL(origin);
25
- return hostname === '127.0.0.1' || hostname === 'localhost' || hostname === '[::1]';
26
- }
27
- catch {
28
- return false;
29
- }
30
- },
31
- };
32
- //# sourceMappingURL=config.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,sFAAsF;IACtF,IAAI,EAAE,WAAW;IACjB,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC;IAE7C,8BAA8B;IAC9B,GAAG,EAAE;QACH,wDAAwD;QACxD,kBAAkB,EAAE,CAAC;QACrB,qDAAqD;QACrD,iBAAiB,EAAE,EAAE;QACrB,sEAAsE;QACtE,cAAc,EAAE,KAAK;KACtB;IAED;;;;OAIG;IACH,eAAe,CAAC,MAA0B;QACxC,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;YACrC,OAAO,QAAQ,KAAK,WAAW,IAAI,QAAQ,KAAK,WAAW,IAAI,QAAQ,KAAK,OAAO,CAAC;QACtF,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACO,CAAC"}