bunosh 0.5.0 → 0.5.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 +0 -1
- package/bunosh.js +0 -39
- package/index.js +4 -3
- package/package.json +18 -2
- package/src/formatters/console.js +5 -1
- package/src/io.js +0 -5
- package/src/printer.js +29 -9
- package/src/program.js +46 -12
- package/src/task.js +8 -1
- package/src/tasks/exec.js +4 -248
- package/src/tasks/fetch.js +2 -1
- package/src/tasks/shell.js +194 -119
- package/src/upgrade.js +135 -30
- package/src/mcp-server.js +0 -575
package/src/tasks/shell.js
CHANGED
|
@@ -1,66 +1,34 @@
|
|
|
1
|
-
import { TaskResult, createTaskInfo, finishTaskInfo, getCurrentTaskId, runningTasks } from
|
|
2
|
-
import Printer from
|
|
1
|
+
import { TaskResult, createTaskInfo, finishTaskInfo, getCurrentTaskId, runningTasks } from '../task.js';
|
|
2
|
+
import Printer from '../printer.js';
|
|
3
3
|
|
|
4
4
|
const isBun = typeof Bun !== 'undefined' && typeof Bun.spawn === 'function';
|
|
5
5
|
|
|
6
6
|
export default function shell(strings, ...values) {
|
|
7
|
-
let envs = null;
|
|
8
|
-
let cwd = null;
|
|
9
|
-
|
|
10
7
|
// Check if called as regular function instead of template literal
|
|
11
8
|
if (!Array.isArray(strings)) {
|
|
12
|
-
// If first argument is a string, treat it as the command
|
|
13
9
|
if (typeof strings === 'string') {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
console.log('Note: shell() with string argument falls back to exec()');
|
|
17
|
-
const cmdPromise = (async () => {
|
|
18
|
-
const { default: exec } = await import("./exec.js");
|
|
19
|
-
let execPromise = exec(strings);
|
|
20
|
-
if (envs) execPromise = execPromise.env(envs);
|
|
21
|
-
if (cwd) execPromise = execPromise.cwd(cwd);
|
|
22
|
-
return execPromise;
|
|
23
|
-
})();
|
|
24
|
-
|
|
25
|
-
// Add .env and .cwd methods
|
|
26
|
-
cmdPromise.env = (newEnvs) => {
|
|
27
|
-
envs = newEnvs;
|
|
28
|
-
return cmdPromise;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
cmdPromise.cwd = (newCwd) => {
|
|
32
|
-
cwd = newCwd;
|
|
33
|
-
return cmdPromise;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
return cmdPromise;
|
|
10
|
+
strings = [strings];
|
|
11
|
+
values = [];
|
|
37
12
|
} else {
|
|
38
|
-
throw new Error('shell() must be called as a template literal: shell`command`');
|
|
13
|
+
throw new Error('shell() must be called as a template literal: shell`command` or shell("command")');
|
|
39
14
|
}
|
|
40
15
|
}
|
|
41
|
-
|
|
16
|
+
|
|
42
17
|
const cmd = strings.reduce((accumulator, str, i) => {
|
|
43
|
-
return accumulator + str + (values[i] ||
|
|
44
|
-
},
|
|
18
|
+
return accumulator + str + (values[i] || '');
|
|
19
|
+
}, '');
|
|
45
20
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (cwd) extraInfo.cwd = cwd;
|
|
49
|
-
if (envs) extraInfo.env = envs;
|
|
21
|
+
let envs = null;
|
|
22
|
+
let cwd = null;
|
|
50
23
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const result = await execPromise;
|
|
57
|
-
resolve(result);
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
24
|
+
const cmdPromise = new Promise(async (resolve) => {
|
|
25
|
+
// Wait for the next event loop tick to ensure .env() and .cwd() have been called
|
|
26
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
27
|
+
|
|
28
|
+
const currentTaskId = getCurrentTaskId();
|
|
60
29
|
|
|
61
30
|
// Check if parent task is silent
|
|
62
31
|
let isParentSilent = false;
|
|
63
|
-
const currentTaskId = getCurrentTaskId();
|
|
64
32
|
if (currentTaskId) {
|
|
65
33
|
const parentTask = runningTasks.get(currentTaskId);
|
|
66
34
|
if (parentTask && parentTask.isSilent) {
|
|
@@ -68,91 +36,144 @@ export default function shell(strings, ...values) {
|
|
|
68
36
|
}
|
|
69
37
|
}
|
|
70
38
|
|
|
71
|
-
const
|
|
72
|
-
|
|
39
|
+
const extraInfo = {};
|
|
40
|
+
if (cwd) extraInfo.cwd = cwd;
|
|
41
|
+
if (envs) extraInfo.env = envs;
|
|
42
|
+
|
|
43
|
+
const taskInfo = createTaskInfo(cmd, currentTaskId, isParentSilent);
|
|
44
|
+
const printer = new Printer('shell', taskInfo.id);
|
|
73
45
|
printer.start(cmd, extraInfo);
|
|
74
46
|
|
|
75
47
|
try {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
48
|
+
if (global.disableBunForTesting || !isBun) {
|
|
49
|
+
const result = await nodeShell(cmd, extraInfo, printer);
|
|
50
|
+
if (result.status === 'success') {
|
|
51
|
+
finishTaskInfo(taskInfo, true, null, result.output);
|
|
52
|
+
resolve(result);
|
|
53
|
+
} else {
|
|
54
|
+
finishTaskInfo(taskInfo, false, new Error(result.output), result.output);
|
|
55
|
+
resolve(result);
|
|
56
|
+
}
|
|
57
|
+
return;
|
|
86
58
|
}
|
|
87
59
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
60
|
+
// Bun implementation with real-time streaming
|
|
61
|
+
const needsShell = cmd.includes('|') || cmd.includes('>') || cmd.includes('<') || cmd.includes('&&') || cmd.includes('||') || cmd.includes("'") || cmd.includes('"') || cmd.includes(';') || cmd.includes('$') || cmd.includes('`') || cmd.includes('\n');
|
|
62
|
+
|
|
63
|
+
const { spawn } = Bun;
|
|
64
|
+
const proc = spawn({
|
|
65
|
+
cmd: needsShell ? ['/bin/sh', '-c', cmd] : cmd.trim().split(/\s+/),
|
|
66
|
+
cwd: cwd || process.cwd(),
|
|
67
|
+
env: {
|
|
68
|
+
...(envs ? { ...process.env, ...envs } : process.env),
|
|
69
|
+
},
|
|
70
|
+
stdout: "pipe",
|
|
71
|
+
stderr: "pipe",
|
|
72
|
+
stdin: "ignore"
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const decoder = new TextDecoder();
|
|
76
|
+
let output = '';
|
|
77
|
+
let stdout = '';
|
|
78
|
+
let stderr = '';
|
|
79
|
+
let finished = false;
|
|
80
|
+
|
|
81
|
+
// Process stdout
|
|
82
|
+
const readStdout = async () => {
|
|
83
|
+
const reader = proc.stdout.getReader();
|
|
84
|
+
let buffer = '';
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
while (!finished) {
|
|
88
|
+
const { done, value } = await reader.read();
|
|
89
|
+
if (done) break;
|
|
90
|
+
|
|
91
|
+
const text = decoder.decode(value, { stream: true });
|
|
92
|
+
buffer += text;
|
|
93
|
+
|
|
94
|
+
const lines = buffer.split('\n');
|
|
95
|
+
buffer = lines.pop();
|
|
96
|
+
|
|
97
|
+
for (const line of lines) {
|
|
98
|
+
if (line.trim()) {
|
|
99
|
+
printer.output(line);
|
|
100
|
+
output += line + '\n';
|
|
101
|
+
stdout += line + '\n';
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (buffer.trim()) {
|
|
107
|
+
printer.output(buffer);
|
|
108
|
+
output += buffer + '\n';
|
|
109
|
+
stdout += buffer + '\n';
|
|
110
|
+
}
|
|
111
|
+
} finally {
|
|
112
|
+
reader.releaseLock();
|
|
122
113
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// Process stderr
|
|
117
|
+
const readStderr = async () => {
|
|
118
|
+
const reader = proc.stderr.getReader();
|
|
119
|
+
let buffer = '';
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
while (!finished) {
|
|
123
|
+
const { done, value } = await reader.read();
|
|
124
|
+
if (done) break;
|
|
125
|
+
|
|
126
|
+
const text = decoder.decode(value, { stream: true });
|
|
127
|
+
buffer += text;
|
|
128
|
+
|
|
129
|
+
const lines = buffer.split('\n');
|
|
130
|
+
buffer = lines.pop();
|
|
131
|
+
|
|
131
132
|
for (const line of lines) {
|
|
132
133
|
if (line.trim()) {
|
|
133
134
|
printer.output(line, true);
|
|
135
|
+
output += line + '\n';
|
|
136
|
+
stderr += line + '\n';
|
|
134
137
|
}
|
|
135
138
|
}
|
|
136
139
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const error = new Error(`Exit code: ${shellError.exitCode}`);
|
|
146
|
-
printer.error(cmd, null, { exitCode: shellError.exitCode });
|
|
147
|
-
finishTaskInfo(taskInfo, false, error, errorOutput);
|
|
148
|
-
resolve(TaskResult.fail(errorOutput, metadata));
|
|
149
|
-
return;
|
|
150
|
-
} else {
|
|
151
|
-
const errorMessage = shellError.message || shellError.toString();
|
|
152
|
-
printer.error(cmd, shellError);
|
|
153
|
-
finishTaskInfo(taskInfo, false, shellError, errorMessage);
|
|
154
|
-
resolve(TaskResult.fail(errorMessage, { taskType: 'shell' }));
|
|
140
|
+
|
|
141
|
+
if (buffer.trim()) {
|
|
142
|
+
printer.output(buffer, true);
|
|
143
|
+
output += buffer + '\n';
|
|
144
|
+
stderr += buffer + '\n';
|
|
145
|
+
}
|
|
146
|
+
} finally {
|
|
147
|
+
reader.releaseLock();
|
|
155
148
|
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// Start reading both streams
|
|
152
|
+
const [, , exitResult] = await Promise.all([
|
|
153
|
+
readStdout(),
|
|
154
|
+
readStderr(),
|
|
155
|
+
proc.exited
|
|
156
|
+
]);
|
|
157
|
+
|
|
158
|
+
finished = true;
|
|
159
|
+
const exitCode = parseInt(exitResult, 10);
|
|
160
|
+
|
|
161
|
+
const metadata = {
|
|
162
|
+
taskType: 'shell',
|
|
163
|
+
exitCode,
|
|
164
|
+
stdout: stdout.trim(),
|
|
165
|
+
stderr: stderr.trim()
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
if (exitCode === 0) {
|
|
169
|
+
printer.finish(cmd);
|
|
170
|
+
finishTaskInfo(taskInfo, true, null, output.trim());
|
|
171
|
+
resolve(TaskResult.success(output.trim(), metadata));
|
|
172
|
+
} else {
|
|
173
|
+
const error = new Error(`Exit code: ${exitCode}`);
|
|
174
|
+
printer.error(cmd, null, { exitCode });
|
|
175
|
+
finishTaskInfo(taskInfo, false, error, output.trim());
|
|
176
|
+
resolve(TaskResult.fail(output.trim(), metadata));
|
|
156
177
|
}
|
|
157
178
|
} catch (error) {
|
|
158
179
|
printer.error(cmd, error);
|
|
@@ -173,3 +194,57 @@ export default function shell(strings, ...values) {
|
|
|
173
194
|
|
|
174
195
|
return cmdPromise;
|
|
175
196
|
}
|
|
197
|
+
|
|
198
|
+
async function nodeShell(cmd, extraInfo, printer) {
|
|
199
|
+
// Node.js fallback - simple execution without real-time output
|
|
200
|
+
const { spawn } = await import('child_process');
|
|
201
|
+
|
|
202
|
+
return new Promise((resolve) => {
|
|
203
|
+
const proc = spawn('sh', ['-c', cmd], {
|
|
204
|
+
cwd: extraInfo.cwd || process.cwd(),
|
|
205
|
+
env: extraInfo.env ? { ...process.env, ...extraInfo.env } : process.env,
|
|
206
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
let output = '';
|
|
210
|
+
let stdout = '';
|
|
211
|
+
let stderr = '';
|
|
212
|
+
|
|
213
|
+
proc.stdout.on('data', (data) => {
|
|
214
|
+
const text = data.toString();
|
|
215
|
+
printer.output(text.trim());
|
|
216
|
+
output += text;
|
|
217
|
+
stdout += text;
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
proc.stderr.on('data', (data) => {
|
|
221
|
+
const text = data.toString();
|
|
222
|
+
printer.output(text.trim(), true);
|
|
223
|
+
output += text;
|
|
224
|
+
stderr += text;
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
proc.on('close', (code) => {
|
|
228
|
+
const combinedOutput = output.trim();
|
|
229
|
+
const metadata = {
|
|
230
|
+
taskType: 'shell',
|
|
231
|
+
exitCode: code,
|
|
232
|
+
stdout: stdout.trim(),
|
|
233
|
+
stderr: stderr.trim()
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
if (code === 0) {
|
|
237
|
+
printer.finish(cmd);
|
|
238
|
+
resolve(TaskResult.success(combinedOutput, metadata));
|
|
239
|
+
} else {
|
|
240
|
+
printer.error(cmd, new Error(`Exit code: ${code}`));
|
|
241
|
+
resolve(TaskResult.fail(combinedOutput, metadata));
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
proc.on('error', (error) => {
|
|
246
|
+
printer.error(cmd, error);
|
|
247
|
+
resolve(TaskResult.fail(error.message, { taskType: 'shell' }));
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
}
|
package/src/upgrade.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { exec, execSync } from 'child_process';
|
|
1
|
+
import { exec, execSync, spawn } from 'child_process';
|
|
2
2
|
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
3
3
|
import { platform } from 'os';
|
|
4
4
|
import { homedir } from 'os';
|
|
@@ -13,18 +13,44 @@ export async function upgradeCommand(options = {}) {
|
|
|
13
13
|
const installMethod = detectInstallMethod();
|
|
14
14
|
console.log(`Bunosh is installed via: ${color.bold(installMethod)}`);
|
|
15
15
|
|
|
16
|
-
if (installMethod === 'bun') {
|
|
17
|
-
|
|
16
|
+
if (installMethod === 'bun' || installMethod === 'npm') {
|
|
17
|
+
const currentVersion = getCurrentVersion();
|
|
18
|
+
let latestVersion = null;
|
|
19
|
+
try {
|
|
20
|
+
latestVersion = await getLatestNpmVersion();
|
|
21
|
+
} catch (e) {
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
console.log(`Current version: ${color.bold(currentVersion)}`);
|
|
25
|
+
if (latestVersion) {
|
|
26
|
+
console.log(`Latest version: ${color.bold(latestVersion)}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (latestVersion && !force && !isNewerVersion(latestVersion, currentVersion)) {
|
|
30
|
+
console.log(color.green('You are already on the latest version!'));
|
|
31
|
+
if (!force) {
|
|
32
|
+
console.log(` Use ${color.bold('--force')} to reinstall the current version.`);
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const upgradeCmd = installMethod === 'bun'
|
|
38
|
+
? 'bun add -g bunosh@latest --force'
|
|
39
|
+
: 'npm install -g bunosh@latest';
|
|
40
|
+
|
|
41
|
+
if (check) {
|
|
42
|
+
console.log('Will upgrade with: ' + color.bold(upgradeCmd));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (installMethod === 'bun') {
|
|
18
47
|
await upgradeWithBun();
|
|
19
48
|
} else {
|
|
20
|
-
console.log('Will upgrade with: ' + color.bold('bun add -g bunosh'));
|
|
21
|
-
}
|
|
22
|
-
} else if (installMethod === 'npm') {
|
|
23
|
-
if (!check) {
|
|
24
49
|
await upgradeWithNpm();
|
|
25
|
-
} else {
|
|
26
|
-
console.log('Will upgrade with: ' + color.bold('npm update -g bunosh'));
|
|
27
50
|
}
|
|
51
|
+
|
|
52
|
+
console.log();
|
|
53
|
+
console.log(`Run ${color.bold('bunosh --version')} to verify the new version.`);
|
|
28
54
|
} else if (installMethod === 'executable') {
|
|
29
55
|
await upgradeExecutable({ force, check });
|
|
30
56
|
} else {
|
|
@@ -101,36 +127,115 @@ function detectInstallMethod() {
|
|
|
101
127
|
return 'unknown';
|
|
102
128
|
}
|
|
103
129
|
|
|
104
|
-
|
|
105
|
-
console.log('Upgrading with Bun...');
|
|
106
|
-
|
|
130
|
+
function runStreaming(command, args) {
|
|
107
131
|
return new Promise((resolve, reject) => {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
132
|
+
const child = spawn(command, args, { stdio: 'inherit' });
|
|
133
|
+
child.on('error', (error) => reject(new Error(`${command} not available: ${error.message}`)));
|
|
134
|
+
child.on('close', (code) => {
|
|
135
|
+
if (code === 0) {
|
|
136
|
+
resolve();
|
|
111
137
|
return;
|
|
112
138
|
}
|
|
113
|
-
|
|
114
|
-
console.log(color.green('Upgrade successful!'));
|
|
115
|
-
resolve();
|
|
139
|
+
reject(new Error(`${command} ${args.join(' ')} exited with code ${code}`));
|
|
116
140
|
});
|
|
117
141
|
});
|
|
118
142
|
}
|
|
119
143
|
|
|
144
|
+
async function upgradeWithBun() {
|
|
145
|
+
console.log('Upgrading with Bun...');
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
await runStreaming('bun', ['add', '-g', 'bunosh@latest', '--force']);
|
|
149
|
+
console.log(color.green('Upgrade successful!'));
|
|
150
|
+
return;
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.warn(color.yellow(`Bun upgrade failed: ${error.message}`));
|
|
153
|
+
console.warn(color.yellow('Falling back to npm (a known Bun global-install bug)...'));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
await upgradeWithNpm();
|
|
157
|
+
}
|
|
158
|
+
|
|
120
159
|
async function upgradeWithNpm() {
|
|
121
160
|
console.log('Upgrading with npm...');
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
161
|
+
|
|
162
|
+
await runStreaming('npm', ['install', '-g', 'bunosh@latest']);
|
|
163
|
+
console.log(color.green('Upgrade successful!'));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export async function getLatestNpmVersion(timeoutMs = 3000) {
|
|
167
|
+
const controller = new AbortController();
|
|
168
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
const response = await fetch('https://registry.npmjs.org/bunosh/latest', {
|
|
172
|
+
headers: { 'User-Agent': 'bunosh' },
|
|
173
|
+
signal: controller.signal,
|
|
132
174
|
});
|
|
133
|
-
|
|
175
|
+
|
|
176
|
+
if (!response.ok) {
|
|
177
|
+
throw new Error(`npm registry error: ${response.status}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const data = await response.json();
|
|
181
|
+
return data.version;
|
|
182
|
+
} finally {
|
|
183
|
+
clearTimeout(timer);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function getCachedLatestNpmVersion() {
|
|
188
|
+
const cacheDir = join(homedir(), '.bunosh');
|
|
189
|
+
const cacheFile = join(cacheDir, 'version-check.json');
|
|
190
|
+
const TTL = 12 * 60 * 60 * 1000;
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
if (existsSync(cacheFile)) {
|
|
194
|
+
const cache = JSON.parse(readFileSync(cacheFile, 'utf8'));
|
|
195
|
+
if (cache.latest && cache.checkedAt && Date.now() - cache.checkedAt < TTL) {
|
|
196
|
+
return cache.latest;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
} catch (e) {
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
let latest = null;
|
|
203
|
+
try {
|
|
204
|
+
latest = await getLatestNpmVersion(2000);
|
|
205
|
+
} catch (e) {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
await mkdir(cacheDir, { recursive: true });
|
|
211
|
+
writeFileSync(cacheFile, JSON.stringify({ checkedAt: Date.now(), latest }));
|
|
212
|
+
} catch (e) {
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return latest;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export async function printUpgradeNoticeIfAvailable() {
|
|
219
|
+
if (process.env.NODE_ENV === 'test' || process.env.VITEST_WORKER_ID !== undefined) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
const currentVersion = getCurrentVersion();
|
|
225
|
+
if (!currentVersion || currentVersion === 'unknown') {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const latestVersion = await getCachedLatestNpmVersion();
|
|
230
|
+
if (!latestVersion || !isNewerVersion(latestVersion, currentVersion)) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
console.log();
|
|
235
|
+
console.log(color.yellow(`🦾 A new version of Bunosh is available: ${color.bold(currentVersion)} → ${color.bold(latestVersion)}`));
|
|
236
|
+
console.log(color.dim(` Run ${color.bold('bunosh upgrade')} to update.`));
|
|
237
|
+
} catch (e) {
|
|
238
|
+
}
|
|
134
239
|
}
|
|
135
240
|
|
|
136
241
|
export async function upgradeExecutable(options = {}) {
|
|
@@ -199,7 +304,7 @@ function getCurrentVersion() {
|
|
|
199
304
|
}
|
|
200
305
|
|
|
201
306
|
async function getLatestRelease() {
|
|
202
|
-
const response = await fetch('https://api.github.com/repos/
|
|
307
|
+
const response = await fetch('https://api.github.com/repos/DavertMik/bunosh/releases/latest', {
|
|
203
308
|
headers: {
|
|
204
309
|
'User-Agent': 'bunosh'
|
|
205
310
|
}
|