open-agents-ai 0.23.2 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +331 -100
- package/dist/launcher.cjs +71 -6
- package/dist/preinstall.cjs +70 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6628,111 +6628,158 @@ var init_vision = __esm({
|
|
|
6628
6628
|
if (!rawPath) {
|
|
6629
6629
|
return { success: false, output: "", error: "image path is required", durationMs: 0 };
|
|
6630
6630
|
}
|
|
6631
|
+
if ((action === "query" || action === "detect" || action === "point") && !prompt) {
|
|
6632
|
+
return {
|
|
6633
|
+
success: false,
|
|
6634
|
+
output: "",
|
|
6635
|
+
error: `prompt is required for ${action} action`,
|
|
6636
|
+
durationMs: performance.now() - start
|
|
6637
|
+
};
|
|
6638
|
+
}
|
|
6631
6639
|
try {
|
|
6632
6640
|
const { buffer, fullPath } = loadImageBuffer(this.workingDir, rawPath);
|
|
6633
|
-
const
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6641
|
+
const filename = basename4(fullPath);
|
|
6642
|
+
let client = null;
|
|
6643
|
+
try {
|
|
6644
|
+
client = await getMoondreamClient();
|
|
6645
|
+
} catch {
|
|
6646
|
+
}
|
|
6647
|
+
if (client) {
|
|
6648
|
+
return await this.runMoondream(client, buffer, filename, action, prompt, length, start);
|
|
6649
|
+
}
|
|
6650
|
+
const ollamaResult = await this.tryOllamaVision(buffer, filename, action, prompt, length, start);
|
|
6651
|
+
if (ollamaResult)
|
|
6652
|
+
return ollamaResult;
|
|
6653
|
+
return {
|
|
6654
|
+
success: false,
|
|
6655
|
+
output: "",
|
|
6656
|
+
error: "No vision backend available.\nTo enable vision, either:\n 1. ollama pull moondream \u2014 uses Ollama (easiest)\n 2. pip install moondream-station \u2014 dedicated server\n 3. Set MOONDREAM_API_KEY for cloud inference",
|
|
6657
|
+
durationMs: performance.now() - start
|
|
6658
|
+
};
|
|
6659
|
+
} catch (error) {
|
|
6660
|
+
return {
|
|
6661
|
+
success: false,
|
|
6662
|
+
output: "",
|
|
6663
|
+
error: error instanceof Error ? error.message : String(error),
|
|
6664
|
+
durationMs: performance.now() - start
|
|
6665
|
+
};
|
|
6666
|
+
}
|
|
6667
|
+
}
|
|
6668
|
+
async runMoondream(client, buffer, filename, action, prompt, length, start) {
|
|
6669
|
+
switch (action) {
|
|
6670
|
+
case "caption": {
|
|
6671
|
+
const result = await client.caption({ image: buffer, length });
|
|
6672
|
+
const caption = typeof result.caption === "string" ? result.caption : "(streaming not supported)";
|
|
6673
|
+
return { success: true, output: `Caption (${length}) for ${filename}:
|
|
6674
|
+
${caption}`, durationMs: performance.now() - start };
|
|
6675
|
+
}
|
|
6676
|
+
case "query": {
|
|
6677
|
+
const result = await client.query({ image: buffer, question: prompt });
|
|
6678
|
+
const answer = typeof result.answer === "string" ? result.answer : "(streaming not supported)";
|
|
6679
|
+
return { success: true, output: `Q: ${prompt}
|
|
6659
6680
|
A: ${answer}
|
|
6660
6681
|
|
|
6661
|
-
Image: ${
|
|
6662
|
-
|
|
6663
|
-
|
|
6682
|
+
Image: ${filename}`, durationMs: performance.now() - start };
|
|
6683
|
+
}
|
|
6684
|
+
case "detect": {
|
|
6685
|
+
const result = await client.detect({ image: buffer, object: prompt });
|
|
6686
|
+
const objects = result.objects ?? [];
|
|
6687
|
+
if (objects.length === 0) {
|
|
6688
|
+
return { success: true, output: `No "${prompt}" detected in ${filename}`, durationMs: performance.now() - start };
|
|
6664
6689
|
}
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
output: "",
|
|
6670
|
-
error: "prompt is required for detect action (the object to find)",
|
|
6671
|
-
durationMs: performance.now() - start
|
|
6672
|
-
};
|
|
6673
|
-
}
|
|
6674
|
-
const result = await client.detect({ image: buffer, object: prompt });
|
|
6675
|
-
const objects = result.objects ?? [];
|
|
6676
|
-
if (objects.length === 0) {
|
|
6677
|
-
return {
|
|
6678
|
-
success: true,
|
|
6679
|
-
output: `No "${prompt}" detected in ${basename4(fullPath)}`,
|
|
6680
|
-
durationMs: performance.now() - start
|
|
6681
|
-
};
|
|
6682
|
-
}
|
|
6683
|
-
const formatted = objects.map((obj, i) => ` ${i + 1}. bbox: [${obj.x_min.toFixed(3)}, ${obj.y_min.toFixed(3)}, ${obj.x_max.toFixed(3)}, ${obj.y_max.toFixed(3)}] (normalized 0-1)`).join("\n");
|
|
6684
|
-
return {
|
|
6685
|
-
success: true,
|
|
6686
|
-
output: `Detected ${objects.length} "${prompt}" in ${basename4(fullPath)}:
|
|
6690
|
+
const formatted = objects.map((obj, i) => ` ${i + 1}. bbox: [${obj.x_min.toFixed(3)}, ${obj.y_min.toFixed(3)}, ${obj.x_max.toFixed(3)}, ${obj.y_max.toFixed(3)}] (normalized 0-1)`).join("\n");
|
|
6691
|
+
return {
|
|
6692
|
+
success: true,
|
|
6693
|
+
output: `Detected ${objects.length} "${prompt}" in ${filename}:
|
|
6687
6694
|
${formatted}
|
|
6688
6695
|
|
|
6689
6696
|
Coordinates are normalized (0-1). Multiply by image width/height for pixel values.`,
|
|
6690
|
-
|
|
6691
|
-
|
|
6697
|
+
durationMs: performance.now() - start
|
|
6698
|
+
};
|
|
6699
|
+
}
|
|
6700
|
+
case "point": {
|
|
6701
|
+
const result = await client.point({ image: buffer, object: prompt });
|
|
6702
|
+
const points = result.points ?? [];
|
|
6703
|
+
if (points.length === 0) {
|
|
6704
|
+
return { success: true, output: `No "${prompt}" found in ${filename}`, durationMs: performance.now() - start };
|
|
6692
6705
|
}
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6706
|
+
const formatted = points.map((pt, i) => ` ${i + 1}. (${pt.x.toFixed(4)}, ${pt.y.toFixed(4)}) \u2014 normalized 0-1`).join("\n");
|
|
6707
|
+
return {
|
|
6708
|
+
success: true,
|
|
6709
|
+
output: `Found ${points.length} "${prompt}" location(s) in ${filename}:
|
|
6710
|
+
${formatted}
|
|
6711
|
+
|
|
6712
|
+
Coordinates are normalized (0-1). Multiply by image width/height for pixel values.`,
|
|
6713
|
+
durationMs: performance.now() - start
|
|
6714
|
+
};
|
|
6715
|
+
}
|
|
6716
|
+
default:
|
|
6717
|
+
return { success: false, output: "", error: `Unknown action: ${action}. Use: caption, query, detect, point`, durationMs: performance.now() - start };
|
|
6718
|
+
}
|
|
6719
|
+
}
|
|
6720
|
+
async tryOllamaVision(buffer, filename, action, prompt, length, start) {
|
|
6721
|
+
const ollamaHost = process.env["OLLAMA_HOST"] || "http://localhost:11434";
|
|
6722
|
+
const model = process.env["OLLAMA_VISION_MODEL"] || "moondream";
|
|
6723
|
+
const imageBase64 = buffer.toString("base64");
|
|
6724
|
+
let ollamaPrompt;
|
|
6725
|
+
switch (action) {
|
|
6726
|
+
case "caption":
|
|
6727
|
+
ollamaPrompt = length === "short" ? "Briefly describe this image." : length === "long" ? "Describe this image in detail." : "Describe this image.";
|
|
6728
|
+
break;
|
|
6729
|
+
case "query":
|
|
6730
|
+
ollamaPrompt = prompt;
|
|
6731
|
+
break;
|
|
6732
|
+
case "detect":
|
|
6733
|
+
ollamaPrompt = `Detect all instances of "${prompt}" in this image. For each, describe its location.`;
|
|
6734
|
+
break;
|
|
6735
|
+
case "point":
|
|
6736
|
+
ollamaPrompt = `Point to ${prompt}`;
|
|
6737
|
+
break;
|
|
6738
|
+
default:
|
|
6739
|
+
return null;
|
|
6740
|
+
}
|
|
6741
|
+
try {
|
|
6742
|
+
const res = await fetch(`${ollamaHost}/api/generate`, {
|
|
6743
|
+
method: "POST",
|
|
6744
|
+
headers: { "Content-Type": "application/json" },
|
|
6745
|
+
body: JSON.stringify({ model, prompt: ollamaPrompt, images: [imageBase64], stream: false }),
|
|
6746
|
+
signal: AbortSignal.timeout(6e4)
|
|
6747
|
+
});
|
|
6748
|
+
if (!res.ok)
|
|
6749
|
+
return null;
|
|
6750
|
+
const data = await res.json();
|
|
6751
|
+
const response = data.response || "";
|
|
6752
|
+
if (!response)
|
|
6753
|
+
return null;
|
|
6754
|
+
if (action === "point") {
|
|
6755
|
+
const matches = [...response.matchAll(/<point\s+x="([\d.]+)"\s+y="([\d.]+)"\s*\/?>/g)];
|
|
6756
|
+
if (matches.length > 0) {
|
|
6757
|
+
const formatted = matches.map((m, i) => ` ${i + 1}. (${parseFloat(m[1]).toFixed(4)}, ${parseFloat(m[2]).toFixed(4)}) \u2014 normalized 0-1`).join("\n");
|
|
6712
6758
|
return {
|
|
6713
6759
|
success: true,
|
|
6714
|
-
output: `Found ${
|
|
6760
|
+
output: `Found ${matches.length} "${prompt}" location(s) in ${filename} (via Ollama):
|
|
6715
6761
|
${formatted}
|
|
6716
6762
|
|
|
6717
6763
|
Coordinates are normalized (0-1). Multiply by image width/height for pixel values.`,
|
|
6718
6764
|
durationMs: performance.now() - start
|
|
6719
6765
|
};
|
|
6720
6766
|
}
|
|
6721
|
-
|
|
6722
|
-
return {
|
|
6723
|
-
success: false,
|
|
6724
|
-
output: "",
|
|
6725
|
-
error: `Unknown action: ${action}. Use: caption, query, detect, point`,
|
|
6726
|
-
durationMs: performance.now() - start
|
|
6727
|
-
};
|
|
6767
|
+
return { success: true, output: `Could not extract coordinates for "${prompt}" from ${filename}. Model response: ${response}`, durationMs: performance.now() - start };
|
|
6728
6768
|
}
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6769
|
+
if (action === "caption") {
|
|
6770
|
+
return { success: true, output: `Caption (${length}) for ${filename} (via Ollama):
|
|
6771
|
+
${response}`, durationMs: performance.now() - start };
|
|
6772
|
+
}
|
|
6773
|
+
if (action === "query") {
|
|
6774
|
+
return { success: true, output: `Q: ${prompt}
|
|
6775
|
+
A: ${response}
|
|
6776
|
+
|
|
6777
|
+
Image: ${filename} (via Ollama)`, durationMs: performance.now() - start };
|
|
6778
|
+
}
|
|
6779
|
+
return { success: true, output: `Detection results for "${prompt}" in ${filename} (via Ollama):
|
|
6780
|
+
${response}`, durationMs: performance.now() - start };
|
|
6781
|
+
} catch {
|
|
6782
|
+
return null;
|
|
6736
6783
|
}
|
|
6737
6784
|
}
|
|
6738
6785
|
};
|
|
@@ -6981,18 +7028,44 @@ var init_desktop_click = __esm({
|
|
|
6981
7028
|
visionWorked = true;
|
|
6982
7029
|
} catch {
|
|
6983
7030
|
}
|
|
7031
|
+
if (!visionWorked) {
|
|
7032
|
+
try {
|
|
7033
|
+
const ollamaHost = process.env["OLLAMA_HOST"] || "http://localhost:11434";
|
|
7034
|
+
const ollamaModel = process.env["OLLAMA_VISION_MODEL"] || "moondream";
|
|
7035
|
+
const imageBase64 = readFileSync11(screenshotPath).toString("base64");
|
|
7036
|
+
const res = await fetch(`${ollamaHost}/api/generate`, {
|
|
7037
|
+
method: "POST",
|
|
7038
|
+
headers: { "Content-Type": "application/json" },
|
|
7039
|
+
body: JSON.stringify({ model: ollamaModel, prompt: `Point to ${target}`, images: [imageBase64], stream: false }),
|
|
7040
|
+
signal: AbortSignal.timeout(6e4)
|
|
7041
|
+
});
|
|
7042
|
+
if (res.ok) {
|
|
7043
|
+
const data = await res.json();
|
|
7044
|
+
const response = data.response || "";
|
|
7045
|
+
const pointMatches = [...response.matchAll(/<point\s+x="([\d.]+)"\s+y="([\d.]+)"\s*\/?>/g)];
|
|
7046
|
+
if (pointMatches.length > 0) {
|
|
7047
|
+
points = pointMatches.map((m) => ({ x: parseFloat(m[1]), y: parseFloat(m[2]) }));
|
|
7048
|
+
visionWorked = true;
|
|
7049
|
+
}
|
|
7050
|
+
}
|
|
7051
|
+
} catch {
|
|
7052
|
+
}
|
|
7053
|
+
}
|
|
6984
7054
|
if (!visionWorked) {
|
|
6985
7055
|
const hints = [
|
|
6986
|
-
`(
|
|
7056
|
+
`(No vision backend available \u2014 cannot locate "${target}" on screen)`,
|
|
6987
7057
|
`Screenshot saved: ${screenshotPath}`,
|
|
6988
7058
|
`Screen: ${dims.width}x${dims.height}`,
|
|
6989
7059
|
"",
|
|
7060
|
+
"To enable vision-guided clicking, either:",
|
|
7061
|
+
" ollama pull moondream \u2014 then Ollama handles point detection",
|
|
7062
|
+
" pip install moondream-station \u2014 dedicated Moondream server",
|
|
7063
|
+
"",
|
|
6990
7064
|
"Use shell commands to interact with the desktop instead:",
|
|
6991
7065
|
" xdotool search --name 'pattern' \u2014 find windows by title",
|
|
6992
7066
|
" xdotool key 'ctrl+s' \u2014 send keyboard shortcuts",
|
|
6993
7067
|
" xdotool mousemove X Y click 1 \u2014 click at known coordinates",
|
|
6994
|
-
" wmctrl -a 'window title' \u2014 activate a window by name"
|
|
6995
|
-
" xdg-open <url> \u2014 open a URL in the default browser"
|
|
7068
|
+
" wmctrl -a 'window title' \u2014 activate a window by name"
|
|
6996
7069
|
];
|
|
6997
7070
|
return {
|
|
6998
7071
|
success: true,
|
|
@@ -7120,26 +7193,61 @@ ${caption}`);
|
|
|
7120
7193
|
} catch {
|
|
7121
7194
|
}
|
|
7122
7195
|
if (!visionWorked) {
|
|
7123
|
-
let ocrText = "";
|
|
7124
7196
|
try {
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7197
|
+
const ollamaHost = process.env["OLLAMA_HOST"] || "http://localhost:11434";
|
|
7198
|
+
const ollamaModel = process.env["OLLAMA_VISION_MODEL"] || "moondream";
|
|
7199
|
+
const imageBase64 = imageBuffer.toString("base64");
|
|
7200
|
+
const ollamaPrompt = question || "Describe what you see on this desktop screenshot in detail. Include visible applications, windows, text, and UI elements.";
|
|
7201
|
+
const res = await fetch(`${ollamaHost}/api/generate`, {
|
|
7202
|
+
method: "POST",
|
|
7203
|
+
headers: { "Content-Type": "application/json" },
|
|
7204
|
+
body: JSON.stringify({ model: ollamaModel, prompt: ollamaPrompt, images: [imageBase64], stream: false }),
|
|
7205
|
+
signal: AbortSignal.timeout(6e4)
|
|
7206
|
+
});
|
|
7207
|
+
if (res.ok) {
|
|
7208
|
+
const data = await res.json();
|
|
7209
|
+
if (data.response) {
|
|
7210
|
+
if (question) {
|
|
7211
|
+
parts.push(`Q: ${question}`);
|
|
7212
|
+
parts.push(`A: ${data.response}`);
|
|
7213
|
+
} else {
|
|
7214
|
+
parts.push(`Desktop description (via Ollama):
|
|
7215
|
+
${data.response}`);
|
|
7216
|
+
}
|
|
7217
|
+
visionWorked = true;
|
|
7218
|
+
}
|
|
7219
|
+
}
|
|
7129
7220
|
} catch {
|
|
7130
7221
|
}
|
|
7222
|
+
}
|
|
7223
|
+
if (!visionWorked) {
|
|
7224
|
+
let ocrText = "";
|
|
7225
|
+
const tess = ensureCommand("tesseract");
|
|
7226
|
+
if (tess.available) {
|
|
7227
|
+
try {
|
|
7228
|
+
ocrText = execSync11(`tesseract ${JSON.stringify(screenshotPath)} stdout 2>/dev/null`, {
|
|
7229
|
+
encoding: "utf8",
|
|
7230
|
+
timeout: 15e3
|
|
7231
|
+
}).trim();
|
|
7232
|
+
} catch {
|
|
7233
|
+
}
|
|
7234
|
+
}
|
|
7131
7235
|
if (ocrText) {
|
|
7132
|
-
parts.push("(
|
|
7236
|
+
parts.push("(Vision models not available \u2014 using OCR text extraction)");
|
|
7133
7237
|
parts.push(`
|
|
7134
7238
|
Visible text on screen:
|
|
7135
7239
|
${ocrText}`);
|
|
7136
7240
|
} else {
|
|
7137
|
-
parts.push("(
|
|
7241
|
+
parts.push("(No vision backend available)");
|
|
7138
7242
|
parts.push("Screenshot captured but cannot describe contents.");
|
|
7243
|
+
parts.push("To enable desktop vision, either:");
|
|
7244
|
+
parts.push(" ollama pull moondream \u2014 then Ollama handles vision");
|
|
7245
|
+
parts.push(" pip install moondream-station \u2014 dedicated Moondream server");
|
|
7246
|
+
parts.push(" sudo apt install tesseract-ocr \u2014 basic OCR text extraction");
|
|
7247
|
+
parts.push("");
|
|
7139
7248
|
parts.push("Use shell commands to check window state instead:");
|
|
7140
7249
|
parts.push(" xdotool getactivewindow getwindowname \u2014 get active window title");
|
|
7141
7250
|
parts.push(" wmctrl -l \u2014 list all open windows");
|
|
7142
|
-
parts.push(" xdg-open <url> \u2014 open a URL in the default browser");
|
|
7143
7251
|
}
|
|
7144
7252
|
}
|
|
7145
7253
|
if (dims) {
|
|
@@ -12576,6 +12684,55 @@ function findModel(models, query) {
|
|
|
12576
12684
|
const fuzzy = models.find((m) => m.name.includes(query));
|
|
12577
12685
|
return fuzzy;
|
|
12578
12686
|
}
|
|
12687
|
+
async function queryModelContextSize(baseUrl, modelName) {
|
|
12688
|
+
try {
|
|
12689
|
+
const normalized = normalizeBaseUrl(baseUrl);
|
|
12690
|
+
const res = await fetch(`${normalized}/api/show`, {
|
|
12691
|
+
method: "POST",
|
|
12692
|
+
headers: { "Content-Type": "application/json" },
|
|
12693
|
+
body: JSON.stringify({ name: modelName }),
|
|
12694
|
+
signal: AbortSignal.timeout(1e4)
|
|
12695
|
+
});
|
|
12696
|
+
if (!res.ok)
|
|
12697
|
+
return null;
|
|
12698
|
+
const data = await res.json();
|
|
12699
|
+
if (data.parameters) {
|
|
12700
|
+
const match = data.parameters.match(/num_ctx\s+(\d+)/);
|
|
12701
|
+
if (match)
|
|
12702
|
+
return parseInt(match[1], 10);
|
|
12703
|
+
}
|
|
12704
|
+
if (data.model_info) {
|
|
12705
|
+
for (const [key, value] of Object.entries(data.model_info)) {
|
|
12706
|
+
if (key.endsWith(".context_length") && typeof value === "number") {
|
|
12707
|
+
return value;
|
|
12708
|
+
}
|
|
12709
|
+
}
|
|
12710
|
+
}
|
|
12711
|
+
return null;
|
|
12712
|
+
} catch {
|
|
12713
|
+
return null;
|
|
12714
|
+
}
|
|
12715
|
+
}
|
|
12716
|
+
async function queryOpenAIContextSize(baseUrl, modelName, apiKey) {
|
|
12717
|
+
try {
|
|
12718
|
+
const models = await fetchOpenAIModels(baseUrl, apiKey);
|
|
12719
|
+
const model = models.find((m) => m.name === modelName);
|
|
12720
|
+
if (model?.size) {
|
|
12721
|
+
const match = model.size.match(/(\d+)K ctx/);
|
|
12722
|
+
if (match)
|
|
12723
|
+
return parseInt(match[1], 10) * 1024;
|
|
12724
|
+
}
|
|
12725
|
+
return null;
|
|
12726
|
+
} catch {
|
|
12727
|
+
return null;
|
|
12728
|
+
}
|
|
12729
|
+
}
|
|
12730
|
+
async function queryContextSize(baseUrl, modelName, apiKey) {
|
|
12731
|
+
const ollamaSize = await queryModelContextSize(baseUrl, modelName);
|
|
12732
|
+
if (ollamaSize)
|
|
12733
|
+
return ollamaSize;
|
|
12734
|
+
return queryOpenAIContextSize(baseUrl, modelName, apiKey);
|
|
12735
|
+
}
|
|
12579
12736
|
function formatBytes(bytes) {
|
|
12580
12737
|
if (bytes < 1024)
|
|
12581
12738
|
return `${bytes} B`;
|
|
@@ -13065,6 +13222,8 @@ function renderSlashHelp() {
|
|
|
13065
13222
|
["/evaluate", "Evaluate last completed task (LLM quality scoring)"],
|
|
13066
13223
|
["/task-type", "Set task type (code, document, analysis, plan, general, auto)"],
|
|
13067
13224
|
["/stats", "Show session dashboard (metrics, tool usage, task history)"],
|
|
13225
|
+
["/stop", "Stop current task and save progress (alias: /pause)"],
|
|
13226
|
+
["/resume", "Resume a previously stopped task"],
|
|
13068
13227
|
["/bruteforce", "Toggle brute-force mode (auto re-engage on turn limit)"],
|
|
13069
13228
|
["/tools", "List agent-created custom tools"],
|
|
13070
13229
|
["/skills", "List available AIWG skills"],
|
|
@@ -14989,6 +15148,34 @@ async function handleSlashCommand(input, ctx) {
|
|
|
14989
15148
|
renderInfo(`Colors ${next ? "enabled" : "disabled"}.`);
|
|
14990
15149
|
return "handled";
|
|
14991
15150
|
}
|
|
15151
|
+
case "stop":
|
|
15152
|
+
case "pause": {
|
|
15153
|
+
if (!ctx.hasActiveTask?.()) {
|
|
15154
|
+
renderWarning("No active task to stop.");
|
|
15155
|
+
return "handled";
|
|
15156
|
+
}
|
|
15157
|
+
const saved = ctx.savePendingTaskState?.() ?? false;
|
|
15158
|
+
const aborted = ctx.abortTask?.() ?? false;
|
|
15159
|
+
if (saved && aborted) {
|
|
15160
|
+
renderInfo("Task stopped and saved. Use /resume to continue later.");
|
|
15161
|
+
} else if (aborted) {
|
|
15162
|
+
renderWarning("Task stopped but state could not be saved.");
|
|
15163
|
+
} else {
|
|
15164
|
+
renderWarning("Could not stop the task.");
|
|
15165
|
+
}
|
|
15166
|
+
return "handled";
|
|
15167
|
+
}
|
|
15168
|
+
case "resume": {
|
|
15169
|
+
if (ctx.hasActiveTask?.()) {
|
|
15170
|
+
renderWarning("A task is already running. Stop it first with /stop.");
|
|
15171
|
+
return "handled";
|
|
15172
|
+
}
|
|
15173
|
+
const resumed = ctx.resumeTask?.() ?? false;
|
|
15174
|
+
if (!resumed) {
|
|
15175
|
+
renderWarning("No saved task to resume.");
|
|
15176
|
+
}
|
|
15177
|
+
return "handled";
|
|
15178
|
+
}
|
|
14992
15179
|
default: {
|
|
14993
15180
|
const skills = discoverSkills(ctx.repoRoot);
|
|
14994
15181
|
const skill = skills.find((s) => s.name === cmd || s.name === cmd.replace(/_/g, "-"));
|
|
@@ -15241,6 +15428,12 @@ async function switchModel(query, ctx, local = false) {
|
|
|
15241
15428
|
if (local) {
|
|
15242
15429
|
renderInfo("Saved as project-local override.");
|
|
15243
15430
|
}
|
|
15431
|
+
if (ctx.setContextWindowSize) {
|
|
15432
|
+
const ctxSize = await queryContextSize(ctx.config.backendUrl, finalModel, ctx.config.apiKey);
|
|
15433
|
+
if (ctxSize) {
|
|
15434
|
+
ctx.setContextWindowSize(ctxSize);
|
|
15435
|
+
}
|
|
15436
|
+
}
|
|
15244
15437
|
} catch (err) {
|
|
15245
15438
|
renderError(`Failed to switch model: ${err instanceof Error ? err.message : String(err)}`);
|
|
15246
15439
|
}
|
|
@@ -19229,6 +19422,11 @@ async function startInteractive(config, repoPath) {
|
|
|
19229
19422
|
end: () => statusBar.endContentWrite()
|
|
19230
19423
|
});
|
|
19231
19424
|
}
|
|
19425
|
+
queryContextSize(config.backendUrl, config.model, config.apiKey).then((ctxSize) => {
|
|
19426
|
+
if (ctxSize)
|
|
19427
|
+
statusBar.setContextWindowSize(ctxSize);
|
|
19428
|
+
}).catch(() => {
|
|
19429
|
+
});
|
|
19232
19430
|
const provider = detectProvider(config.backendUrl);
|
|
19233
19431
|
const costTracker = new CostTracker(provider.id);
|
|
19234
19432
|
const sessionMetrics = new SessionMetrics();
|
|
@@ -19422,7 +19620,7 @@ async function startInteractive(config, repoPath) {
|
|
|
19422
19620
|
if (lastSubmittedPrompt && activeTask) {
|
|
19423
19621
|
savePendingTask(repoRoot, {
|
|
19424
19622
|
prompt: lastSubmittedPrompt,
|
|
19425
|
-
progressSummary: `
|
|
19623
|
+
progressSummary: `Task paused by user. ${sessionToolCallCount} tool calls completed.`,
|
|
19426
19624
|
filesModified: sessionFilesTouched,
|
|
19427
19625
|
bruteForce: bruteForceEnabled,
|
|
19428
19626
|
savedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -19444,6 +19642,12 @@ async function startInteractive(config, repoPath) {
|
|
|
19444
19642
|
writeContent(() => renderToolCallStart(event.toolName ?? "unknown", event.toolArgs ?? {}));
|
|
19445
19643
|
} else if (event.type === "tool_result") {
|
|
19446
19644
|
writeContent(() => renderToolResult(event.toolName ?? "unknown", event.success ?? false, event.content ?? ""));
|
|
19645
|
+
} else if (event.type === "token_usage" && event.tokenUsage) {
|
|
19646
|
+
statusBar.updateMetrics({
|
|
19647
|
+
...event.tokenUsage,
|
|
19648
|
+
estimatedCost: costTracker?.currentCost,
|
|
19649
|
+
hasPricing: costTracker?.hasPricing
|
|
19650
|
+
});
|
|
19447
19651
|
}
|
|
19448
19652
|
}).then((state) => {
|
|
19449
19653
|
writeContent(() => renderDreamEnd(state));
|
|
@@ -19520,7 +19724,33 @@ async function startInteractive(config, repoPath) {
|
|
|
19520
19724
|
getEmojis: () => getEmojisEnabled(),
|
|
19521
19725
|
setEmojis: (enabled) => setEmojisEnabled(enabled),
|
|
19522
19726
|
getColors: () => getColorsEnabled(),
|
|
19523
|
-
setColors: (enabled) => setColorsEnabled(enabled)
|
|
19727
|
+
setColors: (enabled) => setColorsEnabled(enabled),
|
|
19728
|
+
setContextWindowSize: (size) => statusBar.setContextWindowSize(size),
|
|
19729
|
+
hasActiveTask: () => activeTask !== null,
|
|
19730
|
+
abortTask() {
|
|
19731
|
+
if (!activeTask)
|
|
19732
|
+
return false;
|
|
19733
|
+
activeTask.runner.abort();
|
|
19734
|
+
writeContent(() => renderInfo("Task aborted."));
|
|
19735
|
+
return true;
|
|
19736
|
+
},
|
|
19737
|
+
resumeTask() {
|
|
19738
|
+
const pendingTask = loadPendingTask(repoRoot);
|
|
19739
|
+
if (!pendingTask)
|
|
19740
|
+
return false;
|
|
19741
|
+
setTimeout(() => {
|
|
19742
|
+
const resumeContext = [
|
|
19743
|
+
`[RESUMED] Original task: ${pendingTask.prompt}`,
|
|
19744
|
+
pendingTask.progressSummary ? `Progress so far: ${pendingTask.progressSummary}` : "",
|
|
19745
|
+
pendingTask.filesModified.length > 0 ? `Files modified before stop: ${pendingTask.filesModified.join(", ")}` : "",
|
|
19746
|
+
`Tool calls completed before stop: ${pendingTask.toolCallCount}`,
|
|
19747
|
+
"Continue where you left off. Do not repeat work already done."
|
|
19748
|
+
].filter(Boolean).join("\n\n");
|
|
19749
|
+
writeContent(() => renderInfo(`Resuming task: ${pendingTask.prompt.slice(0, 100)}${pendingTask.prompt.length > 100 ? "..." : ""}`));
|
|
19750
|
+
rl.emit("line", resumeContext);
|
|
19751
|
+
}, 100);
|
|
19752
|
+
return true;
|
|
19753
|
+
}
|
|
19524
19754
|
};
|
|
19525
19755
|
showPrompt();
|
|
19526
19756
|
if (hasTaskToResume) {
|
|
@@ -19895,6 +20125,7 @@ var init_interactive = __esm({
|
|
|
19895
20125
|
init_updater();
|
|
19896
20126
|
init_commands();
|
|
19897
20127
|
init_setup();
|
|
20128
|
+
init_model_picker();
|
|
19898
20129
|
init_project_context();
|
|
19899
20130
|
init_dist7();
|
|
19900
20131
|
init_oa_directory();
|
package/dist/launcher.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Node version gate — uses only ES5 syntax so it parses on any Node version.
|
|
3
|
-
// On old Node: walks through nvm install, then Node 20, then reinstall.
|
|
3
|
+
// On old Node: walks through curl/wget install, nvm install, then Node 20, then reinstall.
|
|
4
4
|
var nodeVersion = parseInt(process.versions.node, 10);
|
|
5
5
|
|
|
6
6
|
if (nodeVersion < 18) {
|
|
@@ -36,6 +36,15 @@ if (nodeVersion < 18) {
|
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
function hasCmd(cmd) {
|
|
40
|
+
try {
|
|
41
|
+
childProcess.execSync("which " + cmd, { stdio: "pipe", timeout: 3000 });
|
|
42
|
+
return true;
|
|
43
|
+
} catch (e) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
39
48
|
function run(cmd) {
|
|
40
49
|
console.log(" $ " + cmd);
|
|
41
50
|
try {
|
|
@@ -53,7 +62,58 @@ if (nodeVersion < 18) {
|
|
|
53
62
|
process.exit(1);
|
|
54
63
|
}
|
|
55
64
|
|
|
56
|
-
|
|
65
|
+
// Returns "curl -o-" or "wget -qO-" or null
|
|
66
|
+
function getDownloader() {
|
|
67
|
+
if (hasCmd("curl")) return "curl -o-";
|
|
68
|
+
if (hasCmd("wget")) return "wget -qO-";
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Try to install curl if neither curl nor wget is available
|
|
73
|
+
function ensureDownloader(next) {
|
|
74
|
+
var dl = getDownloader();
|
|
75
|
+
if (dl) { next(dl); return; }
|
|
76
|
+
|
|
77
|
+
console.log(" Neither curl nor wget found.");
|
|
78
|
+
console.log("");
|
|
79
|
+
|
|
80
|
+
ask(" Install curl now? [Y/n] ", function(a) {
|
|
81
|
+
if (a === "n" || a === "no") {
|
|
82
|
+
bail(
|
|
83
|
+
"\n Install manually:\n" +
|
|
84
|
+
" sudo apt install curl (Debian/Ubuntu)\n" +
|
|
85
|
+
" sudo dnf install curl (Fedora)\n" +
|
|
86
|
+
" sudo pacman -S curl (Arch)\n"
|
|
87
|
+
);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
console.log("");
|
|
91
|
+
var ok = false;
|
|
92
|
+
if (hasCmd("apt-get")) {
|
|
93
|
+
ok = run("sudo -n apt-get install -y curl 2>/dev/null || sudo apt-get install -y curl");
|
|
94
|
+
} else if (hasCmd("dnf")) {
|
|
95
|
+
ok = run("sudo -n dnf install -y curl 2>/dev/null || sudo dnf install -y curl");
|
|
96
|
+
} else if (hasCmd("pacman")) {
|
|
97
|
+
ok = run("sudo -n pacman -S --noconfirm curl 2>/dev/null || sudo pacman -S --noconfirm curl");
|
|
98
|
+
} else if (hasCmd("apk")) {
|
|
99
|
+
ok = run("apk add curl");
|
|
100
|
+
} else {
|
|
101
|
+
console.log(" No supported package manager found (tried apt, dnf, pacman, apk).");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (ok && hasCmd("curl")) {
|
|
105
|
+
console.log("");
|
|
106
|
+
next("curl -o-");
|
|
107
|
+
} else {
|
|
108
|
+
bail(
|
|
109
|
+
"\n Could not install curl. Install it manually, then re-run:\n" +
|
|
110
|
+
" npm i -g open-agents-ai\n"
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function doInstallNvm(dl, next) {
|
|
57
117
|
if (hasNvm) {
|
|
58
118
|
console.log(" nvm found at " + nvmDir);
|
|
59
119
|
console.log("");
|
|
@@ -65,6 +125,7 @@ if (nodeVersion < 18) {
|
|
|
65
125
|
bail(
|
|
66
126
|
"\n Install nvm manually:\n" +
|
|
67
127
|
" curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
|
|
128
|
+
" # or: wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
|
|
68
129
|
" source ~/.bashrc\n" +
|
|
69
130
|
" nvm install 20\n" +
|
|
70
131
|
" npm i -g open-agents-ai\n"
|
|
@@ -72,7 +133,7 @@ if (nodeVersion < 18) {
|
|
|
72
133
|
return;
|
|
73
134
|
}
|
|
74
135
|
console.log("");
|
|
75
|
-
var ok = run("
|
|
136
|
+
var ok = run(dl + " https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash");
|
|
76
137
|
if (!ok) {
|
|
77
138
|
bail("\n nvm install failed. See https://github.com/nvm-sh/nvm#installing-and-updating\n");
|
|
78
139
|
return;
|
|
@@ -151,6 +212,7 @@ if (nodeVersion < 18) {
|
|
|
151
212
|
bail(
|
|
152
213
|
"\n To install manually:\n" +
|
|
153
214
|
" curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
|
|
215
|
+
" # or: wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
|
|
154
216
|
" source ~/.bashrc\n" +
|
|
155
217
|
" nvm install 20\n" +
|
|
156
218
|
" nvm alias default 20\n" +
|
|
@@ -159,9 +221,12 @@ if (nodeVersion < 18) {
|
|
|
159
221
|
return;
|
|
160
222
|
}
|
|
161
223
|
console.log("");
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
224
|
+
// Ensure curl or wget is available first
|
|
225
|
+
ensureDownloader(function(dl) {
|
|
226
|
+
doInstallNvm(dl, function() {
|
|
227
|
+
doInstallNode(function() {
|
|
228
|
+
doReinstall();
|
|
229
|
+
});
|
|
165
230
|
});
|
|
166
231
|
});
|
|
167
232
|
});
|
package/dist/preinstall.cjs
CHANGED
|
@@ -42,6 +42,15 @@ function ask(question, cb) {
|
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
function hasCmd(cmd) {
|
|
46
|
+
try {
|
|
47
|
+
childProcess.execSync("which " + cmd, { stdio: "pipe", timeout: 3000 });
|
|
48
|
+
return true;
|
|
49
|
+
} catch (e) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
45
54
|
function run(cmd, opts) {
|
|
46
55
|
console.log(" $ " + cmd);
|
|
47
56
|
try {
|
|
@@ -58,7 +67,58 @@ function bail(msg) {
|
|
|
58
67
|
process.exit(1);
|
|
59
68
|
}
|
|
60
69
|
|
|
61
|
-
|
|
70
|
+
// Returns "curl -o-" or "wget -qO-" or null
|
|
71
|
+
function getDownloader() {
|
|
72
|
+
if (hasCmd("curl")) return "curl -o-";
|
|
73
|
+
if (hasCmd("wget")) return "wget -qO-";
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Try to install curl if neither curl nor wget is available
|
|
78
|
+
function ensureDownloader(next) {
|
|
79
|
+
var dl = getDownloader();
|
|
80
|
+
if (dl) { next(dl); return; }
|
|
81
|
+
|
|
82
|
+
console.log(" Neither curl nor wget found.");
|
|
83
|
+
console.log("");
|
|
84
|
+
|
|
85
|
+
ask(" Install curl now? [Y/n] ", function(a) {
|
|
86
|
+
if (a === "n" || a === "no") {
|
|
87
|
+
bail(
|
|
88
|
+
"\n Install manually:\n" +
|
|
89
|
+
" sudo apt install curl (Debian/Ubuntu)\n" +
|
|
90
|
+
" sudo dnf install curl (Fedora)\n" +
|
|
91
|
+
" sudo pacman -S curl (Arch)\n"
|
|
92
|
+
);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
console.log("");
|
|
96
|
+
var ok = false;
|
|
97
|
+
if (hasCmd("apt-get")) {
|
|
98
|
+
ok = run("sudo -n apt-get install -y curl 2>/dev/null || sudo apt-get install -y curl");
|
|
99
|
+
} else if (hasCmd("dnf")) {
|
|
100
|
+
ok = run("sudo -n dnf install -y curl 2>/dev/null || sudo dnf install -y curl");
|
|
101
|
+
} else if (hasCmd("pacman")) {
|
|
102
|
+
ok = run("sudo -n pacman -S --noconfirm curl 2>/dev/null || sudo pacman -S --noconfirm curl");
|
|
103
|
+
} else if (hasCmd("apk")) {
|
|
104
|
+
ok = run("apk add curl");
|
|
105
|
+
} else {
|
|
106
|
+
console.log(" No supported package manager found (tried apt, dnf, pacman, apk).");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (ok && hasCmd("curl")) {
|
|
110
|
+
console.log("");
|
|
111
|
+
next("curl -o-");
|
|
112
|
+
} else {
|
|
113
|
+
bail(
|
|
114
|
+
"\n Could not install curl. Install it manually, then re-run:\n" +
|
|
115
|
+
" npm i -g open-agents-ai\n"
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function doInstallNvm(dl, next) {
|
|
62
122
|
if (hasNvm) {
|
|
63
123
|
console.log(" nvm found at " + nvmDir);
|
|
64
124
|
console.log("");
|
|
@@ -70,6 +130,7 @@ function doInstallNvm(next) {
|
|
|
70
130
|
bail(
|
|
71
131
|
"\n Install nvm manually:\n" +
|
|
72
132
|
" curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
|
|
133
|
+
" # or: wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
|
|
73
134
|
" source ~/.bashrc\n" +
|
|
74
135
|
" nvm install 20\n" +
|
|
75
136
|
" npm i -g open-agents-ai\n"
|
|
@@ -77,7 +138,7 @@ function doInstallNvm(next) {
|
|
|
77
138
|
return;
|
|
78
139
|
}
|
|
79
140
|
console.log("");
|
|
80
|
-
var ok = run("
|
|
141
|
+
var ok = run(dl + " https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash");
|
|
81
142
|
if (!ok) {
|
|
82
143
|
bail("\n nvm install failed. See https://github.com/nvm-sh/nvm#installing-and-updating\n");
|
|
83
144
|
return;
|
|
@@ -157,6 +218,7 @@ ask(" Install Node.js 20 now? [Y/n] ", function(a) {
|
|
|
157
218
|
bail(
|
|
158
219
|
"\n To install manually:\n" +
|
|
159
220
|
" curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
|
|
221
|
+
" # or: wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
|
|
160
222
|
" source ~/.bashrc\n" +
|
|
161
223
|
" nvm install 20\n" +
|
|
162
224
|
" nvm alias default 20\n" +
|
|
@@ -165,9 +227,12 @@ ask(" Install Node.js 20 now? [Y/n] ", function(a) {
|
|
|
165
227
|
return;
|
|
166
228
|
}
|
|
167
229
|
console.log("");
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
230
|
+
// Ensure curl or wget is available first
|
|
231
|
+
ensureDownloader(function(dl) {
|
|
232
|
+
doInstallNvm(dl, function() {
|
|
233
|
+
doInstallNode(function() {
|
|
234
|
+
doReinstall();
|
|
235
|
+
});
|
|
171
236
|
});
|
|
172
237
|
});
|
|
173
238
|
});
|
package/package.json
CHANGED