summaryrefslogtreecommitdiff
path: root/packages/core/src/utils/shell-utils.ts
diff options
context:
space:
mode:
authorAbhi <[email protected]>2025-08-17 00:02:54 -0400
committerGitHub <[email protected]>2025-08-17 04:02:54 +0000
commit33b9bdb11e9371dfa6891ba1be47a12b958f493d (patch)
tree46ac192df7dd2aeaf35cb5eef5c121bc5decada9 /packages/core/src/utils/shell-utils.ts
parente7dbc607a598c5270507d0ce7d55a3c98dcb0c0c (diff)
feat(cli): Introduce arguments for shell execution in custom commands (#5966)
Diffstat (limited to 'packages/core/src/utils/shell-utils.ts')
-rw-r--r--packages/core/src/utils/shell-utils.ts96
1 files changed, 96 insertions, 0 deletions
diff --git a/packages/core/src/utils/shell-utils.ts b/packages/core/src/utils/shell-utils.ts
index 4164cdca..2c818c8e 100644
--- a/packages/core/src/utils/shell-utils.ts
+++ b/packages/core/src/utils/shell-utils.ts
@@ -5,6 +5,102 @@
*/
import { Config } from '../config/config.js';
+import os from 'os';
+import { quote } from 'shell-quote';
+
+/**
+ * An identifier for the shell type.
+ */
+export type ShellType = 'cmd' | 'powershell' | 'bash';
+
+/**
+ * Defines the configuration required to execute a command string within a specific shell.
+ */
+export interface ShellConfiguration {
+ /** The path or name of the shell executable (e.g., 'bash', 'cmd.exe'). */
+ executable: string;
+ /**
+ * The arguments required by the shell to execute a subsequent string argument.
+ */
+ argsPrefix: string[];
+ /** An identifier for the shell type. */
+ shell: ShellType;
+}
+
+/**
+ * Determines the appropriate shell configuration for the current platform.
+ *
+ * This ensures we can execute command strings predictably and securely across platforms
+ * using the `spawn(executable, [...argsPrefix, commandString], { shell: false })` pattern.
+ *
+ * @returns The ShellConfiguration for the current environment.
+ */
+export function getShellConfiguration(): ShellConfiguration {
+ if (isWindows()) {
+ const comSpec = process.env.ComSpec || 'cmd.exe';
+ const executable = comSpec.toLowerCase();
+
+ if (
+ executable.endsWith('powershell.exe') ||
+ executable.endsWith('pwsh.exe')
+ ) {
+ // For PowerShell, the arguments are different.
+ // -NoProfile: Speeds up startup.
+ // -Command: Executes the following command.
+ return {
+ executable: comSpec,
+ argsPrefix: ['-NoProfile', '-Command'],
+ shell: 'powershell',
+ };
+ }
+
+ // Default to cmd.exe for anything else on Windows.
+ // Flags for CMD:
+ // /d: Skip execution of AutoRun commands.
+ // /s: Modifies the treatment of the command string (important for quoting).
+ // /c: Carries out the command specified by the string and then terminates.
+ return {
+ executable: comSpec,
+ argsPrefix: ['/d', '/s', '/c'],
+ shell: 'cmd',
+ };
+ }
+
+ // Unix-like systems (Linux, macOS)
+ return { executable: 'bash', argsPrefix: ['-c'], shell: 'bash' };
+}
+
+/**
+ * Export the platform detection constant for use in process management (e.g., killing processes).
+ */
+export const isWindows = () => os.platform() === 'win32';
+
+/**
+ * Escapes a string so that it can be safely used as a single argument
+ * in a shell command, preventing command injection.
+ *
+ * @param arg The argument string to escape.
+ * @param shell The type of shell the argument is for.
+ * @returns The shell-escaped string.
+ */
+export function escapeShellArg(arg: string, shell: ShellType): string {
+ if (!arg) {
+ return '';
+ }
+
+ switch (shell) {
+ case 'powershell':
+ // For PowerShell, wrap in single quotes and escape internal single quotes by doubling them.
+ return `'${arg.replace(/'/g, "''")}'`;
+ case 'cmd':
+ // Simple Windows escaping for cmd.exe: wrap in double quotes and escape inner double quotes.
+ return `"${arg.replace(/"/g, '""')}"`;
+ case 'bash':
+ default:
+ // POSIX shell escaping using shell-quote.
+ return quote([arg]);
+ }
+}
/**
* Splits a shell command into a list of individual commands, respecting quotes.