open-agents-ai 0.187.179 → 0.187.180

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 +133 -132
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -302317,6 +302317,13 @@ async function handleMcp(subcommand, ctx3) {
302317
302317
  }
302318
302318
  await showMcpMainMenu(ctx3);
302319
302319
  }
302320
+ function validateMcpConfigShape(parsed) {
302321
+ if (!parsed || typeof parsed !== "object") return "Config must be a JSON object";
302322
+ if (typeof parsed.command !== "string" && typeof parsed.url !== "string") {
302323
+ return "Config must have either 'command' (stdio) or 'url' (http)";
302324
+ }
302325
+ return null;
302326
+ }
302320
302327
  async function showMcpMainMenu(ctx3) {
302321
302328
  const manager = ctx3.getMcpManager?.();
302322
302329
  if (!manager) return;
@@ -302354,19 +302361,76 @@ async function showMcpMainMenu(ctx3) {
302354
302361
  }
302355
302362
  items.push({ key: "hdr_actions", label: selectColors.dim("\u2500\u2500\u2500 Actions \u2500\u2500\u2500"), kind: "header" });
302356
302363
  skipKeys.push("hdr_actions");
302357
- items.push({ key: "act_add", label: c3.cyan("\u2795 Add new server"), detail: "Paste/enter a config", kind: "action" });
302364
+ items.push({ key: "act_add", label: c3.cyan("\u2795 Add new server"), detail: "Inline name + JSON intake", kind: "action" });
302358
302365
  items.push({ key: "act_reload", label: c3.cyan("\u27F3 Reload all from config"), detail: "Re-read .oa/mcp.json + reconnect", kind: "action" });
302359
- items.push({ key: "act_open", label: c3.cyan("\u{1F4DD} Open .oa/mcp.json in editor"), detail: "External edit", kind: "action" });
302366
+ items.push({ key: "act_open", label: c3.cyan("\u{1F4DD} Show .oa/mcp.json path"), detail: "For external editing", kind: "action" });
302367
+ let pendingAdd = null;
302368
+ const postMessages = [];
302360
302369
  const result = await tuiSelect({
302361
302370
  items,
302362
302371
  title: "MCP Server Registry",
302363
- skipKeys
302372
+ skipKeys,
302373
+ onEnter: (item, helpers) => {
302374
+ if (item.key === "act_add") {
302375
+ helpers.getInput("Server name (e.g. memory):", "").then(async (name10) => {
302376
+ if (!name10 || name10.trim().length === 0) {
302377
+ postMessages.push({ kind: "info", text: "Add cancelled \u2014 no name entered." });
302378
+ helpers.render();
302379
+ return;
302380
+ }
302381
+ const cfgStr = await helpers.getInput(
302382
+ 'Config JSON (e.g. {"command":"npx","args":["-y","@modelcontextprotocol/server-memory"]}):',
302383
+ ""
302384
+ );
302385
+ if (!cfgStr || cfgStr.trim().length === 0) {
302386
+ postMessages.push({ kind: "info", text: "Add cancelled \u2014 no config entered." });
302387
+ helpers.render();
302388
+ return;
302389
+ }
302390
+ let parsed;
302391
+ try {
302392
+ parsed = JSON.parse(cfgStr);
302393
+ } catch (e2) {
302394
+ postMessages.push({ kind: "error", text: `Invalid JSON: ${e2 instanceof Error ? e2.message : String(e2)}` });
302395
+ helpers.render();
302396
+ return;
302397
+ }
302398
+ const shapeErr = validateMcpConfigShape(parsed);
302399
+ if (shapeErr) {
302400
+ postMessages.push({ kind: "error", text: shapeErr });
302401
+ helpers.render();
302402
+ return;
302403
+ }
302404
+ pendingAdd = { name: name10.trim(), config: parsed };
302405
+ helpers.resolve({ confirmed: true, key: "__pending_add__", index: -1 });
302406
+ });
302407
+ return true;
302408
+ }
302409
+ return false;
302410
+ }
302364
302411
  });
302365
- if (!result || !result.confirmed || !result.key) return;
302366
- if (result.key === "act_add") {
302367
- await mcpAddServerInteractive(ctx3);
302412
+ for (const msg of postMessages) {
302413
+ if (msg.kind === "info") renderInfo(msg.text);
302414
+ else if (msg.kind === "warn") renderWarning(msg.text);
302415
+ else renderError(msg.text);
302416
+ }
302417
+ if (pendingAdd) {
302418
+ const { name: name10, config } = pendingAdd;
302419
+ try {
302420
+ const c22 = await manager.addServer(name10, config);
302421
+ await ctx3.refreshMcpTools?.();
302422
+ if (c22.status === "connected") {
302423
+ renderInfo(`\u2714 ${name10} added and connected (${c22.tools.length} tools)`);
302424
+ } else {
302425
+ renderWarning(`\u26A0 ${name10} saved to .oa/mcp.json but not connected: ${c22.error ?? "unknown"}`);
302426
+ }
302427
+ } catch (e2) {
302428
+ renderError(`Add failed: ${e2 instanceof Error ? e2.message : String(e2)}`);
302429
+ }
302430
+ await showMcpMainMenu(ctx3);
302368
302431
  return;
302369
302432
  }
302433
+ if (!result || !result.confirmed || !result.key) return;
302370
302434
  if (result.key === "act_reload") {
302371
302435
  renderInfo("Reloading MCP servers...");
302372
302436
  await manager.disconnectAll().catch(() => {
@@ -302450,15 +302514,76 @@ async function showMcpDetailMenu(ctx3, serverName) {
302450
302514
  }
302451
302515
  items.push({ key: "hdr_actions", label: selectColors.dim("\u2500\u2500\u2500 Actions \u2500\u2500\u2500"), kind: "header" });
302452
302516
  skipKeys.push("hdr_actions");
302453
- items.push({ key: "act_edit", label: c3.cyan("\u270E Edit config"), detail: "Modify command/args/env/url/headers", kind: "action" });
302517
+ items.push({ key: "act_edit", label: c3.cyan("\u270E Edit config"), detail: "Modify command/args/env/url/headers (inline)", kind: "action" });
302454
302518
  items.push({ key: "act_reconnect", label: c3.cyan("\u27F3 Reconnect"), detail: "Disconnect + reconnect this server", kind: "action" });
302455
302519
  items.push({ key: "act_delete", label: c3.red("\u{1F5D1} Delete"), detail: "Remove from config + disconnect", kind: "action" });
302456
302520
  items.push({ key: "act_back", label: c3.dim("\u2190 Back to server list"), kind: "action" });
302521
+ let pendingEdit = null;
302522
+ const postMessages = [];
302457
302523
  const result = await tuiSelect({
302458
302524
  items,
302459
302525
  title: `MCP: ${serverName}`,
302460
- skipKeys
302526
+ skipKeys,
302527
+ onEnter: (item, helpers) => {
302528
+ if (item.key === "act_edit") {
302529
+ const currentJson = JSON.stringify(persisted ?? {});
302530
+ helpers.getInput(
302531
+ `New JSON config for ${serverName} (current shown \u2014 edit then Enter; Esc to cancel):`,
302532
+ currentJson
302533
+ ).then(async (newJson) => {
302534
+ if (newJson === null || newJson.trim().length === 0) {
302535
+ postMessages.push({ kind: "info", text: "Edit cancelled \u2014 no change." });
302536
+ helpers.render();
302537
+ return;
302538
+ }
302539
+ if (newJson.trim() === currentJson.trim()) {
302540
+ postMessages.push({ kind: "info", text: "Edit cancelled \u2014 config unchanged." });
302541
+ helpers.render();
302542
+ return;
302543
+ }
302544
+ let parsed;
302545
+ try {
302546
+ parsed = JSON.parse(newJson);
302547
+ } catch (e2) {
302548
+ postMessages.push({ kind: "error", text: `Invalid JSON: ${e2 instanceof Error ? e2.message : String(e2)}` });
302549
+ helpers.render();
302550
+ return;
302551
+ }
302552
+ const shapeErr = validateMcpConfigShape(parsed);
302553
+ if (shapeErr) {
302554
+ postMessages.push({ kind: "error", text: shapeErr });
302555
+ helpers.render();
302556
+ return;
302557
+ }
302558
+ pendingEdit = parsed;
302559
+ helpers.resolve({ confirmed: true, key: "__pending_edit__", index: -1 });
302560
+ });
302561
+ return true;
302562
+ }
302563
+ return false;
302564
+ }
302461
302565
  });
302566
+ for (const msg of postMessages) {
302567
+ if (msg.kind === "info") renderInfo(msg.text);
302568
+ else if (msg.kind === "warn") renderWarning(msg.text);
302569
+ else renderError(msg.text);
302570
+ }
302571
+ if (pendingEdit) {
302572
+ try {
302573
+ await manager.disconnect(serverName);
302574
+ const c22 = await manager.addServer(serverName, pendingEdit);
302575
+ await ctx3.refreshMcpTools?.();
302576
+ if (c22.status === "connected") {
302577
+ renderInfo(`\u2714 ${serverName} updated and reconnected (${c22.tools.length} tools)`);
302578
+ } else {
302579
+ renderWarning(`\u26A0 ${serverName} saved but not connected: ${c22.error ?? "unknown"}`);
302580
+ }
302581
+ } catch (e2) {
302582
+ renderError(`Save failed: ${e2 instanceof Error ? e2.message : String(e2)}`);
302583
+ }
302584
+ await showMcpDetailMenu(ctx3, serverName);
302585
+ return;
302586
+ }
302462
302587
  if (!result || !result.confirmed || !result.key) {
302463
302588
  await showMcpMainMenu(ctx3);
302464
302589
  return;
@@ -302467,10 +302592,6 @@ async function showMcpDetailMenu(ctx3, serverName) {
302467
302592
  await showMcpMainMenu(ctx3);
302468
302593
  return;
302469
302594
  }
302470
- if (result.key === "act_edit") {
302471
- await mcpEditServer(ctx3, serverName, persisted);
302472
- return;
302473
- }
302474
302595
  if (result.key === "act_reconnect") {
302475
302596
  renderInfo(`Reconnecting ${serverName}...`);
302476
302597
  try {
@@ -302503,126 +302624,6 @@ async function showMcpDetailMenu(ctx3, serverName) {
302503
302624
  return;
302504
302625
  }
302505
302626
  }
302506
- async function mcpEditServer(ctx3, serverName, current) {
302507
- const manager = ctx3.getMcpManager?.();
302508
- if (!manager) return;
302509
- const ask2 = (prompt) => new Promise((res) => {
302510
- process.stdout.write(prompt);
302511
- let buf = "";
302512
- const onData = (d2) => {
302513
- const s2 = d2.toString();
302514
- if (s2.includes("\n")) {
302515
- process.stdin.removeListener("data", onData);
302516
- process.stdin.pause();
302517
- res((buf + s2).split("\n")[0]);
302518
- } else {
302519
- buf += s2;
302520
- }
302521
- };
302522
- process.stdin.resume();
302523
- process.stdin.on("data", onData);
302524
- });
302525
- process.stdout.write(`
302526
- ${c3.cyan("Current config for ")}${c3.bold(serverName)}:
302527
- `);
302528
- process.stdout.write(` ${c3.dim(JSON.stringify(current ?? {}, null, 2).split("\n").join("\n "))}
302529
-
302530
- `);
302531
- process.stdout.write(` ${c3.cyan("Paste a new JSON config (single line) or press Enter to keep current.")}
302532
- `);
302533
- const newConfigJson = await ask2(" > ");
302534
- if (!newConfigJson || newConfigJson.trim().length === 0) {
302535
- renderInfo("No change.");
302536
- await showMcpDetailMenu(ctx3, serverName);
302537
- return;
302538
- }
302539
- let parsed;
302540
- try {
302541
- parsed = JSON.parse(newConfigJson);
302542
- } catch (e2) {
302543
- renderError(`Invalid JSON: ${e2 instanceof Error ? e2.message : String(e2)}`);
302544
- await showMcpDetailMenu(ctx3, serverName);
302545
- return;
302546
- }
302547
- if (!parsed || typeof parsed.command !== "string" && typeof parsed.url !== "string") {
302548
- renderError("Config must have either 'command' (stdio) or 'url' (http)");
302549
- await showMcpDetailMenu(ctx3, serverName);
302550
- return;
302551
- }
302552
- try {
302553
- await manager.disconnect(serverName);
302554
- const c22 = await manager.addServer(serverName, parsed);
302555
- await ctx3.refreshMcpTools?.();
302556
- if (c22.status === "connected") {
302557
- renderInfo(`\u2714 ${serverName} updated and reconnected (${c22.tools.length} tools)`);
302558
- } else {
302559
- renderWarning(`\u26A0 ${serverName} saved but not connected: ${c22.error}`);
302560
- }
302561
- } catch (e2) {
302562
- renderError(`Save failed: ${e2 instanceof Error ? e2.message : String(e2)}`);
302563
- }
302564
- await showMcpMainMenu(ctx3);
302565
- }
302566
- async function mcpAddServerInteractive(ctx3) {
302567
- const ask2 = (prompt) => new Promise((res) => {
302568
- process.stdout.write(prompt);
302569
- let buf = "";
302570
- const onData = (d2) => {
302571
- const s2 = d2.toString();
302572
- if (s2.includes("\n")) {
302573
- process.stdin.removeListener("data", onData);
302574
- process.stdin.pause();
302575
- res((buf + s2).split("\n")[0]);
302576
- } else {
302577
- buf += s2;
302578
- }
302579
- };
302580
- process.stdin.resume();
302581
- process.stdin.on("data", onData);
302582
- });
302583
- process.stdout.write(`
302584
- ${c3.cyan("Add MCP server")}
302585
- `);
302586
- process.stdout.write(` ${c3.dim("Tip: drag-drop a markdown file with an MCP config block onto the terminal for auto-intake.")}
302587
-
302588
- `);
302589
- const name10 = await ask2(` ${c3.cyan("Server name:")} `);
302590
- if (!name10 || name10.trim().length === 0) {
302591
- renderInfo("Cancelled.");
302592
- return;
302593
- }
302594
- process.stdout.write(` ${c3.cyan('Config JSON (one line, e.g. {"command":"npx","args":["-y","@modelcontextprotocol/server-memory"]}):')}
302595
- `);
302596
- const cfgStr = await ask2(" > ");
302597
- let parsed;
302598
- try {
302599
- parsed = JSON.parse(cfgStr);
302600
- } catch (e2) {
302601
- renderError(`Invalid JSON: ${e2 instanceof Error ? e2.message : String(e2)}`);
302602
- return;
302603
- }
302604
- if (!parsed || typeof parsed.command !== "string" && typeof parsed.url !== "string") {
302605
- renderError("Config must have either 'command' (stdio) or 'url' (http)");
302606
- return;
302607
- }
302608
- const manager = ctx3.getMcpManager?.();
302609
- if (!manager) {
302610
- renderError("MCP manager not available");
302611
- return;
302612
- }
302613
- try {
302614
- const c22 = await manager.addServer(name10.trim(), parsed);
302615
- await ctx3.refreshMcpTools?.();
302616
- if (c22.status === "connected") {
302617
- renderInfo(`\u2714 ${name10.trim()} added and connected (${c22.tools.length} tools)`);
302618
- } else {
302619
- renderWarning(`\u26A0 ${name10.trim()} saved but not connected: ${c22.error}`);
302620
- }
302621
- } catch (e2) {
302622
- renderError(`Add failed: ${e2 instanceof Error ? e2.message : String(e2)}`);
302623
- }
302624
- await showMcpMainMenu(ctx3);
302625
- }
302626
302627
  async function handleUpdate(subcommand, ctx3) {
302627
302628
  const repoRoot = ctx3.repoRoot;
302628
302629
  if (subcommand === "auto") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.179",
3
+ "version": "0.187.180",
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",