agentbnb 4.0.0 → 4.0.2

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.
Files changed (44) hide show
  1. package/README.md +2 -0
  2. package/dist/{card-IE5UV5QX.js → card-RSGDCHCV.js} +11 -4
  3. package/dist/chunk-3MJT4PZG.js +50 -0
  4. package/dist/{chunk-HEVXCYCY.js → chunk-4P3EMGL4.js} +61 -24
  5. package/dist/chunk-5AH3CMOX.js +62 -0
  6. package/dist/{chunk-QO67IGCW.js → chunk-5KFI5X7B.js} +1 -1
  7. package/dist/chunk-75OC6E4F.js +33 -0
  8. package/dist/{chunk-CUVIWPQO.js → chunk-7NA43XCG.js} +7 -6
  9. package/dist/{conduct-IQYAT6ZU.js → chunk-BH6WGYFB.js} +70 -33
  10. package/dist/{chunk-QVV2P3FN.js → chunk-DNWT5FZQ.js} +22 -2
  11. package/dist/chunk-FF226TIV.js +148 -0
  12. package/dist/{chunk-UJWYE7VL.js → chunk-GGYC5U2Z.js} +28 -111
  13. package/dist/chunk-HH24WMFN.js +373 -0
  14. package/dist/{websocket-client-5TIQDYQ4.js → chunk-JOY533UH.js} +38 -4
  15. package/dist/chunk-QITOPASZ.js +96 -0
  16. package/dist/{chunk-3Y36WQDV.js → chunk-QT7TEVNV.js} +14 -2
  17. package/dist/{chunk-UOGDK2S2.js → chunk-T7NS2J2B.js} +1 -1
  18. package/dist/{chunk-XA63SD4T.js → chunk-WGZ5AGOX.js} +37 -0
  19. package/dist/{chunk-RSX4SCPN.js → chunk-XND2DWTZ.js} +4 -3
  20. package/dist/cli/index.js +2924 -835
  21. package/dist/{client-IOTK6GOS.js → client-T5MTY3CS.js} +3 -3
  22. package/dist/conduct-GZQNFTRP.js +19 -0
  23. package/dist/conduct-N52JX7RT.js +52 -0
  24. package/dist/{conductor-mode-XU7ONJWC.js → conductor-mode-XUWGR4ZE.js} +16 -9
  25. package/dist/execute-PNGQOMYO.js +10 -0
  26. package/dist/index.d.ts +1148 -915
  27. package/dist/index.js +589 -127
  28. package/dist/{peers-G36URZYB.js → peers-K7FSHPN3.js} +2 -1
  29. package/dist/request-4GQSSM4B.js +196 -0
  30. package/dist/serve-skill-TPHZH6BS.js +104 -0
  31. package/dist/server-365V3GYD.js +295 -0
  32. package/dist/websocket-client-6IIDGXKB.js +7 -0
  33. package/package.json +3 -6
  34. package/skills/agentbnb/HEARTBEAT.rules.md +47 -0
  35. package/skills/agentbnb/SKILL.md +166 -0
  36. package/skills/agentbnb/auto-request.ts +14 -0
  37. package/skills/agentbnb/auto-share.ts +10 -0
  38. package/skills/agentbnb/bootstrap.test.ts +323 -0
  39. package/skills/agentbnb/bootstrap.ts +126 -0
  40. package/skills/agentbnb/credit-mgr.ts +11 -0
  41. package/skills/agentbnb/gateway.ts +12 -0
  42. package/skills/agentbnb/install.sh +210 -0
  43. package/dist/chunk-BEI5MTNZ.js +0 -91
  44. package/dist/execute-GDGBU6DJ.js +0 -10
package/README.md CHANGED
@@ -70,6 +70,8 @@ Read the full design philosophy in [AGENT-NATIVE-PROTOCOL.md](AGENT-NATIVE-PROTO
70
70
 
71
71
  <p align="center"><code>1,001 tests · v4.0 shipped · Ed25519 signed escrow · 5 execution modes · MCP Server · Hub Agents</code></p>
72
72
 
73
+ The Hub shows not just what agents can do — but how trusted they are. Every capability card displays execution-backed trust signals: **performance tier** (Listed / Active / Trusted), **authority source** (Self-declared / Platform observed / Org-backed), and live success rates drawn from real execution history. Trust is earned, not declared.
74
+
73
75
  ---
74
76
 
75
77
  ## Platform Support
@@ -1,15 +1,22 @@
1
1
  import {
2
2
  CapabilityCardV2Schema
3
- } from "./chunk-XA63SD4T.js";
3
+ } from "./chunk-WGZ5AGOX.js";
4
4
 
5
5
  // src/conductor/card.ts
6
+ import { createHash } from "crypto";
6
7
  var CONDUCTOR_OWNER = "agentbnb-conductor";
7
8
  var CONDUCTOR_CARD_ID = "00000000-0000-4000-8000-000000000001";
8
- function buildConductorCard() {
9
+ function ownerToCardId(owner) {
10
+ const hash = createHash("sha256").update(owner).digest("hex").slice(0, 32);
11
+ return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-4${hash.slice(13, 16)}-8${hash.slice(17, 20)}-${hash.slice(20, 32)}`;
12
+ }
13
+ function buildConductorCard(owner) {
14
+ const cardOwner = owner ?? CONDUCTOR_OWNER;
15
+ const cardId = owner ? ownerToCardId(owner) : CONDUCTOR_CARD_ID;
9
16
  const card = {
10
17
  spec_version: "2.0",
11
- id: CONDUCTOR_CARD_ID,
12
- owner: CONDUCTOR_OWNER,
18
+ id: cardId,
19
+ owner: cardOwner,
13
20
  agent_name: "AgentBnB Conductor",
14
21
  skills: [
15
22
  {
@@ -0,0 +1,50 @@
1
+ // src/utils/interpolation.ts
2
+ function resolvePath(obj, path) {
3
+ const segments = path.replace(/\[(\d+)\]/g, ".$1").split(".").filter((s) => s.length > 0);
4
+ let current = obj;
5
+ for (const segment of segments) {
6
+ if (current === null || current === void 0) {
7
+ return void 0;
8
+ }
9
+ if (typeof current !== "object") {
10
+ return void 0;
11
+ }
12
+ current = current[segment];
13
+ }
14
+ return current;
15
+ }
16
+ function interpolate(template, context) {
17
+ return template.replace(/\$\{([^}]+)\}/g, (_match, expression) => {
18
+ const resolved = resolvePath(context, expression.trim());
19
+ if (resolved === void 0 || resolved === null) {
20
+ return "";
21
+ }
22
+ if (typeof resolved === "object") {
23
+ return JSON.stringify(resolved);
24
+ }
25
+ return String(resolved);
26
+ });
27
+ }
28
+ function interpolateObject(obj, context) {
29
+ const result = {};
30
+ for (const [key, value] of Object.entries(obj)) {
31
+ result[key] = interpolateValue(value, context);
32
+ }
33
+ return result;
34
+ }
35
+ function interpolateValue(value, context) {
36
+ if (typeof value === "string") {
37
+ return interpolate(value, context);
38
+ }
39
+ if (Array.isArray(value)) {
40
+ return value.map((item) => interpolateValue(item, context));
41
+ }
42
+ if (value !== null && typeof value === "object") {
43
+ return interpolateObject(value, context);
44
+ }
45
+ return value;
46
+ }
47
+
48
+ export {
49
+ interpolateObject
50
+ };
@@ -1,11 +1,16 @@
1
1
  import {
2
- interpolateObject,
3
- scorePeers,
2
+ interpolateObject
3
+ } from "./chunk-3MJT4PZG.js";
4
+ import {
5
+ scorePeers
6
+ } from "./chunk-GGYC5U2Z.js";
7
+ import {
8
+ fetchRemoteCards,
4
9
  searchCards
5
- } from "./chunk-UJWYE7VL.js";
10
+ } from "./chunk-FF226TIV.js";
6
11
  import {
7
12
  requestCapability
8
- } from "./chunk-RSX4SCPN.js";
13
+ } from "./chunk-XND2DWTZ.js";
9
14
 
10
15
  // src/conductor/task-decomposer.ts
11
16
  import { randomUUID } from "crypto";
@@ -118,10 +123,17 @@ function decompose(task, _availableCapabilities) {
118
123
 
119
124
  // src/conductor/capability-matcher.ts
120
125
  var MAX_ALTERNATIVES = 2;
121
- function matchSubTasks(opts) {
122
- const { db, subtasks, conductorOwner } = opts;
123
- return subtasks.map((subtask) => {
124
- const cards = searchCards(db, subtask.required_capability, { online: true });
126
+ async function matchSubTasks(opts) {
127
+ const { db, subtasks, conductorOwner, registryUrl } = opts;
128
+ return Promise.all(subtasks.map(async (subtask) => {
129
+ let cards = searchCards(db, subtask.required_capability, { online: true });
130
+ if (cards.length === 0 && registryUrl) {
131
+ try {
132
+ cards = await fetchRemoteCards(registryUrl, { q: subtask.required_capability, online: true });
133
+ } catch {
134
+ cards = [];
135
+ }
136
+ }
125
137
  const candidates = [];
126
138
  for (const card of cards) {
127
139
  const cardAsV2 = card;
@@ -163,11 +175,12 @@ function matchSubTasks(opts) {
163
175
  subtask_id: subtask.id,
164
176
  selected_agent: top.card.owner,
165
177
  selected_skill: top.skillId ?? "",
178
+ selected_card_id: top.card.id,
166
179
  score: top.rawScore,
167
180
  credits: top.cost,
168
181
  alternatives
169
182
  };
170
- });
183
+ }));
171
184
  }
172
185
 
173
186
  // src/conductor/budget-controller.ts
@@ -262,7 +275,7 @@ function computeWaves(subtasks) {
262
275
  return waves;
263
276
  }
264
277
  async function orchestrate(opts) {
265
- const { subtasks, matches, gatewayToken, resolveAgentUrl, timeoutMs = 3e4, maxBudget } = opts;
278
+ const { subtasks, matches, gatewayToken, resolveAgentUrl, timeoutMs = 3e5, maxBudget, relayClient, requesterOwner } = opts;
266
279
  const startTime = Date.now();
267
280
  if (subtasks.length === 0) {
268
281
  return {
@@ -313,26 +326,50 @@ async function orchestrate(opts) {
313
326
  );
314
327
  const primary = resolveAgentUrl(m.selected_agent);
315
328
  try {
316
- const res = await requestCapability({
317
- gatewayUrl: primary.url,
318
- token: gatewayToken,
319
- cardId: primary.cardId,
320
- params: interpolatedParams,
321
- timeoutMs
322
- });
329
+ let res;
330
+ if (primary.url.startsWith("relay://") && relayClient) {
331
+ const targetOwner = primary.url.replace("relay://", "");
332
+ res = await relayClient.request({
333
+ targetOwner,
334
+ cardId: primary.cardId,
335
+ params: interpolatedParams,
336
+ requester: requesterOwner,
337
+ timeoutMs
338
+ });
339
+ } else {
340
+ res = await requestCapability({
341
+ gatewayUrl: primary.url,
342
+ token: gatewayToken,
343
+ cardId: primary.cardId,
344
+ params: interpolatedParams,
345
+ timeoutMs
346
+ });
347
+ }
323
348
  return { taskId, result: res, credits: m.credits };
324
349
  } catch (primaryErr) {
325
350
  if (m.alternatives.length > 0) {
326
351
  const alt = m.alternatives[0];
327
352
  const altAgent = resolveAgentUrl(alt.agent);
328
353
  try {
329
- const altRes = await requestCapability({
330
- gatewayUrl: altAgent.url,
331
- token: gatewayToken,
332
- cardId: altAgent.cardId,
333
- params: interpolatedParams,
334
- timeoutMs
335
- });
354
+ let altRes;
355
+ if (altAgent.url.startsWith("relay://") && relayClient) {
356
+ const targetOwner = altAgent.url.replace("relay://", "");
357
+ altRes = await relayClient.request({
358
+ targetOwner,
359
+ cardId: altAgent.cardId,
360
+ params: interpolatedParams,
361
+ requester: requesterOwner,
362
+ timeoutMs
363
+ });
364
+ } else {
365
+ altRes = await requestCapability({
366
+ gatewayUrl: altAgent.url,
367
+ token: gatewayToken,
368
+ cardId: altAgent.cardId,
369
+ params: interpolatedParams,
370
+ timeoutMs
371
+ });
372
+ }
336
373
  return { taskId, result: altRes, credits: alt.credits };
337
374
  } catch (altErr) {
338
375
  throw new Error(
@@ -0,0 +1,62 @@
1
+ import {
2
+ getConfigDir
3
+ } from "./chunk-75OC6E4F.js";
4
+
5
+ // src/cli/peers.ts
6
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
7
+ import { join } from "path";
8
+ function getPeersPath() {
9
+ return join(getConfigDir(), "peers.json");
10
+ }
11
+ function loadPeers() {
12
+ const peersPath = getPeersPath();
13
+ if (!existsSync(peersPath)) {
14
+ return [];
15
+ }
16
+ try {
17
+ const raw = readFileSync(peersPath, "utf-8");
18
+ return JSON.parse(raw);
19
+ } catch {
20
+ return [];
21
+ }
22
+ }
23
+ function writePeers(peers) {
24
+ const dir = getConfigDir();
25
+ if (!existsSync(dir)) {
26
+ mkdirSync(dir, { recursive: true });
27
+ }
28
+ writeFileSync(getPeersPath(), JSON.stringify(peers, null, 2), "utf-8");
29
+ }
30
+ function savePeer(peer) {
31
+ const peers = loadPeers();
32
+ const lowerName = peer.name.toLowerCase();
33
+ const existing = peers.findIndex((p) => p.name.toLowerCase() === lowerName);
34
+ if (existing >= 0) {
35
+ peers[existing] = peer;
36
+ } else {
37
+ peers.push(peer);
38
+ }
39
+ writePeers(peers);
40
+ }
41
+ function removePeer(name) {
42
+ const peers = loadPeers();
43
+ const lowerName = name.toLowerCase();
44
+ const filtered = peers.filter((p) => p.name.toLowerCase() !== lowerName);
45
+ if (filtered.length === peers.length) {
46
+ return false;
47
+ }
48
+ writePeers(filtered);
49
+ return true;
50
+ }
51
+ function findPeer(name) {
52
+ const peers = loadPeers();
53
+ const lowerName = name.toLowerCase();
54
+ return peers.find((p) => p.name.toLowerCase() === lowerName) ?? null;
55
+ }
56
+
57
+ export {
58
+ loadPeers,
59
+ savePeer,
60
+ removePeer,
61
+ findPeer
62
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  AgentBnBError
3
- } from "./chunk-XA63SD4T.js";
3
+ } from "./chunk-WGZ5AGOX.js";
4
4
 
5
5
  // src/credit/signing.ts
6
6
  import { generateKeyPairSync, sign, verify, createPublicKey, createPrivateKey } from "crypto";
@@ -0,0 +1,33 @@
1
+ // src/cli/config.ts
2
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
3
+ import { homedir } from "os";
4
+ import { join } from "path";
5
+ function getConfigDir() {
6
+ return process.env["AGENTBNB_DIR"] ?? join(homedir(), ".agentbnb");
7
+ }
8
+ function getConfigPath() {
9
+ return join(getConfigDir(), "config.json");
10
+ }
11
+ function loadConfig() {
12
+ const configPath = getConfigPath();
13
+ if (!existsSync(configPath)) return null;
14
+ try {
15
+ const raw = readFileSync(configPath, "utf-8");
16
+ return JSON.parse(raw);
17
+ } catch {
18
+ return null;
19
+ }
20
+ }
21
+ function saveConfig(config) {
22
+ const dir = getConfigDir();
23
+ if (!existsSync(dir)) {
24
+ mkdirSync(dir, { recursive: true });
25
+ }
26
+ writeFileSync(getConfigPath(), JSON.stringify(config, null, 2), "utf-8");
27
+ }
28
+
29
+ export {
30
+ getConfigDir,
31
+ loadConfig,
32
+ saveConfig
33
+ };
@@ -2,7 +2,7 @@ import {
2
2
  getCard,
3
3
  insertRequestLog,
4
4
  updateReputation
5
- } from "./chunk-UOGDK2S2.js";
5
+ } from "./chunk-T7NS2J2B.js";
6
6
  import {
7
7
  confirmEscrowDebit,
8
8
  getBalance,
@@ -10,13 +10,13 @@ import {
10
10
  recordEarning,
11
11
  releaseEscrow,
12
12
  settleEscrow
13
- } from "./chunk-QVV2P3FN.js";
13
+ } from "./chunk-DNWT5FZQ.js";
14
14
  import {
15
15
  verifyEscrowReceipt
16
- } from "./chunk-QO67IGCW.js";
16
+ } from "./chunk-5KFI5X7B.js";
17
17
  import {
18
18
  AgentBnBError
19
- } from "./chunk-XA63SD4T.js";
19
+ } from "./chunk-WGZ5AGOX.js";
20
20
 
21
21
  // src/gateway/execute.ts
22
22
  import { randomUUID } from "crypto";
@@ -51,7 +51,8 @@ async function executeCapabilityRequest(opts) {
51
51
  escrowReceipt: receipt,
52
52
  skillExecutor,
53
53
  handlerUrl,
54
- timeoutMs = 3e4
54
+ timeoutMs = 3e5,
55
+ onProgress
55
56
  } = opts;
56
57
  const card = getCard(registryDb, cardId);
57
58
  if (!card) {
@@ -158,7 +159,7 @@ async function executeCapabilityRequest(opts) {
158
159
  if (skillExecutor) {
159
160
  const targetSkillId = resolvedSkillId ?? skillId ?? cardId;
160
161
  try {
161
- const execResult = await skillExecutor.execute(targetSkillId, params);
162
+ const execResult = await skillExecutor.execute(targetSkillId, params, onProgress);
162
163
  if (!execResult.success) {
163
164
  return handleFailure("failure", execResult.latency_ms, execResult.error ?? "Execution failed");
164
165
  }
@@ -4,23 +4,25 @@ import {
4
4
  decompose,
5
5
  matchSubTasks,
6
6
  orchestrate
7
- } from "./chunk-HEVXCYCY.js";
7
+ } from "./chunk-4P3EMGL4.js";
8
8
  import {
9
9
  BudgetManager
10
- } from "./chunk-UJWYE7VL.js";
11
- import "./chunk-RSX4SCPN.js";
10
+ } from "./chunk-GGYC5U2Z.js";
12
11
  import {
13
- loadConfig,
14
12
  loadPeers
15
- } from "./chunk-BEI5MTNZ.js";
13
+ } from "./chunk-5AH3CMOX.js";
14
+ import {
15
+ loadConfig
16
+ } from "./chunk-75OC6E4F.js";
16
17
  import {
17
18
  openDatabase
18
- } from "./chunk-UOGDK2S2.js";
19
+ } from "./chunk-T7NS2J2B.js";
19
20
  import {
20
21
  openCreditDb
21
- } from "./chunk-QVV2P3FN.js";
22
- import "./chunk-QO67IGCW.js";
23
- import "./chunk-XA63SD4T.js";
22
+ } from "./chunk-DNWT5FZQ.js";
23
+ import {
24
+ RelayClient
25
+ } from "./chunk-JOY533UH.js";
24
26
 
25
27
  // src/cli/conduct.ts
26
28
  async function conductAction(task, opts) {
@@ -36,10 +38,11 @@ async function conductAction(task, opts) {
36
38
  const db = openDatabase(config.db_path);
37
39
  let matchResults;
38
40
  try {
39
- matchResults = matchSubTasks({
41
+ matchResults = await matchSubTasks({
40
42
  db,
41
43
  subtasks,
42
- conductorOwner: config.owner
44
+ conductorOwner: config.owner,
45
+ registryUrl: config.registry
43
46
  });
44
47
  } finally {
45
48
  db.close();
@@ -74,33 +77,66 @@ async function conductAction(task, opts) {
74
77
  return { success: true, plan: planOutput };
75
78
  }
76
79
  const peers = loadPeers();
80
+ const matchMap = new Map(
81
+ matchResults.map((m) => [m.subtask_id, m])
82
+ );
77
83
  const resolveAgentUrl = (owner) => {
78
84
  const peer = peers.find((p) => p.name.toLowerCase() === owner.toLowerCase());
79
- if (!peer) {
80
- throw new Error(
81
- `Unknown peer "${owner}". Add with: agentbnb peers add ${owner} <url> <token>`
82
- );
85
+ if (peer) {
86
+ const execDb = openDatabase(config.db_path);
87
+ try {
88
+ const stmt = execDb.prepare("SELECT id FROM capability_cards WHERE owner = ? LIMIT 1");
89
+ const row = stmt.get(owner);
90
+ return { url: peer.url, cardId: row?.id ?? owner };
91
+ } finally {
92
+ execDb.close();
93
+ }
83
94
  }
84
- const execDb = openDatabase(config.db_path);
85
- try {
86
- const stmt = execDb.prepare("SELECT id FROM capability_cards WHERE owner = ? LIMIT 1");
87
- const row = stmt.get(owner);
88
- return { url: peer.url, cardId: row?.id ?? owner };
89
- } finally {
90
- execDb.close();
95
+ if (config.registry) {
96
+ let cardId = owner;
97
+ for (const m of matchMap.values()) {
98
+ if (m.selected_agent === owner && m.selected_card_id) {
99
+ cardId = m.selected_card_id;
100
+ break;
101
+ }
102
+ }
103
+ return { url: `relay://${owner}`, cardId };
91
104
  }
105
+ throw new Error(
106
+ `Unknown peer "${owner}". Add with: agentbnb peers add ${owner} <url> <token>`
107
+ );
92
108
  };
93
- const matchMap = new Map(
94
- matchResults.map((m) => [m.subtask_id, m])
95
- );
96
- const orchResult = await orchestrate({
97
- subtasks,
98
- matches: matchMap,
99
- gatewayToken: config.token ?? "",
100
- resolveAgentUrl,
101
- timeoutMs: 3e4,
102
- maxBudget
103
- });
109
+ let relay;
110
+ if (config.registry) {
111
+ relay = new RelayClient({
112
+ registryUrl: config.registry,
113
+ owner: config.owner,
114
+ token: config.token ?? "",
115
+ card: { id: config.owner, owner: config.owner, name: "conductor" },
116
+ onRequest: async () => ({ error: { code: -32601, message: "Conductor does not accept requests" } }),
117
+ silent: true
118
+ });
119
+ try {
120
+ await relay.connect();
121
+ } catch {
122
+ relay = void 0;
123
+ }
124
+ }
125
+ let orchResult;
126
+ try {
127
+ orchResult = await orchestrate({
128
+ subtasks,
129
+ matches: matchMap,
130
+ gatewayToken: config.token ?? "",
131
+ resolveAgentUrl,
132
+ timeoutMs: 3e5,
133
+ maxBudget,
134
+ relayClient: relay,
135
+ requesterOwner: config.owner
136
+ });
137
+ } finally {
138
+ relay?.disconnect();
139
+ }
104
140
  const resultObj = {};
105
141
  for (const [key, value] of orchResult.results) {
106
142
  resultObj[key] = value;
@@ -114,6 +150,7 @@ async function conductAction(task, opts) {
114
150
  errors: orchResult.errors
115
151
  };
116
152
  }
153
+
117
154
  export {
118
155
  conductAction
119
156
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  AgentBnBError
3
- } from "./chunk-XA63SD4T.js";
3
+ } from "./chunk-WGZ5AGOX.js";
4
4
 
5
5
  // src/credit/escrow.ts
6
6
  import { randomUUID } from "crypto";
@@ -178,6 +178,25 @@ function recordEarning(db, owner, amount, _cardId, receiptNonce) {
178
178
  ).run(randomUUID2(), owner, amount, "remote_earning", receiptNonce, now);
179
179
  })();
180
180
  }
181
+ function migrateOwner(db, oldOwner, newOwner) {
182
+ if (oldOwner === newOwner) return;
183
+ const now = (/* @__PURE__ */ new Date()).toISOString();
184
+ db.transaction(() => {
185
+ const oldRow = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(oldOwner);
186
+ if (!oldRow) return;
187
+ const newRow = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(newOwner);
188
+ if (newRow) {
189
+ db.prepare("UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?").run(oldRow.balance, now, newOwner);
190
+ } else {
191
+ db.prepare("UPDATE credit_balances SET owner = ?, updated_at = ? WHERE owner = ?").run(newOwner, now, oldOwner);
192
+ }
193
+ if (newRow) {
194
+ db.prepare("DELETE FROM credit_balances WHERE owner = ?").run(oldOwner);
195
+ }
196
+ db.prepare("UPDATE credit_transactions SET owner = ? WHERE owner = ?").run(newOwner, oldOwner);
197
+ db.prepare("UPDATE credit_escrow SET owner = ? WHERE owner = ?").run(newOwner, oldOwner);
198
+ })();
199
+ }
181
200
 
182
201
  export {
183
202
  holdEscrow,
@@ -188,5 +207,6 @@ export {
188
207
  bootstrapAgent,
189
208
  getBalance,
190
209
  getTransactions,
191
- recordEarning
210
+ recordEarning,
211
+ migrateOwner
192
212
  };