agentbnb 5.1.11 → 7.0.0-beta.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 +245 -39
- package/dist/{card-RSGDCHCV.js → card-REW7BSWW.js} +1 -1
- package/dist/{chunk-FLY3WIQR.js → chunk-2HSUPCBT.js} +3 -3
- package/dist/{chunk-WGZ5AGOX.js → chunk-3CIMVISQ.js} +24 -1
- package/dist/{chunk-NH2FIERR.js → chunk-574W3HHE.js} +1 -1
- package/dist/{chunk-WTXRY7R2.js → chunk-APEG4QIN.js} +157 -9
- package/dist/chunk-BP3L2TET.js +148 -0
- package/dist/{chunk-NLAWT4DT.js → chunk-CWYPTQRQ.js} +7 -7
- package/dist/{chunk-UKT6H7YT.js → chunk-DUW6RX6I.js} +5 -2
- package/dist/chunk-EAD4A4KG.js +430 -0
- package/dist/{chunk-QT7TEVNV.js → chunk-EHSHB7TY.js} +23 -1
- package/dist/{chunk-B5FTAGFN.js → chunk-ETGOKDFR.js} +75 -75
- package/dist/{chunk-5KFI5X7B.js → chunk-F53QQIM2.js} +1 -1
- package/dist/{chunk-MLS6IGGG.js → chunk-FK2MDNTB.js} +117 -117
- package/dist/{chunk-EGUOAHCW.js → chunk-GO4FVRVN.js} +15 -13
- package/dist/{chunk-CRFCWD6V.js → chunk-J2K5S5MX.js} +136 -173
- package/dist/chunk-K5FO42YF.js +1136 -0
- package/dist/{chunk-DFBX3BBD.js → chunk-KA2VIEGM.js} +211 -16
- package/dist/chunk-NWIQJ2CL.js +108 -0
- package/dist/chunk-OCSU2S6W.js +168 -0
- package/dist/{chunk-QQFBFV4V.js → chunk-PGDBUUGR.js} +60 -19
- package/dist/{chunk-QITOPASZ.js → chunk-PSQHUZ7X.js} +1 -1
- package/dist/{chunk-C6KPAFCC.js → chunk-PU7LXOQ3.js} +23 -1
- package/dist/{chunk-JOY533UH.js → chunk-TW65F5EU.js} +1 -1
- 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/cli/index.js +755 -379
- package/dist/{client-T5MTY3CS.js → client-HRYRJKSA.js} +3 -3
- package/dist/{conduct-WU3VEXB6.js → conduct-JNYJCDHQ.js} +14 -13
- package/dist/conduct-KJUD2RTB.js +22 -0
- package/dist/{conductor-mode-ZMTFZGJP.js → conductor-mode-2VVFMKVE.js} +313 -14
- package/dist/conductor-mode-VGUU54QI.js +276 -0
- package/dist/execute-I4PKSNJM.js +12 -0
- package/dist/execute-MOXSSA3Q.js +15 -0
- package/dist/index.d.ts +795 -2
- package/dist/index.js +861 -111
- package/dist/{process-guard-CC7CNRQJ.js → process-guard-QCCBGILS.js} +1 -1
- package/dist/publish-capability-TS6CNR5G.js +12 -0
- package/dist/reliability-metrics-QG7WC5QK.js +18 -0
- package/dist/{request-VOXBFUOG.js → request-E7TA7COA.js} +19 -18
- package/dist/{serve-skill-IH7UAJNR.js → serve-skill-HIOWYKRU.js} +13 -11
- package/dist/{server-JVQW2TID.js → server-I63CXFX3.js} +17 -16
- package/dist/{service-coordinator-EYRDTHL5.js → service-coordinator-XBNT3SMU.js} +369 -260
- package/dist/skill-config-FETXPNVP.js +22 -0
- package/dist/skills/agentbnb/bootstrap.js +430 -84
- package/dist/websocket-client-5MH6QRJK.js +7 -0
- package/dist/{websocket-client-WRN3HO73.js → websocket-client-PFGVTXNE.js} +1 -1
- package/openclaw.plugin.json +2 -2
- package/package.json +2 -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-EANI2N2V.js +0 -309
- package/dist/chunk-EPIWHNB2.js +0 -946
- package/dist/conduct-6LKIJJKQ.js +0 -21
- package/dist/conductor-mode-Q4IIDY5E.js +0 -123
- package/dist/execute-4D4ITQCL.js +0 -10
- package/dist/execute-T7Y6RKSW.js +0 -13
- package/dist/websocket-client-6IIDGXKB.js +0 -7
|
@@ -0,0 +1,1136 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ensureReliabilityTable,
|
|
3
|
+
recordSuccessfulHire
|
|
4
|
+
} from "./chunk-NWIQJ2CL.js";
|
|
5
|
+
import {
|
|
6
|
+
AgentBnBError,
|
|
7
|
+
AnyCardSchema,
|
|
8
|
+
CapabilityCardSchema
|
|
9
|
+
} from "./chunk-3CIMVISQ.js";
|
|
10
|
+
|
|
11
|
+
// src/registry/request-log.ts
|
|
12
|
+
var SINCE_MS = {
|
|
13
|
+
"24h": 864e5,
|
|
14
|
+
"7d": 6048e5,
|
|
15
|
+
"30d": 2592e6
|
|
16
|
+
};
|
|
17
|
+
function createRequestLogTable(db) {
|
|
18
|
+
db.exec(`
|
|
19
|
+
CREATE TABLE IF NOT EXISTS request_log (
|
|
20
|
+
id TEXT PRIMARY KEY,
|
|
21
|
+
card_id TEXT NOT NULL,
|
|
22
|
+
card_name TEXT NOT NULL,
|
|
23
|
+
requester TEXT NOT NULL,
|
|
24
|
+
status TEXT NOT NULL CHECK(status IN ('success', 'failure', 'timeout')),
|
|
25
|
+
latency_ms INTEGER NOT NULL,
|
|
26
|
+
credits_charged INTEGER NOT NULL,
|
|
27
|
+
created_at TEXT NOT NULL
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
CREATE INDEX IF NOT EXISTS request_log_created_at_idx
|
|
31
|
+
ON request_log (created_at DESC);
|
|
32
|
+
`);
|
|
33
|
+
try {
|
|
34
|
+
db.exec("ALTER TABLE request_log ADD COLUMN skill_id TEXT");
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
db.exec("ALTER TABLE request_log ADD COLUMN action_type TEXT");
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
db.exec("ALTER TABLE request_log ADD COLUMN tier_invoked INTEGER");
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
db.exec("ALTER TABLE request_log ADD COLUMN failure_reason TEXT");
|
|
47
|
+
} catch {
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
db.exec("ALTER TABLE request_log ADD COLUMN team_id TEXT");
|
|
51
|
+
} catch {
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
db.exec("ALTER TABLE request_log ADD COLUMN role TEXT");
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
db.exec("ALTER TABLE request_log ADD COLUMN capability_type TEXT");
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function insertRequestLog(db, entry) {
|
|
63
|
+
const stmt = db.prepare(`
|
|
64
|
+
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)
|
|
65
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
66
|
+
`);
|
|
67
|
+
stmt.run(
|
|
68
|
+
entry.id,
|
|
69
|
+
entry.card_id,
|
|
70
|
+
entry.card_name,
|
|
71
|
+
entry.requester,
|
|
72
|
+
entry.status,
|
|
73
|
+
entry.latency_ms,
|
|
74
|
+
entry.credits_charged,
|
|
75
|
+
entry.created_at,
|
|
76
|
+
entry.skill_id ?? null,
|
|
77
|
+
entry.action_type ?? null,
|
|
78
|
+
entry.tier_invoked ?? null,
|
|
79
|
+
entry.failure_reason ?? null,
|
|
80
|
+
entry.team_id ?? null,
|
|
81
|
+
entry.role ?? null,
|
|
82
|
+
entry.capability_type ?? null
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
function getSkillRequestCount(db, skillId, windowMs) {
|
|
86
|
+
const cutoff = new Date(Date.now() - windowMs).toISOString();
|
|
87
|
+
const stmt = db.prepare(
|
|
88
|
+
`SELECT COUNT(*) as cnt FROM request_log
|
|
89
|
+
WHERE skill_id = ? AND created_at >= ? AND status = 'success' AND action_type IS NULL`
|
|
90
|
+
);
|
|
91
|
+
const row = stmt.get(skillId, cutoff);
|
|
92
|
+
return row.cnt;
|
|
93
|
+
}
|
|
94
|
+
function getActivityFeed(db, limit = 20, since) {
|
|
95
|
+
const effectiveLimit = Math.min(limit, 100);
|
|
96
|
+
if (since !== void 0) {
|
|
97
|
+
const stmt2 = db.prepare(`
|
|
98
|
+
SELECT r.id, r.card_name, r.requester, c.owner AS provider,
|
|
99
|
+
r.status, r.credits_charged, r.latency_ms, r.created_at, r.action_type
|
|
100
|
+
FROM request_log r
|
|
101
|
+
LEFT JOIN capability_cards c ON r.card_id = c.id
|
|
102
|
+
WHERE (r.action_type IS NULL OR r.action_type IN ('auto_share', 'agent_joined'))
|
|
103
|
+
AND r.created_at > ?
|
|
104
|
+
ORDER BY r.created_at DESC
|
|
105
|
+
LIMIT ?
|
|
106
|
+
`);
|
|
107
|
+
return stmt2.all(since, effectiveLimit);
|
|
108
|
+
}
|
|
109
|
+
const stmt = db.prepare(`
|
|
110
|
+
SELECT r.id, r.card_name, r.requester, c.owner AS provider,
|
|
111
|
+
r.status, r.credits_charged, r.latency_ms, r.created_at, r.action_type
|
|
112
|
+
FROM request_log r
|
|
113
|
+
LEFT JOIN capability_cards c ON r.card_id = c.id
|
|
114
|
+
WHERE (r.action_type IS NULL OR r.action_type IN ('auto_share', 'agent_joined'))
|
|
115
|
+
ORDER BY r.created_at DESC
|
|
116
|
+
LIMIT ?
|
|
117
|
+
`);
|
|
118
|
+
return stmt.all(effectiveLimit);
|
|
119
|
+
}
|
|
120
|
+
function getRequestLog(db, limit = 10, since) {
|
|
121
|
+
if (since !== void 0) {
|
|
122
|
+
const cutoff = new Date(Date.now() - SINCE_MS[since]).toISOString();
|
|
123
|
+
const stmt2 = db.prepare(`
|
|
124
|
+
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
|
|
125
|
+
FROM request_log
|
|
126
|
+
WHERE created_at >= ?
|
|
127
|
+
ORDER BY created_at DESC
|
|
128
|
+
LIMIT ?
|
|
129
|
+
`);
|
|
130
|
+
return stmt2.all(cutoff, limit);
|
|
131
|
+
}
|
|
132
|
+
const stmt = db.prepare(`
|
|
133
|
+
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
|
|
134
|
+
FROM request_log
|
|
135
|
+
ORDER BY created_at DESC
|
|
136
|
+
LIMIT ?
|
|
137
|
+
`);
|
|
138
|
+
return stmt.all(limit);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/registry/store.ts
|
|
142
|
+
import Database from "better-sqlite3";
|
|
143
|
+
|
|
144
|
+
// src/feedback/store.ts
|
|
145
|
+
import { randomUUID } from "crypto";
|
|
146
|
+
function initFeedbackTable(db) {
|
|
147
|
+
db.exec(`
|
|
148
|
+
CREATE TABLE IF NOT EXISTS feedback (
|
|
149
|
+
id TEXT PRIMARY KEY,
|
|
150
|
+
transaction_id TEXT NOT NULL,
|
|
151
|
+
provider_agent TEXT NOT NULL,
|
|
152
|
+
skill_id TEXT NOT NULL,
|
|
153
|
+
requester_agent TEXT NOT NULL,
|
|
154
|
+
rating INTEGER NOT NULL,
|
|
155
|
+
latency_ms INTEGER NOT NULL,
|
|
156
|
+
result_quality TEXT NOT NULL,
|
|
157
|
+
quality_details TEXT,
|
|
158
|
+
would_reuse INTEGER NOT NULL,
|
|
159
|
+
cost_value_ratio TEXT NOT NULL,
|
|
160
|
+
timestamp TEXT NOT NULL,
|
|
161
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
CREATE INDEX IF NOT EXISTS feedback_provider_idx ON feedback(provider_agent);
|
|
165
|
+
CREATE INDEX IF NOT EXISTS feedback_skill_idx ON feedback(skill_id);
|
|
166
|
+
`);
|
|
167
|
+
}
|
|
168
|
+
function insertFeedback(db, feedback) {
|
|
169
|
+
const id = randomUUID();
|
|
170
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
171
|
+
db.prepare(`
|
|
172
|
+
INSERT INTO feedback (
|
|
173
|
+
id, transaction_id, provider_agent, skill_id, requester_agent,
|
|
174
|
+
rating, latency_ms, result_quality, quality_details,
|
|
175
|
+
would_reuse, cost_value_ratio, timestamp, created_at
|
|
176
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
177
|
+
`).run(
|
|
178
|
+
id,
|
|
179
|
+
feedback.transaction_id,
|
|
180
|
+
feedback.provider_agent,
|
|
181
|
+
feedback.skill_id,
|
|
182
|
+
feedback.requester_agent,
|
|
183
|
+
feedback.rating,
|
|
184
|
+
feedback.latency_ms,
|
|
185
|
+
feedback.result_quality,
|
|
186
|
+
feedback.quality_details ?? null,
|
|
187
|
+
feedback.would_reuse ? 1 : 0,
|
|
188
|
+
feedback.cost_value_ratio,
|
|
189
|
+
feedback.timestamp,
|
|
190
|
+
now
|
|
191
|
+
);
|
|
192
|
+
return id;
|
|
193
|
+
}
|
|
194
|
+
function getFeedbackForSkill(db, skillId, limit = 20) {
|
|
195
|
+
const rows = db.prepare(`
|
|
196
|
+
SELECT * FROM feedback
|
|
197
|
+
WHERE skill_id = ?
|
|
198
|
+
ORDER BY timestamp DESC
|
|
199
|
+
LIMIT ?
|
|
200
|
+
`).all(skillId, limit);
|
|
201
|
+
return rows.map(rowToFeedback);
|
|
202
|
+
}
|
|
203
|
+
function getFeedbackForProvider(db, providerAgent, sinceDays) {
|
|
204
|
+
let rows;
|
|
205
|
+
if (sinceDays !== void 0) {
|
|
206
|
+
rows = db.prepare(`
|
|
207
|
+
SELECT * FROM feedback
|
|
208
|
+
WHERE provider_agent = ?
|
|
209
|
+
AND timestamp >= datetime('now', ? || ' days')
|
|
210
|
+
ORDER BY timestamp DESC
|
|
211
|
+
`).all(providerAgent, `-${sinceDays}`);
|
|
212
|
+
} else {
|
|
213
|
+
rows = db.prepare(`
|
|
214
|
+
SELECT * FROM feedback
|
|
215
|
+
WHERE provider_agent = ?
|
|
216
|
+
ORDER BY timestamp DESC
|
|
217
|
+
`).all(providerAgent);
|
|
218
|
+
}
|
|
219
|
+
return rows.map(rowToFeedback);
|
|
220
|
+
}
|
|
221
|
+
function rowToFeedback(row) {
|
|
222
|
+
return {
|
|
223
|
+
transaction_id: row["transaction_id"],
|
|
224
|
+
provider_agent: row["provider_agent"],
|
|
225
|
+
skill_id: row["skill_id"],
|
|
226
|
+
requester_agent: row["requester_agent"],
|
|
227
|
+
rating: row["rating"],
|
|
228
|
+
latency_ms: row["latency_ms"],
|
|
229
|
+
result_quality: row["result_quality"],
|
|
230
|
+
quality_details: row["quality_details"] ?? void 0,
|
|
231
|
+
would_reuse: row["would_reuse"] === 1,
|
|
232
|
+
cost_value_ratio: row["cost_value_ratio"],
|
|
233
|
+
timestamp: row["timestamp"]
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/evolution/store.ts
|
|
238
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
239
|
+
function initEvolutionTable(db) {
|
|
240
|
+
db.exec(`
|
|
241
|
+
CREATE TABLE IF NOT EXISTS evolution_versions (
|
|
242
|
+
id TEXT PRIMARY KEY,
|
|
243
|
+
template_name TEXT NOT NULL,
|
|
244
|
+
template_version TEXT NOT NULL,
|
|
245
|
+
publisher_agent TEXT NOT NULL,
|
|
246
|
+
changelog TEXT NOT NULL,
|
|
247
|
+
core_memory_snapshot TEXT NOT NULL,
|
|
248
|
+
fitness_improvement REAL NOT NULL,
|
|
249
|
+
timestamp TEXT NOT NULL,
|
|
250
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
CREATE INDEX IF NOT EXISTS evolution_template_idx
|
|
254
|
+
ON evolution_versions(template_name, created_at DESC);
|
|
255
|
+
`);
|
|
256
|
+
}
|
|
257
|
+
function insertEvolution(db, ev) {
|
|
258
|
+
const id = randomUUID2();
|
|
259
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
260
|
+
db.prepare(`
|
|
261
|
+
INSERT INTO evolution_versions (
|
|
262
|
+
id, template_name, template_version, publisher_agent,
|
|
263
|
+
changelog, core_memory_snapshot, fitness_improvement, timestamp, created_at
|
|
264
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
265
|
+
`).run(
|
|
266
|
+
id,
|
|
267
|
+
ev.template_name,
|
|
268
|
+
ev.template_version,
|
|
269
|
+
ev.publisher_agent,
|
|
270
|
+
ev.changelog,
|
|
271
|
+
JSON.stringify(ev.core_memory_snapshot),
|
|
272
|
+
ev.fitness_improvement,
|
|
273
|
+
ev.timestamp,
|
|
274
|
+
now
|
|
275
|
+
);
|
|
276
|
+
return id;
|
|
277
|
+
}
|
|
278
|
+
function getLatestEvolution(db, templateName) {
|
|
279
|
+
const row = db.prepare(`
|
|
280
|
+
SELECT * FROM evolution_versions
|
|
281
|
+
WHERE template_name = ?
|
|
282
|
+
ORDER BY created_at DESC
|
|
283
|
+
LIMIT 1
|
|
284
|
+
`).get(templateName);
|
|
285
|
+
if (!row) return null;
|
|
286
|
+
return rowToEvolution(row);
|
|
287
|
+
}
|
|
288
|
+
function getEvolutionHistory(db, templateName, limit = 20) {
|
|
289
|
+
const rows = db.prepare(`
|
|
290
|
+
SELECT * FROM evolution_versions
|
|
291
|
+
WHERE template_name = ?
|
|
292
|
+
ORDER BY created_at DESC
|
|
293
|
+
LIMIT ?
|
|
294
|
+
`).all(templateName, limit);
|
|
295
|
+
return rows.map(rowToEvolution);
|
|
296
|
+
}
|
|
297
|
+
function rowToEvolution(row) {
|
|
298
|
+
return {
|
|
299
|
+
template_name: row["template_name"],
|
|
300
|
+
template_version: row["template_version"],
|
|
301
|
+
publisher_agent: row["publisher_agent"],
|
|
302
|
+
changelog: row["changelog"],
|
|
303
|
+
core_memory_snapshot: JSON.parse(row["core_memory_snapshot"]),
|
|
304
|
+
fitness_improvement: row["fitness_improvement"],
|
|
305
|
+
timestamp: row["timestamp"]
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// src/registry/store.ts
|
|
310
|
+
var V2_FTS_TRIGGERS = `
|
|
311
|
+
DROP TRIGGER IF EXISTS cards_ai;
|
|
312
|
+
DROP TRIGGER IF EXISTS cards_au;
|
|
313
|
+
DROP TRIGGER IF EXISTS cards_ad;
|
|
314
|
+
|
|
315
|
+
CREATE TRIGGER cards_ai AFTER INSERT ON capability_cards BEGIN
|
|
316
|
+
INSERT INTO cards_fts(rowid, id, owner, name, description, tags)
|
|
317
|
+
VALUES (
|
|
318
|
+
new.rowid,
|
|
319
|
+
new.id,
|
|
320
|
+
new.owner,
|
|
321
|
+
COALESCE(
|
|
322
|
+
(SELECT group_concat(json_extract(value, '$.name'), ' ')
|
|
323
|
+
FROM json_each(json_extract(new.data, '$.skills'))),
|
|
324
|
+
json_extract(new.data, '$.name'),
|
|
325
|
+
''
|
|
326
|
+
),
|
|
327
|
+
COALESCE(
|
|
328
|
+
(SELECT group_concat(json_extract(value, '$.description'), ' ')
|
|
329
|
+
FROM json_each(json_extract(new.data, '$.skills'))),
|
|
330
|
+
json_extract(new.data, '$.description'),
|
|
331
|
+
''
|
|
332
|
+
),
|
|
333
|
+
COALESCE(
|
|
334
|
+
(SELECT group_concat(json_extract(value, '$.metadata.tags'), ' ')
|
|
335
|
+
FROM json_each(json_extract(new.data, '$.skills'))),
|
|
336
|
+
(SELECT group_concat(value, ' ')
|
|
337
|
+
FROM json_each(json_extract(new.data, '$.metadata.tags'))),
|
|
338
|
+
''
|
|
339
|
+
)
|
|
340
|
+
|| ' ' || COALESCE(
|
|
341
|
+
(SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
|
|
342
|
+
FROM json_each(json_extract(new.data, '$.skills')) AS skill),
|
|
343
|
+
''
|
|
344
|
+
)
|
|
345
|
+
|| ' ' || COALESCE(
|
|
346
|
+
(SELECT group_concat(cap_type.value, ' ')
|
|
347
|
+
FROM json_each(json_extract(new.data, '$.skills')) AS skill,
|
|
348
|
+
json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
|
|
349
|
+
''
|
|
350
|
+
)
|
|
351
|
+
);
|
|
352
|
+
END;
|
|
353
|
+
|
|
354
|
+
CREATE TRIGGER cards_au AFTER UPDATE ON capability_cards BEGIN
|
|
355
|
+
INSERT INTO cards_fts(cards_fts, rowid, id, owner, name, description, tags)
|
|
356
|
+
VALUES (
|
|
357
|
+
'delete',
|
|
358
|
+
old.rowid,
|
|
359
|
+
old.id,
|
|
360
|
+
old.owner,
|
|
361
|
+
COALESCE(
|
|
362
|
+
(SELECT group_concat(json_extract(value, '$.name'), ' ')
|
|
363
|
+
FROM json_each(json_extract(old.data, '$.skills'))),
|
|
364
|
+
json_extract(old.data, '$.name'),
|
|
365
|
+
''
|
|
366
|
+
),
|
|
367
|
+
COALESCE(
|
|
368
|
+
(SELECT group_concat(json_extract(value, '$.description'), ' ')
|
|
369
|
+
FROM json_each(json_extract(old.data, '$.skills'))),
|
|
370
|
+
json_extract(old.data, '$.description'),
|
|
371
|
+
''
|
|
372
|
+
),
|
|
373
|
+
COALESCE(
|
|
374
|
+
(SELECT group_concat(json_extract(value, '$.metadata.tags'), ' ')
|
|
375
|
+
FROM json_each(json_extract(old.data, '$.skills'))),
|
|
376
|
+
(SELECT group_concat(value, ' ')
|
|
377
|
+
FROM json_each(json_extract(old.data, '$.metadata.tags'))),
|
|
378
|
+
''
|
|
379
|
+
)
|
|
380
|
+
|| ' ' || COALESCE(
|
|
381
|
+
(SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
|
|
382
|
+
FROM json_each(json_extract(old.data, '$.skills')) AS skill),
|
|
383
|
+
''
|
|
384
|
+
)
|
|
385
|
+
|| ' ' || COALESCE(
|
|
386
|
+
(SELECT group_concat(cap_type.value, ' ')
|
|
387
|
+
FROM json_each(json_extract(old.data, '$.skills')) AS skill,
|
|
388
|
+
json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
|
|
389
|
+
''
|
|
390
|
+
)
|
|
391
|
+
);
|
|
392
|
+
INSERT INTO cards_fts(rowid, id, owner, name, description, tags)
|
|
393
|
+
VALUES (
|
|
394
|
+
new.rowid,
|
|
395
|
+
new.id,
|
|
396
|
+
new.owner,
|
|
397
|
+
COALESCE(
|
|
398
|
+
(SELECT group_concat(json_extract(value, '$.name'), ' ')
|
|
399
|
+
FROM json_each(json_extract(new.data, '$.skills'))),
|
|
400
|
+
json_extract(new.data, '$.name'),
|
|
401
|
+
''
|
|
402
|
+
),
|
|
403
|
+
COALESCE(
|
|
404
|
+
(SELECT group_concat(json_extract(value, '$.description'), ' ')
|
|
405
|
+
FROM json_each(json_extract(new.data, '$.skills'))),
|
|
406
|
+
json_extract(new.data, '$.description'),
|
|
407
|
+
''
|
|
408
|
+
),
|
|
409
|
+
COALESCE(
|
|
410
|
+
(SELECT group_concat(json_extract(value, '$.metadata.tags'), ' ')
|
|
411
|
+
FROM json_each(json_extract(new.data, '$.skills'))),
|
|
412
|
+
(SELECT group_concat(value, ' ')
|
|
413
|
+
FROM json_each(json_extract(new.data, '$.metadata.tags'))),
|
|
414
|
+
''
|
|
415
|
+
)
|
|
416
|
+
|| ' ' || COALESCE(
|
|
417
|
+
(SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
|
|
418
|
+
FROM json_each(json_extract(new.data, '$.skills')) AS skill),
|
|
419
|
+
''
|
|
420
|
+
)
|
|
421
|
+
|| ' ' || COALESCE(
|
|
422
|
+
(SELECT group_concat(cap_type.value, ' ')
|
|
423
|
+
FROM json_each(json_extract(new.data, '$.skills')) AS skill,
|
|
424
|
+
json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
|
|
425
|
+
''
|
|
426
|
+
)
|
|
427
|
+
);
|
|
428
|
+
END;
|
|
429
|
+
|
|
430
|
+
CREATE TRIGGER cards_ad AFTER DELETE ON capability_cards BEGIN
|
|
431
|
+
INSERT INTO cards_fts(cards_fts, rowid, id, owner, name, description, tags)
|
|
432
|
+
VALUES (
|
|
433
|
+
'delete',
|
|
434
|
+
old.rowid,
|
|
435
|
+
old.id,
|
|
436
|
+
old.owner,
|
|
437
|
+
COALESCE(
|
|
438
|
+
(SELECT group_concat(json_extract(value, '$.name'), ' ')
|
|
439
|
+
FROM json_each(json_extract(old.data, '$.skills'))),
|
|
440
|
+
json_extract(old.data, '$.name'),
|
|
441
|
+
''
|
|
442
|
+
),
|
|
443
|
+
COALESCE(
|
|
444
|
+
(SELECT group_concat(json_extract(value, '$.description'), ' ')
|
|
445
|
+
FROM json_each(json_extract(old.data, '$.skills'))),
|
|
446
|
+
json_extract(old.data, '$.description'),
|
|
447
|
+
''
|
|
448
|
+
),
|
|
449
|
+
COALESCE(
|
|
450
|
+
(SELECT group_concat(json_extract(value, '$.metadata.tags'), ' ')
|
|
451
|
+
FROM json_each(json_extract(old.data, '$.skills'))),
|
|
452
|
+
(SELECT group_concat(value, ' ')
|
|
453
|
+
FROM json_each(json_extract(old.data, '$.metadata.tags'))),
|
|
454
|
+
''
|
|
455
|
+
)
|
|
456
|
+
|| ' ' || COALESCE(
|
|
457
|
+
(SELECT group_concat(json_extract(skill.value, '$.capability_type'), ' ')
|
|
458
|
+
FROM json_each(json_extract(old.data, '$.skills')) AS skill),
|
|
459
|
+
''
|
|
460
|
+
)
|
|
461
|
+
|| ' ' || COALESCE(
|
|
462
|
+
(SELECT group_concat(cap_type.value, ' ')
|
|
463
|
+
FROM json_each(json_extract(old.data, '$.skills')) AS skill,
|
|
464
|
+
json_each(json_extract(skill.value, '$.capability_types')) AS cap_type),
|
|
465
|
+
''
|
|
466
|
+
)
|
|
467
|
+
);
|
|
468
|
+
END;
|
|
469
|
+
`;
|
|
470
|
+
function openDatabase(path = ":memory:") {
|
|
471
|
+
const db = new Database(path);
|
|
472
|
+
db.pragma("journal_mode = WAL");
|
|
473
|
+
db.pragma("foreign_keys = ON");
|
|
474
|
+
db.exec(`
|
|
475
|
+
CREATE TABLE IF NOT EXISTS capability_cards (
|
|
476
|
+
id TEXT PRIMARY KEY,
|
|
477
|
+
owner TEXT NOT NULL,
|
|
478
|
+
data TEXT NOT NULL,
|
|
479
|
+
created_at TEXT NOT NULL,
|
|
480
|
+
updated_at TEXT NOT NULL
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
CREATE TABLE IF NOT EXISTS pending_requests (
|
|
484
|
+
id TEXT PRIMARY KEY,
|
|
485
|
+
skill_query TEXT NOT NULL,
|
|
486
|
+
max_cost_credits REAL NOT NULL,
|
|
487
|
+
selected_peer TEXT,
|
|
488
|
+
selected_card_id TEXT,
|
|
489
|
+
selected_skill_id TEXT,
|
|
490
|
+
credits REAL NOT NULL,
|
|
491
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
492
|
+
params TEXT,
|
|
493
|
+
created_at TEXT NOT NULL,
|
|
494
|
+
resolved_at TEXT
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS cards_fts USING fts5(
|
|
498
|
+
id UNINDEXED,
|
|
499
|
+
owner,
|
|
500
|
+
name,
|
|
501
|
+
description,
|
|
502
|
+
tags,
|
|
503
|
+
content=""
|
|
504
|
+
);
|
|
505
|
+
`);
|
|
506
|
+
createRequestLogTable(db);
|
|
507
|
+
initFeedbackTable(db);
|
|
508
|
+
initEvolutionTable(db);
|
|
509
|
+
runMigrations(db);
|
|
510
|
+
return db;
|
|
511
|
+
}
|
|
512
|
+
function runMigrations(db) {
|
|
513
|
+
const version = db.pragma("user_version")[0]?.user_version ?? 0;
|
|
514
|
+
if (version < 2) {
|
|
515
|
+
migrateV1toV2(db);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
function migrateV1toV2(db) {
|
|
519
|
+
const migrate = db.transaction(() => {
|
|
520
|
+
const rows = db.prepare("SELECT rowid, id, data FROM capability_cards").all();
|
|
521
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
522
|
+
for (const row of rows) {
|
|
523
|
+
const parsed = JSON.parse(row.data);
|
|
524
|
+
if (parsed["spec_version"] === "2.0") continue;
|
|
525
|
+
const v1 = parsed;
|
|
526
|
+
const v2 = {
|
|
527
|
+
spec_version: "2.0",
|
|
528
|
+
id: v1.id,
|
|
529
|
+
owner: v1.owner,
|
|
530
|
+
agent_name: v1.name,
|
|
531
|
+
skills: [
|
|
532
|
+
{
|
|
533
|
+
id: `skill-${v1.id}`,
|
|
534
|
+
name: v1.name,
|
|
535
|
+
description: v1.description,
|
|
536
|
+
level: v1.level,
|
|
537
|
+
inputs: v1.inputs,
|
|
538
|
+
outputs: v1.outputs,
|
|
539
|
+
pricing: v1.pricing,
|
|
540
|
+
availability: { online: v1.availability.online },
|
|
541
|
+
powered_by: v1.powered_by,
|
|
542
|
+
metadata: v1.metadata,
|
|
543
|
+
_internal: v1._internal
|
|
544
|
+
}
|
|
545
|
+
],
|
|
546
|
+
availability: v1.availability,
|
|
547
|
+
created_at: v1.created_at,
|
|
548
|
+
updated_at: now
|
|
549
|
+
};
|
|
550
|
+
db.prepare("UPDATE capability_cards SET data = ?, updated_at = ? WHERE id = ?").run(
|
|
551
|
+
JSON.stringify(v2),
|
|
552
|
+
now,
|
|
553
|
+
v2.id
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
db.exec(V2_FTS_TRIGGERS);
|
|
557
|
+
db.exec(`INSERT INTO cards_fts(cards_fts) VALUES('delete-all')`);
|
|
558
|
+
const allRows = db.prepare("SELECT rowid, id, owner, data FROM capability_cards").all();
|
|
559
|
+
const ftsInsert = db.prepare(
|
|
560
|
+
"INSERT INTO cards_fts(rowid, id, owner, name, description, tags) VALUES (?, ?, ?, ?, ?, ?)"
|
|
561
|
+
);
|
|
562
|
+
for (const row of allRows) {
|
|
563
|
+
const data = JSON.parse(row.data);
|
|
564
|
+
const skills = data["skills"] ?? [];
|
|
565
|
+
let name;
|
|
566
|
+
let description;
|
|
567
|
+
let tags;
|
|
568
|
+
if (skills.length > 0) {
|
|
569
|
+
name = skills.map((s) => String(s["name"] ?? "")).join(" ");
|
|
570
|
+
description = skills.map((s) => String(s["description"] ?? "")).join(" ");
|
|
571
|
+
tags = [
|
|
572
|
+
// tags from metadata.tags[]
|
|
573
|
+
...skills.flatMap((s) => {
|
|
574
|
+
const meta = s["metadata"];
|
|
575
|
+
return meta?.["tags"] ?? [];
|
|
576
|
+
}),
|
|
577
|
+
// capability_type (singular)
|
|
578
|
+
...skills.map((s) => s["capability_type"]).filter((v) => typeof v === "string" && v.length > 0),
|
|
579
|
+
// capability_types[] (plural)
|
|
580
|
+
...skills.flatMap((s) => s["capability_types"] ?? [])
|
|
581
|
+
].join(" ");
|
|
582
|
+
} else {
|
|
583
|
+
name = String(data["name"] ?? "");
|
|
584
|
+
description = String(data["description"] ?? "");
|
|
585
|
+
const meta = data["metadata"];
|
|
586
|
+
const rawTags = meta?.["tags"] ?? [];
|
|
587
|
+
tags = rawTags.join(" ");
|
|
588
|
+
}
|
|
589
|
+
ftsInsert.run(row.rowid, row.id, row.owner, name, description, tags);
|
|
590
|
+
}
|
|
591
|
+
db.pragma("user_version = 2");
|
|
592
|
+
});
|
|
593
|
+
migrate();
|
|
594
|
+
}
|
|
595
|
+
function insertCard(db, card) {
|
|
596
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
597
|
+
const withTimestamps = { ...card, created_at: card.created_at ?? now, updated_at: now };
|
|
598
|
+
const parsed = CapabilityCardSchema.safeParse(withTimestamps);
|
|
599
|
+
if (!parsed.success) {
|
|
600
|
+
throw new AgentBnBError(
|
|
601
|
+
`Card validation failed: ${parsed.error.message}`,
|
|
602
|
+
"VALIDATION_ERROR"
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
const stmt = db.prepare(`
|
|
606
|
+
INSERT INTO capability_cards (id, owner, data, created_at, updated_at)
|
|
607
|
+
VALUES (?, ?, ?, ?, ?)
|
|
608
|
+
`);
|
|
609
|
+
stmt.run(
|
|
610
|
+
parsed.data.id,
|
|
611
|
+
parsed.data.owner,
|
|
612
|
+
JSON.stringify(parsed.data),
|
|
613
|
+
parsed.data.created_at ?? now,
|
|
614
|
+
parsed.data.updated_at ?? now
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
function getCard(db, id) {
|
|
618
|
+
const stmt = db.prepare("SELECT data FROM capability_cards WHERE id = ?");
|
|
619
|
+
const row = stmt.get(id);
|
|
620
|
+
if (!row) return null;
|
|
621
|
+
return JSON.parse(row.data);
|
|
622
|
+
}
|
|
623
|
+
function updateCard(db, id, owner, updates) {
|
|
624
|
+
const existing = getCard(db, id);
|
|
625
|
+
if (!existing) {
|
|
626
|
+
throw new AgentBnBError(`Card not found: ${id}`, "NOT_FOUND");
|
|
627
|
+
}
|
|
628
|
+
if (existing.owner !== owner) {
|
|
629
|
+
throw new AgentBnBError("Forbidden: you do not own this card", "FORBIDDEN");
|
|
630
|
+
}
|
|
631
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
632
|
+
const merged = { ...existing, ...updates, updated_at: now };
|
|
633
|
+
const parsed = AnyCardSchema.safeParse(merged);
|
|
634
|
+
if (!parsed.success) {
|
|
635
|
+
throw new AgentBnBError(
|
|
636
|
+
`Card validation failed: ${parsed.error.message}`,
|
|
637
|
+
"VALIDATION_ERROR"
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
const stmt = db.prepare(`
|
|
641
|
+
UPDATE capability_cards
|
|
642
|
+
SET data = ?, updated_at = ?
|
|
643
|
+
WHERE id = ?
|
|
644
|
+
`);
|
|
645
|
+
stmt.run(JSON.stringify(parsed.data), now, id);
|
|
646
|
+
}
|
|
647
|
+
function updateReputation(db, cardId, success, latencyMs) {
|
|
648
|
+
const existing = getCard(db, cardId);
|
|
649
|
+
if (!existing) return;
|
|
650
|
+
const ALPHA = 0.1;
|
|
651
|
+
const observed = success ? 1 : 0;
|
|
652
|
+
const prevSuccessRate = existing.metadata?.success_rate;
|
|
653
|
+
const prevLatency = existing.metadata?.avg_latency_ms;
|
|
654
|
+
const newSuccessRate = prevSuccessRate === void 0 ? observed : ALPHA * observed + (1 - ALPHA) * prevSuccessRate;
|
|
655
|
+
const newLatency = prevLatency === void 0 ? latencyMs : ALPHA * latencyMs + (1 - ALPHA) * prevLatency;
|
|
656
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
657
|
+
const updatedMetadata = {
|
|
658
|
+
...existing.metadata,
|
|
659
|
+
success_rate: Math.round(newSuccessRate * 1e3) / 1e3,
|
|
660
|
+
avg_latency_ms: Math.round(newLatency)
|
|
661
|
+
};
|
|
662
|
+
const updatedCard = { ...existing, metadata: updatedMetadata, updated_at: now };
|
|
663
|
+
const stmt = db.prepare(`
|
|
664
|
+
UPDATE capability_cards
|
|
665
|
+
SET data = ?, updated_at = ?
|
|
666
|
+
WHERE id = ?
|
|
667
|
+
`);
|
|
668
|
+
stmt.run(JSON.stringify(updatedCard), now, cardId);
|
|
669
|
+
}
|
|
670
|
+
function updateSkillAvailability(db, cardId, skillId, online) {
|
|
671
|
+
const row = db.prepare("SELECT data FROM capability_cards WHERE id = ?").get(cardId);
|
|
672
|
+
if (!row) return;
|
|
673
|
+
const card = JSON.parse(row.data);
|
|
674
|
+
const skills = card["skills"];
|
|
675
|
+
if (!skills) return;
|
|
676
|
+
const skill = skills.find((s) => s["id"] === skillId);
|
|
677
|
+
if (!skill) return;
|
|
678
|
+
const existing = skill["availability"] ?? {};
|
|
679
|
+
skill["availability"] = { ...existing, online };
|
|
680
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
681
|
+
db.prepare("UPDATE capability_cards SET data = ?, updated_at = ? WHERE id = ?").run(
|
|
682
|
+
JSON.stringify(card),
|
|
683
|
+
now,
|
|
684
|
+
cardId
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
function updateSkillIdleRate(db, cardId, skillId, idleRate) {
|
|
688
|
+
const row = db.prepare("SELECT data FROM capability_cards WHERE id = ?").get(cardId);
|
|
689
|
+
if (!row) return;
|
|
690
|
+
const card = JSON.parse(row.data);
|
|
691
|
+
const skills = card["skills"];
|
|
692
|
+
if (!skills) return;
|
|
693
|
+
const skill = skills.find((s) => s["id"] === skillId);
|
|
694
|
+
if (!skill) return;
|
|
695
|
+
const existing = skill["_internal"] ?? {};
|
|
696
|
+
skill["_internal"] = {
|
|
697
|
+
...existing,
|
|
698
|
+
idle_rate: idleRate,
|
|
699
|
+
idle_rate_computed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
700
|
+
};
|
|
701
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
702
|
+
db.prepare("UPDATE capability_cards SET data = ?, updated_at = ? WHERE id = ?").run(
|
|
703
|
+
JSON.stringify(card),
|
|
704
|
+
now,
|
|
705
|
+
cardId
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
function listCards(db, owner) {
|
|
709
|
+
let stmt;
|
|
710
|
+
let rows;
|
|
711
|
+
if (owner !== void 0) {
|
|
712
|
+
stmt = db.prepare("SELECT data FROM capability_cards WHERE owner = ?");
|
|
713
|
+
rows = stmt.all(owner);
|
|
714
|
+
} else {
|
|
715
|
+
stmt = db.prepare("SELECT data FROM capability_cards");
|
|
716
|
+
rows = stmt.all();
|
|
717
|
+
}
|
|
718
|
+
return rows.map((row) => JSON.parse(row.data));
|
|
719
|
+
}
|
|
720
|
+
function getCardsByCapabilityType(db, capabilityType) {
|
|
721
|
+
const rows = db.prepare(
|
|
722
|
+
"SELECT data FROM capability_cards WHERE json_extract(data, '$.capability_type') = ?"
|
|
723
|
+
).all(capabilityType);
|
|
724
|
+
return rows.map((row) => JSON.parse(row.data));
|
|
725
|
+
}
|
|
726
|
+
function getCardsBySkillCapability(db, capabilityType) {
|
|
727
|
+
const rows = db.prepare("SELECT data FROM capability_cards").all();
|
|
728
|
+
return rows.map((row) => JSON.parse(row.data)).filter((card) => {
|
|
729
|
+
const skills = card["skills"];
|
|
730
|
+
if (!skills) return false;
|
|
731
|
+
return skills.some((skill) => {
|
|
732
|
+
if (skill["capability_type"] === capabilityType) return true;
|
|
733
|
+
const types = skill["capability_types"];
|
|
734
|
+
return Array.isArray(types) && types.includes(capabilityType);
|
|
735
|
+
});
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// src/credit/ledger.ts
|
|
740
|
+
import Database2 from "better-sqlite3";
|
|
741
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
742
|
+
var CREDIT_SCHEMA = `
|
|
743
|
+
CREATE TABLE IF NOT EXISTS credit_balances (
|
|
744
|
+
owner TEXT PRIMARY KEY,
|
|
745
|
+
balance INTEGER NOT NULL DEFAULT 0,
|
|
746
|
+
updated_at TEXT NOT NULL
|
|
747
|
+
);
|
|
748
|
+
|
|
749
|
+
CREATE TABLE IF NOT EXISTS credit_transactions (
|
|
750
|
+
id TEXT PRIMARY KEY,
|
|
751
|
+
owner TEXT NOT NULL,
|
|
752
|
+
amount INTEGER NOT NULL,
|
|
753
|
+
reason TEXT NOT NULL,
|
|
754
|
+
reference_id TEXT,
|
|
755
|
+
created_at TEXT NOT NULL
|
|
756
|
+
);
|
|
757
|
+
|
|
758
|
+
CREATE TABLE IF NOT EXISTS credit_escrow (
|
|
759
|
+
id TEXT PRIMARY KEY,
|
|
760
|
+
owner TEXT NOT NULL,
|
|
761
|
+
amount INTEGER NOT NULL,
|
|
762
|
+
card_id TEXT NOT NULL,
|
|
763
|
+
status TEXT NOT NULL DEFAULT 'held',
|
|
764
|
+
created_at TEXT NOT NULL,
|
|
765
|
+
settled_at TEXT
|
|
766
|
+
);
|
|
767
|
+
|
|
768
|
+
CREATE TABLE IF NOT EXISTS provider_registry (
|
|
769
|
+
owner TEXT PRIMARY KEY,
|
|
770
|
+
provider_number INTEGER NOT NULL,
|
|
771
|
+
registered_at TEXT NOT NULL
|
|
772
|
+
);
|
|
773
|
+
|
|
774
|
+
CREATE TABLE IF NOT EXISTS demand_vouchers (
|
|
775
|
+
id TEXT PRIMARY KEY,
|
|
776
|
+
owner TEXT NOT NULL,
|
|
777
|
+
amount INTEGER NOT NULL,
|
|
778
|
+
remaining INTEGER NOT NULL,
|
|
779
|
+
created_at TEXT NOT NULL,
|
|
780
|
+
expires_at TEXT NOT NULL,
|
|
781
|
+
is_active INTEGER NOT NULL DEFAULT 1
|
|
782
|
+
);
|
|
783
|
+
|
|
784
|
+
CREATE INDEX IF NOT EXISTS idx_transactions_owner ON credit_transactions(owner, created_at);
|
|
785
|
+
CREATE INDEX IF NOT EXISTS idx_escrow_owner ON credit_escrow(owner);
|
|
786
|
+
`;
|
|
787
|
+
function openCreditDb(path = ":memory:") {
|
|
788
|
+
const db = new Database2(path);
|
|
789
|
+
db.pragma("journal_mode = WAL");
|
|
790
|
+
db.pragma("foreign_keys = ON");
|
|
791
|
+
db.exec(CREDIT_SCHEMA);
|
|
792
|
+
try {
|
|
793
|
+
db.exec("ALTER TABLE credit_escrow ADD COLUMN funding_source TEXT NOT NULL DEFAULT 'balance'");
|
|
794
|
+
} catch {
|
|
795
|
+
}
|
|
796
|
+
ensureReliabilityTable(db);
|
|
797
|
+
return db;
|
|
798
|
+
}
|
|
799
|
+
function bootstrapAgent(db, owner, amount = 100) {
|
|
800
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
801
|
+
let isNew = false;
|
|
802
|
+
db.transaction(() => {
|
|
803
|
+
const result = db.prepare("INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, ?, ?)").run(owner, amount, now);
|
|
804
|
+
if (result.changes > 0) {
|
|
805
|
+
isNew = true;
|
|
806
|
+
db.prepare(
|
|
807
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
808
|
+
).run(randomUUID3(), owner, amount, "bootstrap", null, now);
|
|
809
|
+
}
|
|
810
|
+
})();
|
|
811
|
+
if (isNew) {
|
|
812
|
+
issueVoucher(db, owner, 50, 30);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
function getBalance(db, owner) {
|
|
816
|
+
const row = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(owner);
|
|
817
|
+
return row?.balance ?? 0;
|
|
818
|
+
}
|
|
819
|
+
function getTransactions(db, owner, limit = 100) {
|
|
820
|
+
return db.prepare(
|
|
821
|
+
"SELECT id, owner, amount, reason, reference_id, created_at FROM credit_transactions WHERE owner = ? ORDER BY created_at DESC LIMIT ?"
|
|
822
|
+
).all(owner, limit);
|
|
823
|
+
}
|
|
824
|
+
function registerProvider(db, owner) {
|
|
825
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
826
|
+
const maxRow = db.prepare("SELECT MAX(provider_number) as maxNum FROM provider_registry").get();
|
|
827
|
+
const nextNum = (maxRow?.maxNum ?? 0) + 1;
|
|
828
|
+
db.prepare("INSERT OR IGNORE INTO provider_registry (owner, provider_number, registered_at) VALUES (?, ?, ?)").run(owner, nextNum, now);
|
|
829
|
+
const row = db.prepare("SELECT provider_number FROM provider_registry WHERE owner = ?").get(owner);
|
|
830
|
+
return row.provider_number;
|
|
831
|
+
}
|
|
832
|
+
function getProviderNumber(db, owner) {
|
|
833
|
+
const row = db.prepare("SELECT provider_number FROM provider_registry WHERE owner = ?").get(owner);
|
|
834
|
+
return row?.provider_number ?? null;
|
|
835
|
+
}
|
|
836
|
+
function getProviderBonus(providerNumber) {
|
|
837
|
+
if (providerNumber <= 50) return 2;
|
|
838
|
+
if (providerNumber <= 200) return 1.5;
|
|
839
|
+
return 1;
|
|
840
|
+
}
|
|
841
|
+
function issueVoucher(db, owner, amount = 50, daysValid = 30) {
|
|
842
|
+
const id = randomUUID3();
|
|
843
|
+
const now = /* @__PURE__ */ new Date();
|
|
844
|
+
const expiresAt = new Date(now.getTime() + daysValid * 24 * 60 * 60 * 1e3);
|
|
845
|
+
db.prepare(
|
|
846
|
+
"INSERT INTO demand_vouchers (id, owner, amount, remaining, created_at, expires_at, is_active) VALUES (?, ?, ?, ?, ?, ?, 1)"
|
|
847
|
+
).run(id, owner, amount, amount, now.toISOString(), expiresAt.toISOString());
|
|
848
|
+
return id;
|
|
849
|
+
}
|
|
850
|
+
function getActiveVoucher(db, owner) {
|
|
851
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
852
|
+
const row = db.prepare(
|
|
853
|
+
"SELECT id, remaining, expires_at FROM demand_vouchers WHERE owner = ? AND is_active = 1 AND remaining > 0 AND expires_at > ? ORDER BY created_at ASC LIMIT 1"
|
|
854
|
+
).get(owner, now);
|
|
855
|
+
return row ?? null;
|
|
856
|
+
}
|
|
857
|
+
function consumeVoucher(db, voucherId, amount) {
|
|
858
|
+
db.prepare(
|
|
859
|
+
"UPDATE demand_vouchers SET remaining = remaining - ? WHERE id = ? AND remaining >= ?"
|
|
860
|
+
).run(amount, voucherId, amount);
|
|
861
|
+
}
|
|
862
|
+
function recordEarning(db, owner, amount, _cardId, receiptNonce) {
|
|
863
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
864
|
+
db.transaction(() => {
|
|
865
|
+
const existing = db.prepare(
|
|
866
|
+
"SELECT id FROM credit_transactions WHERE reference_id = ? AND reason = 'remote_earning'"
|
|
867
|
+
).get(receiptNonce);
|
|
868
|
+
if (existing) return;
|
|
869
|
+
db.prepare(
|
|
870
|
+
"INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, 0, ?)"
|
|
871
|
+
).run(owner, now);
|
|
872
|
+
db.prepare(
|
|
873
|
+
"UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
|
|
874
|
+
).run(amount, now, owner);
|
|
875
|
+
db.prepare(
|
|
876
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
877
|
+
).run(randomUUID3(), owner, amount, "remote_earning", receiptNonce, now);
|
|
878
|
+
})();
|
|
879
|
+
}
|
|
880
|
+
function migrateOwner(db, oldOwner, newOwner) {
|
|
881
|
+
if (oldOwner === newOwner) return;
|
|
882
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
883
|
+
db.transaction(() => {
|
|
884
|
+
const oldRow = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(oldOwner);
|
|
885
|
+
if (!oldRow) return;
|
|
886
|
+
const newRow = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(newOwner);
|
|
887
|
+
if (newRow) {
|
|
888
|
+
db.prepare("UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?").run(oldRow.balance, now, newOwner);
|
|
889
|
+
} else {
|
|
890
|
+
db.prepare("UPDATE credit_balances SET owner = ?, updated_at = ? WHERE owner = ?").run(newOwner, now, oldOwner);
|
|
891
|
+
}
|
|
892
|
+
if (newRow) {
|
|
893
|
+
db.prepare("DELETE FROM credit_balances WHERE owner = ?").run(oldOwner);
|
|
894
|
+
}
|
|
895
|
+
db.prepare("UPDATE credit_transactions SET owner = ? WHERE owner = ?").run(newOwner, oldOwner);
|
|
896
|
+
db.prepare("UPDATE credit_escrow SET owner = ? WHERE owner = ?").run(newOwner, oldOwner);
|
|
897
|
+
})();
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// src/credit/escrow.ts
|
|
901
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
902
|
+
var NETWORK_FEE_RATE = 0.05;
|
|
903
|
+
function holdEscrow(db, owner, amount, cardId) {
|
|
904
|
+
const escrowId = randomUUID4();
|
|
905
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
906
|
+
const hold = db.transaction(() => {
|
|
907
|
+
const voucher = getActiveVoucher(db, owner);
|
|
908
|
+
if (voucher && voucher.remaining >= amount) {
|
|
909
|
+
consumeVoucher(db, voucher.id, amount);
|
|
910
|
+
db.prepare(
|
|
911
|
+
"INSERT INTO credit_escrow (id, owner, amount, card_id, status, created_at, funding_source) VALUES (?, ?, ?, ?, ?, ?, ?)"
|
|
912
|
+
).run(escrowId, owner, amount, cardId, "held", now, "voucher");
|
|
913
|
+
db.prepare(
|
|
914
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
915
|
+
).run(randomUUID4(), owner, -amount, "voucher_hold", escrowId, now);
|
|
916
|
+
} else {
|
|
917
|
+
const row = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(owner);
|
|
918
|
+
if (!row || row.balance < amount) {
|
|
919
|
+
throw new AgentBnBError("Insufficient credits", "INSUFFICIENT_CREDITS");
|
|
920
|
+
}
|
|
921
|
+
db.prepare(
|
|
922
|
+
"UPDATE credit_balances SET balance = balance - ?, updated_at = ? WHERE owner = ? AND balance >= ?"
|
|
923
|
+
).run(amount, now, owner, amount);
|
|
924
|
+
db.prepare(
|
|
925
|
+
"INSERT INTO credit_escrow (id, owner, amount, card_id, status, created_at, funding_source) VALUES (?, ?, ?, ?, ?, ?, ?)"
|
|
926
|
+
).run(escrowId, owner, amount, cardId, "held", now, "balance");
|
|
927
|
+
db.prepare(
|
|
928
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
929
|
+
).run(randomUUID4(), owner, -amount, "escrow_hold", escrowId, now);
|
|
930
|
+
}
|
|
931
|
+
});
|
|
932
|
+
hold();
|
|
933
|
+
return escrowId;
|
|
934
|
+
}
|
|
935
|
+
function settleEscrow(db, escrowId, recipientOwner) {
|
|
936
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
937
|
+
const settle = db.transaction(() => {
|
|
938
|
+
const escrow = db.prepare("SELECT id, owner, amount, status, funding_source FROM credit_escrow WHERE id = ?").get(escrowId);
|
|
939
|
+
if (!escrow) {
|
|
940
|
+
throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
|
|
941
|
+
}
|
|
942
|
+
if (escrow.status !== "held") {
|
|
943
|
+
throw new AgentBnBError(
|
|
944
|
+
`Escrow ${escrowId} is already ${escrow.status}`,
|
|
945
|
+
"ESCROW_ALREADY_SETTLED"
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
const feeAmount = Math.floor(escrow.amount * NETWORK_FEE_RATE);
|
|
949
|
+
const providerAmount = escrow.amount - feeAmount;
|
|
950
|
+
db.prepare(
|
|
951
|
+
"INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, 0, ?)"
|
|
952
|
+
).run(recipientOwner, now);
|
|
953
|
+
db.prepare(
|
|
954
|
+
"UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
|
|
955
|
+
).run(providerAmount, now, recipientOwner);
|
|
956
|
+
if (feeAmount > 0) {
|
|
957
|
+
db.prepare(
|
|
958
|
+
"INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, 0, ?)"
|
|
959
|
+
).run("platform_treasury", now);
|
|
960
|
+
db.prepare(
|
|
961
|
+
"UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
|
|
962
|
+
).run(feeAmount, now, "platform_treasury");
|
|
963
|
+
db.prepare(
|
|
964
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
965
|
+
).run(randomUUID4(), "platform_treasury", feeAmount, "network_fee", escrowId, now);
|
|
966
|
+
}
|
|
967
|
+
db.prepare(
|
|
968
|
+
"UPDATE credit_escrow SET status = ?, settled_at = ? WHERE id = ?"
|
|
969
|
+
).run("settled", now, escrowId);
|
|
970
|
+
db.prepare(
|
|
971
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
972
|
+
).run(randomUUID4(), recipientOwner, providerAmount, "settlement", escrowId, now);
|
|
973
|
+
let providerNum = getProviderNumber(db, recipientOwner);
|
|
974
|
+
if (providerNum === null) {
|
|
975
|
+
providerNum = registerProvider(db, recipientOwner);
|
|
976
|
+
}
|
|
977
|
+
const bonus = getProviderBonus(providerNum);
|
|
978
|
+
if (bonus > 1) {
|
|
979
|
+
const bonusAmount = Math.floor(providerAmount * (bonus - 1));
|
|
980
|
+
if (bonusAmount > 0) {
|
|
981
|
+
db.prepare(
|
|
982
|
+
"INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, 0, ?)"
|
|
983
|
+
).run("platform_treasury", now);
|
|
984
|
+
db.prepare(
|
|
985
|
+
"UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
|
|
986
|
+
).run(bonusAmount, now, recipientOwner);
|
|
987
|
+
db.prepare(
|
|
988
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
989
|
+
).run(randomUUID4(), recipientOwner, bonusAmount, "provider_bonus", escrowId, now);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
try {
|
|
993
|
+
recordSuccessfulHire(db, recipientOwner, escrow.owner);
|
|
994
|
+
} catch {
|
|
995
|
+
}
|
|
996
|
+
});
|
|
997
|
+
settle();
|
|
998
|
+
}
|
|
999
|
+
function releaseEscrow(db, escrowId) {
|
|
1000
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1001
|
+
const release = db.transaction(() => {
|
|
1002
|
+
const escrow = db.prepare("SELECT id, owner, amount, status, funding_source FROM credit_escrow WHERE id = ?").get(escrowId);
|
|
1003
|
+
if (!escrow) {
|
|
1004
|
+
throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
|
|
1005
|
+
}
|
|
1006
|
+
if (escrow.status !== "held") {
|
|
1007
|
+
throw new AgentBnBError(
|
|
1008
|
+
`Escrow ${escrowId} is already ${escrow.status}`,
|
|
1009
|
+
"ESCROW_ALREADY_SETTLED"
|
|
1010
|
+
);
|
|
1011
|
+
}
|
|
1012
|
+
db.prepare(
|
|
1013
|
+
"UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
|
|
1014
|
+
).run(escrow.amount, now, escrow.owner);
|
|
1015
|
+
db.prepare(
|
|
1016
|
+
"UPDATE credit_escrow SET status = ?, settled_at = ? WHERE id = ?"
|
|
1017
|
+
).run("released", now, escrowId);
|
|
1018
|
+
db.prepare(
|
|
1019
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
1020
|
+
).run(randomUUID4(), escrow.owner, escrow.amount, "refund", escrowId, now);
|
|
1021
|
+
});
|
|
1022
|
+
release();
|
|
1023
|
+
}
|
|
1024
|
+
function confirmEscrowDebit(db, escrowId) {
|
|
1025
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1026
|
+
const confirm = db.transaction(() => {
|
|
1027
|
+
const escrow = db.prepare("SELECT id, owner, amount, status, funding_source FROM credit_escrow WHERE id = ?").get(escrowId);
|
|
1028
|
+
if (!escrow) {
|
|
1029
|
+
throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
|
|
1030
|
+
}
|
|
1031
|
+
if (escrow.status !== "held") {
|
|
1032
|
+
throw new AgentBnBError(
|
|
1033
|
+
`Escrow ${escrowId} is already ${escrow.status}`,
|
|
1034
|
+
"ESCROW_ALREADY_SETTLED"
|
|
1035
|
+
);
|
|
1036
|
+
}
|
|
1037
|
+
db.prepare(
|
|
1038
|
+
"UPDATE credit_escrow SET status = ?, settled_at = ? WHERE id = ?"
|
|
1039
|
+
).run("settled", now, escrowId);
|
|
1040
|
+
db.prepare(
|
|
1041
|
+
"INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
|
|
1042
|
+
).run(randomUUID4(), escrow.owner, 0, "remote_settlement_confirmed", escrowId, now);
|
|
1043
|
+
});
|
|
1044
|
+
confirm();
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
// src/credit/signing.ts
|
|
1048
|
+
import { generateKeyPairSync, sign, verify, createPublicKey, createPrivateKey } from "crypto";
|
|
1049
|
+
import { writeFileSync, readFileSync, existsSync, chmodSync } from "fs";
|
|
1050
|
+
import { join } from "path";
|
|
1051
|
+
function generateKeyPair() {
|
|
1052
|
+
const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
|
|
1053
|
+
publicKeyEncoding: { type: "spki", format: "der" },
|
|
1054
|
+
privateKeyEncoding: { type: "pkcs8", format: "der" }
|
|
1055
|
+
});
|
|
1056
|
+
return {
|
|
1057
|
+
publicKey: Buffer.from(publicKey),
|
|
1058
|
+
privateKey: Buffer.from(privateKey)
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
function saveKeyPair(configDir, keys) {
|
|
1062
|
+
const privatePath = join(configDir, "private.key");
|
|
1063
|
+
const publicPath = join(configDir, "public.key");
|
|
1064
|
+
writeFileSync(privatePath, keys.privateKey);
|
|
1065
|
+
chmodSync(privatePath, 384);
|
|
1066
|
+
writeFileSync(publicPath, keys.publicKey);
|
|
1067
|
+
}
|
|
1068
|
+
function loadKeyPair(configDir) {
|
|
1069
|
+
const privatePath = join(configDir, "private.key");
|
|
1070
|
+
const publicPath = join(configDir, "public.key");
|
|
1071
|
+
if (!existsSync(privatePath) || !existsSync(publicPath)) {
|
|
1072
|
+
throw new AgentBnBError("Keypair not found. Run `agentbnb init` to generate one.", "KEYPAIR_NOT_FOUND");
|
|
1073
|
+
}
|
|
1074
|
+
return {
|
|
1075
|
+
publicKey: readFileSync(publicPath),
|
|
1076
|
+
privateKey: readFileSync(privatePath)
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
function canonicalJson(data) {
|
|
1080
|
+
return JSON.stringify(data, Object.keys(data).sort());
|
|
1081
|
+
}
|
|
1082
|
+
function signEscrowReceipt(data, privateKey) {
|
|
1083
|
+
const message = Buffer.from(canonicalJson(data), "utf-8");
|
|
1084
|
+
const keyObject = createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" });
|
|
1085
|
+
const signature = sign(null, message, keyObject);
|
|
1086
|
+
return signature.toString("base64url");
|
|
1087
|
+
}
|
|
1088
|
+
function verifyEscrowReceipt(data, signature, publicKey) {
|
|
1089
|
+
try {
|
|
1090
|
+
const message = Buffer.from(canonicalJson(data), "utf-8");
|
|
1091
|
+
const keyObject = createPublicKey({ key: publicKey, format: "der", type: "spki" });
|
|
1092
|
+
const sigBuffer = Buffer.from(signature, "base64url");
|
|
1093
|
+
return verify(null, message, keyObject, sigBuffer);
|
|
1094
|
+
} catch {
|
|
1095
|
+
return false;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
export {
|
|
1100
|
+
insertRequestLog,
|
|
1101
|
+
getSkillRequestCount,
|
|
1102
|
+
getActivityFeed,
|
|
1103
|
+
getRequestLog,
|
|
1104
|
+
insertFeedback,
|
|
1105
|
+
getFeedbackForSkill,
|
|
1106
|
+
getFeedbackForProvider,
|
|
1107
|
+
insertEvolution,
|
|
1108
|
+
getLatestEvolution,
|
|
1109
|
+
getEvolutionHistory,
|
|
1110
|
+
openDatabase,
|
|
1111
|
+
insertCard,
|
|
1112
|
+
getCard,
|
|
1113
|
+
updateCard,
|
|
1114
|
+
updateReputation,
|
|
1115
|
+
updateSkillAvailability,
|
|
1116
|
+
updateSkillIdleRate,
|
|
1117
|
+
listCards,
|
|
1118
|
+
getCardsByCapabilityType,
|
|
1119
|
+
getCardsBySkillCapability,
|
|
1120
|
+
openCreditDb,
|
|
1121
|
+
bootstrapAgent,
|
|
1122
|
+
getBalance,
|
|
1123
|
+
getTransactions,
|
|
1124
|
+
recordEarning,
|
|
1125
|
+
migrateOwner,
|
|
1126
|
+
NETWORK_FEE_RATE,
|
|
1127
|
+
holdEscrow,
|
|
1128
|
+
settleEscrow,
|
|
1129
|
+
releaseEscrow,
|
|
1130
|
+
confirmEscrowDebit,
|
|
1131
|
+
generateKeyPair,
|
|
1132
|
+
saveKeyPair,
|
|
1133
|
+
loadKeyPair,
|
|
1134
|
+
signEscrowReceipt,
|
|
1135
|
+
verifyEscrowReceipt
|
|
1136
|
+
};
|