open-agents-ai 0.186.16 → 0.186.18

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 +131 -41
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8895,36 +8895,13 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
8895
8895
  const currentScriptHash = createHash("sha256").update(DAEMON_SCRIPT).digest("hex").slice(0, 16);
8896
8896
  const existingPid = this.getDaemonPid();
8897
8897
  if (existingPid) {
8898
- const daemonPath2 = join14(this.nexusDir, "nexus-daemon.mjs");
8899
- let needsRestart = true;
8900
- if (existsSync11(daemonPath2)) {
8901
- try {
8902
- const onDisk = readFileSync8(daemonPath2, "utf8");
8903
- const diskHash = createHash("sha256").update(onDisk).digest("hex").slice(0, 16);
8904
- if (diskHash === currentScriptHash) {
8905
- needsRestart = false;
8906
- }
8907
- } catch {
8908
- }
8898
+ let processAlive = false;
8899
+ try {
8900
+ process.kill(existingPid, 0);
8901
+ processAlive = true;
8902
+ } catch {
8909
8903
  }
8910
- if (needsRestart) {
8911
- try {
8912
- process.kill(existingPid, "SIGTERM");
8913
- } catch {
8914
- }
8915
- await new Promise((r) => setTimeout(r, 1e3));
8916
- try {
8917
- process.kill(existingPid, 0);
8918
- process.kill(existingPid, "SIGKILL");
8919
- } catch {
8920
- }
8921
- for (const f of ["daemon.pid", "status.json", "cmd.json", "resp.json"]) {
8922
- const p = join14(this.nexusDir, f);
8923
- if (existsSync11(p))
8924
- await unlink(p).catch(() => {
8925
- });
8926
- }
8927
- } else {
8904
+ if (processAlive) {
8928
8905
  const statusFile2 = join14(this.nexusDir, "status.json");
8929
8906
  if (existsSync11(statusFile2)) {
8930
8907
  try {
@@ -8933,14 +8910,46 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
8933
8910
  await this.ensureWallet();
8934
8911
  return `Already connected (pid: ${existingPid}, peerId: ${status.peerId})`;
8935
8912
  }
8913
+ if (status.error) {
8914
+ try {
8915
+ process.kill(existingPid, "SIGTERM");
8916
+ } catch {
8917
+ }
8918
+ await new Promise((r) => setTimeout(r, 500));
8919
+ }
8920
+ return await this.waitForDaemonReady(existingPid);
8936
8921
  } catch {
8937
8922
  }
8938
8923
  }
8924
+ for (let i = 0; i < 10; i++) {
8925
+ await new Promise((r) => setTimeout(r, 500));
8926
+ if (existsSync11(statusFile2)) {
8927
+ try {
8928
+ const status = JSON.parse(await readFile8(statusFile2, "utf8"));
8929
+ if (status.connected && status.peerId) {
8930
+ await this.ensureWallet();
8931
+ return `Already connected (pid: ${existingPid}, peerId: ${status.peerId})`;
8932
+ }
8933
+ } catch {
8934
+ }
8935
+ }
8936
+ }
8939
8937
  try {
8940
8938
  process.kill(existingPid, "SIGTERM");
8941
8939
  } catch {
8942
8940
  }
8943
8941
  await new Promise((r) => setTimeout(r, 500));
8942
+ try {
8943
+ process.kill(existingPid, 0);
8944
+ process.kill(existingPid, "SIGKILL");
8945
+ } catch {
8946
+ }
8947
+ }
8948
+ for (const f of ["daemon.pid", "status.json", "cmd.json", "resp.json"]) {
8949
+ const p = join14(this.nexusDir, f);
8950
+ if (existsSync11(p))
8951
+ await unlink(p).catch(() => {
8952
+ });
8944
8953
  }
8945
8954
  }
8946
8955
  for (const f of ["daemon.pid", "status.json", "cmd.json", "resp.json"]) {
@@ -9144,6 +9153,28 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
9144
9153
  }
9145
9154
  return `Daemon failed to start.${earlyError ? "\n" + earlyError : ""}${earlyOutput ? "\n" + earlyOutput : ""}`;
9146
9155
  }
9156
+ /** Wait for an already-spawned daemon to write connected status (up to 20s). */
9157
+ async waitForDaemonReady(pid) {
9158
+ const statusFile = join14(this.nexusDir, "status.json");
9159
+ for (let i = 0; i < 40; i++) {
9160
+ await new Promise((r) => setTimeout(r, 500));
9161
+ if (existsSync11(statusFile)) {
9162
+ try {
9163
+ const status = JSON.parse(await readFile8(statusFile, "utf8"));
9164
+ if (status.error)
9165
+ return `Nexus daemon failed: ${status.error}`;
9166
+ if (status.connected && status.peerId) {
9167
+ await this.ensureWallet();
9168
+ return `Connected to nexus P2P network.
9169
+ Peer ID: ${status.peerId}
9170
+ Daemon PID: ${status.pid}`;
9171
+ }
9172
+ } catch {
9173
+ }
9174
+ }
9175
+ }
9176
+ return `Daemon spawned (pid: ${pid}) but still connecting. Check status in a moment.`;
9177
+ }
9147
9178
  async doDisconnect() {
9148
9179
  const pid = this.getDaemonPid();
9149
9180
  if (!pid)
@@ -49282,18 +49313,26 @@ import { join as join60 } from "node:path";
49282
49313
  function startSponsorHeartbeat(payload, getExposeGateway) {
49283
49314
  stopSponsorHeartbeat();
49284
49315
  _lastRegisteredSponsorPayload = { ...payload };
49316
+ const _stableGetGateway = getExposeGateway;
49285
49317
  const HEARTBEAT_MS = 5 * 60 * 1e3;
49286
49318
  _sponsorHeartbeatTimer = setInterval(async () => {
49287
49319
  if (!_lastRegisteredSponsorPayload)
49288
49320
  return;
49289
49321
  try {
49290
- const gw = getExposeGateway?.();
49291
- if (gw && gw.tunnelUrl && gw.tunnelUrl !== _lastRegisteredSponsorPayload.tunnelUrl) {
49292
- _lastRegisteredSponsorPayload.tunnelUrl = gw.tunnelUrl;
49322
+ const gw = _stableGetGateway?.();
49323
+ if (gw) {
49324
+ if (gw.tunnelUrl && gw.tunnelUrl !== _lastRegisteredSponsorPayload.tunnelUrl) {
49325
+ _lastRegisteredSponsorPayload.tunnelUrl = gw.tunnelUrl;
49326
+ }
49327
+ if (gw.models && Array.isArray(gw.models)) {
49328
+ _lastRegisteredSponsorPayload.models = gw.models;
49329
+ }
49293
49330
  _lastRegisteredSponsorPayload.status = "active";
49294
49331
  }
49295
49332
  } catch {
49296
49333
  }
49334
+ if (!_lastRegisteredSponsorPayload.tunnelUrl)
49335
+ return;
49297
49336
  try {
49298
49337
  await fetch("https://openagents.nexus/api/v1/sponsors", {
49299
49338
  method: "POST",
@@ -51079,6 +51118,15 @@ Clone a new voice: /voice clone <wav-file> [name]`);
51079
51118
  if (pauseGw && "setSponsorLimits" in pauseGw) {
51080
51119
  pauseGw.setSponsorLimits({ maxRequestsPerMinute: 0, maxTokensPerDay: 0, maxConcurrent: 0, allowedModels: [] });
51081
51120
  }
51121
+ try {
51122
+ await fetch("https://openagents.nexus/api/v1/sponsors", {
51123
+ method: "POST",
51124
+ headers: { "Content-Type": "application/json" },
51125
+ body: JSON.stringify({ name: existingConfig.header?.message || "unknown", status: "inactive", tunnelUrl: pauseGw?.tunnelUrl || "" }),
51126
+ signal: AbortSignal.timeout(5e3)
51127
+ });
51128
+ } catch {
51129
+ }
51082
51130
  renderInfo("Sponsorship paused. Tunnel still alive for quick resume.");
51083
51131
  renderInfo("/sponsor to resume, /sponsor remove to fully stop.");
51084
51132
  return "handled";
@@ -51134,13 +51182,44 @@ Clone a new voice: /voice clone <wav-file> [name]`);
51134
51182
  case "pause":
51135
51183
  existingConfig.status = "paused";
51136
51184
  saveSponsorConfig2(projectDir, existingConfig);
51185
+ stopSponsorHeartbeat();
51186
+ try {
51187
+ await fetch("https://openagents.nexus/api/v1/sponsors", {
51188
+ method: "POST",
51189
+ headers: { "Content-Type": "application/json" },
51190
+ body: JSON.stringify({ name: existingConfig.header?.message || "unknown", status: "inactive" }),
51191
+ signal: AbortSignal.timeout(5e3)
51192
+ });
51193
+ } catch {
51194
+ }
51137
51195
  renderInfo("Sponsorship paused. /sponsor to resume.");
51138
51196
  return "handled";
51139
- case "resume":
51197
+ case "resume": {
51140
51198
  existingConfig.status = "active";
51141
51199
  saveSponsorConfig2(projectDir, existingConfig);
51142
- renderInfo("Sponsorship resumed.");
51200
+ const resumeGw = ctx.getExposeGateway?.();
51201
+ if (resumeGw?.tunnelUrl) {
51202
+ const resumePayload = {
51203
+ name: existingConfig.header?.message || "OA Sponsor",
51204
+ tunnelUrl: resumeGw.tunnelUrl,
51205
+ authKey: resumeGw.authKey || "",
51206
+ models: [],
51207
+ status: "active"
51208
+ };
51209
+ startSponsorHeartbeat(resumePayload, ctx.getExposeGateway);
51210
+ try {
51211
+ await fetch("https://openagents.nexus/api/v1/sponsors", {
51212
+ method: "POST",
51213
+ headers: { "Content-Type": "application/json" },
51214
+ body: JSON.stringify(resumePayload),
51215
+ signal: AbortSignal.timeout(5e3)
51216
+ });
51217
+ } catch {
51218
+ }
51219
+ }
51220
+ renderInfo("Sponsorship resumed. Heartbeat restarted.");
51143
51221
  return "handled";
51222
+ }
51144
51223
  case "remove":
51145
51224
  existingConfig.status = "inactive";
51146
51225
  saveSponsorConfig2(projectDir, existingConfig);
@@ -53183,9 +53262,15 @@ async function handleSponsoredEndpoint(ctx, local) {
53183
53262
  if (isConnected) {
53184
53263
  renderInfo("Using existing nexus connection...");
53185
53264
  } else {
53186
- renderInfo("Starting nexus daemon...");
53187
- await nexusTool.execute({ action: "connect" });
53188
- await new Promise((r) => setTimeout(r, 3e3));
53265
+ const hasPid = statusResult.includes("PID:") || statusResult.includes("pid:");
53266
+ if (!hasPid) {
53267
+ renderInfo("Starting nexus daemon...");
53268
+ await nexusTool.execute({ action: "connect" });
53269
+ await new Promise((r) => setTimeout(r, 3e3));
53270
+ } else {
53271
+ renderInfo("Waiting for nexus daemon to connect...");
53272
+ await new Promise((r) => setTimeout(r, 5e3));
53273
+ }
53189
53274
  }
53190
53275
  renderInfo("Querying sponsors on the mesh...");
53191
53276
  const discoverResult = await nexusTool.execute({
@@ -53315,9 +53400,14 @@ async function handleSponsoredEndpoint(ctx, local) {
53315
53400
  if (sp.authKey)
53316
53401
  headers["Authorization"] = `Bearer ${sp.authKey}`;
53317
53402
  const resp = await fetch(`${base}/v1/models`, { headers, signal: AbortSignal.timeout(15e3) });
53403
+ if (resp.status === 429 || resp.status === 503)
53404
+ return true;
53318
53405
  if (!resp.ok && sp.authKey) {
53319
- const noAuth = await fetch(`${base}/v1/models`, { signal: AbortSignal.timeout(1e4) });
53320
- return noAuth.ok;
53406
+ if (resp.status === 401) {
53407
+ const noAuth = await fetch(`${base}/v1/models`, { signal: AbortSignal.timeout(1e4) });
53408
+ return noAuth.ok || noAuth.status === 429 || noAuth.status === 503;
53409
+ }
53410
+ return false;
53321
53411
  }
53322
53412
  return resp.ok;
53323
53413
  } catch {
@@ -71763,13 +71853,13 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
71763
71853
  if (sponsor.authKey)
71764
71854
  headers["Authorization"] = `Bearer ${sponsor.authKey}`;
71765
71855
  const probe = await fetch(testUrl, { headers, signal: AbortSignal.timeout(15e3) });
71766
- if (probe.ok) {
71856
+ if (probe.ok || probe.status === 429 || probe.status === 503) {
71767
71857
  best = sponsor;
71768
71858
  break;
71769
71859
  }
71770
71860
  if (probe.status === 401 && sponsor.authKey) {
71771
71861
  const noAuthProbe = await fetch(testUrl, { signal: AbortSignal.timeout(1e4) });
71772
- if (noAuthProbe.ok) {
71862
+ if (noAuthProbe.ok || noAuthProbe.status === 429 || noAuthProbe.status === 503) {
71773
71863
  best = sponsor;
71774
71864
  bestNoAuth = true;
71775
71865
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.186.16",
3
+ "version": "0.186.18",
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",