lsh-framework 1.1.0 → 1.2.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 +70 -4
- package/dist/cli.js +104 -486
- package/dist/commands/doctor.js +427 -0
- package/dist/commands/init.js +371 -0
- package/dist/constants/api.js +94 -0
- package/dist/constants/commands.js +64 -0
- package/dist/constants/config.js +56 -0
- package/dist/constants/database.js +21 -0
- package/dist/constants/errors.js +79 -0
- package/dist/constants/index.js +28 -0
- package/dist/constants/paths.js +28 -0
- package/dist/constants/ui.js +73 -0
- package/dist/constants/validation.js +124 -0
- package/dist/daemon/lshd.js +11 -32
- package/dist/lib/daemon-client-helper.js +7 -4
- package/dist/lib/daemon-client.js +9 -2
- package/dist/lib/format-utils.js +163 -0
- package/dist/lib/job-manager.js +2 -1
- package/dist/lib/platform-utils.js +211 -0
- package/dist/lib/secrets-manager.js +11 -1
- package/dist/lib/string-utils.js +128 -0
- package/dist/services/daemon/daemon-registrar.js +3 -2
- package/dist/services/secrets/secrets.js +154 -30
- package/package.json +10 -74
- package/dist/app.js +0 -33
- package/dist/cicd/analytics.js +0 -261
- package/dist/cicd/auth.js +0 -269
- package/dist/cicd/cache-manager.js +0 -172
- package/dist/cicd/data-retention.js +0 -305
- package/dist/cicd/performance-monitor.js +0 -224
- package/dist/cicd/webhook-receiver.js +0 -640
- package/dist/commands/api.js +0 -346
- package/dist/commands/theme.js +0 -261
- package/dist/commands/zsh-import.js +0 -240
- package/dist/components/App.js +0 -1
- package/dist/components/Divider.js +0 -29
- package/dist/components/REPL.js +0 -43
- package/dist/components/Terminal.js +0 -232
- package/dist/components/UserInput.js +0 -30
- package/dist/daemon/api-server.js +0 -316
- package/dist/daemon/monitoring-api.js +0 -220
- package/dist/lib/api-error-handler.js +0 -185
- package/dist/lib/associative-arrays.js +0 -285
- package/dist/lib/base-api-server.js +0 -290
- package/dist/lib/brace-expansion.js +0 -160
- package/dist/lib/builtin-commands.js +0 -439
- package/dist/lib/executors/builtin-executor.js +0 -52
- package/dist/lib/extended-globbing.js +0 -411
- package/dist/lib/extended-parameter-expansion.js +0 -227
- package/dist/lib/interactive-shell.js +0 -460
- package/dist/lib/job-builtins.js +0 -582
- package/dist/lib/pathname-expansion.js +0 -216
- package/dist/lib/script-runner.js +0 -226
- package/dist/lib/shell-executor.js +0 -2504
- package/dist/lib/shell-parser.js +0 -958
- package/dist/lib/shell-types.js +0 -6
- package/dist/lib/shell.lib.js +0 -40
- package/dist/lib/theme-manager.js +0 -476
- package/dist/lib/variable-expansion.js +0 -385
- package/dist/lib/zsh-compatibility.js +0 -659
- package/dist/lib/zsh-import-manager.js +0 -707
- package/dist/lib/zsh-options.js +0 -328
- package/dist/pipeline/job-tracker.js +0 -491
- package/dist/pipeline/mcli-bridge.js +0 -309
- package/dist/pipeline/pipeline-service.js +0 -1119
- package/dist/pipeline/workflow-engine.js +0 -870
- package/dist/services/api/api.js +0 -58
- package/dist/services/api/auth.js +0 -35
- package/dist/services/api/config.js +0 -7
- package/dist/services/api/file.js +0 -22
- package/dist/services/shell/shell.js +0 -28
- package/dist/services/zapier.js +0 -16
- package/dist/simple-api-server.js +0 -148
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* POSIX Pathname Expansion Implementation
|
|
3
|
-
* Implements POSIX.1-2017 Section 2.13 Pathname Expansion (Globbing)
|
|
4
|
-
*/
|
|
5
|
-
import * as fs from 'fs';
|
|
6
|
-
import * as path from 'path';
|
|
7
|
-
export class PathnameExpander {
|
|
8
|
-
cwd;
|
|
9
|
-
constructor(cwd = process.cwd()) {
|
|
10
|
-
this.cwd = cwd;
|
|
11
|
-
}
|
|
12
|
-
async expandPathnames(pattern, options = {}) {
|
|
13
|
-
// If no glob characters, return as-is
|
|
14
|
-
if (!this.containsGlobChars(pattern)) {
|
|
15
|
-
return [pattern];
|
|
16
|
-
}
|
|
17
|
-
// Handle tilde expansion first
|
|
18
|
-
const expandedPattern = this.expandTilde(pattern);
|
|
19
|
-
// Perform pathname expansion
|
|
20
|
-
return this.glob(expandedPattern, options);
|
|
21
|
-
}
|
|
22
|
-
containsGlobChars(str) {
|
|
23
|
-
// Check for unescaped glob characters
|
|
24
|
-
return /[*?[\]{}~]/.test(str);
|
|
25
|
-
}
|
|
26
|
-
expandTilde(pattern) {
|
|
27
|
-
if (pattern.startsWith('~/')) {
|
|
28
|
-
const homeDir = process.env.HOME || '/';
|
|
29
|
-
return path.join(homeDir, pattern.slice(2));
|
|
30
|
-
}
|
|
31
|
-
if (pattern === '~') {
|
|
32
|
-
return process.env.HOME || '/';
|
|
33
|
-
}
|
|
34
|
-
return pattern;
|
|
35
|
-
}
|
|
36
|
-
async glob(pattern, options) {
|
|
37
|
-
const baseCwd = options.cwd || this.cwd;
|
|
38
|
-
// Handle absolute vs relative paths
|
|
39
|
-
const isAbsolute = path.isAbsolute(pattern);
|
|
40
|
-
const searchBase = isAbsolute ? '/' : baseCwd;
|
|
41
|
-
const relativePath = isAbsolute ? pattern.slice(1) : pattern;
|
|
42
|
-
// Split pattern into segments
|
|
43
|
-
const segments = relativePath.split('/').filter(seg => seg.length > 0);
|
|
44
|
-
if (segments.length === 0) {
|
|
45
|
-
return [searchBase];
|
|
46
|
-
}
|
|
47
|
-
// Start recursive matching
|
|
48
|
-
const results = await this.matchSegments(searchBase, segments, options);
|
|
49
|
-
// Sort results for consistent output
|
|
50
|
-
return results.sort();
|
|
51
|
-
}
|
|
52
|
-
async matchSegments(currentPath, remainingSegments, options) {
|
|
53
|
-
if (remainingSegments.length === 0) {
|
|
54
|
-
return [currentPath];
|
|
55
|
-
}
|
|
56
|
-
const [currentSegment, ...restSegments] = remainingSegments;
|
|
57
|
-
const results = [];
|
|
58
|
-
try {
|
|
59
|
-
const entries = await fs.promises.readdir(currentPath, { withFileTypes: true });
|
|
60
|
-
for (const entry of entries) {
|
|
61
|
-
// Skip hidden files unless explicitly included
|
|
62
|
-
if (!options.includeHidden && entry.name.startsWith('.')) {
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
// Check if this entry matches the current segment pattern
|
|
66
|
-
if (this.matchSegment(entry.name, currentSegment)) {
|
|
67
|
-
const fullPath = path.join(currentPath, entry.name);
|
|
68
|
-
if (restSegments.length === 0) {
|
|
69
|
-
// This is the final segment, add to results
|
|
70
|
-
results.push(fullPath);
|
|
71
|
-
}
|
|
72
|
-
else if (entry.isDirectory()) {
|
|
73
|
-
// Recurse into directory for remaining segments
|
|
74
|
-
const subResults = await this.matchSegments(fullPath, restSegments, options);
|
|
75
|
-
results.push(...subResults);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
catch (_error) {
|
|
81
|
-
// Directory doesn't exist or not readable, return empty
|
|
82
|
-
return [];
|
|
83
|
-
}
|
|
84
|
-
return results;
|
|
85
|
-
}
|
|
86
|
-
matchSegment(filename, pattern) {
|
|
87
|
-
// Convert glob pattern to regex
|
|
88
|
-
const regex = this.patternToRegex(pattern);
|
|
89
|
-
return regex.test(filename);
|
|
90
|
-
}
|
|
91
|
-
patternToRegex(pattern) {
|
|
92
|
-
let regexStr = '';
|
|
93
|
-
let i = 0;
|
|
94
|
-
while (i < pattern.length) {
|
|
95
|
-
const char = pattern[i];
|
|
96
|
-
switch (char) {
|
|
97
|
-
case '*':
|
|
98
|
-
regexStr += '.*';
|
|
99
|
-
break;
|
|
100
|
-
case '?':
|
|
101
|
-
regexStr += '.';
|
|
102
|
-
break;
|
|
103
|
-
case '[': {
|
|
104
|
-
// Character class
|
|
105
|
-
const closeIdx = this.findClosingBracket(pattern, i);
|
|
106
|
-
if (closeIdx === -1) {
|
|
107
|
-
// Invalid bracket, treat as literal
|
|
108
|
-
regexStr += '\\[';
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
let charClass = pattern.slice(i + 1, closeIdx);
|
|
112
|
-
// Handle negation
|
|
113
|
-
if (charClass.startsWith('!') || charClass.startsWith('^')) {
|
|
114
|
-
charClass = '^' + charClass.slice(1);
|
|
115
|
-
}
|
|
116
|
-
// Handle character ranges and classes
|
|
117
|
-
charClass = this.processCharacterClass(charClass);
|
|
118
|
-
regexStr += '[' + charClass + ']';
|
|
119
|
-
i = closeIdx;
|
|
120
|
-
}
|
|
121
|
-
break;
|
|
122
|
-
}
|
|
123
|
-
case '{': {
|
|
124
|
-
// Brace expansion - simplified implementation
|
|
125
|
-
const braceEnd = this.findClosingBrace(pattern, i);
|
|
126
|
-
if (braceEnd === -1) {
|
|
127
|
-
regexStr += '\\{';
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
const braceContent = pattern.slice(i + 1, braceEnd);
|
|
131
|
-
const alternatives = braceContent.split(',');
|
|
132
|
-
regexStr += '(' + alternatives.map(alt => this.escapeRegex(alt)).join('|') + ')';
|
|
133
|
-
i = braceEnd;
|
|
134
|
-
}
|
|
135
|
-
break;
|
|
136
|
-
}
|
|
137
|
-
case '\\':
|
|
138
|
-
// Escape next character
|
|
139
|
-
if (i + 1 < pattern.length) {
|
|
140
|
-
regexStr += '\\' + pattern[i + 1];
|
|
141
|
-
i++;
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
regexStr += '\\\\';
|
|
145
|
-
}
|
|
146
|
-
break;
|
|
147
|
-
default:
|
|
148
|
-
// Literal character - escape regex special chars
|
|
149
|
-
regexStr += this.escapeRegex(char);
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
i++;
|
|
153
|
-
}
|
|
154
|
-
return new RegExp('^' + regexStr + '$');
|
|
155
|
-
}
|
|
156
|
-
findClosingBracket(str, startIdx) {
|
|
157
|
-
let depth = 1;
|
|
158
|
-
for (let i = startIdx + 1; i < str.length; i++) {
|
|
159
|
-
if (str[i] === '[')
|
|
160
|
-
depth++;
|
|
161
|
-
else if (str[i] === ']') {
|
|
162
|
-
depth--;
|
|
163
|
-
if (depth === 0)
|
|
164
|
-
return i;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
return -1;
|
|
168
|
-
}
|
|
169
|
-
findClosingBrace(str, startIdx) {
|
|
170
|
-
let depth = 1;
|
|
171
|
-
for (let i = startIdx + 1; i < str.length; i++) {
|
|
172
|
-
if (str[i] === '{')
|
|
173
|
-
depth++;
|
|
174
|
-
else if (str[i] === '}') {
|
|
175
|
-
depth--;
|
|
176
|
-
if (depth === 0)
|
|
177
|
-
return i;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
return -1;
|
|
181
|
-
}
|
|
182
|
-
processCharacterClass(charClass) {
|
|
183
|
-
// Handle POSIX character classes like [:alpha:]
|
|
184
|
-
charClass = charClass.replace(/\[:alpha:\]/g, 'a-zA-Z');
|
|
185
|
-
charClass = charClass.replace(/\[:digit:\]/g, '0-9');
|
|
186
|
-
charClass = charClass.replace(/\[:alnum:\]/g, 'a-zA-Z0-9');
|
|
187
|
-
charClass = charClass.replace(/\[:lower:\]/g, 'a-z');
|
|
188
|
-
charClass = charClass.replace(/\[:upper:\]/g, 'A-Z');
|
|
189
|
-
charClass = charClass.replace(/\[:space:\]/g, ' \\t\\n\\r\\f\\v');
|
|
190
|
-
charClass = charClass.replace(/\[:blank:\]/g, ' \\t');
|
|
191
|
-
charClass = charClass.replace(/\[:punct:\]/g, '!-/:-@\\[-`{-~');
|
|
192
|
-
charClass = charClass.replace(/\[:cntrl:\]/g, '\\x00-\\x1F\\x7F');
|
|
193
|
-
charClass = charClass.replace(/\[:print:\]/g, '\\x20-\\x7E');
|
|
194
|
-
charClass = charClass.replace(/\[:graph:\]/g, '\\x21-\\x7E');
|
|
195
|
-
charClass = charClass.replace(/\[:xdigit:\]/g, '0-9A-Fa-f');
|
|
196
|
-
return charClass;
|
|
197
|
-
}
|
|
198
|
-
escapeRegex(str) {
|
|
199
|
-
return str.replace(/[.+^$()|[\]{}\\]/g, '\\$&');
|
|
200
|
-
}
|
|
201
|
-
// Utility method for expanding multiple patterns
|
|
202
|
-
async expandMultiplePatterns(patterns, options = {}) {
|
|
203
|
-
const results = [];
|
|
204
|
-
for (const pattern of patterns) {
|
|
205
|
-
const expanded = await this.expandPathnames(pattern, options);
|
|
206
|
-
results.push(...expanded);
|
|
207
|
-
}
|
|
208
|
-
// Remove duplicates and sort
|
|
209
|
-
return [...new Set(results)].sort();
|
|
210
|
-
}
|
|
211
|
-
// Check if a pattern would match any files (useful for error reporting)
|
|
212
|
-
async hasMatches(pattern, options = {}) {
|
|
213
|
-
const matches = await this.expandPathnames(pattern, options);
|
|
214
|
-
return matches.length > 0 && matches[0] !== pattern;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shell Script Runner
|
|
3
|
-
* Executes shell scripts with LSH
|
|
4
|
-
*/
|
|
5
|
-
import { ShellExecutor } from './shell-executor.js';
|
|
6
|
-
import { parseShellCommand } from './shell-parser.js';
|
|
7
|
-
import * as fs from 'fs';
|
|
8
|
-
export class ScriptRunner {
|
|
9
|
-
executor;
|
|
10
|
-
constructor(options) {
|
|
11
|
-
this.executor = new ShellExecutor({
|
|
12
|
-
cwd: options?.cwd || process.cwd(),
|
|
13
|
-
env: {
|
|
14
|
-
...Object.fromEntries(Object.entries(process.env).filter(([_, v]) => v !== undefined)),
|
|
15
|
-
...options?.env
|
|
16
|
-
},
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Execute a shell script file
|
|
21
|
-
*/
|
|
22
|
-
async executeScript(scriptPath, options = {}) {
|
|
23
|
-
try {
|
|
24
|
-
// Read script file
|
|
25
|
-
const scriptContent = fs.readFileSync(scriptPath, 'utf8');
|
|
26
|
-
// Set up script context
|
|
27
|
-
if (options.args) {
|
|
28
|
-
this.executor.setPositionalParams(options.args);
|
|
29
|
-
}
|
|
30
|
-
// Parse and execute script
|
|
31
|
-
const ast = parseShellCommand(scriptContent);
|
|
32
|
-
const result = await this.executor.execute(ast);
|
|
33
|
-
return {
|
|
34
|
-
success: result.success,
|
|
35
|
-
exitCode: result.exitCode,
|
|
36
|
-
output: result.stdout,
|
|
37
|
-
errors: result.stderr,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
return {
|
|
42
|
-
success: false,
|
|
43
|
-
exitCode: 1,
|
|
44
|
-
output: '',
|
|
45
|
-
errors: `Script execution failed: ${error.message}`,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Execute shell commands from string
|
|
51
|
-
*/
|
|
52
|
-
async executeCommands(commands, options = {}) {
|
|
53
|
-
try {
|
|
54
|
-
// Set up context
|
|
55
|
-
if (options.args) {
|
|
56
|
-
this.executor.setPositionalParams(options.args);
|
|
57
|
-
}
|
|
58
|
-
// Parse and execute commands
|
|
59
|
-
const ast = parseShellCommand(commands);
|
|
60
|
-
const result = await this.executor.execute(ast);
|
|
61
|
-
return {
|
|
62
|
-
success: result.success,
|
|
63
|
-
exitCode: result.exitCode,
|
|
64
|
-
output: result.stdout,
|
|
65
|
-
errors: result.stderr,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
catch (error) {
|
|
69
|
-
return {
|
|
70
|
-
success: false,
|
|
71
|
-
exitCode: 1,
|
|
72
|
-
output: '',
|
|
73
|
-
errors: `Command execution failed: ${error.message}`,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Execute script with shebang detection
|
|
79
|
-
*/
|
|
80
|
-
async executeWithShebang(scriptPath, options = {}) {
|
|
81
|
-
try {
|
|
82
|
-
const scriptContent = fs.readFileSync(scriptPath, 'utf8');
|
|
83
|
-
// Check for shebang
|
|
84
|
-
const lines = scriptContent.split('\n');
|
|
85
|
-
const firstLine = lines[0];
|
|
86
|
-
if (firstLine.startsWith('#!')) {
|
|
87
|
-
const interpreter = firstLine.substring(2).trim();
|
|
88
|
-
// Handle different interpreters
|
|
89
|
-
if (interpreter.includes('sh') || interpreter.includes('bash') || interpreter.includes('zsh')) {
|
|
90
|
-
// Execute as shell script
|
|
91
|
-
return await this.executeScript(scriptPath, options);
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
// For other interpreters, delegate to system
|
|
95
|
-
return await this.executeSystemScript(scriptPath, options);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
// No shebang, execute as shell script
|
|
100
|
-
return await this.executeScript(scriptPath, options);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
catch (error) {
|
|
104
|
-
return {
|
|
105
|
-
success: false,
|
|
106
|
-
exitCode: 1,
|
|
107
|
-
output: '',
|
|
108
|
-
errors: `Script execution failed: ${error.message}`,
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Execute system script (fallback)
|
|
114
|
-
*/
|
|
115
|
-
async executeSystemScript(scriptPath, options) {
|
|
116
|
-
const { spawn } = await import('child_process');
|
|
117
|
-
return new Promise((resolve) => {
|
|
118
|
-
const child = spawn('sh', [scriptPath], {
|
|
119
|
-
cwd: options.cwd || process.cwd(),
|
|
120
|
-
env: { ...process.env, ...options.env },
|
|
121
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
122
|
-
});
|
|
123
|
-
let stdout = '';
|
|
124
|
-
let stderr = '';
|
|
125
|
-
child.stdout?.on('data', (data) => {
|
|
126
|
-
stdout += data.toString();
|
|
127
|
-
});
|
|
128
|
-
child.stderr?.on('data', (data) => {
|
|
129
|
-
stderr += data.toString();
|
|
130
|
-
});
|
|
131
|
-
child.on('close', (code) => {
|
|
132
|
-
resolve({
|
|
133
|
-
success: code === 0,
|
|
134
|
-
exitCode: code || 0,
|
|
135
|
-
output: stdout,
|
|
136
|
-
errors: stderr,
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
child.on('error', (error) => {
|
|
140
|
-
resolve({
|
|
141
|
-
success: false,
|
|
142
|
-
exitCode: 1,
|
|
143
|
-
output: '',
|
|
144
|
-
errors: error.message,
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Validate shell script syntax
|
|
151
|
-
*/
|
|
152
|
-
validateScript(scriptPath) {
|
|
153
|
-
try {
|
|
154
|
-
const scriptContent = fs.readFileSync(scriptPath, 'utf8');
|
|
155
|
-
const _ast = parseShellCommand(scriptContent);
|
|
156
|
-
// Basic validation - if parsing succeeds, syntax is valid
|
|
157
|
-
return { valid: true, errors: [] };
|
|
158
|
-
}
|
|
159
|
-
catch (error) {
|
|
160
|
-
return { valid: false, errors: [error.message] };
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Get script information
|
|
165
|
-
*/
|
|
166
|
-
getScriptInfo(scriptPath) {
|
|
167
|
-
try {
|
|
168
|
-
const stats = fs.statSync(scriptPath);
|
|
169
|
-
const content = fs.readFileSync(scriptPath, 'utf8');
|
|
170
|
-
const firstLine = content.split('\n')[0];
|
|
171
|
-
let shebang;
|
|
172
|
-
let interpreter;
|
|
173
|
-
if (firstLine.startsWith('#!')) {
|
|
174
|
-
shebang = firstLine;
|
|
175
|
-
interpreter = firstLine.substring(2).trim();
|
|
176
|
-
}
|
|
177
|
-
return {
|
|
178
|
-
exists: true,
|
|
179
|
-
executable: stats.mode & 0o111 ? true : false,
|
|
180
|
-
size: stats.size,
|
|
181
|
-
shebang,
|
|
182
|
-
interpreter,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
catch {
|
|
186
|
-
return {
|
|
187
|
-
exists: false,
|
|
188
|
-
executable: false,
|
|
189
|
-
size: 0,
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Make script executable
|
|
195
|
-
*/
|
|
196
|
-
makeExecutable(scriptPath) {
|
|
197
|
-
try {
|
|
198
|
-
const stats = fs.statSync(scriptPath);
|
|
199
|
-
fs.chmodSync(scriptPath, stats.mode | 0o111);
|
|
200
|
-
return true;
|
|
201
|
-
}
|
|
202
|
-
catch {
|
|
203
|
-
return false;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Create a simple shell script
|
|
208
|
-
*/
|
|
209
|
-
createScript(scriptPath, content, makeExecutable = true) {
|
|
210
|
-
try {
|
|
211
|
-
// Add shebang if not present
|
|
212
|
-
if (!content.startsWith('#!')) {
|
|
213
|
-
content = '#!/bin/sh\n' + content;
|
|
214
|
-
}
|
|
215
|
-
fs.writeFileSync(scriptPath, content, 'utf8');
|
|
216
|
-
if (makeExecutable) {
|
|
217
|
-
this.makeExecutable(scriptPath);
|
|
218
|
-
}
|
|
219
|
-
return true;
|
|
220
|
-
}
|
|
221
|
-
catch {
|
|
222
|
-
return false;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
export default ScriptRunner;
|