bunosh 0.4.1 → 0.4.6
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 +236 -60
- package/bunosh.js +207 -65
- package/package.json +7 -5
- package/src/completion.js +15 -2
- package/src/formatters/console.js +10 -5
- package/src/formatters/factory.js +30 -0
- package/src/io.js +5 -0
- package/src/mcp-server.js +575 -0
- package/src/printer.js +1 -1
- package/src/program.js +564 -486
- package/src/task.js +72 -9
- package/src/tasks/ai.js +10 -2
- package/src/tasks/exec.js +31 -9
- package/src/tasks/fetch.js +11 -3
- package/src/tasks/shell.js +18 -4
- package/src/upgrade.js +279 -199
package/src/task.js
CHANGED
|
@@ -185,7 +185,7 @@ export async function task(name, fn, isSilent = false) {
|
|
|
185
185
|
printer.finish(name);
|
|
186
186
|
runningTasks.delete(taskInfo.id);
|
|
187
187
|
|
|
188
|
-
return result;
|
|
188
|
+
return TaskResult.success(result, { taskType: 'task' });
|
|
189
189
|
} catch (err) {
|
|
190
190
|
const endTime = Date.now();
|
|
191
191
|
const duration = endTime - taskInfo.startTime;
|
|
@@ -212,13 +212,14 @@ export async function task(name, fn, isSilent = false) {
|
|
|
212
212
|
process.exit(1);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
|
|
215
|
+
return TaskResult.fail(err.message, { taskType: 'task', error: err });
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
// Add try method to task function
|
|
220
220
|
task.try = tryTask;
|
|
221
221
|
|
|
222
|
+
|
|
222
223
|
export class SilentTaskWrapper {
|
|
223
224
|
constructor() {
|
|
224
225
|
this.silent = true;
|
|
@@ -246,9 +247,10 @@ export function silent() {
|
|
|
246
247
|
}
|
|
247
248
|
|
|
248
249
|
export class TaskResult {
|
|
249
|
-
constructor({ status, output }) {
|
|
250
|
+
constructor({ status, output, metadata = {} }) {
|
|
250
251
|
this.status = status;
|
|
251
252
|
this.output = output;
|
|
253
|
+
this._metadata = metadata;
|
|
252
254
|
}
|
|
253
255
|
|
|
254
256
|
get hasFailed() {
|
|
@@ -263,15 +265,76 @@ export class TaskResult {
|
|
|
263
265
|
return this.status === TaskStatus.WARNING;
|
|
264
266
|
}
|
|
265
267
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
+
async json() {
|
|
269
|
+
const taskType = this._metadata.taskType || 'unknown';
|
|
270
|
+
|
|
271
|
+
switch (taskType) {
|
|
272
|
+
case 'fetch':
|
|
273
|
+
// For fetch tasks, parse the response body as JSON
|
|
274
|
+
if (this._metadata.response) {
|
|
275
|
+
try {
|
|
276
|
+
return await this._metadata.response.json();
|
|
277
|
+
} catch (error) {
|
|
278
|
+
throw new Error(`Failed to parse fetch response as JSON: ${error.message}`);
|
|
279
|
+
}
|
|
280
|
+
} else if (typeof this.output === 'string') {
|
|
281
|
+
try {
|
|
282
|
+
return JSON.parse(this.output);
|
|
283
|
+
} catch (error) {
|
|
284
|
+
throw new Error(`Failed to parse fetch output as JSON: ${error.message}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
throw new Error('No JSON data available from fetch task');
|
|
288
|
+
|
|
289
|
+
case 'exec':
|
|
290
|
+
case 'shell':
|
|
291
|
+
// For exec/shell tasks, return structured output
|
|
292
|
+
const lines = this.output ? this.output.split('\n').filter(line => line.trim()) : [];
|
|
293
|
+
return {
|
|
294
|
+
stdout: this._metadata.stdout || this.output || '',
|
|
295
|
+
stderr: this._metadata.stderr || '',
|
|
296
|
+
exitCode: this._metadata.exitCode || (this.hasFailed ? 1 : 0),
|
|
297
|
+
lines
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
case 'ai':
|
|
301
|
+
// For AI tasks, return the structured output if available
|
|
302
|
+
if (typeof this.output === 'object') {
|
|
303
|
+
return this.output;
|
|
304
|
+
} else if (typeof this.output === 'string') {
|
|
305
|
+
try {
|
|
306
|
+
return JSON.parse(this.output);
|
|
307
|
+
} catch (error) {
|
|
308
|
+
// If it's not JSON, return as text property
|
|
309
|
+
return { text: this.output };
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return { text: this.output || '' };
|
|
313
|
+
|
|
314
|
+
default:
|
|
315
|
+
// For unknown task types, try to parse output as JSON or return as text
|
|
316
|
+
if (typeof this.output === 'object') {
|
|
317
|
+
return this.output;
|
|
318
|
+
} else if (typeof this.output === 'string') {
|
|
319
|
+
try {
|
|
320
|
+
return JSON.parse(this.output);
|
|
321
|
+
} catch (error) {
|
|
322
|
+
return { text: this.output };
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return { text: this.output || '' };
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
static fail(output = null, metadata = {}) {
|
|
330
|
+
return new TaskResult({ status: TaskStatus.FAIL, output, metadata });
|
|
268
331
|
}
|
|
269
332
|
|
|
270
|
-
static success(output = null) {
|
|
271
|
-
return new TaskResult({ status: TaskStatus.SUCCESS, output });
|
|
333
|
+
static success(output = null, metadata = {}) {
|
|
334
|
+
return new TaskResult({ status: TaskStatus.SUCCESS, output, metadata });
|
|
272
335
|
}
|
|
273
336
|
|
|
274
|
-
static warning(output = null) {
|
|
275
|
-
return new TaskResult({ status: TaskStatus.WARNING, output });
|
|
337
|
+
static warning(output = null, metadata = {}) {
|
|
338
|
+
return new TaskResult({ status: TaskStatus.WARNING, output, metadata });
|
|
276
339
|
}
|
|
277
340
|
}
|
package/src/tasks/ai.js
CHANGED
|
@@ -118,15 +118,23 @@ async function ai(prompt, outputFormat = null) {
|
|
|
118
118
|
process.stdout.write('\r' + ' '.repeat(20) + '\r');
|
|
119
119
|
const tokenInfo = usage ? `${usage.totalTokens} tokens` : '';
|
|
120
120
|
printer.finish(taskName, { tokenInfo });
|
|
121
|
+
|
|
122
|
+
const metadata = {
|
|
123
|
+
taskType: 'ai',
|
|
124
|
+
outputFormat: !!outputFormat,
|
|
125
|
+
usage,
|
|
126
|
+
prompt: cleanPrompt
|
|
127
|
+
};
|
|
128
|
+
|
|
121
129
|
finishTaskInfo(taskInfo, true, null, outputFormat ? JSON.stringify(result, null, 2) : result);
|
|
122
|
-
return TaskResult.success(result);
|
|
130
|
+
return TaskResult.success(result, metadata);
|
|
123
131
|
|
|
124
132
|
} catch (error) {
|
|
125
133
|
clearInterval(progressInterval);
|
|
126
134
|
process.stdout.write('\r' + ' '.repeat(20) + '\r');
|
|
127
135
|
printer.error(taskName, error);
|
|
128
136
|
finishTaskInfo(taskInfo, false, error, error.message);
|
|
129
|
-
return TaskResult.fail(error.message);
|
|
137
|
+
return TaskResult.fail(error.message, { taskType: 'ai' });
|
|
130
138
|
}
|
|
131
139
|
}
|
|
132
140
|
|
package/src/tasks/exec.js
CHANGED
|
@@ -62,6 +62,8 @@ export default function exec(strings, ...values) {
|
|
|
62
62
|
|
|
63
63
|
const decoder = new TextDecoder();
|
|
64
64
|
let output = '';
|
|
65
|
+
let stdout = '';
|
|
66
|
+
let stderr = '';
|
|
65
67
|
let finished = false;
|
|
66
68
|
|
|
67
69
|
// Process stdout
|
|
@@ -84,6 +86,7 @@ export default function exec(strings, ...values) {
|
|
|
84
86
|
if (line.trim()) {
|
|
85
87
|
printer.output(line);
|
|
86
88
|
output += line + '\n';
|
|
89
|
+
stdout += line + '\n';
|
|
87
90
|
}
|
|
88
91
|
}
|
|
89
92
|
}
|
|
@@ -91,6 +94,7 @@ export default function exec(strings, ...values) {
|
|
|
91
94
|
if (buffer.trim()) {
|
|
92
95
|
printer.output(buffer);
|
|
93
96
|
output += buffer + '\n';
|
|
97
|
+
stdout += buffer + '\n';
|
|
94
98
|
}
|
|
95
99
|
} finally {
|
|
96
100
|
reader.releaseLock();
|
|
@@ -117,6 +121,7 @@ export default function exec(strings, ...values) {
|
|
|
117
121
|
if (line.trim()) {
|
|
118
122
|
printer.output(line, true);
|
|
119
123
|
output += line + '\n';
|
|
124
|
+
stderr += line + '\n';
|
|
120
125
|
}
|
|
121
126
|
}
|
|
122
127
|
}
|
|
@@ -124,6 +129,7 @@ export default function exec(strings, ...values) {
|
|
|
124
129
|
if (buffer.trim()) {
|
|
125
130
|
printer.output(buffer, true);
|
|
126
131
|
output += buffer + '\n';
|
|
132
|
+
stderr += buffer + '\n';
|
|
127
133
|
}
|
|
128
134
|
} finally {
|
|
129
135
|
reader.releaseLock();
|
|
@@ -140,20 +146,27 @@ export default function exec(strings, ...values) {
|
|
|
140
146
|
finished = true;
|
|
141
147
|
const exitCode = parseInt(exitResult, 10);
|
|
142
148
|
|
|
149
|
+
const metadata = {
|
|
150
|
+
taskType: 'exec',
|
|
151
|
+
exitCode,
|
|
152
|
+
stdout: stdout.trim(),
|
|
153
|
+
stderr: stderr.trim()
|
|
154
|
+
};
|
|
155
|
+
|
|
143
156
|
if (exitCode === 0) {
|
|
144
157
|
printer.finish(cmd);
|
|
145
158
|
finishTaskInfo(taskInfo, true, null, output.trim());
|
|
146
|
-
resolve(TaskResult.success(output.trim()));
|
|
159
|
+
resolve(TaskResult.success(output.trim(), metadata));
|
|
147
160
|
} else {
|
|
148
161
|
const error = new Error(`Exit code: ${exitCode}`);
|
|
149
162
|
printer.error(cmd, null, { exitCode });
|
|
150
163
|
finishTaskInfo(taskInfo, false, error, output.trim());
|
|
151
|
-
resolve(TaskResult.fail(output.trim()));
|
|
164
|
+
resolve(TaskResult.fail(output.trim(), metadata));
|
|
152
165
|
}
|
|
153
166
|
} catch (error) {
|
|
154
167
|
printer.error(cmd, error);
|
|
155
168
|
finishTaskInfo(taskInfo, false, error, error.message);
|
|
156
|
-
resolve(TaskResult.fail(error.message));
|
|
169
|
+
resolve(TaskResult.fail(error.message, { taskType: 'exec' }));
|
|
157
170
|
}
|
|
158
171
|
});
|
|
159
172
|
|
|
@@ -182,35 +195,44 @@ async function nodeExec(cmd, extraInfo, printer, taskInfo) {
|
|
|
182
195
|
});
|
|
183
196
|
|
|
184
197
|
let output = '';
|
|
185
|
-
let
|
|
198
|
+
let stdout = '';
|
|
199
|
+
let stderr = '';
|
|
186
200
|
|
|
187
201
|
proc.stdout.on('data', (data) => {
|
|
188
202
|
const text = data.toString();
|
|
189
203
|
printer.output(text.trim());
|
|
190
204
|
output += text;
|
|
205
|
+
stdout += text;
|
|
191
206
|
});
|
|
192
207
|
|
|
193
208
|
proc.stderr.on('data', (data) => {
|
|
194
209
|
const text = data.toString();
|
|
195
210
|
printer.output(text.trim(), true);
|
|
196
|
-
|
|
211
|
+
output += text;
|
|
212
|
+
stderr += text;
|
|
197
213
|
});
|
|
198
214
|
|
|
199
215
|
proc.on('close', (code) => {
|
|
200
|
-
const combinedOutput = (output
|
|
216
|
+
const combinedOutput = (output).trim();
|
|
217
|
+
const metadata = {
|
|
218
|
+
taskType: 'exec',
|
|
219
|
+
exitCode: code,
|
|
220
|
+
stdout: stdout.trim(),
|
|
221
|
+
stderr: stderr.trim()
|
|
222
|
+
};
|
|
201
223
|
|
|
202
224
|
if (code === 0) {
|
|
203
225
|
printer.finish(cmd);
|
|
204
|
-
resolve(TaskResult.success(combinedOutput));
|
|
226
|
+
resolve(TaskResult.success(combinedOutput, metadata));
|
|
205
227
|
} else {
|
|
206
228
|
printer.error(cmd, new Error(`Exit code: ${code}`));
|
|
207
|
-
resolve(TaskResult.fail(combinedOutput));
|
|
229
|
+
resolve(TaskResult.fail(combinedOutput, metadata));
|
|
208
230
|
}
|
|
209
231
|
});
|
|
210
232
|
|
|
211
233
|
proc.on('error', (error) => {
|
|
212
234
|
printer.error(cmd, error);
|
|
213
|
-
resolve(TaskResult.fail(error.message));
|
|
235
|
+
resolve(TaskResult.fail(error.message, { taskType: 'exec' }));
|
|
214
236
|
});
|
|
215
237
|
});
|
|
216
238
|
}
|
package/src/tasks/fetch.js
CHANGED
|
@@ -28,21 +28,29 @@ export default async function httpFetch() {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
const metadata = {
|
|
32
|
+
taskType: 'fetch',
|
|
33
|
+
response: response.clone(), // Clone to allow json() to be called later
|
|
34
|
+
status: response.status,
|
|
35
|
+
statusText: response.statusText,
|
|
36
|
+
headers: Object.fromEntries(response.headers.entries())
|
|
37
|
+
};
|
|
38
|
+
|
|
31
39
|
if (response.ok) {
|
|
32
40
|
printer.finish(taskName, { status: `${response.status} ${response.statusText}` });
|
|
33
41
|
finishTaskInfo(taskInfo, true, null, output.trim());
|
|
34
|
-
return TaskResult.success(output.trim());
|
|
42
|
+
return TaskResult.success(output.trim(), metadata);
|
|
35
43
|
} else {
|
|
36
44
|
const errorMsg = `HTTP ${response.status} ${response.statusText}`;
|
|
37
45
|
const error = new Error(errorMsg);
|
|
38
46
|
printer.error(taskName, errorMsg);
|
|
39
47
|
finishTaskInfo(taskInfo, false, error, errorMsg);
|
|
40
|
-
return TaskResult.fail(errorMsg);
|
|
48
|
+
return TaskResult.fail(errorMsg, metadata);
|
|
41
49
|
}
|
|
42
50
|
|
|
43
51
|
} catch (error) {
|
|
44
52
|
printer.error(taskName, error);
|
|
45
53
|
finishTaskInfo(taskInfo, false, error, error.message);
|
|
46
|
-
return TaskResult.fail(error.message);
|
|
54
|
+
return TaskResult.fail(error.message, { taskType: 'fetch' });
|
|
47
55
|
}
|
|
48
56
|
}
|
package/src/tasks/shell.js
CHANGED
|
@@ -81,9 +81,16 @@ export default function shell(strings, ...values) {
|
|
|
81
81
|
|
|
82
82
|
const output = await result.text();
|
|
83
83
|
|
|
84
|
+
const metadata = {
|
|
85
|
+
taskType: 'shell',
|
|
86
|
+
exitCode: 0,
|
|
87
|
+
stdout: output.trim(),
|
|
88
|
+
stderr: ''
|
|
89
|
+
};
|
|
90
|
+
|
|
84
91
|
printer.finish(cmd);
|
|
85
92
|
finishTaskInfo(taskInfo, true, null, output.trim());
|
|
86
|
-
resolve(TaskResult.success(output.trim()));
|
|
93
|
+
resolve(TaskResult.success(output.trim(), metadata));
|
|
87
94
|
return;
|
|
88
95
|
|
|
89
96
|
} catch (shellError) {
|
|
@@ -118,22 +125,29 @@ export default function shell(strings, ...values) {
|
|
|
118
125
|
}
|
|
119
126
|
}
|
|
120
127
|
|
|
128
|
+
const metadata = {
|
|
129
|
+
taskType: 'shell',
|
|
130
|
+
exitCode: shellError.exitCode,
|
|
131
|
+
stdout: stdout.trim(),
|
|
132
|
+
stderr: stderr.trim()
|
|
133
|
+
};
|
|
134
|
+
|
|
121
135
|
const error = new Error(`Exit code: ${shellError.exitCode}`);
|
|
122
136
|
printer.error(cmd, null, { exitCode: shellError.exitCode });
|
|
123
137
|
finishTaskInfo(taskInfo, false, error, errorOutput);
|
|
124
|
-
resolve(TaskResult.fail(errorOutput));
|
|
138
|
+
resolve(TaskResult.fail(errorOutput, metadata));
|
|
125
139
|
return;
|
|
126
140
|
} else {
|
|
127
141
|
const errorMessage = shellError.message || shellError.toString();
|
|
128
142
|
printer.error(cmd, shellError);
|
|
129
143
|
finishTaskInfo(taskInfo, false, shellError, errorMessage);
|
|
130
|
-
resolve(TaskResult.fail(errorMessage));
|
|
144
|
+
resolve(TaskResult.fail(errorMessage, { taskType: 'shell' }));
|
|
131
145
|
}
|
|
132
146
|
}
|
|
133
147
|
} catch (error) {
|
|
134
148
|
printer.error(cmd, error);
|
|
135
149
|
finishTaskInfo(taskInfo, false, error, error.message);
|
|
136
|
-
resolve(TaskResult.fail(error.message));
|
|
150
|
+
resolve(TaskResult.fail(error.message, { taskType: 'shell' }));
|
|
137
151
|
}
|
|
138
152
|
});
|
|
139
153
|
|