@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
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleSurface = handleSurface;
|
|
4
|
+
const client_1 = require("../client");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function formatSurfaceList(result) {
|
|
7
|
+
const list = result;
|
|
8
|
+
if (!Array.isArray(list) || list.length === 0) {
|
|
9
|
+
console.log('No surfaces found.');
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const maxId = Math.max(...list.map((s) => s.id.length));
|
|
13
|
+
const maxTitle = Math.max(...list.map((s) => (s.title ?? '').length), 5);
|
|
14
|
+
console.log('ID'.padEnd(maxId + 2) +
|
|
15
|
+
'TITLE'.padEnd(maxTitle + 2) +
|
|
16
|
+
'SHELL');
|
|
17
|
+
console.log('-'.repeat(maxId + maxTitle + 20));
|
|
18
|
+
for (const s of list) {
|
|
19
|
+
console.log(s.id.padEnd(maxId + 2) +
|
|
20
|
+
(s.title ?? '').padEnd(maxTitle + 2) +
|
|
21
|
+
(s.shell ?? ''));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function handleSurface(cmd, args, jsonMode) {
|
|
25
|
+
let response;
|
|
26
|
+
switch (cmd) {
|
|
27
|
+
case 'list-surfaces': {
|
|
28
|
+
response = await (0, client_1.sendRequest)('surface.list', {});
|
|
29
|
+
if (jsonMode) {
|
|
30
|
+
(0, utils_1.printResult)(response);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
(0, utils_1.printError)(response);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
formatSurfaceList(response.result);
|
|
38
|
+
}
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
case 'new-surface': {
|
|
42
|
+
response = await (0, client_1.sendRequest)('surface.new', {});
|
|
43
|
+
if (jsonMode) {
|
|
44
|
+
(0, utils_1.printResult)(response);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
(0, utils_1.printError)(response);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const s = response.result;
|
|
52
|
+
console.log(`Created surface: ${s?.id ?? '(unknown)'}`);
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
case 'focus-surface': {
|
|
57
|
+
const id = args[0];
|
|
58
|
+
if (!id) {
|
|
59
|
+
console.error('Error: focus-surface requires <id>');
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
response = await (0, client_1.sendRequest)('surface.focus', { id });
|
|
63
|
+
if (jsonMode) {
|
|
64
|
+
(0, utils_1.printResult)(response);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
(0, utils_1.printError)(response);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
console.log(`Focused surface: ${id}`);
|
|
72
|
+
}
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
case 'close-surface': {
|
|
76
|
+
const id = args[0];
|
|
77
|
+
if (!id) {
|
|
78
|
+
console.error('Error: close-surface requires <id>');
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
response = await (0, client_1.sendRequest)('surface.close', { id });
|
|
82
|
+
if (jsonMode) {
|
|
83
|
+
(0, utils_1.printResult)(response);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
(0, utils_1.printError)(response);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
console.log(`Closed surface: ${id}`);
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
default:
|
|
95
|
+
console.error(`Unknown surface command: ${cmd}`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleSystem = handleSystem;
|
|
4
|
+
const client_1 = require("../client");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
async function handleSystem(cmd, args, jsonMode) {
|
|
7
|
+
let response;
|
|
8
|
+
switch (cmd) {
|
|
9
|
+
case 'identify': {
|
|
10
|
+
response = await (0, client_1.sendRequest)('system.identify', {});
|
|
11
|
+
if (jsonMode) {
|
|
12
|
+
(0, utils_1.printResult)(response);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
(0, utils_1.printError)(response);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const info = response.result;
|
|
20
|
+
console.log(`app: ${info?.app ?? 'wmux'}`);
|
|
21
|
+
console.log(`version: ${info?.version ?? '1.0.0'}`);
|
|
22
|
+
console.log(`platform: ${info?.platform ?? process.platform}`);
|
|
23
|
+
}
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
case 'capabilities': {
|
|
27
|
+
response = await (0, client_1.sendRequest)('system.capabilities', {});
|
|
28
|
+
if (jsonMode) {
|
|
29
|
+
(0, utils_1.printResult)(response);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
(0, utils_1.printError)(response);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
// server may return { methods: string[] } or string[] directly
|
|
37
|
+
const result = response.result;
|
|
38
|
+
const methods = Array.isArray(result) ? result : (result?.methods || []);
|
|
39
|
+
if (methods.length > 0) {
|
|
40
|
+
console.log('Supported RPC methods:');
|
|
41
|
+
for (const m of methods) {
|
|
42
|
+
console.log(` ${m}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
console.log(JSON.stringify(response.result, null, 2));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
case 'set-status': {
|
|
52
|
+
const text = args[0];
|
|
53
|
+
if (text === undefined) {
|
|
54
|
+
console.error('Error: set-status requires <text>');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
response = await (0, client_1.sendRequest)('meta.setStatus', { text });
|
|
58
|
+
if (jsonMode) {
|
|
59
|
+
(0, utils_1.printResult)(response);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
(0, utils_1.printError)(response);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
console.log(`Status set: "${text}"`);
|
|
67
|
+
}
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
case 'set-progress': {
|
|
71
|
+
const raw = args[0];
|
|
72
|
+
if (raw === undefined) {
|
|
73
|
+
console.error('Error: set-progress requires <0-100>');
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
const value = Number(raw);
|
|
77
|
+
if (isNaN(value) || value < 0 || value > 100) {
|
|
78
|
+
console.error('Error: progress value must be a number between 0 and 100');
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
response = await (0, client_1.sendRequest)('meta.setProgress', { value });
|
|
82
|
+
if (jsonMode) {
|
|
83
|
+
(0, utils_1.printResult)(response);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
(0, utils_1.printError)(response);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
console.log(`Progress set: ${value}%`);
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
default:
|
|
95
|
+
console.error(`Unknown system command: ${cmd}`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleWorkspace = handleWorkspace;
|
|
4
|
+
const client_1 = require("../client");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function formatWorkspaceList(result) {
|
|
7
|
+
const list = result;
|
|
8
|
+
if (!Array.isArray(list) || list.length === 0) {
|
|
9
|
+
console.log('No workspaces found.');
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const maxId = Math.max(...list.map((w) => w.id.length));
|
|
13
|
+
console.log('ID'.padEnd(maxId + 2) + 'NAME' + ' ' + 'STATUS');
|
|
14
|
+
console.log('-'.repeat(maxId + 30));
|
|
15
|
+
for (const ws of list) {
|
|
16
|
+
const active = ws.isActive ? ' (active)' : '';
|
|
17
|
+
console.log(ws.id.padEnd(maxId + 2) + ws.name + active);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function formatWorkspaceCurrent(result) {
|
|
21
|
+
const ws = result;
|
|
22
|
+
if (!ws) {
|
|
23
|
+
console.log('No active workspace.');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
console.log(`Current workspace: ${ws.name} (${ws.id})`);
|
|
27
|
+
}
|
|
28
|
+
async function handleWorkspace(cmd, args, jsonMode) {
|
|
29
|
+
let response;
|
|
30
|
+
switch (cmd) {
|
|
31
|
+
case 'list-workspaces': {
|
|
32
|
+
response = await (0, client_1.sendRequest)('workspace.list', {});
|
|
33
|
+
if (jsonMode) {
|
|
34
|
+
(0, utils_1.printResult)(response);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
(0, utils_1.printError)(response);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
formatWorkspaceList(response.result);
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
case 'new-workspace': {
|
|
46
|
+
const name = (0, utils_1.parseFlag)(args, '--name') ?? `workspace-${Date.now()}`;
|
|
47
|
+
response = await (0, client_1.sendRequest)('workspace.new', { name });
|
|
48
|
+
if (jsonMode) {
|
|
49
|
+
(0, utils_1.printResult)(response);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
(0, utils_1.printError)(response);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const ws = response.result;
|
|
57
|
+
console.log(`Created workspace: ${ws?.name ?? name} (${ws?.id ?? ''})`);
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
case 'focus-workspace': {
|
|
62
|
+
const id = args[0];
|
|
63
|
+
if (!id) {
|
|
64
|
+
console.error('Error: focus-workspace requires <id>');
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
response = await (0, client_1.sendRequest)('workspace.focus', { id });
|
|
68
|
+
if (jsonMode) {
|
|
69
|
+
(0, utils_1.printResult)(response);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
(0, utils_1.printError)(response);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
console.log(`Focused workspace: ${id}`);
|
|
77
|
+
}
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
case 'close-workspace': {
|
|
81
|
+
const id = args[0];
|
|
82
|
+
if (!id) {
|
|
83
|
+
console.error('Error: close-workspace requires <id>');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
response = await (0, client_1.sendRequest)('workspace.close', { id });
|
|
87
|
+
if (jsonMode) {
|
|
88
|
+
(0, utils_1.printResult)(response);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
(0, utils_1.printError)(response);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
console.log(`Closed workspace: ${id}`);
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case 'current-workspace': {
|
|
100
|
+
response = await (0, client_1.sendRequest)('workspace.current', {});
|
|
101
|
+
if (jsonMode) {
|
|
102
|
+
(0, utils_1.printResult)(response);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
if (!response.ok) {
|
|
106
|
+
(0, utils_1.printError)(response);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
formatWorkspaceCurrent(response.result);
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
default:
|
|
114
|
+
console.error(`Unknown workspace command: ${cmd}`);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
process.on('SIGINT', () => {
|
|
5
|
+
process.exit(130);
|
|
6
|
+
});
|
|
7
|
+
const utils_1 = require("./utils");
|
|
8
|
+
const workspace_1 = require("./commands/workspace");
|
|
9
|
+
const surface_1 = require("./commands/surface");
|
|
10
|
+
const pane_1 = require("./commands/pane");
|
|
11
|
+
const input_1 = require("./commands/input");
|
|
12
|
+
const notify_1 = require("./commands/notify");
|
|
13
|
+
const system_1 = require("./commands/system");
|
|
14
|
+
const browser_1 = require("./commands/browser");
|
|
15
|
+
const HELP_TEXT = `
|
|
16
|
+
wmux CLI
|
|
17
|
+
|
|
18
|
+
USAGE
|
|
19
|
+
wmux <command> [options]
|
|
20
|
+
|
|
21
|
+
WORKSPACE COMMANDS
|
|
22
|
+
list-workspaces List all workspaces
|
|
23
|
+
new-workspace [--name <name>] Create a new workspace
|
|
24
|
+
focus-workspace <id> Focus a workspace by ID
|
|
25
|
+
close-workspace <id> Close a workspace by ID
|
|
26
|
+
current-workspace Show the active workspace
|
|
27
|
+
|
|
28
|
+
SURFACE COMMANDS
|
|
29
|
+
list-surfaces List all surfaces in the active workspace
|
|
30
|
+
new-surface Open a new surface (terminal tab)
|
|
31
|
+
focus-surface <id> Focus a surface by ID
|
|
32
|
+
close-surface <id> Close a surface by ID
|
|
33
|
+
|
|
34
|
+
PANE COMMANDS
|
|
35
|
+
list-panes List all panes in the active workspace
|
|
36
|
+
focus-pane <id> Focus a pane by ID
|
|
37
|
+
split [--direction right|down] Split the active pane (default: right)
|
|
38
|
+
|
|
39
|
+
INPUT COMMANDS
|
|
40
|
+
send <text> Send text to the active terminal
|
|
41
|
+
send-key <keystroke> Send a key (e.g. Enter, ctrl-c, Tab)
|
|
42
|
+
read-screen Read the current terminal screen content
|
|
43
|
+
|
|
44
|
+
NOTIFICATION COMMANDS
|
|
45
|
+
notify --title <title> --body <body> Show a notification in wmux
|
|
46
|
+
|
|
47
|
+
SYSTEM COMMANDS
|
|
48
|
+
set-status <text> Set a status message on the active workspace
|
|
49
|
+
set-progress <0-100> Set a progress value on the active workspace
|
|
50
|
+
identify Show wmux app info
|
|
51
|
+
capabilities List all supported RPC methods
|
|
52
|
+
|
|
53
|
+
BROWSER COMMANDS
|
|
54
|
+
browser snapshot Return the full page HTML of the active browser surface
|
|
55
|
+
browser click <selector> Click an element by CSS selector
|
|
56
|
+
browser fill <selector> <text> Fill an input field by CSS selector
|
|
57
|
+
browser eval <code> Execute JavaScript in the browser context
|
|
58
|
+
browser navigate <url> Navigate the browser surface to a URL
|
|
59
|
+
|
|
60
|
+
GLOBAL FLAGS
|
|
61
|
+
--json Output raw JSON (useful for scripting)
|
|
62
|
+
--help Show this help text
|
|
63
|
+
|
|
64
|
+
EXAMPLES
|
|
65
|
+
wmux list-workspaces
|
|
66
|
+
wmux new-workspace --name dev
|
|
67
|
+
wmux send "echo hello"
|
|
68
|
+
wmux notify --title "Done" --body "Build finished"
|
|
69
|
+
wmux identify --json
|
|
70
|
+
wmux browser snapshot
|
|
71
|
+
wmux browser navigate "https://example.com"
|
|
72
|
+
wmux browser click "#login-btn"
|
|
73
|
+
`.trimStart();
|
|
74
|
+
const WORKSPACE_CMDS = new Set([
|
|
75
|
+
'list-workspaces',
|
|
76
|
+
'new-workspace',
|
|
77
|
+
'focus-workspace',
|
|
78
|
+
'close-workspace',
|
|
79
|
+
'current-workspace',
|
|
80
|
+
]);
|
|
81
|
+
const SURFACE_CMDS = new Set([
|
|
82
|
+
'list-surfaces',
|
|
83
|
+
'new-surface',
|
|
84
|
+
'focus-surface',
|
|
85
|
+
'close-surface',
|
|
86
|
+
]);
|
|
87
|
+
const PANE_CMDS = new Set(['list-panes', 'focus-pane', 'split']);
|
|
88
|
+
const INPUT_CMDS = new Set(['send', 'send-key', 'read-screen']);
|
|
89
|
+
const SYSTEM_CMDS = new Set([
|
|
90
|
+
'identify',
|
|
91
|
+
'capabilities',
|
|
92
|
+
'set-status',
|
|
93
|
+
'set-progress',
|
|
94
|
+
]);
|
|
95
|
+
async function main() {
|
|
96
|
+
// process.argv = ['node', 'index.js', ...userArgs]
|
|
97
|
+
const argv = process.argv.slice(2);
|
|
98
|
+
if (argv.length === 0 || (0, utils_1.hasFlag)(argv, '--help') || (0, utils_1.hasFlag)(argv, '-h')) {
|
|
99
|
+
process.stdout.write(HELP_TEXT);
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
const jsonMode = (0, utils_1.hasFlag)(argv, '--json');
|
|
103
|
+
// Strip global flags so commands see clean args
|
|
104
|
+
const args = argv.filter((a) => a !== '--json' && a !== '--help' && a !== '-h');
|
|
105
|
+
const cmd = args[0];
|
|
106
|
+
const rest = args.slice(1);
|
|
107
|
+
try {
|
|
108
|
+
if (WORKSPACE_CMDS.has(cmd)) {
|
|
109
|
+
await (0, workspace_1.handleWorkspace)(cmd, rest, jsonMode);
|
|
110
|
+
}
|
|
111
|
+
else if (SURFACE_CMDS.has(cmd)) {
|
|
112
|
+
await (0, surface_1.handleSurface)(cmd, rest, jsonMode);
|
|
113
|
+
}
|
|
114
|
+
else if (PANE_CMDS.has(cmd)) {
|
|
115
|
+
await (0, pane_1.handlePane)(cmd, rest, jsonMode);
|
|
116
|
+
}
|
|
117
|
+
else if (INPUT_CMDS.has(cmd)) {
|
|
118
|
+
await (0, input_1.handleInput)(cmd, rest, jsonMode);
|
|
119
|
+
}
|
|
120
|
+
else if (cmd === 'notify') {
|
|
121
|
+
await (0, notify_1.handleNotify)(rest, jsonMode);
|
|
122
|
+
}
|
|
123
|
+
else if (SYSTEM_CMDS.has(cmd)) {
|
|
124
|
+
await (0, system_1.handleSystem)(cmd, rest, jsonMode);
|
|
125
|
+
}
|
|
126
|
+
else if (cmd === 'browser') {
|
|
127
|
+
await (0, browser_1.handleBrowser)(rest, jsonMode);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
console.error(`Unknown command: "${cmd}". Run 'wmux --help' for usage.`);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
136
|
+
console.error(message);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
main();
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printResult = printResult;
|
|
4
|
+
exports.printError = printError;
|
|
5
|
+
exports.parseFlag = parseFlag;
|
|
6
|
+
exports.hasFlag = hasFlag;
|
|
7
|
+
/**
|
|
8
|
+
* Print the result field of a successful RPC response as JSON.
|
|
9
|
+
* If the response contains an error, the error is printed to stderr and
|
|
10
|
+
* the process exits with code 1.
|
|
11
|
+
*/
|
|
12
|
+
function printResult(response) {
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
printError(response);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
console.log(JSON.stringify(response.result, null, 2));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Print the error field of a failed RPC response to stderr and exit with 1.
|
|
21
|
+
*/
|
|
22
|
+
function printError(response) {
|
|
23
|
+
const msg = !response.ok ? response.error : 'Unknown error from wmux';
|
|
24
|
+
console.error(`Error: ${msg}`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parse a named flag value from an argv array.
|
|
29
|
+
* e.g. parseFlag(['--name', 'dev'], '--name') => 'dev'
|
|
30
|
+
* Returns undefined when the flag is not present.
|
|
31
|
+
*/
|
|
32
|
+
function parseFlag(args, flag) {
|
|
33
|
+
const idx = args.indexOf(flag);
|
|
34
|
+
if (idx === -1)
|
|
35
|
+
return undefined;
|
|
36
|
+
const value = args[idx + 1];
|
|
37
|
+
if (value === undefined || value.startsWith('-'))
|
|
38
|
+
return undefined;
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Check whether a bare flag is present in argv.
|
|
43
|
+
* e.g. hasFlag(['--json', 'identify'], '--json') => true
|
|
44
|
+
*/
|
|
45
|
+
function hasFlag(args, flag) {
|
|
46
|
+
return args.includes(flag);
|
|
47
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ENV_KEYS = exports.PIPE_NAME = exports.IPC = void 0;
|
|
4
|
+
exports.getPipeName = getPipeName;
|
|
5
|
+
exports.getAuthTokenPath = getAuthTokenPath;
|
|
6
|
+
// IPC Channel names
|
|
7
|
+
exports.IPC = {
|
|
8
|
+
PTY_CREATE: 'pty:create',
|
|
9
|
+
PTY_WRITE: 'pty:write',
|
|
10
|
+
PTY_RESIZE: 'pty:resize',
|
|
11
|
+
PTY_DISPOSE: 'pty:dispose',
|
|
12
|
+
PTY_DATA: 'pty:data',
|
|
13
|
+
PTY_EXIT: 'pty:exit',
|
|
14
|
+
SHELL_LIST: 'shell:list',
|
|
15
|
+
SESSION_SAVE: 'session:save',
|
|
16
|
+
SESSION_LOAD: 'session:load',
|
|
17
|
+
NOTIFICATION: 'notification:new',
|
|
18
|
+
CWD_CHANGED: 'notification:cwd-changed',
|
|
19
|
+
METADATA_UPDATE: 'metadata:update',
|
|
20
|
+
METADATA_REQUEST: 'metadata:request',
|
|
21
|
+
// Phase 3: RPC bridge (Main ↔ Renderer)
|
|
22
|
+
RPC_COMMAND: 'rpc:command',
|
|
23
|
+
RPC_RESPONSE: 'rpc:response',
|
|
24
|
+
// Clipboard (main process bridge)
|
|
25
|
+
CLIPBOARD_WRITE: 'clipboard:write',
|
|
26
|
+
CLIPBOARD_READ: 'clipboard:read',
|
|
27
|
+
// Phase 4: Auto updater
|
|
28
|
+
UPDATE_CHECK: 'update:check',
|
|
29
|
+
UPDATE_AVAILABLE: 'update:available',
|
|
30
|
+
UPDATE_NOT_AVAILABLE: 'update:not-available',
|
|
31
|
+
UPDATE_ERROR: 'update:error',
|
|
32
|
+
UPDATE_DOWNLOAD: 'update:download',
|
|
33
|
+
UPDATE_INSTALL: 'update:install',
|
|
34
|
+
// Settings sync (renderer → main)
|
|
35
|
+
TOAST_ENABLED: 'settings:toast-enabled',
|
|
36
|
+
};
|
|
37
|
+
// Named Pipe path for wmux API
|
|
38
|
+
// Fixed name so MCP clients (e.g. Claude Code) can reconnect across wmux restarts
|
|
39
|
+
exports.PIPE_NAME = '\\\\.\\pipe\\wmux';
|
|
40
|
+
function getPipeName() {
|
|
41
|
+
return exports.PIPE_NAME;
|
|
42
|
+
}
|
|
43
|
+
// Environment variable names injected into PTY sessions
|
|
44
|
+
exports.ENV_KEYS = {
|
|
45
|
+
WORKSPACE_ID: 'WMUX_WORKSPACE_ID',
|
|
46
|
+
SURFACE_ID: 'WMUX_SURFACE_ID',
|
|
47
|
+
SOCKET_PATH: 'WMUX_SOCKET_PATH',
|
|
48
|
+
AUTH_TOKEN: 'WMUX_AUTH_TOKEN',
|
|
49
|
+
};
|
|
50
|
+
// Auth token file path — written by wmux main process, read by MCP server
|
|
51
|
+
function getAuthTokenPath() {
|
|
52
|
+
const home = process.env.USERPROFILE || process.env.HOME || '';
|
|
53
|
+
return `${home}/.wmux-auth-token`;
|
|
54
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// === JSON-RPC Protocol Types ===
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.ALL_RPC_METHODS = void 0;
|
|
5
|
+
// All available methods as array (for system.capabilities)
|
|
6
|
+
exports.ALL_RPC_METHODS = [
|
|
7
|
+
'workspace.list',
|
|
8
|
+
'workspace.new',
|
|
9
|
+
'workspace.focus',
|
|
10
|
+
'workspace.close',
|
|
11
|
+
'workspace.current',
|
|
12
|
+
'surface.list',
|
|
13
|
+
'surface.new',
|
|
14
|
+
'surface.focus',
|
|
15
|
+
'surface.close',
|
|
16
|
+
'pane.list',
|
|
17
|
+
'pane.focus',
|
|
18
|
+
'pane.split',
|
|
19
|
+
'input.send',
|
|
20
|
+
'input.sendKey',
|
|
21
|
+
'input.readScreen',
|
|
22
|
+
'notify',
|
|
23
|
+
'meta.setStatus',
|
|
24
|
+
'meta.setProgress',
|
|
25
|
+
'system.identify',
|
|
26
|
+
'system.capabilities',
|
|
27
|
+
'browser.open',
|
|
28
|
+
'browser.snapshot',
|
|
29
|
+
'browser.click',
|
|
30
|
+
'browser.fill',
|
|
31
|
+
'browser.eval',
|
|
32
|
+
'browser.navigate',
|
|
33
|
+
];
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateId = generateId;
|
|
4
|
+
exports.sanitizePtyText = sanitizePtyText;
|
|
5
|
+
exports.validateName = validateName;
|
|
6
|
+
exports.validateMessage = validateMessage;
|
|
7
|
+
exports.createSurface = createSurface;
|
|
8
|
+
exports.createLeafPane = createLeafPane;
|
|
9
|
+
exports.createWorkspace = createWorkspace;
|
|
10
|
+
// === Utility: generate unique IDs ===
|
|
11
|
+
function generateId(prefix) {
|
|
12
|
+
return `${prefix}-${crypto.randomUUID()}`;
|
|
13
|
+
}
|
|
14
|
+
// === Security: sanitize text before PTY write ===
|
|
15
|
+
/**
|
|
16
|
+
* Strips control characters (\r, \n, \x00-\x1f except \t) from text
|
|
17
|
+
* that will be written to a PTY, preventing embedded command injection.
|
|
18
|
+
*/
|
|
19
|
+
function sanitizePtyText(text) {
|
|
20
|
+
// Remove all control chars except tab (\x09)
|
|
21
|
+
// eslint-disable-next-line no-control-regex
|
|
22
|
+
return text.replace(/[\x00-\x08\x0a-\x1f\x7f\u0080-\u009f]/g, '');
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Validates and clamps a user-supplied name string.
|
|
26
|
+
* Returns the trimmed string if valid, or throws if invalid.
|
|
27
|
+
*/
|
|
28
|
+
function validateName(value, label, maxLength = 100) {
|
|
29
|
+
const trimmed = value.trim();
|
|
30
|
+
if (trimmed.length === 0) {
|
|
31
|
+
throw new Error(`${label} must not be empty`);
|
|
32
|
+
}
|
|
33
|
+
if (trimmed.length > maxLength) {
|
|
34
|
+
throw new Error(`${label} must be ${maxLength} characters or fewer`);
|
|
35
|
+
}
|
|
36
|
+
return trimmed;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Validates a message body string.
|
|
40
|
+
* Returns the trimmed string if valid, or throws if invalid.
|
|
41
|
+
*/
|
|
42
|
+
function validateMessage(value, maxLength = 10000) {
|
|
43
|
+
const trimmed = value.trim();
|
|
44
|
+
if (trimmed.length === 0) {
|
|
45
|
+
throw new Error('Message must not be empty');
|
|
46
|
+
}
|
|
47
|
+
if (trimmed.length > maxLength) {
|
|
48
|
+
throw new Error(`Message must be ${maxLength} characters or fewer`);
|
|
49
|
+
}
|
|
50
|
+
return trimmed;
|
|
51
|
+
}
|
|
52
|
+
// === Factory functions ===
|
|
53
|
+
function createSurface(ptyId, shell, cwd) {
|
|
54
|
+
return {
|
|
55
|
+
id: generateId('surface'),
|
|
56
|
+
ptyId,
|
|
57
|
+
title: shell,
|
|
58
|
+
shell,
|
|
59
|
+
cwd,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function createLeafPane(surface) {
|
|
63
|
+
const surfaces = surface ? [surface] : [];
|
|
64
|
+
return {
|
|
65
|
+
id: generateId('pane'),
|
|
66
|
+
type: 'leaf',
|
|
67
|
+
surfaces,
|
|
68
|
+
activeSurfaceId: surfaces[0]?.id || '',
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function createWorkspace(name) {
|
|
72
|
+
const rootPane = createLeafPane();
|
|
73
|
+
return {
|
|
74
|
+
id: generateId('ws'),
|
|
75
|
+
name,
|
|
76
|
+
rootPane,
|
|
77
|
+
activePaneId: rootPane.id,
|
|
78
|
+
};
|
|
79
|
+
}
|