@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
@@ -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
+ }