open-agents-ai 0.186.22 → 0.186.24

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 +36 -16
  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,10 +51433,25 @@ 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
- if (!sponsorUrl) {
51421
- renderWarning("No tunnel URL available \u2014 sponsor not registered in directory. Start with cloudflared or libp2p first.");
51436
+ let sponsorPeerId = tunnelGw?.peerId || "";
51437
+ if (!sponsorPeerId) {
51438
+ try {
51439
+ const nexus = new NexusTool(projectDir);
51440
+ const st = String(await nexus.execute({ action: "status" }) ?? "");
51441
+ const pm = st.match(/Peer ID:\s*(12D3KooW\S+)/i);
51442
+ if (pm)
51443
+ sponsorPeerId = pm[1];
51444
+ } catch {
51445
+ }
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).");
51422
51450
  return false;
51423
51451
  }
51452
+ if (!sponsorUrl) {
51453
+ renderInfo(`P2P-only sponsor (peerId: ${sponsorPeerId.slice(0, 20)}...)`);
51454
+ }
51424
51455
  let sponsorName = (config.header.message || "").replace(/^\/+/, "").trim();
51425
51456
  if (!sponsorName || sponsorName.length < 2) {
51426
51457
  try {
@@ -51433,18 +51464,8 @@ Clone a new voice: /voice clone <wav-file> [name]`);
51433
51464
  if (!sponsorName)
51434
51465
  sponsorName = "OA Sponsor";
51435
51466
  }
51436
- let stablePeerId = `oa-${Date.now()}`;
51437
- let libp2pPeerId = "";
51438
- try {
51439
- const nexus = new NexusTool(projectDir);
51440
- const statusCheck = String(await nexus.execute({ action: "status" }) ?? "");
51441
- const peerMatch = statusCheck.match(/peerId:\s*(12D3KooW\S+)/i);
51442
- if (peerMatch) {
51443
- stablePeerId = peerMatch[1];
51444
- libp2pPeerId = peerMatch[1];
51445
- }
51446
- } catch {
51447
- }
51467
+ const stablePeerId = sponsorPeerId || `oa-${Date.now()}`;
51468
+ const libp2pPeerId = sponsorPeerId;
51448
51469
  const sponsorPayload = {
51449
51470
  peerId: stablePeerId,
51450
51471
  libp2pPeerId,
@@ -51460,8 +51481,7 @@ Clone a new voice: /voice clone <wav-file> [name]`);
51460
51481
  message: config.header.message || sponsorName,
51461
51482
  linkUrl: config.header.linkUrl,
51462
51483
  linkText: config.header.linkText,
51463
- status: sponsorUrl ? "active" : "inactive"
51464
- // don't register without a URL
51484
+ status: sponsorUrl || libp2pPeerId ? "active" : "inactive"
51465
51485
  };
51466
51486
  try {
51467
51487
  const kvResp = await fetch("https://openagents.nexus/api/v1/sponsors", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.186.22",
3
+ "version": "0.186.24",
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",