open-agents-ai 0.187.215 → 0.187.217

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 (2) hide show
  1. package/dist/index.js +242 -30
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -293277,6 +293277,7 @@ __export(setup_exports, {
293277
293277
  pullModelWithAutoUpdate: () => pullModelWithAutoUpdate,
293278
293278
  recommendModel: () => recommendModel,
293279
293279
  renderScoreBar: () => renderScoreBar,
293280
+ runElevatedCommand: () => runElevatedCommand,
293280
293281
  runSetupWizard: () => runSetupWizard,
293281
293282
  updateOllama: () => updateOllama
293282
293283
  });
@@ -293564,15 +293565,24 @@ function ensureZstd() {
293564
293565
  process.stdout.write(`
293565
293566
  ${c3.cyan("●")} Installing zstd (required by ollama install.sh)...
293566
293567
  `);
293567
- const candidates = [
293568
- `pkexec sh -c "${installCmd}"`,
293569
- `sudo ${installCmd}`,
293570
- installCmd
293571
- // bare (works if already root)
293572
- ];
293573
- for (const cmd of candidates) {
293568
+ const hasDisplay = !!(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
293569
+ const askpassHelper = detectAskpassHelper() ?? writeAskpassHelper();
293570
+ const askpassEnv = askpassHelper ? { ...process.env, SUDO_ASKPASS: askpassHelper, DEBIAN_FRONTEND: "noninteractive" } : { ...process.env, DEBIAN_FRONTEND: "noninteractive" };
293571
+ const candidates = [];
293572
+ if (hasCmd("pkexec") && hasDisplay) {
293573
+ candidates.push({ cmd: `pkexec sh -c ${shellEscape(installCmd)}`, env: process.env });
293574
+ }
293575
+ if (askpassHelper) {
293576
+ candidates.push({ cmd: `sudo -A ${installCmd}`, env: askpassEnv });
293577
+ }
293578
+ candidates.push({ cmd: installCmd, env: process.env });
293579
+ for (const cand of candidates) {
293574
293580
  try {
293575
- execSync48(cmd, { stdio: "inherit", timeout: 18e4 });
293581
+ execSync48(cand.cmd, {
293582
+ stdio: ["ignore", "inherit", "pipe"],
293583
+ env: cand.env,
293584
+ timeout: 18e4
293585
+ });
293576
293586
  if (hasCmd("zstd")) {
293577
293587
  process.stdout.write(` ${c3.green("✔")} zstd installed.
293578
293588
  `);
@@ -293585,17 +293595,159 @@ function ensureZstd() {
293585
293595
  `);
293586
293596
  return false;
293587
293597
  }
293598
+ function detectAskpassHelper() {
293599
+ const linuxHelpers = [
293600
+ "/usr/lib/ssh/ssh-askpass",
293601
+ // openssh-askpass
293602
+ "/usr/lib/openssh/ssh-askpass",
293603
+ "/usr/libexec/openssh/ssh-askpass",
293604
+ "/usr/bin/ksshaskpass",
293605
+ // KDE
293606
+ "/usr/bin/lxqt-openssh-askpass"
293607
+ ];
293608
+ if (process.platform === "linux") {
293609
+ for (const path5 of linuxHelpers) {
293610
+ try {
293611
+ if (existsSync59(path5)) return path5;
293612
+ } catch {
293613
+ }
293614
+ }
293615
+ for (const name10 of ["ssh-askpass", "ksshaskpass", "x11-ssh-askpass"]) {
293616
+ if (hasCmd(name10)) {
293617
+ try {
293618
+ const p2 = execSync48(`command -v ${name10}`, { encoding: "utf8" }).trim();
293619
+ if (p2) return p2;
293620
+ } catch {
293621
+ }
293622
+ }
293623
+ }
293624
+ }
293625
+ return null;
293626
+ }
293627
+ function writeAskpassHelper() {
293628
+ const tmpDir = join76(homedir26(), ".open-agents");
293629
+ try {
293630
+ mkdirSync33(tmpDir, { recursive: true });
293631
+ } catch {
293632
+ }
293633
+ const helperPath = join76(tmpDir, "askpass-helper.sh");
293634
+ let body = "";
293635
+ if (process.platform === "darwin") {
293636
+ body = `#!/bin/sh
293637
+ osascript -e 'Tell application "System Events" to display dialog "Open Agents needs admin access to update Ollama.\\n\\nPassword:" with title "Open Agents" with hidden answer default answer "" buttons {"Cancel","OK"} default button "OK"' -e 'text returned of result' 2>/dev/null
293638
+ `;
293639
+ } else if (process.platform === "linux") {
293640
+ const hasZenity = hasCmd("zenity");
293641
+ const hasKdialog = hasCmd("kdialog");
293642
+ const hasYad = hasCmd("yad");
293643
+ if (!hasZenity && !hasKdialog && !hasYad) return null;
293644
+ const lines = ["#!/bin/sh", 'TITLE="Open Agents"', 'TEXT="Open Agents needs admin access to update Ollama. Enter your password:"'];
293645
+ if (hasZenity) {
293646
+ lines.push('zenity --password --title="$TITLE" --text="$TEXT" 2>/dev/null && exit 0');
293647
+ }
293648
+ if (hasKdialog) {
293649
+ lines.push('kdialog --title "$TITLE" --password "$TEXT" 2>/dev/null && exit 0');
293650
+ }
293651
+ if (hasYad) {
293652
+ lines.push('yad --title="$TITLE" --text="$TEXT" --entry --hide-text 2>/dev/null && exit 0');
293653
+ }
293654
+ lines.push("exit 1");
293655
+ body = lines.join("\n") + "\n";
293656
+ } else {
293657
+ return null;
293658
+ }
293659
+ try {
293660
+ writeFileSync30(helperPath, body, { mode: 448 });
293661
+ execSync48(`chmod 700 "${helperPath}"`, { stdio: "ignore" });
293662
+ return helperPath;
293663
+ } catch {
293664
+ return null;
293665
+ }
293666
+ }
293667
+ function buildElevatedInstall() {
293668
+ const installCmd = "curl -fsSL https://ollama.com/install.sh | sh";
293669
+ const baseEnv = { ...process.env };
293670
+ if (process.platform === "linux" && hasCmd("pkexec") && (process.env.DISPLAY || process.env.WAYLAND_DISPLAY)) {
293671
+ return {
293672
+ cmd: `pkexec sh -c ${shellEscape(installCmd)}`,
293673
+ env: baseEnv,
293674
+ mode: "pkexec"
293675
+ };
293676
+ }
293677
+ const nativeAskpass = detectAskpassHelper();
293678
+ const writtenHelper = nativeAskpass ?? writeAskpassHelper();
293679
+ if (writtenHelper) {
293680
+ return {
293681
+ cmd: installCmd,
293682
+ // unchanged — install.sh's internal sudo calls will use ASKPASS
293683
+ env: { ...baseEnv, SUDO_ASKPASS: writtenHelper, DEBIAN_FRONTEND: "noninteractive" },
293684
+ mode: "askpass"
293685
+ };
293686
+ }
293687
+ return {
293688
+ cmd: installCmd,
293689
+ env: baseEnv,
293690
+ mode: "fallback"
293691
+ };
293692
+ }
293693
+ function shellEscape(s2) {
293694
+ return `'${s2.replace(/'/g, `'\\''`)}'`;
293695
+ }
293696
+ function runElevatedCommand(command, opts = {}) {
293697
+ const timeout2 = opts.timeoutMs ?? 6e4;
293698
+ const hasDisplay = !!(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
293699
+ const askpassHelper = detectAskpassHelper() ?? writeAskpassHelper();
293700
+ const askpassEnv = askpassHelper ? { ...process.env, SUDO_ASKPASS: askpassHelper } : process.env;
293701
+ const candidates = [];
293702
+ if (hasCmd("pkexec") && hasDisplay) {
293703
+ candidates.push({ cmd: `pkexec sh -c ${shellEscape(command)}`, env: process.env });
293704
+ }
293705
+ if (askpassHelper) {
293706
+ candidates.push({ cmd: `sudo -A sh -c ${shellEscape(command)}`, env: askpassEnv });
293707
+ }
293708
+ candidates.push({ cmd: command, env: process.env });
293709
+ let lastErr = null;
293710
+ for (const cand of candidates) {
293711
+ try {
293712
+ execSync48(cand.cmd, {
293713
+ stdio: ["ignore", "pipe", "pipe"],
293714
+ // never inherit TUI stdin
293715
+ env: cand.env,
293716
+ timeout: timeout2
293717
+ });
293718
+ return;
293719
+ } catch (err) {
293720
+ lastErr = err;
293721
+ }
293722
+ }
293723
+ if (!opts.swallowErrors && lastErr) {
293724
+ throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
293725
+ }
293726
+ }
293588
293727
  function runOllamaInstallScript() {
293589
293728
  const zstdReady = ensureZstd();
293590
293729
  if (!zstdReady) {
293591
293730
  process.stdout.write(` ${c3.yellow("⚠")} Proceeding without zstd — install may fail if the script requires it.
293592
293731
  `);
293593
293732
  }
293594
- const cmd = "curl -fsSL https://ollama.com/install.sh | sh";
293733
+ const elevated = buildElevatedInstall();
293734
+ if (elevated.mode === "pkexec") {
293735
+ process.stdout.write(` ${c3.cyan("●")} Elevation: pkexec (graphical PolicyKit prompt)
293736
+ `);
293737
+ } else if (elevated.mode === "askpass") {
293738
+ process.stdout.write(` ${c3.cyan("●")} Elevation: SUDO_ASKPASS (GUI password modal)
293739
+ `);
293740
+ } else {
293741
+ process.stdout.write(` ${c3.yellow("⚠")} No GUI password helper available — install may stall waiting for sudo.
293742
+ `);
293743
+ process.stdout.write(` ${c3.yellow("⚠")} Install one of: pkexec / zenity / kdialog / yad / ssh-askpass
293744
+ `);
293745
+ }
293595
293746
  const runOnce = () => {
293596
- execSync48(cmd, {
293597
- stdio: ["inherit", "inherit", "pipe"],
293598
- timeout: 3e5
293747
+ execSync48(elevated.cmd, {
293748
+ stdio: ["ignore", "inherit", "pipe"],
293749
+ env: elevated.env,
293750
+ timeout: 6e5
293599
293751
  });
293600
293752
  };
293601
293753
  try {
@@ -304975,10 +305127,13 @@ async function handleParallel(arg, ctx3) {
304975
305127
  const overrideContent = `[Service]
304976
305128
  Environment="OLLAMA_NUM_PARALLEL=${n2}"
304977
305129
  `;
304978
- execSync57(`sudo mkdir -p ${overrideDir}`, { stdio: "pipe" });
304979
- execSync57(`echo '${overrideContent}' | sudo tee ${overrideFile} > /dev/null`, { stdio: "pipe" });
304980
- execSync57("sudo systemctl daemon-reload", { stdio: "pipe" });
304981
- execSync57("sudo systemctl restart ollama.service", { stdio: "pipe" });
305130
+ const { runElevatedCommand: runElev } = await Promise.resolve().then(() => (init_setup(), setup_exports));
305131
+ runElev(`mkdir -p ${overrideDir}`, { timeoutMs: 3e4 });
305132
+ const escapedContent = overrideContent.replace(/'/g, `'\\''`);
305133
+ runElev(`bash -c 'cat > ${overrideFile} <<EOF
305134
+ ${escapedContent}EOF'`, { timeoutMs: 3e4 });
305135
+ runElev("systemctl daemon-reload", { timeoutMs: 3e4 });
305136
+ runElev("systemctl restart ollama.service", { timeoutMs: 3e4 });
304982
305137
  let ready = false;
304983
305138
  for (let i2 = 0; i2 < 30 && !ready; i2++) {
304984
305139
  await new Promise((r2) => setTimeout(r2, 500));
@@ -305410,8 +305565,8 @@ async function handleUpdate(subcommand, ctx3) {
305410
305565
  if (doUpdateOllama()) {
305411
305566
  renderInfo("Ollama updated successfully.");
305412
305567
  try {
305413
- const { execSync: es } = await import("node:child_process");
305414
- es("sudo systemctl restart ollama 2>/dev/null || true", { timeout: 1e4, stdio: "pipe" });
305568
+ const { runElevatedCommand: runElevatedCommand2 } = await Promise.resolve().then(() => (init_setup(), setup_exports));
305569
+ runElevatedCommand2("systemctl restart ollama", { timeoutMs: 1e4, swallowErrors: true });
305415
305570
  } catch {
305416
305571
  }
305417
305572
  } else {
@@ -316896,17 +317051,16 @@ body {
316896
317051
 
316897
317052
  /* WO-TASK-02 — task progress strip (shares processes-row styling) */
316898
317053
  #tasks-row {
316899
- display: none; /* hidden until tasks exist */
317054
+ display: none; /* hidden until tasks exist; flipped to flex when populated */
317055
+ flex-flow: row wrap; /* wrap items onto multiple lines for long lists */
316900
317056
  align-items: center;
316901
- gap: 6px;
317057
+ gap: 6px 6px; /* row gap + column gap once wrapping kicks in */
316902
317058
  padding: 4px 16px;
316903
317059
  min-height: 22px;
316904
317060
  background: #17171a;
316905
317061
  border-bottom: 1px solid #2a2a30;
316906
- overflow-x: auto;
316907
- scrollbar-width: none;
317062
+ /* Wrap instead of horizontal scroll so all tasks are visible */
316908
317063
  }
316909
- #tasks-row::-webkit-scrollbar { display: none; }
316910
317064
  #tasks-row .tasks-label {
316911
317065
  color: #444;
316912
317066
  font-size: 0.55rem;
@@ -317707,17 +317861,75 @@ async function sendMessage() {
317707
317861
  details.style.cssText = 'background:#1e1e22;border-left:2px solid #b2920a;margin:2px 0;font-size:0.7rem';
317708
317862
  const summary = document.createElement('summary');
317709
317863
  summary.style.cssText = 'padding:4px 8px;color:#b2920a;cursor:pointer';
317710
- summary.textContent = '\\u25B8 ' + (chunk.tool || 'tool');
317864
+
317865
+ // Build a compact one-line label so the user sees what the tool
317866
+ // is actually doing without expanding. todo_write gets a special
317867
+ // status-counter summary; everything else gets a short args
317868
+ // preview that JSON-stringifies arrays/objects so they don't
317869
+ // render as "[object Object],[object Object],...".
317870
+ const toolName = chunk.tool || 'tool';
317871
+ const a = (chunk.args && typeof chunk.args === 'object') ? chunk.args : {};
317872
+ let inlineSummary = '';
317873
+ if (toolName === 'todo_write' && Array.isArray(a.todos)) {
317874
+ const todos = a.todos;
317875
+ let p = 0, ip = 0, c = 0, b = 0;
317876
+ for (const t of todos) {
317877
+ const s = (t && typeof t === 'object') ? t.status : '';
317878
+ if (s === 'completed') c++;
317879
+ else if (s === 'in_progress') ip++;
317880
+ else if (s === 'blocked') b++;
317881
+ else p++;
317882
+ }
317883
+ inlineSummary = ' — ' + todos.length + ' items (' + c + '◉ ' + ip + '◐ ' + p + '〇' + (b > 0 ? ' ' + b + '◍' : '') + ')';
317884
+ } else {
317885
+ const previewParts = [];
317886
+ for (const [k, v] of Object.entries(a)) {
317887
+ let vs;
317888
+ if (v === null || v === undefined) vs = String(v);
317889
+ else if (typeof v === 'string') vs = v;
317890
+ else if (typeof v === 'number' || typeof v === 'boolean') vs = String(v);
317891
+ else { try { vs = JSON.stringify(v); } catch { vs = '[object]'; } }
317892
+ if (vs.length > 60) vs = vs.slice(0, 57) + '…';
317893
+ previewParts.push(k + '=' + vs);
317894
+ if (previewParts.length >= 3) break;
317895
+ }
317896
+ if (previewParts.length) inlineSummary = ' — ' + previewParts.join(', ');
317897
+ }
317898
+ summary.textContent = '▸ ' + toolName + inlineSummary;
317711
317899
  details.appendChild(summary);
317712
- // Expandable args — unpack all key-value pairs
317900
+
317901
+ // Expandable args — unpack all key-value pairs. CRITICAL: stringify
317902
+ // arrays/objects so they don't show as "[object Object]". For
317903
+ // todo_write specifically render a checklist instead of a blob.
317713
317904
  if (chunk.args && typeof chunk.args === 'object') {
317714
317905
  const argsDiv = document.createElement('div');
317715
317906
  argsDiv.style.cssText = 'padding:4px 8px 6px 16px;color:#888;font-size:0.65rem;border-top:1px solid #2a2a30';
317716
- for (const [k, v] of Object.entries(chunk.args)) {
317717
- const row = document.createElement('div');
317718
- row.style.cssText = 'padding:2px 0;display:flex;gap:8px';
317719
- row.innerHTML = '<span style="color:#b2920a;min-width:60px">' + k + '</span><span style="color:#b0b0b0;word-break:break-all">' + escHtml(String(v).slice(0, 500)) + '</span>';
317720
- argsDiv.appendChild(row);
317907
+ if (toolName === 'todo_write' && Array.isArray(a.todos)) {
317908
+ for (const t of a.todos) {
317909
+ if (!t || typeof t !== 'object') continue;
317910
+ const row = document.createElement('div');
317911
+ row.style.cssText = 'padding:2px 0';
317912
+ let mark = '〇';
317913
+ let color = '#666';
317914
+ if (t.status === 'completed') { mark = '◉'; color = '#4a7a4a'; }
317915
+ else if (t.status === 'in_progress') { mark = '◐'; color = '#b2920a'; }
317916
+ else if (t.status === 'blocked') { mark = '◍'; color = '#b25f5f'; }
317917
+ row.innerHTML = '<span style="color:' + color + '">' + mark + '</span> <span style="color:#b0b0b0">' + escHtml(String(t.content || '').slice(0, 300)) + '</span>' + (t.blocker ? ' <span style="color:#b25f5f">(blocked: ' + escHtml(String(t.blocker).slice(0, 100)) + ')</span>' : '');
317918
+ argsDiv.appendChild(row);
317919
+ }
317920
+ } else {
317921
+ for (const [k, v] of Object.entries(chunk.args)) {
317922
+ const row = document.createElement('div');
317923
+ row.style.cssText = 'padding:2px 0;display:flex;gap:8px';
317924
+ let vs;
317925
+ if (v === null || v === undefined) vs = String(v);
317926
+ else if (typeof v === 'string') vs = v;
317927
+ else if (typeof v === 'number' || typeof v === 'boolean') vs = String(v);
317928
+ else { try { vs = JSON.stringify(v, null, 2); } catch { vs = '[object]'; } }
317929
+ if (vs.length > 500) vs = vs.slice(0, 497) + '…';
317930
+ row.innerHTML = '<span style="color:#b2920a;min-width:60px">' + k + '</span><span style="color:#b0b0b0;word-break:break-all;white-space:pre-wrap">' + escHtml(vs) + '</span>';
317931
+ argsDiv.appendChild(row);
317932
+ }
317721
317933
  }
317722
317934
  details.appendChild(argsDiv);
317723
317935
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.215",
3
+ "version": "0.187.217",
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",