agentbnb 8.3.2 → 8.4.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.
Files changed (45) hide show
  1. package/dist/{card-T2XJZA5A.js → card-HYTD2BJQ.js} +1 -1
  2. package/dist/{chunk-S7DZHKCG.js → chunk-2PP5MQPD.js} +5 -3
  3. package/dist/{chunk-75OC6E4F.js → chunk-3XPBFF6H.js} +1 -0
  4. package/dist/{chunk-5AH3CMOX.js → chunk-3YQ73ZM6.js} +1 -1
  5. package/dist/{chunk-FELGHDCA.js → chunk-6FZ4WYQL.js} +38 -4
  6. package/dist/{chunk-D242QZCR.js → chunk-6XCN62YU.js} +10 -54
  7. package/dist/chunk-AZKVGC5T.js +53 -0
  8. package/dist/{chunk-NHVHLUYS.js → chunk-CFHCG5FE.js} +6 -217
  9. package/dist/{chunk-5AIYALBX.js → chunk-COA2D7QM.js} +4 -3
  10. package/dist/chunk-G5WKW3ED.js +41 -0
  11. package/dist/{chunk-F3KIEVJ2.js → chunk-HU46M4JA.js} +2 -87
  12. package/dist/{chunk-77KGEDH4.js → chunk-MZSVVG55.js} +2 -41
  13. package/dist/chunk-PCQEHIGF.js +750 -0
  14. package/dist/chunk-PIPCGRCR.js +91 -0
  15. package/dist/{chunk-QFPXZITP.js → chunk-PMRTQ2RL.js} +34 -0
  16. package/dist/{chunk-7IQE34QK.js → chunk-UF6R2RVN.js} +5 -3
  17. package/dist/{chunk-IGQNP3ZO.js → chunk-VAAEBCMU.js} +1 -1
  18. package/dist/chunk-VRPLSK34.js +214 -0
  19. package/dist/{chunk-BARHNIKG.js → chunk-WK2QSO4E.js} +1 -1
  20. package/dist/chunk-WPB5LFGI.js +132 -0
  21. package/dist/chunk-XGOA5J2K.js +173 -0
  22. package/dist/{chunk-VJ7XBEY6.js → chunk-Z5726VPY.js} +6 -6
  23. package/dist/cli/index.js +118 -789
  24. package/dist/conduct-FZPUMPCI.js +25 -0
  25. package/dist/{conduct-VYYBCPHA.js → conduct-J2NXU6RM.js} +12 -9
  26. package/dist/{conductor-mode-SBDCRIX6.js → conductor-mode-HNNMWZIH.js} +11 -8
  27. package/dist/config-IRWLG6IW.js +12 -0
  28. package/dist/{execute-FZLQGIXB.js → execute-UAD5T3BQ.js} +1 -1
  29. package/dist/{execute-TEZPQ5WP.js → execute-UP46R7KS.js} +6 -5
  30. package/dist/index.js +34 -0
  31. package/dist/openclaw-setup-KA72IIEW.js +296 -0
  32. package/dist/openclaw-skills-CT673RBL.js +370 -0
  33. package/dist/{peers-K7FSHPN3.js → peers-F2EWUMVQ.js} +2 -2
  34. package/dist/{publish-capability-HVYILTPR.js → publish-capability-GNH5FHKG.js} +2 -2
  35. package/dist/{request-KJNKR27T.js → request-IM3ZLAOA.js} +13 -9
  36. package/dist/scanner-F5VNV5FP.js +8 -0
  37. package/dist/{serve-skill-GC6NIQ5T.js → serve-skill-6RKMVDMK.js} +6 -5
  38. package/dist/{server-6I7GU2OZ.js → server-MH7FTZFN.js} +11 -9
  39. package/dist/{service-coordinator-LZJCAQSJ.js → service-coordinator-R5LZVM6A.js} +31 -26
  40. package/dist/skills/agentbnb/bootstrap.js +34 -2
  41. package/dist/store-4Z446745.js +32 -0
  42. package/dist/writer-4QJ3U3WE.js +16 -0
  43. package/package.json +1 -1
  44. package/skills/agentbnb/bootstrap.ts +45 -2
  45. package/dist/conduct-4JDMWBQD.js +0 -22
@@ -0,0 +1,750 @@
1
+ import {
2
+ DEFAULT_BUDGET_CONFIG
3
+ } from "./chunk-AZKVGC5T.js";
4
+ import {
5
+ KNOWN_API_KEYS,
6
+ buildDraftCard,
7
+ detectApiKeys,
8
+ detectOpenPorts
9
+ } from "./chunk-CFHCG5FE.js";
10
+ import {
11
+ createLedger,
12
+ loadOrRepairIdentity
13
+ } from "./chunk-WK2QSO4E.js";
14
+ import {
15
+ DEFAULT_AUTONOMY_CONFIG
16
+ } from "./chunk-G5WKW3ED.js";
17
+ import {
18
+ bootstrapAgent,
19
+ getBalance,
20
+ migrateOwner,
21
+ openCreditDb
22
+ } from "./chunk-HU46M4JA.js";
23
+ import {
24
+ getConfigDir,
25
+ loadConfig,
26
+ saveConfig
27
+ } from "./chunk-3XPBFF6H.js";
28
+ import {
29
+ parseSoulMd
30
+ } from "./chunk-VAAEBCMU.js";
31
+ import {
32
+ attachCanonicalAgentId,
33
+ insertCard,
34
+ listCards,
35
+ openDatabase
36
+ } from "./chunk-COA2D7QM.js";
37
+ import {
38
+ createAgentRecord,
39
+ lookupAgent,
40
+ lookupAgentByOwner,
41
+ updateAgentRecord
42
+ } from "./chunk-WTHMHNKC.js";
43
+ import {
44
+ AgentBnBError,
45
+ CapabilityCardV2Schema
46
+ } from "./chunk-I7KWA7OB.js";
47
+
48
+ // src/openclaw/soul-sync.ts
49
+ import { randomUUID } from "crypto";
50
+ var SKILL_META_GLOBAL_RE = /(?:^|\s)-\s*(capability_types|requires(?:_capabilities)?|visibility)\s*:\s*([^-][^]*?)(?=\s+-\s+(?:capability_types|requires(?:_capabilities)?|visibility)\s*:|$)/gi;
51
+ function extractSkillMeta(raw) {
52
+ let capability_types;
53
+ let requires_capabilities;
54
+ let visibility;
55
+ const removedRanges = [];
56
+ SKILL_META_GLOBAL_RE.lastIndex = 0;
57
+ let m;
58
+ while ((m = SKILL_META_GLOBAL_RE.exec(raw)) !== null) {
59
+ const key = m[1].toLowerCase();
60
+ const val = m[2].trim();
61
+ if (key === "capability_types") {
62
+ capability_types = val.split(",").map((v) => v.trim()).filter(Boolean);
63
+ } else if (key === "requires" || key === "requires_capabilities") {
64
+ requires_capabilities = val.split(",").map((v) => v.trim()).filter(Boolean);
65
+ } else if (key === "visibility") {
66
+ const vis = val.toLowerCase();
67
+ if (vis === "public" || vis === "private") {
68
+ visibility = vis;
69
+ }
70
+ }
71
+ removedRanges.push({ start: m.index, end: m.index + m[0].length });
72
+ }
73
+ let description = raw;
74
+ for (const { start, end } of removedRanges.slice().reverse()) {
75
+ description = description.slice(0, start) + description.slice(end);
76
+ }
77
+ description = description.trim();
78
+ return { description, capability_types, requires_capabilities, visibility };
79
+ }
80
+ function parseSoulMdV2(content) {
81
+ const parsed = parseSoulMd(content);
82
+ const skills = parsed.capabilities.map((cap) => {
83
+ const sanitizedId = cap.name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
84
+ const id = sanitizedId.length > 0 ? sanitizedId : randomUUID();
85
+ const { description: cleanDesc, capability_types, requires_capabilities, visibility } = extractSkillMeta(cap.description);
86
+ const finalDescription = (cleanDesc || cap.name).slice(0, 500);
87
+ const skill = {
88
+ id,
89
+ name: cap.name,
90
+ description: finalDescription,
91
+ level: 2,
92
+ inputs: [
93
+ {
94
+ name: "input",
95
+ type: "text",
96
+ description: "Input for the skill",
97
+ required: true
98
+ }
99
+ ],
100
+ outputs: [
101
+ {
102
+ name: "output",
103
+ type: "text",
104
+ description: "Output from the skill",
105
+ required: true
106
+ }
107
+ ],
108
+ pricing: { credits_per_call: cap.pricing !== void 0 ? cap.pricing : 10 },
109
+ availability: { online: true }
110
+ };
111
+ if (capability_types !== void 0) skill.capability_types = capability_types;
112
+ if (requires_capabilities !== void 0) skill.requires_capabilities = requires_capabilities;
113
+ if (visibility !== void 0) skill.visibility = visibility;
114
+ return skill;
115
+ });
116
+ return {
117
+ agentName: parsed.name || "Unknown Agent",
118
+ description: parsed.description,
119
+ skills
120
+ };
121
+ }
122
+ function publishFromSoulV2(db, soulContent, owner, sharedSkills) {
123
+ const { agentName, skills: allSkills } = parseSoulMdV2(soulContent);
124
+ const skills = allSkills.filter((skill) => {
125
+ if (sharedSkills && sharedSkills.length > 0) {
126
+ return sharedSkills.includes(skill.id);
127
+ }
128
+ return skill.visibility !== "private";
129
+ });
130
+ if (skills.length === 0) {
131
+ throw new AgentBnBError("SOUL.md has no H2 sections", "VALIDATION_ERROR");
132
+ }
133
+ const existingCards = listCards(db, owner);
134
+ const existingV2 = existingCards.find(
135
+ (c) => c.spec_version === "2.0"
136
+ );
137
+ const now = (/* @__PURE__ */ new Date()).toISOString();
138
+ const cardId = existingV2?.id ?? randomUUID();
139
+ const card = {
140
+ spec_version: "2.0",
141
+ id: cardId,
142
+ owner,
143
+ agent_name: agentName,
144
+ skills,
145
+ availability: { online: true },
146
+ created_at: existingV2?.created_at ?? now,
147
+ updated_at: now
148
+ };
149
+ const storedCard = attachCanonicalAgentId(db, card);
150
+ CapabilityCardV2Schema.parse(storedCard);
151
+ if (existingV2) {
152
+ db.prepare(
153
+ "UPDATE capability_cards SET data = ?, updated_at = ? WHERE id = ?"
154
+ ).run(JSON.stringify(storedCard), now, cardId);
155
+ } else {
156
+ db.prepare(
157
+ "INSERT INTO capability_cards (id, owner, data, created_at, updated_at) VALUES (?, ?, ?, ?, ?)"
158
+ ).run(cardId, storedCard.owner, JSON.stringify(storedCard), now, now);
159
+ }
160
+ return storedCard;
161
+ }
162
+
163
+ // src/openclaw/heartbeat-writer.ts
164
+ import { readFileSync, writeFileSync, existsSync } from "fs";
165
+ var HEARTBEAT_MARKER_START = "<!-- agentbnb:start -->";
166
+ var HEARTBEAT_MARKER_END = "<!-- agentbnb:end -->";
167
+ function generateHeartbeatSection(autonomy, budget) {
168
+ const t1 = autonomy.tier1_max_credits;
169
+ const t2 = autonomy.tier2_max_credits;
170
+ const reserve = budget.reserve_credits;
171
+ return [
172
+ HEARTBEAT_MARKER_START,
173
+ "## AgentBnB Protocol",
174
+ "",
175
+ "You are a participant in the AgentBnB capability sharing network.",
176
+ "",
177
+ "### Sharing Rules",
178
+ "- When your idle_rate exceeds 70%, automatically share idle capabilities",
179
+ "- Accept incoming requests that match your published Capability Card",
180
+ "- Track credit earnings and report to owner weekly",
181
+ "",
182
+ "### Requesting Rules",
183
+ "- When you encounter a task you cannot complete with local skills:",
184
+ " 1. Query AgentBnB network for matching capabilities",
185
+ " 2. If found and credit sufficient, automatically request",
186
+ " 3. Integrate result into your current workflow",
187
+ "- Budget limits:",
188
+ ` - < ${t1} credits: auto-execute, no notification`,
189
+ ` - ${t1}-${t2} credits: execute, notify owner after`,
190
+ ` - > ${t2} credits: ask owner before executing`,
191
+ "",
192
+ "### Credit Management",
193
+ `- Maintain minimum balance of ${reserve} credits (reserve for emergencies)`,
194
+ `- If balance drops below ${reserve}, increase sharing priority`,
195
+ "- If balance exceeds 500, notify owner of surplus",
196
+ HEARTBEAT_MARKER_END
197
+ ].join("\n");
198
+ }
199
+ function injectHeartbeatSection(heartbeatPath, section) {
200
+ if (!existsSync(heartbeatPath)) {
201
+ writeFileSync(heartbeatPath, section + "\n", "utf-8");
202
+ return;
203
+ }
204
+ let content = readFileSync(heartbeatPath, "utf-8");
205
+ const startIdx = content.indexOf(HEARTBEAT_MARKER_START);
206
+ const endIdx = content.indexOf(HEARTBEAT_MARKER_END);
207
+ if (startIdx !== -1 && endIdx !== -1) {
208
+ content = content.slice(0, startIdx) + section + content.slice(endIdx + HEARTBEAT_MARKER_END.length);
209
+ } else {
210
+ content = content + "\n" + section + "\n";
211
+ }
212
+ writeFileSync(heartbeatPath, content, "utf-8");
213
+ }
214
+
215
+ // src/openclaw/skill.ts
216
+ function getOpenClawStatus(config, db, creditDb) {
217
+ const autonomy = config.autonomy ?? DEFAULT_AUTONOMY_CONFIG;
218
+ const budget = config.budget ?? DEFAULT_BUDGET_CONFIG;
219
+ const balance = getBalance(creditDb, config.owner);
220
+ const allCards = listCards(db, config.owner);
221
+ const skills = [];
222
+ for (const card of allCards) {
223
+ const anyCard = card;
224
+ if (anyCard.spec_version !== "2.0" || !Array.isArray(anyCard.skills)) continue;
225
+ for (const skill of anyCard.skills) {
226
+ const internal = skill["_internal"] ?? {};
227
+ const idleRate = typeof internal["idle_rate"] === "number" ? internal["idle_rate"] : null;
228
+ const availability = skill["availability"];
229
+ const online = availability?.online ?? false;
230
+ skills.push({
231
+ id: String(skill["id"] ?? ""),
232
+ name: String(skill["name"] ?? ""),
233
+ idle_rate: idleRate,
234
+ online
235
+ });
236
+ }
237
+ }
238
+ return {
239
+ installed: true,
240
+ owner: config.owner,
241
+ gateway_url: config.gateway_url,
242
+ tier: autonomy,
243
+ balance,
244
+ reserve: budget.reserve_credits,
245
+ skills
246
+ };
247
+ }
248
+
249
+ // src/cli/init-action.ts
250
+ import { randomBytes } from "crypto";
251
+ import { join as join2 } from "path";
252
+ import { networkInterfaces } from "os";
253
+
254
+ // src/onboarding/index.ts
255
+ import { randomUUID as randomUUID2 } from "crypto";
256
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
257
+ import { join } from "path";
258
+
259
+ // src/onboarding/capability-templates.ts
260
+ var API_PATTERNS = [
261
+ {
262
+ pattern: /openai|gpt-4|gpt-3|chatgpt|dall-e/i,
263
+ capability: { key: "openai", name: "OpenAI Text Generation", category: "Text Gen", credits_per_call: 3, tags: ["llm", "text", "generation"] }
264
+ },
265
+ {
266
+ pattern: /elevenlabs|eleven.?labs/i,
267
+ capability: { key: "elevenlabs", name: "ElevenLabs TTS", category: "TTS", credits_per_call: 5, tags: ["tts", "audio", "voice"] }
268
+ },
269
+ {
270
+ pattern: /anthropic|claude/i,
271
+ capability: { key: "anthropic", name: "Anthropic Claude", category: "Text Gen", credits_per_call: 3, tags: ["llm", "text", "generation"] }
272
+ },
273
+ {
274
+ pattern: /recraft/i,
275
+ capability: { key: "recraft", name: "Recraft V4 Image Gen", category: "Image Gen", credits_per_call: 8, tags: ["image", "generation", "design"] }
276
+ },
277
+ {
278
+ pattern: /kling/i,
279
+ capability: { key: "kling", name: "Kling AI Video Gen", category: "Video Gen", credits_per_call: 10, tags: ["video", "generation"] }
280
+ },
281
+ {
282
+ pattern: /stable.?diffusion|sdxl|comfyui/i,
283
+ capability: { key: "stable-diffusion", name: "Stable Diffusion Image Gen", category: "Image Gen", credits_per_call: 6, tags: ["image", "generation", "diffusion"] }
284
+ },
285
+ {
286
+ pattern: /whisper|speech.?to.?text|stt/i,
287
+ capability: { key: "whisper", name: "Whisper Speech-to-Text", category: "STT", credits_per_call: 3, tags: ["stt", "audio", "transcription"] }
288
+ },
289
+ {
290
+ pattern: /puppeteer|playwright|selenium/i,
291
+ capability: { key: "puppeteer", name: "Web Scraping & Automation", category: "Web Scraping", credits_per_call: 2, tags: ["scraping", "automation", "browser"] }
292
+ },
293
+ {
294
+ pattern: /ffmpeg/i,
295
+ capability: { key: "ffmpeg", name: "FFmpeg Media Processing", category: "Media Processing", credits_per_call: 3, tags: ["media", "audio", "video", "processing"] }
296
+ },
297
+ {
298
+ pattern: /tesseract|ocr/i,
299
+ capability: { key: "tesseract", name: "OCR Text Extraction", category: "OCR", credits_per_call: 4, tags: ["ocr", "text", "extraction"] }
300
+ }
301
+ ];
302
+ var INTERACTIVE_TEMPLATES = [
303
+ { key: "openai", name: "Text Generation (GPT-4o / Claude / Gemini)", category: "Text Gen", credits_per_call: 3, tags: ["llm", "text", "generation"] },
304
+ { key: "image-gen", name: "Image Generation (DALL-E / Recraft / Stable Diffusion)", category: "Image Gen", credits_per_call: 8, tags: ["image", "generation"] },
305
+ { key: "tts", name: "TTS / Voice (ElevenLabs / Google TTS)", category: "TTS", credits_per_call: 5, tags: ["tts", "audio", "voice"] },
306
+ { key: "video-gen", name: "Video Generation (Kling / Runway)", category: "Video Gen", credits_per_call: 10, tags: ["video", "generation"] },
307
+ { key: "code-review", name: "Code Review / Analysis", category: "Code", credits_per_call: 3, tags: ["code", "review", "analysis"] },
308
+ { key: "scraping", name: "Web Scraping / Data Extraction", category: "Web Scraping", credits_per_call: 2, tags: ["scraping", "data", "extraction"] },
309
+ { key: "translation", name: "Translation", category: "Translation", credits_per_call: 3, tags: ["translation", "language", "text"] },
310
+ { key: "custom", name: "Custom (describe it)", category: "Custom", credits_per_call: 5, tags: ["custom"] }
311
+ ];
312
+
313
+ // src/onboarding/detect-from-docs.ts
314
+ function detectFromDocs(content) {
315
+ if (!content || content.trim().length === 0) {
316
+ return [];
317
+ }
318
+ const seen = /* @__PURE__ */ new Set();
319
+ const results = [];
320
+ for (const entry of API_PATTERNS) {
321
+ if (entry.pattern.test(content) && !seen.has(entry.capability.key)) {
322
+ seen.add(entry.capability.key);
323
+ results.push({ ...entry.capability });
324
+ }
325
+ }
326
+ return results;
327
+ }
328
+
329
+ // src/onboarding/interactive.ts
330
+ import { createInterface } from "readline";
331
+ async function interactiveTemplateMenu() {
332
+ console.log("\nNo capabilities auto-detected.\n");
333
+ console.log("What can your agent do? Pick from templates:\n");
334
+ for (let i = 0; i < INTERACTIVE_TEMPLATES.length; i++) {
335
+ console.log(` ${i + 1}. ${INTERACTIVE_TEMPLATES[i].name}`);
336
+ }
337
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
338
+ const answer = await new Promise((resolve) => {
339
+ rl.question("\nSelect [1-8, comma-separated]: ", (ans) => {
340
+ rl.close();
341
+ resolve(ans);
342
+ });
343
+ });
344
+ return parseSelection(answer);
345
+ }
346
+ function parseSelection(input) {
347
+ if (!input || input.trim().length === 0) {
348
+ return [];
349
+ }
350
+ const selected = [];
351
+ const seen = /* @__PURE__ */ new Set();
352
+ const parts = input.split(",").map((s) => s.trim());
353
+ for (const part of parts) {
354
+ const num = parseInt(part, 10);
355
+ if (isNaN(num) || num < 1 || num > INTERACTIVE_TEMPLATES.length) {
356
+ continue;
357
+ }
358
+ const template = INTERACTIVE_TEMPLATES[num - 1];
359
+ if (!seen.has(template.key)) {
360
+ seen.add(template.key);
361
+ selected.push({ ...template });
362
+ }
363
+ }
364
+ return selected;
365
+ }
366
+
367
+ // src/onboarding/index.ts
368
+ var DOC_FILES = ["SOUL.md", "CLAUDE.md", "AGENTS.md", "README.md"];
369
+ function detectCapabilities(opts = {}) {
370
+ const cwd = opts.cwd ?? process.cwd();
371
+ if (opts.fromFile) {
372
+ const filePath = opts.fromFile.startsWith("/") ? opts.fromFile : join(cwd, opts.fromFile);
373
+ if (existsSync2(filePath)) {
374
+ const content = readFileSync2(filePath, "utf-8");
375
+ const capabilities = detectFromDocs(content);
376
+ if (capabilities.length > 0) {
377
+ return { source: "docs", capabilities, sourceFile: filePath };
378
+ }
379
+ }
380
+ return { source: "none", capabilities: [] };
381
+ }
382
+ for (const fileName of DOC_FILES) {
383
+ const filePath = join(cwd, fileName);
384
+ if (!existsSync2(filePath)) continue;
385
+ const content = readFileSync2(filePath, "utf-8");
386
+ if (fileName === "SOUL.md") {
387
+ return { source: "soul", capabilities: [], soulContent: content, sourceFile: filePath };
388
+ }
389
+ const capabilities = detectFromDocs(content);
390
+ if (capabilities.length > 0) {
391
+ return { source: "docs", capabilities, sourceFile: filePath };
392
+ }
393
+ }
394
+ const envKeys = detectApiKeys(KNOWN_API_KEYS);
395
+ if (envKeys.length > 0) {
396
+ return { source: "env", capabilities: [], envKeys };
397
+ }
398
+ return { source: "none", capabilities: [] };
399
+ }
400
+ function capabilitiesToV2Card(capabilities, owner, agentName) {
401
+ const now = (/* @__PURE__ */ new Date()).toISOString();
402
+ const skills = capabilities.map((cap) => ({
403
+ id: cap.key,
404
+ name: cap.name,
405
+ description: `${cap.name} capability \u2014 ${cap.category}`,
406
+ level: 1,
407
+ category: cap.category.toLowerCase().replace(/\s+/g, "_"),
408
+ inputs: [{ name: "input", type: "text", required: true }],
409
+ outputs: [{ name: "output", type: "text", required: true }],
410
+ pricing: { credits_per_call: cap.credits_per_call },
411
+ availability: { online: true },
412
+ metadata: {
413
+ tags: cap.tags
414
+ }
415
+ }));
416
+ const card = {
417
+ spec_version: "2.0",
418
+ id: randomUUID2(),
419
+ owner,
420
+ agent_name: agentName ?? owner,
421
+ skills,
422
+ availability: { online: true },
423
+ created_at: now,
424
+ updated_at: now
425
+ };
426
+ return CapabilityCardV2Schema.parse(card);
427
+ }
428
+
429
+ // src/cli/init-action.ts
430
+ import { createInterface as createInterface2 } from "readline";
431
+ async function confirm(question) {
432
+ const rl = createInterface2({ input: process.stdin, output: process.stdout });
433
+ try {
434
+ return await new Promise((resolve) => {
435
+ rl.question(question, (answer) => {
436
+ resolve(answer.trim().toLowerCase() === "y");
437
+ });
438
+ });
439
+ } finally {
440
+ rl.close();
441
+ }
442
+ }
443
+ function getLanIp() {
444
+ const nets = networkInterfaces();
445
+ for (const ifaces of Object.values(nets)) {
446
+ for (const iface of ifaces ?? []) {
447
+ if (iface.family === "IPv4" && !iface.internal) return iface.address;
448
+ }
449
+ }
450
+ return "localhost";
451
+ }
452
+ function loadIdentityAuth(owner) {
453
+ const configDir = getConfigDir();
454
+ const { identity, keys } = loadOrRepairIdentity(configDir, owner);
455
+ return {
456
+ agentId: identity.agent_id,
457
+ publicKey: identity.public_key,
458
+ privateKey: keys.privateKey
459
+ };
460
+ }
461
+ function syncAgentRecord(db, identity, owner, displayName) {
462
+ const existingAgent = lookupAgent(db, identity.agent_id) ?? lookupAgentByOwner(db, owner);
463
+ if (!existingAgent) {
464
+ createAgentRecord(db, {
465
+ agent_id: identity.agent_id,
466
+ display_name: displayName,
467
+ public_key: identity.public_key,
468
+ legacy_owner: owner
469
+ });
470
+ return;
471
+ }
472
+ const updates = {};
473
+ if (existingAgent.display_name !== displayName) {
474
+ updates.display_name = displayName;
475
+ }
476
+ if (existingAgent.legacy_owner !== owner) {
477
+ updates.legacy_owner = owner;
478
+ }
479
+ if (Object.keys(updates).length > 0) {
480
+ updateAgentRecord(db, existingAgent.agent_id, updates);
481
+ }
482
+ }
483
+ async function performInit(opts) {
484
+ const configDir = getConfigDir();
485
+ const dbPath = join2(configDir, "registry.db");
486
+ const creditDbPath = join2(configDir, "credit.db");
487
+ const port = parseInt(opts.port, 10);
488
+ const ip = opts.host ?? getLanIp();
489
+ const yesMode = opts.yes ?? opts.nonInteractive ?? false;
490
+ const existingConfig = loadConfig();
491
+ const owner = opts.agentId ?? opts.owner ?? existingConfig?.owner ?? `agent-${randomBytes(4).toString("hex")}`;
492
+ const config = {
493
+ ...existingConfig,
494
+ owner,
495
+ gateway_url: `http://${ip}:${port}`,
496
+ gateway_port: port,
497
+ db_path: dbPath,
498
+ credit_db_path: creditDbPath,
499
+ token: existingConfig?.token ?? randomBytes(32).toString("hex"),
500
+ api_key: existingConfig?.api_key ?? randomBytes(32).toString("hex"),
501
+ ...existingConfig?.registry ? { registry: existingConfig.registry } : yesMode ? { registry: "https://agentbnb.fly.dev" } : {}
502
+ };
503
+ saveConfig(config);
504
+ const identityMaterial = loadOrRepairIdentity(configDir, owner);
505
+ const identity = identityMaterial.identity;
506
+ const keypairStatus = identityMaterial.status === "generated" ? "generated" : "existing";
507
+ const creditDb = openCreditDb(creditDbPath);
508
+ const registryDb = openDatabase(dbPath);
509
+ syncAgentRecord(creditDb, identity, owner, config.display_name ?? owner);
510
+ syncAgentRecord(registryDb, identity, owner, config.display_name ?? owner);
511
+ if (existingConfig?.owner && existingConfig.owner !== owner) {
512
+ migrateOwner(creditDb, existingConfig.owner, owner);
513
+ const rows = registryDb.prepare("SELECT id, owner, data FROM capability_cards WHERE owner = ?").all(existingConfig.owner);
514
+ for (const row of rows) {
515
+ try {
516
+ const card = JSON.parse(row.data);
517
+ const updatedCard = attachCanonicalAgentId(registryDb, {
518
+ ...card,
519
+ owner
520
+ });
521
+ registryDb.prepare("UPDATE capability_cards SET owner = ?, data = ? WHERE id = ?").run(owner, JSON.stringify(updatedCard), row.id);
522
+ } catch {
523
+ }
524
+ }
525
+ if (!opts.json && rows.length > 0) {
526
+ console.log(`Migrated ${rows.length} card(s) \u2192 ${owner}`);
527
+ }
528
+ const allOwners = creditDb.prepare("SELECT owner FROM credit_balances WHERE owner != ?").all(owner);
529
+ for (const { owner: oldOwner } of allOwners) {
530
+ migrateOwner(creditDb, oldOwner, owner);
531
+ }
532
+ if (existingConfig.registry) {
533
+ try {
534
+ const renameAuth = loadIdentityAuth(owner);
535
+ const renameLedger = createLedger({
536
+ registryUrl: existingConfig.registry,
537
+ ownerPublicKey: renameAuth.publicKey,
538
+ privateKey: renameAuth.privateKey
539
+ });
540
+ await renameLedger.rename(existingConfig.owner, owner);
541
+ if (!opts.json) {
542
+ console.log(`Migrated Registry credits: ${existingConfig.owner} \u2192 ${owner}`);
543
+ }
544
+ } catch (err) {
545
+ if (!opts.json) {
546
+ console.warn(`Warning: could not migrate Registry credits: ${err.message}`);
547
+ }
548
+ }
549
+ }
550
+ if (!opts.json) {
551
+ console.log(`Migrated local credits: ${existingConfig.owner} \u2192 ${owner}`);
552
+ }
553
+ }
554
+ if (!config.agent_id || config.agent_id !== identity.agent_id) {
555
+ config.agent_id = identity.agent_id;
556
+ saveConfig(config);
557
+ }
558
+ bootstrapAgent(creditDb, identity.agent_id, 100);
559
+ registryDb.close();
560
+ creditDb.close();
561
+ let registryBalance;
562
+ if (config.registry) {
563
+ try {
564
+ const identityAuth = loadIdentityAuth(owner);
565
+ const ledger = createLedger({
566
+ registryUrl: config.registry,
567
+ ownerPublicKey: identityAuth.publicKey,
568
+ privateKey: identityAuth.privateKey
569
+ });
570
+ await ledger.grant(owner, 50);
571
+ registryBalance = await ledger.getBalance(owner);
572
+ } catch (err) {
573
+ console.warn(`Warning: could not connect to Registry for credit grant: ${err.message}`);
574
+ }
575
+ }
576
+ const skipDetect = opts.detect === false;
577
+ const publishedCards = [];
578
+ let detectedSource = "none";
579
+ if (!skipDetect) {
580
+ if (!opts.json) {
581
+ console.log("\nDetecting capabilities...");
582
+ }
583
+ const result = detectCapabilities({ fromFile: opts.from, cwd: process.cwd() });
584
+ detectedSource = result.source;
585
+ if (result.source === "soul") {
586
+ if (!opts.json) {
587
+ console.log(` Found SOUL.md \u2014 extracting capabilities...`);
588
+ }
589
+ const db = openDatabase(dbPath);
590
+ try {
591
+ const card = publishFromSoulV2(db, result.soulContent, owner);
592
+ publishedCards.push({ id: card.id, name: card.agent_name });
593
+ if (!opts.json) {
594
+ console.log(` Published v2.0 card: ${card.agent_name} (${card.skills.length} skills)`);
595
+ }
596
+ } finally {
597
+ db.close();
598
+ }
599
+ } else if (result.source === "docs") {
600
+ if (!opts.json) {
601
+ console.log(` Found ${result.sourceFile ?? "docs"} \u2014 detected ${result.capabilities.length} capabilities:`);
602
+ for (const cap of result.capabilities) {
603
+ console.log(` ${cap.name} (${cap.category}, cr ${cap.credits_per_call}/call)`);
604
+ }
605
+ }
606
+ const card = capabilitiesToV2Card(result.capabilities, owner);
607
+ if (yesMode) {
608
+ const db = openDatabase(dbPath);
609
+ try {
610
+ const storedCard = attachCanonicalAgentId(db, card);
611
+ db.prepare(
612
+ `INSERT OR REPLACE INTO capability_cards (id, owner, data, created_at, updated_at)
613
+ VALUES (?, ?, ?, ?, ?)`
614
+ ).run(
615
+ storedCard.id,
616
+ storedCard.owner,
617
+ JSON.stringify(storedCard),
618
+ storedCard.created_at,
619
+ storedCard.updated_at
620
+ );
621
+ publishedCards.push({ id: storedCard.id, name: storedCard.agent_name });
622
+ if (!opts.json) {
623
+ console.log(` Published v2.0 card: ${storedCard.agent_name} (${storedCard.skills.length} skills)`);
624
+ }
625
+ } finally {
626
+ db.close();
627
+ }
628
+ } else if (process.stdout.isTTY) {
629
+ const yes = await confirm(`
630
+ Publish these ${card.skills.length} capabilities? [y/N] `);
631
+ if (yes) {
632
+ const db = openDatabase(dbPath);
633
+ try {
634
+ const storedCard = attachCanonicalAgentId(db, card);
635
+ db.prepare(
636
+ `INSERT OR REPLACE INTO capability_cards (id, owner, data, created_at, updated_at)
637
+ VALUES (?, ?, ?, ?, ?)`
638
+ ).run(
639
+ storedCard.id,
640
+ storedCard.owner,
641
+ JSON.stringify(storedCard),
642
+ storedCard.created_at,
643
+ storedCard.updated_at
644
+ );
645
+ publishedCards.push({ id: storedCard.id, name: storedCard.agent_name });
646
+ console.log(` Published v2.0 card: ${storedCard.agent_name} (${storedCard.skills.length} skills)`);
647
+ } finally {
648
+ db.close();
649
+ }
650
+ } else {
651
+ console.log(" Skipped publishing.");
652
+ }
653
+ } else {
654
+ if (!opts.json) {
655
+ console.log(" Non-interactive environment. Re-run with --yes to auto-publish.");
656
+ }
657
+ }
658
+ } else if (result.source === "env") {
659
+ const detectedKeys = result.envKeys ?? [];
660
+ if (!opts.json) {
661
+ console.log(` Detected ${detectedKeys.length} API key${detectedKeys.length > 1 ? "s" : ""}: ${detectedKeys.join(", ")}`);
662
+ }
663
+ const detectedPorts = await detectOpenPorts([7700, 7701, 8080, 3e3, 8e3, 11434]);
664
+ if (detectedPorts.length > 0 && !opts.json) {
665
+ console.log(` Found services on ports: ${detectedPorts.join(", ")}`);
666
+ }
667
+ const drafts = detectedKeys.map((key) => buildDraftCard(key, owner)).filter((card) => card !== null);
668
+ if (yesMode) {
669
+ const db = openDatabase(dbPath);
670
+ try {
671
+ for (const card of drafts) {
672
+ insertCard(db, card);
673
+ publishedCards.push({ id: card.id, name: card.name });
674
+ if (!opts.json) {
675
+ console.log(` Published: ${card.name} (${card.id})`);
676
+ }
677
+ }
678
+ } finally {
679
+ db.close();
680
+ }
681
+ } else if (process.stdout.isTTY) {
682
+ const db = openDatabase(dbPath);
683
+ try {
684
+ for (const card of drafts) {
685
+ const yes = await confirm(`Publish "${card.name}"? [y/N] `);
686
+ if (yes) {
687
+ insertCard(db, card);
688
+ publishedCards.push({ id: card.id, name: card.name });
689
+ console.log(` Published: ${card.name} (${card.id})`);
690
+ } else {
691
+ console.log(` Skipped: ${card.name}`);
692
+ }
693
+ }
694
+ } finally {
695
+ db.close();
696
+ }
697
+ } else {
698
+ if (!opts.json) {
699
+ console.log(" Non-interactive environment. Re-run with --yes to auto-publish.");
700
+ }
701
+ }
702
+ } else {
703
+ if (process.stdout.isTTY && !yesMode && !opts.json) {
704
+ const selected = await interactiveTemplateMenu();
705
+ if (selected.length > 0) {
706
+ const card = capabilitiesToV2Card(selected, owner);
707
+ const db = openDatabase(dbPath);
708
+ try {
709
+ const storedCard = attachCanonicalAgentId(db, card);
710
+ db.prepare(
711
+ `INSERT OR REPLACE INTO capability_cards (id, owner, data, created_at, updated_at)
712
+ VALUES (?, ?, ?, ?, ?)`
713
+ ).run(
714
+ storedCard.id,
715
+ storedCard.owner,
716
+ JSON.stringify(storedCard),
717
+ storedCard.created_at,
718
+ storedCard.updated_at
719
+ );
720
+ publishedCards.push({ id: storedCard.id, name: storedCard.agent_name });
721
+ console.log(`
722
+ Published v2.0 card: ${storedCard.agent_name} (${storedCard.skills.length} skills)`);
723
+ } finally {
724
+ db.close();
725
+ }
726
+ }
727
+ } else if (!opts.json) {
728
+ console.log(" No capabilities detected. You can manually publish with `agentbnb publish`.");
729
+ }
730
+ }
731
+ }
732
+ return {
733
+ config,
734
+ owner,
735
+ configDir,
736
+ publishedCards,
737
+ registryBalance,
738
+ identity: { agent_id: identity.agent_id },
739
+ keypairStatus,
740
+ detectedSource
741
+ };
742
+ }
743
+
744
+ export {
745
+ publishFromSoulV2,
746
+ generateHeartbeatSection,
747
+ injectHeartbeatSection,
748
+ getOpenClawStatus,
749
+ performInit
750
+ };