open-agents-ai 0.186.64 → 0.186.66

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 CHANGED
@@ -247398,11 +247398,31 @@ async function autoLaunchStation(port = 2020) {
247398
247398
  return false;
247399
247399
  return new Promise((resolvePromise) => {
247400
247400
  const child = spawn8(pythonBin, [launcherScript, "--port", String(port)], {
247401
- stdio: ["ignore", "pipe", "pipe"],
247402
- detached: true
247401
+ stdio: ["ignore", "pipe", "pipe"]
247403
247402
  });
247404
247403
  stationProcess = child;
247405
- child.unref();
247404
+ const cleanupStation = () => {
247405
+ if (stationProcess && stationProcess.pid && !stationProcess.killed) {
247406
+ try {
247407
+ process.kill(-stationProcess.pid, "SIGKILL");
247408
+ } catch {
247409
+ }
247410
+ try {
247411
+ stationProcess.kill("SIGKILL");
247412
+ } catch {
247413
+ }
247414
+ stationProcess = null;
247415
+ }
247416
+ };
247417
+ process.on("exit", cleanupStation);
247418
+ process.on("SIGTERM", () => {
247419
+ cleanupStation();
247420
+ process.exit(0);
247421
+ });
247422
+ process.on("SIGINT", () => {
247423
+ cleanupStation();
247424
+ process.exit(0);
247425
+ });
247406
247426
  let resolved = false;
247407
247427
  const timer = setTimeout(() => {
247408
247428
  if (!resolved) {
@@ -247575,9 +247595,9 @@ var init_vision = __esm({
247575
247595
  if (ollamaResult)
247576
247596
  return ollamaResult;
247577
247597
  try {
247578
- const { execSync: execSync37 } = await import("node:child_process");
247598
+ const { execSync: execSync38 } = await import("node:child_process");
247579
247599
  try {
247580
- execSync37("pip3 install --user moondream 2>/dev/null || pip install --user moondream 2>/dev/null", {
247600
+ execSync38("pip3 install --user moondream 2>/dev/null || pip install --user moondream 2>/dev/null", {
247581
247601
  timeout: 12e4,
247582
247602
  stdio: "pipe"
247583
247603
  });
@@ -247590,7 +247610,7 @@ var init_vision = __esm({
247590
247610
  } catch {
247591
247611
  }
247592
247612
  try {
247593
- execSync37("ollama pull moondream", { timeout: 3e5, stdio: "pipe" });
247613
+ execSync38("ollama pull moondream", { timeout: 3e5, stdio: "pipe" });
247594
247614
  const retryOllama = await this.tryOllamaVision(buffer2, filename, action, prompt, length4, start2);
247595
247615
  if (retryOllama)
247596
247616
  return retryOllama;
@@ -247698,8 +247718,8 @@ Coordinates are normalized (0-1). Multiply by image width/height for pixel value
247698
247718
  const errText = await res.text().catch(() => "");
247699
247719
  if (res.status === 404 || /not found|does not exist/i.test(errText)) {
247700
247720
  try {
247701
- const { execSync: execSync37 } = await import("node:child_process");
247702
- execSync37("ollama pull moondream", { timeout: 3e5, stdio: "pipe" });
247721
+ const { execSync: execSync38 } = await import("node:child_process");
247722
+ execSync38("ollama pull moondream", { timeout: 3e5, stdio: "pipe" });
247703
247723
  res = await fetch(`${ollamaHost}/api/generate`, {
247704
247724
  method: "POST",
247705
247725
  headers: { "Content-Type": "application/json" },
@@ -249000,7 +249020,6 @@ async function launchService() {
249000
249020
  }
249001
249021
  serviceProcess = spawn9(python, [SCRAPE_SCRIPT], {
249002
249022
  stdio: "ignore",
249003
- detached: true,
249004
249023
  env: {
249005
249024
  ...process.env,
249006
249025
  SCRAPE_PORT: String(DEFAULT_PORT),
@@ -249008,7 +249027,28 @@ async function launchService() {
249008
249027
  SCRAPE_REQUIRE_AUTH: "0"
249009
249028
  }
249010
249029
  });
249011
- serviceProcess.unref();
249030
+ const cleanupService = () => {
249031
+ if (serviceProcess && serviceProcess.pid && !serviceProcess.killed) {
249032
+ try {
249033
+ process.kill(-serviceProcess.pid, "SIGKILL");
249034
+ } catch {
249035
+ }
249036
+ try {
249037
+ serviceProcess.kill("SIGKILL");
249038
+ } catch {
249039
+ }
249040
+ serviceProcess = null;
249041
+ }
249042
+ };
249043
+ process.on("exit", cleanupService);
249044
+ process.on("SIGTERM", () => {
249045
+ cleanupService();
249046
+ process.exit(0);
249047
+ });
249048
+ process.on("SIGINT", () => {
249049
+ cleanupService();
249050
+ process.exit(0);
249051
+ });
249012
249052
  for (let i2 = 0; i2 < 120; i2++) {
249013
249053
  await new Promise((r2) => setTimeout(r2, 500));
249014
249054
  if (await probeService())
@@ -253231,10 +253271,21 @@ function spawnFullSubAgent(task, opts, onOutput, onComplete) {
253231
253271
  NO_COLOR: "1",
253232
253272
  CI: "true"
253233
253273
  },
253234
- stdio: ["pipe", "pipe", "pipe"],
253235
- detached: true
253274
+ stdio: ["pipe", "pipe", "pipe"]
253236
253275
  });
253237
- child.unref();
253276
+ const cleanupChild = () => {
253277
+ if (child.pid && !child.killed) {
253278
+ try {
253279
+ process.kill(-child.pid, "SIGKILL");
253280
+ } catch {
253281
+ }
253282
+ try {
253283
+ child.kill("SIGKILL");
253284
+ } catch {
253285
+ }
253286
+ }
253287
+ };
253288
+ process.on("exit", cleanupChild);
253238
253289
  const outputBuffer = [];
253239
253290
  const entry = {
253240
253291
  id,
@@ -260684,7 +260735,7 @@ ${transcript}`
260684
260735
  const buffer2 = Buffer.from(rawBase64, "base64");
260685
260736
  let resizedBase64 = null;
260686
260737
  try {
260687
- const { execSync: execSync37 } = await import("node:child_process");
260738
+ const { execSync: execSync38 } = await import("node:child_process");
260688
260739
  const { writeFileSync: writeFileSync33, readFileSync: readFileSync49, unlinkSync: unlinkSync13 } = await import("node:fs");
260689
260740
  const { join: join83 } = await import("node:path");
260690
260741
  const { tmpdir: tmpdir11 } = await import("node:os");
@@ -260694,7 +260745,7 @@ ${transcript}`
260694
260745
  const pyBin = process.platform === "win32" ? "python" : "python3";
260695
260746
  const escapedIn = tmpIn.replace(/\\/g, "\\\\");
260696
260747
  const escapedOut = tmpOut.replace(/\\/g, "\\\\");
260697
- execSync37(`${pyBin} -c "from PIL import Image; img = Image.open('${escapedIn}'); img.thumbnail((512, 512), Image.LANCZOS); img = img.convert('RGB'); img.save('${escapedOut}', 'JPEG', quality=75)"`, { timeout: 1e4, stdio: "pipe" });
260748
+ execSync38(`${pyBin} -c "from PIL import Image; img = Image.open('${escapedIn}'); img.thumbnail((512, 512), Image.LANCZOS); img = img.convert('RGB'); img.save('${escapedOut}', 'JPEG', quality=75)"`, { timeout: 1e4, stdio: "pipe" });
260698
260749
  const resizedBuf = readFileSync49(tmpOut);
260699
260750
  resizedBase64 = `data:image/jpeg;base64,${resizedBuf.toString("base64")}`;
260700
260751
  try {
@@ -260748,8 +260799,8 @@ ${transcript}`
260748
260799
  if (!res.ok && model === "moondream" && res.status === 404) {
260749
260800
  this.emit({ type: "status", content: `Pulling moondream vision model...`, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
260750
260801
  try {
260751
- const { execSync: execSync37 } = await import("node:child_process");
260752
- execSync37("ollama pull moondream", { timeout: 3e5, stdio: "pipe" });
260802
+ const { execSync: execSync38 } = await import("node:child_process");
260803
+ execSync38("ollama pull moondream", { timeout: 3e5, stdio: "pipe" });
260753
260804
  res = await fetch(`${ollamaHost}/api/generate`, {
260754
260805
  method: "POST",
260755
260806
  headers: { "Content-Type": "application/json" },
@@ -285666,7 +285717,7 @@ async function handlePeerEndpoint(peerId, authKey, ctx3, local) {
285666
285717
  }
285667
285718
  }
285668
285719
  async function handleParallel(arg, ctx3) {
285669
- const { execSync: execSync37 } = await import("node:child_process");
285720
+ const { execSync: execSync38 } = await import("node:child_process");
285670
285721
  const baseUrl = ctx3.config.backendUrl || "http://localhost:11434";
285671
285722
  const isRemote = ctx3.config.backendType === "nexus";
285672
285723
  if (isRemote) {
@@ -285690,7 +285741,7 @@ async function handleParallel(arg, ctx3) {
285690
285741
  }
285691
285742
  let systemdVal = "";
285692
285743
  try {
285693
- const out = execSync37("systemctl show ollama.service -p Environment 2>/dev/null || true", { encoding: "utf8" });
285744
+ const out = execSync38("systemctl show ollama.service -p Environment 2>/dev/null || true", { encoding: "utf8" });
285694
285745
  const match = out.match(/OLLAMA_NUM_PARALLEL=(\d+)/);
285695
285746
  if (match)
285696
285747
  systemdVal = match[1];
@@ -285719,7 +285770,7 @@ async function handleParallel(arg, ctx3) {
285719
285770
  }
285720
285771
  const isSystemd = (() => {
285721
285772
  try {
285722
- const out = execSync37("systemctl is-active ollama.service 2>/dev/null", { encoding: "utf8" }).trim();
285773
+ const out = execSync38("systemctl is-active ollama.service 2>/dev/null", { encoding: "utf8" }).trim();
285723
285774
  return out === "active" || out === "inactive";
285724
285775
  } catch {
285725
285776
  return false;
@@ -285733,10 +285784,10 @@ async function handleParallel(arg, ctx3) {
285733
285784
  const overrideContent = `[Service]
285734
285785
  Environment="OLLAMA_NUM_PARALLEL=${n2}"
285735
285786
  `;
285736
- execSync37(`sudo mkdir -p ${overrideDir}`, { stdio: "pipe" });
285737
- execSync37(`echo '${overrideContent}' | sudo tee ${overrideFile} > /dev/null`, { stdio: "pipe" });
285738
- execSync37("sudo systemctl daemon-reload", { stdio: "pipe" });
285739
- execSync37("sudo systemctl restart ollama.service", { stdio: "pipe" });
285787
+ execSync38(`sudo mkdir -p ${overrideDir}`, { stdio: "pipe" });
285788
+ execSync38(`echo '${overrideContent}' | sudo tee ${overrideFile} > /dev/null`, { stdio: "pipe" });
285789
+ execSync38("sudo systemctl daemon-reload", { stdio: "pipe" });
285790
+ execSync38("sudo systemctl restart ollama.service", { stdio: "pipe" });
285740
285791
  let ready = false;
285741
285792
  for (let i2 = 0; i2 < 30 && !ready; i2++) {
285742
285793
  await new Promise((r2) => setTimeout(r2, 500));
@@ -285763,7 +285814,7 @@ Environment="OLLAMA_NUM_PARALLEL=${n2}"
285763
285814
  renderInfo(`Setting OLLAMA_NUM_PARALLEL=${n2}...`);
285764
285815
  try {
285765
285816
  try {
285766
- execSync37("pkill -f 'ollama serve' 2>/dev/null || true", { stdio: "pipe" });
285817
+ execSync38("pkill -f 'ollama serve' 2>/dev/null || true", { stdio: "pipe" });
285767
285818
  } catch {
285768
285819
  }
285769
285820
  await new Promise((r2) => setTimeout(r2, 1e3));
@@ -286724,18 +286775,18 @@ async function showExposeDashboard(gateway, rl, ctx3) {
286724
286775
  const cmd = `/endpoint ${id} --auth ${gateway.authKey ?? ""}`;
286725
286776
  let copied = false;
286726
286777
  try {
286727
- const { execSync: execSync37 } = __require("node:child_process");
286778
+ const { execSync: execSync38 } = __require("node:child_process");
286728
286779
  const platform6 = process.platform;
286729
286780
  if (platform6 === "darwin") {
286730
- execSync37("pbcopy", { input: cmd, timeout: 3e3 });
286781
+ execSync38("pbcopy", { input: cmd, timeout: 3e3 });
286731
286782
  copied = true;
286732
286783
  } else if (platform6 === "win32") {
286733
- execSync37("clip", { input: cmd, timeout: 3e3 });
286784
+ execSync38("clip", { input: cmd, timeout: 3e3 });
286734
286785
  copied = true;
286735
286786
  } else {
286736
286787
  for (const tool of ["xclip -selection clipboard", "xsel --clipboard --input", "wl-copy"]) {
286737
286788
  try {
286738
- execSync37(tool, { input: cmd, timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] });
286789
+ execSync38(tool, { input: cmd, timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] });
286739
286790
  copied = true;
286740
286791
  break;
286741
286792
  } catch {
@@ -300099,7 +300150,7 @@ import * as https3 from "node:https";
300099
300150
  import { createRequire as createRequire4 } from "node:module";
300100
300151
  import { fileURLToPath as fileURLToPath15 } from "node:url";
300101
300152
  import { dirname as dirname23, join as join76, resolve as resolve33 } from "node:path";
300102
- import { spawn as spawn22 } from "node:child_process";
300153
+ import { spawn as spawn22, execSync as execSync36 } from "node:child_process";
300103
300154
  import { mkdirSync as mkdirSync31, writeFileSync as writeFileSync29, readFileSync as readFileSync46, readdirSync as readdirSync22, existsSync as existsSync58 } from "node:fs";
300104
300155
  import { randomBytes as randomBytes19, randomUUID as randomUUID5 } from "node:crypto";
300105
300156
  function getVersion3() {
@@ -300984,8 +301035,8 @@ async function handleV1Run(req2, res) {
300984
301035
  env: runEnv,
300985
301036
  stdio: ["ignore", "pipe", "pipe"],
300986
301037
  detached: true
301038
+ // Own process group so we can kill the whole tree with -pid
300987
301039
  });
300988
- child.unref();
300989
301040
  job.sandbox = sandbox;
300990
301041
  }
300991
301042
  job.pid = child.pid ?? 0;
@@ -301092,11 +301143,31 @@ function handleV1RunsDelete(res, id) {
301092
301143
  }
301093
301144
  if (job.status === "running") {
301094
301145
  const child = runningProcesses.get(id);
301095
- if (child && !child.killed) {
301096
- child.kill("SIGTERM");
301097
- } else if (job.pid > 0) {
301146
+ const pid = child?.pid ?? job.pid;
301147
+ if (pid > 0) {
301098
301148
  try {
301099
- process.kill(job.pid, "SIGTERM");
301149
+ process.kill(-pid, "SIGTERM");
301150
+ } catch {
301151
+ }
301152
+ try {
301153
+ process.kill(pid, "SIGTERM");
301154
+ } catch {
301155
+ }
301156
+ setTimeout(() => {
301157
+ try {
301158
+ process.kill(-pid, "SIGKILL");
301159
+ } catch {
301160
+ }
301161
+ try {
301162
+ process.kill(pid, "SIGKILL");
301163
+ } catch {
301164
+ }
301165
+ }, 3e3);
301166
+ }
301167
+ const containerName = `oa-${id}`;
301168
+ if (job.sandbox === "container") {
301169
+ try {
301170
+ execSync36(`docker stop ${containerName}`, { timeout: 5e3, stdio: "ignore" });
301100
301171
  } catch {
301101
301172
  }
301102
301173
  }
@@ -301947,11 +302018,34 @@ function startApiServer(options2 = {}) {
301947
302018
  const shutdown = () => {
301948
302019
  log22("\n Shutting down API server...\n");
301949
302020
  for (const [id, child] of runningProcesses) {
301950
- if (!child.killed) {
301951
- child.kill("SIGTERM");
302021
+ const pid = child.pid;
302022
+ if (pid && pid > 0) {
302023
+ try {
302024
+ process.kill(-pid, "SIGTERM");
302025
+ } catch {
302026
+ }
302027
+ try {
302028
+ process.kill(pid, "SIGTERM");
302029
+ } catch {
302030
+ }
301952
302031
  }
301953
302032
  runningProcesses.delete(id);
301954
302033
  }
302034
+ setTimeout(() => {
302035
+ for (const [, child] of runningProcesses) {
302036
+ const pid = child.pid;
302037
+ if (pid && pid > 0) {
302038
+ try {
302039
+ process.kill(-pid, "SIGKILL");
302040
+ } catch {
302041
+ }
302042
+ try {
302043
+ process.kill(pid, "SIGKILL");
302044
+ } catch {
302045
+ }
302046
+ }
302047
+ }
302048
+ }, 2e3).unref();
301955
302049
  server.close(() => {
301956
302050
  log22(" Server stopped.\n");
301957
302051
  process.exit(0);
@@ -302013,7 +302107,7 @@ import { createRequire as createRequire5 } from "node:module";
302013
302107
  import { fileURLToPath as fileURLToPath16 } from "node:url";
302014
302108
  import { readFileSync as readFileSync47, writeFileSync as writeFileSync30, appendFileSync as appendFileSync6, rmSync as rmSync3, readdirSync as readdirSync23, mkdirSync as mkdirSync32 } from "node:fs";
302015
302109
  import { existsSync as existsSync59 } from "node:fs";
302016
- import { execSync as execSync36 } from "node:child_process";
302110
+ import { execSync as execSync37 } from "node:child_process";
302017
302111
  import { homedir as homedir21 } from "node:os";
302018
302112
  function formatTimeAgo(date) {
302019
302113
  const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
@@ -305783,7 +305877,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
305783
305877
  try {
305784
305878
  if (process.platform === "win32") {
305785
305879
  try {
305786
- execSync36(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
305880
+ execSync37(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
305787
305881
  } catch {
305788
305882
  }
305789
305883
  } else {
@@ -305810,7 +305904,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
305810
305904
  if (pid > 0) {
305811
305905
  if (process.platform === "win32") {
305812
305906
  try {
305813
- execSync36(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
305907
+ execSync37(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
305814
305908
  } catch {
305815
305909
  }
305816
305910
  } else {
@@ -305827,7 +305921,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
305827
305921
  } catch {
305828
305922
  }
305829
305923
  try {
305830
- execSync36(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.5", { timeout: 3e3, stdio: "ignore" });
305924
+ execSync37(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.5", { timeout: 3e3, stdio: "ignore" });
305831
305925
  } catch {
305832
305926
  }
305833
305927
  const oaPath = join77(repoRoot, OA_DIR);
@@ -305841,14 +305935,14 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
305841
305935
  } catch (err) {
305842
305936
  if (attempt < 2) {
305843
305937
  try {
305844
- execSync36(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.3", { timeout: 3e3, stdio: "ignore" });
305938
+ execSync37(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.3", { timeout: 3e3, stdio: "ignore" });
305845
305939
  } catch {
305846
305940
  }
305847
305941
  } else {
305848
305942
  writeContent(() => renderWarning(`Could not fully remove ${OA_DIR}/: ${err instanceof Error ? err.message : String(err)}`));
305849
305943
  if (process.platform === "win32") {
305850
305944
  try {
305851
- execSync36(`rd /s /q "${oaPath}"`, { timeout: 1e4, stdio: "ignore" });
305945
+ execSync37(`rd /s /q "${oaPath}"`, { timeout: 1e4, stdio: "ignore" });
305852
305946
  deleted = true;
305853
305947
  } catch {
305854
305948
  }
@@ -307250,8 +307344,16 @@ async function runBackground(task, config, opts) {
307250
307344
  env: { ...process.env, OA_JOB_ID: id },
307251
307345
  stdio: ["ignore", "pipe", "pipe"],
307252
307346
  detached: true
307347
+ // Own process group for tree kill
307348
+ });
307349
+ process.on("exit", () => {
307350
+ if (child.pid && !child.killed) {
307351
+ try {
307352
+ process.kill(-child.pid, "SIGKILL");
307353
+ } catch {
307354
+ }
307355
+ }
307253
307356
  });
307254
- child.unref();
307255
307357
  job.pid = child.pid ?? 0;
307256
307358
  writeFileSync31(join78(dir, `${id}.json`), JSON.stringify(job, null, 2));
307257
307359
  let output = "";
@@ -773,11 +773,33 @@ _clean_thread.start()
773
773
 
774
774
  @atexit.register
775
775
  def _shutdown_cleanup():
776
+ """Clean up ALL resources: Chrome browser, frame cache thread."""
776
777
  _CLEAN_STOP.set()
778
+ # CRITICAL: Close the Chrome browser to prevent orphaned Chrome processes
779
+ try:
780
+ Tools.close_browser()
781
+ except Exception:
782
+ pass
777
783
  with contextlib.suppress(Exception):
778
784
  _clean_thread.join(timeout=2.0)
779
785
 
780
786
 
787
+ # Signal handlers: ensure Chrome is killed on SIGTERM/SIGINT
788
+ import signal as _signal
789
+
790
+ def _handle_terminate(signum, frame):
791
+ """Graceful shutdown on SIGTERM/SIGINT — close Chrome then exit."""
792
+ try:
793
+ Tools.close_browser()
794
+ except Exception:
795
+ pass
796
+ _CLEAN_STOP.set()
797
+ raise SystemExit(0)
798
+
799
+ _signal.signal(_signal.SIGTERM, _handle_terminate)
800
+ _signal.signal(_signal.SIGINT, _handle_terminate)
801
+
802
+
781
803
  # ──────────────────────────────────────────────────────────────
782
804
  # 7) Utility responses
783
805
  # ──────────────────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.186.64",
3
+ "version": "0.186.66",
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",