@push.rocks/smartshell 3.2.3 → 3.3.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 CHANGED
@@ -1,136 +1,617 @@
1
- # @push.rocks/smartshell
2
- shell actions designed as promises
1
+ # @push.rocks/smartshell 🐚
2
+ **Execute shell commands with superpowers in Node.js**
3
3
 
4
- ## Install
4
+ [![npm version](https://img.shields.io/npm/v/@push.rocks/smartshell.svg)](https://www.npmjs.com/package/@push.rocks/smartshell)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
6
 
6
- To install `@push.rocks/smartshell`, use npm:
7
+ ## ⚠️ Security Notice
7
8
 
8
- ```sh
9
+ **IMPORTANT:** Please read the [Security Guide](#security-guide) below for critical information about command execution and input handling. Always use `execSpawn` methods for untrusted input.
10
+
11
+ ## Why smartshell? 🚀
12
+
13
+ Tired of wrestling with Node.js child processes? Meet `@push.rocks/smartshell` - your promise-based shell command companion that makes executing system commands feel like a breeze. Whether you're building automation scripts, CI/CD pipelines, or need fine-grained control over shell execution, smartshell has got you covered.
14
+
15
+ ### ✨ Key Features
16
+
17
+ - 🎯 **Promise-based API** - Async/await ready for modern codebases
18
+ - 🔇 **Silent execution modes** - Control output verbosity
19
+ - 📡 **Streaming support** - Real-time output for long-running processes
20
+ - 🎮 **Interactive commands** - Handle user input programmatically
21
+ - 🛡️ **Secure execution** - Shell-free methods for untrusted input
22
+ - ⚡ **Smart execution modes** - Strict, silent, or streaming
23
+ - 🔍 **Pattern matching** - Wait for specific output patterns
24
+ - 🌍 **Environment management** - Custom env vars and PATH handling
25
+ - 💾 **Memory protection** - Built-in buffer limits prevent OOM
26
+ - ⏱️ **Timeout support** - Automatic process termination
27
+ - 🖥️ **PTY support** - Full terminal emulation (optional)
28
+ - 🎨 **Cross-platform** - Windows, macOS, and Linux ready
29
+ - 🛡️ **TypeScript first** - Full type safety and IntelliSense
30
+
31
+ ## Installation 📦
32
+
33
+ ```bash
34
+ # Using npm
9
35
  npm install @push.rocks/smartshell --save
36
+
37
+ # Using yarn
38
+ yarn add @push.rocks/smartshell
39
+
40
+ # Using pnpm (recommended)
41
+ pnpm add @push.rocks/smartshell
42
+
43
+ # Optional: For PTY support (terminal emulation)
44
+ pnpm add --save-optional node-pty
10
45
  ```
11
46
 
12
- Or if you prefer using Yarn:
47
+ ## Quick Start 🏃‍♂️
13
48
 
14
- ```sh
15
- yarn add @push.rocks/smartshell
49
+ ```typescript
50
+ import { Smartshell } from '@push.rocks/smartshell';
51
+
52
+ // Create your shell instance
53
+ const shell = new Smartshell({
54
+ executor: 'bash' // or 'sh' for lighter shells
55
+ });
56
+
57
+ // Run a simple command
58
+ const result = await shell.exec('echo "Hello, World!"');
59
+ console.log(result.stdout); // "Hello, World!"
60
+ console.log(result.signal); // undefined (no signal)
61
+ console.log(result.stderr); // "" (no errors)
16
62
  ```
17
63
 
18
- Ensure that you have TypeScript and the related dependencies installed as well since `@push.rocks/smartshell` is designed to work with TypeScript.
64
+ ## Security-First Execution 🔒
65
+
66
+ ### Secure Command Execution with execSpawn
19
67
 
20
- ## Usage
68
+ When dealing with untrusted input, **always use execSpawn methods** which don't use shell interpretation:
21
69
 
22
- The `@push.rocks/smartshell` package simplifies running shell commands within Node.js applications by wrapping these commands within promises. This approach enhances the readability and maintainability of code that relies on shell execution, making it particularly useful in automation scripts, build processes, and any scenario where interaction with the system shell is required.
70
+ ```typescript
71
+ // ❌ DANGEROUS with untrusted input
72
+ const userInput = "file.txt; rm -rf /";
73
+ await shell.exec(`cat ${userInput}`); // Command injection!
23
74
 
24
- ### Getting Started with `@push.rocks/smartshell`
75
+ // SAFE with untrusted input
76
+ await shell.execSpawn('cat', [userInput]); // Arguments are properly escaped
77
+ ```
25
78
 
26
- First, ensure that you import `Smartshell` from `@push.rocks/smartshell` using ESM syntax in your TypeScript file:
79
+ ### execSpawn Family Methods
27
80
 
28
81
  ```typescript
29
- import { Smartshell } from '@push.rocks/smartshell';
82
+ // Basic secure execution
83
+ const result = await shell.execSpawn('ls', ['-la', '/home']);
84
+
85
+ // Streaming secure execution
86
+ const streaming = await shell.execSpawnStreaming('npm', ['install']);
87
+ await streaming.finalPromise;
88
+
89
+ // Interactive secure execution
90
+ const interactive = await shell.execSpawnInteractiveControl('cat', []);
91
+ await interactive.sendLine('Hello');
92
+ interactive.endInput();
93
+ await interactive.finalPromise;
94
+ ```
95
+
96
+ ## Production Features 🏭
97
+
98
+ ### Resource Management
99
+
100
+ Prevent memory issues with built-in buffer limits:
101
+
102
+ ```typescript
103
+ const result = await shell.exec('large-output-command', {
104
+ maxBuffer: 10 * 1024 * 1024, // 10MB limit
105
+ onData: (chunk) => {
106
+ // Process chunks as they arrive
107
+ console.log('Received:', chunk.toString());
108
+ }
109
+ });
110
+ ```
111
+
112
+ ### Timeout Support
113
+
114
+ Automatically terminate long-running processes:
115
+
116
+ ```typescript
117
+ try {
118
+ const result = await shell.execSpawn('long-process', [], {
119
+ timeout: 5000 // 5 second timeout
120
+ });
121
+ } catch (error) {
122
+ console.log('Process timed out');
123
+ }
124
+ ```
125
+
126
+ ### Debug Mode
127
+
128
+ Enable detailed logging for troubleshooting:
129
+
130
+ ```typescript
131
+ const result = await shell.exec('command', {
132
+ debug: true // Logs process lifecycle events
133
+ });
30
134
  ```
31
135
 
32
- ### Creating a Smartshell Instance
136
+ ### Custom Environment
33
137
 
34
- Before executing any shell command, you need to create an instance of `Smartshell`. The constructor accepts configuration options such as the shell executor (`bash` or `sh`), and optionally, paths to source files and directories to include in the shell’s environment.
138
+ Control the execution environment precisely:
35
139
 
36
140
  ```typescript
37
- const smartShellInstance = new Smartshell({
38
- executor: 'bash', // or 'sh'
141
+ const result = await shell.execSpawn('node', ['script.js'], {
142
+ env: {
143
+ NODE_ENV: 'production',
144
+ PATH: '/usr/bin:/bin',
145
+ CUSTOM_VAR: 'value'
146
+ }
39
147
  });
40
148
  ```
41
149
 
42
- ### Executing Commands
150
+ ## Interactive Control 🎮
43
151
 
44
- #### Basic Execution
152
+ ### Programmatic Input Control
45
153
 
46
- To execute a shell command, use the `exec` method. This method returns a promise that resolves with an execution result object containing `exitCode` and `stdout`.
154
+ Send input to processes programmatically:
47
155
 
48
156
  ```typescript
49
- (async () => {
50
- const result = await smartShellInstance.exec('echo "Hello, SmartShell"');
51
- console.log(result.stdout); // Outputs: Hello, SmartShell
52
- })();
157
+ const interactive = await shell.execInteractiveControl('cat');
158
+
159
+ // Send input line by line
160
+ await interactive.sendLine('Line 1');
161
+ await interactive.sendLine('Line 2');
162
+
163
+ // Send raw input without newline
164
+ await interactive.sendInput('partial');
165
+
166
+ // Close stdin
167
+ interactive.endInput();
168
+
169
+ // Wait for completion
170
+ const result = await interactive.finalPromise;
53
171
  ```
54
172
 
55
- #### Silent Execution
173
+ ### Passthrough Mode
56
174
 
57
- If you prefer not to display the output in the console, use `execSilent`:
175
+ Connect stdin for real keyboard interaction:
58
176
 
59
177
  ```typescript
60
- (async () => {
61
- const result = await smartShellInstance.execSilent('ls');
62
- console.log(result.stdout); // Outputs the list of files and directories
63
- })();
178
+ // User can type directly
179
+ await shell.execPassthrough('vim file.txt');
64
180
  ```
65
181
 
66
- #### Strict Execution
182
+ ## PTY Support - Full Terminal Emulation 🖥️
183
+
184
+ Smartshell provides two modes for executing interactive commands:
185
+
186
+ 1. **Pipe Mode (Default)** - Fast, simple, no dependencies
187
+ 2. **PTY Mode** - Full terminal emulation for advanced interactive programs
67
188
 
68
- For scenarios where an execution error should immediately throw an exception, use `execStrict`:
189
+ ### When to Use Each Mode
190
+
191
+ #### Use Pipe Mode (Default) When:
192
+ - Running simple commands that read from stdin
193
+ - Using tools like `cat`, `grep`, `sed`, `awk`
194
+ - Running basic scripts that don't need terminal features
195
+ - You want maximum performance and simplicity
196
+ - You don't want native dependencies
197
+
198
+ #### Use PTY Mode When:
199
+ - Running commands that require a real terminal:
200
+ - Password prompts (`sudo`, `ssh`, `su`)
201
+ - Interactive editors (`vim`, `nano`, `emacs`)
202
+ - Terminal UIs (`htop`, `less`, `more`)
203
+ - Programs with fancy prompts (`bash read -p`)
204
+ - Tab completion and readline features
205
+ - You need terminal features:
206
+ - ANSI colors and escape sequences
207
+ - Terminal size control
208
+ - Signal handling (Ctrl+C, Ctrl+Z)
209
+ - Line discipline and special key handling
210
+
211
+ ### Installing PTY Support
212
+
213
+ PTY support requires the optional `node-pty` dependency:
214
+
215
+ ```bash
216
+ # Install as optional dependency
217
+ pnpm add --save-optional node-pty
218
+
219
+ # Note: node-pty requires compilation and has platform-specific requirements
220
+ # - On Windows: Requires Visual Studio Build Tools
221
+ # - On macOS/Linux: Requires Python and build tools
222
+ ```
223
+
224
+ ### PTY Usage Examples
69
225
 
70
226
  ```typescript
71
- (async () => {
72
- try {
73
- const result = await smartShellInstance.execStrict('exit 1');
74
- } catch (error) {
75
- console.error('Command execution failed');
227
+ // Use PTY for commands that need terminal features
228
+ const ptyInteractive = await shell.execInteractiveControlPty(
229
+ "bash -c 'read -p \"Enter name: \" name && echo \"Hello, $name\"'"
230
+ );
231
+ await ptyInteractive.sendLine('John');
232
+ const result = await ptyInteractive.finalPromise;
233
+ // With PTY, the prompt "Enter name: " will be visible in stdout
234
+
235
+ // Streaming with PTY for real-time interaction
236
+ const ptyStreaming = await shell.execStreamingInteractiveControlPty('vim test.txt');
237
+ await ptyStreaming.sendInput('i'); // Enter insert mode
238
+ await ptyStreaming.sendInput('Hello from PTY!');
239
+ await ptyStreaming.sendInput('\x1b'); // ESC key
240
+ await ptyStreaming.sendInput(':wq\r'); // Save and quit
241
+ ```
242
+
243
+ ### PTY vs Pipe Mode Comparison
244
+
245
+ | Feature | Pipe Mode | PTY Mode |
246
+ |---------|-----------|----------|
247
+ | Dependencies | None | node-pty |
248
+ | Terminal Detection | `isatty()` returns false | `isatty()` returns true |
249
+ | Prompt Display | May not show | Always shows |
250
+ | Colors | Often disabled | Enabled |
251
+ | Signal Handling | Basic | Full (Ctrl+C, Ctrl+Z, etc.) |
252
+ | Line Ending | `\n` | `\r` (carriage return) |
253
+ | EOF Signal | Stream end | `\x04` (Ctrl+D) |
254
+
255
+ ### Common PTY Patterns
256
+
257
+ ```typescript
258
+ // Password input (PTY required)
259
+ const sudo = await shell.execInteractiveControlPty('sudo ls /root');
260
+ await sudo.sendLine('mypassword');
261
+ const result = await sudo.finalPromise;
262
+
263
+ // Interactive REPL with colors
264
+ const node = await shell.execStreamingInteractiveControlPty('node');
265
+ await node.sendLine('console.log("PTY supports colors!")');
266
+ await node.sendLine('.exit');
267
+
268
+ // Handling terminal colors
269
+ const ls = await shell.execInteractiveControlPty('ls --color=always');
270
+ const result = await ls.finalPromise;
271
+ // result.stdout will contain ANSI color codes
272
+ ```
273
+
274
+ ### PTY Fallback Strategy
275
+
276
+ Always provide a fallback for when PTY isn't available:
277
+
278
+ ```typescript
279
+ try {
280
+ // Try PTY mode first
281
+ const result = await shell.execInteractiveControlPty(command);
282
+ // ...
283
+ } catch (error) {
284
+ if (error.message.includes('node-pty')) {
285
+ // Fallback to pipe mode
286
+ console.warn('PTY not available, using pipe mode');
287
+ const result = await shell.execInteractiveControl(command);
288
+ // ...
76
289
  }
77
- })();
290
+ }
78
291
  ```
79
292
 
80
- #### Streaming Output
293
+ ## Advanced Pattern Matching 🔍
294
+
295
+ ### Enhanced execAndWaitForLine
81
296
 
82
- Some commands benefit from streaming output as they execute, especially long-running tasks. For these cases, use `execStreaming`:
297
+ Wait for patterns with timeout and auto-termination:
83
298
 
84
299
  ```typescript
85
- (async () => {
86
- const execStreamingResult = await smartShellInstance.execStreaming('tail -f /var/log/system.log');
87
-
88
- execStreamingResult.childProcess.stdout.on('data', (data) => {
89
- console.log(data.toString());
90
- });
300
+ // Wait with timeout
301
+ await shell.execAndWaitForLine(
302
+ 'npm start',
303
+ /Server listening on port/,
304
+ false, // silent
305
+ {
306
+ timeout: 30000, // 30 second timeout
307
+ terminateOnMatch: true // Kill process after match
308
+ }
309
+ );
310
+ ```
311
+
312
+ ## Core Concepts 💡
313
+
314
+ ### The Smartshell Instance
315
+
316
+ The heart of smartshell is the `Smartshell` class. Each instance maintains its own environment and configuration:
91
317
 
92
- // Remember to handle the process termination as necessary.
93
- })();
318
+ ```typescript
319
+ const shell = new Smartshell({
320
+ executor: 'bash', // Choose your shell: 'bash' or 'sh'
321
+ sourceFilePaths: ['/path/to/env.sh'], // Optional: source files on init
322
+ });
94
323
  ```
95
324
 
96
- ### Advanced Usage
325
+ ## Execution Modes 🎛️
326
+
327
+ ### Standard Execution
328
+
329
+ Perfect for general commands where you want to see the output:
330
+
331
+ ```typescript
332
+ const result = await shell.exec('ls -la');
333
+ console.log(result.stdout); // Directory listing
334
+ console.log(result.exitCode); // 0 for success
335
+ console.log(result.signal); // Signal if terminated
336
+ console.log(result.stderr); // Error output
337
+ ```
97
338
 
98
- #### Executing With Custom Environment Variables
339
+ ### Silent Execution
99
340
 
100
- `smartshell` allows for the execution of commands within a modified environment, facilitating the use of custom variables or altered PATH values:
341
+ Run commands without printing to console - ideal for capturing output:
101
342
 
102
343
  ```typescript
103
- (async () => {
104
- smartShellInstance.shellEnv.addSourceFiles(['/path/to/envFile']);
105
- smartShellInstance.shellEnv.pathDirArray.push('/custom/bin');
344
+ const result = await shell.execSilent('cat /etc/hostname');
345
+ // Output is NOT printed to console but IS captured in result
346
+ console.log(result.stdout); // Access the captured output here
347
+ ```
348
+
349
+ ### Strict Execution
350
+
351
+ Throws an error if the command fails - great for critical operations:
352
+
353
+ ```typescript
354
+ try {
355
+ await shell.execStrict('critical-command');
356
+ console.log('✅ Command succeeded!');
357
+ } catch (error) {
358
+ console.error('❌ Command failed:', error.message);
359
+ // Error includes exit code or signal information
360
+ }
361
+ ```
362
+
363
+ ### Streaming Execution
364
+
365
+ For long-running processes or when you need real-time output:
366
+
367
+ ```typescript
368
+ const streaming = await shell.execStreaming('npm install');
369
+
370
+ // Access the child process directly
371
+ streaming.childProcess.stdout.on('data', (chunk) => {
372
+ console.log('📦 Installing:', chunk.toString());
373
+ });
374
+
375
+ // Control the process
376
+ await streaming.terminate(); // SIGTERM
377
+ await streaming.kill(); // SIGKILL
378
+ await streaming.keyboardInterrupt(); // SIGINT
379
+
380
+ // Wait for completion
381
+ await streaming.finalPromise;
382
+ ```
383
+
384
+ ## Real-World Examples 🌍
385
+
386
+ ### Build Pipeline
387
+
388
+ ```typescript
389
+ const shell = new Smartshell({ executor: 'bash' });
390
+
391
+ // Clean build directory
392
+ await shell.execSilent('rm -rf dist');
393
+
394
+ // Run TypeScript compiler
395
+ const buildResult = await shell.execStrict('tsc');
396
+
397
+ // Run tests
398
+ await shell.execStrict('npm test');
399
+
400
+ // Build succeeded!
401
+ console.log('✅ Build pipeline completed successfully');
402
+ ```
403
+
404
+ ### CI/CD with Security
405
+
406
+ ```typescript
407
+ async function deployApp(branch: string, untrustedTag: string) {
408
+ const shell = new Smartshell({ executor: 'bash' });
409
+
410
+ // Use execSpawn for untrusted input
411
+ await shell.execSpawnStrict('git', ['checkout', branch]);
412
+ await shell.execSpawnStrict('git', ['tag', untrustedTag]);
106
413
 
107
- const result = await smartShellInstance.exec('echo $CUSTOM_VAR');
108
- console.log(result.stdout); // Outputs the value of CUSTOM_VAR
109
- })();
414
+ // Safe to use exec for hardcoded commands
415
+ await shell.execStrict('npm run build');
416
+ await shell.execStrict('npm run deploy');
417
+ }
110
418
  ```
111
419
 
112
- ### Interactive Mode
420
+ ### Docker Compose Helper
113
421
 
114
- For commands that require interactive terminal input (not typically recommended for automated scripts), you can use `execInteractive`:
422
+ ```typescript
423
+ const shell = new Smartshell({ executor: 'bash' });
424
+
425
+ // Start services and wait for readiness
426
+ console.log('🐳 Starting Docker services...');
427
+ await shell.execAndWaitForLine(
428
+ 'docker-compose up',
429
+ /All services are ready/,
430
+ false,
431
+ {
432
+ timeout: 60000,
433
+ terminateOnMatch: false // Keep running after match
434
+ }
435
+ );
436
+
437
+ // Run migrations
438
+ await shell.execStrict('docker-compose exec app npm run migrate');
439
+ console.log('✅ Environment ready!');
440
+ ```
441
+
442
+ ### Development Server with Auto-Restart
443
+
444
+ ```typescript
445
+ import { SmartExecution } from '@push.rocks/smartshell';
446
+
447
+ const shell = new Smartshell({ executor: 'bash' });
448
+ const devServer = new SmartExecution(shell, 'npm run dev');
449
+
450
+ // Watch for file changes and restart
451
+ fs.watch('./src', async () => {
452
+ console.log('🔄 Changes detected, restarting...');
453
+ await devServer.restart();
454
+ });
455
+ ```
456
+
457
+ ## API Reference 📚
458
+
459
+ ### Smartshell Class
460
+
461
+ | Method | Description | Security |
462
+ |--------|-------------|----------|
463
+ | `exec(command)` | Execute with shell | ⚠️ Vulnerable to injection |
464
+ | `execSpawn(cmd, args)` | Execute without shell | ✅ Safe for untrusted input |
465
+ | `execSilent(command)` | Execute without console output | ⚠️ Vulnerable to injection |
466
+ | `execStrict(command)` | Execute, throw on failure | ⚠️ Vulnerable to injection |
467
+ | `execStreaming(command)` | Stream output in real-time | ⚠️ Vulnerable to injection |
468
+ | `execSpawnStreaming(cmd, args)` | Stream without shell | ✅ Safe for untrusted input |
469
+ | `execInteractive(command)` | Interactive terminal mode | ⚠️ Vulnerable to injection |
470
+ | `execInteractiveControl(command)` | Programmatic input control | ⚠️ Vulnerable to injection |
471
+ | `execSpawnInteractiveControl(cmd, args)` | Programmatic control without shell | ✅ Safe for untrusted input |
472
+ | `execPassthrough(command)` | Connect stdin passthrough | ⚠️ Vulnerable to injection |
473
+ | `execInteractiveControlPty(command)` | PTY with programmatic control | ⚠️ Vulnerable to injection |
474
+ | `execStreamingInteractiveControlPty(command)` | PTY streaming with control | ⚠️ Vulnerable to injection |
475
+ | `execAndWaitForLine(cmd, regex, silent, opts)` | Wait for pattern match | ⚠️ Vulnerable to injection |
476
+
477
+ ### Result Interfaces
478
+
479
+ ```typescript
480
+ interface IExecResult {
481
+ exitCode: number; // Process exit code
482
+ stdout: string; // Standard output
483
+ signal?: NodeJS.Signals; // Termination signal
484
+ stderr?: string; // Error output
485
+ }
486
+
487
+ interface IExecResultStreaming {
488
+ childProcess: ChildProcess; // Node.js ChildProcess instance
489
+ finalPromise: Promise<IExecResult>; // Resolves when process exits
490
+ sendInput: (input: string) => Promise<void>;
491
+ sendLine: (line: string) => Promise<void>;
492
+ endInput: () => void;
493
+ kill: () => Promise<void>;
494
+ terminate: () => Promise<void>;
495
+ keyboardInterrupt: () => Promise<void>;
496
+ customSignal: (signal: NodeJS.Signals) => Promise<void>;
497
+ }
498
+
499
+ interface IExecResultInteractive extends IExecResult {
500
+ sendInput: (input: string) => Promise<void>;
501
+ sendLine: (line: string) => Promise<void>;
502
+ endInput: () => void;
503
+ finalPromise: Promise<IExecResult>;
504
+ }
505
+ ```
506
+
507
+ ### Options Interface
508
+
509
+ ```typescript
510
+ interface IExecOptions {
511
+ silent?: boolean; // Suppress console output
512
+ strict?: boolean; // Throw on non-zero exit
513
+ streaming?: boolean; // Return streaming interface
514
+ interactive?: boolean; // Interactive mode
515
+ passthrough?: boolean; // Connect stdin
516
+ interactiveControl?: boolean; // Programmatic input
517
+ usePty?: boolean; // Use pseudo-terminal
518
+ ptyShell?: string; // Custom PTY shell
519
+ ptyCols?: number; // PTY columns (default 120)
520
+ ptyRows?: number; // PTY rows (default 30)
521
+ ptyTerm?: string; // Terminal type (default 'xterm-256color')
522
+ maxBuffer?: number; // Max output buffer (bytes)
523
+ onData?: (chunk: Buffer | string) => void; // Data callback
524
+ timeout?: number; // Execution timeout (ms)
525
+ debug?: boolean; // Enable debug logging
526
+ env?: NodeJS.ProcessEnv; // Custom environment
527
+ signal?: AbortSignal; // Abort signal
528
+ }
529
+ ```
530
+
531
+ ## Security Guide
532
+
533
+ ### Command Injection Prevention
534
+
535
+ The standard `exec` methods use `shell: true`, which can lead to command injection vulnerabilities:
536
+
537
+ ```typescript
538
+ // ❌ DANGEROUS - Never do this with untrusted input
539
+ const userInput = "file.txt; rm -rf /";
540
+ await smartshell.exec(`cat ${userInput}`); // Will execute rm -rf /
541
+
542
+ // ✅ SAFE - Arguments are properly escaped
543
+ await smartshell.execSpawn('cat', [userInput]); // Will look for literal filename
544
+ ```
545
+
546
+ ### Best Practices
547
+
548
+ 1. **Always validate input**: Even with secure methods, validate user input
549
+ 2. **Use execSpawn for untrusted data**: Never pass user input to shell-based methods
550
+ 3. **Set resource limits**: Use `maxBuffer` and `timeout` for untrusted commands
551
+ 4. **Control environment**: Don't inherit all env vars for sensitive operations
552
+ 5. **Restrict signals**: Only allow specific signals from user input
553
+
554
+ ### Path Traversal Protection
115
555
 
116
556
  ```typescript
117
- (async () => {
118
- await smartShellInstance.execInteractive('npm init');
119
- })();
557
+ // VULNERABLE
558
+ const file = userInput; // Could be "../../../etc/passwd"
559
+ await shell.exec(`cat ${file}`);
560
+
561
+ // ✅ SECURE
562
+ const path = require('path');
563
+ const safePath = path.join('/allowed/directory', path.basename(userInput));
564
+ await shell.execSpawn('cat', [safePath]);
120
565
  ```
121
566
 
122
- ### Waiting for Specific Output
567
+ ## Tips & Best Practices 💎
568
+
569
+ 1. **Security first**: Use `execSpawn` for any untrusted input
570
+ 2. **Set resource limits**: Always use `maxBuffer` and `timeout` for untrusted commands
571
+ 3. **Choose the right executor**: Use `bash` for full features, `sh` for minimal overhead
572
+ 4. **Use strict mode for critical operations**: Ensures failures don't go unnoticed
573
+ 5. **Stream long-running processes**: Better UX and memory efficiency
574
+ 6. **Leverage silent modes**: When you only need to capture output
575
+ 7. **Handle errors gracefully**: Check both `exitCode` and `signal`
576
+ 8. **Clean up resources**: Streaming processes should be properly terminated
577
+ 9. **Control environment**: Don't inherit all env vars for sensitive operations
578
+ 10. **Enable debug mode**: For development and troubleshooting
579
+ 11. **Use PTY for terminal UIs**: When programs need real terminal features
580
+ 12. **Provide fallbacks**: Always handle PTY unavailability gracefully
581
+
582
+ ## Environment Customization
123
583
 
124
- To wait for a specific line before proceeding, you might use `execAndWaitForLine`. This is useful for waiting on a process to log a certain message:
584
+ Smartshell provides powerful environment management:
125
585
 
126
586
  ```typescript
127
- (async () => {
128
- await smartShellInstance.execAndWaitForLine('npm run watch', /Compilation complete./);
129
- console.log('The watch process has finished compiling.');
130
- })();
587
+ // Add custom source files
588
+ shell.shellEnv.addSourceFiles([
589
+ '/home/user/.custom_env',
590
+ './project.env.sh'
591
+ ]);
592
+
593
+ // Modify PATH
594
+ shell.shellEnv.pathDirArray.push('/custom/bin');
595
+ shell.shellEnv.pathDirArray.push('/usr/local/special');
596
+
597
+ // Your custom environment is ready
598
+ const result = await shell.exec('my-custom-command');
131
599
  ```
132
600
 
133
- Given the vast array of features offered by `@push.rocks/smartshell`, integrating shell operations into your TypeScript applications becomes both straightforward and powerful. By harnessing promises and async/await syntax, `smartshell` effectively streamlines shell interactions, making your code cleaner and more intuitive.
601
+ ## Shell Detection
602
+
603
+ Need to check if a command exists? We export the `which` utility:
604
+
605
+ ```typescript
606
+ import { which } from '@push.rocks/smartshell';
607
+
608
+ try {
609
+ const gitPath = await which('git');
610
+ console.log(`Git found at: ${gitPath}`);
611
+ } catch (error) {
612
+ console.log('Git is not installed');
613
+ }
614
+ ```
134
615
 
135
616
  ## License and Legal Information
136
617
 
@@ -149,4 +630,4 @@ Registered at District court Bremen HRB 35230 HB, Germany
149
630
 
150
631
  For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
151
632
 
152
- By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
633
+ By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.