agentbnb 2.2.0 → 3.1.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/dist/card-P5C36VBD.js +81 -0
- package/dist/chunk-2LLXUKMY.js +489 -0
- package/dist/chunk-3Y36WQDV.js +70 -0
- package/dist/chunk-4Q7D24DP.js +257 -0
- package/dist/chunk-BEI5MTNZ.js +91 -0
- package/dist/chunk-QVIGMCHA.js +486 -0
- package/dist/chunk-T7ZJPQHD.js +372 -0
- package/dist/chunk-TQMI73LL.js +125 -0
- package/dist/chunk-ZJCIBK6O.js +192 -0
- package/dist/cli/index.js +715 -1538
- package/dist/conduct-M57F72RK.js +117 -0
- package/dist/conductor-mode-CF6PSRRA.js +112 -0
- package/dist/execute-3T5RF3DP.js +9 -0
- package/dist/index.js +2432 -183
- package/dist/peers-G36URZYB.js +12 -0
- package/dist/websocket-client-5TIQDYQ4.js +275 -0
- package/package.json +5 -2
- package/dist/index.d.ts +0 -676
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getCard,
|
|
3
|
+
insertRequestLog,
|
|
4
|
+
updateReputation
|
|
5
|
+
} from "./chunk-2LLXUKMY.js";
|
|
6
|
+
import {
|
|
7
|
+
confirmEscrowDebit,
|
|
8
|
+
getBalance,
|
|
9
|
+
holdEscrow,
|
|
10
|
+
recordEarning,
|
|
11
|
+
releaseEscrow,
|
|
12
|
+
settleEscrow
|
|
13
|
+
} from "./chunk-ZJCIBK6O.js";
|
|
14
|
+
import {
|
|
15
|
+
AgentBnBError
|
|
16
|
+
} from "./chunk-TQMI73LL.js";
|
|
17
|
+
|
|
18
|
+
// src/gateway/execute.ts
|
|
19
|
+
import { randomUUID } from "crypto";
|
|
20
|
+
|
|
21
|
+
// src/credit/signing.ts
|
|
22
|
+
import { generateKeyPairSync, sign, verify, createPublicKey, createPrivateKey } from "crypto";
|
|
23
|
+
import { writeFileSync, readFileSync, existsSync, chmodSync } from "fs";
|
|
24
|
+
import { join } from "path";
|
|
25
|
+
function generateKeyPair() {
|
|
26
|
+
const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
|
|
27
|
+
publicKeyEncoding: { type: "spki", format: "der" },
|
|
28
|
+
privateKeyEncoding: { type: "pkcs8", format: "der" }
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
publicKey: Buffer.from(publicKey),
|
|
32
|
+
privateKey: Buffer.from(privateKey)
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function saveKeyPair(configDir, keys) {
|
|
36
|
+
const privatePath = join(configDir, "private.key");
|
|
37
|
+
const publicPath = join(configDir, "public.key");
|
|
38
|
+
writeFileSync(privatePath, keys.privateKey);
|
|
39
|
+
chmodSync(privatePath, 384);
|
|
40
|
+
writeFileSync(publicPath, keys.publicKey);
|
|
41
|
+
}
|
|
42
|
+
function loadKeyPair(configDir) {
|
|
43
|
+
const privatePath = join(configDir, "private.key");
|
|
44
|
+
const publicPath = join(configDir, "public.key");
|
|
45
|
+
if (!existsSync(privatePath) || !existsSync(publicPath)) {
|
|
46
|
+
throw new AgentBnBError("Keypair not found. Run `agentbnb init` to generate one.", "KEYPAIR_NOT_FOUND");
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
publicKey: readFileSync(publicPath),
|
|
50
|
+
privateKey: readFileSync(privatePath)
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function canonicalJson(data) {
|
|
54
|
+
return JSON.stringify(data, Object.keys(data).sort());
|
|
55
|
+
}
|
|
56
|
+
function signEscrowReceipt(data, privateKey) {
|
|
57
|
+
const message = Buffer.from(canonicalJson(data), "utf-8");
|
|
58
|
+
const keyObject = createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" });
|
|
59
|
+
const signature = sign(null, message, keyObject);
|
|
60
|
+
return signature.toString("base64url");
|
|
61
|
+
}
|
|
62
|
+
function verifyEscrowReceipt(data, signature, publicKey) {
|
|
63
|
+
try {
|
|
64
|
+
const message = Buffer.from(canonicalJson(data), "utf-8");
|
|
65
|
+
const keyObject = createPublicKey({ key: publicKey, format: "der", type: "spki" });
|
|
66
|
+
const sigBuffer = Buffer.from(signature, "base64url");
|
|
67
|
+
return verify(null, message, keyObject, sigBuffer);
|
|
68
|
+
} catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/credit/settlement.ts
|
|
74
|
+
function settleProviderEarning(providerDb, providerOwner, receipt) {
|
|
75
|
+
recordEarning(
|
|
76
|
+
providerDb,
|
|
77
|
+
providerOwner,
|
|
78
|
+
receipt.amount,
|
|
79
|
+
receipt.card_id,
|
|
80
|
+
receipt.nonce
|
|
81
|
+
);
|
|
82
|
+
return { settled: true };
|
|
83
|
+
}
|
|
84
|
+
function settleRequesterEscrow(requesterDb, escrowId) {
|
|
85
|
+
confirmEscrowDebit(requesterDb, escrowId);
|
|
86
|
+
}
|
|
87
|
+
function releaseRequesterEscrow(requesterDb, escrowId) {
|
|
88
|
+
releaseEscrow(requesterDb, escrowId);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/gateway/execute.ts
|
|
92
|
+
async function executeCapabilityRequest(opts) {
|
|
93
|
+
const {
|
|
94
|
+
registryDb,
|
|
95
|
+
creditDb,
|
|
96
|
+
cardId,
|
|
97
|
+
skillId,
|
|
98
|
+
params,
|
|
99
|
+
requester,
|
|
100
|
+
escrowReceipt: receipt,
|
|
101
|
+
skillExecutor,
|
|
102
|
+
handlerUrl,
|
|
103
|
+
timeoutMs = 3e4
|
|
104
|
+
} = opts;
|
|
105
|
+
const card = getCard(registryDb, cardId);
|
|
106
|
+
if (!card) {
|
|
107
|
+
return { success: false, error: { code: -32602, message: `Card not found: ${cardId}` } };
|
|
108
|
+
}
|
|
109
|
+
let creditsNeeded;
|
|
110
|
+
let cardName;
|
|
111
|
+
let resolvedSkillId;
|
|
112
|
+
const rawCard = card;
|
|
113
|
+
if (Array.isArray(rawCard["skills"])) {
|
|
114
|
+
const v2card = card;
|
|
115
|
+
const skill = skillId ? v2card.skills.find((s) => s.id === skillId) : v2card.skills[0];
|
|
116
|
+
if (!skill) {
|
|
117
|
+
return { success: false, error: { code: -32602, message: `Skill not found: ${skillId}` } };
|
|
118
|
+
}
|
|
119
|
+
creditsNeeded = skill.pricing.credits_per_call;
|
|
120
|
+
cardName = skill.name;
|
|
121
|
+
resolvedSkillId = skill.id;
|
|
122
|
+
} else {
|
|
123
|
+
creditsNeeded = card.pricing.credits_per_call;
|
|
124
|
+
cardName = card.name;
|
|
125
|
+
}
|
|
126
|
+
let escrowId = null;
|
|
127
|
+
let isRemoteEscrow = false;
|
|
128
|
+
if (receipt) {
|
|
129
|
+
const { signature, ...receiptData2 } = receipt;
|
|
130
|
+
const publicKeyBuf = Buffer.from(receipt.requester_public_key, "hex");
|
|
131
|
+
const valid = verifyEscrowReceipt(receiptData2, signature, publicKeyBuf);
|
|
132
|
+
if (!valid) {
|
|
133
|
+
return { success: false, error: { code: -32603, message: "Invalid escrow receipt signature" } };
|
|
134
|
+
}
|
|
135
|
+
if (receipt.amount < creditsNeeded) {
|
|
136
|
+
return { success: false, error: { code: -32603, message: "Insufficient escrow amount" } };
|
|
137
|
+
}
|
|
138
|
+
const receiptAge = Date.now() - new Date(receipt.timestamp).getTime();
|
|
139
|
+
if (receiptAge > 5 * 60 * 1e3) {
|
|
140
|
+
return { success: false, error: { code: -32603, message: "Escrow receipt expired" } };
|
|
141
|
+
}
|
|
142
|
+
isRemoteEscrow = true;
|
|
143
|
+
} else {
|
|
144
|
+
try {
|
|
145
|
+
const balance = getBalance(creditDb, requester);
|
|
146
|
+
if (balance < creditsNeeded) {
|
|
147
|
+
return { success: false, error: { code: -32603, message: "Insufficient credits" } };
|
|
148
|
+
}
|
|
149
|
+
escrowId = holdEscrow(creditDb, requester, creditsNeeded, cardId);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
const msg = err instanceof AgentBnBError ? err.message : "Failed to hold escrow";
|
|
152
|
+
return { success: false, error: { code: -32603, message: msg } };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const startMs = Date.now();
|
|
156
|
+
const receiptData = isRemoteEscrow ? { receipt_released: true } : void 0;
|
|
157
|
+
const handleFailure = (status, latencyMs, message) => {
|
|
158
|
+
if (!isRemoteEscrow && escrowId) releaseEscrow(creditDb, escrowId);
|
|
159
|
+
updateReputation(registryDb, cardId, false, latencyMs);
|
|
160
|
+
try {
|
|
161
|
+
insertRequestLog(registryDb, {
|
|
162
|
+
id: randomUUID(),
|
|
163
|
+
card_id: cardId,
|
|
164
|
+
card_name: cardName,
|
|
165
|
+
skill_id: resolvedSkillId,
|
|
166
|
+
requester,
|
|
167
|
+
status,
|
|
168
|
+
latency_ms: latencyMs,
|
|
169
|
+
credits_charged: 0,
|
|
170
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
171
|
+
});
|
|
172
|
+
} catch {
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
success: false,
|
|
176
|
+
error: { code: -32603, message, ...receiptData ? { data: receiptData } : {} }
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
const handleSuccess = (result, latencyMs) => {
|
|
180
|
+
if (isRemoteEscrow && receipt) {
|
|
181
|
+
settleProviderEarning(creditDb, card.owner, receipt);
|
|
182
|
+
} else if (escrowId) {
|
|
183
|
+
settleEscrow(creditDb, escrowId, card.owner);
|
|
184
|
+
}
|
|
185
|
+
updateReputation(registryDb, cardId, true, latencyMs);
|
|
186
|
+
try {
|
|
187
|
+
insertRequestLog(registryDb, {
|
|
188
|
+
id: randomUUID(),
|
|
189
|
+
card_id: cardId,
|
|
190
|
+
card_name: cardName,
|
|
191
|
+
skill_id: resolvedSkillId,
|
|
192
|
+
requester,
|
|
193
|
+
status: "success",
|
|
194
|
+
latency_ms: latencyMs,
|
|
195
|
+
credits_charged: creditsNeeded,
|
|
196
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
197
|
+
});
|
|
198
|
+
} catch {
|
|
199
|
+
}
|
|
200
|
+
const successResult = isRemoteEscrow ? {
|
|
201
|
+
...typeof result === "object" && result !== null ? result : { data: result },
|
|
202
|
+
receipt_settled: true,
|
|
203
|
+
receipt_nonce: receipt.nonce
|
|
204
|
+
} : result;
|
|
205
|
+
return { success: true, result: successResult };
|
|
206
|
+
};
|
|
207
|
+
if (skillExecutor) {
|
|
208
|
+
const targetSkillId = resolvedSkillId ?? skillId ?? cardId;
|
|
209
|
+
try {
|
|
210
|
+
const execResult = await skillExecutor.execute(targetSkillId, params);
|
|
211
|
+
if (!execResult.success) {
|
|
212
|
+
return handleFailure("failure", execResult.latency_ms, execResult.error ?? "Execution failed");
|
|
213
|
+
}
|
|
214
|
+
return handleSuccess(execResult.result, execResult.latency_ms);
|
|
215
|
+
} catch (err) {
|
|
216
|
+
const message = err instanceof Error ? err.message : "Execution error";
|
|
217
|
+
return handleFailure("failure", Date.now() - startMs, message);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (!handlerUrl) {
|
|
221
|
+
return handleFailure("failure", Date.now() - startMs, "No skill executor or handler URL configured");
|
|
222
|
+
}
|
|
223
|
+
const controller = new AbortController();
|
|
224
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
225
|
+
try {
|
|
226
|
+
const response = await fetch(handlerUrl, {
|
|
227
|
+
method: "POST",
|
|
228
|
+
headers: { "Content-Type": "application/json" },
|
|
229
|
+
body: JSON.stringify({ card_id: cardId, skill_id: resolvedSkillId, params }),
|
|
230
|
+
signal: controller.signal
|
|
231
|
+
});
|
|
232
|
+
clearTimeout(timer);
|
|
233
|
+
if (!response.ok) {
|
|
234
|
+
return handleFailure("failure", Date.now() - startMs, `Handler returned ${response.status}`);
|
|
235
|
+
}
|
|
236
|
+
const result = await response.json();
|
|
237
|
+
return handleSuccess(result, Date.now() - startMs);
|
|
238
|
+
} catch (err) {
|
|
239
|
+
clearTimeout(timer);
|
|
240
|
+
const isTimeout = err instanceof Error && err.name === "AbortError";
|
|
241
|
+
return handleFailure(
|
|
242
|
+
isTimeout ? "timeout" : "failure",
|
|
243
|
+
Date.now() - startMs,
|
|
244
|
+
isTimeout ? "Execution timeout" : "Handler error"
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export {
|
|
250
|
+
generateKeyPair,
|
|
251
|
+
saveKeyPair,
|
|
252
|
+
loadKeyPair,
|
|
253
|
+
signEscrowReceipt,
|
|
254
|
+
settleRequesterEscrow,
|
|
255
|
+
releaseRequesterEscrow,
|
|
256
|
+
executeCapabilityRequest
|
|
257
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// src/cli/peers.ts
|
|
2
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
3
|
+
import { join as join2 } from "path";
|
|
4
|
+
|
|
5
|
+
// src/cli/config.ts
|
|
6
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
function getConfigDir() {
|
|
10
|
+
return process.env["AGENTBNB_DIR"] ?? join(homedir(), ".agentbnb");
|
|
11
|
+
}
|
|
12
|
+
function getConfigPath() {
|
|
13
|
+
return join(getConfigDir(), "config.json");
|
|
14
|
+
}
|
|
15
|
+
function loadConfig() {
|
|
16
|
+
const configPath = getConfigPath();
|
|
17
|
+
if (!existsSync(configPath)) return null;
|
|
18
|
+
try {
|
|
19
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
20
|
+
return JSON.parse(raw);
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function saveConfig(config) {
|
|
26
|
+
const dir = getConfigDir();
|
|
27
|
+
if (!existsSync(dir)) {
|
|
28
|
+
mkdirSync(dir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
writeFileSync(getConfigPath(), JSON.stringify(config, null, 2), "utf-8");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// src/cli/peers.ts
|
|
34
|
+
function getPeersPath() {
|
|
35
|
+
return join2(getConfigDir(), "peers.json");
|
|
36
|
+
}
|
|
37
|
+
function loadPeers() {
|
|
38
|
+
const peersPath = getPeersPath();
|
|
39
|
+
if (!existsSync2(peersPath)) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const raw = readFileSync2(peersPath, "utf-8");
|
|
44
|
+
return JSON.parse(raw);
|
|
45
|
+
} catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function writePeers(peers) {
|
|
50
|
+
const dir = getConfigDir();
|
|
51
|
+
if (!existsSync2(dir)) {
|
|
52
|
+
mkdirSync2(dir, { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
writeFileSync2(getPeersPath(), JSON.stringify(peers, null, 2), "utf-8");
|
|
55
|
+
}
|
|
56
|
+
function savePeer(peer) {
|
|
57
|
+
const peers = loadPeers();
|
|
58
|
+
const lowerName = peer.name.toLowerCase();
|
|
59
|
+
const existing = peers.findIndex((p) => p.name.toLowerCase() === lowerName);
|
|
60
|
+
if (existing >= 0) {
|
|
61
|
+
peers[existing] = peer;
|
|
62
|
+
} else {
|
|
63
|
+
peers.push(peer);
|
|
64
|
+
}
|
|
65
|
+
writePeers(peers);
|
|
66
|
+
}
|
|
67
|
+
function removePeer(name) {
|
|
68
|
+
const peers = loadPeers();
|
|
69
|
+
const lowerName = name.toLowerCase();
|
|
70
|
+
const filtered = peers.filter((p) => p.name.toLowerCase() !== lowerName);
|
|
71
|
+
if (filtered.length === peers.length) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
writePeers(filtered);
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
function findPeer(name) {
|
|
78
|
+
const peers = loadPeers();
|
|
79
|
+
const lowerName = name.toLowerCase();
|
|
80
|
+
return peers.find((p) => p.name.toLowerCase() === lowerName) ?? null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export {
|
|
84
|
+
getConfigDir,
|
|
85
|
+
loadConfig,
|
|
86
|
+
saveConfig,
|
|
87
|
+
loadPeers,
|
|
88
|
+
savePeer,
|
|
89
|
+
removePeer,
|
|
90
|
+
findPeer
|
|
91
|
+
};
|