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,16 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
fetchRemoteCards,
|
|
3
|
+
searchCards
|
|
4
|
+
} from "./chunk-QJEOCKVF.js";
|
|
5
|
+
import {
|
|
6
|
+
requestCapability
|
|
7
|
+
} from "./chunk-KJG2UJV5.js";
|
|
1
8
|
import {
|
|
2
9
|
findPeer
|
|
3
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-5AH3CMOX.js";
|
|
4
11
|
import {
|
|
5
12
|
getBalance,
|
|
6
13
|
holdEscrow,
|
|
7
14
|
releaseEscrow,
|
|
8
|
-
settleEscrow
|
|
9
|
-
|
|
10
|
-
} from "./chunk-QHQPXO67.js";
|
|
15
|
+
settleEscrow
|
|
16
|
+
} from "./chunk-XQHN6ITI.js";
|
|
11
17
|
import {
|
|
12
18
|
AgentBnBError
|
|
13
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-FNKBHBYK.js";
|
|
14
20
|
|
|
15
21
|
// src/autonomy/tiers.ts
|
|
16
22
|
import { randomUUID } from "crypto";
|
|
@@ -93,116 +99,10 @@ var BudgetManager = class {
|
|
|
93
99
|
}
|
|
94
100
|
};
|
|
95
101
|
|
|
96
|
-
// src/registry/matcher.ts
|
|
97
|
-
function searchCards(db, query, filters = {}) {
|
|
98
|
-
const words = query.trim().split(/\s+/).map((w) => w.replace(/["*^{}():]/g, "")).filter((w) => w.length > 0);
|
|
99
|
-
if (words.length === 0) return [];
|
|
100
|
-
const ftsQuery = words.map((w) => `"${w}"`).join(" OR ");
|
|
101
|
-
const conditions = [];
|
|
102
|
-
const params = [ftsQuery];
|
|
103
|
-
if (filters.level !== void 0) {
|
|
104
|
-
conditions.push(`json_extract(cc.data, '$.level') = ?`);
|
|
105
|
-
params.push(filters.level);
|
|
106
|
-
}
|
|
107
|
-
if (filters.online !== void 0) {
|
|
108
|
-
conditions.push(`json_extract(cc.data, '$.availability.online') = ?`);
|
|
109
|
-
params.push(filters.online ? 1 : 0);
|
|
110
|
-
}
|
|
111
|
-
const whereClause = conditions.length > 0 ? `AND ${conditions.join(" AND ")}` : "";
|
|
112
|
-
const sql = `
|
|
113
|
-
SELECT cc.data
|
|
114
|
-
FROM capability_cards cc
|
|
115
|
-
JOIN cards_fts ON cc.rowid = cards_fts.rowid
|
|
116
|
-
WHERE cards_fts MATCH ?
|
|
117
|
-
${whereClause}
|
|
118
|
-
ORDER BY bm25(cards_fts)
|
|
119
|
-
LIMIT 50
|
|
120
|
-
`;
|
|
121
|
-
const stmt = db.prepare(sql);
|
|
122
|
-
const rows = stmt.all(...params);
|
|
123
|
-
const results = rows.map((row) => JSON.parse(row.data));
|
|
124
|
-
if (filters.apis_used && filters.apis_used.length > 0) {
|
|
125
|
-
const requiredApis = filters.apis_used;
|
|
126
|
-
return results.filter((card) => {
|
|
127
|
-
const cardApis = card.metadata?.apis_used ?? [];
|
|
128
|
-
return requiredApis.every((api) => cardApis.includes(api));
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
return results;
|
|
132
|
-
}
|
|
133
|
-
function filterCards(db, filters) {
|
|
134
|
-
const conditions = [];
|
|
135
|
-
const params = [];
|
|
136
|
-
if (filters.level !== void 0) {
|
|
137
|
-
conditions.push(`json_extract(data, '$.level') = ?`);
|
|
138
|
-
params.push(filters.level);
|
|
139
|
-
}
|
|
140
|
-
if (filters.online !== void 0) {
|
|
141
|
-
conditions.push(`json_extract(data, '$.availability.online') = ?`);
|
|
142
|
-
params.push(filters.online ? 1 : 0);
|
|
143
|
-
}
|
|
144
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
145
|
-
const sql = `SELECT data FROM capability_cards ${whereClause}`;
|
|
146
|
-
const stmt = db.prepare(sql);
|
|
147
|
-
const rows = stmt.all(...params);
|
|
148
|
-
return rows.map((row) => JSON.parse(row.data));
|
|
149
|
-
}
|
|
150
|
-
|
|
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
102
|
// src/autonomy/pending-requests.ts
|
|
203
|
-
import { randomUUID as
|
|
103
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
204
104
|
function createPendingRequest(db, opts) {
|
|
205
|
-
const id =
|
|
105
|
+
const id = randomUUID2();
|
|
206
106
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
207
107
|
const paramsJson = opts.params !== void 0 ? JSON.stringify(opts.params) : null;
|
|
208
108
|
db.prepare(`
|
|
@@ -277,6 +177,7 @@ var AutoRequestor = class {
|
|
|
277
177
|
creditDb;
|
|
278
178
|
autonomyConfig;
|
|
279
179
|
budgetManager;
|
|
180
|
+
registryUrl;
|
|
280
181
|
/**
|
|
281
182
|
* Creates a new AutoRequestor.
|
|
282
183
|
*
|
|
@@ -288,6 +189,7 @@ var AutoRequestor = class {
|
|
|
288
189
|
this.creditDb = opts.creditDb;
|
|
289
190
|
this.autonomyConfig = opts.autonomyConfig;
|
|
290
191
|
this.budgetManager = opts.budgetManager;
|
|
192
|
+
this.registryUrl = opts.registryUrl;
|
|
291
193
|
}
|
|
292
194
|
/**
|
|
293
195
|
* Executes an autonomous capability request.
|
|
@@ -308,7 +210,23 @@ var AutoRequestor = class {
|
|
|
308
210
|
* @returns The result of the auto-request attempt.
|
|
309
211
|
*/
|
|
310
212
|
async requestWithAutonomy(need) {
|
|
311
|
-
|
|
213
|
+
let cards = searchCards(this.registryDb, need.query, { online: true });
|
|
214
|
+
if (cards.length === 0 && this.registryUrl) {
|
|
215
|
+
try {
|
|
216
|
+
cards = await fetchRemoteCards(this.registryUrl, { q: need.query, online: true });
|
|
217
|
+
} catch {
|
|
218
|
+
insertAuditEvent(this.registryDb, {
|
|
219
|
+
type: "auto_request_failed",
|
|
220
|
+
card_id: "none",
|
|
221
|
+
skill_id: "none",
|
|
222
|
+
tier_invoked: 3,
|
|
223
|
+
credits: 0,
|
|
224
|
+
peer: "none",
|
|
225
|
+
reason: `Remote registry fallback failed for query "${need.query}"`
|
|
226
|
+
});
|
|
227
|
+
cards = [];
|
|
228
|
+
}
|
|
229
|
+
}
|
|
312
230
|
const candidates = [];
|
|
313
231
|
for (const card of cards) {
|
|
314
232
|
const cardAsV2 = card;
|
|
@@ -429,65 +347,14 @@ var AutoRequestor = class {
|
|
|
429
347
|
}
|
|
430
348
|
};
|
|
431
349
|
|
|
432
|
-
// src/utils/interpolation.ts
|
|
433
|
-
function resolvePath(obj, path) {
|
|
434
|
-
const segments = path.replace(/\[(\d+)\]/g, ".$1").split(".").filter((s) => s.length > 0);
|
|
435
|
-
let current = obj;
|
|
436
|
-
for (const segment of segments) {
|
|
437
|
-
if (current === null || current === void 0) {
|
|
438
|
-
return void 0;
|
|
439
|
-
}
|
|
440
|
-
if (typeof current !== "object") {
|
|
441
|
-
return void 0;
|
|
442
|
-
}
|
|
443
|
-
current = current[segment];
|
|
444
|
-
}
|
|
445
|
-
return current;
|
|
446
|
-
}
|
|
447
|
-
function interpolate(template, context) {
|
|
448
|
-
return template.replace(/\$\{([^}]+)\}/g, (_match, expression) => {
|
|
449
|
-
const resolved = resolvePath(context, expression.trim());
|
|
450
|
-
if (resolved === void 0 || resolved === null) {
|
|
451
|
-
return "";
|
|
452
|
-
}
|
|
453
|
-
if (typeof resolved === "object") {
|
|
454
|
-
return JSON.stringify(resolved);
|
|
455
|
-
}
|
|
456
|
-
return String(resolved);
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
function interpolateObject(obj, context) {
|
|
460
|
-
const result = {};
|
|
461
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
462
|
-
result[key] = interpolateValue(value, context);
|
|
463
|
-
}
|
|
464
|
-
return result;
|
|
465
|
-
}
|
|
466
|
-
function interpolateValue(value, context) {
|
|
467
|
-
if (typeof value === "string") {
|
|
468
|
-
return interpolate(value, context);
|
|
469
|
-
}
|
|
470
|
-
if (Array.isArray(value)) {
|
|
471
|
-
return value.map((item) => interpolateValue(item, context));
|
|
472
|
-
}
|
|
473
|
-
if (value !== null && typeof value === "object") {
|
|
474
|
-
return interpolateObject(value, context);
|
|
475
|
-
}
|
|
476
|
-
return value;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
350
|
export {
|
|
480
351
|
DEFAULT_AUTONOMY_CONFIG,
|
|
481
352
|
getAutonomyTier,
|
|
482
353
|
insertAuditEvent,
|
|
483
354
|
DEFAULT_BUDGET_CONFIG,
|
|
484
355
|
BudgetManager,
|
|
485
|
-
searchCards,
|
|
486
|
-
filterCards,
|
|
487
|
-
requestCapability,
|
|
488
356
|
listPendingRequests,
|
|
489
357
|
resolvePendingRequest,
|
|
490
358
|
scorePeers,
|
|
491
|
-
AutoRequestor
|
|
492
|
-
interpolateObject
|
|
359
|
+
AutoRequestor
|
|
493
360
|
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/cli/config.ts
|
|
2
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
function getConfigDir() {
|
|
6
|
+
return process.env["AGENTBNB_DIR"] ?? join(homedir(), ".agentbnb");
|
|
7
|
+
}
|
|
8
|
+
function getConfigPath() {
|
|
9
|
+
return join(getConfigDir(), "config.json");
|
|
10
|
+
}
|
|
11
|
+
function loadConfig() {
|
|
12
|
+
const configPath = getConfigPath();
|
|
13
|
+
if (!existsSync(configPath)) return null;
|
|
14
|
+
try {
|
|
15
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
16
|
+
return JSON.parse(raw);
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function saveConfig(config) {
|
|
22
|
+
const dir = getConfigDir();
|
|
23
|
+
if (!existsSync(dir)) {
|
|
24
|
+
mkdirSync(dir, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
writeFileSync(getConfigPath(), JSON.stringify(config, null, 2), "utf-8");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
getConfigDir,
|
|
31
|
+
loadConfig,
|
|
32
|
+
saveConfig
|
|
33
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AgentBnBError
|
|
3
|
+
} from "./chunk-FNKBHBYK.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
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
RelayMessageSchema
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-QT7TEVNV.js";
|
|
4
4
|
|
|
5
5
|
// src/relay/websocket-client.ts
|
|
6
6
|
import WebSocket from "ws";
|
|
@@ -36,7 +36,8 @@ var RelayClient = class {
|
|
|
36
36
|
type: "register",
|
|
37
37
|
owner: this.opts.owner,
|
|
38
38
|
token: this.opts.token,
|
|
39
|
-
card: this.opts.card
|
|
39
|
+
card: this.opts.card,
|
|
40
|
+
...this.opts.cards && this.opts.cards.length > 0 ? { cards: this.opts.cards } : {}
|
|
40
41
|
});
|
|
41
42
|
});
|
|
42
43
|
this.ws.on("message", (raw) => {
|
|
@@ -101,13 +102,13 @@ var RelayClient = class {
|
|
|
101
102
|
throw new Error("Not connected to registry relay");
|
|
102
103
|
}
|
|
103
104
|
const id = randomUUID();
|
|
104
|
-
const timeoutMs = opts.timeoutMs ??
|
|
105
|
+
const timeoutMs = opts.timeoutMs ?? 3e5;
|
|
105
106
|
return new Promise((resolve, reject) => {
|
|
106
107
|
const timeout = setTimeout(() => {
|
|
107
108
|
this.pendingRequests.delete(id);
|
|
108
109
|
reject(new Error("Relay request timeout"));
|
|
109
110
|
}, timeoutMs);
|
|
110
|
-
this.pendingRequests.set(id, { resolve, reject, timeout });
|
|
111
|
+
this.pendingRequests.set(id, { resolve, reject, timeout, timeoutMs, onProgress: opts.onProgress });
|
|
111
112
|
this.send({
|
|
112
113
|
type: "relay_request",
|
|
113
114
|
id,
|
|
@@ -120,6 +121,22 @@ var RelayClient = class {
|
|
|
120
121
|
});
|
|
121
122
|
});
|
|
122
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Send a relay_progress message to the relay server for a given request.
|
|
126
|
+
* Used by the onRequest handler to forward SkillExecutor progress updates
|
|
127
|
+
* to the requesting agent so it can reset its timeout window.
|
|
128
|
+
*
|
|
129
|
+
* @param requestId - The relay request ID to associate progress with.
|
|
130
|
+
* @param info - Progress details (step, total, message).
|
|
131
|
+
*/
|
|
132
|
+
sendProgress(requestId, info) {
|
|
133
|
+
this.send({
|
|
134
|
+
type: "relay_progress",
|
|
135
|
+
id: requestId,
|
|
136
|
+
progress: Math.round(info.step / info.total * 100),
|
|
137
|
+
message: info.message
|
|
138
|
+
});
|
|
139
|
+
}
|
|
123
140
|
/** Whether the client is connected and registered */
|
|
124
141
|
get isConnected() {
|
|
125
142
|
return this.ws !== null && this.ws.readyState === WebSocket.OPEN && this.registered;
|
|
@@ -166,6 +183,9 @@ var RelayClient = class {
|
|
|
166
183
|
case "error":
|
|
167
184
|
this.handleError(msg);
|
|
168
185
|
break;
|
|
186
|
+
case "relay_progress":
|
|
187
|
+
this.handleProgress(msg);
|
|
188
|
+
break;
|
|
169
189
|
default:
|
|
170
190
|
break;
|
|
171
191
|
}
|
|
@@ -211,6 +231,19 @@ var RelayClient = class {
|
|
|
211
231
|
}
|
|
212
232
|
}
|
|
213
233
|
}
|
|
234
|
+
handleProgress(msg) {
|
|
235
|
+
const pending = this.pendingRequests.get(msg.id);
|
|
236
|
+
if (!pending) return;
|
|
237
|
+
clearTimeout(pending.timeout);
|
|
238
|
+
const newTimeout = setTimeout(() => {
|
|
239
|
+
this.pendingRequests.delete(msg.id);
|
|
240
|
+
pending.reject(new Error("Relay request timeout"));
|
|
241
|
+
}, pending.timeoutMs);
|
|
242
|
+
pending.timeout = newTimeout;
|
|
243
|
+
if (pending.onProgress) {
|
|
244
|
+
pending.onProgress({ id: msg.id, progress: msg.progress, message: msg.message });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
214
247
|
send(msg) {
|
|
215
248
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
216
249
|
this.ws.send(JSON.stringify(msg));
|
|
@@ -270,6 +303,7 @@ var RelayClient = class {
|
|
|
270
303
|
}, delay);
|
|
271
304
|
}
|
|
272
305
|
};
|
|
306
|
+
|
|
273
307
|
export {
|
|
274
308
|
RelayClient
|
|
275
309
|
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {
|
|
2
|
+
signEscrowReceipt
|
|
3
|
+
} from "./chunk-DVAS2443.js";
|
|
4
|
+
import {
|
|
5
|
+
AgentBnBError
|
|
6
|
+
} from "./chunk-FNKBHBYK.js";
|
|
7
|
+
|
|
8
|
+
// src/gateway/client.ts
|
|
9
|
+
import { randomUUID } from "crypto";
|
|
10
|
+
async function requestCapability(opts) {
|
|
11
|
+
const { gatewayUrl, token, cardId, params = {}, timeoutMs = 3e5, 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
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateKeyPair,
|
|
3
|
+
loadKeyPair,
|
|
4
|
+
saveKeyPair
|
|
5
|
+
} from "./chunk-DVAS2443.js";
|
|
6
|
+
|
|
7
|
+
// src/identity/identity.ts
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { createHash } from "crypto";
|
|
10
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
var AgentIdentitySchema = z.object({
|
|
13
|
+
/** Deterministic ID derived from public key: sha256(hex).slice(0, 16). */
|
|
14
|
+
agent_id: z.string().min(1),
|
|
15
|
+
/** Human-readable owner name (from config or init). */
|
|
16
|
+
owner: z.string().min(1),
|
|
17
|
+
/** Hex-encoded Ed25519 public key. */
|
|
18
|
+
public_key: z.string().min(1),
|
|
19
|
+
/** ISO 8601 timestamp of identity creation. */
|
|
20
|
+
created_at: z.string().datetime(),
|
|
21
|
+
/** Optional guarantor info if linked to a human. */
|
|
22
|
+
guarantor: z.object({
|
|
23
|
+
github_login: z.string().min(1),
|
|
24
|
+
verified_at: z.string().datetime()
|
|
25
|
+
}).optional()
|
|
26
|
+
});
|
|
27
|
+
var AgentCertificateSchema = z.object({
|
|
28
|
+
identity: AgentIdentitySchema,
|
|
29
|
+
/** ISO 8601 timestamp of certificate issuance. */
|
|
30
|
+
issued_at: z.string().datetime(),
|
|
31
|
+
/** ISO 8601 timestamp of certificate expiry. */
|
|
32
|
+
expires_at: z.string().datetime(),
|
|
33
|
+
/** Hex-encoded public key of the issuer (same as identity for self-signed). */
|
|
34
|
+
issuer_public_key: z.string().min(1),
|
|
35
|
+
/** Base64url Ed25519 signature over { identity, issued_at, expires_at, issuer_public_key }. */
|
|
36
|
+
signature: z.string().min(1)
|
|
37
|
+
});
|
|
38
|
+
var IDENTITY_FILENAME = "identity.json";
|
|
39
|
+
function deriveAgentId(publicKeyHex) {
|
|
40
|
+
return createHash("sha256").update(publicKeyHex, "hex").digest("hex").slice(0, 16);
|
|
41
|
+
}
|
|
42
|
+
function createIdentity(configDir, owner) {
|
|
43
|
+
if (!existsSync(configDir)) {
|
|
44
|
+
mkdirSync(configDir, { recursive: true });
|
|
45
|
+
}
|
|
46
|
+
let keys;
|
|
47
|
+
try {
|
|
48
|
+
keys = loadKeyPair(configDir);
|
|
49
|
+
} catch {
|
|
50
|
+
keys = generateKeyPair();
|
|
51
|
+
saveKeyPair(configDir, keys);
|
|
52
|
+
}
|
|
53
|
+
const publicKeyHex = keys.publicKey.toString("hex");
|
|
54
|
+
const agentId = deriveAgentId(publicKeyHex);
|
|
55
|
+
const identity = {
|
|
56
|
+
agent_id: agentId,
|
|
57
|
+
owner,
|
|
58
|
+
public_key: publicKeyHex,
|
|
59
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
60
|
+
};
|
|
61
|
+
saveIdentity(configDir, identity);
|
|
62
|
+
return identity;
|
|
63
|
+
}
|
|
64
|
+
function loadIdentity(configDir) {
|
|
65
|
+
const filePath = join(configDir, IDENTITY_FILENAME);
|
|
66
|
+
if (!existsSync(filePath)) return null;
|
|
67
|
+
try {
|
|
68
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
69
|
+
return AgentIdentitySchema.parse(JSON.parse(raw));
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function saveIdentity(configDir, identity) {
|
|
75
|
+
if (!existsSync(configDir)) {
|
|
76
|
+
mkdirSync(configDir, { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
const filePath = join(configDir, IDENTITY_FILENAME);
|
|
79
|
+
writeFileSync(filePath, JSON.stringify(identity, null, 2), "utf-8");
|
|
80
|
+
}
|
|
81
|
+
function ensureIdentity(configDir, owner) {
|
|
82
|
+
const existing = loadIdentity(configDir);
|
|
83
|
+
if (existing) return existing;
|
|
84
|
+
return createIdentity(configDir, owner);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export {
|
|
88
|
+
deriveAgentId,
|
|
89
|
+
ensureIdentity
|
|
90
|
+
};
|