@wong2kim/wmux 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/README.md +157 -0
  2. package/assets/icon.ico +0 -0
  3. package/assets/icon.svg +6 -0
  4. package/dist/cli/cli/client.js +102 -0
  5. package/dist/cli/cli/commands/browser.js +137 -0
  6. package/dist/cli/cli/commands/input.js +80 -0
  7. package/dist/cli/cli/commands/notify.js +28 -0
  8. package/dist/cli/cli/commands/pane.js +88 -0
  9. package/dist/cli/cli/commands/surface.js +98 -0
  10. package/dist/cli/cli/commands/system.js +98 -0
  11. package/dist/cli/cli/commands/workspace.js +117 -0
  12. package/dist/cli/cli/index.js +140 -0
  13. package/dist/cli/cli/utils.js +47 -0
  14. package/dist/cli/shared/constants.js +54 -0
  15. package/dist/cli/shared/rpc.js +33 -0
  16. package/dist/cli/shared/types.js +79 -0
  17. package/dist/mcp/mcp/index.js +60 -0
  18. package/dist/mcp/mcp/wmux-client.js +146 -0
  19. package/dist/mcp/shared/constants.js +54 -0
  20. package/dist/mcp/shared/rpc.js +33 -0
  21. package/dist/mcp/shared/types.js +79 -0
  22. package/forge.config.ts +61 -0
  23. package/index.html +12 -0
  24. package/package.json +84 -0
  25. package/postcss.config.js +6 -0
  26. package/src/cli/client.ts +76 -0
  27. package/src/cli/commands/browser.ts +128 -0
  28. package/src/cli/commands/input.ts +72 -0
  29. package/src/cli/commands/notify.ts +29 -0
  30. package/src/cli/commands/pane.ts +90 -0
  31. package/src/cli/commands/surface.ts +102 -0
  32. package/src/cli/commands/system.ts +95 -0
  33. package/src/cli/commands/workspace.ts +116 -0
  34. package/src/cli/index.ts +145 -0
  35. package/src/cli/utils.ts +44 -0
  36. package/src/main/index.ts +86 -0
  37. package/src/main/ipc/handlers/clipboard.handler.ts +20 -0
  38. package/src/main/ipc/handlers/metadata.handler.ts +56 -0
  39. package/src/main/ipc/handlers/pty.handler.ts +69 -0
  40. package/src/main/ipc/handlers/session.handler.ts +17 -0
  41. package/src/main/ipc/handlers/shell.handler.ts +11 -0
  42. package/src/main/ipc/registerHandlers.ts +31 -0
  43. package/src/main/mcp/McpRegistrar.ts +156 -0
  44. package/src/main/metadata/MetadataCollector.ts +58 -0
  45. package/src/main/notification/ToastManager.ts +32 -0
  46. package/src/main/pipe/PipeServer.ts +190 -0
  47. package/src/main/pipe/RpcRouter.ts +46 -0
  48. package/src/main/pipe/handlers/_bridge.ts +40 -0
  49. package/src/main/pipe/handlers/browser.rpc.ts +132 -0
  50. package/src/main/pipe/handlers/input.rpc.ts +120 -0
  51. package/src/main/pipe/handlers/meta.rpc.ts +59 -0
  52. package/src/main/pipe/handlers/notify.rpc.ts +53 -0
  53. package/src/main/pipe/handlers/pane.rpc.ts +39 -0
  54. package/src/main/pipe/handlers/surface.rpc.ts +43 -0
  55. package/src/main/pipe/handlers/system.rpc.ts +36 -0
  56. package/src/main/pipe/handlers/workspace.rpc.ts +52 -0
  57. package/src/main/pty/AgentDetector.ts +247 -0
  58. package/src/main/pty/OscParser.ts +81 -0
  59. package/src/main/pty/PTYBridge.ts +88 -0
  60. package/src/main/pty/PTYManager.ts +104 -0
  61. package/src/main/pty/ShellDetector.ts +63 -0
  62. package/src/main/session/SessionManager.ts +53 -0
  63. package/src/main/updater/AutoUpdater.ts +132 -0
  64. package/src/main/window/createWindow.ts +71 -0
  65. package/src/mcp/README.md +56 -0
  66. package/src/mcp/index.ts +153 -0
  67. package/src/mcp/wmux-client.ts +127 -0
  68. package/src/preload/index.ts +111 -0
  69. package/src/preload/preload.ts +108 -0
  70. package/src/renderer/App.tsx +5 -0
  71. package/src/renderer/components/Browser/BrowserPanel.tsx +219 -0
  72. package/src/renderer/components/Browser/BrowserToolbar.tsx +253 -0
  73. package/src/renderer/components/Company/ApprovalDialog.tsx +3 -0
  74. package/src/renderer/components/Company/CompanyView.tsx +7 -0
  75. package/src/renderer/components/Company/MessageFeedPanel.tsx +3 -0
  76. package/src/renderer/components/Layout/AppLayout.tsx +234 -0
  77. package/src/renderer/components/Notification/NotificationPanel.tsx +129 -0
  78. package/src/renderer/components/Palette/CommandPalette.tsx +409 -0
  79. package/src/renderer/components/Palette/PaletteItem.tsx +55 -0
  80. package/src/renderer/components/Pane/Pane.tsx +122 -0
  81. package/src/renderer/components/Pane/PaneContainer.tsx +41 -0
  82. package/src/renderer/components/Pane/SurfaceTabs.tsx +46 -0
  83. package/src/renderer/components/Settings/SettingsPanel.tsx +886 -0
  84. package/src/renderer/components/Sidebar/MiniSidebar.tsx +67 -0
  85. package/src/renderer/components/Sidebar/Sidebar.tsx +84 -0
  86. package/src/renderer/components/Sidebar/WorkspaceItem.tsx +241 -0
  87. package/src/renderer/components/StatusBar/StatusBar.tsx +93 -0
  88. package/src/renderer/components/Terminal/SearchBar.tsx +126 -0
  89. package/src/renderer/components/Terminal/Terminal.tsx +102 -0
  90. package/src/renderer/components/Terminal/ViCopyMode.tsx +104 -0
  91. package/src/renderer/hooks/useKeyboard.ts +310 -0
  92. package/src/renderer/hooks/useNotificationListener.ts +80 -0
  93. package/src/renderer/hooks/useNotificationSound.ts +75 -0
  94. package/src/renderer/hooks/useRpcBridge.ts +451 -0
  95. package/src/renderer/hooks/useT.ts +11 -0
  96. package/src/renderer/hooks/useTerminal.ts +349 -0
  97. package/src/renderer/hooks/useViCopyMode.ts +320 -0
  98. package/src/renderer/i18n/index.ts +69 -0
  99. package/src/renderer/i18n/locales/en.ts +157 -0
  100. package/src/renderer/i18n/locales/ja.ts +155 -0
  101. package/src/renderer/i18n/locales/ko.ts +155 -0
  102. package/src/renderer/i18n/locales/zh.ts +155 -0
  103. package/src/renderer/index.tsx +6 -0
  104. package/src/renderer/stores/index.ts +19 -0
  105. package/src/renderer/stores/slices/notificationSlice.ts +56 -0
  106. package/src/renderer/stores/slices/paneSlice.ts +141 -0
  107. package/src/renderer/stores/slices/surfaceSlice.ts +122 -0
  108. package/src/renderer/stores/slices/uiSlice.ts +247 -0
  109. package/src/renderer/stores/slices/workspaceSlice.ts +120 -0
  110. package/src/renderer/styles/globals.css +150 -0
  111. package/src/renderer/themes.ts +99 -0
  112. package/src/shared/constants.ts +53 -0
  113. package/src/shared/electron.d.ts +11 -0
  114. package/src/shared/rpc.ts +71 -0
  115. package/src/shared/types.ts +176 -0
  116. package/tailwind.config.js +11 -0
  117. package/tsconfig.cli.json +24 -0
  118. package/tsconfig.json +21 -0
  119. package/tsconfig.mcp.json +25 -0
  120. package/vite.main.config.ts +14 -0
  121. package/vite.preload.config.ts +9 -0
  122. package/vite.renderer.config.ts +6 -0
package/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # wmux
2
+
3
+ > Terminal multiplexer for Windows with built-in browser, notifications, and AI agent status detection.
4
+
5
+ Windows-native terminal multiplexer with workspaces, panes, tabs, integrated browser, notification system, CLI/API access, and session management.
6
+
7
+ ## Quick Install
8
+
9
+ **PowerShell (recommended):**
10
+ ```powershell
11
+ irm https://raw.githubusercontent.com/openwong2kim/wmux/main/install.ps1 | iex
12
+ ```
13
+
14
+ **Git Bash / WSL:**
15
+ ```bash
16
+ curl -fsSL https://raw.githubusercontent.com/openwong2kim/wmux/main/install.sh | bash
17
+ ```
18
+
19
+ **Manual:**
20
+ ```bash
21
+ git clone https://github.com/openwong2kim/wmux.git
22
+ cd wmux
23
+ npm install
24
+ npm start
25
+ ```
26
+
27
+ ## Requirements
28
+
29
+ - Windows 10/11
30
+ - Node.js 18+
31
+ - Git
32
+
33
+ ## Features
34
+
35
+ - **Terminal**: xterm.js + WebGL GPU rendering, ConPTY, PowerShell native
36
+ - **Workspaces**: Vertical sidebar, drag-and-drop, Ctrl+1~9 switching
37
+ - **Split Panes**: Ctrl+D / Ctrl+Shift+D, directional focus
38
+ - **In-App Browser**: Ctrl+Shift+L, scriptable API
39
+ - **Notifications**: OSC 9/99/777, rings, toast, Ctrl+I panel
40
+ - **AI Agent Status**: Detects Claude Code, Cursor, Aider, Codex, Gemini, Copilot, OpenCode
41
+ - **Command Palette**: Ctrl+K fuzzy search
42
+ - **Terminal Search**: Ctrl+F
43
+ - **Vi Copy Mode**: Ctrl+Shift+X
44
+ - **CLI + API**: Named Pipe JSON-RPC, `wmux` CLI
45
+ - **MCP Server**: Claude Code can control browser and terminal via MCP tools
46
+ - **Multi-Agent**: Each agent targets its own browser surface via `surfaceId`
47
+ - **Session Management**: Save and restore sessions
48
+ - **i18n**: English, Korean, Japanese, Chinese
49
+
50
+ ## Usage
51
+
52
+ ```bash
53
+ npm start # Run in dev mode
54
+ npm run package # Build executable
55
+ npm run make # Create installer
56
+ wmux --help # CLI commands
57
+ ```
58
+
59
+ ## CLI Examples
60
+
61
+ ```bash
62
+ # Workspace management
63
+ wmux workspace create "MyWorkspace"
64
+ wmux workspace list
65
+ wmux workspace switch "MyWorkspace"
66
+
67
+ # Pane operations
68
+ wmux pane split-right
69
+ wmux pane split-down
70
+ wmux pane send-text "echo hello"
71
+
72
+ # Surface (browser)
73
+ wmux surface open "https://example.com"
74
+ wmux surface eval "window.location"
75
+ ```
76
+
77
+ ## Keyboard Shortcuts
78
+
79
+ | Key | Action |
80
+ |-----|--------|
81
+ | Ctrl+B | Toggle sidebar |
82
+ | Ctrl+N | New workspace |
83
+ | Ctrl+D | Split right |
84
+ | Ctrl+Shift+D | Split down |
85
+ | Ctrl+T | New tab |
86
+ | Ctrl+W | Close tab |
87
+ | Ctrl+F | Search terminal |
88
+ | Ctrl+K | Command palette |
89
+ | Ctrl+I | Notifications |
90
+ | Ctrl+, | Settings |
91
+ | Ctrl+Shift+L | Open browser |
92
+ | Ctrl+Shift+H | Flash pane |
93
+ | Ctrl+1~9 | Switch workspace |
94
+
95
+ ## Tech Stack
96
+
97
+ Electron 41 + React 19 + TypeScript 5.9 + Tailwind 3 + Zustand 5 + xterm.js 6 + node-pty
98
+
99
+ ## MCP Server (Claude Code Integration)
100
+
101
+ wmux includes an MCP server that lets Claude Code directly control the browser and terminal.
102
+
103
+ ### Setup
104
+
105
+ ```bash
106
+ npm run build:mcp
107
+ ```
108
+
109
+ Add to your project's `.mcp.json`:
110
+ ```json
111
+ {
112
+ "mcpServers": {
113
+ "wmux": {
114
+ "command": "node",
115
+ "args": ["<path-to-wmux>/dist/mcp/mcp/index.js"]
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
121
+ `WMUX_SOCKET_PATH` and `WMUX_AUTH_TOKEN` are automatically set in wmux terminal sessions — no extra env config needed when running Claude Code inside wmux.
122
+
123
+ ### Available MCP Tools
124
+
125
+ | Tool | Description |
126
+ |------|-------------|
127
+ | `browser_navigate` | Navigate browser to URL |
128
+ | `browser_snapshot` | Get page HTML |
129
+ | `browser_click` | Click element by CSS selector |
130
+ | `browser_fill` | Fill input by CSS selector |
131
+ | `browser_eval` | Execute JS in browser |
132
+ | `terminal_read` | Read terminal screen |
133
+ | `terminal_send` | Send text to terminal |
134
+ | `terminal_send_key` | Send key (enter, ctrl+c, etc.) |
135
+ | `workspace_list` | List workspaces |
136
+ | `surface_list` | List surfaces (terminals + browsers) |
137
+ | `pane_list` | List panes |
138
+
139
+ ### Multi-Agent Usage
140
+
141
+ All browser tools accept an optional `surfaceId` parameter for multi-agent scenarios:
142
+
143
+ ```
144
+ 1. Call surface_list → find your browser surface ID
145
+ 2. Call browser_navigate with surfaceId="<your-browser-id>"
146
+ 3. Call browser_snapshot with surfaceId="<your-browser-id>"
147
+ ```
148
+
149
+ Each agent can independently control its own browser surface. When `surfaceId` is omitted, the currently active browser surface is used.
150
+
151
+ ## Note on AI Agents
152
+
153
+ WinMux detects AI coding agents for status display purposes only. It does not call any AI APIs, capture agent outputs, or automate agent interactions. Users are responsible for complying with their AI provider's Terms of Service.
154
+
155
+ ## License
156
+
157
+ MIT
Binary file
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
2
+ <rect width="256" height="256" rx="48" fill="#1e1e2e"/>
3
+ <rect x="8" y="8" width="240" height="240" rx="40" fill="#181825" stroke="#313244" stroke-width="2"/>
4
+ <text x="128" y="170" text-anchor="middle" font-family="monospace" font-weight="bold" font-size="140" fill="#89b4fa">W</text>
5
+ <circle cx="200" cy="64" r="16" fill="#a6e3a1"/>
6
+ </svg>
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.sendRequest = sendRequest;
38
+ const net = __importStar(require("net"));
39
+ const crypto = __importStar(require("crypto"));
40
+ const PIPE_NAME = process.env.WMUX_SOCKET_PATH || '\\\\.\\pipe\\wmux';
41
+ const TIMEOUT_MS = 5000;
42
+ function sendRequest(method, params = {}) {
43
+ return new Promise((resolve, reject) => {
44
+ const id = crypto.randomUUID();
45
+ const token = process.env.WMUX_AUTH_TOKEN;
46
+ const request = { id, method, params, token };
47
+ const socket = net.connect(PIPE_NAME);
48
+ let buffer = '';
49
+ let settled = false;
50
+ const timer = setTimeout(() => {
51
+ if (!settled) {
52
+ settled = true;
53
+ socket.destroy();
54
+ reject(new Error('Request timed out after 5 seconds.'));
55
+ }
56
+ }, TIMEOUT_MS);
57
+ socket.on('connect', () => {
58
+ socket.write(JSON.stringify(request) + '\n');
59
+ });
60
+ socket.on('data', (chunk) => {
61
+ buffer += chunk.toString('utf8');
62
+ const lines = buffer.split('\n');
63
+ buffer = lines.pop() ?? '';
64
+ for (const line of lines) {
65
+ const trimmed = line.trim();
66
+ if (!trimmed)
67
+ continue;
68
+ try {
69
+ const response = JSON.parse(trimmed);
70
+ if (response.id === id && !settled) {
71
+ settled = true;
72
+ clearTimeout(timer);
73
+ socket.destroy();
74
+ resolve(response);
75
+ }
76
+ }
77
+ catch {
78
+ // ignore malformed lines
79
+ }
80
+ }
81
+ });
82
+ socket.on('error', (err) => {
83
+ if (!settled) {
84
+ settled = true;
85
+ clearTimeout(timer);
86
+ if (err.code === 'ENOENT' || err.code === 'ECONNREFUSED') {
87
+ reject(new Error('wmux is not running. Start the app first.'));
88
+ }
89
+ else {
90
+ reject(new Error(`Connection error: ${err.message}`));
91
+ }
92
+ }
93
+ });
94
+ socket.on('close', () => {
95
+ if (!settled) {
96
+ settled = true;
97
+ clearTimeout(timer);
98
+ reject(new Error('Connection closed before response was received.'));
99
+ }
100
+ });
101
+ });
102
+ }
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleBrowser = handleBrowser;
4
+ const client_1 = require("../client");
5
+ const utils_1 = require("../utils");
6
+ const BROWSER_HELP = `
7
+ wmux browser — Scriptable Browser API
8
+
9
+ USAGE
10
+ wmux browser <subcommand> [args]
11
+
12
+ SUBCOMMANDS
13
+ snapshot Return the full page HTML (document.documentElement.outerHTML)
14
+ click <selector> Click the first element matching the CSS selector
15
+ fill <selector> <text> Set the value of an input matching the CSS selector
16
+ eval <code> Execute arbitrary JavaScript in the page context
17
+ navigate <url> Navigate the active browser surface to a URL
18
+
19
+ EXAMPLES
20
+ wmux browser snapshot
21
+ wmux browser click "#submit-btn"
22
+ wmux browser fill "input[name=email]" "user@example.com"
23
+ wmux browser eval "document.title"
24
+ wmux browser navigate "https://example.com"
25
+ `.trimStart();
26
+ async function handleBrowser(args, jsonMode) {
27
+ const sub = args[0];
28
+ const rest = args.slice(1);
29
+ if (!sub || sub === '--help' || sub === '-h') {
30
+ process.stdout.write(BROWSER_HELP);
31
+ process.exit(0);
32
+ }
33
+ let response;
34
+ switch (sub) {
35
+ // ── browser snapshot ─────────────────────────────────────────────────────
36
+ case 'snapshot': {
37
+ response = await (0, client_1.sendRequest)('browser.snapshot', {});
38
+ if (jsonMode) {
39
+ (0, utils_1.printResult)(response);
40
+ }
41
+ else {
42
+ if (!response.ok) {
43
+ (0, utils_1.printError)(response);
44
+ return;
45
+ }
46
+ const r = response.result;
47
+ process.stdout.write(r?.html ?? '');
48
+ }
49
+ break;
50
+ }
51
+ // ── browser click <selector> ─────────────────────────────────────────────
52
+ case 'click': {
53
+ const selector = rest[0];
54
+ if (!selector) {
55
+ console.error('Error: browser click requires <selector>');
56
+ process.exit(1);
57
+ }
58
+ response = await (0, client_1.sendRequest)('browser.click', { selector });
59
+ if (jsonMode) {
60
+ (0, utils_1.printResult)(response);
61
+ }
62
+ else {
63
+ if (!response.ok) {
64
+ (0, utils_1.printError)(response);
65
+ return;
66
+ }
67
+ console.log(`Clicked: ${selector}`);
68
+ }
69
+ break;
70
+ }
71
+ // ── browser fill <selector> <text> ───────────────────────────────────────
72
+ case 'fill': {
73
+ const selector = rest[0];
74
+ const text = rest.slice(1).join(' ');
75
+ if (!selector) {
76
+ console.error('Error: browser fill requires <selector> <text>');
77
+ process.exit(1);
78
+ }
79
+ response = await (0, client_1.sendRequest)('browser.fill', { selector, text });
80
+ if (jsonMode) {
81
+ (0, utils_1.printResult)(response);
82
+ }
83
+ else {
84
+ if (!response.ok) {
85
+ (0, utils_1.printError)(response);
86
+ return;
87
+ }
88
+ console.log(`Filled "${selector}" with "${text}"`);
89
+ }
90
+ break;
91
+ }
92
+ // ── browser eval <code> ──────────────────────────────────────────────────
93
+ case 'eval': {
94
+ const code = rest.join(' ');
95
+ if (!code) {
96
+ console.error('Error: browser eval requires <code>');
97
+ process.exit(1);
98
+ }
99
+ response = await (0, client_1.sendRequest)('browser.eval', { code });
100
+ if (jsonMode) {
101
+ (0, utils_1.printResult)(response);
102
+ }
103
+ else {
104
+ if (!response.ok) {
105
+ (0, utils_1.printError)(response);
106
+ return;
107
+ }
108
+ const r = response.result;
109
+ console.log(JSON.stringify(r?.result, null, 2));
110
+ }
111
+ break;
112
+ }
113
+ // ── browser navigate <url> ───────────────────────────────────────────────
114
+ case 'navigate': {
115
+ const url = rest[0];
116
+ if (!url) {
117
+ console.error('Error: browser navigate requires <url>');
118
+ process.exit(1);
119
+ }
120
+ response = await (0, client_1.sendRequest)('browser.navigate', { url });
121
+ if (jsonMode) {
122
+ (0, utils_1.printResult)(response);
123
+ }
124
+ else {
125
+ if (!response.ok) {
126
+ (0, utils_1.printError)(response);
127
+ return;
128
+ }
129
+ console.log(`Navigated to: ${url}`);
130
+ }
131
+ break;
132
+ }
133
+ default:
134
+ console.error(`Unknown browser subcommand: "${sub}". Run 'wmux browser --help' for usage.`);
135
+ process.exit(1);
136
+ }
137
+ }
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleInput = handleInput;
4
+ const client_1 = require("../client");
5
+ const utils_1 = require("../utils");
6
+ async function handleInput(cmd, args, jsonMode) {
7
+ let response;
8
+ switch (cmd) {
9
+ case 'send': {
10
+ const text = args.join(' ');
11
+ if (!text) {
12
+ console.error('Error: send requires <text>');
13
+ process.exit(1);
14
+ }
15
+ response = await (0, client_1.sendRequest)('input.send', { text });
16
+ if (jsonMode) {
17
+ (0, utils_1.printResult)(response);
18
+ }
19
+ else {
20
+ if (!response.ok) {
21
+ (0, utils_1.printError)(response);
22
+ return;
23
+ }
24
+ console.log('Text sent.');
25
+ }
26
+ break;
27
+ }
28
+ case 'send-key': {
29
+ const key = args[0];
30
+ if (!key) {
31
+ console.error('Error: send-key requires <keystroke>');
32
+ process.exit(1);
33
+ }
34
+ response = await (0, client_1.sendRequest)('input.sendKey', { key });
35
+ if (jsonMode) {
36
+ (0, utils_1.printResult)(response);
37
+ }
38
+ else {
39
+ if (!response.ok) {
40
+ (0, utils_1.printError)(response);
41
+ return;
42
+ }
43
+ console.log(`Key sent: ${key}`);
44
+ }
45
+ break;
46
+ }
47
+ case 'read-screen': {
48
+ response = await (0, client_1.sendRequest)('input.readScreen', {});
49
+ if (jsonMode) {
50
+ (0, utils_1.printResult)(response);
51
+ }
52
+ else {
53
+ if (!response.ok) {
54
+ (0, utils_1.printError)(response);
55
+ return;
56
+ }
57
+ // server returns { text: string } object or plain string
58
+ const result = response.result;
59
+ let screen;
60
+ if (typeof result === 'object' && result !== null && 'text' in result) {
61
+ screen = result.text ?? '';
62
+ }
63
+ else if (typeof result === 'string') {
64
+ screen = result;
65
+ }
66
+ else {
67
+ console.log(JSON.stringify(result, null, 2));
68
+ break;
69
+ }
70
+ process.stdout.write(screen);
71
+ if (!screen.endsWith('\n'))
72
+ process.stdout.write('\n');
73
+ }
74
+ break;
75
+ }
76
+ default:
77
+ console.error(`Unknown input command: ${cmd}`);
78
+ process.exit(1);
79
+ }
80
+ }
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleNotify = handleNotify;
4
+ const client_1 = require("../client");
5
+ const utils_1 = require("../utils");
6
+ async function handleNotify(args, jsonMode) {
7
+ const title = (0, utils_1.parseFlag)(args, '--title');
8
+ const body = (0, utils_1.parseFlag)(args, '--body');
9
+ if (!title) {
10
+ console.error('Error: notify requires --title <text>');
11
+ process.exit(1);
12
+ }
13
+ if (!body) {
14
+ console.error('Error: notify requires --body <text>');
15
+ process.exit(1);
16
+ }
17
+ const response = await (0, client_1.sendRequest)('notify', { title, body });
18
+ if (jsonMode) {
19
+ (0, utils_1.printResult)(response);
20
+ }
21
+ else {
22
+ if (!response.ok) {
23
+ (0, utils_1.printError)(response);
24
+ return;
25
+ }
26
+ console.log(`Notification sent: "${title}"`);
27
+ }
28
+ }
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handlePane = handlePane;
4
+ const client_1 = require("../client");
5
+ const utils_1 = require("../utils");
6
+ function formatPaneList(result) {
7
+ const list = result;
8
+ if (!Array.isArray(list) || list.length === 0) {
9
+ console.log('No panes found.');
10
+ return;
11
+ }
12
+ const maxId = Math.max(...list.map((p) => p.id.length));
13
+ console.log('ID'.padEnd(maxId + 2) + 'TYPE'.padEnd(8) + 'DETAILS');
14
+ console.log('-'.repeat(maxId + 30));
15
+ for (const p of list) {
16
+ let details = '';
17
+ if (p.type === 'leaf' && p.activeSurfaceId) {
18
+ details = `active surface: ${p.activeSurfaceId}`;
19
+ }
20
+ else if (p.type === 'branch' && p.direction) {
21
+ details = `direction: ${p.direction}`;
22
+ }
23
+ console.log(p.id.padEnd(maxId + 2) + p.type.padEnd(8) + details);
24
+ }
25
+ }
26
+ async function handlePane(cmd, args, jsonMode) {
27
+ let response;
28
+ switch (cmd) {
29
+ case 'list-panes': {
30
+ response = await (0, client_1.sendRequest)('pane.list', {});
31
+ if (jsonMode) {
32
+ (0, utils_1.printResult)(response);
33
+ }
34
+ else {
35
+ if (!response.ok) {
36
+ (0, utils_1.printError)(response);
37
+ return;
38
+ }
39
+ formatPaneList(response.result);
40
+ }
41
+ break;
42
+ }
43
+ case 'focus-pane': {
44
+ const id = args[0];
45
+ if (!id) {
46
+ console.error('Error: focus-pane requires <id>');
47
+ process.exit(1);
48
+ }
49
+ response = await (0, client_1.sendRequest)('pane.focus', { id });
50
+ if (jsonMode) {
51
+ (0, utils_1.printResult)(response);
52
+ }
53
+ else {
54
+ if (!response.ok) {
55
+ (0, utils_1.printError)(response);
56
+ return;
57
+ }
58
+ console.log(`Focused pane: ${id}`);
59
+ }
60
+ break;
61
+ }
62
+ case 'split': {
63
+ const direction = (0, utils_1.parseFlag)(args, '--direction') ?? 'right';
64
+ if (direction !== 'right' && direction !== 'down') {
65
+ console.error('Error: --direction must be "right" or "down"');
66
+ process.exit(1);
67
+ }
68
+ // right → horizontal, down → vertical (server expects horizontal/vertical)
69
+ const dirMap = { right: 'horizontal', down: 'vertical' };
70
+ const mapped = dirMap[direction] || direction;
71
+ response = await (0, client_1.sendRequest)('pane.split', { direction: mapped });
72
+ if (jsonMode) {
73
+ (0, utils_1.printResult)(response);
74
+ }
75
+ else {
76
+ if (!response.ok) {
77
+ (0, utils_1.printError)(response);
78
+ return;
79
+ }
80
+ console.log(`Split pane ${direction}.`);
81
+ }
82
+ break;
83
+ }
84
+ default:
85
+ console.error(`Unknown pane command: ${cmd}`);
86
+ process.exit(1);
87
+ }
88
+ }