@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.
- package/README.md +157 -0
- package/assets/icon.ico +0 -0
- package/assets/icon.svg +6 -0
- package/dist/cli/cli/client.js +102 -0
- package/dist/cli/cli/commands/browser.js +137 -0
- package/dist/cli/cli/commands/input.js +80 -0
- package/dist/cli/cli/commands/notify.js +28 -0
- package/dist/cli/cli/commands/pane.js +88 -0
- package/dist/cli/cli/commands/surface.js +98 -0
- package/dist/cli/cli/commands/system.js +98 -0
- package/dist/cli/cli/commands/workspace.js +117 -0
- package/dist/cli/cli/index.js +140 -0
- package/dist/cli/cli/utils.js +47 -0
- package/dist/cli/shared/constants.js +54 -0
- package/dist/cli/shared/rpc.js +33 -0
- package/dist/cli/shared/types.js +79 -0
- package/dist/mcp/mcp/index.js +60 -0
- package/dist/mcp/mcp/wmux-client.js +146 -0
- package/dist/mcp/shared/constants.js +54 -0
- package/dist/mcp/shared/rpc.js +33 -0
- package/dist/mcp/shared/types.js +79 -0
- package/forge.config.ts +61 -0
- package/index.html +12 -0
- package/package.json +84 -0
- package/postcss.config.js +6 -0
- package/src/cli/client.ts +76 -0
- package/src/cli/commands/browser.ts +128 -0
- package/src/cli/commands/input.ts +72 -0
- package/src/cli/commands/notify.ts +29 -0
- package/src/cli/commands/pane.ts +90 -0
- package/src/cli/commands/surface.ts +102 -0
- package/src/cli/commands/system.ts +95 -0
- package/src/cli/commands/workspace.ts +116 -0
- package/src/cli/index.ts +145 -0
- package/src/cli/utils.ts +44 -0
- package/src/main/index.ts +86 -0
- package/src/main/ipc/handlers/clipboard.handler.ts +20 -0
- package/src/main/ipc/handlers/metadata.handler.ts +56 -0
- package/src/main/ipc/handlers/pty.handler.ts +69 -0
- package/src/main/ipc/handlers/session.handler.ts +17 -0
- package/src/main/ipc/handlers/shell.handler.ts +11 -0
- package/src/main/ipc/registerHandlers.ts +31 -0
- package/src/main/mcp/McpRegistrar.ts +156 -0
- package/src/main/metadata/MetadataCollector.ts +58 -0
- package/src/main/notification/ToastManager.ts +32 -0
- package/src/main/pipe/PipeServer.ts +190 -0
- package/src/main/pipe/RpcRouter.ts +46 -0
- package/src/main/pipe/handlers/_bridge.ts +40 -0
- package/src/main/pipe/handlers/browser.rpc.ts +132 -0
- package/src/main/pipe/handlers/input.rpc.ts +120 -0
- package/src/main/pipe/handlers/meta.rpc.ts +59 -0
- package/src/main/pipe/handlers/notify.rpc.ts +53 -0
- package/src/main/pipe/handlers/pane.rpc.ts +39 -0
- package/src/main/pipe/handlers/surface.rpc.ts +43 -0
- package/src/main/pipe/handlers/system.rpc.ts +36 -0
- package/src/main/pipe/handlers/workspace.rpc.ts +52 -0
- package/src/main/pty/AgentDetector.ts +247 -0
- package/src/main/pty/OscParser.ts +81 -0
- package/src/main/pty/PTYBridge.ts +88 -0
- package/src/main/pty/PTYManager.ts +104 -0
- package/src/main/pty/ShellDetector.ts +63 -0
- package/src/main/session/SessionManager.ts +53 -0
- package/src/main/updater/AutoUpdater.ts +132 -0
- package/src/main/window/createWindow.ts +71 -0
- package/src/mcp/README.md +56 -0
- package/src/mcp/index.ts +153 -0
- package/src/mcp/wmux-client.ts +127 -0
- package/src/preload/index.ts +111 -0
- package/src/preload/preload.ts +108 -0
- package/src/renderer/App.tsx +5 -0
- package/src/renderer/components/Browser/BrowserPanel.tsx +219 -0
- package/src/renderer/components/Browser/BrowserToolbar.tsx +253 -0
- package/src/renderer/components/Company/ApprovalDialog.tsx +3 -0
- package/src/renderer/components/Company/CompanyView.tsx +7 -0
- package/src/renderer/components/Company/MessageFeedPanel.tsx +3 -0
- package/src/renderer/components/Layout/AppLayout.tsx +234 -0
- package/src/renderer/components/Notification/NotificationPanel.tsx +129 -0
- package/src/renderer/components/Palette/CommandPalette.tsx +409 -0
- package/src/renderer/components/Palette/PaletteItem.tsx +55 -0
- package/src/renderer/components/Pane/Pane.tsx +122 -0
- package/src/renderer/components/Pane/PaneContainer.tsx +41 -0
- package/src/renderer/components/Pane/SurfaceTabs.tsx +46 -0
- package/src/renderer/components/Settings/SettingsPanel.tsx +886 -0
- package/src/renderer/components/Sidebar/MiniSidebar.tsx +67 -0
- package/src/renderer/components/Sidebar/Sidebar.tsx +84 -0
- package/src/renderer/components/Sidebar/WorkspaceItem.tsx +241 -0
- package/src/renderer/components/StatusBar/StatusBar.tsx +93 -0
- package/src/renderer/components/Terminal/SearchBar.tsx +126 -0
- package/src/renderer/components/Terminal/Terminal.tsx +102 -0
- package/src/renderer/components/Terminal/ViCopyMode.tsx +104 -0
- package/src/renderer/hooks/useKeyboard.ts +310 -0
- package/src/renderer/hooks/useNotificationListener.ts +80 -0
- package/src/renderer/hooks/useNotificationSound.ts +75 -0
- package/src/renderer/hooks/useRpcBridge.ts +451 -0
- package/src/renderer/hooks/useT.ts +11 -0
- package/src/renderer/hooks/useTerminal.ts +349 -0
- package/src/renderer/hooks/useViCopyMode.ts +320 -0
- package/src/renderer/i18n/index.ts +69 -0
- package/src/renderer/i18n/locales/en.ts +157 -0
- package/src/renderer/i18n/locales/ja.ts +155 -0
- package/src/renderer/i18n/locales/ko.ts +155 -0
- package/src/renderer/i18n/locales/zh.ts +155 -0
- package/src/renderer/index.tsx +6 -0
- package/src/renderer/stores/index.ts +19 -0
- package/src/renderer/stores/slices/notificationSlice.ts +56 -0
- package/src/renderer/stores/slices/paneSlice.ts +141 -0
- package/src/renderer/stores/slices/surfaceSlice.ts +122 -0
- package/src/renderer/stores/slices/uiSlice.ts +247 -0
- package/src/renderer/stores/slices/workspaceSlice.ts +120 -0
- package/src/renderer/styles/globals.css +150 -0
- package/src/renderer/themes.ts +99 -0
- package/src/shared/constants.ts +53 -0
- package/src/shared/electron.d.ts +11 -0
- package/src/shared/rpc.ts +71 -0
- package/src/shared/types.ts +176 -0
- package/tailwind.config.js +11 -0
- package/tsconfig.cli.json +24 -0
- package/tsconfig.json +21 -0
- package/tsconfig.mcp.json +25 -0
- package/vite.main.config.ts +14 -0
- package/vite.preload.config.ts +9 -0
- 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
|
package/assets/icon.ico
ADDED
|
Binary file
|
package/assets/icon.svg
ADDED
|
@@ -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
|
+
}
|