starmux 0.12.0 → 0.13.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/README.md +2 -2
- package/dist/src/cli/mac-mcp-gateway.js +4 -3
- package/dist/src/cli/mac-mcp-gateway.js.map +1 -1
- package/dist/src/cli/starmux.js +1 -1
- package/dist/src/cli/starmux.js.map +1 -1
- package/dist/src/runtime-paths.js +3 -3
- package/dist/src/runtime-paths.js.map +1 -1
- package/dist/vendor/desktop-commander/LICENSE +21 -0
- package/dist/vendor/desktop-commander/dist/bootstrap.js +22 -0
- package/dist/vendor/desktop-commander/dist/command-manager.js +246 -0
- package/dist/vendor/desktop-commander/dist/config-field-definitions.js +37 -0
- package/dist/vendor/desktop-commander/dist/config-manager.js +634 -0
- package/dist/vendor/desktop-commander/dist/config-recovery.js +81 -0
- package/dist/vendor/desktop-commander/dist/config.js +19 -0
- package/dist/vendor/desktop-commander/dist/custom-stdio.js +358 -0
- package/dist/vendor/desktop-commander/dist/error-handlers.js +15 -0
- package/dist/vendor/desktop-commander/dist/handlers/edit-search-handlers.js +6 -0
- package/dist/vendor/desktop-commander/dist/handlers/filesystem-handlers.js +450 -0
- package/dist/vendor/desktop-commander/dist/handlers/history-handlers.js +67 -0
- package/dist/vendor/desktop-commander/dist/handlers/index.js +7 -0
- package/dist/vendor/desktop-commander/dist/handlers/process-handlers.js +15 -0
- package/dist/vendor/desktop-commander/dist/handlers/search-answers.js +172 -0
- package/dist/vendor/desktop-commander/dist/handlers/search-handlers.js +136 -0
- package/dist/vendor/desktop-commander/dist/handlers/terminal-handlers.js +35 -0
- package/dist/vendor/desktop-commander/dist/index.js +88 -0
- package/dist/vendor/desktop-commander/dist/search-manager.js +1262 -0
- package/dist/vendor/desktop-commander/dist/server.js +1414 -0
- package/dist/vendor/desktop-commander/dist/terminal-manager.js +757 -0
- package/dist/vendor/desktop-commander/dist/tools/config.js +211 -0
- package/dist/vendor/desktop-commander/dist/tools/edit.js +455 -0
- package/dist/vendor/desktop-commander/dist/tools/feedback.js +10 -0
- package/dist/vendor/desktop-commander/dist/tools/filesystem.js +1047 -0
- package/dist/vendor/desktop-commander/dist/tools/fuzzySearch.js +74 -0
- package/dist/vendor/desktop-commander/dist/tools/fuzzySearchCore.js +125 -0
- package/dist/vendor/desktop-commander/dist/tools/improved-process-tools.js +726 -0
- package/dist/vendor/desktop-commander/dist/tools/mime-types.js +28 -0
- package/dist/vendor/desktop-commander/dist/tools/pdf/extract-images.js +131 -0
- package/dist/vendor/desktop-commander/dist/tools/pdf/index.js +4 -0
- package/dist/vendor/desktop-commander/dist/tools/pdf/lib/pdf2md.js +78 -0
- package/dist/vendor/desktop-commander/dist/tools/pdf/manipulations.js +112 -0
- package/dist/vendor/desktop-commander/dist/tools/pdf/markdown.js +30 -0
- package/dist/vendor/desktop-commander/dist/tools/pdf/utils.js +45 -0
- package/dist/vendor/desktop-commander/dist/tools/process.js +90 -0
- package/dist/vendor/desktop-commander/dist/tools/prompts.js +259 -0
- package/dist/vendor/desktop-commander/dist/tools/schemas.js +242 -0
- package/dist/vendor/desktop-commander/dist/tools/usage.js +24 -0
- package/dist/vendor/desktop-commander/dist/types.js +1 -0
- package/dist/vendor/desktop-commander/dist/ui/config-editor/config-editor-runtime.js +150 -0
- package/dist/vendor/desktop-commander/dist/ui/config-editor/index.html +13 -0
- package/dist/vendor/desktop-commander/dist/ui/config-editor/styles.css +587 -0
- package/dist/vendor/desktop-commander/dist/ui/contracts.js +57 -0
- package/dist/vendor/desktop-commander/dist/ui/file-preview/index.html +16 -0
- package/dist/vendor/desktop-commander/dist/ui/file-preview/preview-runtime.js +471 -0
- package/dist/vendor/desktop-commander/dist/ui/file-preview/shared/preview-file-types.js +59 -0
- package/dist/vendor/desktop-commander/dist/ui/file-preview/styles.css +1631 -0
- package/dist/vendor/desktop-commander/dist/ui/resources.js +93 -0
- package/dist/vendor/desktop-commander/dist/utils/ab-test.js +122 -0
- package/dist/vendor/desktop-commander/dist/utils/atomic-write.js +80 -0
- package/dist/vendor/desktop-commander/dist/utils/capture.js +40 -0
- package/dist/vendor/desktop-commander/dist/utils/dockerPrompt.js +9 -0
- package/dist/vendor/desktop-commander/dist/utils/exit-process.js +31 -0
- package/dist/vendor/desktop-commander/dist/utils/feature-flags.js +18 -0
- package/dist/vendor/desktop-commander/dist/utils/files/base.js +5 -0
- package/dist/vendor/desktop-commander/dist/utils/files/binary.js +65 -0
- package/dist/vendor/desktop-commander/dist/utils/files/docx.js +694 -0
- package/dist/vendor/desktop-commander/dist/utils/files/excel.js +497 -0
- package/dist/vendor/desktop-commander/dist/utils/files/factory.js +168 -0
- package/dist/vendor/desktop-commander/dist/utils/files/image.js +89 -0
- package/dist/vendor/desktop-commander/dist/utils/files/index.js +14 -0
- package/dist/vendor/desktop-commander/dist/utils/files/pdf.js +124 -0
- package/dist/vendor/desktop-commander/dist/utils/files/text.js +423 -0
- package/dist/vendor/desktop-commander/dist/utils/fuzzySearchLogger.js +126 -0
- package/dist/vendor/desktop-commander/dist/utils/internal-facts.js +23 -0
- package/dist/vendor/desktop-commander/dist/utils/lazy-package.js +44 -0
- package/dist/vendor/desktop-commander/dist/utils/lineEndingHandler.js +77 -0
- package/dist/vendor/desktop-commander/dist/utils/logger.js +72 -0
- package/dist/vendor/desktop-commander/dist/utils/mcp-ui-ab-test.js +62 -0
- package/dist/vendor/desktop-commander/dist/utils/open-browser.js +2 -0
- package/dist/vendor/desktop-commander/dist/utils/process-detection.js +260 -0
- package/dist/vendor/desktop-commander/dist/utils/process-tree.js +223 -0
- package/dist/vendor/desktop-commander/dist/utils/rename.js +74 -0
- package/dist/vendor/desktop-commander/dist/utils/ripgrep-resolver.js +69 -0
- package/dist/vendor/desktop-commander/dist/utils/shell.js +187 -0
- package/dist/vendor/desktop-commander/dist/utils/system-info.js +727 -0
- package/dist/vendor/desktop-commander/dist/utils/terminal-timestamps.js +18 -0
- package/dist/vendor/desktop-commander/dist/utils/toolHistory.js +309 -0
- package/dist/vendor/desktop-commander/dist/utils/trackTools.js +55 -0
- package/dist/vendor/desktop-commander/dist/utils/unsupportedParams.js +66 -0
- package/dist/vendor/desktop-commander/dist/utils/usageTracker.js +225 -0
- package/dist/vendor/desktop-commander/dist/utils/welcome-onboarding.js +2 -0
- package/dist/vendor/desktop-commander/dist/utils/withTimeout.js +85 -0
- package/dist/vendor/desktop-commander/dist/version.js +1 -0
- package/dist/vendor/desktop-commander/package.json +15 -0
- package/package.json +22 -4
- package/docs/assets/starmux-app-en.png +0 -0
- package/docs/assets/starmux-architecture.svg +0 -315
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { configManager } from './config-manager.js';
|
|
3
|
+
import { capture } from "./utils/capture.js";
|
|
4
|
+
class CommandManager {
|
|
5
|
+
getBaseCommand(command) {
|
|
6
|
+
return command.split(' ')[0].toLowerCase().trim();
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The command name of each command in a command line (chained, piped, in $(), ...).
|
|
10
|
+
* asTyped: each first word as typed instead of its base name, for telemetry,
|
|
11
|
+
* which then replaces a path whole (a base name alone can't be told from one).
|
|
12
|
+
*/
|
|
13
|
+
extractCommands(commandString, asTyped = false) {
|
|
14
|
+
try {
|
|
15
|
+
// Trim any leading/trailing whitespace
|
|
16
|
+
commandString = commandString.trim();
|
|
17
|
+
// Define command separators - these are the operators that can chain commands
|
|
18
|
+
const separators = [';', '&&', '||', '|', '&'];
|
|
19
|
+
// This will store our extracted commands
|
|
20
|
+
const commands = [];
|
|
21
|
+
// Split by common separators while preserving quotes
|
|
22
|
+
let inQuote = false;
|
|
23
|
+
let quoteChar = '';
|
|
24
|
+
let currentCmd = '';
|
|
25
|
+
let escaped = false;
|
|
26
|
+
for (let i = 0; i < commandString.length; i++) {
|
|
27
|
+
const char = commandString[i];
|
|
28
|
+
// Handle escape characters
|
|
29
|
+
if (char === '\\' && !escaped) {
|
|
30
|
+
escaped = true;
|
|
31
|
+
currentCmd += char;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
// If this character is escaped, just add it
|
|
35
|
+
if (escaped) {
|
|
36
|
+
escaped = false;
|
|
37
|
+
currentCmd += char;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
// Handle quotes (both single and double)
|
|
41
|
+
if ((char === '"' || char === "'") && !inQuote) {
|
|
42
|
+
inQuote = true;
|
|
43
|
+
quoteChar = char;
|
|
44
|
+
currentCmd += char;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
else if (char === quoteChar && inQuote) {
|
|
48
|
+
inQuote = false;
|
|
49
|
+
quoteChar = '';
|
|
50
|
+
currentCmd += char;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
// Handle $() command substitution even inside quotes (fixes blocklist bypass)
|
|
54
|
+
if (char === '$' && i + 1 < commandString.length && commandString[i + 1] === '(') {
|
|
55
|
+
const startIndex = i;
|
|
56
|
+
let openParens = 1;
|
|
57
|
+
let j = i + 2; // skip past $(
|
|
58
|
+
while (j < commandString.length && openParens > 0) {
|
|
59
|
+
if (commandString[j] === '(')
|
|
60
|
+
openParens++;
|
|
61
|
+
if (commandString[j] === ')')
|
|
62
|
+
openParens--;
|
|
63
|
+
j++;
|
|
64
|
+
}
|
|
65
|
+
if (j <= commandString.length && openParens === 0) {
|
|
66
|
+
const subContent = commandString.substring(i + 2, j - 1);
|
|
67
|
+
const subCommands = this.extractCommands(subContent, asTyped);
|
|
68
|
+
commands.push(...subCommands);
|
|
69
|
+
i = j - 1;
|
|
70
|
+
if (!inQuote) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
currentCmd += commandString.substring(startIndex, j);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Handle backtick command substitution even inside quotes
|
|
80
|
+
if (char === '`') {
|
|
81
|
+
const startIndex = i;
|
|
82
|
+
let j = i + 1;
|
|
83
|
+
while (j < commandString.length && commandString[j] !== '`') {
|
|
84
|
+
j++;
|
|
85
|
+
}
|
|
86
|
+
if (j < commandString.length) {
|
|
87
|
+
const subContent = commandString.substring(i + 1, j);
|
|
88
|
+
const subCommands = this.extractCommands(subContent, asTyped);
|
|
89
|
+
commands.push(...subCommands);
|
|
90
|
+
i = j;
|
|
91
|
+
if (!inQuote) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
currentCmd += commandString.substring(startIndex, j + 1);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// If we're inside quotes, just add the character
|
|
101
|
+
if (inQuote) {
|
|
102
|
+
currentCmd += char;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
// Handle subshells - if we see an opening parenthesis, we need to find its matching closing parenthesis
|
|
106
|
+
if (char === '(') {
|
|
107
|
+
// Find the matching closing parenthesis
|
|
108
|
+
let openParens = 1;
|
|
109
|
+
let j = i + 1;
|
|
110
|
+
while (j < commandString.length && openParens > 0) {
|
|
111
|
+
if (commandString[j] === '(')
|
|
112
|
+
openParens++;
|
|
113
|
+
if (commandString[j] === ')')
|
|
114
|
+
openParens--;
|
|
115
|
+
j++;
|
|
116
|
+
}
|
|
117
|
+
// Skip to after the closing parenthesis only if properly balanced
|
|
118
|
+
if (j <= commandString.length && openParens === 0) {
|
|
119
|
+
const subshellContent = commandString.substring(i + 1, j - 1);
|
|
120
|
+
// Recursively extract commands from the subshell
|
|
121
|
+
const subCommands = this.extractCommands(subshellContent, asTyped);
|
|
122
|
+
commands.push(...subCommands);
|
|
123
|
+
// Move position past the subshell
|
|
124
|
+
i = j - 1;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Check for separators
|
|
129
|
+
let isSeparator = false;
|
|
130
|
+
for (const separator of separators) {
|
|
131
|
+
if (commandString.startsWith(separator, i)) {
|
|
132
|
+
// We found a separator - extract the command before it
|
|
133
|
+
if (currentCmd.trim()) {
|
|
134
|
+
const baseCommand = this.extractBaseCommand(currentCmd.trim(), asTyped);
|
|
135
|
+
if (baseCommand)
|
|
136
|
+
commands.push(baseCommand);
|
|
137
|
+
}
|
|
138
|
+
// Move past the separator
|
|
139
|
+
i += separator.length - 1;
|
|
140
|
+
currentCmd = '';
|
|
141
|
+
isSeparator = true;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (!isSeparator) {
|
|
146
|
+
currentCmd += char;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Don't forget to add the last command
|
|
150
|
+
if (currentCmd.trim()) {
|
|
151
|
+
const baseCommand = this.extractBaseCommand(currentCmd.trim(), asTyped);
|
|
152
|
+
if (baseCommand)
|
|
153
|
+
commands.push(baseCommand);
|
|
154
|
+
}
|
|
155
|
+
// Remove duplicates and return
|
|
156
|
+
return [...new Set(commands)];
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
// If anything goes wrong, log the error but return the basic command to not break execution
|
|
160
|
+
capture('server_request_error', {
|
|
161
|
+
error: 'Error extracting commands'
|
|
162
|
+
});
|
|
163
|
+
const baseCmd = this.extractBaseCommand(commandString, asTyped);
|
|
164
|
+
return baseCmd ? [baseCmd] : [];
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// This extracts the actual command name from a command string (asTyped: see extractCommands)
|
|
168
|
+
extractBaseCommand(commandStr, asTyped = false) {
|
|
169
|
+
try {
|
|
170
|
+
// Remove environment variables (patterns like KEY=value)
|
|
171
|
+
const withoutEnvVars = commandStr.replace(/\w+=\S+\s*/g, '').trim();
|
|
172
|
+
// If nothing remains after removing env vars, return null
|
|
173
|
+
if (!withoutEnvVars)
|
|
174
|
+
return null;
|
|
175
|
+
// Get the first token (the command)
|
|
176
|
+
const tokens = withoutEnvVars.split(/\s+/);
|
|
177
|
+
let firstToken = null;
|
|
178
|
+
// Find the first valid token (skip variables)
|
|
179
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
180
|
+
const token = tokens[i];
|
|
181
|
+
// Skip dollar-prefixed tokens (variables) but not $() command substitutions
|
|
182
|
+
if (token.startsWith('$') && !token.startsWith('$(')) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
// Check if it starts with special characters like ( that might indicate it's not a regular command
|
|
186
|
+
if (token[0] === '(') {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
firstToken = token;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
// No valid command token found
|
|
193
|
+
if (!firstToken) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
// handle $() command substitution - extract the inner command
|
|
197
|
+
if (firstToken.startsWith('$(') && firstToken.endsWith(')')) {
|
|
198
|
+
const inner = firstToken.slice(2, -1).trim();
|
|
199
|
+
if (inner) {
|
|
200
|
+
const innerTokens = inner.split(/\s+/);
|
|
201
|
+
return (asTyped ? innerTokens[0] : path.basename(innerTokens[0])).toLowerCase();
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
// strip path prefix so /usr/bin/sudo gets caught as "sudo"
|
|
206
|
+
const baseName = asTyped ? firstToken : path.basename(firstToken);
|
|
207
|
+
return baseName.toLowerCase();
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
capture('Error extracting base command');
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async validateCommand(command) {
|
|
215
|
+
try {
|
|
216
|
+
// Get blocked commands from config
|
|
217
|
+
const config = await configManager.getConfig();
|
|
218
|
+
const blockedCommands = config.blockedCommands || [];
|
|
219
|
+
// Extract all commands from the command string
|
|
220
|
+
const allCommands = this.extractCommands(command);
|
|
221
|
+
// If there are no commands extracted, fall back to base command
|
|
222
|
+
if (allCommands.length === 0) {
|
|
223
|
+
const baseCommand = this.getBaseCommand(command);
|
|
224
|
+
return !blockedCommands.includes(baseCommand);
|
|
225
|
+
}
|
|
226
|
+
// Check if any of the extracted commands are in the blocked list
|
|
227
|
+
for (const cmd of allCommands) {
|
|
228
|
+
if (blockedCommands.includes(cmd)) {
|
|
229
|
+
return false; // Command is blocked
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// No commands were blocked
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
console.error('Error validating command:', error);
|
|
237
|
+
capture('server_validate_command_error', {
|
|
238
|
+
error: error instanceof Error ? error.message : String(error)
|
|
239
|
+
});
|
|
240
|
+
// Fail closed: deny the command if validation encounters an error.
|
|
241
|
+
// This prevents a config read failure from bypassing all command filtering.
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
export const commandManager = new CommandManager();
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Single source of truth for user-editable configuration fields.
|
|
2
|
+
export const CONFIG_FIELD_DEFINITIONS = {
|
|
3
|
+
blockedCommands: {
|
|
4
|
+
label: 'Blocked Commands',
|
|
5
|
+
description: 'This is your personal safety blocklist. If a command appears here, Desktop Commander will refuse to run it even if a prompt asks for it. Add risky commands you never want executed by mistake.',
|
|
6
|
+
valueType: 'array',
|
|
7
|
+
},
|
|
8
|
+
allowedDirectories: {
|
|
9
|
+
label: 'Allowed Folders',
|
|
10
|
+
description: 'These are the folders Desktop Commander is allowed to read and edit. Think of this as a permission list. Keeping it small is safer. If this list is empty, Desktop Commander can access your entire filesystem.',
|
|
11
|
+
valueType: 'array',
|
|
12
|
+
},
|
|
13
|
+
defaultShell: {
|
|
14
|
+
label: 'Default Shell',
|
|
15
|
+
description: 'This is the shell used for new command sessions (for example /bin/bash or /bin/zsh). Only change this if you know your environment requires a specific shell.',
|
|
16
|
+
valueType: 'string',
|
|
17
|
+
},
|
|
18
|
+
telemetryEnabled: {
|
|
19
|
+
label: 'Anonymous Telemetry',
|
|
20
|
+
description: 'When on, Desktop Commander sends anonymous usage information that helps improve product quality. When off, no telemetry data is sent.',
|
|
21
|
+
valueType: 'boolean',
|
|
22
|
+
},
|
|
23
|
+
fileReadLineLimit: {
|
|
24
|
+
label: 'File Read Limit',
|
|
25
|
+
description: 'Maximum number of lines returned from a file in one read action. Lower numbers keep responses short and safer; higher numbers return more text at once.',
|
|
26
|
+
valueType: 'number',
|
|
27
|
+
},
|
|
28
|
+
fileWriteLineLimit: {
|
|
29
|
+
label: 'File Write Limit',
|
|
30
|
+
description: 'Maximum number of lines that can be written in one edit operation. This helps prevent accidental oversized writes and keeps file changes predictable.',
|
|
31
|
+
valueType: 'number',
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
export const CONFIG_FIELD_KEYS = Object.keys(CONFIG_FIELD_DEFINITIONS);
|
|
35
|
+
export function isConfigFieldKey(value) {
|
|
36
|
+
return Object.prototype.hasOwnProperty.call(CONFIG_FIELD_DEFINITIONS, value);
|
|
37
|
+
}
|