open-agents-ai 0.15.1 → 0.15.3
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 +845 -735
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8870,13 +8870,6 @@ ${newerSummary}` : newerSummary;
|
|
|
8870
8870
|
acc.id = chunk.toolCallId;
|
|
8871
8871
|
if (chunk.toolCallArgs) {
|
|
8872
8872
|
acc.args += chunk.toolCallArgs;
|
|
8873
|
-
this.emit({
|
|
8874
|
-
type: "stream_token",
|
|
8875
|
-
content: chunk.toolCallArgs,
|
|
8876
|
-
streamKind: "tool_args",
|
|
8877
|
-
turn,
|
|
8878
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
8879
|
-
});
|
|
8880
8873
|
}
|
|
8881
8874
|
}
|
|
8882
8875
|
}
|
|
@@ -10552,844 +10545,939 @@ var init_oa_directory = __esm({
|
|
|
10552
10545
|
}
|
|
10553
10546
|
});
|
|
10554
10547
|
|
|
10555
|
-
// packages/cli/dist/tui/
|
|
10556
|
-
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
|
|
10560
|
-
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
|
|
10564
|
-
|
|
10565
|
-
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
|
|
10570
|
-
|
|
10571
|
-
|
|
10572
|
-
|
|
10573
|
-
|
|
10574
|
-
|
|
10575
|
-
|
|
10576
|
-
ctx.clearScreen();
|
|
10577
|
-
return "handled";
|
|
10578
|
-
case "verbose":
|
|
10579
|
-
case "v":
|
|
10580
|
-
ctx.setVerbose(!ctx.config.verbose);
|
|
10581
|
-
if (hasLocal) {
|
|
10582
|
-
ctx.saveLocalSettings({ verbose: ctx.config.verbose });
|
|
10583
|
-
renderInfo(`Verbose mode: ${ctx.config.verbose ? "on" : "off"} (project-local)`);
|
|
10584
|
-
} else {
|
|
10585
|
-
ctx.saveSettings({ verbose: ctx.config.verbose });
|
|
10586
|
-
renderInfo(`Verbose mode: ${ctx.config.verbose ? "on" : "off"}`);
|
|
10548
|
+
// packages/cli/dist/tui/setup.js
|
|
10549
|
+
import * as readline from "node:readline";
|
|
10550
|
+
import { execSync as execSync10 } from "node:child_process";
|
|
10551
|
+
import { existsSync as existsSync13, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "node:fs";
|
|
10552
|
+
import { join as join18 } from "node:path";
|
|
10553
|
+
import { homedir as homedir8 } from "node:os";
|
|
10554
|
+
function detectSystemSpecs() {
|
|
10555
|
+
let totalRamGB = 0;
|
|
10556
|
+
let availableRamGB = 0;
|
|
10557
|
+
let gpuVramGB = 0;
|
|
10558
|
+
let gpuName = "";
|
|
10559
|
+
try {
|
|
10560
|
+
const memInfo = execSync10("free -b 2>/dev/null || sysctl -n hw.memsize 2>/dev/null", {
|
|
10561
|
+
encoding: "utf8",
|
|
10562
|
+
timeout: 5e3
|
|
10563
|
+
});
|
|
10564
|
+
if (memInfo.includes("Mem:")) {
|
|
10565
|
+
const match = memInfo.match(/^Mem:\s+(\d+)\s+\d+\s+\d+\s+\d+\s+\d+\s+(\d+)/m);
|
|
10566
|
+
if (match) {
|
|
10567
|
+
totalRamGB = parseInt(match[1], 10) / 1024 ** 3;
|
|
10568
|
+
availableRamGB = parseInt(match[2], 10) / 1024 ** 3;
|
|
10587
10569
|
}
|
|
10588
|
-
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
backendType: ctx.config.backendType,
|
|
10594
|
-
backendUrl: ctx.config.backendUrl,
|
|
10595
|
-
timeoutMs: String(ctx.config.timeoutMs),
|
|
10596
|
-
maxRetries: String(ctx.config.maxRetries),
|
|
10597
|
-
verbose: String(ctx.config.verbose),
|
|
10598
|
-
dryRun: String(ctx.config.dryRun)
|
|
10599
|
-
});
|
|
10600
|
-
return "handled";
|
|
10601
|
-
case "model":
|
|
10602
|
-
if (arg) {
|
|
10603
|
-
await switchModel(arg, ctx, hasLocal);
|
|
10604
|
-
} else {
|
|
10605
|
-
await showModelPicker(ctx);
|
|
10570
|
+
} else {
|
|
10571
|
+
const bytes = parseInt(memInfo.trim(), 10);
|
|
10572
|
+
if (!isNaN(bytes)) {
|
|
10573
|
+
totalRamGB = bytes / 1024 ** 3;
|
|
10574
|
+
availableRamGB = totalRamGB * 0.7;
|
|
10606
10575
|
}
|
|
10607
|
-
|
|
10608
|
-
|
|
10609
|
-
|
|
10610
|
-
|
|
10611
|
-
|
|
10612
|
-
|
|
10613
|
-
|
|
10614
|
-
|
|
10615
|
-
|
|
10616
|
-
|
|
10617
|
-
|
|
10618
|
-
|
|
10619
|
-
|
|
10620
|
-
|
|
10621
|
-
if (arg) {
|
|
10622
|
-
const msg = await ctx.voiceSetModel(arg);
|
|
10623
|
-
save({ voice: true, voiceModel: arg });
|
|
10624
|
-
renderInfo(msg + (hasLocal ? " (project-local)" : ""));
|
|
10625
|
-
} else {
|
|
10626
|
-
const msg = await ctx.voiceToggle();
|
|
10627
|
-
const isOn = msg.toLowerCase().includes("enabled") || msg.toLowerCase().includes("on");
|
|
10628
|
-
save({ voice: isOn });
|
|
10629
|
-
renderInfo(msg + (hasLocal ? " (project-local)" : ""));
|
|
10576
|
+
}
|
|
10577
|
+
} catch {
|
|
10578
|
+
}
|
|
10579
|
+
try {
|
|
10580
|
+
const nvidiaSmi = execSync10("nvidia-smi --query-gpu=memory.total,name --format=csv,noheader,nounits 2>/dev/null", { encoding: "utf8", timeout: 5e3 });
|
|
10581
|
+
const lines = nvidiaSmi.trim().split("\n");
|
|
10582
|
+
if (lines.length > 0) {
|
|
10583
|
+
for (const line of lines) {
|
|
10584
|
+
const parts = line.split(",").map((s) => s.trim());
|
|
10585
|
+
const vramMB = parseInt(parts[0] ?? "0", 10);
|
|
10586
|
+
if (!isNaN(vramMB))
|
|
10587
|
+
gpuVramGB += vramMB / 1024;
|
|
10588
|
+
if (!gpuName && parts[1])
|
|
10589
|
+
gpuName = parts[1];
|
|
10630
10590
|
}
|
|
10631
|
-
return "handled";
|
|
10632
10591
|
}
|
|
10633
|
-
|
|
10634
|
-
|
|
10635
|
-
|
|
10636
|
-
|
|
10637
|
-
|
|
10638
|
-
|
|
10592
|
+
} catch {
|
|
10593
|
+
}
|
|
10594
|
+
return {
|
|
10595
|
+
totalRamGB: Math.round(totalRamGB * 10) / 10,
|
|
10596
|
+
availableRamGB: Math.round(availableRamGB * 10) / 10,
|
|
10597
|
+
gpuVramGB: Math.round(gpuVramGB * 10) / 10,
|
|
10598
|
+
gpuName
|
|
10599
|
+
};
|
|
10600
|
+
}
|
|
10601
|
+
function recommendModel(specs) {
|
|
10602
|
+
const effectiveGB = Math.max(specs.gpuVramGB, specs.availableRamGB);
|
|
10603
|
+
const budget = effectiveGB * 0.8;
|
|
10604
|
+
const localVariants = QWEN_VARIANTS.filter((v) => !v.cloud);
|
|
10605
|
+
for (let i = localVariants.length - 1; i >= 0; i--) {
|
|
10606
|
+
if (localVariants[i].sizeGB <= budget) {
|
|
10607
|
+
return localVariants[i];
|
|
10639
10608
|
}
|
|
10640
|
-
|
|
10641
|
-
|
|
10642
|
-
|
|
10643
|
-
|
|
10644
|
-
|
|
10645
|
-
|
|
10646
|
-
|
|
10647
|
-
|
|
10648
|
-
|
|
10609
|
+
}
|
|
10610
|
+
return QWEN_VARIANTS.find((v) => v.tag === "qwen3.5:cloud");
|
|
10611
|
+
}
|
|
10612
|
+
function calculateContextWindow(specs, modelSizeGB2) {
|
|
10613
|
+
const totalAvail = Math.max(specs.gpuVramGB, specs.totalRamGB);
|
|
10614
|
+
const remaining = totalAvail - modelSizeGB2;
|
|
10615
|
+
if (remaining >= 200)
|
|
10616
|
+
return { numCtx: 131072, label: "128K" };
|
|
10617
|
+
if (remaining >= 100)
|
|
10618
|
+
return { numCtx: 65536, label: "64K" };
|
|
10619
|
+
if (remaining >= 50)
|
|
10620
|
+
return { numCtx: 32768, label: "32K" };
|
|
10621
|
+
if (remaining >= 20)
|
|
10622
|
+
return { numCtx: 16384, label: "16K" };
|
|
10623
|
+
if (remaining >= 8)
|
|
10624
|
+
return { numCtx: 8192, label: "8K" };
|
|
10625
|
+
return { numCtx: 4096, label: "4K" };
|
|
10626
|
+
}
|
|
10627
|
+
function modelSupportsToolCalling(modelName) {
|
|
10628
|
+
const lower = modelName.toLowerCase();
|
|
10629
|
+
for (const known of TOOL_CALLING_MODELS) {
|
|
10630
|
+
if (lower.startsWith(known) || lower.includes(known))
|
|
10631
|
+
return true;
|
|
10632
|
+
}
|
|
10633
|
+
return false;
|
|
10634
|
+
}
|
|
10635
|
+
function ask(rl, question) {
|
|
10636
|
+
return new Promise((resolve16) => {
|
|
10637
|
+
rl.question(question, (answer) => resolve16(answer.trim()));
|
|
10638
|
+
});
|
|
10639
|
+
}
|
|
10640
|
+
function pullModelWithAutoUpdate(tag) {
|
|
10641
|
+
try {
|
|
10642
|
+
execSync10(`ollama pull ${tag}`, {
|
|
10643
|
+
stdio: "inherit",
|
|
10644
|
+
timeout: 36e5
|
|
10645
|
+
// 1 hour max
|
|
10646
|
+
});
|
|
10647
|
+
} catch (err) {
|
|
10648
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
10649
|
+
const stderr = err?.stderr?.toString?.() ?? errMsg;
|
|
10650
|
+
const combined = errMsg + "\n" + stderr;
|
|
10651
|
+
if (combined.includes("412") || combined.includes("newer version") || combined.includes("requires a newer version")) {
|
|
10652
|
+
process.stdout.write(`
|
|
10653
|
+
${c2.yellow("\u26A0")} Ollama needs to be updated for this model.
|
|
10654
|
+
`);
|
|
10655
|
+
process.stdout.write(` ${c2.cyan("\u25CF")} Updating Ollama via official install script...
|
|
10649
10656
|
|
|
10650
10657
|
`);
|
|
10651
|
-
|
|
10652
|
-
|
|
10658
|
+
try {
|
|
10659
|
+
execSync10("curl -fsSL https://ollama.com/install.sh | sh", {
|
|
10660
|
+
stdio: "inherit",
|
|
10661
|
+
timeout: 3e5
|
|
10662
|
+
// 5 min max for install
|
|
10663
|
+
});
|
|
10664
|
+
process.stdout.write(`
|
|
10665
|
+
${c2.green("\u2714")} Ollama updated successfully.
|
|
10653
10666
|
`);
|
|
10654
|
-
|
|
10667
|
+
process.stdout.write(` ${c2.cyan("\u25CF")} Retrying pull of ${c2.bold(tag)}...
|
|
10668
|
+
|
|
10655
10669
|
`);
|
|
10656
|
-
}
|
|
10657
|
-
|
|
10670
|
+
execSync10(`ollama pull ${tag}`, {
|
|
10671
|
+
stdio: "inherit",
|
|
10672
|
+
timeout: 36e5
|
|
10673
|
+
});
|
|
10674
|
+
} catch (updateErr) {
|
|
10675
|
+
const updateMsg = updateErr instanceof Error ? updateErr.message : String(updateErr);
|
|
10676
|
+
throw new Error(`Failed to update Ollama and retry pull: ${updateMsg}
|
|
10677
|
+
Try manually:
|
|
10678
|
+
curl -fsSL https://ollama.com/install.sh | sh
|
|
10679
|
+
ollama pull ${tag}`);
|
|
10658
10680
|
}
|
|
10659
|
-
|
|
10681
|
+
} else {
|
|
10682
|
+
throw err;
|
|
10660
10683
|
}
|
|
10661
|
-
case "skills":
|
|
10662
|
-
case "skill": {
|
|
10663
|
-
const skills = discoverSkills(ctx.repoRoot);
|
|
10664
|
-
if (skills.length === 0) {
|
|
10665
|
-
renderInfo("No skills found.");
|
|
10666
|
-
renderInfo("Install AIWG to get skills: npm i -g aiwg && aiwg use sdlc");
|
|
10667
|
-
renderInfo("Or add skills manually to .oa/skills/{name}/SKILL.md");
|
|
10668
|
-
} else {
|
|
10669
|
-
let filtered = skills;
|
|
10670
|
-
if (arg) {
|
|
10671
|
-
const q = arg.toLowerCase();
|
|
10672
|
-
filtered = skills.filter((s) => s.name.toLowerCase().includes(q) || s.description.toLowerCase().includes(q) || s.triggers.some((t) => t.toLowerCase().includes(q)));
|
|
10673
|
-
}
|
|
10674
|
-
if (filtered.length === 0) {
|
|
10675
|
-
renderWarning(`No skills matching "${arg}". Showing all ${skills.length} skills:`);
|
|
10676
|
-
filtered = skills;
|
|
10677
|
-
}
|
|
10678
|
-
const bySource = /* @__PURE__ */ new Map();
|
|
10679
|
-
for (const s of filtered) {
|
|
10680
|
-
const group = bySource.get(s.source) ?? [];
|
|
10681
|
-
group.push(s);
|
|
10682
|
-
bySource.set(s.source, group);
|
|
10683
|
-
}
|
|
10684
|
-
process.stdout.write(`
|
|
10685
|
-
${c2.bold(`Available Skills (${filtered.length}):`)}
|
|
10686
|
-
`);
|
|
10687
|
-
for (const [source, group] of bySource) {
|
|
10688
|
-
process.stdout.write(`
|
|
10689
|
-
${c2.dim(`\u2500\u2500 ${source} (${group.length}) \u2500\u2500`)}
|
|
10690
|
-
`);
|
|
10691
|
-
for (const s of group) {
|
|
10692
|
-
process.stdout.write(` ${c2.cyan(s.name.padEnd(32))} ${s.description.slice(0, 60)}
|
|
10693
|
-
`);
|
|
10694
|
-
if (s.triggers.length > 0) {
|
|
10695
|
-
process.stdout.write(` ${"".padEnd(32)} ${c2.dim(`triggers: ${s.triggers.slice(0, 3).join(" | ")}`)}
|
|
10696
|
-
`);
|
|
10697
|
-
}
|
|
10698
|
-
}
|
|
10699
|
-
}
|
|
10700
|
-
process.stdout.write("\n");
|
|
10701
|
-
renderInfo('Invoke directly: /<skill-name> [args] (e.g. /ralph "fix tests" --completion "npm test passes")');
|
|
10702
|
-
renderInfo("Filter with: /skills <keyword>");
|
|
10703
|
-
}
|
|
10704
|
-
return "handled";
|
|
10705
|
-
}
|
|
10706
|
-
case "dream": {
|
|
10707
|
-
if (arg === "stop" || arg === "wake") {
|
|
10708
|
-
if (ctx.isDreaming?.()) {
|
|
10709
|
-
ctx.dreamStop?.();
|
|
10710
|
-
renderInfo("Waking up from dream mode...");
|
|
10711
|
-
} else {
|
|
10712
|
-
renderWarning("Not currently dreaming.");
|
|
10713
|
-
}
|
|
10714
|
-
} else if (ctx.isDreaming?.()) {
|
|
10715
|
-
renderWarning("Already dreaming. Use /dream stop to wake up first.");
|
|
10716
|
-
} else {
|
|
10717
|
-
const mode = arg === "lucid" ? "lucid" : arg === "deep" ? "deep" : "default";
|
|
10718
|
-
ctx.dreamStart?.(mode);
|
|
10719
|
-
}
|
|
10720
|
-
return "handled";
|
|
10721
|
-
}
|
|
10722
|
-
case "listen":
|
|
10723
|
-
case "mic": {
|
|
10724
|
-
if (!ctx.listenToggle) {
|
|
10725
|
-
renderWarning("Listen mode not available in this context.");
|
|
10726
|
-
return "handled";
|
|
10727
|
-
}
|
|
10728
|
-
if (arg === "stop" || arg === "off") {
|
|
10729
|
-
const msg2 = await (ctx.listenStop?.() ?? Promise.resolve("Not listening."));
|
|
10730
|
-
renderInfo(msg2);
|
|
10731
|
-
return "handled";
|
|
10732
|
-
}
|
|
10733
|
-
if (arg === "confirm") {
|
|
10734
|
-
const msg2 = ctx.listenSetMode?.("confirm") ?? "Confirm mode set.";
|
|
10735
|
-
renderInfo(msg2);
|
|
10736
|
-
return "handled";
|
|
10737
|
-
}
|
|
10738
|
-
if (arg === "auto") {
|
|
10739
|
-
const msg2 = ctx.listenSetMode?.("auto") ?? "Auto mode set.";
|
|
10740
|
-
renderInfo(msg2);
|
|
10741
|
-
return "handled";
|
|
10742
|
-
}
|
|
10743
|
-
const modelSizes = ["tiny", "base", "small", "medium", "large", "large-v3"];
|
|
10744
|
-
if (arg && modelSizes.includes(arg.toLowerCase())) {
|
|
10745
|
-
const model = arg.toLowerCase() === "large" ? "large-v3" : arg.toLowerCase();
|
|
10746
|
-
const msg2 = await (ctx.listenSetModel?.(model) ?? Promise.resolve(`Model set to ${model}.`));
|
|
10747
|
-
renderInfo(msg2);
|
|
10748
|
-
return "handled";
|
|
10749
|
-
}
|
|
10750
|
-
const msg = await ctx.listenToggle();
|
|
10751
|
-
renderInfo(msg);
|
|
10752
|
-
return "handled";
|
|
10753
|
-
}
|
|
10754
|
-
case "bruteforce":
|
|
10755
|
-
case "brute": {
|
|
10756
|
-
const isOn = ctx.bruteForceToggle();
|
|
10757
|
-
const save = hasLocal ? ctx.saveLocalSettings.bind(ctx) : ctx.saveSettings.bind(ctx);
|
|
10758
|
-
save({ bruteforce: isOn });
|
|
10759
|
-
renderInfo(`Brute-force mode: ${isOn ? "on" : "off"}${hasLocal ? " (project-local)" : ""}` + (isOn ? " \u2014 agent will auto re-engage when turn limit is hit, reassess and try creative strategies" : ""));
|
|
10760
|
-
return "handled";
|
|
10761
|
-
}
|
|
10762
|
-
case "emojis":
|
|
10763
|
-
case "emoji": {
|
|
10764
|
-
const current = ctx.getEmojis?.() ?? true;
|
|
10765
|
-
const next = !current;
|
|
10766
|
-
ctx.setEmojis?.(next);
|
|
10767
|
-
const save = hasLocal ? ctx.saveLocalSettings.bind(ctx) : ctx.saveSettings.bind(ctx);
|
|
10768
|
-
save({ emojis: next });
|
|
10769
|
-
renderInfo(`Emojis ${next ? "enabled" : "disabled"}.`);
|
|
10770
|
-
return "handled";
|
|
10771
|
-
}
|
|
10772
|
-
case "colors":
|
|
10773
|
-
case "color": {
|
|
10774
|
-
const current = ctx.getColors?.() ?? true;
|
|
10775
|
-
const next = !current;
|
|
10776
|
-
ctx.setColors?.(next);
|
|
10777
|
-
const save = hasLocal ? ctx.saveLocalSettings.bind(ctx) : ctx.saveSettings.bind(ctx);
|
|
10778
|
-
save({ colors: next });
|
|
10779
|
-
renderInfo(`Colors ${next ? "enabled" : "disabled"}.`);
|
|
10780
|
-
return "handled";
|
|
10781
|
-
}
|
|
10782
|
-
default: {
|
|
10783
|
-
const skills = discoverSkills(ctx.repoRoot);
|
|
10784
|
-
const skill = skills.find((s) => s.name === cmd || s.name === cmd.replace(/_/g, "-"));
|
|
10785
|
-
if (skill) {
|
|
10786
|
-
const content = loadSkillContent(skill.filePath);
|
|
10787
|
-
if (content) {
|
|
10788
|
-
renderInfo(`Loading skill: ${c2.bold(skill.name)} (${skill.source})`);
|
|
10789
|
-
return { type: "skill", name: skill.name, content, args: arg };
|
|
10790
|
-
}
|
|
10791
|
-
}
|
|
10792
|
-
renderWarning(`Unknown command: /${cmd}. Type /help for available commands.`);
|
|
10793
|
-
return "handled";
|
|
10794
|
-
}
|
|
10795
|
-
}
|
|
10796
|
-
}
|
|
10797
|
-
async function listModels(ctx) {
|
|
10798
|
-
try {
|
|
10799
|
-
const models = await fetchOllamaModels(ctx.config.backendUrl);
|
|
10800
|
-
renderModelList(models.map((m) => ({ name: m.name, size: m.size, modified: m.modified })), ctx.config.model);
|
|
10801
|
-
} catch (err) {
|
|
10802
|
-
renderError(`Failed to fetch models: ${err instanceof Error ? err.message : String(err)}`);
|
|
10803
10684
|
}
|
|
10804
10685
|
}
|
|
10805
|
-
async function
|
|
10686
|
+
async function runSetupWizard(config) {
|
|
10687
|
+
const rl = readline.createInterface({
|
|
10688
|
+
input: process.stdin,
|
|
10689
|
+
output: process.stdout,
|
|
10690
|
+
terminal: true
|
|
10691
|
+
});
|
|
10806
10692
|
try {
|
|
10807
|
-
|
|
10808
|
-
|
|
10809
|
-
|
|
10810
|
-
return;
|
|
10811
|
-
}
|
|
10812
|
-
renderModelList(models.map((m) => ({ name: m.name, size: m.size, modified: m.modified })), ctx.config.model);
|
|
10813
|
-
} catch (err) {
|
|
10814
|
-
renderError(`Failed to fetch models: ${err instanceof Error ? err.message : String(err)}`);
|
|
10693
|
+
return await doSetup(config, rl);
|
|
10694
|
+
} finally {
|
|
10695
|
+
rl.close();
|
|
10815
10696
|
}
|
|
10816
10697
|
}
|
|
10817
|
-
async function
|
|
10818
|
-
|
|
10819
|
-
|
|
10820
|
-
${c2.bold("Current endpoint:")}
|
|
10821
|
-
|
|
10822
|
-
`);
|
|
10823
|
-
process.stdout.write(` ${c2.cyan("URL".padEnd(12))} ${ctx.config.backendUrl}
|
|
10698
|
+
async function doSetup(config, rl) {
|
|
10699
|
+
process.stdout.write(`
|
|
10700
|
+
${c2.bold(c2.cyan("open-agents"))}
|
|
10824
10701
|
`);
|
|
10825
|
-
|
|
10702
|
+
process.stdout.write(` ${c2.dim("\u2500".repeat(60))}
|
|
10826
10703
|
`);
|
|
10827
|
-
|
|
10704
|
+
process.stdout.write(` ${c2.bold("First-run setup")}
|
|
10705
|
+
|
|
10828
10706
|
`);
|
|
10829
|
-
|
|
10830
|
-
${c2.dim("Usage: /endpoint <url> [--auth <token>]")}
|
|
10707
|
+
process.stdout.write(` ${c2.cyan("\u25CF")} Detecting system specs...
|
|
10831
10708
|
`);
|
|
10832
|
-
|
|
10709
|
+
const specs = detectSystemSpecs();
|
|
10710
|
+
process.stdout.write(` ${c2.dim(" RAM:")} ${specs.totalRamGB.toFixed(1)} GB total, ${specs.availableRamGB.toFixed(1)} GB available
|
|
10833
10711
|
`);
|
|
10834
|
-
|
|
10712
|
+
if (specs.gpuVramGB > 0) {
|
|
10713
|
+
process.stdout.write(` ${c2.dim(" GPU:")} ${specs.gpuName || "NVIDIA"} \u2014 ${specs.gpuVramGB.toFixed(1)} GB VRAM
|
|
10835
10714
|
`);
|
|
10836
|
-
|
|
10837
|
-
|
|
10715
|
+
} else {
|
|
10716
|
+
process.stdout.write(` ${c2.dim(" GPU:")} No NVIDIA GPU detected (CPU inference)
|
|
10838
10717
|
`);
|
|
10839
|
-
return;
|
|
10840
|
-
}
|
|
10841
|
-
const parts = arg.split(/\s+/);
|
|
10842
|
-
const url = parts[0];
|
|
10843
|
-
let apiKey;
|
|
10844
|
-
const authIdx = parts.indexOf("--auth");
|
|
10845
|
-
if (authIdx !== -1 && parts[authIdx + 1]) {
|
|
10846
|
-
apiKey = parts[authIdx + 1];
|
|
10847
10718
|
}
|
|
10719
|
+
process.stdout.write("\n");
|
|
10720
|
+
let models = [];
|
|
10848
10721
|
try {
|
|
10849
|
-
|
|
10722
|
+
models = await fetchOllamaModels(config.backendUrl);
|
|
10850
10723
|
} catch {
|
|
10851
|
-
renderError(`
|
|
10852
|
-
|
|
10853
|
-
|
|
10854
|
-
|
|
10855
|
-
|
|
10856
|
-
|
|
10724
|
+
renderError(`Cannot reach Ollama at ${config.backendUrl}`);
|
|
10725
|
+
renderInfo("Start Ollama with: ollama serve");
|
|
10726
|
+
renderInfo("Or use /endpoint to configure a remote backend after startup.");
|
|
10727
|
+
const answer = await ask(rl, `
|
|
10728
|
+
${c2.bold("Continue without Ollama?")} (y/n) `);
|
|
10729
|
+
if (answer.toLowerCase() !== "y")
|
|
10730
|
+
return null;
|
|
10731
|
+
return config.model;
|
|
10857
10732
|
}
|
|
10858
|
-
|
|
10859
|
-
|
|
10860
|
-
|
|
10861
|
-
|
|
10862
|
-
const headers = {};
|
|
10863
|
-
if (apiKey)
|
|
10864
|
-
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
10865
|
-
const resp = await fetch(healthUrl, {
|
|
10866
|
-
headers,
|
|
10867
|
-
signal: AbortSignal.timeout(1e4)
|
|
10868
|
-
});
|
|
10869
|
-
if (!resp.ok)
|
|
10870
|
-
throw new Error(`HTTP ${resp.status}`);
|
|
10871
|
-
process.stdout.write(`${c2.green("\u2714")} Connected
|
|
10872
|
-
`);
|
|
10873
|
-
} catch (err) {
|
|
10874
|
-
process.stdout.write(`${c2.yellow("\u26A0")} Could not verify
|
|
10733
|
+
const currentModel = findModel(models, config.model);
|
|
10734
|
+
if (currentModel) {
|
|
10735
|
+
process.stdout.write(` ${c2.green("\u2714")} Model ${c2.bold(currentModel.name)} is available.
|
|
10736
|
+
|
|
10875
10737
|
`);
|
|
10876
|
-
|
|
10877
|
-
renderInfo("Setting endpoint anyway \u2014 it may come online later.");
|
|
10878
|
-
}
|
|
10879
|
-
ctx.setEndpoint(url, backendType, apiKey);
|
|
10880
|
-
const endpointSettings = { backendUrl: url, backendType, ...apiKey ? { apiKey } : {} };
|
|
10881
|
-
if (local) {
|
|
10882
|
-
ctx.saveLocalSettings(endpointSettings);
|
|
10883
|
-
} else {
|
|
10884
|
-
setConfigValue("backendUrl", url);
|
|
10885
|
-
setConfigValue("backendType", backendType);
|
|
10886
|
-
if (apiKey) {
|
|
10887
|
-
setConfigValue("apiKey", apiKey);
|
|
10888
|
-
}
|
|
10889
|
-
ctx.saveSettings(endpointSettings);
|
|
10738
|
+
return currentModel.name;
|
|
10890
10739
|
}
|
|
10891
|
-
process.stdout.write(`
|
|
10892
|
-
|
|
10740
|
+
process.stdout.write(` ${c2.yellow("\u26A0")} Default model ${c2.bold(config.model)} is not available.
|
|
10741
|
+
|
|
10893
10742
|
`);
|
|
10894
|
-
|
|
10743
|
+
const toolCallingModels = models.filter((m) => modelSupportsToolCalling(m.name));
|
|
10744
|
+
if (toolCallingModels.length > 0) {
|
|
10745
|
+
process.stdout.write(` ${c2.cyan("\u25CF")} Found ${toolCallingModels.length} model(s) with tool-calling support:
|
|
10746
|
+
|
|
10895
10747
|
`);
|
|
10896
|
-
|
|
10748
|
+
for (let i = 0; i < Math.min(toolCallingModels.length, 10); i++) {
|
|
10749
|
+
const m = toolCallingModels[i];
|
|
10750
|
+
process.stdout.write(` ${c2.bold(String(i + 1))}. ${m.name} ${c2.dim(`(${m.size})`)}
|
|
10897
10751
|
`);
|
|
10898
|
-
|
|
10899
|
-
process.stdout.write(`
|
|
10752
|
+
}
|
|
10753
|
+
process.stdout.write(`
|
|
10754
|
+
${c2.dim("0")}. Pull a new qwen3.5 model instead
|
|
10755
|
+
`);
|
|
10756
|
+
process.stdout.write("\n");
|
|
10757
|
+
const choice = await ask(rl, ` ${c2.bold("Select a model")} (1-${Math.min(toolCallingModels.length, 10)}, or 0 to pull new): `);
|
|
10758
|
+
const idx = parseInt(choice, 10);
|
|
10759
|
+
if (idx > 0 && idx <= toolCallingModels.length) {
|
|
10760
|
+
const selected = toolCallingModels[idx - 1];
|
|
10761
|
+
setConfigValue("model", selected.name);
|
|
10762
|
+
process.stdout.write(`
|
|
10763
|
+
${c2.green("\u2714")} Selected ${c2.bold(selected.name)}. Saved to config.
|
|
10764
|
+
|
|
10900
10765
|
`);
|
|
10766
|
+
return selected.name;
|
|
10767
|
+
}
|
|
10901
10768
|
} else {
|
|
10902
|
-
process.stdout.write(`
|
|
10769
|
+
process.stdout.write(` ${c2.yellow("\u26A0")} No tool-calling capable models found on this system.
|
|
10770
|
+
|
|
10903
10771
|
`);
|
|
10904
10772
|
}
|
|
10905
|
-
|
|
10906
|
-
}
|
|
10907
|
-
|
|
10908
|
-
|
|
10909
|
-
|
|
10910
|
-
|
|
10911
|
-
|
|
10912
|
-
|
|
10913
|
-
|
|
10773
|
+
const recommended = recommendModel(specs);
|
|
10774
|
+
process.stdout.write(` ${c2.cyan("\u25CF")} Recommended model based on your system:
|
|
10775
|
+
|
|
10776
|
+
`);
|
|
10777
|
+
const localVariants = QWEN_VARIANTS.filter((v) => !v.cloud);
|
|
10778
|
+
for (let i = 0; i < localVariants.length; i++) {
|
|
10779
|
+
const v = localVariants[i];
|
|
10780
|
+
const fits = v.sizeGB <= Math.max(specs.gpuVramGB, specs.availableRamGB) * 0.8;
|
|
10781
|
+
const isRec = v.tag === recommended.tag;
|
|
10782
|
+
const marker = isRec ? c2.green("\u2192") : fits ? c2.dim(" ") : c2.red("\u2716");
|
|
10783
|
+
const name = isRec ? c2.bold(c2.green(v.tag)) : fits ? v.tag : c2.dim(v.tag);
|
|
10784
|
+
const label = isRec ? c2.bold(v.label) : c2.dim(v.label);
|
|
10785
|
+
const tooLarge = !fits && !v.cloud ? c2.red(" (exceeds available memory)") : "";
|
|
10786
|
+
process.stdout.write(` ${marker} ${String(i + 1).padStart(2)}. ${name.padEnd(isRec ? 45 : 25)} ${label}${tooLarge}
|
|
10787
|
+
`);
|
|
10914
10788
|
}
|
|
10915
|
-
|
|
10916
|
-
|
|
10917
|
-
|
|
10918
|
-
|
|
10919
|
-
|
|
10920
|
-
|
|
10789
|
+
process.stdout.write(`
|
|
10790
|
+
${c2.dim(" ")} ${String(localVariants.length + 1).padStart(2)}. ${c2.dim("qwen3.5:cloud")} ${c2.dim("Ollama Cloud")}
|
|
10791
|
+
`);
|
|
10792
|
+
process.stdout.write(` ${c2.dim(" ")} ${String(localVariants.length + 2).padStart(2)}. ${c2.dim("qwen3.5:397b-cloud")} ${c2.dim("397B Ollama Cloud")}
|
|
10793
|
+
`);
|
|
10794
|
+
process.stdout.write("\n");
|
|
10795
|
+
const pullChoice = await ask(rl, ` ${c2.bold("Select a model to pull")} (1-${localVariants.length + 2}, or Enter for recommended): `);
|
|
10796
|
+
const pullIdx = pullChoice ? parseInt(pullChoice, 10) : 0;
|
|
10797
|
+
let selectedVariant;
|
|
10798
|
+
if (pullIdx === 0 || isNaN(pullIdx)) {
|
|
10799
|
+
selectedVariant = recommended;
|
|
10800
|
+
} else if (pullIdx <= localVariants.length) {
|
|
10801
|
+
selectedVariant = localVariants[pullIdx - 1];
|
|
10802
|
+
} else if (pullIdx === localVariants.length + 1) {
|
|
10803
|
+
selectedVariant = QWEN_VARIANTS.find((v) => v.tag === "qwen3.5:cloud");
|
|
10804
|
+
} else {
|
|
10805
|
+
selectedVariant = QWEN_VARIANTS.find((v) => v.tag === "qwen3.5:397b-cloud");
|
|
10921
10806
|
}
|
|
10922
|
-
|
|
10923
|
-
|
|
10924
|
-
|
|
10925
|
-
|
|
10926
|
-
|
|
10927
|
-
|
|
10928
|
-
|
|
10929
|
-
|
|
10930
|
-
const candidates = [
|
|
10931
|
-
join28(thisDir, "..", "package.json"),
|
|
10932
|
-
join28(thisDir, "..", "..", "package.json"),
|
|
10933
|
-
join28(thisDir, "..", "..", "..", "package.json")
|
|
10934
|
-
];
|
|
10935
|
-
for (const pkgPath of candidates) {
|
|
10936
|
-
if (existsSync19(pkgPath)) {
|
|
10937
|
-
const pkg = req(pkgPath);
|
|
10938
|
-
if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
|
|
10939
|
-
currentVersion = pkg.version ?? "0.0.0";
|
|
10940
|
-
break;
|
|
10941
|
-
}
|
|
10942
|
-
}
|
|
10943
|
-
}
|
|
10944
|
-
} catch {
|
|
10807
|
+
const confirmPull = await ask(rl, `
|
|
10808
|
+
Pull ${c2.bold(selectedVariant.tag)} (${selectedVariant.label})? (Y/n) `);
|
|
10809
|
+
if (confirmPull.toLowerCase() === "n") {
|
|
10810
|
+
process.stdout.write(`
|
|
10811
|
+
${c2.dim("Skipping model pull. You can pull manually with: ollama pull <model>")}
|
|
10812
|
+
|
|
10813
|
+
`);
|
|
10814
|
+
return config.model;
|
|
10945
10815
|
}
|
|
10946
10816
|
process.stdout.write(`
|
|
10947
|
-
${c2.cyan("\u25CF")}
|
|
10817
|
+
${c2.cyan("\u25CF")} Pulling ${c2.bold(selectedVariant.tag)}... (this may take a while)
|
|
10948
10818
|
`);
|
|
10949
|
-
|
|
10950
|
-
|
|
10951
|
-
process.stdout.write(`
|
|
10819
|
+
try {
|
|
10820
|
+
pullModelWithAutoUpdate(selectedVariant.tag);
|
|
10821
|
+
process.stdout.write(`
|
|
10822
|
+
${c2.green("\u2714")} Model ${c2.bold(selectedVariant.tag)} pulled successfully.
|
|
10952
10823
|
|
|
10953
10824
|
`);
|
|
10954
|
-
|
|
10825
|
+
} catch (err) {
|
|
10826
|
+
renderError(`Failed to pull model: ${err instanceof Error ? err.message : String(err)}`);
|
|
10827
|
+
renderInfo("Try manually: ollama pull " + selectedVariant.tag);
|
|
10828
|
+
return config.model;
|
|
10955
10829
|
}
|
|
10956
|
-
|
|
10830
|
+
if (!selectedVariant.cloud) {
|
|
10831
|
+
const ctx = calculateContextWindow(specs, selectedVariant.sizeGB);
|
|
10832
|
+
const customName = `open-agents-${selectedVariant.tag.replace(":", "-").replace(".", "")}`;
|
|
10833
|
+
process.stdout.write(` ${c2.cyan("\u25CF")} Context window recommendation: ${c2.bold(ctx.label)} (${ctx.numCtx} tokens)
|
|
10957
10834
|
`);
|
|
10958
|
-
|
|
10835
|
+
process.stdout.write(` ${c2.dim(`Based on ${specs.totalRamGB.toFixed(0)} GB RAM, ${selectedVariant.sizeGB} GB model`)}
|
|
10959
10836
|
|
|
10960
10837
|
`);
|
|
10961
|
-
|
|
10962
|
-
|
|
10963
|
-
|
|
10964
|
-
|
|
10965
|
-
|
|
10966
|
-
|
|
10838
|
+
const createModelfile = await ask(rl, ` Create optimized model "${c2.bold(customName)}" with ${ctx.label} context? (Y/n) `);
|
|
10839
|
+
if (createModelfile.toLowerCase() !== "n") {
|
|
10840
|
+
try {
|
|
10841
|
+
const modelfileContent = [
|
|
10842
|
+
`FROM ${selectedVariant.tag}`,
|
|
10843
|
+
`PARAMETER num_ctx ${ctx.numCtx}`,
|
|
10844
|
+
`PARAMETER temperature 0`,
|
|
10845
|
+
`PARAMETER num_predict 16384`,
|
|
10846
|
+
`PARAMETER stop "<|endoftext|>"`
|
|
10847
|
+
].join("\n");
|
|
10848
|
+
const modelDir2 = join18(homedir8(), ".open-agents", "models");
|
|
10849
|
+
mkdirSync7(modelDir2, { recursive: true });
|
|
10850
|
+
const modelfilePath = join18(modelDir2, `Modelfile.${customName}`);
|
|
10851
|
+
writeFileSync7(modelfilePath, modelfileContent + "\n", "utf8");
|
|
10852
|
+
process.stdout.write(` ${c2.dim("Creating model...")} `);
|
|
10853
|
+
execSync10(`ollama create ${customName} -f ${modelfilePath}`, {
|
|
10854
|
+
stdio: "pipe",
|
|
10855
|
+
timeout: 12e4
|
|
10856
|
+
});
|
|
10857
|
+
process.stdout.write(`${c2.green("\u2714")}
|
|
10858
|
+
`);
|
|
10859
|
+
setConfigValue("model", customName);
|
|
10860
|
+
process.stdout.write(`
|
|
10861
|
+
${c2.green("\u2714")} Model ${c2.bold(customName)} created with ${ctx.label} context.
|
|
10862
|
+
`);
|
|
10863
|
+
process.stdout.write(` ${c2.green("\u2714")} Saved as default model in config.
|
|
10864
|
+
|
|
10865
|
+
`);
|
|
10866
|
+
return customName;
|
|
10867
|
+
} catch (err) {
|
|
10868
|
+
renderWarning(`Could not create custom model: ${err instanceof Error ? err.message : String(err)}`);
|
|
10869
|
+
renderInfo(`Using base model ${selectedVariant.tag} instead.`);
|
|
10870
|
+
}
|
|
10967
10871
|
}
|
|
10968
|
-
|
|
10872
|
+
setConfigValue("model", selectedVariant.tag);
|
|
10873
|
+
process.stdout.write(`
|
|
10874
|
+
${c2.green("\u2714")} Saved ${c2.bold(selectedVariant.tag)} as default model.
|
|
10875
|
+
|
|
10876
|
+
`);
|
|
10877
|
+
return selectedVariant.tag;
|
|
10878
|
+
}
|
|
10879
|
+
setConfigValue("model", selectedVariant.tag);
|
|
10880
|
+
process.stdout.write(`
|
|
10881
|
+
${c2.green("\u2714")} Saved ${c2.bold(selectedVariant.tag)} as default model.
|
|
10882
|
+
|
|
10883
|
+
`);
|
|
10884
|
+
return selectedVariant.tag;
|
|
10969
10885
|
}
|
|
10970
|
-
async function
|
|
10886
|
+
async function isModelAvailable(config) {
|
|
10971
10887
|
try {
|
|
10972
|
-
const models = await fetchOllamaModels(
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
|
|
10976
|
-
renderInfo("Available models:");
|
|
10977
|
-
for (const m of models.slice(0, 10)) {
|
|
10978
|
-
renderInfo(` ${m.name}`);
|
|
10979
|
-
}
|
|
10980
|
-
return;
|
|
10981
|
-
}
|
|
10982
|
-
const oldModel = ctx.config.model;
|
|
10983
|
-
ctx.setModel(match.name);
|
|
10984
|
-
if (local) {
|
|
10985
|
-
ctx.saveLocalSettings({ model: match.name });
|
|
10986
|
-
} else {
|
|
10987
|
-
ctx.saveSettings({ model: match.name });
|
|
10988
|
-
}
|
|
10989
|
-
renderModelSwitch(oldModel, match.name);
|
|
10990
|
-
if (local) {
|
|
10991
|
-
renderInfo("Saved as project-local override.");
|
|
10992
|
-
}
|
|
10993
|
-
} catch (err) {
|
|
10994
|
-
renderError(`Failed to switch model: ${err instanceof Error ? err.message : String(err)}`);
|
|
10888
|
+
const models = await fetchOllamaModels(config.backendUrl);
|
|
10889
|
+
return !!findModel(models, config.model);
|
|
10890
|
+
} catch {
|
|
10891
|
+
return false;
|
|
10995
10892
|
}
|
|
10996
10893
|
}
|
|
10997
|
-
|
|
10998
|
-
|
|
10999
|
-
"
|
|
11000
|
-
|
|
11001
|
-
|
|
11002
|
-
init_dist2();
|
|
11003
|
-
init_config();
|
|
11004
|
-
init_updater();
|
|
11005
|
-
init_oa_directory();
|
|
10894
|
+
function isFirstRun() {
|
|
10895
|
+
try {
|
|
10896
|
+
return !existsSync13(join18(homedir8(), ".open-agents", "config.json"));
|
|
10897
|
+
} catch {
|
|
10898
|
+
return true;
|
|
11006
10899
|
}
|
|
11007
|
-
}
|
|
11008
|
-
|
|
11009
|
-
|
|
11010
|
-
|
|
11011
|
-
|
|
11012
|
-
|
|
11013
|
-
|
|
11014
|
-
|
|
11015
|
-
function detectSystemSpecs() {
|
|
11016
|
-
let totalRamGB = 0;
|
|
11017
|
-
let availableRamGB = 0;
|
|
11018
|
-
let gpuVramGB = 0;
|
|
11019
|
-
let gpuName = "";
|
|
10900
|
+
}
|
|
10901
|
+
function expandedModelName(baseModel) {
|
|
10902
|
+
return `open-agents-${baseModel.replace(":", "-").replace(/\./g, "")}`;
|
|
10903
|
+
}
|
|
10904
|
+
async function checkExpandedVariant(modelName, backendUrl) {
|
|
10905
|
+
if (modelName.startsWith("open-agents-"))
|
|
10906
|
+
return null;
|
|
10907
|
+
const target = expandedModelName(modelName);
|
|
11020
10908
|
try {
|
|
11021
|
-
const
|
|
11022
|
-
|
|
11023
|
-
|
|
11024
|
-
});
|
|
11025
|
-
if (memInfo.includes("Mem:")) {
|
|
11026
|
-
const match = memInfo.match(/^Mem:\s+(\d+)\s+\d+\s+\d+\s+\d+\s+\d+\s+(\d+)/m);
|
|
11027
|
-
if (match) {
|
|
11028
|
-
totalRamGB = parseInt(match[1], 10) / 1024 ** 3;
|
|
11029
|
-
availableRamGB = parseInt(match[2], 10) / 1024 ** 3;
|
|
11030
|
-
}
|
|
11031
|
-
} else {
|
|
11032
|
-
const bytes = parseInt(memInfo.trim(), 10);
|
|
11033
|
-
if (!isNaN(bytes)) {
|
|
11034
|
-
totalRamGB = bytes / 1024 ** 3;
|
|
11035
|
-
availableRamGB = totalRamGB * 0.7;
|
|
11036
|
-
}
|
|
11037
|
-
}
|
|
10909
|
+
const models = await fetchOllamaModels(backendUrl);
|
|
10910
|
+
const found = models.find((m) => m.name === target || m.name.startsWith(target + ":"));
|
|
10911
|
+
return found ? found.name : false;
|
|
11038
10912
|
} catch {
|
|
10913
|
+
return false;
|
|
11039
10914
|
}
|
|
10915
|
+
}
|
|
10916
|
+
function modelSizeGB(models, modelName) {
|
|
10917
|
+
const m = findModel(models, modelName);
|
|
10918
|
+
if (m)
|
|
10919
|
+
return m.sizeBytes / 1024 ** 3;
|
|
10920
|
+
const known = QWEN_VARIANTS.find((v) => modelName.includes(v.tag.split(":")[1] ?? ""));
|
|
10921
|
+
return known?.sizeGB ?? 4;
|
|
10922
|
+
}
|
|
10923
|
+
function createExpandedVariant(baseModel, specs, sizeGB) {
|
|
10924
|
+
const customName = expandedModelName(baseModel);
|
|
10925
|
+
const ctx = calculateContextWindow(specs, sizeGB);
|
|
11040
10926
|
try {
|
|
11041
|
-
const
|
|
11042
|
-
|
|
11043
|
-
|
|
11044
|
-
|
|
11045
|
-
|
|
11046
|
-
|
|
11047
|
-
|
|
11048
|
-
|
|
11049
|
-
|
|
11050
|
-
|
|
11051
|
-
|
|
11052
|
-
}
|
|
10927
|
+
const modelfileContent = [
|
|
10928
|
+
`FROM ${baseModel}`,
|
|
10929
|
+
`PARAMETER num_ctx ${ctx.numCtx}`,
|
|
10930
|
+
`PARAMETER temperature 0`,
|
|
10931
|
+
`PARAMETER num_predict 16384`,
|
|
10932
|
+
`PARAMETER stop "<|endoftext|>"`
|
|
10933
|
+
].join("\n");
|
|
10934
|
+
const modelDir2 = join18(homedir8(), ".open-agents", "models");
|
|
10935
|
+
mkdirSync7(modelDir2, { recursive: true });
|
|
10936
|
+
const modelfilePath = join18(modelDir2, `Modelfile.${customName}`);
|
|
10937
|
+
writeFileSync7(modelfilePath, modelfileContent + "\n", "utf8");
|
|
10938
|
+
execSync10(`ollama create ${customName} -f ${modelfilePath}`, {
|
|
10939
|
+
stdio: "pipe",
|
|
10940
|
+
timeout: 12e4
|
|
10941
|
+
});
|
|
10942
|
+
return customName;
|
|
11053
10943
|
} catch {
|
|
10944
|
+
return null;
|
|
11054
10945
|
}
|
|
11055
|
-
return {
|
|
11056
|
-
totalRamGB: Math.round(totalRamGB * 10) / 10,
|
|
11057
|
-
availableRamGB: Math.round(availableRamGB * 10) / 10,
|
|
11058
|
-
gpuVramGB: Math.round(gpuVramGB * 10) / 10,
|
|
11059
|
-
gpuName
|
|
11060
|
-
};
|
|
11061
10946
|
}
|
|
11062
|
-
function
|
|
11063
|
-
|
|
11064
|
-
|
|
11065
|
-
|
|
11066
|
-
|
|
11067
|
-
|
|
11068
|
-
|
|
10947
|
+
async function ensureExpandedContext(modelName, backendUrl) {
|
|
10948
|
+
if (modelName.startsWith("open-agents-")) {
|
|
10949
|
+
const specs2 = detectSystemSpecs();
|
|
10950
|
+
const ctx2 = calculateContextWindow(specs2, 4);
|
|
10951
|
+
return { model: modelName, created: false, contextLabel: ctx2.label, numCtx: ctx2.numCtx };
|
|
10952
|
+
}
|
|
10953
|
+
if (modelName.includes("cloud") || modelName.includes(":cloud")) {
|
|
10954
|
+
return { model: modelName, created: false, contextLabel: "remote", numCtx: 0 };
|
|
10955
|
+
}
|
|
10956
|
+
const existing = await checkExpandedVariant(modelName, backendUrl);
|
|
10957
|
+
if (existing === null) {
|
|
10958
|
+
return { model: modelName, created: false, contextLabel: "", numCtx: 0 };
|
|
10959
|
+
}
|
|
10960
|
+
const specs = detectSystemSpecs();
|
|
10961
|
+
if (typeof existing === "string") {
|
|
10962
|
+
let sizeGB2 = 4;
|
|
10963
|
+
try {
|
|
10964
|
+
const models = await fetchOllamaModels(backendUrl);
|
|
10965
|
+
sizeGB2 = modelSizeGB(models, modelName);
|
|
10966
|
+
} catch {
|
|
11069
10967
|
}
|
|
10968
|
+
const ctx2 = calculateContextWindow(specs, sizeGB2);
|
|
10969
|
+
return { model: existing, created: false, contextLabel: ctx2.label, numCtx: ctx2.numCtx };
|
|
11070
10970
|
}
|
|
11071
|
-
|
|
11072
|
-
|
|
11073
|
-
|
|
11074
|
-
|
|
11075
|
-
|
|
11076
|
-
if (remaining >= 200)
|
|
11077
|
-
return { numCtx: 131072, label: "128K" };
|
|
11078
|
-
if (remaining >= 100)
|
|
11079
|
-
return { numCtx: 65536, label: "64K" };
|
|
11080
|
-
if (remaining >= 50)
|
|
11081
|
-
return { numCtx: 32768, label: "32K" };
|
|
11082
|
-
if (remaining >= 20)
|
|
11083
|
-
return { numCtx: 16384, label: "16K" };
|
|
11084
|
-
if (remaining >= 8)
|
|
11085
|
-
return { numCtx: 8192, label: "8K" };
|
|
11086
|
-
return { numCtx: 4096, label: "4K" };
|
|
11087
|
-
}
|
|
11088
|
-
function modelSupportsToolCalling(modelName) {
|
|
11089
|
-
const lower = modelName.toLowerCase();
|
|
11090
|
-
for (const known of TOOL_CALLING_MODELS) {
|
|
11091
|
-
if (lower.startsWith(known) || lower.includes(known))
|
|
11092
|
-
return true;
|
|
10971
|
+
let sizeGB = 4;
|
|
10972
|
+
try {
|
|
10973
|
+
const models = await fetchOllamaModels(backendUrl);
|
|
10974
|
+
sizeGB = modelSizeGB(models, modelName);
|
|
10975
|
+
} catch {
|
|
11093
10976
|
}
|
|
11094
|
-
|
|
11095
|
-
|
|
11096
|
-
|
|
11097
|
-
|
|
11098
|
-
|
|
11099
|
-
}
|
|
10977
|
+
const ctx = calculateContextWindow(specs, sizeGB);
|
|
10978
|
+
const created = createExpandedVariant(modelName, specs, sizeGB);
|
|
10979
|
+
if (created) {
|
|
10980
|
+
return { model: created, created: true, contextLabel: ctx.label, numCtx: ctx.numCtx };
|
|
10981
|
+
}
|
|
10982
|
+
return { model: modelName, created: false, contextLabel: ctx.label, numCtx: ctx.numCtx };
|
|
11100
10983
|
}
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
11107
|
-
|
|
11108
|
-
|
|
11109
|
-
|
|
11110
|
-
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
11114
|
-
|
|
11115
|
-
|
|
11116
|
-
|
|
11117
|
-
|
|
11118
|
-
|
|
11119
|
-
|
|
11120
|
-
|
|
11121
|
-
|
|
11122
|
-
|
|
11123
|
-
|
|
11124
|
-
|
|
11125
|
-
|
|
11126
|
-
|
|
11127
|
-
|
|
11128
|
-
|
|
10984
|
+
var QWEN_VARIANTS, TOOL_CALLING_MODELS;
|
|
10985
|
+
var init_setup = __esm({
|
|
10986
|
+
"packages/cli/dist/tui/setup.js"() {
|
|
10987
|
+
"use strict";
|
|
10988
|
+
init_model_picker();
|
|
10989
|
+
init_render();
|
|
10990
|
+
init_config();
|
|
10991
|
+
QWEN_VARIANTS = [
|
|
10992
|
+
{ tag: "qwen3.5:0.8b", sizeGB: 1, label: "0.8B params (1.0 GB)", cloud: false },
|
|
10993
|
+
{ tag: "qwen3.5:2b", sizeGB: 2.7, label: "2B params (2.7 GB)", cloud: false },
|
|
10994
|
+
{ tag: "qwen3.5:4b", sizeGB: 3.4, label: "4B params (3.4 GB)", cloud: false },
|
|
10995
|
+
{ tag: "qwen3.5:9b", sizeGB: 6.6, label: "9B params (6.6 GB) \u2014 recommended minimum", cloud: false },
|
|
10996
|
+
{ tag: "qwen3.5:27b", sizeGB: 17, label: "27B params (17 GB)", cloud: false },
|
|
10997
|
+
{ tag: "qwen3.5:35b", sizeGB: 24, label: "35B params (24 GB)", cloud: false },
|
|
10998
|
+
{ tag: "qwen3.5:122b", sizeGB: 81, label: "122B params (81 GB) \u2014 best local", cloud: false },
|
|
10999
|
+
{ tag: "qwen3.5:cloud", sizeGB: 0, label: "Cloud (Ollama Cloud)", cloud: true },
|
|
11000
|
+
{ tag: "qwen3.5:397b-cloud", sizeGB: 0, label: "397B Cloud (Ollama Cloud)", cloud: true }
|
|
11001
|
+
];
|
|
11002
|
+
TOOL_CALLING_MODELS = /* @__PURE__ */ new Set([
|
|
11003
|
+
"qwen3.5",
|
|
11004
|
+
"qwen3",
|
|
11005
|
+
"qwen2.5",
|
|
11006
|
+
"llama3.3",
|
|
11007
|
+
"llama3.1",
|
|
11008
|
+
"mistral",
|
|
11009
|
+
"mixtral",
|
|
11010
|
+
"command-r",
|
|
11011
|
+
"gemma3",
|
|
11012
|
+
"devstral",
|
|
11013
|
+
"deepseek"
|
|
11014
|
+
]);
|
|
11015
|
+
}
|
|
11016
|
+
});
|
|
11129
11017
|
|
|
11130
|
-
|
|
11131
|
-
|
|
11132
|
-
|
|
11133
|
-
|
|
11134
|
-
|
|
11135
|
-
|
|
11136
|
-
|
|
11137
|
-
|
|
11138
|
-
|
|
11139
|
-
|
|
11140
|
-
|
|
11018
|
+
// packages/cli/dist/tui/commands.js
|
|
11019
|
+
async function handleSlashCommand(input, ctx) {
|
|
11020
|
+
const trimmed = input.trim();
|
|
11021
|
+
if (!trimmed.startsWith("/"))
|
|
11022
|
+
return "not_a_command";
|
|
11023
|
+
const [cmd, ...rest] = trimmed.slice(1).split(/\s+/);
|
|
11024
|
+
const hasLocal = rest.includes("--local");
|
|
11025
|
+
const filteredRest = rest.filter((r) => r !== "--local");
|
|
11026
|
+
const arg = filteredRest.join(" ").trim();
|
|
11027
|
+
switch (cmd) {
|
|
11028
|
+
case "help":
|
|
11029
|
+
case "h":
|
|
11030
|
+
case "?":
|
|
11031
|
+
renderSlashHelp();
|
|
11032
|
+
return "handled";
|
|
11033
|
+
case "quit":
|
|
11034
|
+
case "exit":
|
|
11035
|
+
case "q":
|
|
11036
|
+
return "exit";
|
|
11037
|
+
case "clear":
|
|
11038
|
+
case "cls":
|
|
11039
|
+
ctx.clearScreen();
|
|
11040
|
+
return "handled";
|
|
11041
|
+
case "verbose":
|
|
11042
|
+
case "v":
|
|
11043
|
+
ctx.setVerbose(!ctx.config.verbose);
|
|
11044
|
+
if (hasLocal) {
|
|
11045
|
+
ctx.saveLocalSettings({ verbose: ctx.config.verbose });
|
|
11046
|
+
renderInfo(`Verbose mode: ${ctx.config.verbose ? "on" : "off"} (project-local)`);
|
|
11047
|
+
} else {
|
|
11048
|
+
ctx.saveSettings({ verbose: ctx.config.verbose });
|
|
11049
|
+
renderInfo(`Verbose mode: ${ctx.config.verbose ? "on" : "off"}`);
|
|
11141
11050
|
}
|
|
11142
|
-
|
|
11143
|
-
|
|
11051
|
+
return "handled";
|
|
11052
|
+
case "config":
|
|
11053
|
+
case "cfg":
|
|
11054
|
+
renderConfig({
|
|
11055
|
+
model: ctx.config.model,
|
|
11056
|
+
backendType: ctx.config.backendType,
|
|
11057
|
+
backendUrl: ctx.config.backendUrl,
|
|
11058
|
+
timeoutMs: String(ctx.config.timeoutMs),
|
|
11059
|
+
maxRetries: String(ctx.config.maxRetries),
|
|
11060
|
+
verbose: String(ctx.config.verbose),
|
|
11061
|
+
dryRun: String(ctx.config.dryRun)
|
|
11062
|
+
});
|
|
11063
|
+
return "handled";
|
|
11064
|
+
case "model":
|
|
11065
|
+
if (arg) {
|
|
11066
|
+
await switchModel(arg, ctx, hasLocal);
|
|
11067
|
+
} else {
|
|
11068
|
+
await showModelPicker(ctx);
|
|
11069
|
+
}
|
|
11070
|
+
return "handled";
|
|
11071
|
+
case "models":
|
|
11072
|
+
await listModels(ctx);
|
|
11073
|
+
return "handled";
|
|
11074
|
+
case "endpoint":
|
|
11075
|
+
case "ep":
|
|
11076
|
+
await handleEndpoint(arg, ctx, hasLocal);
|
|
11077
|
+
return "handled";
|
|
11078
|
+
case "update":
|
|
11079
|
+
case "upgrade":
|
|
11080
|
+
await handleUpdate(arg, ctx.repoRoot);
|
|
11081
|
+
return "handled";
|
|
11082
|
+
case "voice": {
|
|
11083
|
+
const save = hasLocal ? ctx.saveLocalSettings.bind(ctx) : ctx.saveSettings.bind(ctx);
|
|
11084
|
+
if (arg) {
|
|
11085
|
+
const msg = await ctx.voiceSetModel(arg);
|
|
11086
|
+
save({ voice: true, voiceModel: arg });
|
|
11087
|
+
renderInfo(msg + (hasLocal ? " (project-local)" : ""));
|
|
11088
|
+
} else {
|
|
11089
|
+
const msg = await ctx.voiceToggle();
|
|
11090
|
+
const isOn = msg.toLowerCase().includes("enabled") || msg.toLowerCase().includes("on");
|
|
11091
|
+
save({ voice: isOn });
|
|
11092
|
+
renderInfo(msg + (hasLocal ? " (project-local)" : ""));
|
|
11093
|
+
}
|
|
11094
|
+
return "handled";
|
|
11144
11095
|
}
|
|
11145
|
-
|
|
11146
|
-
|
|
11147
|
-
|
|
11148
|
-
|
|
11149
|
-
|
|
11150
|
-
|
|
11151
|
-
|
|
11152
|
-
|
|
11153
|
-
|
|
11154
|
-
|
|
11155
|
-
|
|
11156
|
-
|
|
11157
|
-
|
|
11158
|
-
}
|
|
11159
|
-
|
|
11160
|
-
|
|
11161
|
-
|
|
11096
|
+
case "stream": {
|
|
11097
|
+
const isOn = ctx.streamToggle();
|
|
11098
|
+
const save = hasLocal ? ctx.saveLocalSettings.bind(ctx) : ctx.saveSettings.bind(ctx);
|
|
11099
|
+
save({ stream: isOn });
|
|
11100
|
+
renderInfo(`Token streaming: ${isOn ? "on" : "off"}${hasLocal ? " (project-local)" : ""}` + (isOn ? " \u2014 thinking tokens in grey italics, responses with pastel syntax highlighting" : ""));
|
|
11101
|
+
return "handled";
|
|
11102
|
+
}
|
|
11103
|
+
case "tools": {
|
|
11104
|
+
const tools = listCustomToolFiles(ctx.repoRoot);
|
|
11105
|
+
if (tools.length === 0) {
|
|
11106
|
+
renderInfo("No custom tools installed.");
|
|
11107
|
+
renderInfo("The agent will automatically create tools when it detects repeated workflows (3+ times).");
|
|
11108
|
+
renderInfo('Or ask the agent: "create a tool for [workflow]"');
|
|
11109
|
+
} else {
|
|
11110
|
+
process.stdout.write(`
|
|
11111
|
+
${c2.bold("Custom Tools:")}
|
|
11112
|
+
|
|
11162
11113
|
`);
|
|
11163
|
-
|
|
11114
|
+
for (const t of tools) {
|
|
11115
|
+
process.stdout.write(` ${c2.cyan(t.name.padEnd(28))} ${c2.dim(`(${t.scope}, v${t.version}, ${t.stepsCount} steps)`)}
|
|
11164
11116
|
`);
|
|
11165
|
-
|
|
11166
|
-
|
|
11117
|
+
process.stdout.write(` ${"".padEnd(28)} ${t.description}
|
|
11167
11118
|
`);
|
|
11168
|
-
|
|
11119
|
+
}
|
|
11120
|
+
process.stdout.write("\n");
|
|
11121
|
+
}
|
|
11122
|
+
return "handled";
|
|
11123
|
+
}
|
|
11124
|
+
case "skills":
|
|
11125
|
+
case "skill": {
|
|
11126
|
+
const skills = discoverSkills(ctx.repoRoot);
|
|
11127
|
+
if (skills.length === 0) {
|
|
11128
|
+
renderInfo("No skills found.");
|
|
11129
|
+
renderInfo("Install AIWG to get skills: npm i -g aiwg && aiwg use sdlc");
|
|
11130
|
+
renderInfo("Or add skills manually to .oa/skills/{name}/SKILL.md");
|
|
11131
|
+
} else {
|
|
11132
|
+
let filtered = skills;
|
|
11133
|
+
if (arg) {
|
|
11134
|
+
const q = arg.toLowerCase();
|
|
11135
|
+
filtered = skills.filter((s) => s.name.toLowerCase().includes(q) || s.description.toLowerCase().includes(q) || s.triggers.some((t) => t.toLowerCase().includes(q)));
|
|
11136
|
+
}
|
|
11137
|
+
if (filtered.length === 0) {
|
|
11138
|
+
renderWarning(`No skills matching "${arg}". Showing all ${skills.length} skills:`);
|
|
11139
|
+
filtered = skills;
|
|
11140
|
+
}
|
|
11141
|
+
const bySource = /* @__PURE__ */ new Map();
|
|
11142
|
+
for (const s of filtered) {
|
|
11143
|
+
const group = bySource.get(s.source) ?? [];
|
|
11144
|
+
group.push(s);
|
|
11145
|
+
bySource.set(s.source, group);
|
|
11146
|
+
}
|
|
11147
|
+
process.stdout.write(`
|
|
11148
|
+
${c2.bold(`Available Skills (${filtered.length}):`)}
|
|
11169
11149
|
`);
|
|
11170
|
-
|
|
11171
|
-
|
|
11150
|
+
for (const [source, group] of bySource) {
|
|
11151
|
+
process.stdout.write(`
|
|
11152
|
+
${c2.dim(`\u2500\u2500 ${source} (${group.length}) \u2500\u2500`)}
|
|
11172
11153
|
`);
|
|
11173
|
-
|
|
11174
|
-
|
|
11154
|
+
for (const s of group) {
|
|
11155
|
+
process.stdout.write(` ${c2.cyan(s.name.padEnd(32))} ${s.description.slice(0, 60)}
|
|
11175
11156
|
`);
|
|
11176
|
-
|
|
11177
|
-
|
|
11157
|
+
if (s.triggers.length > 0) {
|
|
11158
|
+
process.stdout.write(` ${"".padEnd(32)} ${c2.dim(`triggers: ${s.triggers.slice(0, 3).join(" | ")}`)}
|
|
11178
11159
|
`);
|
|
11160
|
+
}
|
|
11161
|
+
}
|
|
11162
|
+
}
|
|
11163
|
+
process.stdout.write("\n");
|
|
11164
|
+
renderInfo('Invoke directly: /<skill-name> [args] (e.g. /ralph "fix tests" --completion "npm test passes")');
|
|
11165
|
+
renderInfo("Filter with: /skills <keyword>");
|
|
11166
|
+
}
|
|
11167
|
+
return "handled";
|
|
11168
|
+
}
|
|
11169
|
+
case "dream": {
|
|
11170
|
+
if (arg === "stop" || arg === "wake") {
|
|
11171
|
+
if (ctx.isDreaming?.()) {
|
|
11172
|
+
ctx.dreamStop?.();
|
|
11173
|
+
renderInfo("Waking up from dream mode...");
|
|
11174
|
+
} else {
|
|
11175
|
+
renderWarning("Not currently dreaming.");
|
|
11176
|
+
}
|
|
11177
|
+
} else if (ctx.isDreaming?.()) {
|
|
11178
|
+
renderWarning("Already dreaming. Use /dream stop to wake up first.");
|
|
11179
|
+
} else {
|
|
11180
|
+
const mode = arg === "lucid" ? "lucid" : arg === "deep" ? "deep" : "default";
|
|
11181
|
+
ctx.dreamStart?.(mode);
|
|
11182
|
+
}
|
|
11183
|
+
return "handled";
|
|
11184
|
+
}
|
|
11185
|
+
case "listen":
|
|
11186
|
+
case "mic": {
|
|
11187
|
+
if (!ctx.listenToggle) {
|
|
11188
|
+
renderWarning("Listen mode not available in this context.");
|
|
11189
|
+
return "handled";
|
|
11190
|
+
}
|
|
11191
|
+
if (arg === "stop" || arg === "off") {
|
|
11192
|
+
const msg2 = await (ctx.listenStop?.() ?? Promise.resolve("Not listening."));
|
|
11193
|
+
renderInfo(msg2);
|
|
11194
|
+
return "handled";
|
|
11195
|
+
}
|
|
11196
|
+
if (arg === "confirm") {
|
|
11197
|
+
const msg2 = ctx.listenSetMode?.("confirm") ?? "Confirm mode set.";
|
|
11198
|
+
renderInfo(msg2);
|
|
11199
|
+
return "handled";
|
|
11200
|
+
}
|
|
11201
|
+
if (arg === "auto") {
|
|
11202
|
+
const msg2 = ctx.listenSetMode?.("auto") ?? "Auto mode set.";
|
|
11203
|
+
renderInfo(msg2);
|
|
11204
|
+
return "handled";
|
|
11205
|
+
}
|
|
11206
|
+
const modelSizes = ["tiny", "base", "small", "medium", "large", "large-v3"];
|
|
11207
|
+
if (arg && modelSizes.includes(arg.toLowerCase())) {
|
|
11208
|
+
const model = arg.toLowerCase() === "large" ? "large-v3" : arg.toLowerCase();
|
|
11209
|
+
const msg2 = await (ctx.listenSetModel?.(model) ?? Promise.resolve(`Model set to ${model}.`));
|
|
11210
|
+
renderInfo(msg2);
|
|
11211
|
+
return "handled";
|
|
11212
|
+
}
|
|
11213
|
+
const msg = await ctx.listenToggle();
|
|
11214
|
+
renderInfo(msg);
|
|
11215
|
+
return "handled";
|
|
11216
|
+
}
|
|
11217
|
+
case "bruteforce":
|
|
11218
|
+
case "brute": {
|
|
11219
|
+
const isOn = ctx.bruteForceToggle();
|
|
11220
|
+
const save = hasLocal ? ctx.saveLocalSettings.bind(ctx) : ctx.saveSettings.bind(ctx);
|
|
11221
|
+
save({ bruteforce: isOn });
|
|
11222
|
+
renderInfo(`Brute-force mode: ${isOn ? "on" : "off"}${hasLocal ? " (project-local)" : ""}` + (isOn ? " \u2014 agent will auto re-engage when turn limit is hit, reassess and try creative strategies" : ""));
|
|
11223
|
+
return "handled";
|
|
11224
|
+
}
|
|
11225
|
+
case "emojis":
|
|
11226
|
+
case "emoji": {
|
|
11227
|
+
const current = ctx.getEmojis?.() ?? true;
|
|
11228
|
+
const next = !current;
|
|
11229
|
+
ctx.setEmojis?.(next);
|
|
11230
|
+
const save = hasLocal ? ctx.saveLocalSettings.bind(ctx) : ctx.saveSettings.bind(ctx);
|
|
11231
|
+
save({ emojis: next });
|
|
11232
|
+
renderInfo(`Emojis ${next ? "enabled" : "disabled"}.`);
|
|
11233
|
+
return "handled";
|
|
11234
|
+
}
|
|
11235
|
+
case "colors":
|
|
11236
|
+
case "color": {
|
|
11237
|
+
const current = ctx.getColors?.() ?? true;
|
|
11238
|
+
const next = !current;
|
|
11239
|
+
ctx.setColors?.(next);
|
|
11240
|
+
const save = hasLocal ? ctx.saveLocalSettings.bind(ctx) : ctx.saveSettings.bind(ctx);
|
|
11241
|
+
save({ colors: next });
|
|
11242
|
+
renderInfo(`Colors ${next ? "enabled" : "disabled"}.`);
|
|
11243
|
+
return "handled";
|
|
11244
|
+
}
|
|
11245
|
+
default: {
|
|
11246
|
+
const skills = discoverSkills(ctx.repoRoot);
|
|
11247
|
+
const skill = skills.find((s) => s.name === cmd || s.name === cmd.replace(/_/g, "-"));
|
|
11248
|
+
if (skill) {
|
|
11249
|
+
const content = loadSkillContent(skill.filePath);
|
|
11250
|
+
if (content) {
|
|
11251
|
+
renderInfo(`Loading skill: ${c2.bold(skill.name)} (${skill.source})`);
|
|
11252
|
+
return { type: "skill", name: skill.name, content, args: arg };
|
|
11253
|
+
}
|
|
11254
|
+
}
|
|
11255
|
+
renderWarning(`Unknown command: /${cmd}. Type /help for available commands.`);
|
|
11256
|
+
return "handled";
|
|
11257
|
+
}
|
|
11179
11258
|
}
|
|
11180
|
-
|
|
11181
|
-
|
|
11259
|
+
}
|
|
11260
|
+
async function listModels(ctx) {
|
|
11182
11261
|
try {
|
|
11183
|
-
models = await fetchOllamaModels(config.backendUrl);
|
|
11184
|
-
|
|
11185
|
-
|
|
11186
|
-
|
|
11187
|
-
renderInfo("Or use /endpoint to configure a remote backend after startup.");
|
|
11188
|
-
const answer = await ask(rl, `
|
|
11189
|
-
${c2.bold("Continue without Ollama?")} (y/n) `);
|
|
11190
|
-
if (answer.toLowerCase() !== "y")
|
|
11191
|
-
return null;
|
|
11192
|
-
return config.model;
|
|
11193
|
-
}
|
|
11194
|
-
const currentModel = findModel(models, config.model);
|
|
11195
|
-
if (currentModel) {
|
|
11196
|
-
process.stdout.write(` ${c2.green("\u2714")} Model ${c2.bold(currentModel.name)} is available.
|
|
11197
|
-
|
|
11198
|
-
`);
|
|
11199
|
-
return currentModel.name;
|
|
11262
|
+
const models = await fetchOllamaModels(ctx.config.backendUrl);
|
|
11263
|
+
renderModelList(models.map((m) => ({ name: m.name, size: m.size, modified: m.modified })), ctx.config.model);
|
|
11264
|
+
} catch (err) {
|
|
11265
|
+
renderError(`Failed to fetch models: ${err instanceof Error ? err.message : String(err)}`);
|
|
11200
11266
|
}
|
|
11201
|
-
|
|
11202
|
-
|
|
11203
|
-
|
|
11204
|
-
|
|
11205
|
-
|
|
11206
|
-
|
|
11207
|
-
|
|
11208
|
-
`);
|
|
11209
|
-
for (let i = 0; i < Math.min(toolCallingModels.length, 10); i++) {
|
|
11210
|
-
const m = toolCallingModels[i];
|
|
11211
|
-
process.stdout.write(` ${c2.bold(String(i + 1))}. ${m.name} ${c2.dim(`(${m.size})`)}
|
|
11212
|
-
`);
|
|
11213
|
-
}
|
|
11214
|
-
process.stdout.write(`
|
|
11215
|
-
${c2.dim("0")}. Pull a new qwen3.5 model instead
|
|
11216
|
-
`);
|
|
11217
|
-
process.stdout.write("\n");
|
|
11218
|
-
const choice = await ask(rl, ` ${c2.bold("Select a model")} (1-${Math.min(toolCallingModels.length, 10)}, or 0 to pull new): `);
|
|
11219
|
-
const idx = parseInt(choice, 10);
|
|
11220
|
-
if (idx > 0 && idx <= toolCallingModels.length) {
|
|
11221
|
-
const selected = toolCallingModels[idx - 1];
|
|
11222
|
-
setConfigValue("model", selected.name);
|
|
11223
|
-
process.stdout.write(`
|
|
11224
|
-
${c2.green("\u2714")} Selected ${c2.bold(selected.name)}. Saved to config.
|
|
11225
|
-
|
|
11226
|
-
`);
|
|
11227
|
-
return selected.name;
|
|
11267
|
+
}
|
|
11268
|
+
async function showModelPicker(ctx) {
|
|
11269
|
+
try {
|
|
11270
|
+
const models = await fetchOllamaModels(ctx.config.backendUrl);
|
|
11271
|
+
if (models.length === 0) {
|
|
11272
|
+
renderWarning("No models found. Pull a model with: ollama pull <model>");
|
|
11273
|
+
return;
|
|
11228
11274
|
}
|
|
11229
|
-
|
|
11230
|
-
|
|
11231
|
-
|
|
11232
|
-
`);
|
|
11275
|
+
renderModelList(models.map((m) => ({ name: m.name, size: m.size, modified: m.modified })), ctx.config.model);
|
|
11276
|
+
} catch (err) {
|
|
11277
|
+
renderError(`Failed to fetch models: ${err instanceof Error ? err.message : String(err)}`);
|
|
11233
11278
|
}
|
|
11234
|
-
|
|
11235
|
-
|
|
11279
|
+
}
|
|
11280
|
+
async function handleEndpoint(arg, ctx, local = false) {
|
|
11281
|
+
if (!arg) {
|
|
11282
|
+
process.stdout.write(`
|
|
11283
|
+
${c2.bold("Current endpoint:")}
|
|
11236
11284
|
|
|
11237
11285
|
`);
|
|
11238
|
-
|
|
11239
|
-
|
|
11240
|
-
|
|
11241
|
-
|
|
11242
|
-
|
|
11243
|
-
|
|
11244
|
-
const name = isRec ? c2.bold(c2.green(v.tag)) : fits ? v.tag : c2.dim(v.tag);
|
|
11245
|
-
const label = isRec ? c2.bold(v.label) : c2.dim(v.label);
|
|
11246
|
-
const tooLarge = !fits && !v.cloud ? c2.red(" (exceeds available memory)") : "";
|
|
11247
|
-
process.stdout.write(` ${marker} ${String(i + 1).padStart(2)}. ${name.padEnd(isRec ? 45 : 25)} ${label}${tooLarge}
|
|
11248
|
-
`);
|
|
11249
|
-
}
|
|
11250
|
-
process.stdout.write(`
|
|
11251
|
-
${c2.dim(" ")} ${String(localVariants.length + 1).padStart(2)}. ${c2.dim("qwen3.5:cloud")} ${c2.dim("Ollama Cloud")}
|
|
11252
|
-
`);
|
|
11253
|
-
process.stdout.write(` ${c2.dim(" ")} ${String(localVariants.length + 2).padStart(2)}. ${c2.dim("qwen3.5:397b-cloud")} ${c2.dim("397B Ollama Cloud")}
|
|
11254
|
-
`);
|
|
11255
|
-
process.stdout.write("\n");
|
|
11256
|
-
const pullChoice = await ask(rl, ` ${c2.bold("Select a model to pull")} (1-${localVariants.length + 2}, or Enter for recommended): `);
|
|
11257
|
-
const pullIdx = pullChoice ? parseInt(pullChoice, 10) : 0;
|
|
11258
|
-
let selectedVariant;
|
|
11259
|
-
if (pullIdx === 0 || isNaN(pullIdx)) {
|
|
11260
|
-
selectedVariant = recommended;
|
|
11261
|
-
} else if (pullIdx <= localVariants.length) {
|
|
11262
|
-
selectedVariant = localVariants[pullIdx - 1];
|
|
11263
|
-
} else if (pullIdx === localVariants.length + 1) {
|
|
11264
|
-
selectedVariant = QWEN_VARIANTS.find((v) => v.tag === "qwen3.5:cloud");
|
|
11265
|
-
} else {
|
|
11266
|
-
selectedVariant = QWEN_VARIANTS.find((v) => v.tag === "qwen3.5:397b-cloud");
|
|
11267
|
-
}
|
|
11268
|
-
const confirmPull = await ask(rl, `
|
|
11269
|
-
Pull ${c2.bold(selectedVariant.tag)} (${selectedVariant.label})? (Y/n) `);
|
|
11270
|
-
if (confirmPull.toLowerCase() === "n") {
|
|
11286
|
+
process.stdout.write(` ${c2.cyan("URL".padEnd(12))} ${ctx.config.backendUrl}
|
|
11287
|
+
`);
|
|
11288
|
+
process.stdout.write(` ${c2.cyan("Type".padEnd(12))} ${ctx.config.backendType}
|
|
11289
|
+
`);
|
|
11290
|
+
process.stdout.write(` ${c2.cyan("Auth".padEnd(12))} ${ctx.config.apiKey ? "Bearer token set" : "none"}
|
|
11291
|
+
`);
|
|
11271
11292
|
process.stdout.write(`
|
|
11272
|
-
${c2.dim("
|
|
11293
|
+
${c2.dim("Usage: /endpoint <url> [--auth <token>]")}
|
|
11294
|
+
`);
|
|
11295
|
+
process.stdout.write(` ${c2.dim(" /endpoint http://localhost:11434 (Ollama, no auth)")}
|
|
11296
|
+
`);
|
|
11297
|
+
process.stdout.write(` ${c2.dim(" /endpoint http://remote:8000/v1 --auth sk-... (OpenAI-compatible)")}
|
|
11298
|
+
`);
|
|
11299
|
+
process.stdout.write(` ${c2.dim(" /endpoint http://remote:8000/v1 (OpenAI-compatible, no auth)")}
|
|
11273
11300
|
|
|
11274
11301
|
`);
|
|
11275
|
-
return
|
|
11302
|
+
return;
|
|
11303
|
+
}
|
|
11304
|
+
const parts = arg.split(/\s+/);
|
|
11305
|
+
const url = parts[0];
|
|
11306
|
+
let apiKey;
|
|
11307
|
+
const authIdx = parts.indexOf("--auth");
|
|
11308
|
+
if (authIdx !== -1 && parts[authIdx + 1]) {
|
|
11309
|
+
apiKey = parts[authIdx + 1];
|
|
11310
|
+
}
|
|
11311
|
+
try {
|
|
11312
|
+
new URL(url);
|
|
11313
|
+
} catch {
|
|
11314
|
+
renderError(`Invalid URL: "${url}"`);
|
|
11315
|
+
return;
|
|
11316
|
+
}
|
|
11317
|
+
let backendType = "ollama";
|
|
11318
|
+
if (url.includes("/v1") || url.includes(":8000") || apiKey) {
|
|
11319
|
+
backendType = "vllm";
|
|
11276
11320
|
}
|
|
11277
11321
|
process.stdout.write(`
|
|
11278
|
-
${c2.
|
|
11279
|
-
`);
|
|
11322
|
+
${c2.dim("Testing connection...")} `);
|
|
11280
11323
|
try {
|
|
11281
|
-
|
|
11282
|
-
|
|
11283
|
-
|
|
11284
|
-
|
|
11324
|
+
const healthUrl = backendType === "ollama" ? `${url.replace(/\/$/, "")}/api/tags` : `${url.replace(/\/$/, "")}/models`;
|
|
11325
|
+
const headers = {};
|
|
11326
|
+
if (apiKey)
|
|
11327
|
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
11328
|
+
const resp = await fetch(healthUrl, {
|
|
11329
|
+
headers,
|
|
11330
|
+
signal: AbortSignal.timeout(1e4)
|
|
11331
|
+
});
|
|
11332
|
+
if (!resp.ok)
|
|
11333
|
+
throw new Error(`HTTP ${resp.status}`);
|
|
11334
|
+
process.stdout.write(`${c2.green("\u2714")} Connected
|
|
11285
11335
|
`);
|
|
11286
11336
|
} catch (err) {
|
|
11287
|
-
|
|
11288
|
-
|
|
11289
|
-
|
|
11337
|
+
process.stdout.write(`${c2.yellow("\u26A0")} Could not verify
|
|
11338
|
+
`);
|
|
11339
|
+
renderWarning(`Endpoint may not be reachable: ${err instanceof Error ? err.message : String(err)}`);
|
|
11340
|
+
renderInfo("Setting endpoint anyway \u2014 it may come online later.");
|
|
11290
11341
|
}
|
|
11291
|
-
|
|
11292
|
-
|
|
11293
|
-
|
|
11294
|
-
|
|
11342
|
+
ctx.setEndpoint(url, backendType, apiKey);
|
|
11343
|
+
const endpointSettings = { backendUrl: url, backendType, ...apiKey ? { apiKey } : {} };
|
|
11344
|
+
if (local) {
|
|
11345
|
+
ctx.saveLocalSettings(endpointSettings);
|
|
11346
|
+
} else {
|
|
11347
|
+
setConfigValue("backendUrl", url);
|
|
11348
|
+
setConfigValue("backendType", backendType);
|
|
11349
|
+
if (apiKey) {
|
|
11350
|
+
setConfigValue("apiKey", apiKey);
|
|
11351
|
+
}
|
|
11352
|
+
ctx.saveSettings(endpointSettings);
|
|
11353
|
+
}
|
|
11354
|
+
process.stdout.write(`
|
|
11355
|
+
${c2.green("\u2714")} Endpoint updated and saved${local ? " (project-local)" : ""}:
|
|
11295
11356
|
`);
|
|
11296
|
-
|
|
11297
|
-
|
|
11357
|
+
process.stdout.write(` ${c2.cyan("URL".padEnd(8))} ${url}
|
|
11298
11358
|
`);
|
|
11299
|
-
|
|
11300
|
-
if (createModelfile.toLowerCase() !== "n") {
|
|
11301
|
-
try {
|
|
11302
|
-
const modelfileContent = [
|
|
11303
|
-
`FROM ${selectedVariant.tag}`,
|
|
11304
|
-
`PARAMETER num_ctx ${ctx.numCtx}`,
|
|
11305
|
-
`PARAMETER temperature 0`,
|
|
11306
|
-
`PARAMETER num_predict 16384`,
|
|
11307
|
-
`PARAMETER stop "<|endoftext|>"`
|
|
11308
|
-
].join("\n");
|
|
11309
|
-
const modelDir2 = join18(homedir8(), ".open-agents", "models");
|
|
11310
|
-
mkdirSync7(modelDir2, { recursive: true });
|
|
11311
|
-
const modelfilePath = join18(modelDir2, `Modelfile.${customName}`);
|
|
11312
|
-
writeFileSync7(modelfilePath, modelfileContent + "\n", "utf8");
|
|
11313
|
-
process.stdout.write(` ${c2.dim("Creating model...")} `);
|
|
11314
|
-
execSync10(`ollama create ${customName} -f ${modelfilePath}`, {
|
|
11315
|
-
stdio: "pipe",
|
|
11316
|
-
timeout: 12e4
|
|
11317
|
-
});
|
|
11318
|
-
process.stdout.write(`${c2.green("\u2714")}
|
|
11359
|
+
process.stdout.write(` ${c2.cyan("Type".padEnd(8))} ${backendType}
|
|
11319
11360
|
`);
|
|
11320
|
-
|
|
11321
|
-
|
|
11322
|
-
${c2.green("\u2714")} Model ${c2.bold(customName)} created with ${ctx.label} context.
|
|
11361
|
+
if (apiKey) {
|
|
11362
|
+
process.stdout.write(` ${c2.cyan("Auth".padEnd(8))} Bearer ${apiKey.slice(0, 8)}...
|
|
11323
11363
|
`);
|
|
11324
|
-
|
|
11325
|
-
|
|
11364
|
+
} else {
|
|
11365
|
+
process.stdout.write(` ${c2.cyan("Auth".padEnd(8))} none
|
|
11326
11366
|
`);
|
|
11327
|
-
|
|
11328
|
-
|
|
11329
|
-
|
|
11330
|
-
|
|
11367
|
+
}
|
|
11368
|
+
process.stdout.write("\n");
|
|
11369
|
+
}
|
|
11370
|
+
async function handleUpdate(subcommand, repoRoot) {
|
|
11371
|
+
if (subcommand === "auto") {
|
|
11372
|
+
const settings = { updateMode: "auto" };
|
|
11373
|
+
saveProjectSettings(repoRoot, settings);
|
|
11374
|
+
saveGlobalSettings(settings);
|
|
11375
|
+
renderInfo("Update mode: auto \u2014 updates will install automatically after task completion.");
|
|
11376
|
+
return;
|
|
11377
|
+
}
|
|
11378
|
+
if (subcommand === "manual") {
|
|
11379
|
+
const settings = { updateMode: "manual" };
|
|
11380
|
+
saveProjectSettings(repoRoot, settings);
|
|
11381
|
+
saveGlobalSettings(settings);
|
|
11382
|
+
renderInfo("Update mode: manual \u2014 updates only install when you run /update.");
|
|
11383
|
+
return;
|
|
11384
|
+
}
|
|
11385
|
+
let currentVersion = "0.0.0";
|
|
11386
|
+
try {
|
|
11387
|
+
const { createRequire: createRequire4 } = await import("node:module");
|
|
11388
|
+
const { fileURLToPath: fileURLToPath3 } = await import("node:url");
|
|
11389
|
+
const { dirname: dirname5, join: join28 } = await import("node:path");
|
|
11390
|
+
const { existsSync: existsSync19 } = await import("node:fs");
|
|
11391
|
+
const req = createRequire4(import.meta.url);
|
|
11392
|
+
const thisDir = dirname5(fileURLToPath3(import.meta.url));
|
|
11393
|
+
const candidates = [
|
|
11394
|
+
join28(thisDir, "..", "package.json"),
|
|
11395
|
+
join28(thisDir, "..", "..", "package.json"),
|
|
11396
|
+
join28(thisDir, "..", "..", "..", "package.json")
|
|
11397
|
+
];
|
|
11398
|
+
for (const pkgPath of candidates) {
|
|
11399
|
+
if (existsSync19(pkgPath)) {
|
|
11400
|
+
const pkg = req(pkgPath);
|
|
11401
|
+
if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
|
|
11402
|
+
currentVersion = pkg.version ?? "0.0.0";
|
|
11403
|
+
break;
|
|
11404
|
+
}
|
|
11331
11405
|
}
|
|
11332
11406
|
}
|
|
11333
|
-
|
|
11334
|
-
process.stdout.write(`
|
|
11335
|
-
${c2.green("\u2714")} Saved ${c2.bold(selectedVariant.tag)} as default model.
|
|
11336
|
-
|
|
11337
|
-
`);
|
|
11338
|
-
return selectedVariant.tag;
|
|
11407
|
+
} catch {
|
|
11339
11408
|
}
|
|
11340
|
-
setConfigValue("model", selectedVariant.tag);
|
|
11341
11409
|
process.stdout.write(`
|
|
11342
|
-
${c2.
|
|
11410
|
+
${c2.cyan("\u25CF")} Checking for updates... ${c2.dim(`(current: v${currentVersion})`)}
|
|
11411
|
+
`);
|
|
11412
|
+
const info = await checkForUpdate(currentVersion, true);
|
|
11413
|
+
if (!info) {
|
|
11414
|
+
process.stdout.write(` ${c2.green("\u2714")} You're on the latest version (v${currentVersion}).
|
|
11343
11415
|
|
|
11344
11416
|
`);
|
|
11345
|
-
|
|
11346
|
-
}
|
|
11347
|
-
async function isModelAvailable(config) {
|
|
11348
|
-
try {
|
|
11349
|
-
const models = await fetchOllamaModels(config.backendUrl);
|
|
11350
|
-
return !!findModel(models, config.model);
|
|
11351
|
-
} catch {
|
|
11352
|
-
return false;
|
|
11417
|
+
return;
|
|
11353
11418
|
}
|
|
11419
|
+
process.stdout.write(` ${c2.yellow("\u26A0")} Update available: v${info.currentVersion} \u2192 v${c2.bold(c2.green(info.latestVersion))}
|
|
11420
|
+
`);
|
|
11421
|
+
process.stdout.write(` ${c2.cyan("\u25CF")} Installing in background...
|
|
11422
|
+
|
|
11423
|
+
`);
|
|
11424
|
+
const { exec } = await import("node:child_process");
|
|
11425
|
+
exec(`npm cache clean --force open-agents-ai 2>/dev/null; npm install -g open-agents-ai@latest --force`, { timeout: 18e4 }, (err) => {
|
|
11426
|
+
if (err) {
|
|
11427
|
+
renderWarning("Update install failed. Try manually: npm i -g open-agents-ai");
|
|
11428
|
+
} else {
|
|
11429
|
+
renderInfo(`${c2.green("\u2714")} Updated to v${info.latestVersion}. Takes effect next session.`);
|
|
11430
|
+
}
|
|
11431
|
+
});
|
|
11354
11432
|
}
|
|
11355
|
-
function
|
|
11433
|
+
async function switchModel(query, ctx, local = false) {
|
|
11356
11434
|
try {
|
|
11357
|
-
|
|
11358
|
-
|
|
11359
|
-
|
|
11435
|
+
const models = await fetchOllamaModels(ctx.config.backendUrl);
|
|
11436
|
+
const match = findModel(models, query);
|
|
11437
|
+
if (!match) {
|
|
11438
|
+
renderError(`Model not found: "${query}"`);
|
|
11439
|
+
renderInfo("Available models:");
|
|
11440
|
+
for (const m of models.slice(0, 10)) {
|
|
11441
|
+
renderInfo(` ${m.name}`);
|
|
11442
|
+
}
|
|
11443
|
+
return;
|
|
11444
|
+
}
|
|
11445
|
+
let finalModel = match.name;
|
|
11446
|
+
if (ctx.config.backendType === "ollama") {
|
|
11447
|
+
const result = await ensureExpandedContext(match.name, ctx.config.backendUrl);
|
|
11448
|
+
if (result.created) {
|
|
11449
|
+
renderInfo(`Created expanded context variant: ${c2.bold(result.model)} (${result.contextLabel}, ${result.numCtx} tokens)`);
|
|
11450
|
+
finalModel = result.model;
|
|
11451
|
+
} else if (result.model !== match.name) {
|
|
11452
|
+
renderInfo(`Using expanded context variant: ${c2.bold(result.model)} (${result.contextLabel})`);
|
|
11453
|
+
finalModel = result.model;
|
|
11454
|
+
}
|
|
11455
|
+
}
|
|
11456
|
+
const oldModel = ctx.config.model;
|
|
11457
|
+
ctx.setModel(finalModel);
|
|
11458
|
+
if (local) {
|
|
11459
|
+
ctx.saveLocalSettings({ model: finalModel });
|
|
11460
|
+
} else {
|
|
11461
|
+
ctx.saveSettings({ model: finalModel });
|
|
11462
|
+
}
|
|
11463
|
+
renderModelSwitch(oldModel, finalModel);
|
|
11464
|
+
if (local) {
|
|
11465
|
+
renderInfo("Saved as project-local override.");
|
|
11466
|
+
}
|
|
11467
|
+
} catch (err) {
|
|
11468
|
+
renderError(`Failed to switch model: ${err instanceof Error ? err.message : String(err)}`);
|
|
11360
11469
|
}
|
|
11361
11470
|
}
|
|
11362
|
-
var
|
|
11363
|
-
|
|
11364
|
-
"packages/cli/dist/tui/setup.js"() {
|
|
11471
|
+
var init_commands = __esm({
|
|
11472
|
+
"packages/cli/dist/tui/commands.js"() {
|
|
11365
11473
|
"use strict";
|
|
11366
11474
|
init_model_picker();
|
|
11367
11475
|
init_render();
|
|
11476
|
+
init_dist2();
|
|
11368
11477
|
init_config();
|
|
11369
|
-
|
|
11370
|
-
|
|
11371
|
-
|
|
11372
|
-
{ tag: "qwen3.5:4b", sizeGB: 3.4, label: "4B params (3.4 GB)", cloud: false },
|
|
11373
|
-
{ tag: "qwen3.5:9b", sizeGB: 6.6, label: "9B params (6.6 GB) \u2014 recommended minimum", cloud: false },
|
|
11374
|
-
{ tag: "qwen3.5:27b", sizeGB: 17, label: "27B params (17 GB)", cloud: false },
|
|
11375
|
-
{ tag: "qwen3.5:35b", sizeGB: 24, label: "35B params (24 GB)", cloud: false },
|
|
11376
|
-
{ tag: "qwen3.5:122b", sizeGB: 81, label: "122B params (81 GB) \u2014 best local", cloud: false },
|
|
11377
|
-
{ tag: "qwen3.5:cloud", sizeGB: 0, label: "Cloud (Ollama Cloud)", cloud: true },
|
|
11378
|
-
{ tag: "qwen3.5:397b-cloud", sizeGB: 0, label: "397B Cloud (Ollama Cloud)", cloud: true }
|
|
11379
|
-
];
|
|
11380
|
-
TOOL_CALLING_MODELS = /* @__PURE__ */ new Set([
|
|
11381
|
-
"qwen3.5",
|
|
11382
|
-
"qwen3",
|
|
11383
|
-
"qwen2.5",
|
|
11384
|
-
"llama3.3",
|
|
11385
|
-
"llama3.1",
|
|
11386
|
-
"mistral",
|
|
11387
|
-
"mixtral",
|
|
11388
|
-
"command-r",
|
|
11389
|
-
"gemma3",
|
|
11390
|
-
"devstral",
|
|
11391
|
-
"deepseek"
|
|
11392
|
-
]);
|
|
11478
|
+
init_updater();
|
|
11479
|
+
init_oa_directory();
|
|
11480
|
+
init_setup();
|
|
11393
11481
|
}
|
|
11394
11482
|
});
|
|
11395
11483
|
|
|
@@ -14965,6 +15053,19 @@ async function startInteractive(config, repoPath) {
|
|
|
14965
15053
|
config = { ...config, model: setupModel };
|
|
14966
15054
|
}
|
|
14967
15055
|
}
|
|
15056
|
+
if (config.backendType === "ollama" && !config.model.startsWith("open-agents-")) {
|
|
15057
|
+
try {
|
|
15058
|
+
const expandResult = await ensureExpandedContext(config.model, config.backendUrl);
|
|
15059
|
+
if (expandResult.created) {
|
|
15060
|
+
renderInfo(`Created expanded context model: ${expandResult.model} (${expandResult.contextLabel}, ${expandResult.numCtx} tokens)`);
|
|
15061
|
+
config = { ...config, model: expandResult.model };
|
|
15062
|
+
} else if (expandResult.model !== config.model) {
|
|
15063
|
+
renderInfo(`Using expanded context model: ${expandResult.model} (${expandResult.contextLabel})`);
|
|
15064
|
+
config = { ...config, model: expandResult.model };
|
|
15065
|
+
}
|
|
15066
|
+
} catch {
|
|
15067
|
+
}
|
|
15068
|
+
}
|
|
14968
15069
|
if (!isResumed) {
|
|
14969
15070
|
try {
|
|
14970
15071
|
const healthUrl = config.backendType === "ollama" ? `${config.backendUrl}/api/tags` : `${config.backendUrl}/v1/models`;
|
|
@@ -15486,6 +15587,15 @@ async function runWithTUI(task, config, repoPath) {
|
|
|
15486
15587
|
}
|
|
15487
15588
|
config = { ...config, model: setupModel };
|
|
15488
15589
|
}
|
|
15590
|
+
if (config.backendType === "ollama" && !config.model.startsWith("open-agents-")) {
|
|
15591
|
+
try {
|
|
15592
|
+
const expandResult = await ensureExpandedContext(config.model, config.backendUrl);
|
|
15593
|
+
if (expandResult.model !== config.model) {
|
|
15594
|
+
config = { ...config, model: expandResult.model };
|
|
15595
|
+
}
|
|
15596
|
+
} catch {
|
|
15597
|
+
}
|
|
15598
|
+
}
|
|
15489
15599
|
try {
|
|
15490
15600
|
const healthUrl = config.backendType === "ollama" ? `${config.backendUrl}/api/tags` : `${config.backendUrl}/v1/models`;
|
|
15491
15601
|
const resp = await fetch(healthUrl, { signal: AbortSignal.timeout(1e4) });
|