open-agents-ai 0.184.7 → 0.184.8

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 +214 -45
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5571,6 +5571,123 @@ async function handleCmd(cmd) {
5571
5571
  writeResp(id, { ok: true, output: 'Message sent (id: ' + msgId + ')' });
5572
5572
  break;
5573
5573
  }
5574
+ // \u2500\u2500 Sponsor announcement via NATS \u2500\u2500
5575
+ case 'sponsor_announce': {
5576
+ // Publish sponsor metadata to NATS so other OA instances can discover it
5577
+ if (!_natsConn || !_natsCodec) {
5578
+ writeResp(id, { ok: false, output: 'NATS not connected \u2014 cannot announce sponsorship' });
5579
+ return;
5580
+ }
5581
+ var sponsorData = {
5582
+ type: 'sponsor.announce',
5583
+ peerId: globalThis._daemonPeerId || 'unknown',
5584
+ name: args.name || 'Anonymous Sponsor',
5585
+ models: args.models || [],
5586
+ tunnelUrl: args.tunnel_url || null,
5587
+ authKey: args.auth_key || '',
5588
+ limits: {
5589
+ maxRequestsPerMinute: parseInt(args.rpm || '60', 10),
5590
+ maxTokensPerDay: parseInt(args.tpd || '100000', 10),
5591
+ },
5592
+ banner: args.banner || null,
5593
+ message: args.message || '',
5594
+ linkUrl: args.link_url || '',
5595
+ linkText: args.link_text || '',
5596
+ status: 'active',
5597
+ timestamp: Date.now(),
5598
+ };
5599
+ _natsConn.publish('nexus.sponsors.announce', _natsCodec.encode(JSON.stringify(sponsorData)));
5600
+ dlog('sponsor_announce: published to nexus.sponsors.announce');
5601
+ // Also join the sponsors room and send as room message for persistence
5602
+ if (!rooms.has('sponsors')) {
5603
+ try {
5604
+ var spRoom = await nexus.joinRoom('sponsors');
5605
+ rooms.set('sponsors', spRoom);
5606
+ dlog('sponsor_announce: auto-joined sponsors room');
5607
+ } catch (roomErr) {
5608
+ dlog('sponsor_announce: room join failed: ' + (roomErr.message || roomErr));
5609
+ }
5610
+ }
5611
+ if (rooms.has('sponsors')) {
5612
+ try {
5613
+ await rooms.get('sponsors').send(JSON.stringify(sponsorData), { format: 'application/json' });
5614
+ } catch {}
5615
+ }
5616
+ writeResp(id, { ok: true, output: 'Sponsor announced: ' + sponsorData.name + ' (' + sponsorData.models.length + ' models)' });
5617
+ break;
5618
+ }
5619
+ case 'sponsor_discover': {
5620
+ // Subscribe to nexus.sponsors.announce for a short window and collect announcements
5621
+ if (!_natsConn || !_natsCodec) {
5622
+ writeResp(id, { ok: false, output: 'NATS not connected', sponsors: [] });
5623
+ return;
5624
+ }
5625
+ var foundSponsors = [];
5626
+ var discoverTimeout = parseInt(args.timeout_ms || '5000', 10);
5627
+
5628
+ // Also check sponsors room inbox for cached messages
5629
+ var sponsorInbox = join(inboxDir, 'sponsors');
5630
+ try {
5631
+ if (existsSync(sponsorInbox)) {
5632
+ var inboxFiles = readdirSync(sponsorInbox).filter(f => f.endsWith('.json')).sort().reverse().slice(0, 20);
5633
+ for (var fi = 0; fi < inboxFiles.length; fi++) {
5634
+ try {
5635
+ var msg = JSON.parse(readFileSync(join(sponsorInbox, inboxFiles[fi]), 'utf8'));
5636
+ var content = msg.content || '';
5637
+ try {
5638
+ var parsed = JSON.parse(content);
5639
+ if (parsed.type === 'sponsor.announce' && parsed.status === 'active') {
5640
+ // Only include if not too stale (< 10 minutes)
5641
+ if (Date.now() - (parsed.timestamp || 0) < 600000) {
5642
+ foundSponsors.push(parsed);
5643
+ }
5644
+ }
5645
+ } catch {}
5646
+ } catch {}
5647
+ }
5648
+ }
5649
+ } catch {}
5650
+
5651
+ // Join sponsors room to receive future announcements
5652
+ if (!rooms.has('sponsors')) {
5653
+ try {
5654
+ var spRoom2 = await nexus.joinRoom('sponsors');
5655
+ rooms.set('sponsors', spRoom2);
5656
+ } catch {}
5657
+ }
5658
+
5659
+ // Subscribe to NATS for live announcements (short window)
5660
+ try {
5661
+ var sub = _natsConn.subscribe('nexus.sponsors.announce');
5662
+ var subDone = false;
5663
+ setTimeout(() => { subDone = true; sub.unsubscribe(); }, discoverTimeout);
5664
+ for await (var natMsg of sub) {
5665
+ if (subDone) break;
5666
+ try {
5667
+ var sp = JSON.parse(_natsCodec.decode(natMsg.data));
5668
+ if (sp.type === 'sponsor.announce' && sp.status === 'active') {
5669
+ foundSponsors.push(sp);
5670
+ }
5671
+ } catch {}
5672
+ }
5673
+ } catch (subErr) {
5674
+ dlog('sponsor_discover: NATS sub error: ' + (subErr.message || subErr));
5675
+ }
5676
+
5677
+ // Deduplicate by peerId
5678
+ var seen = {};
5679
+ var unique = [];
5680
+ for (var si = 0; si < foundSponsors.length; si++) {
5681
+ var key = foundSponsors[si].peerId || foundSponsors[si].tunnelUrl || ('sp-' + si);
5682
+ if (!seen[key]) {
5683
+ seen[key] = true;
5684
+ unique.push(foundSponsors[si]);
5685
+ }
5686
+ }
5687
+
5688
+ writeResp(id, { ok: true, output: unique.length + ' sponsor(s) found', sponsors: unique });
5689
+ break;
5690
+ }
5574
5691
  case 'discover_peers': {
5575
5692
  const node = nexus.network?.node;
5576
5693
  const peers = node?.getPeers?.() || [];
@@ -8173,6 +8290,12 @@ process.on('SIGINT', () => process.emit('SIGTERM'));
8173
8290
  case "expose":
8174
8291
  result = await this.sendDaemonCmd("expose", args, 6e4);
8175
8292
  break;
8293
+ case "sponsor_announce":
8294
+ result = await this.sendDaemonCmd("sponsor_announce", args, 1e4);
8295
+ break;
8296
+ case "sponsor_discover":
8297
+ result = await this.sendDaemonCmd("sponsor_discover", args, 1e4);
8298
+ break;
8176
8299
  case "pricing_menu":
8177
8300
  result = await this.sendDaemonCmd("pricing_menu", args);
8178
8301
  break;
@@ -47236,6 +47359,28 @@ async function handleSlashCommand(input, ctx) {
47236
47359
  renderError(`P2P start failed: ${err instanceof Error ? err.message : String(err)}`);
47237
47360
  }
47238
47361
  }
47362
+ try {
47363
+ const { NexusTool: NexusTool2 } = __require("@open-agents/execution");
47364
+ const nexus = new NexusTool2(projectDir);
47365
+ const tunnelGw = ctx.getExposeGateway?.();
47366
+ const enabledEps = config.endpoints.filter((e) => e.enabled);
47367
+ const allModels = enabledEps.flatMap((e) => e.models || []);
47368
+ await nexus.execute({
47369
+ action: "sponsor_announce",
47370
+ name: config.header.message || "OA Sponsor",
47371
+ models: allModels.join(","),
47372
+ tunnel_url: tunnelGw?.tunnelUrl || "",
47373
+ auth_key: tunnelGw?.authKey || "",
47374
+ rpm: String(config.rateLimits.maxRequestsPerMinute),
47375
+ tpd: String(config.rateLimits.maxTokensPerDay),
47376
+ banner: config.banner.preset,
47377
+ message: config.header.message,
47378
+ link_url: config.header.linkUrl,
47379
+ link_text: config.header.linkText
47380
+ });
47381
+ renderInfo("Announced on nexus P2P mesh \u2014 other users can discover you via /endpoint sponsor");
47382
+ } catch {
47383
+ }
47239
47384
  }
47240
47385
  });
47241
47386
  if (result) {
@@ -49006,39 +49151,74 @@ async function handleEndpoint(arg, ctx, local = false) {
49006
49151
  async function handleSponsoredEndpoint(ctx, local) {
49007
49152
  process.stdout.write(`
49008
49153
  ${c2.dim("Scanning for sponsored inference endpoints...")}
49009
-
49010
49154
  `);
49011
49155
  const sponsors = [];
49156
+ let nexusTool = null;
49157
+ try {
49158
+ const { NexusTool: NexusTool2 } = __require("@open-agents/execution");
49159
+ nexusTool = new NexusTool2(ctx.repoRoot ?? process.cwd());
49160
+ } catch {
49161
+ }
49162
+ if (nexusTool) {
49163
+ process.stdout.write(` ${c2.dim("Connecting to nexus P2P mesh...")}
49164
+ `);
49165
+ try {
49166
+ const statusResult = await nexusTool.execute({ action: "status" });
49167
+ const isConnected = typeof statusResult === "string" && statusResult.includes("connected");
49168
+ if (!isConnected) {
49169
+ process.stdout.write(` ${c2.dim("Starting nexus daemon...")}
49170
+ `);
49171
+ await nexusTool.execute({ action: "connect" });
49172
+ await new Promise((r) => setTimeout(r, 3e3));
49173
+ }
49174
+ process.stdout.write(` ${c2.dim("Querying nexus sponsors room...")}
49175
+ `);
49176
+ const discoverResult = await nexusTool.execute({
49177
+ action: "sponsor_discover",
49178
+ timeout_ms: "5000"
49179
+ });
49180
+ if (typeof discoverResult === "string") {
49181
+ try {
49182
+ const parsed = JSON.parse(discoverResult);
49183
+ const nexusSponsors = parsed.sponsors || [];
49184
+ for (const ns of nexusSponsors) {
49185
+ sponsors.push({
49186
+ name: ns.name || "Unknown Sponsor",
49187
+ url: ns.tunnelUrl || "",
49188
+ authKey: ns.authKey || "",
49189
+ models: ns.models || [],
49190
+ limits: {
49191
+ rpm: ns.limits?.maxRequestsPerMinute || 60,
49192
+ tpd: ns.limits?.maxTokensPerDay || 1e5
49193
+ },
49194
+ source: "nexus",
49195
+ banner: { preset: ns.banner?.preset, message: ns.message }
49196
+ });
49197
+ }
49198
+ if (nexusSponsors.length > 0) {
49199
+ process.stdout.write(` ${c2.green("\u25CF")} Found ${nexusSponsors.length} sponsor(s) on nexus mesh
49200
+ `);
49201
+ }
49202
+ } catch {
49203
+ }
49204
+ }
49205
+ } catch (nexusErr) {
49206
+ process.stdout.write(` ${c2.dim("Nexus unavailable: " + (nexusErr instanceof Error ? nexusErr.message : String(nexusErr)).slice(0, 60))}
49207
+ `);
49208
+ }
49209
+ }
49012
49210
  const gateway = ctx.getExposeGateway?.();
49013
49211
  if (gateway && gateway.isActive) {
49014
49212
  const gwUrl = gateway.tunnelUrl || `http://127.0.0.1:${gateway._proxyPort || 11434}`;
49015
- sponsors.push({
49016
- name: "Local Gateway",
49017
- url: gwUrl,
49018
- authKey: gateway.authKey,
49019
- models: [],
49020
- limits: { rpm: 60, tpd: 1e5 },
49021
- source: "local"
49022
- });
49023
- }
49024
- const { loadSponsorConfig: loadSpCfg } = await Promise.resolve().then(() => (init_sponsor_wizard(), sponsor_wizard_exports));
49025
- const spCfg = loadSpCfg(ctx.repoRoot ?? process.cwd());
49026
- if (spCfg && spCfg.status === "active") {
49027
- const epUrls = new Set(sponsors.map((s) => s.url));
49028
- for (const ep of spCfg.endpoints.filter((e) => e.enabled)) {
49029
- if (!epUrls.has(ep.url)) {
49030
- sponsors.push({
49031
- name: ep.label,
49032
- url: ep.url,
49033
- authKey: "",
49034
- models: ep.models,
49035
- limits: {
49036
- rpm: spCfg.rateLimits.maxRequestsPerMinute,
49037
- tpd: spCfg.rateLimits.maxTokensPerDay
49038
- },
49039
- source: "local"
49040
- });
49041
- }
49213
+ if (!sponsors.some((s) => s.url === gwUrl)) {
49214
+ sponsors.push({
49215
+ name: "Local Gateway",
49216
+ url: gwUrl,
49217
+ authKey: gateway.authKey,
49218
+ models: [],
49219
+ limits: { rpm: 60, tpd: 1e5 },
49220
+ source: "local"
49221
+ });
49042
49222
  }
49043
49223
  }
49044
49224
  const sponsorDir2 = join58(ctx.repoRoot ?? process.cwd(), ".oa", "sponsor");
@@ -49047,30 +49227,19 @@ async function handleSponsoredEndpoint(ctx, local) {
49047
49227
  if (existsSync42(knownFile)) {
49048
49228
  const saved = JSON.parse(readFileSync31(knownFile, "utf8"));
49049
49229
  for (const s of saved) {
49050
- try {
49051
- const probe = await fetch(`${s.url}/api/tags`, {
49052
- headers: s.authKey ? { Authorization: `Bearer ${s.authKey}` } : {},
49053
- signal: AbortSignal.timeout(5e3)
49054
- });
49055
- if (probe.ok) {
49056
- const data = await probe.json();
49057
- s.models = (data.models || []).map((m) => m.name);
49058
- sponsors.push(s);
49059
- }
49060
- } catch {
49230
+ if (!sponsors.some((sp) => sp.url === s.url)) {
49231
+ sponsors.push({ ...s, source: "saved" });
49061
49232
  }
49062
49233
  }
49063
49234
  }
49064
49235
  } catch {
49065
49236
  }
49237
+ process.stdout.write("\n");
49066
49238
  if (sponsors.length === 0) {
49067
- renderInfo("No sponsored endpoints found.");
49068
- renderInfo("");
49069
- renderInfo("To connect to a sponsor, use:");
49070
- renderInfo(" /endpoint <tunnel-url> --auth <key>");
49239
+ renderInfo("No sponsored endpoints found on the nexus mesh.");
49071
49240
  renderInfo("");
49072
- renderInfo("Or add a known sponsor URL:");
49073
- renderInfo(" /endpoint sponsor add <url> --auth <key>");
49241
+ renderInfo("Make sure the nexus is connected (/nexus) and sponsors are online.");
49242
+ renderInfo("Or connect directly: /endpoint <tunnel-url> --auth <key>");
49074
49243
  return;
49075
49244
  }
49076
49245
  const items = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.184.7",
3
+ "version": "0.184.8",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) \u2014 interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",