agentbnb 5.1.11 → 6.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/dist/{card-RSGDCHCV.js → card-REW7BSWW.js} +1 -1
- package/dist/{chunk-EPIWHNB2.js → chunk-2TLZ6G2B.js} +446 -373
- package/dist/{chunk-WGZ5AGOX.js → chunk-3CIMVISQ.js} +24 -1
- package/dist/{chunk-NH2FIERR.js → chunk-574W3HHE.js} +1 -1
- package/dist/{chunk-B5FTAGFN.js → chunk-7XHDSWRD.js} +75 -75
- package/dist/{chunk-NLAWT4DT.js → chunk-7YLFLC5C.js} +6 -6
- package/dist/chunk-BP3L2TET.js +148 -0
- package/dist/{chunk-EGUOAHCW.js → chunk-C2T4BMRW.js} +12 -12
- package/dist/{chunk-5KFI5X7B.js → chunk-F53QQIM2.js} +1 -1
- package/dist/chunk-JR6TJDIF.js +425 -0
- package/dist/{chunk-DFBX3BBD.js → chunk-KA2VIEGM.js} +211 -16
- package/dist/chunk-NQTE577Q.js +159 -0
- package/dist/{chunk-WTXRY7R2.js → chunk-NYV3NE5Z.js} +157 -9
- package/dist/{chunk-UKT6H7YT.js → chunk-OZXCRLP3.js} +1 -1
- package/dist/{chunk-QITOPASZ.js → chunk-PSQHUZ7X.js} +1 -1
- package/dist/{chunk-EANI2N2V.js → chunk-RVYQSC6L.js} +2 -99
- package/dist/{chunk-MLS6IGGG.js → chunk-TQDV254A.js} +1 -1
- package/dist/{chunk-QQFBFV4V.js → chunk-TR6UZDNX.js} +57 -18
- package/dist/{chunk-ZX5623ER.js → chunk-VMH2YS2I.js} +1 -1
- package/dist/{chunk-XND2DWTZ.js → chunk-VPQ44XKE.js} +2 -2
- package/dist/{chunk-CSATDXZC.js → chunk-Y7T6IMM3.js} +1 -1
- package/dist/{chunk-FLY3WIQR.js → chunk-YRRVFTDR.js} +3 -3
- package/dist/cli/index.js +261 -125
- package/dist/{client-T5MTY3CS.js → client-HRYRJKSA.js} +3 -3
- package/dist/{conduct-WU3VEXB6.js → conduct-LF6FYPLD.js} +11 -11
- package/dist/conduct-QAFZIEY6.js +21 -0
- package/dist/{conductor-mode-ZMTFZGJP.js → conductor-mode-NUDQLZFM.js} +309 -13
- package/dist/conductor-mode-YQ6QSPPT.js +275 -0
- package/dist/{execute-4D4ITQCL.js → execute-ITHIYYOX.js} +4 -3
- package/dist/execute-PNJFABVJ.js +14 -0
- package/dist/index.d.ts +555 -0
- package/dist/index.js +592 -83
- package/dist/{process-guard-CC7CNRQJ.js → process-guard-QCCBGILS.js} +1 -1
- package/dist/publish-capability-TS6CNR5G.js +12 -0
- package/dist/{request-VOXBFUOG.js → request-P6QCTCCG.js} +14 -14
- package/dist/{serve-skill-IH7UAJNR.js → serve-skill-EZOL7UYN.js} +10 -9
- package/dist/{server-JVQW2TID.js → server-3G6ZTASA.js} +16 -16
- package/dist/{service-coordinator-EYRDTHL5.js → service-coordinator-CRSE4GWC.js} +174 -242
- package/dist/skill-config-4W5W5O6T.js +22 -0
- package/dist/skills/agentbnb/bootstrap.js +227 -67
- package/package.json +1 -1
- package/skills/agentbnb/SKILL.md +35 -0
- package/skills/agentbnb/bootstrap.ts +126 -8
- package/skills/agentbnb/install.sh +49 -9
- package/dist/chunk-CRFCWD6V.js +0 -366
- package/dist/conduct-6LKIJJKQ.js +0 -21
- package/dist/conductor-mode-Q4IIDY5E.js +0 -123
- package/dist/execute-T7Y6RKSW.js +0 -13
|
@@ -1,24 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
confirmEscrowDebit,
|
|
3
|
-
getBalance,
|
|
4
|
-
holdEscrow,
|
|
5
|
-
initFeedbackTable,
|
|
6
|
-
recordEarning,
|
|
7
|
-
releaseEscrow,
|
|
8
|
-
settleEscrow,
|
|
9
|
-
verifyEscrowReceipt
|
|
10
|
-
} from "./chunk-CRFCWD6V.js";
|
|
11
1
|
import {
|
|
12
2
|
AgentBnBError,
|
|
13
3
|
AnyCardSchema,
|
|
14
4
|
CapabilityCardSchema
|
|
15
|
-
} from "./chunk-
|
|
16
|
-
|
|
17
|
-
// src/gateway/execute.ts
|
|
18
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
19
|
-
|
|
20
|
-
// src/registry/store.ts
|
|
21
|
-
import Database from "better-sqlite3";
|
|
5
|
+
} from "./chunk-3CIMVISQ.js";
|
|
22
6
|
|
|
23
7
|
// src/registry/request-log.ts
|
|
24
8
|
var SINCE_MS = {
|
|
@@ -54,11 +38,27 @@ function createRequestLogTable(db) {
|
|
|
54
38
|
db.exec("ALTER TABLE request_log ADD COLUMN tier_invoked INTEGER");
|
|
55
39
|
} catch {
|
|
56
40
|
}
|
|
41
|
+
try {
|
|
42
|
+
db.exec("ALTER TABLE request_log ADD COLUMN failure_reason TEXT");
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
db.exec("ALTER TABLE request_log ADD COLUMN team_id TEXT");
|
|
47
|
+
} catch {
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
db.exec("ALTER TABLE request_log ADD COLUMN role TEXT");
|
|
51
|
+
} catch {
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
db.exec("ALTER TABLE request_log ADD COLUMN capability_type TEXT");
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
57
|
}
|
|
58
58
|
function insertRequestLog(db, entry) {
|
|
59
59
|
const stmt = db.prepare(`
|
|
60
|
-
INSERT INTO request_log (id, card_id, card_name, requester, status, latency_ms, credits_charged, created_at, skill_id, action_type, tier_invoked)
|
|
61
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
60
|
+
INSERT INTO request_log (id, card_id, card_name, requester, status, latency_ms, credits_charged, created_at, skill_id, action_type, tier_invoked, failure_reason, team_id, role, capability_type)
|
|
61
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
62
62
|
`);
|
|
63
63
|
stmt.run(
|
|
64
64
|
entry.id,
|
|
@@ -71,7 +71,11 @@ function insertRequestLog(db, entry) {
|
|
|
71
71
|
entry.created_at,
|
|
72
72
|
entry.skill_id ?? null,
|
|
73
73
|
entry.action_type ?? null,
|
|
74
|
-
entry.tier_invoked ?? null
|
|
74
|
+
entry.tier_invoked ?? null,
|
|
75
|
+
entry.failure_reason ?? null,
|
|
76
|
+
entry.team_id ?? null,
|
|
77
|
+
entry.role ?? null,
|
|
78
|
+
entry.capability_type ?? null
|
|
75
79
|
);
|
|
76
80
|
}
|
|
77
81
|
function getSkillRequestCount(db, skillId, windowMs) {
|
|
@@ -113,7 +117,7 @@ function getRequestLog(db, limit = 10, since) {
|
|
|
113
117
|
if (since !== void 0) {
|
|
114
118
|
const cutoff = new Date(Date.now() - SINCE_MS[since]).toISOString();
|
|
115
119
|
const stmt2 = db.prepare(`
|
|
116
|
-
SELECT id, card_id, card_name, requester, status, latency_ms, credits_charged, created_at, skill_id, action_type, tier_invoked
|
|
120
|
+
SELECT id, card_id, card_name, requester, status, latency_ms, credits_charged, created_at, skill_id, action_type, tier_invoked, failure_reason, team_id, role, capability_type
|
|
117
121
|
FROM request_log
|
|
118
122
|
WHERE created_at >= ?
|
|
119
123
|
ORDER BY created_at DESC
|
|
@@ -122,7 +126,7 @@ function getRequestLog(db, limit = 10, since) {
|
|
|
122
126
|
return stmt2.all(cutoff, limit);
|
|
123
127
|
}
|
|
124
128
|
const stmt = db.prepare(`
|
|
125
|
-
SELECT id, card_id, card_name, requester, status, latency_ms, credits_charged, created_at, skill_id, action_type, tier_invoked
|
|
129
|
+
SELECT id, card_id, card_name, requester, status, latency_ms, credits_charged, created_at, skill_id, action_type, tier_invoked, failure_reason, team_id, role
|
|
126
130
|
FROM request_log
|
|
127
131
|
ORDER BY created_at DESC
|
|
128
132
|
LIMIT ?
|
|
@@ -130,8 +134,104 @@ function getRequestLog(db, limit = 10, since) {
|
|
|
130
134
|
return stmt.all(limit);
|
|
131
135
|
}
|
|
132
136
|
|
|
133
|
-
// src/
|
|
137
|
+
// src/registry/store.ts
|
|
138
|
+
import Database from "better-sqlite3";
|
|
139
|
+
|
|
140
|
+
// src/feedback/store.ts
|
|
134
141
|
import { randomUUID } from "crypto";
|
|
142
|
+
function initFeedbackTable(db) {
|
|
143
|
+
db.exec(`
|
|
144
|
+
CREATE TABLE IF NOT EXISTS feedback (
|
|
145
|
+
id TEXT PRIMARY KEY,
|
|
146
|
+
transaction_id TEXT NOT NULL,
|
|
147
|
+
provider_agent TEXT NOT NULL,
|
|
148
|
+
skill_id TEXT NOT NULL,
|
|
149
|
+
requester_agent TEXT NOT NULL,
|
|
150
|
+
rating INTEGER NOT NULL,
|
|
151
|
+
latency_ms INTEGER NOT NULL,
|
|
152
|
+
result_quality TEXT NOT NULL,
|
|
153
|
+
quality_details TEXT,
|
|
154
|
+
would_reuse INTEGER NOT NULL,
|
|
155
|
+
cost_value_ratio TEXT NOT NULL,
|
|
156
|
+
timestamp TEXT NOT NULL,
|
|
157
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
CREATE INDEX IF NOT EXISTS feedback_provider_idx ON feedback(provider_agent);
|
|
161
|
+
CREATE INDEX IF NOT EXISTS feedback_skill_idx ON feedback(skill_id);
|
|
162
|
+
`);
|
|
163
|
+
}
|
|
164
|
+
function insertFeedback(db, feedback) {
|
|
165
|
+
const id = randomUUID();
|
|
166
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
167
|
+
db.prepare(`
|
|
168
|
+
INSERT INTO feedback (
|
|
169
|
+
id, transaction_id, provider_agent, skill_id, requester_agent,
|
|
170
|
+
rating, latency_ms, result_quality, quality_details,
|
|
171
|
+
would_reuse, cost_value_ratio, timestamp, created_at
|
|
172
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
173
|
+
`).run(
|
|
174
|
+
id,
|
|
175
|
+
feedback.transaction_id,
|
|
176
|
+
feedback.provider_agent,
|
|
177
|
+
feedback.skill_id,
|
|
178
|
+
feedback.requester_agent,
|
|
179
|
+
feedback.rating,
|
|
180
|
+
feedback.latency_ms,
|
|
181
|
+
feedback.result_quality,
|
|
182
|
+
feedback.quality_details ?? null,
|
|
183
|
+
feedback.would_reuse ? 1 : 0,
|
|
184
|
+
feedback.cost_value_ratio,
|
|
185
|
+
feedback.timestamp,
|
|
186
|
+
now
|
|
187
|
+
);
|
|
188
|
+
return id;
|
|
189
|
+
}
|
|
190
|
+
function getFeedbackForSkill(db, skillId, limit = 20) {
|
|
191
|
+
const rows = db.prepare(`
|
|
192
|
+
SELECT * FROM feedback
|
|
193
|
+
WHERE skill_id = ?
|
|
194
|
+
ORDER BY timestamp DESC
|
|
195
|
+
LIMIT ?
|
|
196
|
+
`).all(skillId, limit);
|
|
197
|
+
return rows.map(rowToFeedback);
|
|
198
|
+
}
|
|
199
|
+
function getFeedbackForProvider(db, providerAgent, sinceDays) {
|
|
200
|
+
let rows;
|
|
201
|
+
if (sinceDays !== void 0) {
|
|
202
|
+
rows = db.prepare(`
|
|
203
|
+
SELECT * FROM feedback
|
|
204
|
+
WHERE provider_agent = ?
|
|
205
|
+
AND timestamp >= datetime('now', ? || ' days')
|
|
206
|
+
ORDER BY timestamp DESC
|
|
207
|
+
`).all(providerAgent, `-${sinceDays}`);
|
|
208
|
+
} else {
|
|
209
|
+
rows = db.prepare(`
|
|
210
|
+
SELECT * FROM feedback
|
|
211
|
+
WHERE provider_agent = ?
|
|
212
|
+
ORDER BY timestamp DESC
|
|
213
|
+
`).all(providerAgent);
|
|
214
|
+
}
|
|
215
|
+
return rows.map(rowToFeedback);
|
|
216
|
+
}
|
|
217
|
+
function rowToFeedback(row) {
|
|
218
|
+
return {
|
|
219
|
+
transaction_id: row["transaction_id"],
|
|
220
|
+
provider_agent: row["provider_agent"],
|
|
221
|
+
skill_id: row["skill_id"],
|
|
222
|
+
requester_agent: row["requester_agent"],
|
|
223
|
+
rating: row["rating"],
|
|
224
|
+
latency_ms: row["latency_ms"],
|
|
225
|
+
result_quality: row["result_quality"],
|
|
226
|
+
quality_details: row["quality_details"] ?? void 0,
|
|
227
|
+
would_reuse: row["would_reuse"] === 1,
|
|
228
|
+
cost_value_ratio: row["cost_value_ratio"],
|
|
229
|
+
timestamp: row["timestamp"]
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// src/evolution/store.ts
|
|
234
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
135
235
|
function initEvolutionTable(db) {
|
|
136
236
|
db.exec(`
|
|
137
237
|
CREATE TABLE IF NOT EXISTS evolution_versions (
|
|
@@ -151,7 +251,7 @@ function initEvolutionTable(db) {
|
|
|
151
251
|
`);
|
|
152
252
|
}
|
|
153
253
|
function insertEvolution(db, ev) {
|
|
154
|
-
const id =
|
|
254
|
+
const id = randomUUID2();
|
|
155
255
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
156
256
|
db.prepare(`
|
|
157
257
|
INSERT INTO evolution_versions (
|
|
@@ -233,6 +333,17 @@ var V2_FTS_TRIGGERS = `
|
|
|
233
333
|
FROM json_each(json_extract(new.data, '$.metadata.tags'))),
|
|
234
334
|
''
|
|
235
335
|
)
|
|
336
|
+
|| ' ' || COALESCE(
|
|
337
|
+
(SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
|
|
338
|
+
FROM json_each(json_extract(new.data, '$.skills')) AS skill),
|
|
339
|
+
''
|
|
340
|
+
)
|
|
341
|
+
|| ' ' || COALESCE(
|
|
342
|
+
(SELECT group_concat(cap_type.value, ' ')
|
|
343
|
+
FROM json_each(json_extract(new.data, '$.skills')) AS skill,
|
|
344
|
+
json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
|
|
345
|
+
''
|
|
346
|
+
)
|
|
236
347
|
);
|
|
237
348
|
END;
|
|
238
349
|
|
|
@@ -262,6 +373,17 @@ var V2_FTS_TRIGGERS = `
|
|
|
262
373
|
FROM json_each(json_extract(old.data, '$.metadata.tags'))),
|
|
263
374
|
''
|
|
264
375
|
)
|
|
376
|
+
|| ' ' || COALESCE(
|
|
377
|
+
(SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
|
|
378
|
+
FROM json_each(json_extract(old.data, '$.skills')) AS skill),
|
|
379
|
+
''
|
|
380
|
+
)
|
|
381
|
+
|| ' ' || COALESCE(
|
|
382
|
+
(SELECT group_concat(cap_type.value, ' ')
|
|
383
|
+
FROM json_each(json_extract(old.data, '$.skills')) AS skill,
|
|
384
|
+
json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
|
|
385
|
+
''
|
|
386
|
+
)
|
|
265
387
|
);
|
|
266
388
|
INSERT INTO cards_fts(rowid, id, owner, name, description, tags)
|
|
267
389
|
VALUES (
|
|
@@ -287,6 +409,17 @@ var V2_FTS_TRIGGERS = `
|
|
|
287
409
|
FROM json_each(json_extract(new.data, '$.metadata.tags'))),
|
|
288
410
|
''
|
|
289
411
|
)
|
|
412
|
+
|| ' ' || COALESCE(
|
|
413
|
+
(SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
|
|
414
|
+
FROM json_each(json_extract(new.data, '$.skills')) AS skill),
|
|
415
|
+
''
|
|
416
|
+
)
|
|
417
|
+
|| ' ' || COALESCE(
|
|
418
|
+
(SELECT group_concat(cap_type.value, ' ')
|
|
419
|
+
FROM json_each(json_extract(new.data, '$.skills')) AS skill,
|
|
420
|
+
json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
|
|
421
|
+
''
|
|
422
|
+
)
|
|
290
423
|
);
|
|
291
424
|
END;
|
|
292
425
|
|
|
@@ -316,6 +449,17 @@ var V2_FTS_TRIGGERS = `
|
|
|
316
449
|
FROM json_each(json_extract(old.data, '$.metadata.tags'))),
|
|
317
450
|
''
|
|
318
451
|
)
|
|
452
|
+
|| ' ' || COALESCE(
|
|
453
|
+
(SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
|
|
454
|
+
FROM json_each(json_extract(old.data, '$.skills')) AS skill),
|
|
455
|
+
''
|
|
456
|
+
)
|
|
457
|
+
|| ' ' || COALESCE(
|
|
458
|
+
(SELECT group_concat(cap_type.value, ' ')
|
|
459
|
+
FROM json_each(json_extract(old.data, '$.skills')) AS skill,
|
|
460
|
+
json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
|
|
461
|
+
''
|
|
462
|
+
)
|
|
319
463
|
);
|
|
320
464
|
END;
|
|
321
465
|
`;
|
|
@@ -420,10 +564,17 @@ function migrateV1toV2(db) {
|
|
|
420
564
|
if (skills.length > 0) {
|
|
421
565
|
name = skills.map((s) => String(s["name"] ?? "")).join(" ");
|
|
422
566
|
description = skills.map((s) => String(s["description"] ?? "")).join(" ");
|
|
423
|
-
tags =
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
567
|
+
tags = [
|
|
568
|
+
// tags from metadata.tags[]
|
|
569
|
+
...skills.flatMap((s) => {
|
|
570
|
+
const meta = s["metadata"];
|
|
571
|
+
return meta?.["tags"] ?? [];
|
|
572
|
+
}),
|
|
573
|
+
// capability_type (singular)
|
|
574
|
+
...skills.map((s) => s["capability_type"]).filter((v) => typeof v === "string" && v.length > 0),
|
|
575
|
+
// capability_types[] (plural)
|
|
576
|
+
...skills.flatMap((s) => s["capability_types"] ?? [])
|
|
577
|
+
].join(" ");
|
|
427
578
|
} else {
|
|
428
579
|
name = String(data["name"] ?? "");
|
|
429
580
|
description = String(data["description"] ?? "");
|
|
@@ -562,366 +713,271 @@ function listCards(db, owner) {
|
|
|
562
713
|
}
|
|
563
714
|
return rows.map((row) => JSON.parse(row.data));
|
|
564
715
|
}
|
|
716
|
+
function getCardsByCapabilityType(db, capabilityType) {
|
|
717
|
+
const rows = db.prepare(
|
|
718
|
+
"SELECT data FROM capability_cards WHERE json_extract(data, '$.capability_type') = ?"
|
|
719
|
+
).all(capabilityType);
|
|
720
|
+
return rows.map((row) => JSON.parse(row.data));
|
|
721
|
+
}
|
|
722
|
+
function getCardsBySkillCapability(db, capabilityType) {
|
|
723
|
+
const rows = db.prepare("SELECT data FROM capability_cards").all();
|
|
724
|
+
return rows.map((row) => JSON.parse(row.data)).filter((card) => {
|
|
725
|
+
const skills = card["skills"];
|
|
726
|
+
if (!skills) return false;
|
|
727
|
+
return skills.some((skill) => {
|
|
728
|
+
if (skill["capability_type"] === capabilityType) return true;
|
|
729
|
+
const types = skill["capability_types"];
|
|
730
|
+
return Array.isArray(types) && types.includes(capabilityType);
|
|
731
|
+
});
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// src/credit/ledger.ts
|
|
736
|
+
import Database2 from "better-sqlite3";
|
|
737
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
738
|
+
var CREDIT_SCHEMA = `
|
|
739
|
+
CREATE TABLE IF NOT EXISTS credit_balances (
|
|
740
|
+
owner TEXT PRIMARY KEY,
|
|
741
|
+
balance INTEGER NOT NULL DEFAULT 0,
|
|
742
|
+
updated_at TEXT NOT NULL
|
|
743
|
+
);
|
|
744
|
+
|
|
745
|
+
CREATE TABLE IF NOT EXISTS credit_transactions (
|
|
746
|
+
id TEXT PRIMARY KEY,
|
|
747
|
+
owner TEXT NOT NULL,
|
|
748
|
+
amount INTEGER NOT NULL,
|
|
749
|
+
reason TEXT NOT NULL,
|
|
750
|
+
reference_id TEXT,
|
|
751
|
+
created_at TEXT NOT NULL
|
|
752
|
+
);
|
|
565
753
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
754
|
+
CREATE TABLE IF NOT EXISTS credit_escrow (
|
|
755
|
+
id TEXT PRIMARY KEY,
|
|
756
|
+
owner TEXT NOT NULL,
|
|
757
|
+
amount INTEGER NOT NULL,
|
|
758
|
+
card_id TEXT NOT NULL,
|
|
759
|
+
status TEXT NOT NULL DEFAULT 'held',
|
|
760
|
+
created_at TEXT NOT NULL,
|
|
761
|
+
settled_at TEXT
|
|
574
762
|
);
|
|
575
|
-
|
|
763
|
+
|
|
764
|
+
CREATE INDEX IF NOT EXISTS idx_transactions_owner ON credit_transactions(owner, created_at);
|
|
765
|
+
CREATE INDEX IF NOT EXISTS idx_escrow_owner ON credit_escrow(owner);
|
|
766
|
+
`;
|
|
767
|
+
function openCreditDb(path = ":memory:") {
|
|
768
|
+
const db = new Database2(path);
|
|
769
|
+
db.pragma("journal_mode = WAL");
|
|
770
|
+
db.pragma("foreign_keys = ON");
|
|
771
|
+
db.exec(CREDIT_SCHEMA);
|
|
772
|
+
return db;
|
|
576
773
|
}
|
|
577
|
-
function
|
|
578
|
-
|
|
774
|
+
function bootstrapAgent(db, owner, amount = 100) {
|
|
775
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
776
|
+
db.transaction(() => {
|
|
777
|
+
const result = db.prepare("INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, ?, ?)").run(owner, amount, now);
|
|
778
|
+
if (result.changes > 0) {
|
|
779
|
+
db.prepare(
|
|
780
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
781
|
+
).run(randomUUID3(), owner, amount, "bootstrap", null, now);
|
|
782
|
+
}
|
|
783
|
+
})();
|
|
579
784
|
}
|
|
580
|
-
function
|
|
581
|
-
|
|
785
|
+
function getBalance(db, owner) {
|
|
786
|
+
const row = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(owner);
|
|
787
|
+
return row?.balance ?? 0;
|
|
582
788
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
});
|
|
618
|
-
} catch {
|
|
789
|
+
function getTransactions(db, owner, limit = 100) {
|
|
790
|
+
return db.prepare(
|
|
791
|
+
"SELECT id, owner, amount, reason, reference_id, created_at FROM credit_transactions WHERE owner = ? ORDER BY created_at DESC LIMIT ?"
|
|
792
|
+
).all(owner, limit);
|
|
793
|
+
}
|
|
794
|
+
function recordEarning(db, owner, amount, _cardId, receiptNonce) {
|
|
795
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
796
|
+
db.transaction(() => {
|
|
797
|
+
const existing = db.prepare(
|
|
798
|
+
"SELECT id FROM credit_transactions WHERE reference_id = ? AND reason = 'remote_earning'"
|
|
799
|
+
).get(receiptNonce);
|
|
800
|
+
if (existing) return;
|
|
801
|
+
db.prepare(
|
|
802
|
+
"INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, 0, ?)"
|
|
803
|
+
).run(owner, now);
|
|
804
|
+
db.prepare(
|
|
805
|
+
"UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
|
|
806
|
+
).run(amount, now, owner);
|
|
807
|
+
db.prepare(
|
|
808
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
809
|
+
).run(randomUUID3(), owner, amount, "remote_earning", receiptNonce, now);
|
|
810
|
+
})();
|
|
811
|
+
}
|
|
812
|
+
function migrateOwner(db, oldOwner, newOwner) {
|
|
813
|
+
if (oldOwner === newOwner) return;
|
|
814
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
815
|
+
db.transaction(() => {
|
|
816
|
+
const oldRow = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(oldOwner);
|
|
817
|
+
if (!oldRow) return;
|
|
818
|
+
const newRow = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(newOwner);
|
|
819
|
+
if (newRow) {
|
|
820
|
+
db.prepare("UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?").run(oldRow.balance, now, newOwner);
|
|
821
|
+
} else {
|
|
822
|
+
db.prepare("UPDATE credit_balances SET owner = ?, updated_at = ? WHERE owner = ?").run(newOwner, now, oldOwner);
|
|
619
823
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
let creditsNeeded;
|
|
623
|
-
let cardName;
|
|
624
|
-
let resolvedSkillId;
|
|
625
|
-
const rawCard = card;
|
|
626
|
-
if (Array.isArray(rawCard["skills"])) {
|
|
627
|
-
const v2card = card;
|
|
628
|
-
const skill = skillId ? v2card.skills.find((s) => s.id === skillId) : v2card.skills[0];
|
|
629
|
-
if (!skill) {
|
|
630
|
-
return { success: false, error: { code: -32602, message: `Skill not found: ${skillId}` } };
|
|
824
|
+
if (newRow) {
|
|
825
|
+
db.prepare("DELETE FROM credit_balances WHERE owner = ?").run(oldOwner);
|
|
631
826
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
const
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
if (!valid) {
|
|
647
|
-
return { success: false, error: { code: -32603, message: "Invalid escrow receipt signature" } };
|
|
827
|
+
db.prepare("UPDATE credit_transactions SET owner = ? WHERE owner = ?").run(newOwner, oldOwner);
|
|
828
|
+
db.prepare("UPDATE credit_escrow SET owner = ? WHERE owner = ?").run(newOwner, oldOwner);
|
|
829
|
+
})();
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// src/credit/escrow.ts
|
|
833
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
834
|
+
function holdEscrow(db, owner, amount, cardId) {
|
|
835
|
+
const escrowId = randomUUID4();
|
|
836
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
837
|
+
const hold = db.transaction(() => {
|
|
838
|
+
const row = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(owner);
|
|
839
|
+
if (!row || row.balance < amount) {
|
|
840
|
+
throw new AgentBnBError("Insufficient credits", "INSUFFICIENT_CREDITS");
|
|
648
841
|
}
|
|
649
|
-
|
|
650
|
-
|
|
842
|
+
db.prepare(
|
|
843
|
+
"UPDATE credit_balances SET balance = balance - ?, updated_at = ? WHERE owner = ? AND balance >= ?"
|
|
844
|
+
).run(amount, now, owner, amount);
|
|
845
|
+
db.prepare(
|
|
846
|
+
"INSERT INTO credit_escrow (id, owner, amount, card_id, status, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
847
|
+
).run(escrowId, owner, amount, cardId, "held", now);
|
|
848
|
+
db.prepare(
|
|
849
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
850
|
+
).run(randomUUID4(), owner, -amount, "escrow_hold", escrowId, now);
|
|
851
|
+
});
|
|
852
|
+
hold();
|
|
853
|
+
return escrowId;
|
|
854
|
+
}
|
|
855
|
+
function settleEscrow(db, escrowId, recipientOwner) {
|
|
856
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
857
|
+
const settle = db.transaction(() => {
|
|
858
|
+
const escrow = db.prepare("SELECT id, owner, amount, status FROM credit_escrow WHERE id = ?").get(escrowId);
|
|
859
|
+
if (!escrow) {
|
|
860
|
+
throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
|
|
651
861
|
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
862
|
+
if (escrow.status !== "held") {
|
|
863
|
+
throw new AgentBnBError(
|
|
864
|
+
`Escrow ${escrowId} is already ${escrow.status}`,
|
|
865
|
+
"ESCROW_ALREADY_SETTLED"
|
|
866
|
+
);
|
|
655
867
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
868
|
+
db.prepare(
|
|
869
|
+
"INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, 0, ?)"
|
|
870
|
+
).run(recipientOwner, now);
|
|
871
|
+
db.prepare(
|
|
872
|
+
"UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
|
|
873
|
+
).run(escrow.amount, now, recipientOwner);
|
|
874
|
+
db.prepare(
|
|
875
|
+
"UPDATE credit_escrow SET status = ?, settled_at = ? WHERE id = ?"
|
|
876
|
+
).run("settled", now, escrowId);
|
|
877
|
+
db.prepare(
|
|
878
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
879
|
+
).run(randomUUID4(), recipientOwner, escrow.amount, "settlement", escrowId, now);
|
|
880
|
+
});
|
|
881
|
+
settle();
|
|
882
|
+
}
|
|
883
|
+
function releaseEscrow(db, escrowId) {
|
|
884
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
885
|
+
const release = db.transaction(() => {
|
|
886
|
+
const escrow = db.prepare("SELECT id, owner, amount, status FROM credit_escrow WHERE id = ?").get(escrowId);
|
|
887
|
+
if (!escrow) {
|
|
888
|
+
throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
|
|
667
889
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
updateReputation(registryDb, cardId, false, latencyMs);
|
|
674
|
-
try {
|
|
675
|
-
insertRequestLog(registryDb, {
|
|
676
|
-
id: randomUUID2(),
|
|
677
|
-
card_id: cardId,
|
|
678
|
-
card_name: cardName,
|
|
679
|
-
skill_id: resolvedSkillId,
|
|
680
|
-
requester,
|
|
681
|
-
status,
|
|
682
|
-
latency_ms: latencyMs,
|
|
683
|
-
credits_charged: 0,
|
|
684
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
685
|
-
});
|
|
686
|
-
} catch {
|
|
890
|
+
if (escrow.status !== "held") {
|
|
891
|
+
throw new AgentBnBError(
|
|
892
|
+
`Escrow ${escrowId} is already ${escrow.status}`,
|
|
893
|
+
"ESCROW_ALREADY_SETTLED"
|
|
894
|
+
);
|
|
687
895
|
}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
896
|
+
db.prepare(
|
|
897
|
+
"UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
|
|
898
|
+
).run(escrow.amount, now, escrow.owner);
|
|
899
|
+
db.prepare(
|
|
900
|
+
"UPDATE credit_escrow SET status = ?, settled_at = ? WHERE id = ?"
|
|
901
|
+
).run("released", now, escrowId);
|
|
902
|
+
db.prepare(
|
|
903
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
904
|
+
).run(randomUUID4(), escrow.owner, escrow.amount, "refund", escrowId, now);
|
|
905
|
+
});
|
|
906
|
+
release();
|
|
907
|
+
}
|
|
908
|
+
function confirmEscrowDebit(db, escrowId) {
|
|
909
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
910
|
+
const confirm = db.transaction(() => {
|
|
911
|
+
const escrow = db.prepare("SELECT id, owner, amount, status FROM credit_escrow WHERE id = ?").get(escrowId);
|
|
912
|
+
if (!escrow) {
|
|
913
|
+
throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
|
|
698
914
|
}
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
card_name: cardName,
|
|
705
|
-
skill_id: resolvedSkillId,
|
|
706
|
-
requester,
|
|
707
|
-
status: "success",
|
|
708
|
-
latency_ms: latencyMs,
|
|
709
|
-
credits_charged: creditsNeeded,
|
|
710
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
711
|
-
});
|
|
712
|
-
} catch {
|
|
915
|
+
if (escrow.status !== "held") {
|
|
916
|
+
throw new AgentBnBError(
|
|
917
|
+
`Escrow ${escrowId} is already ${escrow.status}`,
|
|
918
|
+
"ESCROW_ALREADY_SETTLED"
|
|
919
|
+
);
|
|
713
920
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
921
|
+
db.prepare(
|
|
922
|
+
"UPDATE credit_escrow SET status = ?, settled_at = ? WHERE id = ?"
|
|
923
|
+
).run("settled", now, escrowId);
|
|
924
|
+
db.prepare(
|
|
925
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
926
|
+
).run(randomUUID4(), escrow.owner, 0, "remote_settlement_confirmed", escrowId, now);
|
|
927
|
+
});
|
|
928
|
+
confirm();
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// src/credit/signing.ts
|
|
932
|
+
import { generateKeyPairSync, sign, verify, createPublicKey, createPrivateKey } from "crypto";
|
|
933
|
+
import { writeFileSync, readFileSync, existsSync, chmodSync } from "fs";
|
|
934
|
+
import { join } from "path";
|
|
935
|
+
function generateKeyPair() {
|
|
936
|
+
const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
|
|
937
|
+
publicKeyEncoding: { type: "spki", format: "der" },
|
|
938
|
+
privateKeyEncoding: { type: "pkcs8", format: "der" }
|
|
939
|
+
});
|
|
940
|
+
return {
|
|
941
|
+
publicKey: Buffer.from(publicKey),
|
|
942
|
+
privateKey: Buffer.from(privateKey)
|
|
720
943
|
};
|
|
721
|
-
if (skillExecutor) {
|
|
722
|
-
let targetSkillId = resolvedSkillId ?? skillId;
|
|
723
|
-
if (!targetSkillId) {
|
|
724
|
-
const available = skillExecutor.listSkills();
|
|
725
|
-
if (available.length > 0) {
|
|
726
|
-
targetSkillId = available[0];
|
|
727
|
-
} else {
|
|
728
|
-
return handleFailure(
|
|
729
|
-
"failure",
|
|
730
|
-
Date.now() - startMs,
|
|
731
|
-
"No skill_id specified and no skills registered on this provider."
|
|
732
|
-
);
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
try {
|
|
736
|
-
const execResult = await skillExecutor.execute(targetSkillId, params, onProgress);
|
|
737
|
-
if (!execResult.success) {
|
|
738
|
-
return handleFailure("failure", execResult.latency_ms, execResult.error ?? "Execution failed");
|
|
739
|
-
}
|
|
740
|
-
return handleSuccess(execResult.result, execResult.latency_ms);
|
|
741
|
-
} catch (err) {
|
|
742
|
-
const message = err instanceof Error ? err.message : "Execution error";
|
|
743
|
-
return handleFailure("failure", Date.now() - startMs, message);
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
if (!handlerUrl) {
|
|
747
|
-
return handleFailure("failure", Date.now() - startMs, "No skill executor or handler URL configured");
|
|
748
|
-
}
|
|
749
|
-
const controller = new AbortController();
|
|
750
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
751
|
-
try {
|
|
752
|
-
const response = await fetch(handlerUrl, {
|
|
753
|
-
method: "POST",
|
|
754
|
-
headers: { "Content-Type": "application/json" },
|
|
755
|
-
body: JSON.stringify({ card_id: cardId, skill_id: resolvedSkillId, params }),
|
|
756
|
-
signal: controller.signal
|
|
757
|
-
});
|
|
758
|
-
clearTimeout(timer);
|
|
759
|
-
if (!response.ok) {
|
|
760
|
-
return handleFailure("failure", Date.now() - startMs, `Handler returned ${response.status}`);
|
|
761
|
-
}
|
|
762
|
-
const result = await response.json();
|
|
763
|
-
return handleSuccess(result, Date.now() - startMs);
|
|
764
|
-
} catch (err) {
|
|
765
|
-
clearTimeout(timer);
|
|
766
|
-
const isTimeout = err instanceof Error && err.name === "AbortError";
|
|
767
|
-
return handleFailure(
|
|
768
|
-
isTimeout ? "timeout" : "failure",
|
|
769
|
-
Date.now() - startMs,
|
|
770
|
-
isTimeout ? "Execution timeout" : "Handler error"
|
|
771
|
-
);
|
|
772
|
-
}
|
|
773
944
|
}
|
|
774
|
-
|
|
775
|
-
const
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
credits_refunded: 0,
|
|
787
|
-
error: `Total requested credits (${sumMaxCredits}) exceeds total_budget (${total_budget})`
|
|
788
|
-
})),
|
|
789
|
-
total_credits_spent: 0,
|
|
790
|
-
total_credits_refunded: 0,
|
|
791
|
-
success: false
|
|
792
|
-
};
|
|
945
|
+
function saveKeyPair(configDir, keys) {
|
|
946
|
+
const privatePath = join(configDir, "private.key");
|
|
947
|
+
const publicPath = join(configDir, "public.key");
|
|
948
|
+
writeFileSync(privatePath, keys.privateKey);
|
|
949
|
+
chmodSync(privatePath, 384);
|
|
950
|
+
writeFileSync(publicPath, keys.publicKey);
|
|
951
|
+
}
|
|
952
|
+
function loadKeyPair(configDir) {
|
|
953
|
+
const privatePath = join(configDir, "private.key");
|
|
954
|
+
const publicPath = join(configDir, "public.key");
|
|
955
|
+
if (!existsSync(privatePath) || !existsSync(publicPath)) {
|
|
956
|
+
throw new AgentBnBError("Keypair not found. Run `agentbnb init` to generate one.", "KEYPAIR_NOT_FOUND");
|
|
793
957
|
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
return {
|
|
798
|
-
request_index: index,
|
|
799
|
-
status: "failed",
|
|
800
|
-
credits_spent: 0,
|
|
801
|
-
credits_refunded: 0,
|
|
802
|
-
error: `Card/skill not found: ${item.skill_id}`
|
|
803
|
-
};
|
|
804
|
-
}
|
|
805
|
-
const rawCard = card;
|
|
806
|
-
let creditsNeeded;
|
|
807
|
-
let resolvedSkillId;
|
|
808
|
-
if (Array.isArray(rawCard["skills"])) {
|
|
809
|
-
const v2card = card;
|
|
810
|
-
const skill = v2card.skills[0];
|
|
811
|
-
if (!skill) {
|
|
812
|
-
return {
|
|
813
|
-
request_index: index,
|
|
814
|
-
status: "failed",
|
|
815
|
-
credits_spent: 0,
|
|
816
|
-
credits_refunded: 0,
|
|
817
|
-
error: `No skills defined on card: ${item.skill_id}`
|
|
818
|
-
};
|
|
819
|
-
}
|
|
820
|
-
creditsNeeded = skill.pricing.credits_per_call;
|
|
821
|
-
resolvedSkillId = skill.id;
|
|
822
|
-
} else {
|
|
823
|
-
creditsNeeded = card.pricing.credits_per_call;
|
|
824
|
-
}
|
|
825
|
-
if (creditsNeeded > item.max_credits) {
|
|
826
|
-
return {
|
|
827
|
-
request_index: index,
|
|
828
|
-
status: "failed",
|
|
829
|
-
credits_spent: 0,
|
|
830
|
-
credits_refunded: 0,
|
|
831
|
-
error: `Skill costs ${creditsNeeded} credits but max_credits is ${item.max_credits}`
|
|
832
|
-
};
|
|
833
|
-
}
|
|
834
|
-
let escrowId;
|
|
835
|
-
try {
|
|
836
|
-
const balance = getBalance(creditDb, owner);
|
|
837
|
-
if (balance < creditsNeeded) {
|
|
838
|
-
return {
|
|
839
|
-
request_index: index,
|
|
840
|
-
status: "failed",
|
|
841
|
-
credits_spent: 0,
|
|
842
|
-
credits_refunded: 0,
|
|
843
|
-
error: "Insufficient credits"
|
|
844
|
-
};
|
|
845
|
-
}
|
|
846
|
-
escrowId = holdEscrow(creditDb, owner, creditsNeeded, card.id);
|
|
847
|
-
} catch (err) {
|
|
848
|
-
const msg = err instanceof AgentBnBError ? err.message : "Failed to hold escrow";
|
|
849
|
-
return {
|
|
850
|
-
request_index: index,
|
|
851
|
-
status: "failed",
|
|
852
|
-
credits_spent: 0,
|
|
853
|
-
credits_refunded: 0,
|
|
854
|
-
error: msg
|
|
855
|
-
};
|
|
856
|
-
}
|
|
857
|
-
const startMs = Date.now();
|
|
858
|
-
const latencyMs = Date.now() - startMs;
|
|
859
|
-
settleEscrow(creditDb, escrowId, card.owner);
|
|
860
|
-
updateReputation(registryDb, card.id, true, latencyMs);
|
|
861
|
-
try {
|
|
862
|
-
insertRequestLog(registryDb, {
|
|
863
|
-
id: randomUUID2(),
|
|
864
|
-
card_id: card.id,
|
|
865
|
-
card_name: card.name,
|
|
866
|
-
skill_id: resolvedSkillId,
|
|
867
|
-
requester: owner,
|
|
868
|
-
status: "success",
|
|
869
|
-
latency_ms: latencyMs,
|
|
870
|
-
credits_charged: creditsNeeded,
|
|
871
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
872
|
-
});
|
|
873
|
-
} catch {
|
|
874
|
-
}
|
|
875
|
-
return {
|
|
876
|
-
request_index: index,
|
|
877
|
-
status: "success",
|
|
878
|
-
result: { card_id: card.id, skill_id: resolvedSkillId },
|
|
879
|
-
credits_spent: creditsNeeded,
|
|
880
|
-
credits_refunded: 0
|
|
881
|
-
};
|
|
958
|
+
return {
|
|
959
|
+
publicKey: readFileSync(publicPath),
|
|
960
|
+
privateKey: readFileSync(privatePath)
|
|
882
961
|
};
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
stopped = true;
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
} else {
|
|
905
|
-
const settled = await Promise.allSettled(
|
|
906
|
-
requests.map((item, i) => executeItem(item, i))
|
|
907
|
-
);
|
|
908
|
-
results = settled.map((outcome, i) => {
|
|
909
|
-
if (outcome.status === "fulfilled") {
|
|
910
|
-
return outcome.value;
|
|
911
|
-
}
|
|
912
|
-
return {
|
|
913
|
-
request_index: i,
|
|
914
|
-
status: "failed",
|
|
915
|
-
credits_spent: 0,
|
|
916
|
-
credits_refunded: 0,
|
|
917
|
-
error: outcome.reason instanceof Error ? outcome.reason.message : "Unknown error"
|
|
918
|
-
};
|
|
919
|
-
});
|
|
962
|
+
}
|
|
963
|
+
function canonicalJson(data) {
|
|
964
|
+
return JSON.stringify(data, Object.keys(data).sort());
|
|
965
|
+
}
|
|
966
|
+
function signEscrowReceipt(data, privateKey) {
|
|
967
|
+
const message = Buffer.from(canonicalJson(data), "utf-8");
|
|
968
|
+
const keyObject = createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" });
|
|
969
|
+
const signature = sign(null, message, keyObject);
|
|
970
|
+
return signature.toString("base64url");
|
|
971
|
+
}
|
|
972
|
+
function verifyEscrowReceipt(data, signature, publicKey) {
|
|
973
|
+
try {
|
|
974
|
+
const message = Buffer.from(canonicalJson(data), "utf-8");
|
|
975
|
+
const keyObject = createPublicKey({ key: publicKey, format: "der", type: "spki" });
|
|
976
|
+
const sigBuffer = Buffer.from(signature, "base64url");
|
|
977
|
+
return verify(null, message, keyObject, sigBuffer);
|
|
978
|
+
} catch {
|
|
979
|
+
return false;
|
|
920
980
|
}
|
|
921
|
-
const total_credits_spent = results.reduce((acc, r) => acc + r.credits_spent, 0);
|
|
922
|
-
const total_credits_refunded = results.reduce((acc, r) => acc + r.credits_refunded, 0);
|
|
923
|
-
const success = results.every((r) => r.status === "success");
|
|
924
|
-
return { results, total_credits_spent, total_credits_refunded, success };
|
|
925
981
|
}
|
|
926
982
|
|
|
927
983
|
export {
|
|
@@ -929,6 +985,9 @@ export {
|
|
|
929
985
|
getSkillRequestCount,
|
|
930
986
|
getActivityFeed,
|
|
931
987
|
getRequestLog,
|
|
988
|
+
insertFeedback,
|
|
989
|
+
getFeedbackForSkill,
|
|
990
|
+
getFeedbackForProvider,
|
|
932
991
|
insertEvolution,
|
|
933
992
|
getLatestEvolution,
|
|
934
993
|
getEvolutionHistory,
|
|
@@ -936,11 +995,25 @@ export {
|
|
|
936
995
|
insertCard,
|
|
937
996
|
getCard,
|
|
938
997
|
updateCard,
|
|
998
|
+
updateReputation,
|
|
939
999
|
updateSkillAvailability,
|
|
940
1000
|
updateSkillIdleRate,
|
|
941
1001
|
listCards,
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1002
|
+
getCardsByCapabilityType,
|
|
1003
|
+
getCardsBySkillCapability,
|
|
1004
|
+
openCreditDb,
|
|
1005
|
+
bootstrapAgent,
|
|
1006
|
+
getBalance,
|
|
1007
|
+
getTransactions,
|
|
1008
|
+
recordEarning,
|
|
1009
|
+
migrateOwner,
|
|
1010
|
+
holdEscrow,
|
|
1011
|
+
settleEscrow,
|
|
1012
|
+
releaseEscrow,
|
|
1013
|
+
confirmEscrowDebit,
|
|
1014
|
+
generateKeyPair,
|
|
1015
|
+
saveKeyPair,
|
|
1016
|
+
loadKeyPair,
|
|
1017
|
+
signEscrowReceipt,
|
|
1018
|
+
verifyEscrowReceipt
|
|
946
1019
|
};
|