open-agents-ai 0.18.0 → 0.19.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.
Files changed (3) hide show
  1. package/README.md +22 -4
  2. package/dist/index.js +372 -107
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -32,6 +32,7 @@ An autonomous multi-turn tool-calling agent that reads your code, makes changes,
32
32
  - **35 autonomous tools** — file I/O, shell, grep, web search/fetch, memory, sub-agents, background tasks, image/OCR, git, diagnostics, vision, desktop automation, structured files, code sandbox
33
33
  - **Moondream vision** — see and interact with the desktop via Moondream VLM (caption, query, detect, point-and-click)
34
34
  - **Desktop automation** — vision-guided clicking: describe a UI element in natural language, the agent finds and clicks it
35
+ - **Auto-install desktop deps** — screenshot, mouse, OCR, and image tools auto-install missing system packages (scrot, xdotool, tesseract, imagemagick) on first use
35
36
  - **Parallel tool execution** — read-only tools run concurrently via `Promise.allSettled`
36
37
  - **Sub-agent delegation** — spawn independent agents for parallel workstreams
37
38
  - **Ralph Loop** — iterative task execution that keeps retrying until completion criteria are met
@@ -226,10 +227,27 @@ python3 -m venv .moondream-venv
226
227
 
227
228
  The vision tools auto-detect a running Moondream Station on `localhost:2020`. For cloud inference, set `MOONDREAM_API_KEY` instead.
228
229
 
229
- **System requirements for desktop interaction:**
230
- - Screenshot: `scrot`, `gnome-screenshot`, or Python PIL (auto-detected)
231
- - Mouse control: `xdotool` or `python-xlib` (auto-detected)
232
- - Vision: Moondream Station (local) or Moondream Cloud API
230
+ **System dependencies (auto-installed on first use):**
231
+
232
+ Desktop tools automatically install missing system packages when first needed. No manual setup required — just use the tool and it handles the rest:
233
+
234
+ | Tool | Linux Package | What It Does |
235
+ |------|--------------|-------------|
236
+ | `scrot` | `apt install scrot` | Screenshot capture |
237
+ | `xdotool` | `apt install xdotool` | Mouse/keyboard automation |
238
+ | `tesseract` | `apt install tesseract-ocr` | OCR text extraction |
239
+ | `identify` | `apt install imagemagick` | Image dimensions/conversion |
240
+
241
+ Supports `apt` (Debian/Ubuntu), `dnf` (Fedora), `pacman` (Arch), and `brew` (macOS). You can also pre-install everything at once:
242
+
243
+ ```bash
244
+ ./scripts/setup-desktop.sh # Install all desktop deps
245
+ ./scripts/setup-desktop.sh --check-only # Just check what's missing
246
+ ```
247
+
248
+ **Vision backend:**
249
+ - Moondream Station (local) — runs entirely on your machine, no API keys needed
250
+ - Moondream Cloud API — set `MOONDREAM_API_KEY` for cloud inference
233
251
 
234
252
  ## Interactive TUI
235
253
 
package/dist/index.js CHANGED
@@ -1304,7 +1304,17 @@ ${stdinInput ?? ""}`);
1304
1304
  let stdout = "";
1305
1305
  let stderr = "";
1306
1306
  let killed = false;
1307
+ let resolved = false;
1307
1308
  const maxBuf = 1024 * 1024;
1309
+ const doResolve = (result) => {
1310
+ if (resolved)
1311
+ return;
1312
+ resolved = true;
1313
+ clearTimeout(timer);
1314
+ if (exitFlushTimer)
1315
+ clearTimeout(exitFlushTimer);
1316
+ resolve19(result);
1317
+ };
1308
1318
  const timer = setTimeout(() => {
1309
1319
  killed = true;
1310
1320
  child.kill("SIGTERM");
@@ -1315,6 +1325,7 @@ ${stdinInput ?? ""}`);
1315
1325
  }
1316
1326
  }, 5e3);
1317
1327
  }, timeout);
1328
+ let exitFlushTimer = null;
1318
1329
  child.stdout.on("data", (data) => {
1319
1330
  stdout += data.toString();
1320
1331
  if (stdout.length > maxBuf) {
@@ -1331,14 +1342,39 @@ ${stdinInput ?? ""}`);
1331
1342
  child.stdin.write(stdinInput);
1332
1343
  }
1333
1344
  child.stdin.end();
1345
+ child.on("exit", (code) => {
1346
+ exitFlushTimer = setTimeout(() => {
1347
+ const durationMs = performance.now() - start;
1348
+ if (killed) {
1349
+ const combined = stdout + stderr;
1350
+ const looksInteractive = /\? .+[›>]|y\/n|yes\/no|\(Y\/n\)|\[y\/N\]/i.test(combined);
1351
+ const hint = looksInteractive ? " The command appears to be waiting for interactive input. Use non-interactive flags (e.g., --yes, --no-input) or provide input via the stdin parameter." : "";
1352
+ doResolve({
1353
+ success: false,
1354
+ output: stdout,
1355
+ error: `Command timed out after ${timeout}ms.${hint}`,
1356
+ durationMs
1357
+ });
1358
+ return;
1359
+ }
1360
+ const success = code === 0;
1361
+ doResolve({
1362
+ success,
1363
+ output: stdout + (stderr && success ? `
1364
+ STDERR:
1365
+ ${stderr}` : ""),
1366
+ error: success ? void 0 : stderr || `Exit code ${code}`,
1367
+ durationMs
1368
+ });
1369
+ }, 1e3);
1370
+ });
1334
1371
  child.on("close", (code) => {
1335
- clearTimeout(timer);
1336
1372
  const durationMs = performance.now() - start;
1337
1373
  if (killed) {
1338
1374
  const combined = stdout + stderr;
1339
1375
  const looksInteractive = /\? .+[›>]|y\/n|yes\/no|\(Y\/n\)|\[y\/N\]/i.test(combined);
1340
1376
  const hint = looksInteractive ? " The command appears to be waiting for interactive input. Use non-interactive flags (e.g., --yes, --no-input) or provide input via the stdin parameter." : "";
1341
- resolve19({
1377
+ doResolve({
1342
1378
  success: false,
1343
1379
  output: stdout,
1344
1380
  error: `Command timed out after ${timeout}ms.${hint}`,
@@ -1347,7 +1383,7 @@ ${stdinInput ?? ""}`);
1347
1383
  return;
1348
1384
  }
1349
1385
  const success = code === 0;
1350
- resolve19({
1386
+ doResolve({
1351
1387
  success,
1352
1388
  output: stdout + (stderr && success ? `
1353
1389
  STDERR:
@@ -1357,8 +1393,7 @@ ${stderr}` : ""),
1357
1393
  });
1358
1394
  });
1359
1395
  child.on("error", (err) => {
1360
- clearTimeout(timer);
1361
- resolve19({
1396
+ doResolve({
1362
1397
  success: false,
1363
1398
  output: stdout,
1364
1399
  error: err.message,
@@ -3893,10 +3928,173 @@ Exit code: ${task.exitCode ?? "N/A"}`,
3893
3928
  }
3894
3929
  });
3895
3930
 
3931
+ // packages/execution/dist/system-deps.js
3932
+ import { execSync as execSync6 } from "node:child_process";
3933
+ function detectPackageManager() {
3934
+ if (_detectedPkgManager !== void 0)
3935
+ return _detectedPkgManager;
3936
+ const plat = process.platform;
3937
+ if (plat === "darwin") {
3938
+ _detectedPkgManager = hasCommand("brew") ? "brew" : null;
3939
+ return _detectedPkgManager;
3940
+ }
3941
+ if (plat === "linux") {
3942
+ if (hasCommand("apt-get"))
3943
+ _detectedPkgManager = "apt";
3944
+ else if (hasCommand("dnf"))
3945
+ _detectedPkgManager = "dnf";
3946
+ else if (hasCommand("pacman"))
3947
+ _detectedPkgManager = "pacman";
3948
+ else
3949
+ _detectedPkgManager = null;
3950
+ return _detectedPkgManager;
3951
+ }
3952
+ _detectedPkgManager = null;
3953
+ return null;
3954
+ }
3955
+ function hasCommand(cmd) {
3956
+ try {
3957
+ execSync6(`which ${cmd}`, { stdio: "pipe", timeout: 3e3 });
3958
+ return true;
3959
+ } catch {
3960
+ return false;
3961
+ }
3962
+ }
3963
+ function runInstall(installCmd) {
3964
+ const needsSudo = process.platform === "linux";
3965
+ let cmd = installCmd;
3966
+ if (needsSudo) {
3967
+ if (_sudoPassword) {
3968
+ cmd = `echo ${JSON.stringify(_sudoPassword)} | sudo -S ${installCmd}`;
3969
+ } else {
3970
+ cmd = `sudo -n ${installCmd}`;
3971
+ }
3972
+ }
3973
+ execSync6(cmd, {
3974
+ stdio: "pipe",
3975
+ timeout: 12e4,
3976
+ // 2 min for package install
3977
+ env: { ...process.env, DEBIAN_FRONTEND: "noninteractive" }
3978
+ });
3979
+ }
3980
+ function ensureCommand(command) {
3981
+ const cached = _cache.get(command);
3982
+ if (cached)
3983
+ return cached;
3984
+ if (hasCommand(command)) {
3985
+ const result2 = { available: true, installed: false };
3986
+ _cache.set(command, result2);
3987
+ return result2;
3988
+ }
3989
+ const dep = DESKTOP_DEPS.find((d) => d.command === command);
3990
+ if (!dep) {
3991
+ const result2 = {
3992
+ available: false,
3993
+ installed: false,
3994
+ error: `Unknown dependency: ${command}. Cannot auto-install.`
3995
+ };
3996
+ _cache.set(command, result2);
3997
+ return result2;
3998
+ }
3999
+ const pm = detectPackageManager();
4000
+ if (!pm) {
4001
+ const result2 = {
4002
+ available: false,
4003
+ installed: false,
4004
+ error: `No supported package manager found. Install ${command} manually.`
4005
+ };
4006
+ _cache.set(command, result2);
4007
+ return result2;
4008
+ }
4009
+ const pkg = dep.packages[pm];
4010
+ if (!pkg) {
4011
+ const result2 = {
4012
+ available: false,
4013
+ installed: false,
4014
+ error: `No package for ${command} on ${pm}. Install manually.`
4015
+ };
4016
+ _cache.set(command, result2);
4017
+ return result2;
4018
+ }
4019
+ let installCmd;
4020
+ switch (pm) {
4021
+ case "apt":
4022
+ installCmd = `apt-get install -y ${pkg}`;
4023
+ break;
4024
+ case "dnf":
4025
+ installCmd = `dnf install -y ${pkg}`;
4026
+ break;
4027
+ case "pacman":
4028
+ installCmd = `pacman -S --noconfirm ${pkg}`;
4029
+ break;
4030
+ case "brew":
4031
+ installCmd = `brew install ${pkg}`;
4032
+ break;
4033
+ }
4034
+ try {
4035
+ runInstall(installCmd);
4036
+ } catch (err) {
4037
+ const msg = err instanceof Error ? err.message : String(err);
4038
+ const result2 = {
4039
+ available: false,
4040
+ installed: false,
4041
+ error: `Failed to install ${pkg} via ${pm}: ${msg.slice(0, 200)}`
4042
+ };
4043
+ _cache.set(command, result2);
4044
+ return result2;
4045
+ }
4046
+ if (hasCommand(command)) {
4047
+ const result2 = { available: true, installed: true };
4048
+ _cache.set(command, result2);
4049
+ return result2;
4050
+ }
4051
+ const result = {
4052
+ available: false,
4053
+ installed: false,
4054
+ error: `Installed ${pkg} but ${command} still not found on PATH.`
4055
+ };
4056
+ _cache.set(command, result);
4057
+ return result;
4058
+ }
4059
+ var DESKTOP_DEPS, _detectedPkgManager, _cache, _sudoPassword;
4060
+ var init_system_deps = __esm({
4061
+ "packages/execution/dist/system-deps.js"() {
4062
+ "use strict";
4063
+ DESKTOP_DEPS = [
4064
+ {
4065
+ command: "scrot",
4066
+ packages: { apt: "scrot", dnf: "scrot", pacman: "scrot", brew: "scrot" },
4067
+ description: "Screenshot capture tool",
4068
+ group: "screenshot"
4069
+ },
4070
+ {
4071
+ command: "xdotool",
4072
+ packages: { apt: "xdotool", dnf: "xdotool", pacman: "xdotool" },
4073
+ description: "X11 desktop automation (mouse/keyboard)",
4074
+ group: "click"
4075
+ },
4076
+ {
4077
+ command: "tesseract",
4078
+ packages: { apt: "tesseract-ocr", dnf: "tesseract", pacman: "tesseract", brew: "tesseract" },
4079
+ description: "OCR text extraction engine",
4080
+ group: "ocr"
4081
+ },
4082
+ {
4083
+ command: "identify",
4084
+ packages: { apt: "imagemagick", dnf: "ImageMagick", pacman: "imagemagick", brew: "imagemagick" },
4085
+ description: "Image metadata and conversion (ImageMagick)",
4086
+ group: "image"
4087
+ }
4088
+ ];
4089
+ _cache = /* @__PURE__ */ new Map();
4090
+ _sudoPassword = null;
4091
+ }
4092
+ });
4093
+
3896
4094
  // packages/execution/dist/tools/image.js
3897
4095
  import { existsSync as existsSync7, readFileSync as readFileSync6, statSync as statSync4 } from "node:fs";
3898
4096
  import { resolve as resolve11, extname as extname2, basename } from "node:path";
3899
- import { execSync as execSync6 } from "node:child_process";
4097
+ import { execSync as execSync7 } from "node:child_process";
3900
4098
  import { tmpdir } from "node:os";
3901
4099
  import { join as join10 } from "node:path";
3902
4100
  function isImagePath(path) {
@@ -3923,7 +4121,7 @@ function getMimeType(filePath) {
3923
4121
  }
3924
4122
  function getImageDimensions(filePath) {
3925
4123
  try {
3926
- const out = execSync6(`identify -format "%w %h" ${JSON.stringify(filePath)}`, {
4124
+ const out = execSync7(`identify -format "%w %h" ${JSON.stringify(filePath)}`, {
3927
4125
  encoding: "utf8",
3928
4126
  stdio: ["pipe", "pipe", "pipe"],
3929
4127
  timeout: 5e3
@@ -3932,9 +4130,23 @@ function getImageDimensions(filePath) {
3932
4130
  if (w && h)
3933
4131
  return { width: w, height: h };
3934
4132
  } catch {
4133
+ const dep = ensureCommand("identify");
4134
+ if (dep.installed) {
4135
+ try {
4136
+ const out = execSync7(`identify -format "%w %h" ${JSON.stringify(filePath)}`, {
4137
+ encoding: "utf8",
4138
+ stdio: ["pipe", "pipe", "pipe"],
4139
+ timeout: 5e3
4140
+ }).trim();
4141
+ const [w, h] = out.split(" ").map(Number);
4142
+ if (w && h)
4143
+ return { width: w, height: h };
4144
+ } catch {
4145
+ }
4146
+ }
3935
4147
  }
3936
4148
  try {
3937
- const out = execSync6(`file ${JSON.stringify(filePath)}`, {
4149
+ const out = execSync7(`file ${JSON.stringify(filePath)}`, {
3938
4150
  encoding: "utf8",
3939
4151
  stdio: ["pipe", "pipe", "pipe"],
3940
4152
  timeout: 5e3
@@ -3948,15 +4160,16 @@ function getImageDimensions(filePath) {
3948
4160
  }
3949
4161
  function hasTesseract() {
3950
4162
  try {
3951
- execSync6("tesseract --version", { stdio: "pipe", timeout: 5e3 });
4163
+ execSync7("tesseract --version", { stdio: "pipe", timeout: 5e3 });
3952
4164
  return true;
3953
4165
  } catch {
3954
- return false;
4166
+ const dep = ensureCommand("tesseract");
4167
+ return dep.available;
3955
4168
  }
3956
4169
  }
3957
4170
  function runOCR(filePath) {
3958
4171
  try {
3959
- return execSync6(`tesseract ${JSON.stringify(filePath)} stdout 2>/dev/null`, {
4172
+ return execSync7(`tesseract ${JSON.stringify(filePath)} stdout 2>/dev/null`, {
3960
4173
  encoding: "utf8",
3961
4174
  stdio: ["pipe", "pipe", "pipe"],
3962
4175
  timeout: 3e4
@@ -3969,6 +4182,7 @@ var IMAGE_EXTENSIONS, ImageReadTool, ScreenshotTool, OCRTool;
3969
4182
  var init_image = __esm({
3970
4183
  "packages/execution/dist/tools/image.js"() {
3971
4184
  "use strict";
4185
+ init_system_deps();
3972
4186
  IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
3973
4187
  ".png",
3974
4188
  ".jpg",
@@ -4089,7 +4303,7 @@ ${ocrText}`);
4089
4303
  };
4090
4304
  }
4091
4305
  try {
4092
- execSync6(cmd, { stdio: "pipe", timeout: 1e4 });
4306
+ execSync7(cmd, { stdio: "pipe", timeout: 1e4 });
4093
4307
  } catch (err) {
4094
4308
  return {
4095
4309
  success: false,
@@ -4137,7 +4351,7 @@ ${ocrText}`);
4137
4351
  if (plat === "linux") {
4138
4352
  for (const tool of ["scrot", "gnome-screenshot", "import"]) {
4139
4353
  try {
4140
- execSync6(`which ${tool}`, { stdio: "pipe" });
4354
+ execSync7(`which ${tool}`, { stdio: "pipe" });
4141
4355
  if (tool === "scrot") {
4142
4356
  if (region === "active")
4143
4357
  return `scrot -u ${out}`;
@@ -4160,6 +4374,16 @@ ${ocrText}`);
4160
4374
  } catch {
4161
4375
  }
4162
4376
  }
4377
+ const dep = ensureCommand("scrot");
4378
+ if (dep.available) {
4379
+ if (region === "active")
4380
+ return `scrot -u ${out}`;
4381
+ if (region !== "full" && region.includes(",")) {
4382
+ const [x, y, w, h] = region.split(",").map(Number);
4383
+ return `scrot -a ${x},${y},${w},${h} ${out}`;
4384
+ }
4385
+ return `scrot ${out}`;
4386
+ }
4163
4387
  }
4164
4388
  return null;
4165
4389
  }
@@ -4204,7 +4428,7 @@ ${ocrText}`);
4204
4428
  return {
4205
4429
  success: false,
4206
4430
  output: "",
4207
- error: "Tesseract not installed. Install with: sudo apt install tesseract-ocr",
4431
+ error: "Tesseract not available. Auto-install failed. Try manually: sudo apt install tesseract-ocr",
4208
4432
  durationMs: Date.now() - start
4209
4433
  };
4210
4434
  }
@@ -4214,14 +4438,14 @@ ${ocrText}`);
4214
4438
  if (x != null && y != null && w != null && h != null) {
4215
4439
  const croppedPath = join10(tmpdir(), `oa-ocr-crop-${Date.now()}.png`);
4216
4440
  try {
4217
- execSync6(`convert ${JSON.stringify(fullPath)} -crop ${w}x${h}+${x}+${y} +repage ${JSON.stringify(croppedPath)}`, { stdio: "pipe", timeout: 1e4 });
4441
+ execSync7(`convert ${JSON.stringify(fullPath)} -crop ${w}x${h}+${x}+${y} +repage ${JSON.stringify(croppedPath)}`, { stdio: "pipe", timeout: 1e4 });
4218
4442
  inputPath = croppedPath;
4219
4443
  } catch {
4220
4444
  }
4221
4445
  }
4222
4446
  }
4223
4447
  try {
4224
- const text = execSync6(`tesseract ${JSON.stringify(inputPath)} stdout -l ${language} --psm ${psm} 2>/dev/null`, { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }).trim();
4448
+ const text = execSync7(`tesseract ${JSON.stringify(inputPath)} stdout -l ${language} --psm ${psm} 2>/dev/null`, { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }).trim();
4225
4449
  if (!text) {
4226
4450
  return { success: true, output: "(no text detected in image)", durationMs: Date.now() - start };
4227
4451
  }
@@ -4756,7 +4980,7 @@ var init_tool_creator = __esm({
4756
4980
  import { existsSync as existsSync9, readdirSync as readdirSync5, readFileSync as readFileSync8 } from "node:fs";
4757
4981
  import { join as join12, basename as basename2, dirname as dirname2 } from "node:path";
4758
4982
  import { homedir as homedir4 } from "node:os";
4759
- import { execSync as execSync7 } from "node:child_process";
4983
+ import { execSync as execSync8 } from "node:child_process";
4760
4984
  function getAiwgPaths() {
4761
4985
  const dataDir = join12(homedir4(), ".local", "share", "ai-writing-guide");
4762
4986
  return {
@@ -4769,7 +4993,7 @@ function findAiwgPackageRoot() {
4769
4993
  if (_cachedAiwgPkgRoot !== void 0)
4770
4994
  return _cachedAiwgPkgRoot;
4771
4995
  try {
4772
- const globalRoot = execSync7("npm root -g", {
4996
+ const globalRoot = execSync8("npm root -g", {
4773
4997
  encoding: "utf-8",
4774
4998
  timeout: 5e3,
4775
4999
  stdio: ["pipe", "pipe", "pipe"]
@@ -5156,7 +5380,7 @@ ${content}`,
5156
5380
  import { existsSync as existsSync10, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, readFileSync as readFileSync9, unlinkSync } from "node:fs";
5157
5381
  import { join as join13, basename as basename3, extname as extname3, resolve as resolve12 } from "node:path";
5158
5382
  import { homedir as homedir5 } from "node:os";
5159
- import { execSync as execSync8, spawn as spawn4 } from "node:child_process";
5383
+ import { execSync as execSync9, spawn as spawn4 } from "node:child_process";
5160
5384
  function isTranscribable(path) {
5161
5385
  const ext = extname3(path).toLowerCase();
5162
5386
  return AUDIO_EXTS.has(ext) || VIDEO_EXTS.has(ext);
@@ -5166,7 +5390,7 @@ async function loadTranscribeCli() {
5166
5390
  return _tcModule;
5167
5391
  _tcChecked = true;
5168
5392
  try {
5169
- const globalRoot = execSync8("npm root -g", {
5393
+ const globalRoot = execSync9("npm root -g", {
5170
5394
  encoding: "utf-8",
5171
5395
  timeout: 5e3,
5172
5396
  stdio: ["pipe", "pipe", "pipe"]
@@ -5331,7 +5555,7 @@ var init_transcribe_tool = __esm({
5331
5555
  const args = [filePath, "-m", model, "-f", "txt"];
5332
5556
  if (diarize)
5333
5557
  args.push("--diarize");
5334
- const output = execSync8(`transcribe-cli ${args.join(" ")}`, {
5558
+ const output = execSync9(`transcribe-cli ${args.join(" ")}`, {
5335
5559
  encoding: "utf-8",
5336
5560
  timeout: 3e5,
5337
5561
  // 5 min max
@@ -5398,12 +5622,12 @@ var init_transcribe_tool = __esm({
5398
5622
  const tmpFile = join13(tmpDir, `download-${Date.now()}${ext}`);
5399
5623
  try {
5400
5624
  try {
5401
- execSync8(`curl -sL -o "${tmpFile}" "${url}"`, {
5625
+ execSync9(`curl -sL -o "${tmpFile}" "${url}"`, {
5402
5626
  timeout: 12e4,
5403
5627
  stdio: ["pipe", "pipe", "pipe"]
5404
5628
  });
5405
5629
  } catch {
5406
- execSync8(`wget -q -O "${tmpFile}" "${url}"`, {
5630
+ execSync9(`wget -q -O "${tmpFile}" "${url}"`, {
5407
5631
  timeout: 12e4,
5408
5632
  stdio: ["pipe", "pipe", "pipe"]
5409
5633
  });
@@ -6216,7 +6440,7 @@ ${parts.join("\n\n")}`,
6216
6440
 
6217
6441
  // packages/execution/dist/tools/vision.js
6218
6442
  import { readFileSync as readFileSync10, existsSync as existsSync11, statSync as statSync5 } from "node:fs";
6219
- import { execSync as execSync9, spawn as spawn6 } from "node:child_process";
6443
+ import { execSync as execSync10, spawn as spawn6 } from "node:child_process";
6220
6444
  import { resolve as resolve15, extname as extname6, basename as basename4, dirname as dirname4, join as join15 } from "node:path";
6221
6445
  import { fileURLToPath } from "node:url";
6222
6446
  async function probeStation(endpoint) {
@@ -6240,14 +6464,14 @@ function findStationBinary() {
6240
6464
  for (const p of localVenvPaths) {
6241
6465
  if (existsSync11(p)) {
6242
6466
  try {
6243
- execSync9(`${JSON.stringify(p)} -c "import moondream_station"`, { stdio: "pipe", timeout: 5e3 });
6467
+ execSync10(`${JSON.stringify(p)} -c "import moondream_station"`, { stdio: "pipe", timeout: 5e3 });
6244
6468
  return p;
6245
6469
  } catch {
6246
6470
  }
6247
6471
  }
6248
6472
  }
6249
6473
  try {
6250
- const path = execSync9("which moondream-station", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e3 }).trim();
6474
+ const path = execSync10("which moondream-station", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e3 }).trim();
6251
6475
  if (path)
6252
6476
  return path;
6253
6477
  } catch {
@@ -6518,13 +6742,13 @@ Coordinates are normalized (0-1). Multiply by image width/height for pixel value
6518
6742
 
6519
6743
  // packages/execution/dist/tools/desktop-click.js
6520
6744
  import { readFileSync as readFileSync11, existsSync as existsSync12 } from "node:fs";
6521
- import { execSync as execSync10 } from "node:child_process";
6745
+ import { execSync as execSync11 } from "node:child_process";
6522
6746
  import { tmpdir as tmpdir3 } from "node:os";
6523
6747
  import { join as join16, dirname as dirname5 } from "node:path";
6524
6748
  import { fileURLToPath as fileURLToPath2 } from "node:url";
6525
- function hasCommand(cmd) {
6749
+ function hasCommand2(cmd) {
6526
6750
  try {
6527
- execSync10(`which ${cmd}`, { stdio: "pipe", timeout: 3e3 });
6751
+ execSync11(`which ${cmd}`, { stdio: "pipe", timeout: 3e3 });
6528
6752
  return true;
6529
6753
  } catch {
6530
6754
  return false;
@@ -6532,7 +6756,7 @@ function hasCommand(cmd) {
6532
6756
  }
6533
6757
  function getImageDimensions2(filePath) {
6534
6758
  try {
6535
- const out = execSync10(`identify -format "%w %h" ${JSON.stringify(filePath)}`, {
6759
+ const out = execSync11(`identify -format "%w %h" ${JSON.stringify(filePath)}`, {
6536
6760
  encoding: "utf8",
6537
6761
  stdio: ["pipe", "pipe", "pipe"],
6538
6762
  timeout: 5e3
@@ -6543,14 +6767,14 @@ function getImageDimensions2(filePath) {
6543
6767
  } catch {
6544
6768
  }
6545
6769
  try {
6546
- const out = execSync10(`python3 -c "from PIL import Image; i=Image.open(${JSON.stringify(filePath)}); print(i.size[0], i.size[1])"`, { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 5e3 }).trim();
6770
+ const out = execSync11(`python3 -c "from PIL import Image; i=Image.open(${JSON.stringify(filePath)}); print(i.size[0], i.size[1])"`, { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 5e3 }).trim();
6547
6771
  const [w, h] = out.split(" ").map(Number);
6548
6772
  if (w && h)
6549
6773
  return { width: w, height: h };
6550
6774
  } catch {
6551
6775
  }
6552
6776
  try {
6553
- const out = execSync10(`file ${JSON.stringify(filePath)}`, {
6777
+ const out = execSync11(`file ${JSON.stringify(filePath)}`, {
6554
6778
  encoding: "utf8",
6555
6779
  stdio: ["pipe", "pipe", "pipe"],
6556
6780
  timeout: 5e3
@@ -6569,25 +6793,30 @@ function captureScreenshot(outputPath) {
6569
6793
  if (plat === "darwin") {
6570
6794
  cmd = `screencapture ${out}`;
6571
6795
  } else if (plat === "linux") {
6572
- if (hasCommand("scrot")) {
6796
+ if (hasCommand2("scrot")) {
6573
6797
  cmd = `scrot ${out}`;
6574
- } else if (hasCommand("gnome-screenshot")) {
6798
+ } else if (hasCommand2("gnome-screenshot")) {
6575
6799
  cmd = `gnome-screenshot -f ${out}`;
6576
- } else if (hasCommand("import")) {
6800
+ } else if (hasCommand2("import")) {
6577
6801
  cmd = `import -window root ${out}`;
6578
6802
  } else {
6579
- try {
6580
- execSync10(`DISPLAY=:0 python3 -c "from PIL import ImageGrab; ImageGrab.grab().save(${JSON.stringify(outputPath)})"`, { stdio: "pipe", timeout: 1e4 });
6581
- if (existsSync12(outputPath))
6582
- return;
6583
- } catch {
6803
+ const dep = ensureCommand("scrot");
6804
+ if (dep.available) {
6805
+ cmd = `scrot ${out}`;
6806
+ } else {
6807
+ try {
6808
+ execSync11(`DISPLAY=:0 python3 -c "from PIL import ImageGrab; ImageGrab.grab().save(${JSON.stringify(outputPath)})"`, { stdio: "pipe", timeout: 1e4 });
6809
+ if (existsSync12(outputPath))
6810
+ return;
6811
+ } catch {
6812
+ }
6584
6813
  }
6585
6814
  }
6586
6815
  }
6587
6816
  if (!cmd) {
6588
- throw new Error("No screenshot tool found. Install one of: scrot, gnome-screenshot, imagemagick, or python3-pil");
6817
+ throw new Error("No screenshot tool found. Auto-install failed. Try manually: sudo apt install scrot");
6589
6818
  }
6590
- execSync10(cmd, { stdio: "pipe", timeout: 1e4 });
6819
+ execSync11(cmd, { stdio: "pipe", timeout: 1e4 });
6591
6820
  if (!existsSync12(outputPath)) {
6592
6821
  throw new Error("Screenshot file was not created");
6593
6822
  }
@@ -6599,12 +6828,17 @@ function clickAt(x, y, button, clickType) {
6599
6828
  const buttonNum = button === "right" ? 3 : button === "middle" ? 2 : 1;
6600
6829
  const clicks = clickType === "double" ? 2 : 1;
6601
6830
  if (plat === "linux") {
6602
- if (hasCommand("xdotool")) {
6603
- execSync10(`xdotool mousemove --sync ${rx} ${ry}`, { stdio: "pipe", timeout: 5e3 });
6831
+ let hasXdotool = hasCommand2("xdotool");
6832
+ if (!hasXdotool) {
6833
+ const dep = ensureCommand("xdotool");
6834
+ hasXdotool = dep.available;
6835
+ }
6836
+ if (hasXdotool) {
6837
+ execSync11(`xdotool mousemove --sync ${rx} ${ry}`, { stdio: "pipe", timeout: 5e3 });
6604
6838
  if (clickType === "double") {
6605
- execSync10(`xdotool click --repeat 2 --delay 50 ${buttonNum}`, { stdio: "pipe", timeout: 5e3 });
6839
+ execSync11(`xdotool click --repeat 2 --delay 50 ${buttonNum}`, { stdio: "pipe", timeout: 5e3 });
6606
6840
  } else {
6607
- execSync10(`xdotool click ${buttonNum}`, { stdio: "pipe", timeout: 5e3 });
6841
+ execSync11(`xdotool click ${buttonNum}`, { stdio: "pipe", timeout: 5e3 });
6608
6842
  }
6609
6843
  return;
6610
6844
  }
@@ -6628,31 +6862,30 @@ for i in range(${clicks}):
6628
6862
  if i < ${clicks - 1}:
6629
6863
  time.sleep(0.05)
6630
6864
  `.trim().replace(/\n/g, "; ");
6631
- const pythonPaths = ["python3"];
6632
6865
  try {
6633
- execSync10(`DISPLAY=:0 python3 -c "${pyScript}"`, { stdio: "pipe", timeout: 5e3 });
6866
+ execSync11(`DISPLAY=:0 python3 -c "${pyScript}"`, { stdio: "pipe", timeout: 5e3 });
6634
6867
  return;
6635
6868
  } catch {
6636
6869
  }
6637
6870
  try {
6638
6871
  const venvPy = join16(__dirname, "../../../../.moondream-venv/bin/python");
6639
- execSync10(`DISPLAY=:0 ${JSON.stringify(venvPy)} -c "${pyScript}"`, { stdio: "pipe", timeout: 5e3 });
6872
+ execSync11(`DISPLAY=:0 ${JSON.stringify(venvPy)} -c "${pyScript}"`, { stdio: "pipe", timeout: 5e3 });
6640
6873
  return;
6641
6874
  } catch {
6642
6875
  }
6643
- throw new Error("No mouse control available. Install one of:\n - xdotool: sudo apt install xdotool\n - python-xlib: pip install python-xlib");
6876
+ throw new Error("No mouse control available. Auto-install of xdotool failed. Try manually:\n sudo apt install xdotool");
6644
6877
  } else if (plat === "darwin") {
6645
- if (!hasCommand("cliclick")) {
6878
+ if (!hasCommand2("cliclick")) {
6646
6879
  throw new Error("cliclick not found. Install with: brew install cliclick");
6647
6880
  }
6648
6881
  const rx2 = Math.round(x);
6649
6882
  const ry2 = Math.round(y);
6650
6883
  if (clickType === "double") {
6651
- execSync10(`cliclick dc:${rx2},${ry2}`, { stdio: "pipe", timeout: 5e3 });
6884
+ execSync11(`cliclick dc:${rx2},${ry2}`, { stdio: "pipe", timeout: 5e3 });
6652
6885
  } else if (button === "right") {
6653
- execSync10(`cliclick rc:${rx2},${ry2}`, { stdio: "pipe", timeout: 5e3 });
6886
+ execSync11(`cliclick rc:${rx2},${ry2}`, { stdio: "pipe", timeout: 5e3 });
6654
6887
  } else {
6655
- execSync10(`cliclick c:${rx2},${ry2}`, { stdio: "pipe", timeout: 5e3 });
6888
+ execSync11(`cliclick c:${rx2},${ry2}`, { stdio: "pipe", timeout: 5e3 });
6656
6889
  }
6657
6890
  } else {
6658
6891
  throw new Error(`Desktop click not supported on platform: ${plat}`);
@@ -6662,6 +6895,7 @@ var __dirname, DesktopClickTool, DesktopDescribeTool;
6662
6895
  var init_desktop_click = __esm({
6663
6896
  "packages/execution/dist/tools/desktop-click.js"() {
6664
6897
  "use strict";
6898
+ init_system_deps();
6665
6899
  __dirname = dirname5(fileURLToPath2(import.meta.url));
6666
6900
  DesktopClickTool = class {
6667
6901
  workingDir;
@@ -6991,6 +7225,7 @@ var init_dist2 = __esm({
6991
7225
  init_structured_read();
6992
7226
  init_vision();
6993
7227
  init_desktop_click();
7228
+ init_system_deps();
6994
7229
  init_shellRunner();
6995
7230
  init_gitWorktree();
6996
7231
  init_patchApplier();
@@ -11476,7 +11711,7 @@ var init_dist5 = __esm({
11476
11711
  });
11477
11712
 
11478
11713
  // packages/cli/dist/tui/listen.js
11479
- import { spawn as spawn7, execSync as execSync11 } from "node:child_process";
11714
+ import { spawn as spawn7, execSync as execSync12 } from "node:child_process";
11480
11715
  import { existsSync as existsSync13, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
11481
11716
  import { join as join19 } from "node:path";
11482
11717
  import { homedir as homedir6 } from "node:os";
@@ -11496,7 +11731,7 @@ function findMicCaptureCommand() {
11496
11731
  const platform3 = process.platform;
11497
11732
  if (platform3 === "linux") {
11498
11733
  try {
11499
- execSync11("which arecord", { stdio: "pipe" });
11734
+ execSync12("which arecord", { stdio: "pipe" });
11500
11735
  return {
11501
11736
  cmd: "arecord",
11502
11737
  args: ["-f", "S16_LE", "-r", "16000", "-c", "1", "-t", "raw", "-q", "-"]
@@ -11506,7 +11741,7 @@ function findMicCaptureCommand() {
11506
11741
  }
11507
11742
  if (platform3 === "darwin") {
11508
11743
  try {
11509
- execSync11("which sox", { stdio: "pipe" });
11744
+ execSync12("which sox", { stdio: "pipe" });
11510
11745
  return {
11511
11746
  cmd: "sox",
11512
11747
  args: ["-d", "-t", "raw", "-r", "16000", "-c", "1", "-b", "16", "-e", "signed-integer", "-"]
@@ -11515,7 +11750,7 @@ function findMicCaptureCommand() {
11515
11750
  }
11516
11751
  }
11517
11752
  try {
11518
- execSync11("which ffmpeg", { stdio: "pipe" });
11753
+ execSync12("which ffmpeg", { stdio: "pipe" });
11519
11754
  if (platform3 === "linux") {
11520
11755
  return {
11521
11756
  cmd: "ffmpeg",
@@ -11564,7 +11799,7 @@ function ensureTranscribeCliBackground() {
11564
11799
  return;
11565
11800
  _bgInstallPromise = (async () => {
11566
11801
  try {
11567
- const globalRoot = execSync11("npm root -g", {
11802
+ const globalRoot = execSync12("npm root -g", {
11568
11803
  encoding: "utf-8",
11569
11804
  timeout: 5e3,
11570
11805
  stdio: ["pipe", "pipe", "pipe"]
@@ -11671,7 +11906,7 @@ var init_listen = __esm({
11671
11906
  }
11672
11907
  if (!this.transcribeCliAvailable) {
11673
11908
  try {
11674
- execSync11("which transcribe-cli", { stdio: "pipe" });
11909
+ execSync12("which transcribe-cli", { stdio: "pipe" });
11675
11910
  this.transcribeCliAvailable = true;
11676
11911
  } catch {
11677
11912
  this.transcribeCliAvailable = false;
@@ -11688,7 +11923,7 @@ var init_listen = __esm({
11688
11923
  } catch {
11689
11924
  }
11690
11925
  try {
11691
- const globalRoot = execSync11("npm root -g", {
11926
+ const globalRoot = execSync12("npm root -g", {
11692
11927
  encoding: "utf-8",
11693
11928
  timeout: 5e3,
11694
11929
  stdio: ["pipe", "pipe", "pipe"]
@@ -11739,7 +11974,7 @@ var init_listen = __esm({
11739
11974
  if (!tc) {
11740
11975
  this.emit("info", "Installing transcribe-cli...");
11741
11976
  try {
11742
- execSync11("npm i -g transcribe-cli", { stdio: "pipe", timeout: 18e4 });
11977
+ execSync12("npm i -g transcribe-cli", { stdio: "pipe", timeout: 18e4 });
11743
11978
  this.transcribeCliAvailable = null;
11744
11979
  tc = await this.loadTranscribeCli();
11745
11980
  } catch {
@@ -11750,14 +11985,23 @@ var init_listen = __esm({
11750
11985
  }
11751
11986
  }
11752
11987
  const TranscribeLive = tc.TranscribeLive;
11753
- this.liveTranscriber = new TranscribeLive({
11754
- model: this.config.model,
11755
- sampleRate: 16e3,
11756
- channels: 1,
11757
- sampleWidth: 2,
11758
- chunkDuration: 3
11759
- // 3s chunks for responsive transcription
11760
- });
11988
+ if (!TranscribeLive) {
11989
+ return "transcribe-cli does not export TranscribeLive. Try updating: npm i -g transcribe-cli@latest";
11990
+ }
11991
+ try {
11992
+ this.liveTranscriber = new TranscribeLive({
11993
+ model: this.config.model,
11994
+ sampleRate: 16e3,
11995
+ channels: 1,
11996
+ sampleWidth: 2,
11997
+ chunkDuration: 3
11998
+ // 3s chunks for responsive transcription
11999
+ });
12000
+ } catch (err) {
12001
+ const arch = process.arch;
12002
+ const armHint = arch === "arm64" || arch === "arm" ? ` Live transcription may not be supported on ${process.platform}-${arch}.` : "";
12003
+ return `Failed to create live transcriber.${armHint} Error: ${err instanceof Error ? err.message : String(err)}`;
12004
+ }
11761
12005
  this.liveTranscriber.on("transcript", (evt) => {
11762
12006
  if (!evt.text.trim())
11763
12007
  return;
@@ -11771,17 +12015,28 @@ var init_listen = __esm({
11771
12015
  this.liveTranscriber.on("error", (err) => {
11772
12016
  this.emit("error", err);
11773
12017
  });
11774
- await new Promise((resolve19, reject) => {
11775
- const timeout = setTimeout(() => reject(new Error("Model load timeout (60s)")), 6e4);
11776
- this.liveTranscriber.on("ready", () => {
11777
- clearTimeout(timeout);
11778
- resolve19();
11779
- });
11780
- this.liveTranscriber.on("error", (err) => {
11781
- clearTimeout(timeout);
11782
- reject(err);
12018
+ try {
12019
+ await new Promise((resolve19, reject) => {
12020
+ const timeout = setTimeout(() => reject(new Error("Model load timeout (60s)")), 6e4);
12021
+ this.liveTranscriber.on("ready", () => {
12022
+ clearTimeout(timeout);
12023
+ resolve19();
12024
+ });
12025
+ this.liveTranscriber.on("error", (err) => {
12026
+ clearTimeout(timeout);
12027
+ reject(err);
12028
+ });
11783
12029
  });
11784
- });
12030
+ } catch (err) {
12031
+ try {
12032
+ this.liveTranscriber.stop();
12033
+ } catch {
12034
+ }
12035
+ this.liveTranscriber = null;
12036
+ const arch = process.arch;
12037
+ const armHint = arch === "arm64" || arch === "arm" ? ` The whisper model or phonemizer WASM may not support ${process.platform}-${arch}. File transcription (/transcribe <file>) may still work.` : "";
12038
+ return `Failed to start live transcription: ${err instanceof Error ? err.message : String(err)}${armHint}`;
12039
+ }
11785
12040
  this.micProcess = spawn7(micCmd.cmd, micCmd.args, {
11786
12041
  stdio: ["pipe", "pipe", "pipe"],
11787
12042
  env: { ...process.env }
@@ -11876,7 +12131,7 @@ var init_listen = __esm({
11876
12131
  }
11877
12132
  if (!tc) {
11878
12133
  try {
11879
- execSync11("npm i -g transcribe-cli", { stdio: "pipe", timeout: 18e4 });
12134
+ execSync12("npm i -g transcribe-cli", { stdio: "pipe", timeout: 18e4 });
11880
12135
  this.transcribeCliAvailable = null;
11881
12136
  tc = await this.loadTranscribeCli();
11882
12137
  } catch {
@@ -13428,7 +13683,7 @@ var init_oa_directory = __esm({
13428
13683
 
13429
13684
  // packages/cli/dist/tui/setup.js
13430
13685
  import * as readline from "node:readline";
13431
- import { execSync as execSync12 } from "node:child_process";
13686
+ import { execSync as execSync13 } from "node:child_process";
13432
13687
  import { existsSync as existsSync15, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "node:fs";
13433
13688
  import { join as join22 } from "node:path";
13434
13689
  import { homedir as homedir8 } from "node:os";
@@ -13438,7 +13693,7 @@ function detectSystemSpecs() {
13438
13693
  let gpuVramGB = 0;
13439
13694
  let gpuName = "";
13440
13695
  try {
13441
- const memInfo = execSync12("free -b 2>/dev/null || sysctl -n hw.memsize 2>/dev/null", {
13696
+ const memInfo = execSync13("free -b 2>/dev/null || sysctl -n hw.memsize 2>/dev/null", {
13442
13697
  encoding: "utf8",
13443
13698
  timeout: 5e3
13444
13699
  });
@@ -13458,7 +13713,7 @@ function detectSystemSpecs() {
13458
13713
  } catch {
13459
13714
  }
13460
13715
  try {
13461
- const nvidiaSmi = execSync12("nvidia-smi --query-gpu=memory.total,name --format=csv,noheader,nounits 2>/dev/null", { encoding: "utf8", timeout: 5e3 });
13716
+ const nvidiaSmi = execSync13("nvidia-smi --query-gpu=memory.total,name --format=csv,noheader,nounits 2>/dev/null", { encoding: "utf8", timeout: 5e3 });
13462
13717
  const lines = nvidiaSmi.trim().split("\n");
13463
13718
  if (lines.length > 0) {
13464
13719
  for (const line of lines) {
@@ -13520,7 +13775,7 @@ function ask(rl, question) {
13520
13775
  }
13521
13776
  function pullModelWithAutoUpdate(tag) {
13522
13777
  try {
13523
- execSync12(`ollama pull ${tag}`, {
13778
+ execSync13(`ollama pull ${tag}`, {
13524
13779
  stdio: "inherit",
13525
13780
  timeout: 36e5
13526
13781
  // 1 hour max
@@ -13537,7 +13792,7 @@ function pullModelWithAutoUpdate(tag) {
13537
13792
 
13538
13793
  `);
13539
13794
  try {
13540
- execSync12("curl -fsSL https://ollama.com/install.sh | sh", {
13795
+ execSync13("curl -fsSL https://ollama.com/install.sh | sh", {
13541
13796
  stdio: "inherit",
13542
13797
  timeout: 3e5
13543
13798
  // 5 min max for install
@@ -13548,7 +13803,7 @@ function pullModelWithAutoUpdate(tag) {
13548
13803
  process.stdout.write(` ${c2.cyan("\u25CF")} Retrying pull of ${c2.bold(tag)}...
13549
13804
 
13550
13805
  `);
13551
- execSync12(`ollama pull ${tag}`, {
13806
+ execSync13(`ollama pull ${tag}`, {
13552
13807
  stdio: "inherit",
13553
13808
  timeout: 36e5
13554
13809
  });
@@ -13869,7 +14124,7 @@ async function doSetup(config, rl) {
13869
14124
  const modelfilePath = join22(modelDir2, `Modelfile.${customName}`);
13870
14125
  writeFileSync7(modelfilePath, modelfileContent + "\n", "utf8");
13871
14126
  process.stdout.write(` ${c2.dim("Creating model...")} `);
13872
- execSync12(`ollama create ${customName} -f ${modelfilePath}`, {
14127
+ execSync13(`ollama create ${customName} -f ${modelfilePath}`, {
13873
14128
  stdio: "pipe",
13874
14129
  timeout: 12e4
13875
14130
  });
@@ -13954,7 +14209,7 @@ function createExpandedVariant(baseModel, specs, sizeGB) {
13954
14209
  mkdirSync7(modelDir2, { recursive: true });
13955
14210
  const modelfilePath = join22(modelDir2, `Modelfile.${customName}`);
13956
14211
  writeFileSync7(modelfilePath, modelfileContent + "\n", "utf8");
13957
- execSync12(`ollama create ${customName} -f ${modelfilePath}`, {
14212
+ execSync13(`ollama create ${customName} -f ${modelfilePath}`, {
13958
14213
  stdio: "pipe",
13959
14214
  timeout: 12e4
13960
14215
  });
@@ -14624,9 +14879,9 @@ async function handleUpdate(subcommand, ctx) {
14624
14879
  process.stdout.write(` ${c2.cyan("\u25CF")} Installing update...
14625
14880
 
14626
14881
  `);
14627
- const { execSync: execSync16 } = await import("node:child_process");
14882
+ const { execSync: execSync17 } = await import("node:child_process");
14628
14883
  try {
14629
- execSync16(`npm cache clean --force open-agents-ai 2>/dev/null; npm install -g open-agents-ai@latest --force`, { stdio: "pipe", timeout: 18e4 });
14884
+ execSync17(`npm cache clean --force open-agents-ai 2>/dev/null; npm install -g open-agents-ai@latest --force`, { stdio: "pipe", timeout: 18e4 });
14630
14885
  } catch {
14631
14886
  renderWarning("Update install failed. Try manually: npm i -g open-agents-ai");
14632
14887
  return;
@@ -14706,7 +14961,7 @@ var init_commands = __esm({
14706
14961
  // packages/cli/dist/tui/project-context.js
14707
14962
  import { existsSync as existsSync16, readFileSync as readFileSync13, readdirSync as readdirSync7 } from "node:fs";
14708
14963
  import { join as join23, basename as basename6 } from "node:path";
14709
- import { execSync as execSync13 } from "node:child_process";
14964
+ import { execSync as execSync14 } from "node:child_process";
14710
14965
  import { homedir as homedir9, platform, release } from "node:os";
14711
14966
  function loadProjectFiles(repoRoot) {
14712
14967
  const discovered = discoverContextFiles(repoRoot);
@@ -14737,19 +14992,19 @@ function loadProjectMap(repoRoot) {
14737
14992
  }
14738
14993
  function getGitInfo(repoRoot) {
14739
14994
  try {
14740
- execSync13("git rev-parse --is-inside-work-tree", { cwd: repoRoot, stdio: "pipe" });
14995
+ execSync14("git rev-parse --is-inside-work-tree", { cwd: repoRoot, stdio: "pipe" });
14741
14996
  } catch {
14742
14997
  return "";
14743
14998
  }
14744
14999
  const lines = [];
14745
15000
  try {
14746
- const branch = execSync13("git branch --show-current", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
15001
+ const branch = execSync14("git branch --show-current", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
14747
15002
  if (branch)
14748
15003
  lines.push(`Branch: ${branch}`);
14749
15004
  } catch {
14750
15005
  }
14751
15006
  try {
14752
- const status = execSync13("git status --porcelain", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
15007
+ const status = execSync14("git status --porcelain", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
14753
15008
  if (status) {
14754
15009
  const changed = status.split("\n").length;
14755
15010
  lines.push(`Working tree: ${changed} changed file(s)`);
@@ -14759,7 +15014,7 @@ function getGitInfo(repoRoot) {
14759
15014
  } catch {
14760
15015
  }
14761
15016
  try {
14762
- const log = execSync13("git log --oneline -5 --no-decorate", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
15017
+ const log = execSync14("git log --oneline -5 --no-decorate", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
14763
15018
  if (log)
14764
15019
  lines.push(`Recent commits:
14765
15020
  ${log}`);
@@ -15807,7 +16062,7 @@ var init_carousel = __esm({
15807
16062
  import { existsSync as existsSync17, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8, readFileSync as readFileSync14, unlinkSync as unlinkSync3 } from "node:fs";
15808
16063
  import { join as join24 } from "node:path";
15809
16064
  import { homedir as homedir10, tmpdir as tmpdir4, platform as platform2 } from "node:os";
15810
- import { execSync as execSync14, spawn as nodeSpawn } from "node:child_process";
16065
+ import { execSync as execSync15, spawn as nodeSpawn } from "node:child_process";
15811
16066
  import { createRequire } from "node:module";
15812
16067
  function voiceDir() {
15813
16068
  return join24(homedir10(), ".open-agents", "voice");
@@ -16225,7 +16480,7 @@ var init_voice = __esm({
16225
16480
  }
16226
16481
  for (const player of ["paplay", "pw-play", "aplay"]) {
16227
16482
  try {
16228
- execSync14(`which ${player}`, { stdio: "pipe" });
16483
+ execSync15(`which ${player}`, { stdio: "pipe" });
16229
16484
  return [player, path];
16230
16485
  } catch {
16231
16486
  }
@@ -16247,6 +16502,8 @@ var init_voice = __esm({
16247
16502
  async ensureRuntime() {
16248
16503
  if (this.ort)
16249
16504
  return;
16505
+ const arch = process.arch;
16506
+ const isArmLinux = (arch === "arm64" || arch === "arm") && process.platform === "linux";
16250
16507
  mkdirSync8(voiceDir(), { recursive: true });
16251
16508
  const pkgPath = join24(voiceDir(), "package.json");
16252
16509
  const expectedDeps = {
@@ -16274,16 +16531,20 @@ var init_voice = __esm({
16274
16531
  try {
16275
16532
  this.ort = voiceRequire("onnxruntime-node");
16276
16533
  } catch {
16534
+ if (isArmLinux) {
16535
+ throw new Error(`Voice synthesis (onnxruntime-node) is not available on ARM Linux (${arch}). Voice feedback is disabled on this architecture.`);
16536
+ }
16277
16537
  renderInfo("Installing ONNX runtime for voice synthesis...");
16278
16538
  try {
16279
- execSync14("npm install --no-audit --no-fund", {
16539
+ execSync15("npm install --no-audit --no-fund", {
16280
16540
  cwd: voiceDir(),
16281
16541
  stdio: "pipe",
16282
16542
  timeout: 12e4
16283
16543
  });
16284
16544
  this.ort = voiceRequire("onnxruntime-node");
16285
16545
  } catch (err) {
16286
- throw new Error(`Failed to install voice dependencies. Try manually: cd ${voiceDir()} && npm install
16546
+ const armHint = arch !== "x64" ? ` onnxruntime-node may not support ${process.platform}-${arch}.` : "";
16547
+ throw new Error(`Failed to install voice dependencies.${armHint} Try manually: cd ${voiceDir()} && npm install
16287
16548
  Error: ${err instanceof Error ? err.message : String(err)}`);
16288
16549
  }
16289
16550
  }
@@ -16291,9 +16552,12 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
16291
16552
  const phonemizerMod = voiceRequire("phonemizer");
16292
16553
  this.phonemizeFn = phonemizerMod.phonemize ?? phonemizerMod.default?.phonemize ?? phonemizerMod;
16293
16554
  } catch {
16555
+ if (isArmLinux) {
16556
+ throw new Error(`Phonemizer (espeak-ng WASM) is not available on ARM Linux (${arch}). Voice feedback is disabled on this architecture.`);
16557
+ }
16294
16558
  renderInfo("Installing phonemizer for voice synthesis...");
16295
16559
  try {
16296
- execSync14("npm install --no-audit --no-fund", {
16560
+ execSync15("npm install --no-audit --no-fund", {
16297
16561
  cwd: voiceDir(),
16298
16562
  stdio: "pipe",
16299
16563
  timeout: 12e4
@@ -16301,7 +16565,8 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
16301
16565
  const phonemizerMod = voiceRequire("phonemizer");
16302
16566
  this.phonemizeFn = phonemizerMod.phonemize ?? phonemizerMod.default?.phonemize ?? phonemizerMod;
16303
16567
  } catch (err) {
16304
- throw new Error(`Failed to install phonemizer. Try manually: cd ${voiceDir()} && npm install
16568
+ const armHint = arch !== "x64" ? ` phonemizer WASM may not support ${process.platform}-${arch}.` : "";
16569
+ throw new Error(`Failed to install phonemizer.${armHint} Try manually: cd ${voiceDir()} && npm install
16305
16570
  Error: ${err instanceof Error ? err.message : String(err)}`);
16306
16571
  }
16307
16572
  }
@@ -16983,7 +17248,7 @@ var init_edit_history = __esm({
16983
17248
  // packages/cli/dist/tui/dream-engine.js
16984
17249
  import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync9, readFileSync as readFileSync15, existsSync as existsSync18, cpSync, rmSync, readdirSync as readdirSync8 } from "node:fs";
16985
17250
  import { join as join26, basename as basename7 } from "node:path";
16986
- import { execSync as execSync15 } from "node:child_process";
17251
+ import { execSync as execSync16 } from "node:child_process";
16987
17252
  function adaptTool(tool) {
16988
17253
  return {
16989
17254
  name: tool.name,
@@ -17236,7 +17501,7 @@ var init_dream_engine = __esm({
17236
17501
  }
17237
17502
  }
17238
17503
  try {
17239
- const output = execSync15(cmd, {
17504
+ const output = execSync16(cmd, {
17240
17505
  cwd: this.repoRoot,
17241
17506
  timeout: 3e4,
17242
17507
  encoding: "utf-8",
@@ -17456,17 +17721,17 @@ Dreams directory: ${this.dreamsDir}`);
17456
17721
  try {
17457
17722
  mkdirSync10(checkpointDir, { recursive: true });
17458
17723
  try {
17459
- const gitStatus = execSync15("git status --porcelain", {
17724
+ const gitStatus = execSync16("git status --porcelain", {
17460
17725
  cwd: this.repoRoot,
17461
17726
  encoding: "utf-8",
17462
17727
  timeout: 1e4
17463
17728
  });
17464
- const gitDiff = execSync15("git diff", {
17729
+ const gitDiff = execSync16("git diff", {
17465
17730
  cwd: this.repoRoot,
17466
17731
  encoding: "utf-8",
17467
17732
  timeout: 1e4
17468
17733
  });
17469
- const gitHash = execSync15("git rev-parse HEAD 2>/dev/null || echo 'no-git'", {
17734
+ const gitHash = execSync16("git rev-parse HEAD 2>/dev/null || echo 'no-git'", {
17470
17735
  cwd: this.repoRoot,
17471
17736
  encoding: "utf-8",
17472
17737
  timeout: 5e3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",