tmuxes 0.1.9 → 0.1.11
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 -21
- package/README.md +283 -296
- package/{server/bin → bin}/tmuxes.js +36 -36
- package/dist/agentHooks.js +91 -0
- package/dist/agentHooks.js.map +1 -0
- package/dist/agentOutput.js +30 -0
- package/dist/agentOutput.js.map +1 -0
- package/dist/agentState.js +45 -0
- package/dist/agentState.js.map +1 -0
- package/dist/config.js +32 -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 +308 -0
- package/dist/files.js.map +1 -0
- package/dist/foldersStore.js +103 -0
- package/dist/foldersStore.js.map +1 -0
- package/dist/index.js +117 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.js +16 -0
- package/dist/logger.js.map +1 -0
- package/{server/src/monitor.ts → dist/monitor.js} +9 -10
- package/dist/monitor.js.map +1 -0
- package/dist/openBrowser.js +31 -0
- package/dist/openBrowser.js.map +1 -0
- package/{server/src/platform.ts → dist/platform.js} +5 -4
- package/dist/platform.js.map +1 -0
- package/dist/rest/router.js +198 -0
- package/dist/rest/router.js.map +1 -0
- package/dist/targetCommand.js +60 -0
- package/dist/targetCommand.js.map +1 -0
- package/dist/targets.js +131 -0
- package/dist/targets.js.map +1 -0
- package/dist/tmux/builder.js +174 -0
- package/dist/tmux/builder.js.map +1 -0
- package/dist/tmux/formats.js +61 -0
- package/dist/tmux/formats.js.map +1 -0
- package/dist/tmux/sessions.js +157 -0
- package/dist/tmux/sessions.js.map +1 -0
- package/dist/validate.js +65 -0
- package/dist/validate.js.map +1 -0
- package/dist/windowsSsh.js +209 -0
- package/dist/windowsSsh.js.map +1 -0
- package/dist/winshell/manager.js +267 -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 -28
- package/public/assets/index-D_X5SnGx.css +1 -0
- package/public/assets/index-Dl69CPyt.js +44 -0
- package/{client → public}/index.html +13 -12
- package/.node-version +0 -1
- package/.nvmrc +0 -1
- package/.tmp-npm-cache/_cacache/content-v2/sha512/43/27/5e000b8b9c56a6ccc66f709485499f4304e2cb1982582ba571321c07b3ef56fcabd2c671898cc8003365a0485b6fd8e73e7b17b073cec0f7d1628c1a99df +0 -0
- package/.tmp-npm-cache/_cacache/content-v2/sha512/51/cf/4301295d74559ed494bae160d54d8741077f89faebb311882ac065019246951e7b53f3dcb913793c42b331e14c7070c4810c3cdc27a427d103a7db4614e0 +0 -0
- package/.tmp-npm-cache/_cacache/content-v2/sha512/c3/4d/d68a454a916e74c2617f586fbf770981b33811d667c2547eb0e9fc21938f4ee7e98f1ceee4bde8ad8815b5f6efe21b60eee798837d68f51a3340d7e5bb7a +0 -0
- package/.tmp-npm-cache/_cacache/content-v2/sha512/fe/40/2abfbefc96299e8bf714aa91d62607190ae299e102cf5933db2e2904640d65d25d67dbbb6fa2ddc92a17f00b9dbfdf2e37487f67d96ec36c64a285b59a7d +0 -0
- package/.tmp-npm-cache/_cacache/index-v5/27/fe/81a3de6ce7ae3d1e41a3421de20c5629998c4ee5d0ffe2037630f03b03b2 +0 -4
- package/.tmp-npm-cache/_cacache/index-v5/65/22/dd66711f62681fce09aabb2357a2907b4a0c778ac5227c4baf9603fd86e8 +0 -4
- package/.tmp-npm-cache/_update-notifier-last-checked +0 -0
- package/AGENTS.md +0 -15
- package/CLAUDE.md +0 -3
- package/README.en.md +0 -304
- package/SECURITY.md +0 -31
- package/client/package.json +0 -29
- package/client/src/App.tsx +0 -123
- package/client/src/activity.ts +0 -5
- package/client/src/api.ts +0 -130
- package/client/src/attention.tsx +0 -157
- package/client/src/components/FileExplorer.tsx +0 -156
- package/client/src/components/FileViewer.tsx +0 -194
- package/client/src/components/SessionRow.tsx +0 -108
- package/client/src/components/SessionTree.tsx +0 -197
- package/client/src/components/SettingsButton.tsx +0 -122
- package/client/src/components/Sidebar.tsx +0 -96
- package/client/src/components/StatusBanner.tsx +0 -31
- package/client/src/components/TargetGroup.tsx +0 -275
- package/client/src/components/TerminalPanel.tsx +0 -192
- package/client/src/folders.ts +0 -245
- package/client/src/hooks/useTerminal.ts +0 -67
- package/client/src/hooks/useTmuxSocket.ts +0 -65
- package/client/src/i18n.ts +0 -213
- package/client/src/main.tsx +0 -17
- package/client/src/settings.tsx +0 -87
- package/client/src/styles.css +0 -723
- package/client/src/types.ts +0 -93
- package/client/src/util.ts +0 -65
- package/client/tsconfig.json +0 -13
- package/client/vite.config.ts +0 -15
- package/fig/fig1.png +0 -0
- package/scripts/prepack.mjs +0 -35
- package/server/package.json +0 -61
- package/server/src/agentHooks.ts +0 -120
- package/server/src/agentOutput.ts +0 -36
- package/server/src/agentState.ts +0 -70
- package/server/src/config.ts +0 -31
- package/server/src/exe.ts +0 -34
- package/server/src/exec.ts +0 -61
- package/server/src/files.ts +0 -330
- package/server/src/foldersStore.ts +0 -114
- package/server/src/index.ts +0 -114
- package/server/src/logger.ts +0 -16
- package/server/src/openBrowser.ts +0 -28
- package/server/src/rest/router.ts +0 -290
- package/server/src/targetCommand.ts +0 -79
- package/server/src/targets.ts +0 -152
- package/server/src/tmux/builder.ts +0 -198
- package/server/src/tmux/formats.ts +0 -95
- package/server/src/tmux/sessions.ts +0 -204
- package/server/src/validate.ts +0 -79
- package/server/src/windowsSsh.ts +0 -239
- package/server/src/winshell/manager.ts +0 -296
- package/server/src/ws/protocol.ts +0 -15
- package/server/src/ws/sshState.ts +0 -36
- package/server/src/ws/terminalSession.ts +0 -207
- package/server/src/ws/wsServer.ts +0 -153
- package/server/src/wsl.ts +0 -38
- package/server/test/agentHooks.test.ts +0 -66
- package/server/test/agentOutput.test.ts +0 -26
- package/server/test/agentState.test.ts +0 -24
- package/server/test/builder.test.ts +0 -162
- package/server/test/files.test.ts +0 -81
- package/server/test/formats.test.ts +0 -123
- package/server/test/monitor.test.ts +0 -25
- package/server/test/validate.test.ts +0 -71
- package/server/test/wsl.test.ts +0 -18
- package/server/tsconfig.json +0 -9
- package/server/vitest.config.ts +0 -12
- package/start.cmd +0 -30
- package/start.command +0 -20
- package/start.sh +0 -20
- package/tsconfig.base.json +0 -19
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// tmuxes — web UI to manage tmux sessions (local / SSH / WSL).
|
|
3
|
-
// Parses a couple of flags, sets the env the server reads, then launches it.
|
|
4
|
-
|
|
5
|
-
const args = process.argv.slice(2);
|
|
6
|
-
|
|
7
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
8
|
-
console.log(`tmuxes — one browser tab to run and supervise tmux sessions (local · SSH · WSL)
|
|
9
|
-
|
|
10
|
-
Usage:
|
|
11
|
-
tmuxes [options]
|
|
12
|
-
|
|
13
|
-
Options:
|
|
14
|
-
--port <n> Port to listen on (default 7420, env TMUXES_PORT)
|
|
15
|
-
--no-open Do not open the browser
|
|
16
|
-
-h, --help Show this help
|
|
17
|
-
|
|
18
|
-
Binds to 127.0.0.1 only (no-auth local UI). Then open http://127.0.0.1:7420
|
|
19
|
-
(opens automatically unless --no-open). Requires tmux on the host you connect to.`);
|
|
20
|
-
process.exit(0);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function flag(name) {
|
|
24
|
-
const i = args.indexOf(name);
|
|
25
|
-
return i >= 0 ? args[i + 1] : undefined;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const port = flag('--port');
|
|
29
|
-
if (port) process.env.TMUXES_PORT = port;
|
|
30
|
-
|
|
31
|
-
// Open the browser by default (the "one-click run" experience), unless the
|
|
32
|
-
// user opted out or already set TMUXES_OPEN.
|
|
33
|
-
if (!args.includes('--no-open') && process.env.TMUXES_OPEN === undefined) {
|
|
34
|
-
process.env.TMUXES_OPEN = '1';
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
await import('../dist/index.js');
|
|
2
|
+
// tmuxes — web UI to manage tmux sessions (local / SSH / WSL).
|
|
3
|
+
// Parses a couple of flags, sets the env the server reads, then launches it.
|
|
4
|
+
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
|
|
7
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
8
|
+
console.log(`tmuxes — one browser tab to run and supervise tmux sessions (local · SSH · WSL)
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
tmuxes [options]
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
--port <n> Port to listen on (default 7420, env TMUXES_PORT)
|
|
15
|
+
--no-open Do not open the browser
|
|
16
|
+
-h, --help Show this help
|
|
17
|
+
|
|
18
|
+
Binds to 127.0.0.1 only (no-auth local UI). Then open http://127.0.0.1:7420
|
|
19
|
+
(opens automatically unless --no-open). Requires tmux on the host you connect to.`);
|
|
20
|
+
process.exit(0);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function flag(name) {
|
|
24
|
+
const i = args.indexOf(name);
|
|
25
|
+
return i >= 0 ? args[i + 1] : undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const port = flag('--port');
|
|
29
|
+
if (port) process.env.TMUXES_PORT = port;
|
|
30
|
+
|
|
31
|
+
// Open the browser by default (the "one-click run" experience), unless the
|
|
32
|
+
// user opted out or already set TMUXES_OPEN.
|
|
33
|
+
if (!args.includes('--no-open') && process.env.TMUXES_OPEN === undefined) {
|
|
34
|
+
process.env.TMUXES_OPEN = '1';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
await import('../dist/index.js');
|
|
@@ -0,0 +1,91 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentHooks.js","sourceRoot":"","sources":["../src/agentHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
package/dist/exe.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { isAbsolute, join, delimiter } from 'node:path';
|
|
3
|
+
import { isWindows } from './platform.js';
|
|
4
|
+
/**
|
|
5
|
+
* Resolve a command name to a full path for node-pty on Windows.
|
|
6
|
+
*
|
|
7
|
+
* Unlike child_process.spawn, node-pty's ConPTY path does NOT search PATH or
|
|
8
|
+
* append a PATHEXT extension, so `pty.spawn('ssh', …)` fails with
|
|
9
|
+
* "File not found". We resolve the executable ourselves (PATH + common system
|
|
10
|
+
* locations). On POSIX, execvp already searches PATH, so the name is returned
|
|
11
|
+
* unchanged.
|
|
12
|
+
*/
|
|
13
|
+
export function resolveExecutable(file) {
|
|
14
|
+
if (!isWindows)
|
|
15
|
+
return file;
|
|
16
|
+
if (isAbsolute(file) && existsSync(file))
|
|
17
|
+
return file;
|
|
18
|
+
const hasExt = /\.[A-Za-z0-9]+$/.test(file);
|
|
19
|
+
const exts = hasExt ? [''] : (process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM').split(';');
|
|
20
|
+
const sysRoot = process.env.SystemRoot || process.env.windir || 'C:\\Windows';
|
|
21
|
+
const dirs = [
|
|
22
|
+
...(process.env.PATH || '').split(delimiter),
|
|
23
|
+
join(sysRoot, 'System32'),
|
|
24
|
+
join(sysRoot, 'System32', 'OpenSSH'), // ssh.exe ships here, not always on PATH
|
|
25
|
+
];
|
|
26
|
+
for (const dir of dirs) {
|
|
27
|
+
if (!dir)
|
|
28
|
+
continue;
|
|
29
|
+
for (const ext of exts) {
|
|
30
|
+
const candidate = join(dir, file + ext);
|
|
31
|
+
if (existsSync(candidate))
|
|
32
|
+
return candidate;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return file; // fall back; node-pty surfaces a clear error if truly missing
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=exe.js.map
|
package/dist/exe.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exe.js","sourceRoot":"","sources":["../src/exe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5B,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtD,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,qBAAqB,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvF,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,aAAa,CAAC;IAC9E,MAAM,IAAI,GAAG;QACX,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC;QAC5C,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC;QACzB,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,yCAAyC;KAChF,CAAC;IACF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC,GAAG;YAAE,SAAS;QACnB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,GAAG,GAAG,CAAC,CAAC;YACxC,IAAI,UAAU,CAAC,SAAS,CAAC;gBAAE,OAAO,SAAS,CAAC;QAC9C,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,CAAC,8DAA8D;AAC7E,CAAC"}
|
package/dist/exec.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Run a command as an argv array with NO shell. Never rejects — a nonzero exit
|
|
4
|
+
* or spawn error resolves with the captured output so callers can interpret it
|
|
5
|
+
* (e.g. tmux "no server running" is a normal empty case, not a throw).
|
|
6
|
+
*/
|
|
7
|
+
export function runCommand(file, args, opts = {}) {
|
|
8
|
+
const encoding = opts.encoding ?? 'utf8';
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
const child = spawn(file, args, { shell: false });
|
|
11
|
+
const outChunks = [];
|
|
12
|
+
const errChunks = [];
|
|
13
|
+
let settled = false;
|
|
14
|
+
const timer = opts.timeoutMs
|
|
15
|
+
? setTimeout(() => {
|
|
16
|
+
child.kill('SIGKILL');
|
|
17
|
+
}, opts.timeoutMs)
|
|
18
|
+
: null;
|
|
19
|
+
child.stdout.on('data', (d) => outChunks.push(d));
|
|
20
|
+
child.stderr.on('data', (d) => errChunks.push(d));
|
|
21
|
+
if (opts.input !== undefined) {
|
|
22
|
+
child.stdin.on('error', () => {
|
|
23
|
+
/* ignore EPIPE if the child exits early */
|
|
24
|
+
});
|
|
25
|
+
child.stdin.end(opts.input);
|
|
26
|
+
}
|
|
27
|
+
const done = (code, signal, extraErr) => {
|
|
28
|
+
if (settled)
|
|
29
|
+
return;
|
|
30
|
+
settled = true;
|
|
31
|
+
if (timer)
|
|
32
|
+
clearTimeout(timer);
|
|
33
|
+
const stderr = Buffer.concat(errChunks).toString(encoding) + (extraErr ?? '');
|
|
34
|
+
resolve({ code, signal, stdout: Buffer.concat(outChunks).toString(encoding), stderr });
|
|
35
|
+
};
|
|
36
|
+
child.on('error', (err) => {
|
|
37
|
+
// e.g. ENOENT when ssh/tmux/wsl.exe is missing — surface as a failed result.
|
|
38
|
+
done(null, null, err.message);
|
|
39
|
+
});
|
|
40
|
+
child.on('close', (code, signal) => done(code, signal));
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=exec.js.map
|
package/dist/exec.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../src/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAiB3C;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,IAAc,EAAE,OAAmB,EAAE;IAC5E,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC;IACzC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAClD,MAAM,SAAS,GAAa,EAAE,CAAC;QAC/B,MAAM,SAAS,GAAa,EAAE,CAAC;QAC/B,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS;YAC1B,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE;gBACd,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACxB,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC;YACpB,CAAC,CAAC,IAAI,CAAC;QAET,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1D,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAE1D,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC7B,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBAC3B,2CAA2C;YAC7C,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,IAAmB,EAAE,MAA6B,EAAE,QAAiB,EAAE,EAAE;YACrF,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,IAAI,KAAK;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;YAC/B,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;YAC9E,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QACzF,CAAC,CAAC;QAEF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,6EAA6E;YAC7E,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;AACL,CAAC"}
|