open-agents-ai 0.24.0 → 0.26.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 +202 -14
- package/dist/launcher.cjs +71 -6
- package/dist/preinstall.cjs +70 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1425,9 +1425,14 @@ var init_file_read = __esm({
|
|
|
1425
1425
|
required: ["path"]
|
|
1426
1426
|
};
|
|
1427
1427
|
workingDir;
|
|
1428
|
+
_contextWindowSize = 0;
|
|
1428
1429
|
constructor(workingDir) {
|
|
1429
1430
|
this.workingDir = workingDir;
|
|
1430
1431
|
}
|
|
1432
|
+
/** Set actual context window size to enable auto-windowing for small contexts */
|
|
1433
|
+
setContextWindowSize(size) {
|
|
1434
|
+
this._contextWindowSize = size;
|
|
1435
|
+
}
|
|
1431
1436
|
async execute(args) {
|
|
1432
1437
|
const filePath = args["path"];
|
|
1433
1438
|
const offset = args["offset"];
|
|
@@ -1437,9 +1442,23 @@ var init_file_read = __esm({
|
|
|
1437
1442
|
const fullPath = resolve(this.workingDir, filePath);
|
|
1438
1443
|
const content = await readFile(fullPath, "utf-8");
|
|
1439
1444
|
let lines = content.split("\n");
|
|
1445
|
+
const totalLines = lines.length;
|
|
1440
1446
|
if (offset !== void 0) {
|
|
1441
1447
|
const startIdx = Math.max(0, offset - 1);
|
|
1442
1448
|
lines = lines.slice(startIdx, limit ? startIdx + limit : void 0);
|
|
1449
|
+
} else if (this._contextWindowSize > 0 && this._contextWindowSize <= 32768 && !limit) {
|
|
1450
|
+
const maxLines = this._contextWindowSize <= 16384 ? 80 : 120;
|
|
1451
|
+
if (totalLines > maxLines) {
|
|
1452
|
+
lines = lines.slice(0, maxLines);
|
|
1453
|
+
const numbered2 = lines.map((line, i) => `${String(i + 1).padStart(6)} | ${line}`).join("\n");
|
|
1454
|
+
return {
|
|
1455
|
+
success: true,
|
|
1456
|
+
output: `${numbered2}
|
|
1457
|
+
|
|
1458
|
+
[File has ${totalLines} lines \u2014 showing first ${maxLines}. Use offset/limit to see more.]`,
|
|
1459
|
+
durationMs: performance.now() - start
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1443
1462
|
}
|
|
1444
1463
|
const numbered = lines.map((line, i) => `${String(i + (offset ?? 1)).padStart(6)} | ${line}`).join("\n");
|
|
1445
1464
|
return {
|
|
@@ -10299,9 +10318,14 @@ Rules:
|
|
|
10299
10318
|
streamEnabled: options?.streamEnabled ?? false,
|
|
10300
10319
|
bruteForce: options?.bruteForce ?? true,
|
|
10301
10320
|
bruteForceMaxCycles: options?.bruteForceMaxCycles ?? 100,
|
|
10302
|
-
modelTier: options?.modelTier ?? "large"
|
|
10321
|
+
modelTier: options?.modelTier ?? "large",
|
|
10322
|
+
contextWindowSize: options?.contextWindowSize ?? 0
|
|
10303
10323
|
};
|
|
10304
10324
|
}
|
|
10325
|
+
/** Update context window size (e.g. after querying Ollama /api/show) */
|
|
10326
|
+
setContextWindowSize(size) {
|
|
10327
|
+
this.options.contextWindowSize = size;
|
|
10328
|
+
}
|
|
10305
10329
|
/** Register a tool for the agent to use */
|
|
10306
10330
|
registerTool(tool) {
|
|
10307
10331
|
this.tools.set(tool.name, tool);
|
|
@@ -10455,11 +10479,13 @@ Integrate this guidance into your current approach. Continue working on the task
|
|
|
10455
10479
|
});
|
|
10456
10480
|
}
|
|
10457
10481
|
const compacted = this.compactMessages(messages);
|
|
10482
|
+
const ctxWindow = this.options.contextWindowSize;
|
|
10483
|
+
const effectiveMaxTokens = ctxWindow > 0 ? Math.min(this.options.maxTokens, Math.max(2048, Math.floor(ctxWindow * 0.25))) : this.options.maxTokens;
|
|
10458
10484
|
const chatRequest = {
|
|
10459
10485
|
messages: compacted,
|
|
10460
10486
|
tools: toolDefs,
|
|
10461
10487
|
temperature: this.options.temperature,
|
|
10462
|
-
maxTokens:
|
|
10488
|
+
maxTokens: effectiveMaxTokens,
|
|
10463
10489
|
timeoutMs: this.options.requestTimeoutMs
|
|
10464
10490
|
};
|
|
10465
10491
|
let response;
|
|
@@ -10553,10 +10579,10 @@ Integrate this guidance into your current approach. Continue working on the task
|
|
|
10553
10579
|
}
|
|
10554
10580
|
}
|
|
10555
10581
|
}
|
|
10556
|
-
const
|
|
10557
|
-
const
|
|
10558
|
-
|
|
10559
|
-
${result.output}`;
|
|
10582
|
+
const ctxW = this.options.contextWindowSize;
|
|
10583
|
+
const maxLen = ctxW > 0 ? Math.max(2e3, Math.min(8e3, Math.floor(ctxW * 0.5))) : 8e3;
|
|
10584
|
+
const output = result.success ? result.output.length > maxLen ? this.foldOutput(result.output, maxLen) : result.output : `Error: ${result.error || "unknown error"}
|
|
10585
|
+
${result.output.length > maxLen ? this.foldOutput(result.output, maxLen) : result.output}`;
|
|
10560
10586
|
this.emit({
|
|
10561
10587
|
type: "tool_result",
|
|
10562
10588
|
toolName: tc.name,
|
|
@@ -10862,6 +10888,29 @@ ${marker}` : marker);
|
|
|
10862
10888
|
return { role: "tool", content: output, tool_call_id: toolCallId };
|
|
10863
10889
|
}
|
|
10864
10890
|
// -------------------------------------------------------------------------
|
|
10891
|
+
// Output folding — keep head + tail, omit middle (preserves errors at end)
|
|
10892
|
+
// -------------------------------------------------------------------------
|
|
10893
|
+
foldOutput(output, maxChars) {
|
|
10894
|
+
const lines = output.split("\n");
|
|
10895
|
+
if (lines.length <= 40) {
|
|
10896
|
+
return output.slice(0, maxChars) + "\n...(truncated)";
|
|
10897
|
+
}
|
|
10898
|
+
const headLines = 20;
|
|
10899
|
+
const tailLines = 10;
|
|
10900
|
+
const head = lines.slice(0, headLines).join("\n");
|
|
10901
|
+
const tail = lines.slice(-tailLines).join("\n");
|
|
10902
|
+
const omitted = lines.length - headLines - tailLines;
|
|
10903
|
+
const folded = `${head}
|
|
10904
|
+
|
|
10905
|
+
[... ${omitted} lines omitted ...]
|
|
10906
|
+
|
|
10907
|
+
${tail}`;
|
|
10908
|
+
if (folded.length > maxChars) {
|
|
10909
|
+
return folded.slice(0, maxChars) + "\n...(truncated)";
|
|
10910
|
+
}
|
|
10911
|
+
return folded;
|
|
10912
|
+
}
|
|
10913
|
+
// -------------------------------------------------------------------------
|
|
10865
10914
|
// Context compaction
|
|
10866
10915
|
// -------------------------------------------------------------------------
|
|
10867
10916
|
compactMessages(messages) {
|
|
@@ -10879,7 +10928,8 @@ ${marker}` : marker);
|
|
|
10879
10928
|
if (estimatedTokens < this.options.compactionThreshold) {
|
|
10880
10929
|
return messages;
|
|
10881
10930
|
}
|
|
10882
|
-
const
|
|
10931
|
+
const ctxWin = this.options.contextWindowSize;
|
|
10932
|
+
const keepRecent = ctxWin > 0 ? Math.max(4, Math.min(12, Math.floor(ctxWin / 4e3))) : 12;
|
|
10883
10933
|
const head = messages.slice(0, 2);
|
|
10884
10934
|
if (messages.length <= 2 + keepRecent)
|
|
10885
10935
|
return messages;
|
|
@@ -12684,6 +12734,55 @@ function findModel(models, query) {
|
|
|
12684
12734
|
const fuzzy = models.find((m) => m.name.includes(query));
|
|
12685
12735
|
return fuzzy;
|
|
12686
12736
|
}
|
|
12737
|
+
async function queryModelContextSize(baseUrl, modelName) {
|
|
12738
|
+
try {
|
|
12739
|
+
const normalized = normalizeBaseUrl(baseUrl);
|
|
12740
|
+
const res = await fetch(`${normalized}/api/show`, {
|
|
12741
|
+
method: "POST",
|
|
12742
|
+
headers: { "Content-Type": "application/json" },
|
|
12743
|
+
body: JSON.stringify({ name: modelName }),
|
|
12744
|
+
signal: AbortSignal.timeout(1e4)
|
|
12745
|
+
});
|
|
12746
|
+
if (!res.ok)
|
|
12747
|
+
return null;
|
|
12748
|
+
const data = await res.json();
|
|
12749
|
+
if (data.parameters) {
|
|
12750
|
+
const match = data.parameters.match(/num_ctx\s+(\d+)/);
|
|
12751
|
+
if (match)
|
|
12752
|
+
return parseInt(match[1], 10);
|
|
12753
|
+
}
|
|
12754
|
+
if (data.model_info) {
|
|
12755
|
+
for (const [key, value] of Object.entries(data.model_info)) {
|
|
12756
|
+
if (key.endsWith(".context_length") && typeof value === "number") {
|
|
12757
|
+
return value;
|
|
12758
|
+
}
|
|
12759
|
+
}
|
|
12760
|
+
}
|
|
12761
|
+
return null;
|
|
12762
|
+
} catch {
|
|
12763
|
+
return null;
|
|
12764
|
+
}
|
|
12765
|
+
}
|
|
12766
|
+
async function queryOpenAIContextSize(baseUrl, modelName, apiKey) {
|
|
12767
|
+
try {
|
|
12768
|
+
const models = await fetchOpenAIModels(baseUrl, apiKey);
|
|
12769
|
+
const model = models.find((m) => m.name === modelName);
|
|
12770
|
+
if (model?.size) {
|
|
12771
|
+
const match = model.size.match(/(\d+)K ctx/);
|
|
12772
|
+
if (match)
|
|
12773
|
+
return parseInt(match[1], 10) * 1024;
|
|
12774
|
+
}
|
|
12775
|
+
return null;
|
|
12776
|
+
} catch {
|
|
12777
|
+
return null;
|
|
12778
|
+
}
|
|
12779
|
+
}
|
|
12780
|
+
async function queryContextSize(baseUrl, modelName, apiKey) {
|
|
12781
|
+
const ollamaSize = await queryModelContextSize(baseUrl, modelName);
|
|
12782
|
+
if (ollamaSize)
|
|
12783
|
+
return ollamaSize;
|
|
12784
|
+
return queryOpenAIContextSize(baseUrl, modelName, apiKey);
|
|
12785
|
+
}
|
|
12687
12786
|
function formatBytes(bytes) {
|
|
12688
12787
|
if (bytes < 1024)
|
|
12689
12788
|
return `${bytes} B`;
|
|
@@ -13173,6 +13272,8 @@ function renderSlashHelp() {
|
|
|
13173
13272
|
["/evaluate", "Evaluate last completed task (LLM quality scoring)"],
|
|
13174
13273
|
["/task-type", "Set task type (code, document, analysis, plan, general, auto)"],
|
|
13175
13274
|
["/stats", "Show session dashboard (metrics, tool usage, task history)"],
|
|
13275
|
+
["/stop", "Stop current task and save progress (alias: /pause)"],
|
|
13276
|
+
["/resume", "Resume a previously stopped task"],
|
|
13176
13277
|
["/bruteforce", "Toggle brute-force mode (auto re-engage on turn limit)"],
|
|
13177
13278
|
["/tools", "List agent-created custom tools"],
|
|
13178
13279
|
["/skills", "List available AIWG skills"],
|
|
@@ -15097,6 +15198,34 @@ async function handleSlashCommand(input, ctx) {
|
|
|
15097
15198
|
renderInfo(`Colors ${next ? "enabled" : "disabled"}.`);
|
|
15098
15199
|
return "handled";
|
|
15099
15200
|
}
|
|
15201
|
+
case "stop":
|
|
15202
|
+
case "pause": {
|
|
15203
|
+
if (!ctx.hasActiveTask?.()) {
|
|
15204
|
+
renderWarning("No active task to stop.");
|
|
15205
|
+
return "handled";
|
|
15206
|
+
}
|
|
15207
|
+
const saved = ctx.savePendingTaskState?.() ?? false;
|
|
15208
|
+
const aborted = ctx.abortTask?.() ?? false;
|
|
15209
|
+
if (saved && aborted) {
|
|
15210
|
+
renderInfo("Task stopped and saved. Use /resume to continue later.");
|
|
15211
|
+
} else if (aborted) {
|
|
15212
|
+
renderWarning("Task stopped but state could not be saved.");
|
|
15213
|
+
} else {
|
|
15214
|
+
renderWarning("Could not stop the task.");
|
|
15215
|
+
}
|
|
15216
|
+
return "handled";
|
|
15217
|
+
}
|
|
15218
|
+
case "resume": {
|
|
15219
|
+
if (ctx.hasActiveTask?.()) {
|
|
15220
|
+
renderWarning("A task is already running. Stop it first with /stop.");
|
|
15221
|
+
return "handled";
|
|
15222
|
+
}
|
|
15223
|
+
const resumed = ctx.resumeTask?.() ?? false;
|
|
15224
|
+
if (!resumed) {
|
|
15225
|
+
renderWarning("No saved task to resume.");
|
|
15226
|
+
}
|
|
15227
|
+
return "handled";
|
|
15228
|
+
}
|
|
15100
15229
|
default: {
|
|
15101
15230
|
const skills = discoverSkills(ctx.repoRoot);
|
|
15102
15231
|
const skill = skills.find((s) => s.name === cmd || s.name === cmd.replace(/_/g, "-"));
|
|
@@ -15349,6 +15478,12 @@ async function switchModel(query, ctx, local = false) {
|
|
|
15349
15478
|
if (local) {
|
|
15350
15479
|
renderInfo("Saved as project-local override.");
|
|
15351
15480
|
}
|
|
15481
|
+
if (ctx.setContextWindowSize) {
|
|
15482
|
+
const ctxSize = await queryContextSize(ctx.config.backendUrl, finalModel, ctx.config.apiKey);
|
|
15483
|
+
if (ctxSize) {
|
|
15484
|
+
ctx.setContextWindowSize(ctxSize);
|
|
15485
|
+
}
|
|
15486
|
+
}
|
|
15352
15487
|
} catch (err) {
|
|
15353
15488
|
renderError(`Failed to switch model: ${err instanceof Error ? err.message : String(err)}`);
|
|
15354
15489
|
}
|
|
@@ -19022,7 +19157,7 @@ Use task_status("${taskId}") or task_output("${taskId}") to check progress.`
|
|
|
19022
19157
|
}
|
|
19023
19158
|
};
|
|
19024
19159
|
}
|
|
19025
|
-
function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType) {
|
|
19160
|
+
function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType, contextWindowSize) {
|
|
19026
19161
|
const modelTier = getModelTier(config.model);
|
|
19027
19162
|
const projectCtx = buildProjectContext(repoRoot, taskStores?.contextStores);
|
|
19028
19163
|
let dynamicContext = formatContextForPrompt(projectCtx, modelTier);
|
|
@@ -19043,10 +19178,19 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
|
|
|
19043
19178
|
modelTier,
|
|
19044
19179
|
streamEnabled: stream?.enabled ?? false,
|
|
19045
19180
|
bruteForce: bruteForce ?? true,
|
|
19046
|
-
bruteForceMaxCycles: 100
|
|
19181
|
+
bruteForceMaxCycles: 100,
|
|
19047
19182
|
// effectively unlimited — hard timeout is the real bound
|
|
19183
|
+
contextWindowSize: contextWindowSize ?? 0
|
|
19048
19184
|
});
|
|
19049
|
-
|
|
19185
|
+
const tools = buildTools(repoRoot, config);
|
|
19186
|
+
if (contextWindowSize && contextWindowSize > 0) {
|
|
19187
|
+
for (const tool of tools) {
|
|
19188
|
+
if ("setContextWindowSize" in tool && typeof tool.setContextWindowSize === "function") {
|
|
19189
|
+
tool.setContextWindowSize(contextWindowSize);
|
|
19190
|
+
}
|
|
19191
|
+
}
|
|
19192
|
+
}
|
|
19193
|
+
runner.registerTools(tools);
|
|
19050
19194
|
const filesTouched = /* @__PURE__ */ new Set();
|
|
19051
19195
|
const toolSequence = [];
|
|
19052
19196
|
const editSessionId = `task-${Date.now()}`;
|
|
@@ -19337,6 +19481,14 @@ async function startInteractive(config, repoPath) {
|
|
|
19337
19481
|
end: () => statusBar.endContentWrite()
|
|
19338
19482
|
});
|
|
19339
19483
|
}
|
|
19484
|
+
let resolvedContextWindowSize = 0;
|
|
19485
|
+
queryContextSize(config.backendUrl, config.model, config.apiKey).then((ctxSize) => {
|
|
19486
|
+
if (ctxSize) {
|
|
19487
|
+
resolvedContextWindowSize = ctxSize;
|
|
19488
|
+
statusBar.setContextWindowSize(ctxSize);
|
|
19489
|
+
}
|
|
19490
|
+
}).catch(() => {
|
|
19491
|
+
});
|
|
19340
19492
|
const provider = detectProvider(config.backendUrl);
|
|
19341
19493
|
const costTracker = new CostTracker(provider.id);
|
|
19342
19494
|
const sessionMetrics = new SessionMetrics();
|
|
@@ -19530,7 +19682,7 @@ async function startInteractive(config, repoPath) {
|
|
|
19530
19682
|
if (lastSubmittedPrompt && activeTask) {
|
|
19531
19683
|
savePendingTask(repoRoot, {
|
|
19532
19684
|
prompt: lastSubmittedPrompt,
|
|
19533
|
-
progressSummary: `
|
|
19685
|
+
progressSummary: `Task paused by user. ${sessionToolCallCount} tool calls completed.`,
|
|
19534
19686
|
filesModified: sessionFilesTouched,
|
|
19535
19687
|
bruteForce: bruteForceEnabled,
|
|
19536
19688
|
savedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -19552,6 +19704,12 @@ async function startInteractive(config, repoPath) {
|
|
|
19552
19704
|
writeContent(() => renderToolCallStart(event.toolName ?? "unknown", event.toolArgs ?? {}));
|
|
19553
19705
|
} else if (event.type === "tool_result") {
|
|
19554
19706
|
writeContent(() => renderToolResult(event.toolName ?? "unknown", event.success ?? false, event.content ?? ""));
|
|
19707
|
+
} else if (event.type === "token_usage" && event.tokenUsage) {
|
|
19708
|
+
statusBar.updateMetrics({
|
|
19709
|
+
...event.tokenUsage,
|
|
19710
|
+
estimatedCost: costTracker?.currentCost,
|
|
19711
|
+
hasPricing: costTracker?.hasPricing
|
|
19712
|
+
});
|
|
19555
19713
|
}
|
|
19556
19714
|
}).then((state) => {
|
|
19557
19715
|
writeContent(() => renderDreamEnd(state));
|
|
@@ -19628,7 +19786,36 @@ async function startInteractive(config, repoPath) {
|
|
|
19628
19786
|
getEmojis: () => getEmojisEnabled(),
|
|
19629
19787
|
setEmojis: (enabled) => setEmojisEnabled(enabled),
|
|
19630
19788
|
getColors: () => getColorsEnabled(),
|
|
19631
|
-
setColors: (enabled) => setColorsEnabled(enabled)
|
|
19789
|
+
setColors: (enabled) => setColorsEnabled(enabled),
|
|
19790
|
+
setContextWindowSize: (size) => {
|
|
19791
|
+
resolvedContextWindowSize = size;
|
|
19792
|
+
statusBar.setContextWindowSize(size);
|
|
19793
|
+
},
|
|
19794
|
+
hasActiveTask: () => activeTask !== null,
|
|
19795
|
+
abortTask() {
|
|
19796
|
+
if (!activeTask)
|
|
19797
|
+
return false;
|
|
19798
|
+
activeTask.runner.abort();
|
|
19799
|
+
writeContent(() => renderInfo("Task aborted."));
|
|
19800
|
+
return true;
|
|
19801
|
+
},
|
|
19802
|
+
resumeTask() {
|
|
19803
|
+
const pendingTask = loadPendingTask(repoRoot);
|
|
19804
|
+
if (!pendingTask)
|
|
19805
|
+
return false;
|
|
19806
|
+
setTimeout(() => {
|
|
19807
|
+
const resumeContext = [
|
|
19808
|
+
`[RESUMED] Original task: ${pendingTask.prompt}`,
|
|
19809
|
+
pendingTask.progressSummary ? `Progress so far: ${pendingTask.progressSummary}` : "",
|
|
19810
|
+
pendingTask.filesModified.length > 0 ? `Files modified before stop: ${pendingTask.filesModified.join(", ")}` : "",
|
|
19811
|
+
`Tool calls completed before stop: ${pendingTask.toolCallCount}`,
|
|
19812
|
+
"Continue where you left off. Do not repeat work already done."
|
|
19813
|
+
].filter(Boolean).join("\n\n");
|
|
19814
|
+
writeContent(() => renderInfo(`Resuming task: ${pendingTask.prompt.slice(0, 100)}${pendingTask.prompt.length > 100 ? "..." : ""}`));
|
|
19815
|
+
rl.emit("line", resumeContext);
|
|
19816
|
+
}, 100);
|
|
19817
|
+
return true;
|
|
19818
|
+
}
|
|
19632
19819
|
};
|
|
19633
19820
|
showPrompt();
|
|
19634
19821
|
if (hasTaskToResume) {
|
|
@@ -19759,7 +19946,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
|
|
|
19759
19946
|
toolPatternStore: toolPatternStore ?? void 0
|
|
19760
19947
|
}, bruteForceEnabled, statusBar, handleSudoRequest, costTracker, (summary) => {
|
|
19761
19948
|
lastCompletedSummary = summary;
|
|
19762
|
-
}, currentTaskType);
|
|
19949
|
+
}, currentTaskType, resolvedContextWindowSize);
|
|
19763
19950
|
activeTask = task;
|
|
19764
19951
|
showPrompt();
|
|
19765
19952
|
await task.promise;
|
|
@@ -19861,7 +20048,7 @@ Summarize or analyze this transcription as appropriate.`;
|
|
|
19861
20048
|
toolPatternStore: toolPatternStore ?? void 0
|
|
19862
20049
|
}, bruteForceEnabled, statusBar, handleSudoRequest, costTracker, (summary) => {
|
|
19863
20050
|
lastCompletedSummary = summary;
|
|
19864
|
-
}, currentTaskType);
|
|
20051
|
+
}, currentTaskType, resolvedContextWindowSize);
|
|
19865
20052
|
activeTask = task;
|
|
19866
20053
|
showPrompt();
|
|
19867
20054
|
await task.promise;
|
|
@@ -20003,6 +20190,7 @@ var init_interactive = __esm({
|
|
|
20003
20190
|
init_updater();
|
|
20004
20191
|
init_commands();
|
|
20005
20192
|
init_setup();
|
|
20193
|
+
init_model_picker();
|
|
20006
20194
|
init_project_context();
|
|
20007
20195
|
init_dist7();
|
|
20008
20196
|
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