diff --git a/ui/desktop/src/components/settings/extensions/utils.test.ts b/ui/desktop/src/components/settings/extensions/utils.test.ts index f23e70a74536..8888e6833e73 100644 --- a/ui/desktop/src/components/settings/extensions/utils.test.ts +++ b/ui/desktop/src/components/settings/extensions/utils.test.ts @@ -251,6 +251,13 @@ describe('Extension Utils', () => { "java -classpath '/path/with spaces/lib.jar' Main", { cmd: 'java', args: ['-classpath', '/path/with spaces/lib.jar', 'Main'] }, ], + [ + '"/Applications/IntelliJ IDEA.app/Contents/jbr/Contents/Home/bin/java" -classpath "/path/with spaces/lib.jar" Main', + { + cmd: '/Applications/IntelliJ IDEA.app/Contents/jbr/Contents/Home/bin/java', + args: ['-classpath', '/path/with spaces/lib.jar', 'Main'], + }, + ], [ 'node --max-old-space-size=4096 app.js', { cmd: 'node', args: ['--max-old-space-size=4096', 'app.js'] }, diff --git a/ui/desktop/src/components/settings/extensions/utils.ts b/ui/desktop/src/components/settings/extensions/utils.ts index e6681bf7034f..b4221ef03275 100644 --- a/ui/desktop/src/components/settings/extensions/utils.ts +++ b/ui/desktop/src/components/settings/extensions/utils.ts @@ -1,4 +1,6 @@ -import { parse, quote } from 'shell-quote'; +import type { FixedExtensionEntry } from '../../ConfigContext'; +import type { ExtensionConfig } from '../../../api/types.gen'; +import { parse as parseShellQuote, quote as quoteShell } from 'shell-quote'; // Default extension timeout in seconds // TODO: keep in sync with rust better @@ -17,9 +19,6 @@ export function nameToKey(name: string): string { .toLowerCase(); } -import { FixedExtensionEntry } from '../../ConfigContext'; -import { ExtensionConfig } from '../../../api/types.gen'; - export interface ExtensionFormData { name: string; description: string; @@ -105,7 +104,7 @@ export function extensionToFormData(extension: FixedExtensionEntry): ExtensionFo extension.type === 'platform' ? 'stdio' : extension.type, - cmd: extension.type === 'stdio' ? quote([extension.cmd, ...extension.args]) : undefined, + cmd: extension.type === 'stdio' ? quoteShell([extension.cmd, ...extension.args]) : undefined, endpoint: extension.type === 'streamable_http' || extension.type === 'sse' ? (extension.uri ?? undefined) @@ -170,8 +169,25 @@ export function createExtensionConfig(formData: ExtensionFormData): ExtensionCon } export function splitCmdAndArgs(str: string): { cmd: string; args: string[] } { - const parts = parse(str.trim()).filter((p): p is string => typeof p === 'string'); - return { cmd: parts[0] || '', args: parts.slice(1) }; + const trimmed = str.trim(); + if (!trimmed) { + return { cmd: '', args: [] }; + } + + const parsed = parseShellQuote(trimmed); + const words = parsed.filter((item): item is string => typeof item === 'string').map(String); + + const cmd = words[0] || ''; + const args = words.slice(1); + + return { + cmd, + args, + }; +} + +export function combineCmdAndArgs(cmd: string, args: string[]): string { + return quoteShell([cmd, ...args]); } export function extractCommand(link: string): string {