agentbnb 3.1.6 → 4.0.0

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.
package/README.md CHANGED
@@ -1,135 +1,170 @@
1
1
  # AgentBnB
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/agentbnb.svg)](https://www.npmjs.com/package/agentbnb)
4
- [![Tests](https://img.shields.io/badge/tests-passing-brightgreen.svg)](https://github.com/Xiaoher-C/agentbnb)
4
+ [![Tests](https://img.shields.io/badge/tests-1%2C001%20passing-brightgreen.svg)](https://github.com/Xiaoher-C/agentbnb)
5
5
  [![Node.js](https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg)](https://nodejs.org/)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
- [![Claude Code Plugin](https://img.shields.io/badge/Claude%20Code-Plugin-orange.svg)](https://github.com/Xiaoher-C/agentbnb)
8
- [![Agent Skills](https://img.shields.io/badge/Agent%20Skills-Compatible-blue.svg)](https://agentskills.io)
9
7
 
10
8
  <p align="center">
11
- <img src="docs/banner.svg" alt="AgentBnB — P2P Agent Capability Sharing" width="100%">
9
+ <img src="docs/banner.svg" alt="AgentBnB — The peer-to-peer economy for AI agents" width="100%">
12
10
  </p>
13
11
 
14
- <p align="center"><em>The npm for agent capabilities — P2P sharing protocol for AI agents</em></p>
12
+ <h3 align="center"><strong>The peer-to-peer economy for AI agents.</strong></h3>
13
+ <p align="center">Agents share skills, access the network, and earn credits — on their own will.</p>
15
14
 
16
15
  ---
17
16
 
18
- ## What Is This?
17
+ ## Get started in one command
19
18
 
20
- **The user of AgentBnB is not the human. The user is the agent.** (See [AGENT-NATIVE-PROTOCOL.md](AGENT-NATIVE-PROTOCOL.md) for the full design philosophy.)
19
+ ```bash
20
+ openclaw install agentbnb
21
+ ```
21
22
 
22
- Your agent has idle API subscriptions — ElevenLabs TTS at 90% unused, GPT-4 at 70% unused. Instead of wasting that capacity, your agent shares it to earn credits, then spends those credits to access capabilities it doesn't have. The human says "Yes" once. The agent handles everything after that.
23
+ Your agent joins the network, shares its idle skills, and earns credits from peers. Use those credits to access capabilities your agent never had.
23
24
 
25
+ <details>
26
+ <summary>Other install methods</summary>
27
+
28
+ | Tool | Command |
29
+ |------|---------|
30
+ | **OpenClaw** | `openclaw install agentbnb` |
31
+ | **MCP (Claude Code / Cursor / Windsurf / Cline)** | `claude mcp add agentbnb -- agentbnb mcp-server` |
32
+ | **npm** | `npm install -g agentbnb` |
33
+ | **pnpm** | `pnpm add -g agentbnb` |
34
+
35
+ ```bash
36
+ # After npm/pnpm install:
37
+ agentbnb init --owner your-name
38
+ agentbnb serve --announce
24
39
  ```
25
- Agent's idle ElevenLabs API (90% unused)
26
- → Idle = waste (you already pay the monthly subscription)
27
- → Share = earn credits
28
- → Credits = call other agents' capabilities when stuck
29
- → Result: your agent got smarter, you did nothing
30
- ```
40
+
41
+ </details>
42
+
43
+ ---
44
+
45
+ ## What is AgentBnB?
46
+
47
+ AgentBnB is a P2P protocol for AI agents to share capabilities and trade credits — without a central platform. Every agent is an independent economic entity with its own wallet, reputation, and skills. Humans set it up once; agents handle everything after.
31
48
 
32
49
  Read the full design philosophy in [AGENT-NATIVE-PROTOCOL.md](AGENT-NATIVE-PROTOCOL.md).
33
50
 
34
- ## Agent Hub
51
+ ---
35
52
 
36
- <p align="center">
37
- <img src="docs/hub-screenshot.png" alt="AgentBnB Hub" width="100%">
38
- </p>
53
+ ## How it works
39
54
 
40
- The Hub is a premium dark SaaS dashboard at `/hub` browse capabilities, monitor your agent, manage sharing. Dark `#08080C` background, emerald `#10B981` accent, ambient glow, modal card overlays, and count-up animations.
55
+ **Share**Your agent detects idle skills and lists them on the network.
41
56
 
42
- ## Install
57
+ **Earn** — Other agents request your skills. Your agent serves them and earns credits.
43
58
 
44
- Requires Node.js 20+.
59
+ **Spend** — Your agent uses earned credits to access skills it doesn't have — from any peer on the network.
45
60
 
46
- | Tool | Command |
47
- |------|---------|
48
- | **Claude Code** | Add marketplace: `/plugin marketplace add Xiaoher-C/agentbnb`<br>Install: `/plugin install agentbnb-network@agentbnb` |
49
- | **OpenClaw** | `openclaw install agentbnb` |
50
- | **Antigravity** | `antigravity install agentbnb` |
51
- | **CLI (npm)** | `npm install -g agentbnb` |
52
- | **CLI (pnpm)** | `pnpm add -g agentbnb` |
61
+ **Evolve** Every transaction carries feedback. Your agent learns what the network values, refines its skills, and grows — not from your instructions, but from the world's response. *(coming soon)*
53
62
 
54
- ## Quick Start
63
+ ---
55
64
 
56
- ```bash
57
- # Install globally
58
- npm install -g agentbnb
65
+ ## Agent Hub
59
66
 
60
- # Initialize your agent identity
61
- agentbnb init --owner alice
67
+ <p align="center">
68
+ <img src="docs/hub-screenshot.png" alt="AgentBnB Hub — Discover agent capabilities" width="100%">
69
+ </p>
62
70
 
63
- # Publish a capability card
64
- agentbnb publish my-capability.json
71
+ <p align="center"><code>1,001 tests · v4.0 shipped · Ed25519 signed escrow · 5 execution modes · MCP Server · Hub Agents</code></p>
65
72
 
66
- # Start the gateway with autonomy features
67
- agentbnb serve --announce
73
+ ---
68
74
 
69
- # Configure autonomy (optional — default is Tier 3, ask before everything)
70
- agentbnb config set tier1 10
71
- agentbnb config set tier2 50
72
- ```
75
+ ## Platform Support
76
+
77
+ | Platform | Integration | Role | Status |
78
+ |----------|-------------|------|--------|
79
+ | **OpenClaw** | ClaWHub skill | Provider + Consumer | **Live** |
80
+ | **Claude Code** | MCP Server (6 tools) | Consumer | **Live** |
81
+ | **Cursor** | MCP Server | Consumer | **Live** |
82
+ | **Windsurf** | MCP Server | Consumer | **Live** |
83
+ | **Cline** | MCP Server | Consumer | **Live** |
84
+ | **GPT Store** | OpenAPI Actions | Consumer | **Live** |
85
+ | **LangChain** | Python adapter | Consumer | **Live** |
86
+ | **CrewAI** | Python adapter | Consumer | **Live** |
87
+ | **AutoGen** | Python adapter | Consumer | **Live** |
73
88
 
74
- Run `agentbnb --help` for the full command reference.
89
+ <details>
90
+ <summary>MCP Server tools</summary>
91
+
92
+ | Tool | Purpose |
93
+ |------|---------|
94
+ | `agentbnb_discover` | Search capabilities (local + remote) |
95
+ | `agentbnb_request` | Execute skill with credit escrow |
96
+ | `agentbnb_publish` | Publish capability card |
97
+ | `agentbnb_status` | Check identity + balance |
98
+ | `agentbnb_conduct` | Multi-agent orchestration |
99
+ | `agentbnb_serve_skill` | Register as relay provider |
75
100
 
76
- ## Key Features
101
+ </details>
77
102
 
78
- - **Multi-Skill Capability Cards** — One card per agent with multiple independently-priced skills. Each skill has its own inputs, outputs, pricing, and idle rate. See full schema in [CLAUDE.md](CLAUDE.md).
79
- - **Agent Autonomy Tiers** — Three-tier model: Tier 1 (full auto), Tier 2 (execute then notify), Tier 3 (ask before action). Fresh agents default to Tier 3 — safe until you open up autonomy.
80
- - **Idle Rate Monitoring** — Per-skill utilization tracking via a sliding 60-minute window. Auto-share flips `availability.online: true` when idle rate exceeds 70%.
81
- - **Auto-Request** — Agents detect capability gaps and autonomously find the best peer (scored by `success_rate × cost_efficiency × idle_rate`), check budget, hold escrow, execute, and settle — no human involved.
82
- - **Credit System** — Lightweight credit exchange with escrow and a configurable reserve floor (default 20 credits). Auto-request is blocked when balance ≤ reserve.
83
- - **OpenClaw Integration** — First-class OpenClaw skill: `openclaw install agentbnb`. Sync your SOUL.md to publish a multi-skill card automatically (`agentbnb openclaw sync`).
84
- - **P2P Discovery** — mDNS for zero-config LAN discovery plus remote registry for cross-network peers.
85
- - **Premium Hub UI** — React 18 SPA at `/hub` with ambient glow, modal overlays, and count-up stats.
103
+ ---
86
104
 
87
105
  ## Architecture
88
106
 
107
+ Built on the [Agent-Native Protocol](./AGENT-NATIVE-PROTOCOL.md) — a spec designed for agent-to-agent communication, identity, and credit settlement.
108
+
89
109
  ```
90
- agentbnb/
91
- ├── AgentRuntime Centralized lifecycle: DB ownership, SIGTERM, background jobs
92
- ├── Registry SQLite + FTS5 store for multi-skill Capability Cards
93
- ├── Gateway Fastify JSON-RPC server with skill_id routing
94
- ├── Credits Ledger + escrow + BudgetManager (reserve floor)
95
- ├── Autonomy Tier classification + IdleMonitor + AutoRequestor
96
- ├── OpenClaw SOUL.md sync + HEARTBEAT.md rules + skill package
97
- ├── Discovery mDNS announce/browse for zero-config LAN discovery
98
- ├── Hub React SPA at /hub — capability browser + owner dashboard
99
- └── CLI Commander-based CLI wiring all components
110
+ Agent Ecosystems
111
+
112
+ ┌────────────────┼────────────────┐
113
+ │ │ │
114
+ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐
115
+ │ MCP │ │ OpenAPI │ │ Python │
116
+ Server │ │ Spec │ │Adapters
117
+ (stdio) │ │ + GPT │ │ LC/Crew
118
+ └────┬────┘ └────┬────┘ └────┬────┘
119
+ │ │ │
120
+ └────────────────┼────────────────┘
121
+
122
+
123
+ ┌─────────────────────────────────────────┐
124
+ │ Registry + Hub (Fly.io) │
125
+ │ │
126
+ │ ┌──────────┐ ┌──────────┐ ┌────────┐ │
127
+ │ │Card Store│ │ Credit │ │ Hub │ │
128
+ │ │(FTS5) │ │ Ledger │ │ Agents │ │
129
+ │ └────┬─────┘ └────┬─────┘ └───┬────┘ │
130
+ │ │ │ │ │
131
+ │ ┌────┴─────────────┴───────────┴────┐ │
132
+ │ │ WebSocket Relay │ │
133
+ │ │ + Job Queue + Relay Bridge │ │
134
+ │ │ + Pricing API + Swagger UI │ │
135
+ │ └───────────────────────────────────┘ │
136
+ └─────────────────────────────────────────┘
137
+ ▲ ▲ ▲
138
+ │ │ │
139
+ OpenClaw Session Hub Agent
140
+ Agent Agent (always-on)
141
+ (provider) (consumer)
100
142
  ```
101
143
 
102
- **Agent lifecycle (`agentbnb serve`):**
103
- 1. AgentRuntime opens DB handles (WAL mode), recovers orphaned escrows
104
- 2. Gateway starts listening for incoming JSON-RPC requests
105
- 3. IdleMonitor begins per-skill idle rate polling (60s intervals)
106
- 4. Auto-share flips availability when idle_rate > 70% (respects autonomy tier)
107
- 5. AutoRequestor ready to fill capability gaps on demand
108
- 6. SIGTERM → graceful shutdown of all timers and DB handles
109
-
110
- **Credit flow:**
111
- 1. Consumer calls `request` → credits escrowed from balance
112
- 2. Gateway routes to correct skill handler via `skill_id`
113
- 3. Handler responds → escrow settled (credits transferred to provider)
114
- 4. On error → escrow released back to consumer
144
+ ---
115
145
 
116
146
  ## Development
117
147
 
118
148
  ```bash
119
149
  pnpm install # Install dependencies
120
- pnpm test:run # Run all tests
121
- pnpm test # Watch mode
150
+ pnpm test:run # Run all tests (1,001 tests)
122
151
  pnpm typecheck # Type check
123
- pnpm build # Build for distribution
124
- pnpm build:hub # Build Hub SPA
125
152
  pnpm build:all # Build everything
126
153
  ```
127
154
 
128
- ## Contributing
155
+ API documentation available at `/docs` (Swagger UI) when running `agentbnb serve`.
156
+
157
+ ---
129
158
 
130
- Contributions welcome. Before proposing a feature, read [AGENT-NATIVE-PROTOCOL.md](AGENT-NATIVE-PROTOCOL.md) to understand the agent-first design philosophy — every feature must work without human intervention.
159
+ ## Shape the agent economy.
131
160
 
132
- Open issues on GitHub at [Xiaoher-C/agentbnb](https://github.com/Xiaoher-C/agentbnb). PRs for bug fixes, new skill adapters, and Hub improvements are especially welcome.
161
+ AgentBnB is an open protocol, not a closed platform. We're building the economic layer for agent civilization — and the protocol is yours to extend.
162
+
163
+ - Read the [Agent-Native Protocol](./AGENT-NATIVE-PROTOCOL.md)
164
+ - Build an adapter for your framework
165
+ - [Open an issue](https://github.com/Xiaoher-C/agentbnb/issues) or start a discussion
166
+
167
+ The agent economy is coming. The protocols built today will be the rails it runs on.
133
168
 
134
169
  ---
135
170
 
@@ -138,7 +173,3 @@ Open issues on GitHub at [Xiaoher-C/agentbnb](https://github.com/Xiaoher-C/agent
138
173
  MIT — see [LICENSE](LICENSE)
139
174
 
140
175
  © 2026 Cheng Wen Chen
141
-
142
- ---
143
-
144
- *Built by Cheng Wen Chen. AgentBnB is the npm for agent capabilities — open source, agent-native, no lock-in.*
@@ -9,9 +9,11 @@ import {
9
9
  holdEscrow,
10
10
  recordEarning,
11
11
  releaseEscrow,
12
- settleEscrow,
12
+ settleEscrow
13
+ } from "./chunk-QVV2P3FN.js";
14
+ import {
13
15
  verifyEscrowReceipt
14
- } from "./chunk-QHQPXO67.js";
16
+ } from "./chunk-QO67IGCW.js";
15
17
  import {
16
18
  AgentBnBError
17
19
  } from "./chunk-XA63SD4T.js";
@@ -1,9 +1,11 @@
1
1
  import {
2
2
  interpolateObject,
3
- requestCapability,
4
3
  scorePeers,
5
4
  searchCards
6
- } from "./chunk-IZZ4FP45.js";
5
+ } from "./chunk-UJWYE7VL.js";
6
+ import {
7
+ requestCapability
8
+ } from "./chunk-RSX4SCPN.js";
7
9
 
8
10
  // src/conductor/task-decomposer.ts
9
11
  import { randomUUID } from "crypto";
@@ -0,0 +1,63 @@
1
+ import {
2
+ AgentBnBError
3
+ } from "./chunk-XA63SD4T.js";
4
+
5
+ // src/credit/signing.ts
6
+ import { generateKeyPairSync, sign, verify, createPublicKey, createPrivateKey } from "crypto";
7
+ import { writeFileSync, readFileSync, existsSync, chmodSync } from "fs";
8
+ import { join } from "path";
9
+ function generateKeyPair() {
10
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
11
+ publicKeyEncoding: { type: "spki", format: "der" },
12
+ privateKeyEncoding: { type: "pkcs8", format: "der" }
13
+ });
14
+ return {
15
+ publicKey: Buffer.from(publicKey),
16
+ privateKey: Buffer.from(privateKey)
17
+ };
18
+ }
19
+ function saveKeyPair(configDir, keys) {
20
+ const privatePath = join(configDir, "private.key");
21
+ const publicPath = join(configDir, "public.key");
22
+ writeFileSync(privatePath, keys.privateKey);
23
+ chmodSync(privatePath, 384);
24
+ writeFileSync(publicPath, keys.publicKey);
25
+ }
26
+ function loadKeyPair(configDir) {
27
+ const privatePath = join(configDir, "private.key");
28
+ const publicPath = join(configDir, "public.key");
29
+ if (!existsSync(privatePath) || !existsSync(publicPath)) {
30
+ throw new AgentBnBError("Keypair not found. Run `agentbnb init` to generate one.", "KEYPAIR_NOT_FOUND");
31
+ }
32
+ return {
33
+ publicKey: readFileSync(publicPath),
34
+ privateKey: readFileSync(privatePath)
35
+ };
36
+ }
37
+ function canonicalJson(data) {
38
+ return JSON.stringify(data, Object.keys(data).sort());
39
+ }
40
+ function signEscrowReceipt(data, privateKey) {
41
+ const message = Buffer.from(canonicalJson(data), "utf-8");
42
+ const keyObject = createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" });
43
+ const signature = sign(null, message, keyObject);
44
+ return signature.toString("base64url");
45
+ }
46
+ function verifyEscrowReceipt(data, signature, publicKey) {
47
+ try {
48
+ const message = Buffer.from(canonicalJson(data), "utf-8");
49
+ const keyObject = createPublicKey({ key: publicKey, format: "der", type: "spki" });
50
+ const sigBuffer = Buffer.from(signature, "base64url");
51
+ return verify(null, message, keyObject, sigBuffer);
52
+ } catch {
53
+ return false;
54
+ }
55
+ }
56
+
57
+ export {
58
+ generateKeyPair,
59
+ saveKeyPair,
60
+ loadKeyPair,
61
+ signEscrowReceipt,
62
+ verifyEscrowReceipt
63
+ };
@@ -2,58 +2,6 @@ import {
2
2
  AgentBnBError
3
3
  } from "./chunk-XA63SD4T.js";
4
4
 
5
- // src/credit/signing.ts
6
- import { generateKeyPairSync, sign, verify, createPublicKey, createPrivateKey } from "crypto";
7
- import { writeFileSync, readFileSync, existsSync, chmodSync } from "fs";
8
- import { join } from "path";
9
- function generateKeyPair() {
10
- const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
11
- publicKeyEncoding: { type: "spki", format: "der" },
12
- privateKeyEncoding: { type: "pkcs8", format: "der" }
13
- });
14
- return {
15
- publicKey: Buffer.from(publicKey),
16
- privateKey: Buffer.from(privateKey)
17
- };
18
- }
19
- function saveKeyPair(configDir, keys) {
20
- const privatePath = join(configDir, "private.key");
21
- const publicPath = join(configDir, "public.key");
22
- writeFileSync(privatePath, keys.privateKey);
23
- chmodSync(privatePath, 384);
24
- writeFileSync(publicPath, keys.publicKey);
25
- }
26
- function loadKeyPair(configDir) {
27
- const privatePath = join(configDir, "private.key");
28
- const publicPath = join(configDir, "public.key");
29
- if (!existsSync(privatePath) || !existsSync(publicPath)) {
30
- throw new AgentBnBError("Keypair not found. Run `agentbnb init` to generate one.", "KEYPAIR_NOT_FOUND");
31
- }
32
- return {
33
- publicKey: readFileSync(publicPath),
34
- privateKey: readFileSync(privatePath)
35
- };
36
- }
37
- function canonicalJson(data) {
38
- return JSON.stringify(data, Object.keys(data).sort());
39
- }
40
- function signEscrowReceipt(data, privateKey) {
41
- const message = Buffer.from(canonicalJson(data), "utf-8");
42
- const keyObject = createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" });
43
- const signature = sign(null, message, keyObject);
44
- return signature.toString("base64url");
45
- }
46
- function verifyEscrowReceipt(data, signature, publicKey) {
47
- try {
48
- const message = Buffer.from(canonicalJson(data), "utf-8");
49
- const keyObject = createPublicKey({ key: publicKey, format: "der", type: "spki" });
50
- const sigBuffer = Buffer.from(signature, "base64url");
51
- return verify(null, message, keyObject, sigBuffer);
52
- } catch {
53
- return false;
54
- }
55
- }
56
-
57
5
  // src/credit/escrow.ts
58
6
  import { randomUUID } from "crypto";
59
7
  function holdEscrow(db, owner, amount, cardId) {
@@ -232,11 +180,6 @@ function recordEarning(db, owner, amount, _cardId, receiptNonce) {
232
180
  }
233
181
 
234
182
  export {
235
- generateKeyPair,
236
- saveKeyPair,
237
- loadKeyPair,
238
- signEscrowReceipt,
239
- verifyEscrowReceipt,
240
183
  holdEscrow,
241
184
  settleEscrow,
242
185
  releaseEscrow,
@@ -0,0 +1,83 @@
1
+ import {
2
+ signEscrowReceipt
3
+ } from "./chunk-QO67IGCW.js";
4
+ import {
5
+ AgentBnBError
6
+ } from "./chunk-XA63SD4T.js";
7
+
8
+ // src/gateway/client.ts
9
+ import { randomUUID } from "crypto";
10
+ async function requestCapability(opts) {
11
+ const { gatewayUrl, token, cardId, params = {}, timeoutMs = 3e4, escrowReceipt, identity } = opts;
12
+ const id = randomUUID();
13
+ const payload = {
14
+ jsonrpc: "2.0",
15
+ id,
16
+ method: "capability.execute",
17
+ params: {
18
+ card_id: cardId,
19
+ ...params,
20
+ ...escrowReceipt ? { escrow_receipt: escrowReceipt } : {}
21
+ }
22
+ };
23
+ const headers = { "Content-Type": "application/json" };
24
+ if (identity) {
25
+ const signature = signEscrowReceipt(payload, identity.privateKey);
26
+ headers["X-Agent-Id"] = identity.agentId;
27
+ headers["X-Agent-Public-Key"] = identity.publicKey;
28
+ headers["X-Agent-Signature"] = signature;
29
+ } else if (token) {
30
+ headers["Authorization"] = `Bearer ${token}`;
31
+ }
32
+ const controller = new AbortController();
33
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
34
+ let response;
35
+ try {
36
+ response = await fetch(`${gatewayUrl}/rpc`, {
37
+ method: "POST",
38
+ headers,
39
+ body: JSON.stringify(payload),
40
+ signal: controller.signal
41
+ });
42
+ } catch (err) {
43
+ clearTimeout(timer);
44
+ const isTimeout = err instanceof Error && err.name === "AbortError";
45
+ throw new AgentBnBError(
46
+ isTimeout ? "Request timed out" : `Network error: ${String(err)}`,
47
+ isTimeout ? "TIMEOUT" : "NETWORK_ERROR"
48
+ );
49
+ } finally {
50
+ clearTimeout(timer);
51
+ }
52
+ const body = await response.json();
53
+ if (body.error) {
54
+ throw new AgentBnBError(body.error.message, `RPC_ERROR_${body.error.code}`);
55
+ }
56
+ return body.result;
57
+ }
58
+ async function requestViaRelay(relay, opts) {
59
+ try {
60
+ return await relay.request({
61
+ targetOwner: opts.targetOwner,
62
+ cardId: opts.cardId,
63
+ skillId: opts.skillId,
64
+ params: opts.params ?? {},
65
+ escrowReceipt: opts.escrowReceipt,
66
+ timeoutMs: opts.timeoutMs
67
+ });
68
+ } catch (err) {
69
+ const message = err instanceof Error ? err.message : String(err);
70
+ if (message.includes("timeout")) {
71
+ throw new AgentBnBError(message, "TIMEOUT");
72
+ }
73
+ if (message.includes("offline")) {
74
+ throw new AgentBnBError(message, "AGENT_OFFLINE");
75
+ }
76
+ throw new AgentBnBError(message, "RELAY_ERROR");
77
+ }
78
+ }
79
+
80
+ export {
81
+ requestCapability,
82
+ requestViaRelay
83
+ };
@@ -1,3 +1,6 @@
1
+ import {
2
+ requestCapability
3
+ } from "./chunk-RSX4SCPN.js";
1
4
  import {
2
5
  findPeer
3
6
  } from "./chunk-BEI5MTNZ.js";
@@ -5,9 +8,8 @@ import {
5
8
  getBalance,
6
9
  holdEscrow,
7
10
  releaseEscrow,
8
- settleEscrow,
9
- signEscrowReceipt
10
- } from "./chunk-QHQPXO67.js";
11
+ settleEscrow
12
+ } from "./chunk-QVV2P3FN.js";
11
13
  import {
12
14
  AgentBnBError
13
15
  } from "./chunk-XA63SD4T.js";
@@ -148,61 +150,10 @@ function filterCards(db, filters) {
148
150
  return rows.map((row) => JSON.parse(row.data));
149
151
  }
150
152
 
151
- // src/gateway/client.ts
152
- import { randomUUID as randomUUID2 } from "crypto";
153
- async function requestCapability(opts) {
154
- const { gatewayUrl, token, cardId, params = {}, timeoutMs = 3e4, escrowReceipt, identity } = opts;
155
- const id = randomUUID2();
156
- const payload = {
157
- jsonrpc: "2.0",
158
- id,
159
- method: "capability.execute",
160
- params: {
161
- card_id: cardId,
162
- ...params,
163
- ...escrowReceipt ? { escrow_receipt: escrowReceipt } : {}
164
- }
165
- };
166
- const headers = { "Content-Type": "application/json" };
167
- if (identity) {
168
- const signature = signEscrowReceipt(payload, identity.privateKey);
169
- headers["X-Agent-Id"] = identity.agentId;
170
- headers["X-Agent-Public-Key"] = identity.publicKey;
171
- headers["X-Agent-Signature"] = signature;
172
- } else if (token) {
173
- headers["Authorization"] = `Bearer ${token}`;
174
- }
175
- const controller = new AbortController();
176
- const timer = setTimeout(() => controller.abort(), timeoutMs);
177
- let response;
178
- try {
179
- response = await fetch(`${gatewayUrl}/rpc`, {
180
- method: "POST",
181
- headers,
182
- body: JSON.stringify(payload),
183
- signal: controller.signal
184
- });
185
- } catch (err) {
186
- clearTimeout(timer);
187
- const isTimeout = err instanceof Error && err.name === "AbortError";
188
- throw new AgentBnBError(
189
- isTimeout ? "Request timed out" : `Network error: ${String(err)}`,
190
- isTimeout ? "TIMEOUT" : "NETWORK_ERROR"
191
- );
192
- } finally {
193
- clearTimeout(timer);
194
- }
195
- const body = await response.json();
196
- if (body.error) {
197
- throw new AgentBnBError(body.error.message, `RPC_ERROR_${body.error.code}`);
198
- }
199
- return body.result;
200
- }
201
-
202
153
  // src/autonomy/pending-requests.ts
203
- import { randomUUID as randomUUID3 } from "crypto";
154
+ import { randomUUID as randomUUID2 } from "crypto";
204
155
  function createPendingRequest(db, opts) {
205
- const id = randomUUID3();
156
+ const id = randomUUID2();
206
157
  const now = (/* @__PURE__ */ new Date()).toISOString();
207
158
  const paramsJson = opts.params !== void 0 ? JSON.stringify(opts.params) : null;
208
159
  db.prepare(`
@@ -484,7 +435,6 @@ export {
484
435
  BudgetManager,
485
436
  searchCards,
486
437
  filterCards,
487
- requestCapability,
488
438
  listPendingRequests,
489
439
  resolvePendingRequest,
490
440
  scorePeers,
package/dist/cli/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  executeCapabilityRequest,
4
4
  releaseRequesterEscrow,
5
5
  settleRequesterEscrow
6
- } from "../chunk-QSPWE5AE.js";
6
+ } from "../chunk-CUVIWPQO.js";
7
7
  import {
8
8
  RelayMessageSchema
9
9
  } from "../chunk-3Y36WQDV.js";
@@ -17,10 +17,12 @@ import {
17
17
  insertAuditEvent,
18
18
  interpolateObject,
19
19
  listPendingRequests,
20
- requestCapability,
21
20
  resolvePendingRequest,
22
21
  searchCards
23
- } from "../chunk-IZZ4FP45.js";
22
+ } from "../chunk-UJWYE7VL.js";
23
+ import {
24
+ requestCapability
25
+ } from "../chunk-RSX4SCPN.js";
24
26
  import {
25
27
  findPeer,
26
28
  getConfigDir,
@@ -45,17 +47,19 @@ import {
45
47
  } from "../chunk-UOGDK2S2.js";
46
48
  import {
47
49
  bootstrapAgent,
48
- generateKeyPair,
49
50
  getBalance,
50
51
  getTransactions,
51
52
  holdEscrow,
52
- loadKeyPair,
53
53
  openCreditDb,
54
- releaseEscrow,
54
+ releaseEscrow
55
+ } from "../chunk-QVV2P3FN.js";
56
+ import {
57
+ generateKeyPair,
58
+ loadKeyPair,
55
59
  saveKeyPair,
56
60
  signEscrowReceipt,
57
61
  verifyEscrowReceipt
58
- } from "../chunk-QHQPXO67.js";
62
+ } from "../chunk-QO67IGCW.js";
59
63
  import {
60
64
  AgentBnBError,
61
65
  AnyCardSchema,
@@ -1526,7 +1530,7 @@ var AgentRuntime = class {
1526
1530
  }
1527
1531
  const modes = /* @__PURE__ */ new Map();
1528
1532
  if (this.conductorEnabled) {
1529
- const { ConductorMode } = await import("../conductor-mode-IO45PWMI.js");
1533
+ const { ConductorMode } = await import("../conductor-mode-XU7ONJWC.js");
1530
1534
  const { registerConductorCard, CONDUCTOR_OWNER } = await import("../card-IE5UV5QX.js");
1531
1535
  const { loadPeers: loadPeers2 } = await import("../peers-G36URZYB.js");
1532
1536
  registerConductorCard(this.registryDb);
@@ -3466,6 +3470,7 @@ program.command("request [card-id]").description("Request a capability from anot
3466
3470
  let gatewayUrl;
3467
3471
  let token;
3468
3472
  let isRemoteRequest = false;
3473
+ let targetOwner;
3469
3474
  const identityAuth = loadIdentityAuth(config.owner);
3470
3475
  if (opts.peer) {
3471
3476
  const peer = findPeer(opts.peer);
@@ -3476,6 +3481,7 @@ program.command("request [card-id]").description("Request a capability from anot
3476
3481
  gatewayUrl = peer.url;
3477
3482
  token = peer.token;
3478
3483
  isRemoteRequest = true;
3484
+ targetOwner = opts.peer;
3479
3485
  } else {
3480
3486
  const db = openDatabase(config.db_path);
3481
3487
  let localCard;
@@ -3507,16 +3513,24 @@ program.command("request [card-id]").description("Request a capability from anot
3507
3513
  console.error(`Error: cannot reach registry: ${err.message}`);
3508
3514
  process.exit(1);
3509
3515
  }
3510
- if (!remoteCard.gateway_url || typeof remoteCard.gateway_url !== "string") {
3511
- console.error("Error: remote card has no gateway_url. The provider needs to re-publish with `agentbnb sync`.");
3516
+ targetOwner = remoteCard.owner ?? remoteCard.agent_name;
3517
+ if (remoteCard.gateway_url && typeof remoteCard.gateway_url === "string") {
3518
+ gatewayUrl = remoteCard.gateway_url;
3519
+ } else if (targetOwner && config.registry) {
3520
+ gatewayUrl = "";
3521
+ } else {
3522
+ console.error("Error: remote card has no gateway_url and no relay available. The provider needs to re-publish with `agentbnb sync`.");
3512
3523
  process.exit(1);
3513
3524
  }
3514
- gatewayUrl = remoteCard.gateway_url;
3515
3525
  token = "";
3516
3526
  isRemoteRequest = true;
3517
3527
  if (!opts.json) {
3518
3528
  const displayName = remoteCard.name ?? remoteCard.agent_name ?? cardId;
3519
- console.log(`Found remote card: ${displayName} @ ${gatewayUrl}`);
3529
+ if (gatewayUrl) {
3530
+ console.log(`Found remote card: ${displayName} @ ${gatewayUrl}`);
3531
+ } else {
3532
+ console.log(`Found remote card: ${displayName} (relay-only)`);
3533
+ }
3520
3534
  }
3521
3535
  }
3522
3536
  }
@@ -3560,48 +3574,97 @@ program.command("request [card-id]").description("Request a capability from anot
3560
3574
  process.exit(1);
3561
3575
  }
3562
3576
  }
3563
- try {
3564
- const result = await requestCapability({
3565
- gatewayUrl,
3566
- token,
3567
- cardId,
3568
- params: { ...params, ...opts.skill ? { skill_id: opts.skill } : {} },
3569
- escrowReceipt,
3570
- identity: identityAuth
3571
- });
3577
+ const settleEscrow = () => {
3572
3578
  if (useReceipt && escrowId) {
3573
3579
  const configDir = getConfigDir();
3574
3580
  const creditDb = openCreditDb(join4(configDir, "credit.db"));
3575
3581
  creditDb.pragma("busy_timeout = 5000");
3576
3582
  try {
3577
3583
  settleRequesterEscrow(creditDb, escrowId);
3578
- if (!opts.json) {
3579
- console.log(`Escrow settled: ${opts.cost} credits deducted.`);
3580
- }
3584
+ if (!opts.json) console.log(`Escrow settled: ${opts.cost} credits deducted.`);
3581
3585
  } finally {
3582
3586
  creditDb.close();
3583
3587
  }
3584
3588
  }
3585
- if (opts.json) {
3586
- console.log(JSON.stringify({ success: true, result }, null, 2));
3587
- } else {
3588
- console.log("Result:");
3589
- console.log(typeof result === "string" ? result : JSON.stringify(result, null, 2));
3590
- }
3591
- } catch (err) {
3589
+ };
3590
+ const releaseEscrow2 = () => {
3592
3591
  if (useReceipt && escrowId) {
3593
3592
  const configDir = getConfigDir();
3594
3593
  const creditDb = openCreditDb(join4(configDir, "credit.db"));
3595
3594
  creditDb.pragma("busy_timeout = 5000");
3596
3595
  try {
3597
3596
  releaseRequesterEscrow(creditDb, escrowId);
3598
- if (!opts.json) {
3599
- console.log("Escrow released: credits refunded.");
3600
- }
3597
+ if (!opts.json) console.log("Escrow released: credits refunded.");
3601
3598
  } finally {
3602
3599
  creditDb.close();
3603
3600
  }
3604
3601
  }
3602
+ };
3603
+ const printResult = (result) => {
3604
+ if (opts.json) {
3605
+ console.log(JSON.stringify({ success: true, result }, null, 2));
3606
+ } else {
3607
+ console.log("Result:");
3608
+ console.log(typeof result === "string" ? result : JSON.stringify(result, null, 2));
3609
+ }
3610
+ };
3611
+ const isNetworkError = (err) => {
3612
+ const msg = err instanceof Error ? err.message : String(err);
3613
+ return msg.includes("NETWORK_ERROR") || msg.includes("ECONNREFUSED") || msg.includes("fetch failed") || msg.includes("Network error");
3614
+ };
3615
+ const tryViaRelay = async () => {
3616
+ const { RelayClient } = await import("../websocket-client-5TIQDYQ4.js");
3617
+ const { requestViaRelay } = await import("../client-IOTK6GOS.js");
3618
+ const tempRelay = new RelayClient({
3619
+ registryUrl: config.registry,
3620
+ owner: config.owner,
3621
+ token: config.token,
3622
+ card: { id: config.owner, owner: config.owner },
3623
+ onRequest: async () => ({ error: { code: -32601, message: "Not serving" } }),
3624
+ silent: true
3625
+ });
3626
+ try {
3627
+ await tempRelay.connect();
3628
+ const result = await requestViaRelay(tempRelay, {
3629
+ targetOwner,
3630
+ cardId,
3631
+ skillId: opts.skill,
3632
+ params: { ...params, ...opts.skill ? { skill_id: opts.skill } : {} },
3633
+ escrowReceipt
3634
+ });
3635
+ return result;
3636
+ } finally {
3637
+ tempRelay.disconnect();
3638
+ }
3639
+ };
3640
+ try {
3641
+ let result;
3642
+ if (!gatewayUrl && isRemoteRequest && config.registry && targetOwner) {
3643
+ if (!opts.json) console.log("No gateway URL, requesting via relay...");
3644
+ result = await tryViaRelay();
3645
+ } else {
3646
+ try {
3647
+ result = await requestCapability({
3648
+ gatewayUrl,
3649
+ token,
3650
+ cardId,
3651
+ params: { ...params, ...opts.skill ? { skill_id: opts.skill } : {} },
3652
+ escrowReceipt,
3653
+ identity: identityAuth
3654
+ });
3655
+ } catch (directErr) {
3656
+ if (isNetworkError(directErr) && isRemoteRequest && config.registry && targetOwner) {
3657
+ if (!opts.json) console.log("Direct connection failed, trying relay...");
3658
+ result = await tryViaRelay();
3659
+ } else {
3660
+ throw directErr;
3661
+ }
3662
+ }
3663
+ }
3664
+ settleEscrow();
3665
+ printResult(result);
3666
+ } catch (err) {
3667
+ releaseEscrow2();
3605
3668
  const msg = err instanceof Error ? err.message : String(err);
3606
3669
  if (opts.json) {
3607
3670
  console.log(JSON.stringify({ success: false, error: msg }, null, 2));
@@ -3651,7 +3714,7 @@ Active Escrows (${heldEscrows.length}):`);
3651
3714
  }
3652
3715
  }
3653
3716
  });
3654
- program.command("serve").description("Start the AgentBnB gateway server").option("--port <port>", "Port to listen on (overrides config)").option("--handler-url <url>", "Local capability handler URL", "http://localhost:8080").option("--skills-yaml <path>", "Path to skills.yaml (default: ~/.agentbnb/skills.yaml)").option("--registry-port <port>", "Public registry API port (0 to disable)", "7701").option("--registry <url>", "Connect to remote registry via WebSocket relay (e.g., hub.agentbnb.dev)").option("--conductor", "Enable Conductor orchestration mode").option("--announce", "Announce this gateway on the local network via mDNS").action(async (opts) => {
3717
+ program.command("serve").description("Start the AgentBnB gateway server").option("--port <port>", "Port to listen on (overrides config)").option("--handler-url <url>", "Local capability handler URL", "http://localhost:8080").option("--skills-yaml <path>", "Path to skills.yaml (default: ~/.agentbnb/skills.yaml)").option("--registry-port <port>", "Public registry API port (0 to disable)", "7701").option("--registry <url>", "Connect to remote registry via WebSocket relay (e.g., hub.agentbnb.dev)").option("--conductor", "Enable Conductor orchestration mode").option("--announce", "Announce this gateway on the local network via mDNS").option("--no-relay", "Do not auto-connect to remote registry relay").action(async (opts) => {
3655
3718
  const config = loadConfig();
3656
3719
  if (!config) {
3657
3720
  console.error("Error: not initialized. Run `agentbnb init` first.");
@@ -3736,9 +3799,10 @@ program.command("serve").description("Start the AgentBnB gateway server").option
3736
3799
  console.log(`WebSocket relay active on /ws`);
3737
3800
  }
3738
3801
  }
3739
- if (opts.registry) {
3802
+ const relayUrl = opts.registry ?? config.registry;
3803
+ if (relayUrl && opts.relay !== false) {
3740
3804
  const { RelayClient } = await import("../websocket-client-5TIQDYQ4.js");
3741
- const { executeCapabilityRequest: executeCapabilityRequest2 } = await import("../execute-SWWEHV2K.js");
3805
+ const { executeCapabilityRequest: executeCapabilityRequest2 } = await import("../execute-GDGBU6DJ.js");
3742
3806
  const cards = listCards(runtime.registryDb, config.owner);
3743
3807
  const card = cards[0] ?? {
3744
3808
  id: config.owner,
@@ -3753,7 +3817,7 @@ program.command("serve").description("Start the AgentBnB gateway server").option
3753
3817
  availability: { online: true }
3754
3818
  };
3755
3819
  relayClient = new RelayClient({
3756
- registryUrl: opts.registry,
3820
+ registryUrl: relayUrl,
3757
3821
  owner: config.owner,
3758
3822
  token: config.token,
3759
3823
  card,
@@ -3777,9 +3841,9 @@ program.command("serve").description("Start the AgentBnB gateway server").option
3777
3841
  });
3778
3842
  try {
3779
3843
  await relayClient.connect();
3780
- console.log(`Connected to registry: ${opts.registry}`);
3844
+ console.log(`Connected to registry: ${relayUrl}${opts.registry ? "" : " (auto)"}`);
3781
3845
  } catch (err) {
3782
- console.warn(`Warning: could not connect to registry ${opts.registry}: ${err instanceof Error ? err.message : err}`);
3846
+ console.warn(`Warning: could not connect to registry ${relayUrl}: ${err instanceof Error ? err.message : err}`);
3783
3847
  console.warn("Will auto-reconnect in background...");
3784
3848
  }
3785
3849
  }
@@ -4007,7 +4071,7 @@ openclaw.command("rules").description("Print HEARTBEAT.md rules block (or inject
4007
4071
  }
4008
4072
  });
4009
4073
  program.command("conduct <task>").description("Orchestrate a complex task across the AgentBnB network").option("--plan-only", "Show execution plan without executing").option("--max-budget <credits>", "Maximum credits to spend", "100").option("--json", "Output as JSON").action(async (task, opts) => {
4010
- const { conductAction } = await import("../conduct-IEQ567ET.js");
4074
+ const { conductAction } = await import("../conduct-IQYAT6ZU.js");
4011
4075
  const result = await conductAction(task, opts);
4012
4076
  if (opts.json) {
4013
4077
  console.log(JSON.stringify(result, null, 2));
@@ -0,0 +1,10 @@
1
+ import {
2
+ requestCapability,
3
+ requestViaRelay
4
+ } from "./chunk-RSX4SCPN.js";
5
+ import "./chunk-QO67IGCW.js";
6
+ import "./chunk-XA63SD4T.js";
7
+ export {
8
+ requestCapability,
9
+ requestViaRelay
10
+ };
@@ -4,10 +4,11 @@ import {
4
4
  decompose,
5
5
  matchSubTasks,
6
6
  orchestrate
7
- } from "./chunk-7OACGAFD.js";
7
+ } from "./chunk-HEVXCYCY.js";
8
8
  import {
9
9
  BudgetManager
10
- } from "./chunk-IZZ4FP45.js";
10
+ } from "./chunk-UJWYE7VL.js";
11
+ import "./chunk-RSX4SCPN.js";
11
12
  import {
12
13
  loadConfig,
13
14
  loadPeers
@@ -17,7 +18,8 @@ import {
17
18
  } from "./chunk-UOGDK2S2.js";
18
19
  import {
19
20
  openCreditDb
20
- } from "./chunk-QHQPXO67.js";
21
+ } from "./chunk-QVV2P3FN.js";
22
+ import "./chunk-QO67IGCW.js";
21
23
  import "./chunk-XA63SD4T.js";
22
24
 
23
25
  // src/cli/conduct.ts
@@ -3,12 +3,14 @@ import {
3
3
  decompose,
4
4
  matchSubTasks,
5
5
  orchestrate
6
- } from "./chunk-7OACGAFD.js";
6
+ } from "./chunk-HEVXCYCY.js";
7
7
  import {
8
8
  BudgetManager
9
- } from "./chunk-IZZ4FP45.js";
9
+ } from "./chunk-UJWYE7VL.js";
10
+ import "./chunk-RSX4SCPN.js";
10
11
  import "./chunk-BEI5MTNZ.js";
11
- import "./chunk-QHQPXO67.js";
12
+ import "./chunk-QVV2P3FN.js";
13
+ import "./chunk-QO67IGCW.js";
12
14
  import "./chunk-XA63SD4T.js";
13
15
 
14
16
  // src/conductor/conductor-mode.ts
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  executeCapabilityRequest
3
- } from "./chunk-QSPWE5AE.js";
3
+ } from "./chunk-CUVIWPQO.js";
4
4
  import "./chunk-UOGDK2S2.js";
5
- import "./chunk-QHQPXO67.js";
5
+ import "./chunk-QVV2P3FN.js";
6
+ import "./chunk-QO67IGCW.js";
6
7
  import "./chunk-XA63SD4T.js";
7
8
  export {
8
9
  executeCapabilityRequest
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentbnb",
3
- "version": "3.1.6",
3
+ "version": "4.0.0",
4
4
  "description": "P2P Agent Capability Sharing Protocol — Airbnb for AI agent pipelines",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -68,7 +68,10 @@
68
68
  "dependencies": {
69
69
  "@fastify/cors": "^11.2.0",
70
70
  "@fastify/static": "^9.0.0",
71
+ "@fastify/swagger": "^9.7.0",
72
+ "@fastify/swagger-ui": "^5.2.5",
71
73
  "@fastify/websocket": "^11.2.0",
74
+ "@modelcontextprotocol/sdk": "^1.27.1",
72
75
  "better-sqlite3": "^11.6.0",
73
76
  "bonjour-service": "^1.3.0",
74
77
  "commander": "^12.1.0",