@wong2kim/wmux 1.0.0 → 1.0.1
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 -0
- package/README.md +209 -157
- package/dist/cli/cli/client.js +1 -1
- package/dist/cli/cli/commands/browser.js +19 -19
- package/dist/cli/cli/index.js +58 -58
- package/dist/cli/shared/constants.js +17 -4
- package/dist/mcp/shared/constants.js +17 -4
- package/package.json +96 -84
- package/assets/icon.ico +0 -0
- package/assets/icon.svg +0 -6
- package/forge.config.ts +0 -61
- package/index.html +0 -12
- package/postcss.config.js +0 -6
- package/src/cli/client.ts +0 -76
- package/src/cli/commands/browser.ts +0 -128
- package/src/cli/commands/input.ts +0 -72
- package/src/cli/commands/notify.ts +0 -29
- package/src/cli/commands/pane.ts +0 -90
- package/src/cli/commands/surface.ts +0 -102
- package/src/cli/commands/system.ts +0 -95
- package/src/cli/commands/workspace.ts +0 -116
- package/src/cli/index.ts +0 -145
- package/src/cli/utils.ts +0 -44
- package/src/main/index.ts +0 -86
- package/src/main/ipc/handlers/clipboard.handler.ts +0 -20
- package/src/main/ipc/handlers/metadata.handler.ts +0 -56
- package/src/main/ipc/handlers/pty.handler.ts +0 -69
- package/src/main/ipc/handlers/session.handler.ts +0 -17
- package/src/main/ipc/handlers/shell.handler.ts +0 -11
- package/src/main/ipc/registerHandlers.ts +0 -31
- package/src/main/mcp/McpRegistrar.ts +0 -156
- package/src/main/metadata/MetadataCollector.ts +0 -58
- package/src/main/notification/ToastManager.ts +0 -32
- package/src/main/pipe/PipeServer.ts +0 -190
- package/src/main/pipe/RpcRouter.ts +0 -46
- package/src/main/pipe/handlers/_bridge.ts +0 -40
- package/src/main/pipe/handlers/browser.rpc.ts +0 -132
- package/src/main/pipe/handlers/input.rpc.ts +0 -120
- package/src/main/pipe/handlers/meta.rpc.ts +0 -59
- package/src/main/pipe/handlers/notify.rpc.ts +0 -53
- package/src/main/pipe/handlers/pane.rpc.ts +0 -39
- package/src/main/pipe/handlers/surface.rpc.ts +0 -43
- package/src/main/pipe/handlers/system.rpc.ts +0 -36
- package/src/main/pipe/handlers/workspace.rpc.ts +0 -52
- package/src/main/pty/AgentDetector.ts +0 -247
- package/src/main/pty/OscParser.ts +0 -81
- package/src/main/pty/PTYBridge.ts +0 -88
- package/src/main/pty/PTYManager.ts +0 -104
- package/src/main/pty/ShellDetector.ts +0 -63
- package/src/main/session/SessionManager.ts +0 -53
- package/src/main/updater/AutoUpdater.ts +0 -132
- package/src/main/window/createWindow.ts +0 -71
- package/src/mcp/README.md +0 -56
- package/src/mcp/index.ts +0 -153
- package/src/mcp/wmux-client.ts +0 -127
- package/src/preload/index.ts +0 -111
- package/src/preload/preload.ts +0 -108
- package/src/renderer/App.tsx +0 -5
- package/src/renderer/components/Browser/BrowserPanel.tsx +0 -219
- package/src/renderer/components/Browser/BrowserToolbar.tsx +0 -253
- package/src/renderer/components/Company/ApprovalDialog.tsx +0 -3
- package/src/renderer/components/Company/CompanyView.tsx +0 -7
- package/src/renderer/components/Company/MessageFeedPanel.tsx +0 -3
- package/src/renderer/components/Layout/AppLayout.tsx +0 -234
- package/src/renderer/components/Notification/NotificationPanel.tsx +0 -129
- package/src/renderer/components/Palette/CommandPalette.tsx +0 -409
- package/src/renderer/components/Palette/PaletteItem.tsx +0 -55
- package/src/renderer/components/Pane/Pane.tsx +0 -122
- package/src/renderer/components/Pane/PaneContainer.tsx +0 -41
- package/src/renderer/components/Pane/SurfaceTabs.tsx +0 -46
- package/src/renderer/components/Settings/SettingsPanel.tsx +0 -886
- package/src/renderer/components/Sidebar/MiniSidebar.tsx +0 -67
- package/src/renderer/components/Sidebar/Sidebar.tsx +0 -84
- package/src/renderer/components/Sidebar/WorkspaceItem.tsx +0 -241
- package/src/renderer/components/StatusBar/StatusBar.tsx +0 -93
- package/src/renderer/components/Terminal/SearchBar.tsx +0 -126
- package/src/renderer/components/Terminal/Terminal.tsx +0 -102
- package/src/renderer/components/Terminal/ViCopyMode.tsx +0 -104
- package/src/renderer/hooks/useKeyboard.ts +0 -310
- package/src/renderer/hooks/useNotificationListener.ts +0 -80
- package/src/renderer/hooks/useNotificationSound.ts +0 -75
- package/src/renderer/hooks/useRpcBridge.ts +0 -451
- package/src/renderer/hooks/useT.ts +0 -11
- package/src/renderer/hooks/useTerminal.ts +0 -349
- package/src/renderer/hooks/useViCopyMode.ts +0 -320
- package/src/renderer/i18n/index.ts +0 -69
- package/src/renderer/i18n/locales/en.ts +0 -157
- package/src/renderer/i18n/locales/ja.ts +0 -155
- package/src/renderer/i18n/locales/ko.ts +0 -155
- package/src/renderer/i18n/locales/zh.ts +0 -155
- package/src/renderer/index.tsx +0 -6
- package/src/renderer/stores/index.ts +0 -19
- package/src/renderer/stores/slices/notificationSlice.ts +0 -56
- package/src/renderer/stores/slices/paneSlice.ts +0 -141
- package/src/renderer/stores/slices/surfaceSlice.ts +0 -122
- package/src/renderer/stores/slices/uiSlice.ts +0 -247
- package/src/renderer/stores/slices/workspaceSlice.ts +0 -120
- package/src/renderer/styles/globals.css +0 -150
- package/src/renderer/themes.ts +0 -99
- package/src/shared/constants.ts +0 -53
- package/src/shared/electron.d.ts +0 -11
- package/src/shared/rpc.ts +0 -71
- package/src/shared/types.ts +0 -176
- package/tailwind.config.js +0 -11
- package/tsconfig.cli.json +0 -24
- package/tsconfig.json +0 -21
- package/tsconfig.mcp.json +0 -25
- package/vite.main.config.ts +0 -14
- package/vite.preload.config.ts +0 -9
- package/vite.renderer.config.ts +0 -6
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ENV_KEYS = exports.
|
|
3
|
+
exports.ENV_KEYS = exports.IPC = void 0;
|
|
4
4
|
exports.getPipeName = getPipeName;
|
|
5
5
|
exports.getAuthTokenPath = getAuthTokenPath;
|
|
6
6
|
// IPC Channel names
|
|
@@ -11,6 +11,8 @@ exports.IPC = {
|
|
|
11
11
|
PTY_DISPOSE: 'pty:dispose',
|
|
12
12
|
PTY_DATA: 'pty:data',
|
|
13
13
|
PTY_EXIT: 'pty:exit',
|
|
14
|
+
PTY_LIST: 'pty:list',
|
|
15
|
+
PTY_RECONNECT: 'pty:reconnect',
|
|
14
16
|
SHELL_LIST: 'shell:list',
|
|
15
17
|
SESSION_SAVE: 'session:save',
|
|
16
18
|
SESSION_LOAD: 'session:load',
|
|
@@ -24,6 +26,8 @@ exports.IPC = {
|
|
|
24
26
|
// Clipboard (main process bridge)
|
|
25
27
|
CLIPBOARD_WRITE: 'clipboard:write',
|
|
26
28
|
CLIPBOARD_READ: 'clipboard:read',
|
|
29
|
+
CLIPBOARD_READ_IMAGE: 'clipboard:read-image',
|
|
30
|
+
CLIPBOARD_HAS_IMAGE: 'clipboard:has-image',
|
|
27
31
|
// Phase 4: Auto updater
|
|
28
32
|
UPDATE_CHECK: 'update:check',
|
|
29
33
|
UPDATE_AVAILABLE: 'update:available',
|
|
@@ -33,12 +37,21 @@ exports.IPC = {
|
|
|
33
37
|
UPDATE_INSTALL: 'update:install',
|
|
34
38
|
// Settings sync (renderer → main)
|
|
35
39
|
TOAST_ENABLED: 'settings:toast-enabled',
|
|
40
|
+
// Agent critical action approval
|
|
41
|
+
APPROVAL_REQUEST: 'approval:request',
|
|
42
|
+
// File system
|
|
43
|
+
FS_READ_DIR: 'fs:read-dir',
|
|
44
|
+
FS_READ_FILE: 'fs:read-file',
|
|
45
|
+
FS_WATCH: 'fs:watch',
|
|
46
|
+
FS_UNWATCH: 'fs:unwatch',
|
|
47
|
+
FS_CHANGED: 'fs:changed',
|
|
36
48
|
};
|
|
37
|
-
// Named Pipe path for wmux API
|
|
49
|
+
// Named Pipe / Unix socket path for wmux API
|
|
38
50
|
// Fixed name so MCP clients (e.g. Claude Code) can reconnect across wmux restarts
|
|
39
|
-
exports.PIPE_NAME = '\\\\.\\pipe\\wmux';
|
|
40
51
|
function getPipeName() {
|
|
41
|
-
|
|
52
|
+
if (process.platform === 'win32')
|
|
53
|
+
return '\\\\.\\pipe\\wmux';
|
|
54
|
+
return '/tmp/wmux.sock';
|
|
42
55
|
}
|
|
43
56
|
// Environment variable names injected into PTY sessions
|
|
44
57
|
exports.ENV_KEYS = {
|
package/package.json
CHANGED
|
@@ -1,84 +1,96 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@wong2kim/wmux",
|
|
3
|
-
"productName": "wmux",
|
|
4
|
-
"version": "1.0.
|
|
5
|
-
"description": "
|
|
6
|
-
"main": ".vite/build/index.js",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"build:
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"@
|
|
58
|
-
"@
|
|
59
|
-
"@
|
|
60
|
-
"
|
|
61
|
-
"electron": "
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
"@
|
|
71
|
-
"@
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
"
|
|
83
|
-
|
|
84
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@wong2kim/wmux",
|
|
3
|
+
"productName": "wmux",
|
|
4
|
+
"version": "1.0.1",
|
|
5
|
+
"description": "Windows terminal multiplexer with MCP server for AI agents - run multiple CLI sessions in parallel, control via Claude Code and other AI tools",
|
|
6
|
+
"main": ".vite/build/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"postinstall": "node scripts/fix-node-pty.js",
|
|
9
|
+
"start": "npm run build:mcp && electron-forge start",
|
|
10
|
+
"package": "npm run build:mcp && electron-forge package",
|
|
11
|
+
"make": "npm run build:mcp && electron-forge make",
|
|
12
|
+
"publish": "electron-forge publish",
|
|
13
|
+
"lint": "eslint --ext .ts,.tsx .",
|
|
14
|
+
"build:cli": "tsc -p tsconfig.cli.json",
|
|
15
|
+
"build:mcp": "tsc -p tsconfig.mcp.json",
|
|
16
|
+
"cli": "node dist/cli/cli/index.js",
|
|
17
|
+
"mcp": "node dist/mcp/mcp/index.js",
|
|
18
|
+
"prepublishOnly": "npm run build:cli && npm run build:mcp"
|
|
19
|
+
},
|
|
20
|
+
"bin": {
|
|
21
|
+
"wmux": "dist/cli/cli/index.js",
|
|
22
|
+
"wmux-mcp": "dist/mcp/mcp/index.js"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist/cli/",
|
|
26
|
+
"dist/mcp/",
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE"
|
|
29
|
+
],
|
|
30
|
+
"keywords": [
|
|
31
|
+
"terminal",
|
|
32
|
+
"multiplexer",
|
|
33
|
+
"mcp",
|
|
34
|
+
"ai-agent",
|
|
35
|
+
"cli",
|
|
36
|
+
"windows",
|
|
37
|
+
"claude-code",
|
|
38
|
+
"electron"
|
|
39
|
+
],
|
|
40
|
+
"author": {
|
|
41
|
+
"name": "openwong2kim",
|
|
42
|
+
"email": "100856670+openwong2kim@users.noreply.github.com"
|
|
43
|
+
},
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/openwong2kim/wmux.git"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/openwong2kim/wmux#readme",
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/openwong2kim/wmux/issues"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=18.0.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@electron-forge/cli": "^7.11.1",
|
|
58
|
+
"@electron-forge/maker-deb": "^7.11.1",
|
|
59
|
+
"@electron-forge/maker-rpm": "^7.11.1",
|
|
60
|
+
"@electron-forge/maker-squirrel": "^7.11.1",
|
|
61
|
+
"@electron-forge/maker-zip": "^7.11.1",
|
|
62
|
+
"@electron-forge/plugin-auto-unpack-natives": "^7.11.1",
|
|
63
|
+
"@electron-forge/plugin-fuses": "^7.11.1",
|
|
64
|
+
"@electron-forge/plugin-vite": "^7.11.1",
|
|
65
|
+
"@electron/fuses": "^1.8.0",
|
|
66
|
+
"@types/electron-squirrel-startup": "^1.0.2",
|
|
67
|
+
"@types/react": "^19.2.14",
|
|
68
|
+
"@types/react-dom": "^19.2.3",
|
|
69
|
+
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
|
70
|
+
"@typescript-eslint/parser": "^5.62.0",
|
|
71
|
+
"@vitejs/plugin-react": "^4.7.0",
|
|
72
|
+
"autoprefixer": "^10.4.27",
|
|
73
|
+
"electron": "41.0.3",
|
|
74
|
+
"eslint": "^8.57.1",
|
|
75
|
+
"eslint-plugin-import": "^2.32.0",
|
|
76
|
+
"postcss": "^8.5.8",
|
|
77
|
+
"tailwindcss": "^3.4.19",
|
|
78
|
+
"typescript": "^5.9.3",
|
|
79
|
+
"vite": "^5.4.21"
|
|
80
|
+
},
|
|
81
|
+
"dependencies": {
|
|
82
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
83
|
+
"@xterm/addon-fit": "^0.11.0",
|
|
84
|
+
"@xterm/addon-search": "^0.16.0",
|
|
85
|
+
"@xterm/addon-webgl": "^0.19.0",
|
|
86
|
+
"@xterm/xterm": "^6.0.0",
|
|
87
|
+
"electron-squirrel-startup": "^1.0.1",
|
|
88
|
+
"immer": "^11.1.4",
|
|
89
|
+
"node-pty": "^1.1.0",
|
|
90
|
+
"react": "^19.2.4",
|
|
91
|
+
"react-dom": "^19.2.4",
|
|
92
|
+
"react-resizable-panels": "^4.7.3",
|
|
93
|
+
"zod": "^4.3.6",
|
|
94
|
+
"zustand": "^5.0.12"
|
|
95
|
+
}
|
|
96
|
+
}
|
package/assets/icon.ico
DELETED
|
Binary file
|
package/assets/icon.svg
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
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>
|
package/forge.config.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import type { ForgeConfig } from '@electron-forge/shared-types';
|
|
2
|
-
import { MakerSquirrel } from '@electron-forge/maker-squirrel';
|
|
3
|
-
import { MakerZIP } from '@electron-forge/maker-zip';
|
|
4
|
-
import { VitePlugin } from '@electron-forge/plugin-vite';
|
|
5
|
-
import { AutoUnpackNativesPlugin } from '@electron-forge/plugin-auto-unpack-natives';
|
|
6
|
-
import { FusesPlugin } from '@electron-forge/plugin-fuses';
|
|
7
|
-
import { FuseV1Options, FuseVersion } from '@electron/fuses';
|
|
8
|
-
|
|
9
|
-
const config: ForgeConfig = {
|
|
10
|
-
packagerConfig: {
|
|
11
|
-
asar: true,
|
|
12
|
-
icon: './assets/icon',
|
|
13
|
-
extraResource: ['./dist/mcp'],
|
|
14
|
-
},
|
|
15
|
-
rebuildConfig: { disablePreGypRecuild: true },
|
|
16
|
-
hooks: {
|
|
17
|
-
// Skip native rebuild — pre-built binaries already in node_modules
|
|
18
|
-
readPackageJson: async (_config, packageJson) => {
|
|
19
|
-
packageJson.dependencies = packageJson.dependencies || {};
|
|
20
|
-
return packageJson;
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
makers: [
|
|
24
|
-
new MakerSquirrel({}),
|
|
25
|
-
new MakerZIP({}, ['darwin']),
|
|
26
|
-
],
|
|
27
|
-
plugins: [
|
|
28
|
-
new AutoUnpackNativesPlugin({}),
|
|
29
|
-
new VitePlugin({
|
|
30
|
-
build: [
|
|
31
|
-
{
|
|
32
|
-
entry: 'src/main/index.ts',
|
|
33
|
-
config: 'vite.main.config.ts',
|
|
34
|
-
target: 'main',
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
entry: 'src/preload/preload.ts',
|
|
38
|
-
config: 'vite.preload.config.ts',
|
|
39
|
-
target: 'preload',
|
|
40
|
-
},
|
|
41
|
-
],
|
|
42
|
-
renderer: [
|
|
43
|
-
{
|
|
44
|
-
name: 'main_window',
|
|
45
|
-
config: 'vite.renderer.config.ts',
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
|
-
}),
|
|
49
|
-
new FusesPlugin({
|
|
50
|
-
version: FuseVersion.V1,
|
|
51
|
-
[FuseV1Options.RunAsNode]: false,
|
|
52
|
-
[FuseV1Options.EnableCookieEncryption]: true,
|
|
53
|
-
[FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
|
|
54
|
-
[FuseV1Options.EnableNodeCliInspectArguments]: false,
|
|
55
|
-
[FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
|
|
56
|
-
[FuseV1Options.OnlyLoadAppFromAsar]: true,
|
|
57
|
-
}),
|
|
58
|
-
],
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export default config;
|
package/index.html
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>WinMux</title>
|
|
7
|
-
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<div id="root"></div>
|
|
10
|
-
<script type="module" src="/src/renderer/index.tsx"></script>
|
|
11
|
-
</body>
|
|
12
|
-
</html>
|
package/postcss.config.js
DELETED
package/src/cli/client.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import * as net from 'net';
|
|
3
|
-
import * as crypto from 'crypto';
|
|
4
|
-
import type { RpcRequest, RpcResponse, RpcMethod } from '../shared/rpc';
|
|
5
|
-
|
|
6
|
-
const PIPE_NAME = process.env.WMUX_SOCKET_PATH || '\\\\.\\pipe\\wmux';
|
|
7
|
-
const TIMEOUT_MS = 5000;
|
|
8
|
-
|
|
9
|
-
export function sendRequest(
|
|
10
|
-
method: RpcMethod,
|
|
11
|
-
params: Record<string, unknown> = {}
|
|
12
|
-
): Promise<RpcResponse> {
|
|
13
|
-
return new Promise((resolve, reject) => {
|
|
14
|
-
const id = crypto.randomUUID();
|
|
15
|
-
const token = process.env.WMUX_AUTH_TOKEN;
|
|
16
|
-
const request: RpcRequest = { id, method, params, token };
|
|
17
|
-
|
|
18
|
-
const socket = net.connect(PIPE_NAME);
|
|
19
|
-
let buffer = '';
|
|
20
|
-
let settled = false;
|
|
21
|
-
|
|
22
|
-
const timer = setTimeout(() => {
|
|
23
|
-
if (!settled) {
|
|
24
|
-
settled = true;
|
|
25
|
-
socket.destroy();
|
|
26
|
-
reject(new Error('Request timed out after 5 seconds.'));
|
|
27
|
-
}
|
|
28
|
-
}, TIMEOUT_MS);
|
|
29
|
-
|
|
30
|
-
socket.on('connect', () => {
|
|
31
|
-
socket.write(JSON.stringify(request) + '\n');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
socket.on('data', (chunk: Buffer) => {
|
|
35
|
-
buffer += chunk.toString('utf8');
|
|
36
|
-
const lines = buffer.split('\n');
|
|
37
|
-
buffer = lines.pop() ?? '';
|
|
38
|
-
|
|
39
|
-
for (const line of lines) {
|
|
40
|
-
const trimmed = line.trim();
|
|
41
|
-
if (!trimmed) continue;
|
|
42
|
-
try {
|
|
43
|
-
const response = JSON.parse(trimmed) as RpcResponse;
|
|
44
|
-
if (response.id === id && !settled) {
|
|
45
|
-
settled = true;
|
|
46
|
-
clearTimeout(timer);
|
|
47
|
-
socket.destroy();
|
|
48
|
-
resolve(response);
|
|
49
|
-
}
|
|
50
|
-
} catch {
|
|
51
|
-
// ignore malformed lines
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
socket.on('error', (err: NodeJS.ErrnoException) => {
|
|
57
|
-
if (!settled) {
|
|
58
|
-
settled = true;
|
|
59
|
-
clearTimeout(timer);
|
|
60
|
-
if (err.code === 'ENOENT' || err.code === 'ECONNREFUSED') {
|
|
61
|
-
reject(new Error('wmux is not running. Start the app first.'));
|
|
62
|
-
} else {
|
|
63
|
-
reject(new Error(`Connection error: ${err.message}`));
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
socket.on('close', () => {
|
|
69
|
-
if (!settled) {
|
|
70
|
-
settled = true;
|
|
71
|
-
clearTimeout(timer);
|
|
72
|
-
reject(new Error('Connection closed before response was received.'));
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { sendRequest } from '../client';
|
|
2
|
-
import { printResult, printError } from '../utils';
|
|
3
|
-
import type { RpcResponse } from '../../shared/rpc';
|
|
4
|
-
|
|
5
|
-
const BROWSER_HELP = `
|
|
6
|
-
wmux browser — Scriptable Browser API
|
|
7
|
-
|
|
8
|
-
USAGE
|
|
9
|
-
wmux browser <subcommand> [args]
|
|
10
|
-
|
|
11
|
-
SUBCOMMANDS
|
|
12
|
-
snapshot Return the full page HTML (document.documentElement.outerHTML)
|
|
13
|
-
click <selector> Click the first element matching the CSS selector
|
|
14
|
-
fill <selector> <text> Set the value of an input matching the CSS selector
|
|
15
|
-
eval <code> Execute arbitrary JavaScript in the page context
|
|
16
|
-
navigate <url> Navigate the active browser surface to a URL
|
|
17
|
-
|
|
18
|
-
EXAMPLES
|
|
19
|
-
wmux browser snapshot
|
|
20
|
-
wmux browser click "#submit-btn"
|
|
21
|
-
wmux browser fill "input[name=email]" "user@example.com"
|
|
22
|
-
wmux browser eval "document.title"
|
|
23
|
-
wmux browser navigate "https://example.com"
|
|
24
|
-
`.trimStart();
|
|
25
|
-
|
|
26
|
-
export async function handleBrowser(
|
|
27
|
-
args: string[],
|
|
28
|
-
jsonMode: boolean,
|
|
29
|
-
): Promise<void> {
|
|
30
|
-
const sub = args[0];
|
|
31
|
-
const rest = args.slice(1);
|
|
32
|
-
|
|
33
|
-
if (!sub || sub === '--help' || sub === '-h') {
|
|
34
|
-
process.stdout.write(BROWSER_HELP);
|
|
35
|
-
process.exit(0);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
let response: RpcResponse;
|
|
39
|
-
|
|
40
|
-
switch (sub) {
|
|
41
|
-
// ── browser snapshot ─────────────────────────────────────────────────────
|
|
42
|
-
case 'snapshot': {
|
|
43
|
-
response = await sendRequest('browser.snapshot', {});
|
|
44
|
-
if (jsonMode) {
|
|
45
|
-
printResult(response);
|
|
46
|
-
} else {
|
|
47
|
-
if (!response.ok) { printError(response); return; }
|
|
48
|
-
const r = response.result as { html?: string } | null;
|
|
49
|
-
process.stdout.write(r?.html ?? '');
|
|
50
|
-
}
|
|
51
|
-
break;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// ── browser click <selector> ─────────────────────────────────────────────
|
|
55
|
-
case 'click': {
|
|
56
|
-
const selector = rest[0];
|
|
57
|
-
if (!selector) {
|
|
58
|
-
console.error('Error: browser click requires <selector>');
|
|
59
|
-
process.exit(1);
|
|
60
|
-
}
|
|
61
|
-
response = await sendRequest('browser.click', { selector });
|
|
62
|
-
if (jsonMode) {
|
|
63
|
-
printResult(response);
|
|
64
|
-
} else {
|
|
65
|
-
if (!response.ok) { printError(response); return; }
|
|
66
|
-
console.log(`Clicked: ${selector}`);
|
|
67
|
-
}
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
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 sendRequest('browser.fill', { selector, text });
|
|
80
|
-
if (jsonMode) {
|
|
81
|
-
printResult(response);
|
|
82
|
-
} else {
|
|
83
|
-
if (!response.ok) { printError(response); return; }
|
|
84
|
-
console.log(`Filled "${selector}" with "${text}"`);
|
|
85
|
-
}
|
|
86
|
-
break;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// ── browser eval <code> ──────────────────────────────────────────────────
|
|
90
|
-
case 'eval': {
|
|
91
|
-
const code = rest.join(' ');
|
|
92
|
-
if (!code) {
|
|
93
|
-
console.error('Error: browser eval requires <code>');
|
|
94
|
-
process.exit(1);
|
|
95
|
-
}
|
|
96
|
-
response = await sendRequest('browser.eval', { code });
|
|
97
|
-
if (jsonMode) {
|
|
98
|
-
printResult(response);
|
|
99
|
-
} else {
|
|
100
|
-
if (!response.ok) { printError(response); return; }
|
|
101
|
-
const r = response.result as { result?: unknown } | null;
|
|
102
|
-
console.log(JSON.stringify(r?.result, null, 2));
|
|
103
|
-
}
|
|
104
|
-
break;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// ── browser navigate <url> ───────────────────────────────────────────────
|
|
108
|
-
case 'navigate': {
|
|
109
|
-
const url = rest[0];
|
|
110
|
-
if (!url) {
|
|
111
|
-
console.error('Error: browser navigate requires <url>');
|
|
112
|
-
process.exit(1);
|
|
113
|
-
}
|
|
114
|
-
response = await sendRequest('browser.navigate', { url });
|
|
115
|
-
if (jsonMode) {
|
|
116
|
-
printResult(response);
|
|
117
|
-
} else {
|
|
118
|
-
if (!response.ok) { printError(response); return; }
|
|
119
|
-
console.log(`Navigated to: ${url}`);
|
|
120
|
-
}
|
|
121
|
-
break;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
default:
|
|
125
|
-
console.error(`Unknown browser subcommand: "${sub}". Run 'wmux browser --help' for usage.`);
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { sendRequest } from '../client';
|
|
2
|
-
import { printResult, printError } from '../utils';
|
|
3
|
-
import type { RpcResponse } from '../../shared/rpc';
|
|
4
|
-
|
|
5
|
-
export async function handleInput(
|
|
6
|
-
cmd: string,
|
|
7
|
-
args: string[],
|
|
8
|
-
jsonMode: boolean
|
|
9
|
-
): Promise<void> {
|
|
10
|
-
let response: RpcResponse;
|
|
11
|
-
|
|
12
|
-
switch (cmd) {
|
|
13
|
-
case 'send': {
|
|
14
|
-
const text = args.join(' ');
|
|
15
|
-
if (!text) {
|
|
16
|
-
console.error('Error: send requires <text>');
|
|
17
|
-
process.exit(1);
|
|
18
|
-
}
|
|
19
|
-
response = await sendRequest('input.send', { text });
|
|
20
|
-
if (jsonMode) {
|
|
21
|
-
printResult(response);
|
|
22
|
-
} else {
|
|
23
|
-
if (!response.ok) { printError(response); return; }
|
|
24
|
-
console.log('Text sent.');
|
|
25
|
-
}
|
|
26
|
-
break;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
case 'send-key': {
|
|
30
|
-
const key = args[0];
|
|
31
|
-
if (!key) {
|
|
32
|
-
console.error('Error: send-key requires <keystroke>');
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
response = await sendRequest('input.sendKey', { key });
|
|
36
|
-
if (jsonMode) {
|
|
37
|
-
printResult(response);
|
|
38
|
-
} else {
|
|
39
|
-
if (!response.ok) { printError(response); return; }
|
|
40
|
-
console.log(`Key sent: ${key}`);
|
|
41
|
-
}
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
case 'read-screen': {
|
|
46
|
-
response = await sendRequest('input.readScreen', {});
|
|
47
|
-
if (jsonMode) {
|
|
48
|
-
printResult(response);
|
|
49
|
-
} else {
|
|
50
|
-
if (!response.ok) { printError(response); return; }
|
|
51
|
-
// server returns { text: string } object or plain string
|
|
52
|
-
const result = response.result as { text?: string } | string;
|
|
53
|
-
let screen: string;
|
|
54
|
-
if (typeof result === 'object' && result !== null && 'text' in result) {
|
|
55
|
-
screen = result.text ?? '';
|
|
56
|
-
} else if (typeof result === 'string') {
|
|
57
|
-
screen = result;
|
|
58
|
-
} else {
|
|
59
|
-
console.log(JSON.stringify(result, null, 2));
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
process.stdout.write(screen);
|
|
63
|
-
if (!screen.endsWith('\n')) process.stdout.write('\n');
|
|
64
|
-
}
|
|
65
|
-
break;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
default:
|
|
69
|
-
console.error(`Unknown input command: ${cmd}`);
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { sendRequest } from '../client';
|
|
2
|
-
import { printResult, printError, parseFlag } from '../utils';
|
|
3
|
-
import type { RpcResponse } from '../../shared/rpc';
|
|
4
|
-
|
|
5
|
-
export async function handleNotify(
|
|
6
|
-
args: string[],
|
|
7
|
-
jsonMode: boolean
|
|
8
|
-
): Promise<void> {
|
|
9
|
-
const title = parseFlag(args, '--title');
|
|
10
|
-
const body = parseFlag(args, '--body');
|
|
11
|
-
|
|
12
|
-
if (!title) {
|
|
13
|
-
console.error('Error: notify requires --title <text>');
|
|
14
|
-
process.exit(1);
|
|
15
|
-
}
|
|
16
|
-
if (!body) {
|
|
17
|
-
console.error('Error: notify requires --body <text>');
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const response: RpcResponse = await sendRequest('notify', { title, body });
|
|
22
|
-
|
|
23
|
-
if (jsonMode) {
|
|
24
|
-
printResult(response);
|
|
25
|
-
} else {
|
|
26
|
-
if (!response.ok) { printError(response); return; }
|
|
27
|
-
console.log(`Notification sent: "${title}"`);
|
|
28
|
-
}
|
|
29
|
-
}
|