open-agents-ai 0.103.16 → 0.103.17

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 +372 -35
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6598,8 +6598,8 @@ async function loadTranscribeCli() {
6598
6598
  const nvmBase = join16(homedir6(), ".nvm", "versions", "node");
6599
6599
  if (existsSync13(nvmBase)) {
6600
6600
  try {
6601
- const { readdirSync: readdirSync15 } = await import("node:fs");
6602
- for (const ver of readdirSync15(nvmBase)) {
6601
+ const { readdirSync: readdirSync16 } = await import("node:fs");
6602
+ for (const ver of readdirSync16(nvmBase)) {
6603
6603
  const tcPath = join16(nvmBase, ver, "lib", "node_modules", "transcribe-cli");
6604
6604
  if (existsSync13(join16(tcPath, "dist", "index.js"))) {
6605
6605
  const { createRequire: createRequire4 } = await import("node:module");
@@ -13062,7 +13062,7 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
13062
13062
  result = await this.sendDaemonCmd("room_members", args);
13063
13063
  break;
13064
13064
  case "expose":
13065
- result = await this.sendDaemonCmd("expose", args, 3e4);
13065
+ result = await this.sendDaemonCmd("expose", args, 6e4);
13066
13066
  break;
13067
13067
  case "pricing_menu":
13068
13068
  result = await this.sendDaemonCmd("pricing_menu", args);
@@ -20590,8 +20590,8 @@ var init_listen = __esm({
20590
20590
  const nvmBase = join34(homedir8(), ".nvm", "versions", "node");
20591
20591
  if (existsSync25(nvmBase)) {
20592
20592
  try {
20593
- const { readdirSync: readdirSync15 } = await import("node:fs");
20594
- for (const ver of readdirSync15(nvmBase)) {
20593
+ const { readdirSync: readdirSync16 } = await import("node:fs");
20594
+ for (const ver of readdirSync16(nvmBase)) {
20595
20595
  const tcPath = join34(nvmBase, ver, "lib", "node_modules", "transcribe-cli");
20596
20596
  if (existsSync25(join34(tcPath, "dist", "index.js"))) {
20597
20597
  const { createRequire: createRequire4 } = await import("node:module");
@@ -26203,7 +26203,7 @@ import { EventEmitter as EventEmitter3 } from "node:events";
26203
26203
  import { randomBytes as randomBytes7 } from "node:crypto";
26204
26204
  import { URL as URL2 } from "node:url";
26205
26205
  import { loadavg, cpus, totalmem, freemem } from "node:os";
26206
- import { existsSync as existsSync26, readFileSync as readFileSync18, writeFileSync as writeFileSync8, unlinkSync as unlinkSync3, mkdirSync as mkdirSync8 } from "node:fs";
26206
+ import { existsSync as existsSync26, readFileSync as readFileSync18, writeFileSync as writeFileSync8, unlinkSync as unlinkSync3, mkdirSync as mkdirSync8, readdirSync as readdirSync7 } from "node:fs";
26207
26207
  import { join as join35 } from "node:path";
26208
26208
  function cleanForwardHeaders(raw, targetHost) {
26209
26209
  const out = {};
@@ -26313,7 +26313,7 @@ async function collectSystemMetricsAsync() {
26313
26313
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
26314
26314
  };
26315
26315
  }
26316
- var HOP_BY_HOP_HEADERS, CF_HEADERS_PREFIX, DEFAULT_TARGETS, STATE_FILE_NAME, ExposeGateway;
26316
+ var HOP_BY_HOP_HEADERS, CF_HEADERS_PREFIX, DEFAULT_TARGETS, STATE_FILE_NAME, ExposeGateway, ExposeP2PGateway;
26317
26317
  var init_expose = __esm({
26318
26318
  "packages/cli/dist/tui/expose.js"() {
26319
26319
  "use strict";
@@ -26951,6 +26951,293 @@ ${this.formatConnectionInfo()}`);
26951
26951
  return lines.join("\n");
26952
26952
  }
26953
26953
  };
26954
+ ExposeP2PGateway = class extends EventEmitter3 {
26955
+ _nexusTool;
26956
+ // NexusTool instance
26957
+ _kind;
26958
+ _targetUrl;
26959
+ _peerId = null;
26960
+ _capabilities = [];
26961
+ _exposedModels = 0;
26962
+ _stateDir;
26963
+ _margin;
26964
+ _pollTimer = null;
26965
+ _stats = {
26966
+ status: "standby",
26967
+ totalRequests: 0,
26968
+ activeConnections: 0,
26969
+ errors: 0,
26970
+ totalTokensIn: 0,
26971
+ totalTokensOut: 0,
26972
+ startedAt: Date.now(),
26973
+ modelUsage: /* @__PURE__ */ new Map(),
26974
+ users: /* @__PURE__ */ new Map()
26975
+ };
26976
+ get peerId() {
26977
+ return this._peerId;
26978
+ }
26979
+ get stats() {
26980
+ return this._stats;
26981
+ }
26982
+ get kind() {
26983
+ return this._kind;
26984
+ }
26985
+ get isActive() {
26986
+ return this._stats.status === "active";
26987
+ }
26988
+ get transport() {
26989
+ return "libp2p";
26990
+ }
26991
+ _onInfo;
26992
+ _onError;
26993
+ constructor(options, nexusTool) {
26994
+ super();
26995
+ this._nexusTool = nexusTool;
26996
+ this._kind = options.kind;
26997
+ this._targetUrl = options.targetUrl ?? DEFAULT_TARGETS[options.kind];
26998
+ this._stateDir = options.stateDir ?? null;
26999
+ this._margin = options.margin ?? 0.5;
27000
+ this._onInfo = options.onInfo;
27001
+ this._onError = options.onError;
27002
+ }
27003
+ async start() {
27004
+ this._onInfo?.("Connecting to nexus P2P network...");
27005
+ const connectResult = await this._nexusTool.execute({
27006
+ action: "connect",
27007
+ agent_name: "open-agents-node",
27008
+ agent_type: "general"
27009
+ });
27010
+ if (!connectResult.success) {
27011
+ throw new Error(`Nexus daemon connect failed: ${connectResult.error}`);
27012
+ }
27013
+ await new Promise((r) => setTimeout(r, 2e3));
27014
+ this._onInfo?.("Registering inference capabilities...");
27015
+ let exposeResult = await this._nexusTool.execute({
27016
+ action: "expose",
27017
+ ollama_url: this._targetUrl,
27018
+ margin: String(this._margin)
27019
+ });
27020
+ if (!exposeResult.success && exposeResult.error?.includes("not running")) {
27021
+ await new Promise((r) => setTimeout(r, 3e3));
27022
+ exposeResult = await this._nexusTool.execute({
27023
+ action: "expose",
27024
+ ollama_url: this._targetUrl,
27025
+ margin: String(this._margin)
27026
+ });
27027
+ }
27028
+ if (!exposeResult.success) {
27029
+ throw new Error(`Expose failed: ${exposeResult.error}`);
27030
+ }
27031
+ const nexusDir = this._nexusTool.getNexusDir();
27032
+ const statusPath = join35(nexusDir, "status.json");
27033
+ for (let i = 0; i < 60; i++) {
27034
+ try {
27035
+ const raw = readFileSync18(statusPath, "utf8");
27036
+ if (raw.length > 10) {
27037
+ const status = JSON.parse(raw);
27038
+ if (status.connected && status.peerId) {
27039
+ this._peerId = status.peerId;
27040
+ this._capabilities = status.capabilities || [];
27041
+ this._exposedModels = status.exposedModels || 0;
27042
+ break;
27043
+ }
27044
+ }
27045
+ } catch {
27046
+ }
27047
+ await new Promise((r) => setTimeout(r, 500));
27048
+ }
27049
+ this._stats.status = "active";
27050
+ this._stats.startedAt = Date.now();
27051
+ this.emitStats();
27052
+ this.startMeteringPoll(nexusDir);
27053
+ return this._peerId ?? "connected (peer ID pending)";
27054
+ }
27055
+ async stop() {
27056
+ this.stopMeteringPoll();
27057
+ this._stats.status = "standby";
27058
+ this._stats.activeConnections = 0;
27059
+ this.emitStats();
27060
+ }
27061
+ /** Poll the daemon's metering.jsonl and status.json for stats */
27062
+ startMeteringPoll(nexusDir) {
27063
+ this.stopMeteringPoll();
27064
+ let lastMeteringSize = 0;
27065
+ this._pollTimer = setInterval(() => {
27066
+ try {
27067
+ const statusPath = join35(nexusDir, "status.json");
27068
+ if (existsSync26(statusPath)) {
27069
+ const status = JSON.parse(readFileSync18(statusPath, "utf8"));
27070
+ if (status.peerId && !this._peerId) {
27071
+ this._peerId = status.peerId;
27072
+ }
27073
+ this._capabilities = status.capabilities || [];
27074
+ }
27075
+ } catch {
27076
+ }
27077
+ try {
27078
+ const invocDir = join35(nexusDir, "invocations");
27079
+ if (existsSync26(invocDir)) {
27080
+ const files = readdirSync7(invocDir);
27081
+ const invocCount = files.filter((f) => f.endsWith(".json")).length;
27082
+ if (invocCount > this._stats.totalRequests) {
27083
+ this._stats.totalRequests = invocCount;
27084
+ this.emitStats();
27085
+ }
27086
+ }
27087
+ } catch {
27088
+ }
27089
+ try {
27090
+ const meteringFile = join35(nexusDir, "metering.jsonl");
27091
+ if (existsSync26(meteringFile)) {
27092
+ const content = readFileSync18(meteringFile, "utf8");
27093
+ if (content.length > lastMeteringSize) {
27094
+ const newContent = content.slice(lastMeteringSize);
27095
+ lastMeteringSize = content.length;
27096
+ for (const line of newContent.split("\n")) {
27097
+ if (!line.trim())
27098
+ continue;
27099
+ try {
27100
+ const record = JSON.parse(line);
27101
+ if (record.tokens) {
27102
+ this._stats.totalTokensIn += record.tokens.input || 0;
27103
+ this._stats.totalTokensOut += record.tokens.output || 0;
27104
+ }
27105
+ const peerId = record.from || record.peerId || "unknown";
27106
+ const shortPeer = peerId.length > 16 ? peerId.slice(0, 16) + "..." : peerId;
27107
+ let user = this._stats.users.get(shortPeer);
27108
+ if (!user) {
27109
+ user = {
27110
+ ip: shortPeer,
27111
+ firstSeen: Date.now(),
27112
+ lastSeen: Date.now(),
27113
+ requests: 0,
27114
+ tokensIn: 0,
27115
+ tokensOut: 0,
27116
+ activeRequests: 0,
27117
+ models: /* @__PURE__ */ new Map()
27118
+ };
27119
+ this._stats.users.set(shortPeer, user);
27120
+ }
27121
+ user.requests++;
27122
+ user.lastSeen = Date.now();
27123
+ if (record.tokens) {
27124
+ user.tokensIn += record.tokens.input || 0;
27125
+ user.tokensOut += record.tokens.output || 0;
27126
+ }
27127
+ if (record.model || record.capability) {
27128
+ const modelName = record.model || record.capability.replace("inference:", "") || "unknown";
27129
+ this._stats.modelUsage.set(modelName, (this._stats.modelUsage.get(modelName) ?? 0) + 1);
27130
+ let mm = user.models.get(modelName);
27131
+ if (!mm) {
27132
+ mm = { requests: 0, tokensIn: 0, tokensOut: 0, lastUsed: Date.now() };
27133
+ user.models.set(modelName, mm);
27134
+ }
27135
+ mm.requests++;
27136
+ mm.lastUsed = Date.now();
27137
+ if (record.tokens) {
27138
+ mm.tokensIn += record.tokens.input || 0;
27139
+ mm.tokensOut += record.tokens.output || 0;
27140
+ }
27141
+ }
27142
+ } catch {
27143
+ }
27144
+ }
27145
+ this.emitStats();
27146
+ }
27147
+ }
27148
+ } catch {
27149
+ }
27150
+ }, 5e3);
27151
+ this._pollTimer.unref();
27152
+ }
27153
+ stopMeteringPoll() {
27154
+ if (this._pollTimer) {
27155
+ clearInterval(this._pollTimer);
27156
+ this._pollTimer = null;
27157
+ }
27158
+ }
27159
+ emitStats() {
27160
+ this.emit("stats", {
27161
+ ...this._stats,
27162
+ modelUsage: new Map(this._stats.modelUsage),
27163
+ users: new Map(this._stats.users)
27164
+ });
27165
+ }
27166
+ /** Format connection info for display */
27167
+ formatConnectionInfo() {
27168
+ const lines = [];
27169
+ lines.push(` ${c2.green("\u25CF")} Expose gateway active ${c2.dim("(libp2p)")}`);
27170
+ lines.push(` ${c2.cyan("Backend".padEnd(12))} ${this._kind} (${this._targetUrl})`);
27171
+ lines.push(` ${c2.cyan("Transport".padEnd(12))} ${c2.bold("libp2p")} (DHT + mDNS + NATS relay)`);
27172
+ lines.push(` ${c2.cyan("Peer ID".padEnd(12))} ${c2.bold(this._peerId ?? "connecting...")}`);
27173
+ lines.push(` ${c2.cyan("Models".padEnd(12))} ${this._exposedModels || this._capabilities.filter((cap) => cap.startsWith("inference:")).length} exposed`);
27174
+ lines.push("");
27175
+ lines.push(` ${c2.dim("Remote peers discover your models via nexus network.")}`);
27176
+ lines.push(` ${c2.dim("Consumers use: /nexus invoke_capability inference:<model>")}`);
27177
+ const infCaps = this._capabilities.filter((cap) => cap.startsWith("inference:"));
27178
+ if (infCaps.length > 0) {
27179
+ lines.push("");
27180
+ lines.push(` ${c2.bold("Exposed capabilities:")}`);
27181
+ for (const cap of infCaps) {
27182
+ lines.push(` ${c2.cyan(cap)}`);
27183
+ }
27184
+ }
27185
+ return lines.join("\n");
27186
+ }
27187
+ /** Format usage stats for display */
27188
+ formatStats() {
27189
+ const s = this._stats;
27190
+ const lines = [];
27191
+ const uptimeSec = Math.floor((Date.now() - s.startedAt) / 1e3);
27192
+ const uptimeStr = uptimeSec < 60 ? `${uptimeSec}s` : uptimeSec < 3600 ? `${Math.floor(uptimeSec / 60)}m ${uptimeSec % 60}s` : `${Math.floor(uptimeSec / 3600)}h ${Math.floor(uptimeSec % 3600 / 60)}m`;
27193
+ lines.push(` ${c2.bold("Expose Gateway Stats")} ${c2.dim("(libp2p)")}`);
27194
+ lines.push(` ${c2.cyan("Status".padEnd(18))} ${s.status === "active" ? c2.green("active") : s.status === "error" ? c2.red("error") : c2.yellow("standby")}`);
27195
+ lines.push(` ${c2.cyan("Transport".padEnd(18))} libp2p`);
27196
+ lines.push(` ${c2.cyan("Peer ID".padEnd(18))} ${this._peerId ?? "n/a"}`);
27197
+ lines.push(` ${c2.cyan("Uptime".padEnd(18))} ${uptimeStr}`);
27198
+ lines.push(` ${c2.cyan("Total requests".padEnd(18))} ${s.totalRequests}`);
27199
+ lines.push(` ${c2.cyan("Active conns".padEnd(18))} ${s.activeConnections}`);
27200
+ lines.push(` ${c2.cyan("Errors".padEnd(18))} ${s.errors}`);
27201
+ lines.push(` ${c2.cyan("Tokens in".padEnd(18))} ${fmtTokens(s.totalTokensIn)}`);
27202
+ lines.push(` ${c2.cyan("Tokens out".padEnd(18))} ${fmtTokens(s.totalTokensOut)}`);
27203
+ if (s.modelUsage.size > 0) {
27204
+ lines.push("");
27205
+ lines.push(` ${c2.bold("Models")}`);
27206
+ for (const [model, count] of s.modelUsage) {
27207
+ let mIn = 0, mOut = 0;
27208
+ for (const user of s.users.values()) {
27209
+ const mm = user.models.get(model);
27210
+ if (mm) {
27211
+ mIn += mm.tokensIn;
27212
+ mOut += mm.tokensOut;
27213
+ }
27214
+ }
27215
+ lines.push(` ${c2.cyan(model.padEnd(30))} ${count} reqs ${c2.dim(`in:${fmtTokens(mIn)} out:${fmtTokens(mOut)}`)}`);
27216
+ }
27217
+ }
27218
+ if (s.users.size > 0) {
27219
+ lines.push("");
27220
+ lines.push(` ${c2.bold("Active Peers")} (${s.users.size})`);
27221
+ for (const user of s.users.values()) {
27222
+ const ageSec = Math.floor((Date.now() - user.firstSeen) / 1e3);
27223
+ const ageStr = ageSec < 60 ? `${ageSec}s` : ageSec < 3600 ? `${Math.floor(ageSec / 60)}m` : `${Math.floor(ageSec / 3600)}h ${Math.floor(ageSec % 3600 / 60)}m`;
27224
+ const lastSec = Math.floor((Date.now() - user.lastSeen) / 1e3);
27225
+ const lastStr = lastSec < 5 ? "now" : lastSec < 60 ? `${lastSec}s ago` : `${Math.floor(lastSec / 60)}m ago`;
27226
+ lines.push(` ${c2.bold(user.ip)}`);
27227
+ lines.push(` ${c2.dim("Session:")} ${ageStr} ${c2.dim("Last seen:")} ${lastStr} ${c2.dim("Requests:")} ${user.requests}`);
27228
+ lines.push(` ${c2.dim("Tokens:")} in:${fmtTokens(user.tokensIn)} out:${fmtTokens(user.tokensOut)}`);
27229
+ if (user.models.size > 0) {
27230
+ const modelParts = [];
27231
+ for (const [m, mm] of user.models) {
27232
+ modelParts.push(`${m} (${mm.requests} reqs, ${fmtTokens(mm.tokensIn + mm.tokensOut)} tokens)`);
27233
+ }
27234
+ lines.push(` ${c2.dim("Models:")} ${modelParts.join(", ")}`);
27235
+ }
27236
+ }
27237
+ }
27238
+ return lines.join("\n");
27239
+ }
27240
+ };
26954
27241
  }
26955
27242
  });
26956
27243
 
@@ -28564,7 +28851,7 @@ var init_dist6 = __esm({
28564
28851
  });
28565
28852
 
28566
28853
  // packages/cli/dist/tui/oa-directory.js
28567
- import { existsSync as existsSync29, mkdirSync as mkdirSync10, readFileSync as readFileSync21, writeFileSync as writeFileSync10, readdirSync as readdirSync7, statSync as statSync9, unlinkSync as unlinkSync4 } from "node:fs";
28854
+ import { existsSync as existsSync29, mkdirSync as mkdirSync10, readFileSync as readFileSync21, writeFileSync as writeFileSync10, readdirSync as readdirSync8, statSync as statSync9, unlinkSync as unlinkSync4 } from "node:fs";
28568
28855
  import { join as join39, relative as relative2, basename as basename9, extname as extname8 } from "node:path";
28569
28856
  import { homedir as homedir9 } from "node:os";
28570
28857
  function initOaDirectory(repoRoot) {
@@ -28756,7 +29043,7 @@ function loadRecentSessions(repoRoot, limit = 5) {
28756
29043
  if (!existsSync29(historyDir))
28757
29044
  return [];
28758
29045
  try {
28759
- const files = readdirSync7(historyDir).filter((f) => f.endsWith(".json") && f !== "pending-task.json").map((f) => {
29046
+ const files = readdirSync8(historyDir).filter((f) => f.endsWith(".json") && f !== "pending-task.json").map((f) => {
28760
29047
  const stat5 = statSync9(join39(historyDir, f));
28761
29048
  return { file: f, mtime: stat5.mtimeMs };
28762
29049
  }).sort((a, b) => b.mtime - a.mtime).slice(0, limit);
@@ -28909,7 +29196,7 @@ function buildDirTree(root, maxDepth, prefix = "", depth = 0) {
28909
29196
  return "";
28910
29197
  let result = "";
28911
29198
  try {
28912
- const entries = readdirSync7(root, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") || e.name === ".github").filter((e) => !SKIP_DIRS.has(e.name)).sort((a, b) => {
29199
+ const entries = readdirSync8(root, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") || e.name === ".github").filter((e) => !SKIP_DIRS.has(e.name)).sort((a, b) => {
28913
29200
  if (a.isDirectory() && !b.isDirectory())
28914
29201
  return -1;
28915
29202
  if (!a.isDirectory() && b.isDirectory())
@@ -28924,7 +29211,7 @@ function buildDirTree(root, maxDepth, prefix = "", depth = 0) {
28924
29211
  if (entry.isDirectory()) {
28925
29212
  let fileCount = 0;
28926
29213
  try {
28927
- fileCount = readdirSync7(join39(root, entry.name)).filter((f) => !f.startsWith(".")).length;
29214
+ fileCount = readdirSync8(join39(root, entry.name)).filter((f) => !f.startsWith(".")).length;
28928
29215
  } catch {
28929
29216
  }
28930
29217
  result += `${prefix}${connector}${entry.name}/ (${fileCount})
@@ -31540,15 +31827,17 @@ async function handleSlashCommand(input, ctx) {
31540
31827
  if (info && ctx.isExposeActive?.()) {
31541
31828
  process.stdout.write("\n" + info.stats + "\n\n");
31542
31829
  } else {
31543
- renderInfo("Usage: /expose <backend> [--key <auth-key>]");
31544
- renderInfo(" /expose ollama Expose local Ollama (11434)");
31830
+ renderInfo("Usage: /expose <backend> [options]");
31831
+ renderInfo(" /expose ollama Expose local Ollama via libp2p (default)");
31832
+ renderInfo(" /expose ollama --tunnel Expose via cloudflared tunnel");
31833
+ renderInfo(" /expose ollama --libp2p Expose via libp2p (explicit)");
31545
31834
  renderInfo(" /expose vllm Expose local vLLM (8000)");
31546
31835
  renderInfo(" /expose llvm Expose local LLVM (8080)");
31547
31836
  renderInfo(" /expose host:port Expose custom address");
31548
31837
  renderInfo(" /expose stop Stop the gateway");
31549
31838
  renderInfo(" /expose status Show usage stats");
31550
- renderInfo(" --key <key> Use specific auth key");
31551
- renderInfo(" --key Auto-generate auth key");
31839
+ renderInfo(" --key <key> Use specific auth key (tunnel mode)");
31840
+ renderInfo(" --key Auto-generate auth key (tunnel mode)");
31552
31841
  }
31553
31842
  return "handled";
31554
31843
  }
@@ -31558,12 +31847,18 @@ async function handleSlashCommand(input, ctx) {
31558
31847
  const exposeParts = arg.split(/\s+/);
31559
31848
  const kindOrUrl = exposeParts[0];
31560
31849
  let exposeAuthKey;
31850
+ let exposeTransport;
31561
31851
  const keyIdx = exposeParts.indexOf("--key");
31562
31852
  if (keyIdx !== -1) {
31563
31853
  exposeAuthKey = exposeParts[keyIdx + 1] ?? "";
31564
31854
  }
31855
+ if (exposeParts.includes("--tunnel")) {
31856
+ exposeTransport = "tunnel";
31857
+ } else if (exposeParts.includes("--libp2p")) {
31858
+ exposeTransport = "libp2p";
31859
+ }
31565
31860
  try {
31566
- const tunnelUrl = await ctx.exposeStart(kindOrUrl, exposeAuthKey);
31861
+ const tunnelUrl = await ctx.exposeStart(kindOrUrl, exposeAuthKey, exposeTransport);
31567
31862
  if (tunnelUrl) {
31568
31863
  const info = ctx.getExposeInfo?.();
31569
31864
  if (info) {
@@ -32761,7 +33056,7 @@ var init_commands = __esm({
32761
33056
  });
32762
33057
 
32763
33058
  // packages/cli/dist/tui/project-context.js
32764
- import { existsSync as existsSync31, readFileSync as readFileSync22, readdirSync as readdirSync8 } from "node:fs";
33059
+ import { existsSync as existsSync31, readFileSync as readFileSync22, readdirSync as readdirSync9 } from "node:fs";
32765
33060
  import { join as join41, basename as basename10 } from "node:path";
32766
33061
  import { execSync as execSync26 } from "node:child_process";
32767
33062
  import { homedir as homedir11, platform as platform2, release } from "node:os";
@@ -32862,7 +33157,7 @@ function loadMemoryDir(memDir, scope) {
32862
33157
  return "";
32863
33158
  const lines = [];
32864
33159
  try {
32865
- const files = readdirSync8(memDir).filter((f) => f.endsWith(".json"));
33160
+ const files = readdirSync9(memDir).filter((f) => f.endsWith(".json"));
32866
33161
  for (const file of files.slice(0, 10)) {
32867
33162
  try {
32868
33163
  const raw = readFileSync22(join41(memDir, file), "utf-8");
@@ -33888,7 +34183,7 @@ var init_carousel = __esm({
33888
34183
  });
33889
34184
 
33890
34185
  // packages/cli/dist/tui/carousel-descriptors.js
33891
- import { existsSync as existsSync32, readFileSync as readFileSync23, writeFileSync as writeFileSync12, mkdirSync as mkdirSync12, readdirSync as readdirSync9 } from "node:fs";
34186
+ import { existsSync as existsSync32, readFileSync as readFileSync23, writeFileSync as writeFileSync12, mkdirSync as mkdirSync12, readdirSync as readdirSync10 } from "node:fs";
33892
34187
  import { join as join42, basename as basename11 } from "node:path";
33893
34188
  function loadToolProfile(repoRoot) {
33894
34189
  const filePath = join42(repoRoot, OA_DIR, "context", TOOL_PROFILE_FILE);
@@ -34101,7 +34396,7 @@ function extractFromMemory(repoRoot, tags) {
34101
34396
  try {
34102
34397
  if (!existsSync32(memoryDir))
34103
34398
  return;
34104
- const files = readdirSync9(memoryDir).filter((f) => f.endsWith(".json"));
34399
+ const files = readdirSync10(memoryDir).filter((f) => f.endsWith(".json"));
34105
34400
  for (const file of files) {
34106
34401
  const topic = file.replace(/\.json$/, "").replace(/[-_]/g, " ");
34107
34402
  tags.push(topic);
@@ -36662,7 +36957,7 @@ var init_promptLoader3 = __esm({
36662
36957
  });
36663
36958
 
36664
36959
  // packages/cli/dist/tui/dream-engine.js
36665
- import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync14, readFileSync as readFileSync26, existsSync as existsSync35, cpSync, rmSync, readdirSync as readdirSync10 } from "node:fs";
36960
+ import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync14, readFileSync as readFileSync26, existsSync as existsSync35, cpSync, rmSync, readdirSync as readdirSync11 } from "node:fs";
36666
36961
  import { join as join46, basename as basename12 } from "node:path";
36667
36962
  import { execSync as execSync28 } from "node:child_process";
36668
36963
  function loadAutoresearchMemory(repoRoot) {
@@ -37868,7 +38163,7 @@ Each proposal includes implementation entrypoints and estimated effort.
37868
38163
  /** Update the master proposal index */
37869
38164
  updateProposalIndex() {
37870
38165
  try {
37871
- const files = readdirSync10(this.dreamsDir).filter((f) => f.endsWith(".md") && f !== "PROPOSAL-INDEX.md" && f !== "dream-state.json").sort();
38166
+ const files = readdirSync11(this.dreamsDir).filter((f) => f.endsWith(".md") && f !== "PROPOSAL-INDEX.md" && f !== "dream-state.json").sort();
37872
38167
  const index = `# Dream Proposals Index
37873
38168
 
37874
38169
  **Last updated**: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
@@ -38270,7 +38565,7 @@ var init_bless_engine = __esm({
38270
38565
  });
38271
38566
 
38272
38567
  // packages/cli/dist/tui/dmn-engine.js
38273
- import { existsSync as existsSync36, readFileSync as readFileSync27, writeFileSync as writeFileSync15, mkdirSync as mkdirSync16, readdirSync as readdirSync11, unlinkSync as unlinkSync6 } from "node:fs";
38568
+ import { existsSync as existsSync36, readFileSync as readFileSync27, writeFileSync as writeFileSync15, mkdirSync as mkdirSync16, readdirSync as readdirSync12, unlinkSync as unlinkSync6 } from "node:fs";
38274
38569
  import { join as join47, basename as basename13 } from "node:path";
38275
38570
  function buildDMNGatherPrompt(recentTaskSummaries, dueReminders, attentionItems, memoryTopics, capabilities, competence, reflectionBuffer) {
38276
38571
  const competenceReport = competence.length > 0 ? competence.map((c3) => {
@@ -38982,7 +39277,7 @@ OUTPUT: Call task_complete with JSON:
38982
39277
  if (!existsSync36(dir))
38983
39278
  continue;
38984
39279
  try {
38985
- const files = readdirSync11(dir).filter((f) => f.endsWith(".json"));
39280
+ const files = readdirSync12(dir).filter((f) => f.endsWith(".json"));
38986
39281
  for (const f of files) {
38987
39282
  const topic = basename13(f, ".json");
38988
39283
  if (!topics.includes(topic))
@@ -39013,7 +39308,7 @@ OUTPUT: Call task_complete with JSON:
39013
39308
  try {
39014
39309
  const filename = `cycle-${result.cycleNumber}-${Date.now()}.json`;
39015
39310
  writeFileSync15(join47(this.historyDir, filename), JSON.stringify(result, null, 2) + "\n", "utf-8");
39016
- const files = readdirSync11(this.historyDir).filter((f) => f.startsWith("cycle-") && f.endsWith(".json")).sort();
39311
+ const files = readdirSync12(this.historyDir).filter((f) => f.startsWith("cycle-") && f.endsWith(".json")).sort();
39017
39312
  if (files.length > 50) {
39018
39313
  for (const old of files.slice(0, files.length - 50)) {
39019
39314
  try {
@@ -39030,7 +39325,7 @@ OUTPUT: Call task_complete with JSON:
39030
39325
  });
39031
39326
 
39032
39327
  // packages/cli/dist/tui/snr-engine.js
39033
- import { existsSync as existsSync37, readdirSync as readdirSync12, readFileSync as readFileSync28 } from "node:fs";
39328
+ import { existsSync as existsSync37, readdirSync as readdirSync13, readFileSync as readFileSync28 } from "node:fs";
39034
39329
  import { join as join48, basename as basename14 } from "node:path";
39035
39330
  function computeDPrime(signalScores, noiseScores) {
39036
39331
  if (signalScores.length === 0 || noiseScores.length === 0)
@@ -39278,7 +39573,7 @@ Call task_complete with the JSON array when done.`, onEvent)
39278
39573
  if (!existsSync37(dir))
39279
39574
  continue;
39280
39575
  try {
39281
- const files = readdirSync12(dir).filter((f) => f.endsWith(".json"));
39576
+ const files = readdirSync13(dir).filter((f) => f.endsWith(".json"));
39282
39577
  for (const f of files) {
39283
39578
  const topic = basename14(f, ".json");
39284
39579
  if (topics.length > 0 && !topics.includes(topic))
@@ -39851,7 +40146,7 @@ var init_tool_policy = __esm({
39851
40146
  });
39852
40147
 
39853
40148
  // packages/cli/dist/tui/telegram-bridge.js
39854
- import { mkdirSync as mkdirSync17, existsSync as existsSync38, unlinkSync as unlinkSync7, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
40149
+ import { mkdirSync as mkdirSync17, existsSync as existsSync38, unlinkSync as unlinkSync7, readdirSync as readdirSync14, statSync as statSync10 } from "node:fs";
39855
40150
  import { join as join49, resolve as resolve27 } from "node:path";
39856
40151
  import { writeFile as writeFileAsync } from "node:fs/promises";
39857
40152
  function convertMarkdownToTelegramHTML(md) {
@@ -42453,7 +42748,7 @@ import { cwd } from "node:process";
42453
42748
  import { resolve as resolve28, join as join50, dirname as dirname17, extname as extname9 } from "node:path";
42454
42749
  import { createRequire as createRequire2 } from "node:module";
42455
42750
  import { fileURLToPath as fileURLToPath12 } from "node:url";
42456
- import { readFileSync as readFileSync29, writeFileSync as writeFileSync16, appendFileSync as appendFileSync3, rmSync as rmSync2, readdirSync as readdirSync14, mkdirSync as mkdirSync18 } from "node:fs";
42751
+ import { readFileSync as readFileSync29, writeFileSync as writeFileSync16, appendFileSync as appendFileSync3, rmSync as rmSync2, readdirSync as readdirSync15, mkdirSync as mkdirSync18 } from "node:fs";
42457
42752
  import { existsSync as existsSync39 } from "node:fs";
42458
42753
  import { homedir as homedir13 } from "node:os";
42459
42754
  function formatTimeAgo(date) {
@@ -42692,7 +42987,7 @@ function gatherMemorySnippets(root) {
42692
42987
  if (!existsSync39(dir))
42693
42988
  continue;
42694
42989
  try {
42695
- for (const f of readdirSync14(dir).filter((f2) => f2.endsWith(".json"))) {
42990
+ for (const f of readdirSync15(dir).filter((f2) => f2.endsWith(".json"))) {
42696
42991
  const data = JSON.parse(readFileSync29(join50(dir, f), "utf-8"));
42697
42992
  for (const val of Object.values(data)) {
42698
42993
  const v = typeof val === "object" && val !== null && "value" in val ? String(val.value) : String(val);
@@ -44419,7 +44714,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
44419
44714
  getCallUrl() {
44420
44715
  return voiceSession?.tunnelUrl ?? null;
44421
44716
  },
44422
- async exposeStart(kindOrUrl, authKey) {
44717
+ async exposeStart(kindOrUrl, authKey, transport) {
44423
44718
  const knownKinds = ["ollama", "vllm", "llvm"];
44424
44719
  let kind;
44425
44720
  let targetUrl;
@@ -44429,8 +44724,42 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
44429
44724
  kind = "custom";
44430
44725
  targetUrl = kindOrUrl.includes("://") ? kindOrUrl : `http://${kindOrUrl}`;
44431
44726
  }
44432
- exposeGateway = new ExposeGateway({ kind, targetUrl, authKey, stateDir: join50(repoRoot, ".oa") });
44433
- exposeGateway.on("stats", (stats) => {
44727
+ const effectiveTransport = transport ?? "libp2p";
44728
+ if (effectiveTransport === "libp2p") {
44729
+ const nexusTool = new NexusTool(repoRoot);
44730
+ const p2pGateway = new ExposeP2PGateway({
44731
+ kind,
44732
+ targetUrl,
44733
+ stateDir: join50(repoRoot, ".oa")
44734
+ }, nexusTool);
44735
+ p2pGateway.on("stats", (stats) => {
44736
+ statusBar.setExposeStatus({
44737
+ status: stats.status,
44738
+ totalRequests: stats.totalRequests,
44739
+ activeConnections: stats.activeConnections,
44740
+ modelUsage: stats.modelUsage
44741
+ });
44742
+ });
44743
+ try {
44744
+ const peerId = await p2pGateway.start();
44745
+ exposeGateway = p2pGateway;
44746
+ writeContent(() => {
44747
+ process.stdout.write("\n" + p2pGateway.formatConnectionInfo() + "\n\n");
44748
+ });
44749
+ return peerId;
44750
+ } catch (err) {
44751
+ if (!transport) {
44752
+ writeContent(() => {
44753
+ renderWarning(`libp2p expose failed: ${err instanceof Error ? err.message : String(err)}`);
44754
+ renderInfo("Falling back to cloudflared tunnel...");
44755
+ });
44756
+ } else {
44757
+ throw err;
44758
+ }
44759
+ }
44760
+ }
44761
+ const tunnelGateway = new ExposeGateway({ kind, targetUrl, authKey, stateDir: join50(repoRoot, ".oa") });
44762
+ tunnelGateway.on("stats", (stats) => {
44434
44763
  statusBar.setExposeStatus({
44435
44764
  status: stats.status,
44436
44765
  totalRequests: stats.totalRequests,
@@ -44439,9 +44768,10 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
44439
44768
  });
44440
44769
  });
44441
44770
  try {
44442
- const url = await exposeGateway.start();
44771
+ const url = await tunnelGateway.start();
44772
+ exposeGateway = tunnelGateway;
44443
44773
  writeContent(() => {
44444
- process.stdout.write("\n" + exposeGateway.formatConnectionInfo() + "\n\n");
44774
+ process.stdout.write("\n" + tunnelGateway.formatConnectionInfo() + "\n\n");
44445
44775
  });
44446
44776
  return url;
44447
44777
  } catch (err) {
@@ -44462,6 +44792,13 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
44462
44792
  getExposeInfo() {
44463
44793
  if (!exposeGateway)
44464
44794
  return null;
44795
+ if (exposeGateway instanceof ExposeP2PGateway) {
44796
+ return {
44797
+ tunnelUrl: exposeGateway.peerId,
44798
+ authKey: "",
44799
+ stats: exposeGateway.formatStats()
44800
+ };
44801
+ }
44465
44802
  return {
44466
44803
  tunnelUrl: exposeGateway.tunnelUrl,
44467
44804
  authKey: exposeGateway.authKey,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.103.16",
3
+ "version": "0.103.17",
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",