open-agents-ai 0.186.23 → 0.186.25

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 +78 -21
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -37389,6 +37389,8 @@ var init_expose = __esm({
37389
37389
  /** Debounce tracking for tunnel restarts */
37390
37390
  _lastRestartAttempt = 0;
37391
37391
  _consecutiveFailures = 0;
37392
+ /** Cloudflare 429 cooldown — no tunnel starts before this timestamp */
37393
+ _cfRateLimitUntil = 0;
37392
37394
  // ── Sponsor rate limiting ──
37393
37395
  /** Per-IP sliding window: IP → array of request timestamps */
37394
37396
  _rateLimitWindows = /* @__PURE__ */ new Map();
@@ -37502,6 +37504,10 @@ var init_expose = __esm({
37502
37504
  let lastStartErr;
37503
37505
  this._consecutiveFailures = 0;
37504
37506
  for (let attempt = 0; attempt < 3; attempt++) {
37507
+ if (this._cfRateLimitUntil > Date.now()) {
37508
+ lastStartErr = new Error("Cloudflare rate limited \u2014 waiting 60s cooldown");
37509
+ break;
37510
+ }
37505
37511
  try {
37506
37512
  this._lastRestartAttempt = Date.now();
37507
37513
  this._tunnelUrl = await this.startCloudflared(port);
@@ -37510,6 +37516,8 @@ var init_expose = __esm({
37510
37516
  } catch (err) {
37511
37517
  lastStartErr = err instanceof Error ? err : new Error(String(err));
37512
37518
  this._consecutiveFailures++;
37519
+ if (this._cfRateLimitUntil > Date.now())
37520
+ break;
37513
37521
  if (attempt < 2) {
37514
37522
  const delaySec = Math.min(15 * Math.pow(2, attempt), 60);
37515
37523
  this.options.onInfo?.(`Tunnel start failed \u2014 retrying in ${delaySec}s (attempt ${attempt + 2}/3)...`);
@@ -38022,6 +38030,7 @@ var init_expose = __esm({
38022
38030
  if (hint.includes("address already in use")) {
38023
38031
  reject(new Error(`Port ${port} already in use. Another expose gateway may be running. Try: /expose stop first.`));
38024
38032
  } else if (hint.includes("429") || hint.includes("Too Many")) {
38033
+ this._cfRateLimitUntil = Date.now() + 6e4;
38025
38034
  reject(new Error(`Cloudflare rate limited (429). Wait 60s or use /expose libp2p instead.`));
38026
38035
  } else {
38027
38036
  reject(new Error(`Cloudflared exited with code ${code}. ${hint ? "Output: " + hint : ""}`));
@@ -38043,6 +38052,13 @@ var init_expose = __esm({
38043
38052
  async debouncedRestart() {
38044
38053
  if (!this.server || this._proxyPort === 0)
38045
38054
  return;
38055
+ if (this._cfRateLimitUntil > Date.now()) {
38056
+ const waitSec2 = Math.ceil((this._cfRateLimitUntil - Date.now()) / 1e3);
38057
+ this._stats.status = "standby";
38058
+ this.emitStats();
38059
+ this.options.onInfo?.(`Cloudflare rate limit active \u2014 tunnel restart paused for ${waitSec2}s. Use /expose tunnel to retry manually.`);
38060
+ return;
38061
+ }
38046
38062
  this._consecutiveFailures++;
38047
38063
  if (this._consecutiveFailures >= 3) {
38048
38064
  this._stats.status = "standby";
@@ -51417,22 +51433,24 @@ Clone a new voice: /voice clone <wav-file> [name]`);
51417
51433
  const enabledEps = config.endpoints.filter((e) => e.enabled);
51418
51434
  const allModels = enabledEps.flatMap((e) => e.models || []);
51419
51435
  const sponsorUrl = tunnelGw?.tunnelUrl || "";
51420
- const sponsorPeerId = tunnelGw?.peerId || "";
51421
- if (!sponsorUrl && !sponsorPeerId) {
51422
- let daemonPeerId = "";
51436
+ let sponsorPeerId = tunnelGw?.peerId || "";
51437
+ if (!sponsorPeerId) {
51423
51438
  try {
51424
51439
  const nexus = new NexusTool(projectDir);
51425
51440
  const st = String(await nexus.execute({ action: "status" }) ?? "");
51426
51441
  const pm = st.match(/Peer ID:\s*(12D3KooW\S+)/i);
51427
51442
  if (pm)
51428
- daemonPeerId = pm[1];
51443
+ sponsorPeerId = pm[1];
51429
51444
  } catch {
51430
51445
  }
51431
- if (!daemonPeerId) {
51432
- renderWarning("No tunnel URL or P2P peerId available \u2014 sponsor not registered.");
51433
- return false;
51434
- }
51435
- renderInfo(`Registering as P2P-only sponsor (peerId: ${daemonPeerId.slice(0, 20)}...)`);
51446
+ }
51447
+ if (!sponsorUrl && !sponsorPeerId) {
51448
+ renderWarning("No tunnel and no nexus connection \u2014 sponsor not registered.");
51449
+ renderInfo("Connect nexus first (/nexus) or start a tunnel (/expose tunnel).");
51450
+ return false;
51451
+ }
51452
+ if (!sponsorUrl) {
51453
+ renderInfo(`P2P-only sponsor (peerId: ${sponsorPeerId.slice(0, 20)}...)`);
51436
51454
  }
51437
51455
  let sponsorName = (config.header.message || "").replace(/^\/+/, "").trim();
51438
51456
  if (!sponsorName || sponsorName.length < 2) {
@@ -51446,18 +51464,8 @@ Clone a new voice: /voice clone <wav-file> [name]`);
51446
51464
  if (!sponsorName)
51447
51465
  sponsorName = "OA Sponsor";
51448
51466
  }
51449
- let stablePeerId = `oa-${Date.now()}`;
51450
- let libp2pPeerId = "";
51451
- try {
51452
- const nexus = new NexusTool(projectDir);
51453
- const statusCheck = String(await nexus.execute({ action: "status" }) ?? "");
51454
- const peerMatch = statusCheck.match(/peerId:\s*(12D3KooW\S+)/i);
51455
- if (peerMatch) {
51456
- stablePeerId = peerMatch[1];
51457
- libp2pPeerId = peerMatch[1];
51458
- }
51459
- } catch {
51460
- }
51467
+ const stablePeerId = sponsorPeerId || `oa-${Date.now()}`;
51468
+ const libp2pPeerId = sponsorPeerId;
51461
51469
  const sponsorPayload = {
51462
51470
  peerId: stablePeerId,
51463
51471
  libp2pPeerId,
@@ -54309,6 +54317,55 @@ async function handleUpdate(subcommand, ctx) {
54309
54317
  installOverlay.dismiss();
54310
54318
  return;
54311
54319
  }
54320
+ if (primaryUpdated || depsUpdated) {
54321
+ installOverlay.setStatus("Cleaning stale daemon scripts...");
54322
+ try {
54323
+ const { existsSync: _fe, unlinkSync: _ul, readdirSync: _rd } = await import("node:fs");
54324
+ const { join: _pj } = await import("node:path");
54325
+ const { homedir: _hd } = await import("node:os");
54326
+ const daemonPaths = [
54327
+ _pj(_hd(), ".open-agents", ".oa", "nexus", "nexus-daemon.mjs"),
54328
+ _pj(process.cwd(), ".oa", "nexus", "nexus-daemon.mjs")
54329
+ ];
54330
+ let cleaned = 0;
54331
+ for (const dp of daemonPaths) {
54332
+ if (_fe(dp)) {
54333
+ try {
54334
+ _ul(dp);
54335
+ cleaned++;
54336
+ } catch {
54337
+ }
54338
+ }
54339
+ }
54340
+ try {
54341
+ const pidPaths = [
54342
+ _pj(_hd(), ".open-agents", ".oa", "nexus", "daemon.pid"),
54343
+ _pj(process.cwd(), ".oa", "nexus", "daemon.pid")
54344
+ ];
54345
+ for (const pp of pidPaths) {
54346
+ if (_fe(pp)) {
54347
+ const { readFileSync: _rf } = await import("node:fs");
54348
+ const pid = parseInt(_rf(pp, "utf8").trim(), 10);
54349
+ if (pid > 0) {
54350
+ try {
54351
+ process.kill(pid, "SIGTERM");
54352
+ } catch {
54353
+ }
54354
+ try {
54355
+ _ul(pp);
54356
+ } catch {
54357
+ }
54358
+ cleaned++;
54359
+ }
54360
+ }
54361
+ }
54362
+ } catch {
54363
+ }
54364
+ if (cleaned > 0)
54365
+ installOverlay.setStatus(`Cleaned ${cleaned} stale daemon artifact(s)`);
54366
+ } catch {
54367
+ }
54368
+ }
54312
54369
  if (doRebuild) {
54313
54370
  installOverlay.setStatus("Rebuilding native modules...");
54314
54371
  await new Promise((resolve36) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.186.23",
3
+ "version": "0.186.25",
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",