open-agents-ai 0.24.0 → 0.25.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/dist/index.js +125 -2
- package/dist/launcher.cjs +71 -6
- package/dist/preinstall.cjs +70 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12684,6 +12684,55 @@ function findModel(models, query) {
|
|
|
12684
12684
|
const fuzzy = models.find((m) => m.name.includes(query));
|
|
12685
12685
|
return fuzzy;
|
|
12686
12686
|
}
|
|
12687
|
+
async function queryModelContextSize(baseUrl, modelName) {
|
|
12688
|
+
try {
|
|
12689
|
+
const normalized = normalizeBaseUrl(baseUrl);
|
|
12690
|
+
const res = await fetch(`${normalized}/api/show`, {
|
|
12691
|
+
method: "POST",
|
|
12692
|
+
headers: { "Content-Type": "application/json" },
|
|
12693
|
+
body: JSON.stringify({ name: modelName }),
|
|
12694
|
+
signal: AbortSignal.timeout(1e4)
|
|
12695
|
+
});
|
|
12696
|
+
if (!res.ok)
|
|
12697
|
+
return null;
|
|
12698
|
+
const data = await res.json();
|
|
12699
|
+
if (data.parameters) {
|
|
12700
|
+
const match = data.parameters.match(/num_ctx\s+(\d+)/);
|
|
12701
|
+
if (match)
|
|
12702
|
+
return parseInt(match[1], 10);
|
|
12703
|
+
}
|
|
12704
|
+
if (data.model_info) {
|
|
12705
|
+
for (const [key, value] of Object.entries(data.model_info)) {
|
|
12706
|
+
if (key.endsWith(".context_length") && typeof value === "number") {
|
|
12707
|
+
return value;
|
|
12708
|
+
}
|
|
12709
|
+
}
|
|
12710
|
+
}
|
|
12711
|
+
return null;
|
|
12712
|
+
} catch {
|
|
12713
|
+
return null;
|
|
12714
|
+
}
|
|
12715
|
+
}
|
|
12716
|
+
async function queryOpenAIContextSize(baseUrl, modelName, apiKey) {
|
|
12717
|
+
try {
|
|
12718
|
+
const models = await fetchOpenAIModels(baseUrl, apiKey);
|
|
12719
|
+
const model = models.find((m) => m.name === modelName);
|
|
12720
|
+
if (model?.size) {
|
|
12721
|
+
const match = model.size.match(/(\d+)K ctx/);
|
|
12722
|
+
if (match)
|
|
12723
|
+
return parseInt(match[1], 10) * 1024;
|
|
12724
|
+
}
|
|
12725
|
+
return null;
|
|
12726
|
+
} catch {
|
|
12727
|
+
return null;
|
|
12728
|
+
}
|
|
12729
|
+
}
|
|
12730
|
+
async function queryContextSize(baseUrl, modelName, apiKey) {
|
|
12731
|
+
const ollamaSize = await queryModelContextSize(baseUrl, modelName);
|
|
12732
|
+
if (ollamaSize)
|
|
12733
|
+
return ollamaSize;
|
|
12734
|
+
return queryOpenAIContextSize(baseUrl, modelName, apiKey);
|
|
12735
|
+
}
|
|
12687
12736
|
function formatBytes(bytes) {
|
|
12688
12737
|
if (bytes < 1024)
|
|
12689
12738
|
return `${bytes} B`;
|
|
@@ -13173,6 +13222,8 @@ function renderSlashHelp() {
|
|
|
13173
13222
|
["/evaluate", "Evaluate last completed task (LLM quality scoring)"],
|
|
13174
13223
|
["/task-type", "Set task type (code, document, analysis, plan, general, auto)"],
|
|
13175
13224
|
["/stats", "Show session dashboard (metrics, tool usage, task history)"],
|
|
13225
|
+
["/stop", "Stop current task and save progress (alias: /pause)"],
|
|
13226
|
+
["/resume", "Resume a previously stopped task"],
|
|
13176
13227
|
["/bruteforce", "Toggle brute-force mode (auto re-engage on turn limit)"],
|
|
13177
13228
|
["/tools", "List agent-created custom tools"],
|
|
13178
13229
|
["/skills", "List available AIWG skills"],
|
|
@@ -15097,6 +15148,34 @@ async function handleSlashCommand(input, ctx) {
|
|
|
15097
15148
|
renderInfo(`Colors ${next ? "enabled" : "disabled"}.`);
|
|
15098
15149
|
return "handled";
|
|
15099
15150
|
}
|
|
15151
|
+
case "stop":
|
|
15152
|
+
case "pause": {
|
|
15153
|
+
if (!ctx.hasActiveTask?.()) {
|
|
15154
|
+
renderWarning("No active task to stop.");
|
|
15155
|
+
return "handled";
|
|
15156
|
+
}
|
|
15157
|
+
const saved = ctx.savePendingTaskState?.() ?? false;
|
|
15158
|
+
const aborted = ctx.abortTask?.() ?? false;
|
|
15159
|
+
if (saved && aborted) {
|
|
15160
|
+
renderInfo("Task stopped and saved. Use /resume to continue later.");
|
|
15161
|
+
} else if (aborted) {
|
|
15162
|
+
renderWarning("Task stopped but state could not be saved.");
|
|
15163
|
+
} else {
|
|
15164
|
+
renderWarning("Could not stop the task.");
|
|
15165
|
+
}
|
|
15166
|
+
return "handled";
|
|
15167
|
+
}
|
|
15168
|
+
case "resume": {
|
|
15169
|
+
if (ctx.hasActiveTask?.()) {
|
|
15170
|
+
renderWarning("A task is already running. Stop it first with /stop.");
|
|
15171
|
+
return "handled";
|
|
15172
|
+
}
|
|
15173
|
+
const resumed = ctx.resumeTask?.() ?? false;
|
|
15174
|
+
if (!resumed) {
|
|
15175
|
+
renderWarning("No saved task to resume.");
|
|
15176
|
+
}
|
|
15177
|
+
return "handled";
|
|
15178
|
+
}
|
|
15100
15179
|
default: {
|
|
15101
15180
|
const skills = discoverSkills(ctx.repoRoot);
|
|
15102
15181
|
const skill = skills.find((s) => s.name === cmd || s.name === cmd.replace(/_/g, "-"));
|
|
@@ -15349,6 +15428,12 @@ async function switchModel(query, ctx, local = false) {
|
|
|
15349
15428
|
if (local) {
|
|
15350
15429
|
renderInfo("Saved as project-local override.");
|
|
15351
15430
|
}
|
|
15431
|
+
if (ctx.setContextWindowSize) {
|
|
15432
|
+
const ctxSize = await queryContextSize(ctx.config.backendUrl, finalModel, ctx.config.apiKey);
|
|
15433
|
+
if (ctxSize) {
|
|
15434
|
+
ctx.setContextWindowSize(ctxSize);
|
|
15435
|
+
}
|
|
15436
|
+
}
|
|
15352
15437
|
} catch (err) {
|
|
15353
15438
|
renderError(`Failed to switch model: ${err instanceof Error ? err.message : String(err)}`);
|
|
15354
15439
|
}
|
|
@@ -19337,6 +19422,11 @@ async function startInteractive(config, repoPath) {
|
|
|
19337
19422
|
end: () => statusBar.endContentWrite()
|
|
19338
19423
|
});
|
|
19339
19424
|
}
|
|
19425
|
+
queryContextSize(config.backendUrl, config.model, config.apiKey).then((ctxSize) => {
|
|
19426
|
+
if (ctxSize)
|
|
19427
|
+
statusBar.setContextWindowSize(ctxSize);
|
|
19428
|
+
}).catch(() => {
|
|
19429
|
+
});
|
|
19340
19430
|
const provider = detectProvider(config.backendUrl);
|
|
19341
19431
|
const costTracker = new CostTracker(provider.id);
|
|
19342
19432
|
const sessionMetrics = new SessionMetrics();
|
|
@@ -19530,7 +19620,7 @@ async function startInteractive(config, repoPath) {
|
|
|
19530
19620
|
if (lastSubmittedPrompt && activeTask) {
|
|
19531
19621
|
savePendingTask(repoRoot, {
|
|
19532
19622
|
prompt: lastSubmittedPrompt,
|
|
19533
|
-
progressSummary: `
|
|
19623
|
+
progressSummary: `Task paused by user. ${sessionToolCallCount} tool calls completed.`,
|
|
19534
19624
|
filesModified: sessionFilesTouched,
|
|
19535
19625
|
bruteForce: bruteForceEnabled,
|
|
19536
19626
|
savedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -19552,6 +19642,12 @@ async function startInteractive(config, repoPath) {
|
|
|
19552
19642
|
writeContent(() => renderToolCallStart(event.toolName ?? "unknown", event.toolArgs ?? {}));
|
|
19553
19643
|
} else if (event.type === "tool_result") {
|
|
19554
19644
|
writeContent(() => renderToolResult(event.toolName ?? "unknown", event.success ?? false, event.content ?? ""));
|
|
19645
|
+
} else if (event.type === "token_usage" && event.tokenUsage) {
|
|
19646
|
+
statusBar.updateMetrics({
|
|
19647
|
+
...event.tokenUsage,
|
|
19648
|
+
estimatedCost: costTracker?.currentCost,
|
|
19649
|
+
hasPricing: costTracker?.hasPricing
|
|
19650
|
+
});
|
|
19555
19651
|
}
|
|
19556
19652
|
}).then((state) => {
|
|
19557
19653
|
writeContent(() => renderDreamEnd(state));
|
|
@@ -19628,7 +19724,33 @@ async function startInteractive(config, repoPath) {
|
|
|
19628
19724
|
getEmojis: () => getEmojisEnabled(),
|
|
19629
19725
|
setEmojis: (enabled) => setEmojisEnabled(enabled),
|
|
19630
19726
|
getColors: () => getColorsEnabled(),
|
|
19631
|
-
setColors: (enabled) => setColorsEnabled(enabled)
|
|
19727
|
+
setColors: (enabled) => setColorsEnabled(enabled),
|
|
19728
|
+
setContextWindowSize: (size) => statusBar.setContextWindowSize(size),
|
|
19729
|
+
hasActiveTask: () => activeTask !== null,
|
|
19730
|
+
abortTask() {
|
|
19731
|
+
if (!activeTask)
|
|
19732
|
+
return false;
|
|
19733
|
+
activeTask.runner.abort();
|
|
19734
|
+
writeContent(() => renderInfo("Task aborted."));
|
|
19735
|
+
return true;
|
|
19736
|
+
},
|
|
19737
|
+
resumeTask() {
|
|
19738
|
+
const pendingTask = loadPendingTask(repoRoot);
|
|
19739
|
+
if (!pendingTask)
|
|
19740
|
+
return false;
|
|
19741
|
+
setTimeout(() => {
|
|
19742
|
+
const resumeContext = [
|
|
19743
|
+
`[RESUMED] Original task: ${pendingTask.prompt}`,
|
|
19744
|
+
pendingTask.progressSummary ? `Progress so far: ${pendingTask.progressSummary}` : "",
|
|
19745
|
+
pendingTask.filesModified.length > 0 ? `Files modified before stop: ${pendingTask.filesModified.join(", ")}` : "",
|
|
19746
|
+
`Tool calls completed before stop: ${pendingTask.toolCallCount}`,
|
|
19747
|
+
"Continue where you left off. Do not repeat work already done."
|
|
19748
|
+
].filter(Boolean).join("\n\n");
|
|
19749
|
+
writeContent(() => renderInfo(`Resuming task: ${pendingTask.prompt.slice(0, 100)}${pendingTask.prompt.length > 100 ? "..." : ""}`));
|
|
19750
|
+
rl.emit("line", resumeContext);
|
|
19751
|
+
}, 100);
|
|
19752
|
+
return true;
|
|
19753
|
+
}
|
|
19632
19754
|
};
|
|
19633
19755
|
showPrompt();
|
|
19634
19756
|
if (hasTaskToResume) {
|
|
@@ -20003,6 +20125,7 @@ var init_interactive = __esm({
|
|
|
20003
20125
|
init_updater();
|
|
20004
20126
|
init_commands();
|
|
20005
20127
|
init_setup();
|
|
20128
|
+
init_model_picker();
|
|
20006
20129
|
init_project_context();
|
|
20007
20130
|
init_dist7();
|
|
20008
20131
|
init_oa_directory();
|
package/dist/launcher.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Node version gate — uses only ES5 syntax so it parses on any Node version.
|
|
3
|
-
// On old Node: walks through nvm install, then Node 20, then reinstall.
|
|
3
|
+
// On old Node: walks through curl/wget install, nvm install, then Node 20, then reinstall.
|
|
4
4
|
var nodeVersion = parseInt(process.versions.node, 10);
|
|
5
5
|
|
|
6
6
|
if (nodeVersion < 18) {
|
|
@@ -36,6 +36,15 @@ if (nodeVersion < 18) {
|
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
function hasCmd(cmd) {
|
|
40
|
+
try {
|
|
41
|
+
childProcess.execSync("which " + cmd, { stdio: "pipe", timeout: 3000 });
|
|
42
|
+
return true;
|
|
43
|
+
} catch (e) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
39
48
|
function run(cmd) {
|
|
40
49
|
console.log(" $ " + cmd);
|
|
41
50
|
try {
|
|
@@ -53,7 +62,58 @@ if (nodeVersion < 18) {
|
|
|
53
62
|
process.exit(1);
|
|
54
63
|
}
|
|
55
64
|
|
|
56
|
-
|
|
65
|
+
// Returns "curl -o-" or "wget -qO-" or null
|
|
66
|
+
function getDownloader() {
|
|
67
|
+
if (hasCmd("curl")) return "curl -o-";
|
|
68
|
+
if (hasCmd("wget")) return "wget -qO-";
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Try to install curl if neither curl nor wget is available
|
|
73
|
+
function ensureDownloader(next) {
|
|
74
|
+
var dl = getDownloader();
|
|
75
|
+
if (dl) { next(dl); return; }
|
|
76
|
+
|
|
77
|
+
console.log(" Neither curl nor wget found.");
|
|
78
|
+
console.log("");
|
|
79
|
+
|
|
80
|
+
ask(" Install curl now? [Y/n] ", function(a) {
|
|
81
|
+
if (a === "n" || a === "no") {
|
|
82
|
+
bail(
|
|
83
|
+
"\n Install manually:\n" +
|
|
84
|
+
" sudo apt install curl (Debian/Ubuntu)\n" +
|
|
85
|
+
" sudo dnf install curl (Fedora)\n" +
|
|
86
|
+
" sudo pacman -S curl (Arch)\n"
|
|
87
|
+
);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
console.log("");
|
|
91
|
+
var ok = false;
|
|
92
|
+
if (hasCmd("apt-get")) {
|
|
93
|
+
ok = run("sudo -n apt-get install -y curl 2>/dev/null || sudo apt-get install -y curl");
|
|
94
|
+
} else if (hasCmd("dnf")) {
|
|
95
|
+
ok = run("sudo -n dnf install -y curl 2>/dev/null || sudo dnf install -y curl");
|
|
96
|
+
} else if (hasCmd("pacman")) {
|
|
97
|
+
ok = run("sudo -n pacman -S --noconfirm curl 2>/dev/null || sudo pacman -S --noconfirm curl");
|
|
98
|
+
} else if (hasCmd("apk")) {
|
|
99
|
+
ok = run("apk add curl");
|
|
100
|
+
} else {
|
|
101
|
+
console.log(" No supported package manager found (tried apt, dnf, pacman, apk).");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (ok && hasCmd("curl")) {
|
|
105
|
+
console.log("");
|
|
106
|
+
next("curl -o-");
|
|
107
|
+
} else {
|
|
108
|
+
bail(
|
|
109
|
+
"\n Could not install curl. Install it manually, then re-run:\n" +
|
|
110
|
+
" npm i -g open-agents-ai\n"
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function doInstallNvm(dl, next) {
|
|
57
117
|
if (hasNvm) {
|
|
58
118
|
console.log(" nvm found at " + nvmDir);
|
|
59
119
|
console.log("");
|
|
@@ -65,6 +125,7 @@ if (nodeVersion < 18) {
|
|
|
65
125
|
bail(
|
|
66
126
|
"\n Install nvm manually:\n" +
|
|
67
127
|
" curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
|
|
128
|
+
" # or: wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
|
|
68
129
|
" source ~/.bashrc\n" +
|
|
69
130
|
" nvm install 20\n" +
|
|
70
131
|
" npm i -g open-agents-ai\n"
|
|
@@ -72,7 +133,7 @@ if (nodeVersion < 18) {
|
|
|
72
133
|
return;
|
|
73
134
|
}
|
|
74
135
|
console.log("");
|
|
75
|
-
var ok = run("
|
|
136
|
+
var ok = run(dl + " https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash");
|
|
76
137
|
if (!ok) {
|
|
77
138
|
bail("\n nvm install failed. See https://github.com/nvm-sh/nvm#installing-and-updating\n");
|
|
78
139
|
return;
|
|
@@ -151,6 +212,7 @@ if (nodeVersion < 18) {
|
|
|
151
212
|
bail(
|
|
152
213
|
"\n To install manually:\n" +
|
|
153
214
|
" curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
|
|
215
|
+
" # or: wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
|
|
154
216
|
" source ~/.bashrc\n" +
|
|
155
217
|
" nvm install 20\n" +
|
|
156
218
|
" nvm alias default 20\n" +
|
|
@@ -159,9 +221,12 @@ if (nodeVersion < 18) {
|
|
|
159
221
|
return;
|
|
160
222
|
}
|
|
161
223
|
console.log("");
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
224
|
+
// Ensure curl or wget is available first
|
|
225
|
+
ensureDownloader(function(dl) {
|
|
226
|
+
doInstallNvm(dl, function() {
|
|
227
|
+
doInstallNode(function() {
|
|
228
|
+
doReinstall();
|
|
229
|
+
});
|
|
165
230
|
});
|
|
166
231
|
});
|
|
167
232
|
});
|
package/dist/preinstall.cjs
CHANGED
|
@@ -42,6 +42,15 @@ function ask(question, cb) {
|
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
function hasCmd(cmd) {
|
|
46
|
+
try {
|
|
47
|
+
childProcess.execSync("which " + cmd, { stdio: "pipe", timeout: 3000 });
|
|
48
|
+
return true;
|
|
49
|
+
} catch (e) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
45
54
|
function run(cmd, opts) {
|
|
46
55
|
console.log(" $ " + cmd);
|
|
47
56
|
try {
|
|
@@ -58,7 +67,58 @@ function bail(msg) {
|
|
|
58
67
|
process.exit(1);
|
|
59
68
|
}
|
|
60
69
|
|
|
61
|
-
|
|
70
|
+
// Returns "curl -o-" or "wget -qO-" or null
|
|
71
|
+
function getDownloader() {
|
|
72
|
+
if (hasCmd("curl")) return "curl -o-";
|
|
73
|
+
if (hasCmd("wget")) return "wget -qO-";
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Try to install curl if neither curl nor wget is available
|
|
78
|
+
function ensureDownloader(next) {
|
|
79
|
+
var dl = getDownloader();
|
|
80
|
+
if (dl) { next(dl); return; }
|
|
81
|
+
|
|
82
|
+
console.log(" Neither curl nor wget found.");
|
|
83
|
+
console.log("");
|
|
84
|
+
|
|
85
|
+
ask(" Install curl now? [Y/n] ", function(a) {
|
|
86
|
+
if (a === "n" || a === "no") {
|
|
87
|
+
bail(
|
|
88
|
+
"\n Install manually:\n" +
|
|
89
|
+
" sudo apt install curl (Debian/Ubuntu)\n" +
|
|
90
|
+
" sudo dnf install curl (Fedora)\n" +
|
|
91
|
+
" sudo pacman -S curl (Arch)\n"
|
|
92
|
+
);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
console.log("");
|
|
96
|
+
var ok = false;
|
|
97
|
+
if (hasCmd("apt-get")) {
|
|
98
|
+
ok = run("sudo -n apt-get install -y curl 2>/dev/null || sudo apt-get install -y curl");
|
|
99
|
+
} else if (hasCmd("dnf")) {
|
|
100
|
+
ok = run("sudo -n dnf install -y curl 2>/dev/null || sudo dnf install -y curl");
|
|
101
|
+
} else if (hasCmd("pacman")) {
|
|
102
|
+
ok = run("sudo -n pacman -S --noconfirm curl 2>/dev/null || sudo pacman -S --noconfirm curl");
|
|
103
|
+
} else if (hasCmd("apk")) {
|
|
104
|
+
ok = run("apk add curl");
|
|
105
|
+
} else {
|
|
106
|
+
console.log(" No supported package manager found (tried apt, dnf, pacman, apk).");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (ok && hasCmd("curl")) {
|
|
110
|
+
console.log("");
|
|
111
|
+
next("curl -o-");
|
|
112
|
+
} else {
|
|
113
|
+
bail(
|
|
114
|
+
"\n Could not install curl. Install it manually, then re-run:\n" +
|
|
115
|
+
" npm i -g open-agents-ai\n"
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function doInstallNvm(dl, next) {
|
|
62
122
|
if (hasNvm) {
|
|
63
123
|
console.log(" nvm found at " + nvmDir);
|
|
64
124
|
console.log("");
|
|
@@ -70,6 +130,7 @@ function doInstallNvm(next) {
|
|
|
70
130
|
bail(
|
|
71
131
|
"\n Install nvm manually:\n" +
|
|
72
132
|
" curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
|
|
133
|
+
" # or: wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
|
|
73
134
|
" source ~/.bashrc\n" +
|
|
74
135
|
" nvm install 20\n" +
|
|
75
136
|
" npm i -g open-agents-ai\n"
|
|
@@ -77,7 +138,7 @@ function doInstallNvm(next) {
|
|
|
77
138
|
return;
|
|
78
139
|
}
|
|
79
140
|
console.log("");
|
|
80
|
-
var ok = run("
|
|
141
|
+
var ok = run(dl + " https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash");
|
|
81
142
|
if (!ok) {
|
|
82
143
|
bail("\n nvm install failed. See https://github.com/nvm-sh/nvm#installing-and-updating\n");
|
|
83
144
|
return;
|
|
@@ -157,6 +218,7 @@ ask(" Install Node.js 20 now? [Y/n] ", function(a) {
|
|
|
157
218
|
bail(
|
|
158
219
|
"\n To install manually:\n" +
|
|
159
220
|
" curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
|
|
221
|
+
" # or: wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
|
|
160
222
|
" source ~/.bashrc\n" +
|
|
161
223
|
" nvm install 20\n" +
|
|
162
224
|
" nvm alias default 20\n" +
|
|
@@ -165,9 +227,12 @@ ask(" Install Node.js 20 now? [Y/n] ", function(a) {
|
|
|
165
227
|
return;
|
|
166
228
|
}
|
|
167
229
|
console.log("");
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
230
|
+
// Ensure curl or wget is available first
|
|
231
|
+
ensureDownloader(function(dl) {
|
|
232
|
+
doInstallNvm(dl, function() {
|
|
233
|
+
doInstallNode(function() {
|
|
234
|
+
doReinstall();
|
|
235
|
+
});
|
|
171
236
|
});
|
|
172
237
|
});
|
|
173
238
|
});
|
package/package.json
CHANGED