agentbnb 3.1.6 → 4.0.1
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 +117 -86
- package/dist/{card-IE5UV5QX.js → card-4XH4AOTE.js} +11 -4
- package/dist/chunk-3MJT4PZG.js +50 -0
- package/dist/{conduct-IEQ567ET.js → chunk-3UKAVIMC.js} +70 -31
- package/dist/chunk-5AH3CMOX.js +62 -0
- package/dist/{chunk-IZZ4FP45.js → chunk-6K5WUVF3.js} +33 -166
- package/dist/chunk-75OC6E4F.js +33 -0
- package/dist/chunk-DVAS2443.js +63 -0
- package/dist/{chunk-XA63SD4T.js → chunk-FNKBHBYK.js} +3 -0
- package/dist/{websocket-client-5TIQDYQ4.js → chunk-JOY533UH.js} +38 -4
- package/dist/chunk-KJG2UJV5.js +83 -0
- package/dist/chunk-M3G5NR2Z.js +90 -0
- package/dist/{chunk-7OACGAFD.js → chunk-MQKYGY5I.js} +63 -24
- package/dist/chunk-ODBGCCEH.js +358 -0
- package/dist/{chunk-QSPWE5AE.js → chunk-Q7HRI666.js} +9 -6
- package/dist/chunk-QJEOCKVF.js +148 -0
- package/dist/{chunk-3Y36WQDV.js → chunk-QT7TEVNV.js} +14 -2
- package/dist/{chunk-UOGDK2S2.js → chunk-TLU7ALCZ.js} +1 -1
- package/dist/{chunk-QHQPXO67.js → chunk-XQHN6ITI.js} +1 -58
- package/dist/cli/index.js +2734 -850
- package/dist/client-BTPIFY7E.js +10 -0
- package/dist/conduct-CW62HBPT.js +52 -0
- package/dist/conduct-FXLVGKD5.js +19 -0
- package/dist/{conductor-mode-IO45PWMI.js → conductor-mode-3JS4VWCR.js} +16 -7
- package/dist/execute-EXOITLHN.js +10 -0
- package/dist/index.d.ts +1005 -916
- package/dist/index.js +516 -120
- package/dist/{peers-G36URZYB.js → peers-K7FSHPN3.js} +2 -1
- package/dist/request-CNZ3XIVX.js +196 -0
- package/dist/serve-skill-SUOGUM7N.js +104 -0
- package/dist/server-2LWHL24P.js +295 -0
- package/dist/types-FGBUZ3QV.js +18 -0
- package/dist/websocket-client-6IIDGXKB.js +7 -0
- package/package.json +4 -1
- package/dist/chunk-BEI5MTNZ.js +0 -91
- package/dist/cli/index.d.ts +0 -1
- package/dist/execute-SWWEHV2K.js +0 -9
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
-
interpolateObject
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
interpolateObject
|
|
3
|
+
} from "./chunk-3MJT4PZG.js";
|
|
4
|
+
import {
|
|
5
|
+
scorePeers
|
|
6
|
+
} from "./chunk-6K5WUVF3.js";
|
|
7
|
+
import {
|
|
8
|
+
fetchRemoteCards,
|
|
5
9
|
searchCards
|
|
6
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-QJEOCKVF.js";
|
|
11
|
+
import {
|
|
12
|
+
requestCapability
|
|
13
|
+
} from "./chunk-KJG2UJV5.js";
|
|
7
14
|
|
|
8
15
|
// src/conductor/task-decomposer.ts
|
|
9
16
|
import { randomUUID } from "crypto";
|
|
@@ -116,10 +123,17 @@ function decompose(task, _availableCapabilities) {
|
|
|
116
123
|
|
|
117
124
|
// src/conductor/capability-matcher.ts
|
|
118
125
|
var MAX_ALTERNATIVES = 2;
|
|
119
|
-
function matchSubTasks(opts) {
|
|
120
|
-
const { db, subtasks, conductorOwner } = opts;
|
|
121
|
-
return subtasks.map((subtask) => {
|
|
122
|
-
|
|
126
|
+
async function matchSubTasks(opts) {
|
|
127
|
+
const { db, subtasks, conductorOwner, registryUrl } = opts;
|
|
128
|
+
return Promise.all(subtasks.map(async (subtask) => {
|
|
129
|
+
let cards = searchCards(db, subtask.required_capability, { online: true });
|
|
130
|
+
if (cards.length === 0 && registryUrl) {
|
|
131
|
+
try {
|
|
132
|
+
cards = await fetchRemoteCards(registryUrl, { q: subtask.required_capability, online: true });
|
|
133
|
+
} catch {
|
|
134
|
+
cards = [];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
123
137
|
const candidates = [];
|
|
124
138
|
for (const card of cards) {
|
|
125
139
|
const cardAsV2 = card;
|
|
@@ -161,11 +175,12 @@ function matchSubTasks(opts) {
|
|
|
161
175
|
subtask_id: subtask.id,
|
|
162
176
|
selected_agent: top.card.owner,
|
|
163
177
|
selected_skill: top.skillId ?? "",
|
|
178
|
+
selected_card_id: top.card.id,
|
|
164
179
|
score: top.rawScore,
|
|
165
180
|
credits: top.cost,
|
|
166
181
|
alternatives
|
|
167
182
|
};
|
|
168
|
-
});
|
|
183
|
+
}));
|
|
169
184
|
}
|
|
170
185
|
|
|
171
186
|
// src/conductor/budget-controller.ts
|
|
@@ -260,7 +275,7 @@ function computeWaves(subtasks) {
|
|
|
260
275
|
return waves;
|
|
261
276
|
}
|
|
262
277
|
async function orchestrate(opts) {
|
|
263
|
-
const { subtasks, matches, gatewayToken, resolveAgentUrl, timeoutMs =
|
|
278
|
+
const { subtasks, matches, gatewayToken, resolveAgentUrl, timeoutMs = 3e5, maxBudget, relayClient, requesterOwner } = opts;
|
|
264
279
|
const startTime = Date.now();
|
|
265
280
|
if (subtasks.length === 0) {
|
|
266
281
|
return {
|
|
@@ -311,26 +326,50 @@ async function orchestrate(opts) {
|
|
|
311
326
|
);
|
|
312
327
|
const primary = resolveAgentUrl(m.selected_agent);
|
|
313
328
|
try {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
329
|
+
let res;
|
|
330
|
+
if (primary.url.startsWith("relay://") && relayClient) {
|
|
331
|
+
const targetOwner = primary.url.replace("relay://", "");
|
|
332
|
+
res = await relayClient.request({
|
|
333
|
+
targetOwner,
|
|
334
|
+
cardId: primary.cardId,
|
|
335
|
+
params: interpolatedParams,
|
|
336
|
+
requester: requesterOwner,
|
|
337
|
+
timeoutMs
|
|
338
|
+
});
|
|
339
|
+
} else {
|
|
340
|
+
res = await requestCapability({
|
|
341
|
+
gatewayUrl: primary.url,
|
|
342
|
+
token: gatewayToken,
|
|
343
|
+
cardId: primary.cardId,
|
|
344
|
+
params: interpolatedParams,
|
|
345
|
+
timeoutMs
|
|
346
|
+
});
|
|
347
|
+
}
|
|
321
348
|
return { taskId, result: res, credits: m.credits };
|
|
322
349
|
} catch (primaryErr) {
|
|
323
350
|
if (m.alternatives.length > 0) {
|
|
324
351
|
const alt = m.alternatives[0];
|
|
325
352
|
const altAgent = resolveAgentUrl(alt.agent);
|
|
326
353
|
try {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
354
|
+
let altRes;
|
|
355
|
+
if (altAgent.url.startsWith("relay://") && relayClient) {
|
|
356
|
+
const targetOwner = altAgent.url.replace("relay://", "");
|
|
357
|
+
altRes = await relayClient.request({
|
|
358
|
+
targetOwner,
|
|
359
|
+
cardId: altAgent.cardId,
|
|
360
|
+
params: interpolatedParams,
|
|
361
|
+
requester: requesterOwner,
|
|
362
|
+
timeoutMs
|
|
363
|
+
});
|
|
364
|
+
} else {
|
|
365
|
+
altRes = await requestCapability({
|
|
366
|
+
gatewayUrl: altAgent.url,
|
|
367
|
+
token: gatewayToken,
|
|
368
|
+
cardId: altAgent.cardId,
|
|
369
|
+
params: interpolatedParams,
|
|
370
|
+
timeoutMs
|
|
371
|
+
});
|
|
372
|
+
}
|
|
334
373
|
return { taskId, result: altRes, credits: alt.credits };
|
|
335
374
|
} catch (altErr) {
|
|
336
375
|
throw new Error(
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bootstrapAgent,
|
|
3
|
+
getBalance,
|
|
4
|
+
getTransactions,
|
|
5
|
+
holdEscrow,
|
|
6
|
+
openCreditDb,
|
|
7
|
+
releaseEscrow,
|
|
8
|
+
settleEscrow
|
|
9
|
+
} from "./chunk-XQHN6ITI.js";
|
|
10
|
+
import {
|
|
11
|
+
signEscrowReceipt,
|
|
12
|
+
verifyEscrowReceipt
|
|
13
|
+
} from "./chunk-DVAS2443.js";
|
|
14
|
+
import {
|
|
15
|
+
AgentBnBError
|
|
16
|
+
} from "./chunk-FNKBHBYK.js";
|
|
17
|
+
|
|
18
|
+
// src/credit/local-credit-ledger.ts
|
|
19
|
+
var LocalCreditLedger = class {
|
|
20
|
+
constructor(db) {
|
|
21
|
+
this.db = db;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Holds credits in escrow during capability execution.
|
|
25
|
+
*
|
|
26
|
+
* @param owner - Agent identifier (requester).
|
|
27
|
+
* @param amount - Number of credits to hold.
|
|
28
|
+
* @param cardId - Capability Card ID being requested.
|
|
29
|
+
* @returns EscrowResult with the new escrowId.
|
|
30
|
+
* @throws {AgentBnBError} with code 'INSUFFICIENT_CREDITS' if balance < amount.
|
|
31
|
+
*/
|
|
32
|
+
async hold(owner, amount, cardId) {
|
|
33
|
+
const escrowId = holdEscrow(this.db, owner, amount, cardId);
|
|
34
|
+
return { escrowId };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Settles an escrow — transfers held credits to the capability provider.
|
|
38
|
+
*
|
|
39
|
+
* @param escrowId - The escrow ID to settle.
|
|
40
|
+
* @param recipientOwner - Agent identifier who will receive the credits.
|
|
41
|
+
* @throws {AgentBnBError} with code 'ESCROW_NOT_FOUND' if escrow does not exist.
|
|
42
|
+
* @throws {AgentBnBError} with code 'ESCROW_ALREADY_SETTLED' if escrow is not in 'held' status.
|
|
43
|
+
*/
|
|
44
|
+
async settle(escrowId, recipientOwner) {
|
|
45
|
+
settleEscrow(this.db, escrowId, recipientOwner);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Releases an escrow — refunds credits back to the requester.
|
|
49
|
+
*
|
|
50
|
+
* @param escrowId - The escrow ID to release.
|
|
51
|
+
* @throws {AgentBnBError} with code 'ESCROW_NOT_FOUND' if escrow does not exist.
|
|
52
|
+
* @throws {AgentBnBError} with code 'ESCROW_ALREADY_SETTLED' if escrow is not in 'held' status.
|
|
53
|
+
*/
|
|
54
|
+
async release(escrowId) {
|
|
55
|
+
releaseEscrow(this.db, escrowId);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Returns the current credit balance for an agent.
|
|
59
|
+
*
|
|
60
|
+
* @param owner - Agent identifier.
|
|
61
|
+
* @returns Current balance in credits (0 if agent is unknown).
|
|
62
|
+
*/
|
|
63
|
+
async getBalance(owner) {
|
|
64
|
+
return getBalance(this.db, owner);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Returns the transaction history for an agent, newest first.
|
|
68
|
+
*
|
|
69
|
+
* @param owner - Agent identifier.
|
|
70
|
+
* @param limit - Maximum number of transactions to return. Defaults to 100.
|
|
71
|
+
* @returns Array of credit transactions ordered newest first.
|
|
72
|
+
*/
|
|
73
|
+
async getHistory(owner, limit) {
|
|
74
|
+
return getTransactions(this.db, owner, limit);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Grants initial credits to an agent (bootstrap grant).
|
|
78
|
+
* Idempotent — calling multiple times has no additional effect on balance.
|
|
79
|
+
*
|
|
80
|
+
* @param owner - Agent identifier.
|
|
81
|
+
* @param amount - Number of credits to grant. Defaults to 100.
|
|
82
|
+
*/
|
|
83
|
+
async grant(owner, amount) {
|
|
84
|
+
bootstrapAgent(this.db, owner, amount);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// src/registry/identity-auth.ts
|
|
89
|
+
var MAX_REQUEST_AGE_MS = 5 * 60 * 1e3;
|
|
90
|
+
async function verifyIdentity(request, reply) {
|
|
91
|
+
const publicKeyHex = request.headers["x-agent-publickey"];
|
|
92
|
+
const signature = request.headers["x-agent-signature"];
|
|
93
|
+
const timestamp = request.headers["x-agent-timestamp"];
|
|
94
|
+
if (!publicKeyHex || !signature || !timestamp) {
|
|
95
|
+
await reply.code(401).send({ error: "Missing identity headers" });
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const requestTime = new Date(timestamp).getTime();
|
|
99
|
+
if (isNaN(requestTime) || Math.abs(Date.now() - requestTime) > MAX_REQUEST_AGE_MS) {
|
|
100
|
+
await reply.code(401).send({ error: "Request expired" });
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
const payload = {
|
|
104
|
+
method: request.method,
|
|
105
|
+
path: request.url,
|
|
106
|
+
timestamp,
|
|
107
|
+
publicKey: publicKeyHex
|
|
108
|
+
};
|
|
109
|
+
let publicKeyBuffer;
|
|
110
|
+
try {
|
|
111
|
+
publicKeyBuffer = Buffer.from(publicKeyHex, "hex");
|
|
112
|
+
} catch {
|
|
113
|
+
await reply.code(401).send({ error: "Invalid identity signature" });
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
const valid = verifyEscrowReceipt(payload, signature, publicKeyBuffer);
|
|
117
|
+
if (!valid) {
|
|
118
|
+
await reply.code(401).send({ error: "Invalid identity signature" });
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
request.agentPublicKey = publicKeyHex;
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
function identityAuthPlugin(fastify) {
|
|
125
|
+
fastify.addHook("onRequest", async (request, reply) => {
|
|
126
|
+
await verifyIdentity(request, reply);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function signRequest(method, path, body, privateKey, publicKeyHex) {
|
|
130
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
131
|
+
const payload = {
|
|
132
|
+
method,
|
|
133
|
+
path,
|
|
134
|
+
timestamp,
|
|
135
|
+
publicKey: publicKeyHex
|
|
136
|
+
};
|
|
137
|
+
void body;
|
|
138
|
+
const signature = signEscrowReceipt(payload, privateKey);
|
|
139
|
+
return {
|
|
140
|
+
"X-Agent-PublicKey": publicKeyHex,
|
|
141
|
+
"X-Agent-Signature": signature,
|
|
142
|
+
"X-Agent-Timestamp": timestamp
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/credit/registry-credit-ledger.ts
|
|
147
|
+
var HTTP_TIMEOUT_MS = 1e4;
|
|
148
|
+
var RegistryCreditLedger = class {
|
|
149
|
+
config;
|
|
150
|
+
constructor(config) {
|
|
151
|
+
this.config = config;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Holds credits in escrow during capability execution.
|
|
155
|
+
*
|
|
156
|
+
* @param owner - Agent identifier (requester).
|
|
157
|
+
* @param amount - Number of credits to hold.
|
|
158
|
+
* @param cardId - Capability Card ID being requested.
|
|
159
|
+
* @returns EscrowResult with the new escrowId.
|
|
160
|
+
* @throws {AgentBnBError} with code 'INSUFFICIENT_CREDITS' if balance < amount.
|
|
161
|
+
*/
|
|
162
|
+
async hold(owner, amount, cardId) {
|
|
163
|
+
if (this.config.mode === "direct") {
|
|
164
|
+
const escrowId = holdEscrow(this.config.db, owner, amount, cardId);
|
|
165
|
+
return { escrowId };
|
|
166
|
+
}
|
|
167
|
+
const data = await this.post("/api/credits/hold", owner, {
|
|
168
|
+
owner,
|
|
169
|
+
amount,
|
|
170
|
+
cardId
|
|
171
|
+
});
|
|
172
|
+
return { escrowId: data.escrowId };
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Settles an escrow — transfers held credits to the capability provider.
|
|
176
|
+
*
|
|
177
|
+
* @param escrowId - The escrow ID to settle.
|
|
178
|
+
* @param recipientOwner - Agent identifier who will receive the credits.
|
|
179
|
+
* @throws {AgentBnBError} with code 'ESCROW_NOT_FOUND' if escrow does not exist.
|
|
180
|
+
* @throws {AgentBnBError} with code 'ESCROW_ALREADY_SETTLED' if escrow is not in 'held' status.
|
|
181
|
+
*/
|
|
182
|
+
async settle(escrowId, recipientOwner) {
|
|
183
|
+
if (this.config.mode === "direct") {
|
|
184
|
+
settleEscrow(this.config.db, escrowId, recipientOwner);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
await this.post("/api/credits/settle", null, { escrowId, recipientOwner });
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Releases an escrow — refunds credits back to the requester.
|
|
191
|
+
*
|
|
192
|
+
* @param escrowId - The escrow ID to release.
|
|
193
|
+
* @throws {AgentBnBError} with code 'ESCROW_NOT_FOUND' if escrow does not exist.
|
|
194
|
+
* @throws {AgentBnBError} with code 'ESCROW_ALREADY_SETTLED' if escrow is not in 'held' status.
|
|
195
|
+
*/
|
|
196
|
+
async release(escrowId) {
|
|
197
|
+
if (this.config.mode === "direct") {
|
|
198
|
+
releaseEscrow(this.config.db, escrowId);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
await this.post("/api/credits/release", null, { escrowId });
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Returns the current credit balance for an agent.
|
|
205
|
+
*
|
|
206
|
+
* @param owner - Agent identifier.
|
|
207
|
+
* @returns Current balance in credits (0 if agent is unknown).
|
|
208
|
+
*/
|
|
209
|
+
async getBalance(owner) {
|
|
210
|
+
if (this.config.mode === "direct") {
|
|
211
|
+
return getBalance(this.config.db, owner);
|
|
212
|
+
}
|
|
213
|
+
const data = await this.get(`/api/credits/${owner}`, owner);
|
|
214
|
+
return data.balance;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Returns the transaction history for an agent, newest first.
|
|
218
|
+
*
|
|
219
|
+
* @param owner - Agent identifier.
|
|
220
|
+
* @param limit - Maximum number of transactions to return. Defaults to 100.
|
|
221
|
+
* @returns Array of credit transactions ordered newest first.
|
|
222
|
+
*/
|
|
223
|
+
async getHistory(owner, limit = 100) {
|
|
224
|
+
if (this.config.mode === "direct") {
|
|
225
|
+
return getTransactions(this.config.db, owner, limit);
|
|
226
|
+
}
|
|
227
|
+
const data = await this.get(
|
|
228
|
+
`/api/credits/${owner}/history?limit=${limit}`,
|
|
229
|
+
owner
|
|
230
|
+
);
|
|
231
|
+
return data.transactions;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Grants initial credits to an agent (bootstrap grant).
|
|
235
|
+
* Idempotent — calling multiple times has no additional effect on balance.
|
|
236
|
+
*
|
|
237
|
+
* @param owner - Agent identifier.
|
|
238
|
+
* @param amount - Number of credits to grant. Defaults to 100.
|
|
239
|
+
*/
|
|
240
|
+
async grant(owner, amount = 100) {
|
|
241
|
+
if (this.config.mode === "direct") {
|
|
242
|
+
bootstrapAgent(this.config.db, owner, amount);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
await this.post("/api/credits/grant", owner, { owner, amount });
|
|
246
|
+
}
|
|
247
|
+
// ─── Private HTTP helpers ─────────────────────────────────────────────────
|
|
248
|
+
/**
|
|
249
|
+
* Makes an authenticated POST request to the Registry HTTP API.
|
|
250
|
+
* Includes a 10s timeout via AbortController.
|
|
251
|
+
*
|
|
252
|
+
* @param path - API path (e.g., '/api/credits/hold').
|
|
253
|
+
* @param ownerForHeader - Agent owner identifier for X-Agent-Owner header, or null to omit.
|
|
254
|
+
* @param body - JSON body to send.
|
|
255
|
+
* @returns Parsed JSON response body.
|
|
256
|
+
* @throws {AgentBnBError} on non-2xx responses or network errors.
|
|
257
|
+
*/
|
|
258
|
+
async post(path, ownerForHeader, body) {
|
|
259
|
+
const cfg = this.config;
|
|
260
|
+
const controller = new AbortController();
|
|
261
|
+
const timeoutId = setTimeout(() => controller.abort(), HTTP_TIMEOUT_MS);
|
|
262
|
+
try {
|
|
263
|
+
const authHeaders = signRequest("POST", path, body, cfg.privateKey, cfg.ownerPublicKey);
|
|
264
|
+
const headers = {
|
|
265
|
+
"Content-Type": "application/json",
|
|
266
|
+
...authHeaders
|
|
267
|
+
};
|
|
268
|
+
void ownerForHeader;
|
|
269
|
+
const res = await fetch(`${cfg.registryUrl}${path}`, {
|
|
270
|
+
method: "POST",
|
|
271
|
+
headers,
|
|
272
|
+
body: JSON.stringify(body),
|
|
273
|
+
signal: controller.signal
|
|
274
|
+
});
|
|
275
|
+
return await this.handleResponse(res);
|
|
276
|
+
} catch (err) {
|
|
277
|
+
if (err instanceof AgentBnBError) throw err;
|
|
278
|
+
throw new AgentBnBError(
|
|
279
|
+
`Registry unreachable: ${err.message}`,
|
|
280
|
+
"REGISTRY_UNREACHABLE"
|
|
281
|
+
);
|
|
282
|
+
} finally {
|
|
283
|
+
clearTimeout(timeoutId);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Makes an authenticated GET request to the Registry HTTP API.
|
|
288
|
+
* Includes a 10s timeout via AbortController.
|
|
289
|
+
*
|
|
290
|
+
* @param path - API path (e.g., '/api/credits/owner-id').
|
|
291
|
+
* @param owner - Agent owner identifier for X-Agent-Owner header.
|
|
292
|
+
* @returns Parsed JSON response body.
|
|
293
|
+
* @throws {AgentBnBError} on non-2xx responses or network errors.
|
|
294
|
+
*/
|
|
295
|
+
async get(path, owner) {
|
|
296
|
+
const cfg = this.config;
|
|
297
|
+
const controller = new AbortController();
|
|
298
|
+
const timeoutId = setTimeout(() => controller.abort(), HTTP_TIMEOUT_MS);
|
|
299
|
+
try {
|
|
300
|
+
const authHeaders = signRequest("GET", path, null, cfg.privateKey, cfg.ownerPublicKey);
|
|
301
|
+
void owner;
|
|
302
|
+
const res = await fetch(`${cfg.registryUrl}${path}`, {
|
|
303
|
+
method: "GET",
|
|
304
|
+
headers: {
|
|
305
|
+
"Content-Type": "application/json",
|
|
306
|
+
...authHeaders
|
|
307
|
+
},
|
|
308
|
+
signal: controller.signal
|
|
309
|
+
});
|
|
310
|
+
return await this.handleResponse(res);
|
|
311
|
+
} catch (err) {
|
|
312
|
+
if (err instanceof AgentBnBError) throw err;
|
|
313
|
+
throw new AgentBnBError(
|
|
314
|
+
`Registry unreachable: ${err.message}`,
|
|
315
|
+
"REGISTRY_UNREACHABLE"
|
|
316
|
+
);
|
|
317
|
+
} finally {
|
|
318
|
+
clearTimeout(timeoutId);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Handles an HTTP response — returns parsed JSON on 2xx, throws AgentBnBError on error.
|
|
323
|
+
*/
|
|
324
|
+
async handleResponse(res) {
|
|
325
|
+
const json = await res.json();
|
|
326
|
+
if (!res.ok) {
|
|
327
|
+
const code = typeof json["code"] === "string" ? json["code"] : "REGISTRY_ERROR";
|
|
328
|
+
const message = typeof json["error"] === "string" ? json["error"] : `HTTP ${res.status}`;
|
|
329
|
+
throw new AgentBnBError(message, code);
|
|
330
|
+
}
|
|
331
|
+
return json;
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// src/credit/create-ledger.ts
|
|
336
|
+
function createLedger(opts) {
|
|
337
|
+
if ("registryUrl" in opts && opts.registryUrl !== void 0) {
|
|
338
|
+
return new RegistryCreditLedger({
|
|
339
|
+
mode: "http",
|
|
340
|
+
registryUrl: opts.registryUrl,
|
|
341
|
+
ownerPublicKey: opts.ownerPublicKey,
|
|
342
|
+
privateKey: opts.privateKey
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
if ("db" in opts && opts.db !== void 0) {
|
|
346
|
+
return new RegistryCreditLedger({
|
|
347
|
+
mode: "direct",
|
|
348
|
+
db: opts.db
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
const db = openCreditDb(opts.creditDbPath);
|
|
352
|
+
return new LocalCreditLedger(db);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export {
|
|
356
|
+
identityAuthPlugin,
|
|
357
|
+
createLedger
|
|
358
|
+
};
|
|
@@ -2,19 +2,21 @@ import {
|
|
|
2
2
|
getCard,
|
|
3
3
|
insertRequestLog,
|
|
4
4
|
updateReputation
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-TLU7ALCZ.js";
|
|
6
6
|
import {
|
|
7
7
|
confirmEscrowDebit,
|
|
8
8
|
getBalance,
|
|
9
9
|
holdEscrow,
|
|
10
10
|
recordEarning,
|
|
11
11
|
releaseEscrow,
|
|
12
|
-
settleEscrow
|
|
12
|
+
settleEscrow
|
|
13
|
+
} from "./chunk-XQHN6ITI.js";
|
|
14
|
+
import {
|
|
13
15
|
verifyEscrowReceipt
|
|
14
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-DVAS2443.js";
|
|
15
17
|
import {
|
|
16
18
|
AgentBnBError
|
|
17
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-FNKBHBYK.js";
|
|
18
20
|
|
|
19
21
|
// src/gateway/execute.ts
|
|
20
22
|
import { randomUUID } from "crypto";
|
|
@@ -49,7 +51,8 @@ async function executeCapabilityRequest(opts) {
|
|
|
49
51
|
escrowReceipt: receipt,
|
|
50
52
|
skillExecutor,
|
|
51
53
|
handlerUrl,
|
|
52
|
-
timeoutMs =
|
|
54
|
+
timeoutMs = 3e5,
|
|
55
|
+
onProgress
|
|
53
56
|
} = opts;
|
|
54
57
|
const card = getCard(registryDb, cardId);
|
|
55
58
|
if (!card) {
|
|
@@ -156,7 +159,7 @@ async function executeCapabilityRequest(opts) {
|
|
|
156
159
|
if (skillExecutor) {
|
|
157
160
|
const targetSkillId = resolvedSkillId ?? skillId ?? cardId;
|
|
158
161
|
try {
|
|
159
|
-
const execResult = await skillExecutor.execute(targetSkillId, params);
|
|
162
|
+
const execResult = await skillExecutor.execute(targetSkillId, params, onProgress);
|
|
160
163
|
if (!execResult.success) {
|
|
161
164
|
return handleFailure("failure", execResult.latency_ms, execResult.error ?? "Execution failed");
|
|
162
165
|
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AgentBnBError
|
|
3
|
+
} from "./chunk-FNKBHBYK.js";
|
|
4
|
+
|
|
5
|
+
// src/registry/matcher.ts
|
|
6
|
+
function searchCards(db, query, filters = {}) {
|
|
7
|
+
const words = query.trim().split(/\s+/).map((w) => w.replace(/["*^{}():]/g, "")).filter((w) => w.length > 0);
|
|
8
|
+
if (words.length === 0) return [];
|
|
9
|
+
const ftsQuery = words.map((w) => `"${w}"`).join(" OR ");
|
|
10
|
+
const conditions = [];
|
|
11
|
+
const params = [ftsQuery];
|
|
12
|
+
if (filters.level !== void 0) {
|
|
13
|
+
conditions.push(`json_extract(cc.data, '$.level') = ?`);
|
|
14
|
+
params.push(filters.level);
|
|
15
|
+
}
|
|
16
|
+
if (filters.online !== void 0) {
|
|
17
|
+
conditions.push(`json_extract(cc.data, '$.availability.online') = ?`);
|
|
18
|
+
params.push(filters.online ? 1 : 0);
|
|
19
|
+
}
|
|
20
|
+
const whereClause = conditions.length > 0 ? `AND ${conditions.join(" AND ")}` : "";
|
|
21
|
+
const sql = `
|
|
22
|
+
SELECT cc.data
|
|
23
|
+
FROM capability_cards cc
|
|
24
|
+
JOIN cards_fts ON cc.rowid = cards_fts.rowid
|
|
25
|
+
WHERE cards_fts MATCH ?
|
|
26
|
+
${whereClause}
|
|
27
|
+
ORDER BY bm25(cards_fts)
|
|
28
|
+
LIMIT 50
|
|
29
|
+
`;
|
|
30
|
+
const stmt = db.prepare(sql);
|
|
31
|
+
const rows = stmt.all(...params);
|
|
32
|
+
const results = rows.map((row) => JSON.parse(row.data));
|
|
33
|
+
if (filters.apis_used && filters.apis_used.length > 0) {
|
|
34
|
+
const requiredApis = filters.apis_used;
|
|
35
|
+
return results.filter((card) => {
|
|
36
|
+
const cardApis = card.metadata?.apis_used ?? [];
|
|
37
|
+
return requiredApis.every((api) => cardApis.includes(api));
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return results;
|
|
41
|
+
}
|
|
42
|
+
function filterCards(db, filters) {
|
|
43
|
+
const conditions = [];
|
|
44
|
+
const params = [];
|
|
45
|
+
if (filters.level !== void 0) {
|
|
46
|
+
conditions.push(`json_extract(data, '$.level') = ?`);
|
|
47
|
+
params.push(filters.level);
|
|
48
|
+
}
|
|
49
|
+
if (filters.online !== void 0) {
|
|
50
|
+
conditions.push(`json_extract(data, '$.availability.online') = ?`);
|
|
51
|
+
params.push(filters.online ? 1 : 0);
|
|
52
|
+
}
|
|
53
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
54
|
+
const sql = `SELECT data FROM capability_cards ${whereClause}`;
|
|
55
|
+
const stmt = db.prepare(sql);
|
|
56
|
+
const rows = stmt.all(...params);
|
|
57
|
+
return rows.map((row) => JSON.parse(row.data));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/cli/remote-registry.ts
|
|
61
|
+
var RegistryTimeoutError = class extends AgentBnBError {
|
|
62
|
+
constructor(url) {
|
|
63
|
+
super(
|
|
64
|
+
`Registry at ${url} did not respond within 5s. Showing local results only.`,
|
|
65
|
+
"REGISTRY_TIMEOUT"
|
|
66
|
+
);
|
|
67
|
+
this.name = "RegistryTimeoutError";
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
var RegistryConnectionError = class extends AgentBnBError {
|
|
71
|
+
constructor(url) {
|
|
72
|
+
super(
|
|
73
|
+
`Cannot reach ${url}. Is the registry running? Showing local results only.`,
|
|
74
|
+
"REGISTRY_CONNECTION"
|
|
75
|
+
);
|
|
76
|
+
this.name = "RegistryConnectionError";
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
var RegistryAuthError = class extends AgentBnBError {
|
|
80
|
+
constructor(url) {
|
|
81
|
+
super(
|
|
82
|
+
`Authentication failed for ${url}. Run \`agentbnb config set token <your-token>\`.`,
|
|
83
|
+
"REGISTRY_AUTH"
|
|
84
|
+
);
|
|
85
|
+
this.name = "RegistryAuthError";
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
async function fetchRemoteCards(registryUrl, params, timeoutMs = 5e3) {
|
|
89
|
+
let cardsUrl;
|
|
90
|
+
try {
|
|
91
|
+
cardsUrl = new URL("/cards", registryUrl);
|
|
92
|
+
} catch {
|
|
93
|
+
throw new AgentBnBError(`Invalid registry URL: ${registryUrl}`, "INVALID_REGISTRY_URL");
|
|
94
|
+
}
|
|
95
|
+
const searchParams = new URLSearchParams();
|
|
96
|
+
if (params.q !== void 0) searchParams.set("q", params.q);
|
|
97
|
+
if (params.level !== void 0) searchParams.set("level", String(params.level));
|
|
98
|
+
if (params.online !== void 0) searchParams.set("online", String(params.online));
|
|
99
|
+
if (params.tag !== void 0) searchParams.set("tag", params.tag);
|
|
100
|
+
searchParams.set("limit", "100");
|
|
101
|
+
cardsUrl.search = searchParams.toString();
|
|
102
|
+
const controller = new AbortController();
|
|
103
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
104
|
+
let response;
|
|
105
|
+
try {
|
|
106
|
+
response = await fetch(cardsUrl.toString(), { signal: controller.signal });
|
|
107
|
+
} catch (err) {
|
|
108
|
+
clearTimeout(timer);
|
|
109
|
+
const isTimeout = err instanceof Error && err.name === "AbortError";
|
|
110
|
+
if (isTimeout) {
|
|
111
|
+
throw new RegistryTimeoutError(registryUrl);
|
|
112
|
+
}
|
|
113
|
+
throw new RegistryConnectionError(registryUrl);
|
|
114
|
+
} finally {
|
|
115
|
+
clearTimeout(timer);
|
|
116
|
+
}
|
|
117
|
+
if (response.status === 401 || response.status === 403) {
|
|
118
|
+
throw new RegistryAuthError(registryUrl);
|
|
119
|
+
}
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
throw new RegistryConnectionError(registryUrl);
|
|
122
|
+
}
|
|
123
|
+
const body = await response.json();
|
|
124
|
+
return body.items;
|
|
125
|
+
}
|
|
126
|
+
function mergeResults(localCards, remoteCards, hasQuery) {
|
|
127
|
+
const taggedLocal = localCards.map((c) => ({ ...c, source: "local" }));
|
|
128
|
+
const taggedRemote = remoteCards.map((c) => ({ ...c, source: "remote" }));
|
|
129
|
+
const localIds = new Set(localCards.map((c) => c.id));
|
|
130
|
+
const dedupedRemote = taggedRemote.filter((c) => !localIds.has(c.id));
|
|
131
|
+
if (!hasQuery) {
|
|
132
|
+
return [...taggedLocal, ...dedupedRemote];
|
|
133
|
+
}
|
|
134
|
+
const result = [];
|
|
135
|
+
const maxLen = Math.max(taggedLocal.length, dedupedRemote.length);
|
|
136
|
+
for (let i = 0; i < maxLen; i++) {
|
|
137
|
+
if (i < taggedLocal.length) result.push(taggedLocal[i]);
|
|
138
|
+
if (i < dedupedRemote.length) result.push(dedupedRemote[i]);
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export {
|
|
144
|
+
searchCards,
|
|
145
|
+
filterCards,
|
|
146
|
+
fetchRemoteCards,
|
|
147
|
+
mergeResults
|
|
148
|
+
};
|