agentbnb 3.1.0 → 3.1.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.
package/dist/cli/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  saveKeyPair,
8
8
  settleRequesterEscrow,
9
9
  signEscrowReceipt
10
- } from "../chunk-4Q7D24DP.js";
10
+ } from "../chunk-SVEZBIGE.js";
11
11
  import {
12
12
  RelayMessageSchema
13
13
  } from "../chunk-3Y36WQDV.js";
@@ -24,7 +24,7 @@ import {
24
24
  requestCapability,
25
25
  resolvePendingRequest,
26
26
  searchCards
27
- } from "../chunk-QVIGMCHA.js";
27
+ } from "../chunk-KTHJ5F3X.js";
28
28
  import {
29
29
  findPeer,
30
30
  getConfigDir,
@@ -46,7 +46,7 @@ import {
46
46
  updateCard,
47
47
  updateSkillAvailability,
48
48
  updateSkillIdleRate
49
- } from "../chunk-2LLXUKMY.js";
49
+ } from "../chunk-UOGDK2S2.js";
50
50
  import {
51
51
  bootstrapAgent,
52
52
  getBalance,
@@ -54,35 +54,115 @@ import {
54
54
  holdEscrow,
55
55
  openCreditDb,
56
56
  releaseEscrow
57
- } from "../chunk-ZJCIBK6O.js";
57
+ } from "../chunk-QVV2P3FN.js";
58
58
  import {
59
59
  AgentBnBError,
60
- CapabilityCardSchema,
60
+ AnyCardSchema,
61
61
  CapabilityCardV2Schema
62
- } from "../chunk-TQMI73LL.js";
62
+ } from "../chunk-XA63SD4T.js";
63
63
 
64
64
  // src/cli/index.ts
65
65
  import { Command } from "commander";
66
- import { readFileSync as readFileSync3 } from "fs";
66
+ import { readFileSync as readFileSync5 } from "fs";
67
67
  import { createRequire } from "module";
68
68
  import { randomBytes } from "crypto";
69
- import { join as join2 } from "path";
69
+ import { join as join4 } from "path";
70
70
  import { networkInterfaces, homedir } from "os";
71
- import { createInterface } from "readline";
71
+ import { createInterface as createInterface2 } from "readline";
72
72
 
73
- // src/credit/escrow-receipt.ts
73
+ // src/identity/identity.ts
74
74
  import { z } from "zod";
75
- import { randomUUID } from "crypto";
76
- var EscrowReceiptSchema = z.object({
77
- requester_owner: z.string().min(1),
78
- requester_public_key: z.string().min(1),
79
- amount: z.number().positive(),
80
- card_id: z.string().min(1),
81
- skill_id: z.string().optional(),
82
- timestamp: z.string(),
83
- nonce: z.string().uuid(),
75
+ import { createHash } from "crypto";
76
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
77
+ import { join } from "path";
78
+ var AgentIdentitySchema = z.object({
79
+ /** Deterministic ID derived from public key: sha256(hex).slice(0, 16). */
80
+ agent_id: z.string().min(1),
81
+ /** Human-readable owner name (from config or init). */
82
+ owner: z.string().min(1),
83
+ /** Hex-encoded Ed25519 public key. */
84
+ public_key: z.string().min(1),
85
+ /** ISO 8601 timestamp of identity creation. */
86
+ created_at: z.string().datetime(),
87
+ /** Optional guarantor info if linked to a human. */
88
+ guarantor: z.object({
89
+ github_login: z.string().min(1),
90
+ verified_at: z.string().datetime()
91
+ }).optional()
92
+ });
93
+ var AgentCertificateSchema = z.object({
94
+ identity: AgentIdentitySchema,
95
+ /** ISO 8601 timestamp of certificate issuance. */
96
+ issued_at: z.string().datetime(),
97
+ /** ISO 8601 timestamp of certificate expiry. */
98
+ expires_at: z.string().datetime(),
99
+ /** Hex-encoded public key of the issuer (same as identity for self-signed). */
100
+ issuer_public_key: z.string().min(1),
101
+ /** Base64url Ed25519 signature over { identity, issued_at, expires_at, issuer_public_key }. */
84
102
  signature: z.string().min(1)
85
103
  });
104
+ var IDENTITY_FILENAME = "identity.json";
105
+ function deriveAgentId(publicKeyHex) {
106
+ return createHash("sha256").update(publicKeyHex, "hex").digest("hex").slice(0, 16);
107
+ }
108
+ function createIdentity(configDir, owner) {
109
+ if (!existsSync(configDir)) {
110
+ mkdirSync(configDir, { recursive: true });
111
+ }
112
+ let keys;
113
+ try {
114
+ keys = loadKeyPair(configDir);
115
+ } catch {
116
+ keys = generateKeyPair();
117
+ saveKeyPair(configDir, keys);
118
+ }
119
+ const publicKeyHex = keys.publicKey.toString("hex");
120
+ const agentId = deriveAgentId(publicKeyHex);
121
+ const identity = {
122
+ agent_id: agentId,
123
+ owner,
124
+ public_key: publicKeyHex,
125
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
126
+ };
127
+ saveIdentity(configDir, identity);
128
+ return identity;
129
+ }
130
+ function loadIdentity(configDir) {
131
+ const filePath = join(configDir, IDENTITY_FILENAME);
132
+ if (!existsSync(filePath)) return null;
133
+ try {
134
+ const raw = readFileSync(filePath, "utf-8");
135
+ return AgentIdentitySchema.parse(JSON.parse(raw));
136
+ } catch {
137
+ return null;
138
+ }
139
+ }
140
+ function saveIdentity(configDir, identity) {
141
+ if (!existsSync(configDir)) {
142
+ mkdirSync(configDir, { recursive: true });
143
+ }
144
+ const filePath = join(configDir, IDENTITY_FILENAME);
145
+ writeFileSync(filePath, JSON.stringify(identity, null, 2), "utf-8");
146
+ }
147
+ function ensureIdentity(configDir, owner) {
148
+ const existing = loadIdentity(configDir);
149
+ if (existing) return existing;
150
+ return createIdentity(configDir, owner);
151
+ }
152
+
153
+ // src/credit/escrow-receipt.ts
154
+ import { z as z2 } from "zod";
155
+ import { randomUUID } from "crypto";
156
+ var EscrowReceiptSchema = z2.object({
157
+ requester_owner: z2.string().min(1),
158
+ requester_public_key: z2.string().min(1),
159
+ amount: z2.number().positive(),
160
+ card_id: z2.string().min(1),
161
+ skill_id: z2.string().optional(),
162
+ timestamp: z2.string(),
163
+ nonce: z2.string().uuid(),
164
+ signature: z2.string().min(1)
165
+ });
86
166
  function createSignedEscrowReceipt(db, privateKey, publicKey, opts) {
87
167
  const escrowId = holdEscrow(db, opts.owner, opts.amount, opts.cardId);
88
168
  const receiptData = {
@@ -447,8 +527,183 @@ function buildDraftCard(apiKey, owner) {
447
527
  };
448
528
  }
449
529
 
530
+ // src/onboarding/index.ts
531
+ import { randomUUID as randomUUID3 } from "crypto";
532
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
533
+ import { join as join2 } from "path";
534
+
535
+ // src/onboarding/capability-templates.ts
536
+ var API_PATTERNS = [
537
+ {
538
+ pattern: /openai|gpt-4|gpt-3|chatgpt|dall-e/i,
539
+ capability: { key: "openai", name: "OpenAI Text Generation", category: "Text Gen", credits_per_call: 3, tags: ["llm", "text", "generation"] }
540
+ },
541
+ {
542
+ pattern: /elevenlabs|eleven.?labs/i,
543
+ capability: { key: "elevenlabs", name: "ElevenLabs TTS", category: "TTS", credits_per_call: 5, tags: ["tts", "audio", "voice"] }
544
+ },
545
+ {
546
+ pattern: /anthropic|claude/i,
547
+ capability: { key: "anthropic", name: "Anthropic Claude", category: "Text Gen", credits_per_call: 3, tags: ["llm", "text", "generation"] }
548
+ },
549
+ {
550
+ pattern: /recraft/i,
551
+ capability: { key: "recraft", name: "Recraft V4 Image Gen", category: "Image Gen", credits_per_call: 8, tags: ["image", "generation", "design"] }
552
+ },
553
+ {
554
+ pattern: /kling/i,
555
+ capability: { key: "kling", name: "Kling AI Video Gen", category: "Video Gen", credits_per_call: 10, tags: ["video", "generation"] }
556
+ },
557
+ {
558
+ pattern: /stable.?diffusion|sdxl|comfyui/i,
559
+ capability: { key: "stable-diffusion", name: "Stable Diffusion Image Gen", category: "Image Gen", credits_per_call: 6, tags: ["image", "generation", "diffusion"] }
560
+ },
561
+ {
562
+ pattern: /whisper|speech.?to.?text|stt/i,
563
+ capability: { key: "whisper", name: "Whisper Speech-to-Text", category: "STT", credits_per_call: 3, tags: ["stt", "audio", "transcription"] }
564
+ },
565
+ {
566
+ pattern: /puppeteer|playwright|selenium/i,
567
+ capability: { key: "puppeteer", name: "Web Scraping & Automation", category: "Web Scraping", credits_per_call: 2, tags: ["scraping", "automation", "browser"] }
568
+ },
569
+ {
570
+ pattern: /ffmpeg/i,
571
+ capability: { key: "ffmpeg", name: "FFmpeg Media Processing", category: "Media Processing", credits_per_call: 3, tags: ["media", "audio", "video", "processing"] }
572
+ },
573
+ {
574
+ pattern: /tesseract|ocr/i,
575
+ capability: { key: "tesseract", name: "OCR Text Extraction", category: "OCR", credits_per_call: 4, tags: ["ocr", "text", "extraction"] }
576
+ }
577
+ ];
578
+ var INTERACTIVE_TEMPLATES = [
579
+ { key: "openai", name: "Text Generation (GPT-4o / Claude / Gemini)", category: "Text Gen", credits_per_call: 3, tags: ["llm", "text", "generation"] },
580
+ { key: "image-gen", name: "Image Generation (DALL-E / Recraft / Stable Diffusion)", category: "Image Gen", credits_per_call: 8, tags: ["image", "generation"] },
581
+ { key: "tts", name: "TTS / Voice (ElevenLabs / Google TTS)", category: "TTS", credits_per_call: 5, tags: ["tts", "audio", "voice"] },
582
+ { key: "video-gen", name: "Video Generation (Kling / Runway)", category: "Video Gen", credits_per_call: 10, tags: ["video", "generation"] },
583
+ { key: "code-review", name: "Code Review / Analysis", category: "Code", credits_per_call: 3, tags: ["code", "review", "analysis"] },
584
+ { key: "scraping", name: "Web Scraping / Data Extraction", category: "Web Scraping", credits_per_call: 2, tags: ["scraping", "data", "extraction"] },
585
+ { key: "translation", name: "Translation", category: "Translation", credits_per_call: 3, tags: ["translation", "language", "text"] },
586
+ { key: "custom", name: "Custom (describe it)", category: "Custom", credits_per_call: 5, tags: ["custom"] }
587
+ ];
588
+
589
+ // src/onboarding/detect-from-docs.ts
590
+ function detectFromDocs(content) {
591
+ if (!content || content.trim().length === 0) {
592
+ return [];
593
+ }
594
+ const seen = /* @__PURE__ */ new Set();
595
+ const results = [];
596
+ for (const entry of API_PATTERNS) {
597
+ if (entry.pattern.test(content) && !seen.has(entry.capability.key)) {
598
+ seen.add(entry.capability.key);
599
+ results.push({ ...entry.capability });
600
+ }
601
+ }
602
+ return results;
603
+ }
604
+
605
+ // src/onboarding/interactive.ts
606
+ import { createInterface } from "readline";
607
+ async function interactiveTemplateMenu() {
608
+ console.log("\nNo capabilities auto-detected.\n");
609
+ console.log("What can your agent do? Pick from templates:\n");
610
+ for (let i = 0; i < INTERACTIVE_TEMPLATES.length; i++) {
611
+ console.log(` ${i + 1}. ${INTERACTIVE_TEMPLATES[i].name}`);
612
+ }
613
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
614
+ const answer = await new Promise((resolve) => {
615
+ rl.question("\nSelect [1-8, comma-separated]: ", (ans) => {
616
+ rl.close();
617
+ resolve(ans);
618
+ });
619
+ });
620
+ return parseSelection(answer);
621
+ }
622
+ function parseSelection(input) {
623
+ if (!input || input.trim().length === 0) {
624
+ return [];
625
+ }
626
+ const selected = [];
627
+ const seen = /* @__PURE__ */ new Set();
628
+ const parts = input.split(",").map((s) => s.trim());
629
+ for (const part of parts) {
630
+ const num = parseInt(part, 10);
631
+ if (isNaN(num) || num < 1 || num > INTERACTIVE_TEMPLATES.length) {
632
+ continue;
633
+ }
634
+ const template = INTERACTIVE_TEMPLATES[num - 1];
635
+ if (!seen.has(template.key)) {
636
+ seen.add(template.key);
637
+ selected.push({ ...template });
638
+ }
639
+ }
640
+ return selected;
641
+ }
642
+
643
+ // src/onboarding/index.ts
644
+ var DOC_FILES = ["SOUL.md", "CLAUDE.md", "AGENTS.md", "README.md"];
645
+ function detectCapabilities(opts = {}) {
646
+ const cwd = opts.cwd ?? process.cwd();
647
+ if (opts.fromFile) {
648
+ const filePath = opts.fromFile.startsWith("/") ? opts.fromFile : join2(cwd, opts.fromFile);
649
+ if (existsSync2(filePath)) {
650
+ const content = readFileSync2(filePath, "utf-8");
651
+ const capabilities = detectFromDocs(content);
652
+ if (capabilities.length > 0) {
653
+ return { source: "docs", capabilities, sourceFile: filePath };
654
+ }
655
+ }
656
+ return { source: "none", capabilities: [] };
657
+ }
658
+ for (const fileName of DOC_FILES) {
659
+ const filePath = join2(cwd, fileName);
660
+ if (!existsSync2(filePath)) continue;
661
+ const content = readFileSync2(filePath, "utf-8");
662
+ if (fileName === "SOUL.md") {
663
+ return { source: "soul", capabilities: [], soulContent: content, sourceFile: filePath };
664
+ }
665
+ const capabilities = detectFromDocs(content);
666
+ if (capabilities.length > 0) {
667
+ return { source: "docs", capabilities, sourceFile: filePath };
668
+ }
669
+ }
670
+ const envKeys = detectApiKeys(KNOWN_API_KEYS);
671
+ if (envKeys.length > 0) {
672
+ return { source: "env", capabilities: [], envKeys };
673
+ }
674
+ return { source: "none", capabilities: [] };
675
+ }
676
+ function capabilitiesToV2Card(capabilities, owner, agentName) {
677
+ const now = (/* @__PURE__ */ new Date()).toISOString();
678
+ const skills = capabilities.map((cap) => ({
679
+ id: cap.key,
680
+ name: cap.name,
681
+ description: `${cap.name} capability \u2014 ${cap.category}`,
682
+ level: 1,
683
+ category: cap.category.toLowerCase().replace(/\s+/g, "_"),
684
+ inputs: [{ name: "input", type: "text", required: true }],
685
+ outputs: [{ name: "output", type: "text", required: true }],
686
+ pricing: { credits_per_call: cap.credits_per_call },
687
+ availability: { online: true },
688
+ metadata: {
689
+ tags: cap.tags
690
+ }
691
+ }));
692
+ const card = {
693
+ spec_version: "2.0",
694
+ id: randomUUID3(),
695
+ owner,
696
+ agent_name: agentName ?? owner,
697
+ skills,
698
+ availability: { online: true },
699
+ created_at: now,
700
+ updated_at: now
701
+ };
702
+ return CapabilityCardV2Schema.parse(card);
703
+ }
704
+
450
705
  // src/runtime/agent-runtime.ts
451
- import { readFileSync, existsSync } from "fs";
706
+ import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
452
707
 
453
708
  // src/skills/executor.ts
454
709
  var SkillExecutor = class {
@@ -531,98 +786,98 @@ function createSkillExecutor(configs, modes) {
531
786
  }
532
787
 
533
788
  // src/skills/skill-config.ts
534
- import { z as z2 } from "zod";
789
+ import { z as z3 } from "zod";
535
790
  import yaml from "js-yaml";
536
- var PricingSchema = z2.object({
537
- credits_per_call: z2.number().nonnegative(),
538
- credits_per_minute: z2.number().nonnegative().optional(),
539
- free_tier: z2.number().nonnegative().optional()
791
+ var PricingSchema = z3.object({
792
+ credits_per_call: z3.number().nonnegative(),
793
+ credits_per_minute: z3.number().nonnegative().optional(),
794
+ free_tier: z3.number().nonnegative().optional()
540
795
  });
541
- var ApiAuthSchema = z2.discriminatedUnion("type", [
542
- z2.object({
543
- type: z2.literal("bearer"),
544
- token: z2.string()
796
+ var ApiAuthSchema = z3.discriminatedUnion("type", [
797
+ z3.object({
798
+ type: z3.literal("bearer"),
799
+ token: z3.string()
545
800
  }),
546
- z2.object({
547
- type: z2.literal("apikey"),
548
- header: z2.string().default("X-API-Key"),
549
- key: z2.string()
801
+ z3.object({
802
+ type: z3.literal("apikey"),
803
+ header: z3.string().default("X-API-Key"),
804
+ key: z3.string()
550
805
  }),
551
- z2.object({
552
- type: z2.literal("basic"),
553
- username: z2.string(),
554
- password: z2.string()
806
+ z3.object({
807
+ type: z3.literal("basic"),
808
+ username: z3.string(),
809
+ password: z3.string()
555
810
  })
556
811
  ]);
557
- var ApiSkillConfigSchema = z2.object({
558
- id: z2.string().min(1),
559
- type: z2.literal("api"),
560
- name: z2.string().min(1),
561
- endpoint: z2.string().min(1),
562
- method: z2.enum(["GET", "POST", "PUT", "DELETE"]),
812
+ var ApiSkillConfigSchema = z3.object({
813
+ id: z3.string().min(1),
814
+ type: z3.literal("api"),
815
+ name: z3.string().min(1),
816
+ endpoint: z3.string().min(1),
817
+ method: z3.enum(["GET", "POST", "PUT", "DELETE"]),
563
818
  auth: ApiAuthSchema.optional(),
564
- input_mapping: z2.record(z2.string()).default({}),
565
- output_mapping: z2.record(z2.string()).default({}),
819
+ input_mapping: z3.record(z3.string()).default({}),
820
+ output_mapping: z3.record(z3.string()).default({}),
566
821
  pricing: PricingSchema,
567
- timeout_ms: z2.number().positive().default(3e4),
568
- retries: z2.number().nonnegative().int().default(0),
569
- provider: z2.string().optional()
822
+ timeout_ms: z3.number().positive().default(3e4),
823
+ retries: z3.number().nonnegative().int().default(0),
824
+ provider: z3.string().optional()
570
825
  });
571
- var PipelineStepSchema = z2.union([
572
- z2.object({
573
- skill_id: z2.string().min(1),
574
- input_mapping: z2.record(z2.string()).default({})
826
+ var PipelineStepSchema = z3.union([
827
+ z3.object({
828
+ skill_id: z3.string().min(1),
829
+ input_mapping: z3.record(z3.string()).default({})
575
830
  }),
576
- z2.object({
577
- command: z2.string().min(1),
578
- input_mapping: z2.record(z2.string()).default({})
831
+ z3.object({
832
+ command: z3.string().min(1),
833
+ input_mapping: z3.record(z3.string()).default({})
579
834
  })
580
835
  ]);
581
- var PipelineSkillConfigSchema = z2.object({
582
- id: z2.string().min(1),
583
- type: z2.literal("pipeline"),
584
- name: z2.string().min(1),
585
- steps: z2.array(PipelineStepSchema).min(1),
836
+ var PipelineSkillConfigSchema = z3.object({
837
+ id: z3.string().min(1),
838
+ type: z3.literal("pipeline"),
839
+ name: z3.string().min(1),
840
+ steps: z3.array(PipelineStepSchema).min(1),
586
841
  pricing: PricingSchema,
587
- timeout_ms: z2.number().positive().optional()
842
+ timeout_ms: z3.number().positive().optional()
588
843
  });
589
- var OpenClawSkillConfigSchema = z2.object({
590
- id: z2.string().min(1),
591
- type: z2.literal("openclaw"),
592
- name: z2.string().min(1),
593
- agent_name: z2.string().min(1),
594
- channel: z2.enum(["telegram", "webhook", "process"]),
844
+ var OpenClawSkillConfigSchema = z3.object({
845
+ id: z3.string().min(1),
846
+ type: z3.literal("openclaw"),
847
+ name: z3.string().min(1),
848
+ agent_name: z3.string().min(1),
849
+ channel: z3.enum(["telegram", "webhook", "process"]),
595
850
  pricing: PricingSchema,
596
- timeout_ms: z2.number().positive().optional()
851
+ timeout_ms: z3.number().positive().optional()
597
852
  });
598
- var CommandSkillConfigSchema = z2.object({
599
- id: z2.string().min(1),
600
- type: z2.literal("command"),
601
- name: z2.string().min(1),
602
- command: z2.string().min(1),
603
- output_type: z2.enum(["json", "text", "file"]),
604
- allowed_commands: z2.array(z2.string()).optional(),
605
- working_dir: z2.string().optional(),
606
- timeout_ms: z2.number().positive().default(3e4),
853
+ var CommandSkillConfigSchema = z3.object({
854
+ id: z3.string().min(1),
855
+ type: z3.literal("command"),
856
+ name: z3.string().min(1),
857
+ command: z3.string().min(1),
858
+ output_type: z3.enum(["json", "text", "file"]),
859
+ allowed_commands: z3.array(z3.string()).optional(),
860
+ working_dir: z3.string().optional(),
861
+ timeout_ms: z3.number().positive().default(3e4),
607
862
  pricing: PricingSchema
608
863
  });
609
- var ConductorSkillConfigSchema = z2.object({
610
- id: z2.string().min(1),
611
- type: z2.literal("conductor"),
612
- name: z2.string().min(1),
613
- conductor_skill: z2.enum(["orchestrate", "plan"]),
864
+ var ConductorSkillConfigSchema = z3.object({
865
+ id: z3.string().min(1),
866
+ type: z3.literal("conductor"),
867
+ name: z3.string().min(1),
868
+ conductor_skill: z3.enum(["orchestrate", "plan"]),
614
869
  pricing: PricingSchema,
615
- timeout_ms: z2.number().positive().optional()
870
+ timeout_ms: z3.number().positive().optional()
616
871
  });
617
- var SkillConfigSchema = z2.discriminatedUnion("type", [
872
+ var SkillConfigSchema = z3.discriminatedUnion("type", [
618
873
  ApiSkillConfigSchema,
619
874
  PipelineSkillConfigSchema,
620
875
  OpenClawSkillConfigSchema,
621
876
  CommandSkillConfigSchema,
622
877
  ConductorSkillConfigSchema
623
878
  ]);
624
- var SkillsFileSchema = z2.object({
625
- skills: z2.array(SkillConfigSchema)
879
+ var SkillsFileSchema = z3.object({
880
+ skills: z3.array(SkillConfigSchema)
626
881
  });
627
882
  function expandEnvVars(value) {
628
883
  return value.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
@@ -1259,19 +1514,19 @@ var AgentRuntime = class {
1259
1514
  * 3. Populate the Map with all 4 modes — SkillExecutor sees them via reference.
1260
1515
  */
1261
1516
  async initSkillExecutor() {
1262
- const hasSkillsYaml = this.skillsYamlPath && existsSync(this.skillsYamlPath);
1517
+ const hasSkillsYaml = this.skillsYamlPath && existsSync3(this.skillsYamlPath);
1263
1518
  if (!hasSkillsYaml && !this.conductorEnabled) {
1264
1519
  return;
1265
1520
  }
1266
1521
  let configs = [];
1267
1522
  if (hasSkillsYaml) {
1268
- const yamlContent = readFileSync(this.skillsYamlPath, "utf8");
1523
+ const yamlContent = readFileSync3(this.skillsYamlPath, "utf8");
1269
1524
  configs = parseSkillsFile(yamlContent);
1270
1525
  }
1271
1526
  const modes = /* @__PURE__ */ new Map();
1272
1527
  if (this.conductorEnabled) {
1273
- const { ConductorMode } = await import("../conductor-mode-CF6PSRRA.js");
1274
- const { registerConductorCard, CONDUCTOR_OWNER } = await import("../card-P5C36VBD.js");
1528
+ const { ConductorMode } = await import("../conductor-mode-6MIVMFBC.js");
1529
+ const { registerConductorCard, CONDUCTOR_OWNER } = await import("../card-IE5UV5QX.js");
1275
1530
  const { loadPeers: loadPeers2 } = await import("../peers-G36URZYB.js");
1276
1531
  registerConductorCard(this.registryDb);
1277
1532
  const resolveAgentUrl = (owner) => {
@@ -1468,12 +1723,12 @@ import Fastify2 from "fastify";
1468
1723
  import cors from "@fastify/cors";
1469
1724
  import fastifyStatic from "@fastify/static";
1470
1725
  import fastifyWebsocket from "@fastify/websocket";
1471
- import { join, dirname } from "path";
1726
+ import { join as join3, dirname } from "path";
1472
1727
  import { fileURLToPath } from "url";
1473
- import { existsSync as existsSync2 } from "fs";
1728
+ import { existsSync as existsSync4 } from "fs";
1474
1729
 
1475
1730
  // src/relay/websocket-relay.ts
1476
- import { randomUUID as randomUUID3 } from "crypto";
1731
+ import { randomUUID as randomUUID4 } from "crypto";
1477
1732
  var RATE_LIMIT_MAX = 60;
1478
1733
  var RATE_LIMIT_WINDOW_MS = 6e4;
1479
1734
  var RELAY_TIMEOUT_MS = 3e4;
@@ -1545,7 +1800,7 @@ function registerWebSocketRelay(server, db) {
1545
1800
  function logAgentJoined(owner, cardName, cardId) {
1546
1801
  try {
1547
1802
  insertRequestLog(db, {
1548
- id: randomUUID3(),
1803
+ id: randomUUID4(),
1549
1804
  card_id: cardId,
1550
1805
  card_name: cardName,
1551
1806
  requester: owner,
@@ -1645,7 +1900,8 @@ function registerWebSocketRelay(server, db) {
1645
1900
  }
1646
1901
  }
1647
1902
  }
1648
- server.get("/ws", { websocket: true }, (socket) => {
1903
+ server.get("/ws", { websocket: true }, (rawSocket, _request) => {
1904
+ const socket = rawSocket;
1649
1905
  let registeredOwner;
1650
1906
  socket.on("message", (raw) => {
1651
1907
  let data;
@@ -1715,6 +1971,125 @@ function registerWebSocketRelay(server, db) {
1715
1971
  };
1716
1972
  }
1717
1973
 
1974
+ // src/identity/guarantor.ts
1975
+ import { z as z4 } from "zod";
1976
+ import { randomUUID as randomUUID5 } from "crypto";
1977
+ var MAX_AGENTS_PER_GUARANTOR = 10;
1978
+ var GUARANTOR_CREDIT_POOL = 50;
1979
+ var GuarantorRecordSchema = z4.object({
1980
+ id: z4.string().uuid(),
1981
+ github_login: z4.string().min(1),
1982
+ agent_count: z4.number().int().nonnegative(),
1983
+ credit_pool: z4.number().int().nonnegative(),
1984
+ created_at: z4.string().datetime()
1985
+ });
1986
+ var GUARANTOR_SCHEMA = `
1987
+ CREATE TABLE IF NOT EXISTS guarantors (
1988
+ id TEXT PRIMARY KEY,
1989
+ github_login TEXT UNIQUE NOT NULL,
1990
+ agent_count INTEGER NOT NULL DEFAULT 0,
1991
+ credit_pool INTEGER NOT NULL DEFAULT ${GUARANTOR_CREDIT_POOL},
1992
+ created_at TEXT NOT NULL
1993
+ );
1994
+
1995
+ CREATE TABLE IF NOT EXISTS agent_guarantors (
1996
+ agent_id TEXT PRIMARY KEY,
1997
+ guarantor_id TEXT NOT NULL,
1998
+ linked_at TEXT NOT NULL,
1999
+ FOREIGN KEY (guarantor_id) REFERENCES guarantors(id)
2000
+ );
2001
+ `;
2002
+ function ensureGuarantorTables(db) {
2003
+ db.exec(GUARANTOR_SCHEMA);
2004
+ }
2005
+ function registerGuarantor(db, githubLogin) {
2006
+ ensureGuarantorTables(db);
2007
+ const existing = db.prepare("SELECT * FROM guarantors WHERE github_login = ?").get(githubLogin);
2008
+ if (existing) {
2009
+ throw new AgentBnBError(
2010
+ `Guarantor already registered: ${githubLogin}`,
2011
+ "GUARANTOR_EXISTS"
2012
+ );
2013
+ }
2014
+ const record = {
2015
+ id: randomUUID5(),
2016
+ github_login: githubLogin,
2017
+ agent_count: 0,
2018
+ credit_pool: GUARANTOR_CREDIT_POOL,
2019
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
2020
+ };
2021
+ db.prepare(
2022
+ "INSERT INTO guarantors (id, github_login, agent_count, credit_pool, created_at) VALUES (?, ?, ?, ?, ?)"
2023
+ ).run(record.id, record.github_login, record.agent_count, record.credit_pool, record.created_at);
2024
+ return record;
2025
+ }
2026
+ function linkAgentToGuarantor(db, agentId, githubLogin) {
2027
+ ensureGuarantorTables(db);
2028
+ const guarantor = db.prepare("SELECT * FROM guarantors WHERE github_login = ?").get(githubLogin);
2029
+ if (!guarantor) {
2030
+ throw new AgentBnBError(
2031
+ `Guarantor not found: ${githubLogin}`,
2032
+ "GUARANTOR_NOT_FOUND"
2033
+ );
2034
+ }
2035
+ if (guarantor["agent_count"] >= MAX_AGENTS_PER_GUARANTOR) {
2036
+ throw new AgentBnBError(
2037
+ `Maximum agents per guarantor reached (${MAX_AGENTS_PER_GUARANTOR})`,
2038
+ "MAX_AGENTS_EXCEEDED"
2039
+ );
2040
+ }
2041
+ const existingLink = db.prepare("SELECT * FROM agent_guarantors WHERE agent_id = ?").get(agentId);
2042
+ if (existingLink) {
2043
+ throw new AgentBnBError(
2044
+ `Agent ${agentId} is already linked to a guarantor`,
2045
+ "AGENT_ALREADY_LINKED"
2046
+ );
2047
+ }
2048
+ db.transaction(() => {
2049
+ db.prepare("INSERT INTO agent_guarantors (agent_id, guarantor_id, linked_at) VALUES (?, ?, ?)").run(
2050
+ agentId,
2051
+ guarantor["id"],
2052
+ (/* @__PURE__ */ new Date()).toISOString()
2053
+ );
2054
+ db.prepare("UPDATE guarantors SET agent_count = agent_count + 1 WHERE id = ?").run(guarantor["id"]);
2055
+ })();
2056
+ return getGuarantor(db, githubLogin);
2057
+ }
2058
+ function getGuarantor(db, githubLogin) {
2059
+ ensureGuarantorTables(db);
2060
+ const row = db.prepare("SELECT * FROM guarantors WHERE github_login = ?").get(githubLogin);
2061
+ if (!row) return null;
2062
+ return {
2063
+ id: row["id"],
2064
+ github_login: row["github_login"],
2065
+ agent_count: row["agent_count"],
2066
+ credit_pool: row["credit_pool"],
2067
+ created_at: row["created_at"]
2068
+ };
2069
+ }
2070
+ function getAgentGuarantor(db, agentId) {
2071
+ ensureGuarantorTables(db);
2072
+ const link = db.prepare(
2073
+ `SELECT g.* FROM guarantors g
2074
+ JOIN agent_guarantors ag ON ag.guarantor_id = g.id
2075
+ WHERE ag.agent_id = ?`
2076
+ ).get(agentId);
2077
+ if (!link) return null;
2078
+ return {
2079
+ id: link["id"],
2080
+ github_login: link["github_login"],
2081
+ agent_count: link["agent_count"],
2082
+ credit_pool: link["credit_pool"],
2083
+ created_at: link["created_at"]
2084
+ };
2085
+ }
2086
+ function initiateGithubAuth() {
2087
+ return {
2088
+ auth_url: "https://github.com/login/oauth/authorize?client_id=PLACEHOLDER&scope=read:user",
2089
+ state: randomUUID5()
2090
+ };
2091
+ }
2092
+
1718
2093
  // src/registry/server.ts
1719
2094
  function stripInternal(card) {
1720
2095
  const { _internal: _, ...publicCard } = card;
@@ -1736,17 +2111,20 @@ function createRegistryServer(opts) {
1736
2111
  const __filename = fileURLToPath(import.meta.url);
1737
2112
  const __dirname = dirname(__filename);
1738
2113
  const hubDistCandidates = [
1739
- join(__dirname, "../../hub/dist"),
2114
+ join3(__dirname, "../../hub/dist"),
1740
2115
  // When running from dist/registry/server.js
1741
- join(__dirname, "../../../hub/dist")
2116
+ join3(__dirname, "../../../hub/dist")
1742
2117
  // Fallback for alternative layouts
1743
2118
  ];
1744
- const hubDistDir = hubDistCandidates.find((p) => existsSync2(p));
2119
+ const hubDistDir = hubDistCandidates.find((p) => existsSync4(p));
1745
2120
  if (hubDistDir) {
1746
2121
  void server.register(fastifyStatic, {
1747
2122
  root: hubDistDir,
1748
2123
  prefix: "/hub/"
1749
2124
  });
2125
+ server.get("/", async (_request, reply) => {
2126
+ return reply.redirect("/hub/");
2127
+ });
1750
2128
  server.get("/hub", async (_request, reply) => {
1751
2129
  return reply.redirect("/hub/");
1752
2130
  });
@@ -1794,12 +2172,47 @@ function createRegistryServer(opts) {
1794
2172
  (c) => (c.metadata?.avg_latency_ms ?? Infinity) <= maxLatencyMs
1795
2173
  );
1796
2174
  }
1797
- if (sort === "success_rate") {
2175
+ const usesStmt = db.prepare(`
2176
+ SELECT card_id, skill_id, COUNT(*) as cnt
2177
+ FROM request_log
2178
+ WHERE status = 'success'
2179
+ AND created_at > datetime('now', '-7 days')
2180
+ AND (action_type IS NULL OR action_type = 'auto_share')
2181
+ GROUP BY card_id, skill_id
2182
+ `);
2183
+ const usesRows = usesStmt.all();
2184
+ const usesMap = /* @__PURE__ */ new Map();
2185
+ for (const row of usesRows) {
2186
+ usesMap.set(row.card_id, (usesMap.get(row.card_id) ?? 0) + row.cnt);
2187
+ if (row.skill_id) {
2188
+ usesMap.set(row.skill_id, (usesMap.get(row.skill_id) ?? 0) + row.cnt);
2189
+ }
2190
+ }
2191
+ if (sort === "popular") {
2192
+ cards = [...cards].sort((a, b) => {
2193
+ const aUses = usesMap.get(a.id) ?? 0;
2194
+ const bUses = usesMap.get(b.id) ?? 0;
2195
+ return bUses - aUses;
2196
+ });
2197
+ } else if (sort === "rated" || sort === "success_rate") {
1798
2198
  cards = [...cards].sort((a, b) => {
1799
2199
  const aRate = a.metadata?.success_rate ?? -1;
1800
2200
  const bRate = b.metadata?.success_rate ?? -1;
1801
2201
  return bRate - aRate;
1802
2202
  });
2203
+ } else if (sort === "cheapest") {
2204
+ cards = [...cards].sort((a, b) => {
2205
+ return a.pricing.credits_per_call - b.pricing.credits_per_call;
2206
+ });
2207
+ } else if (sort === "newest") {
2208
+ const createdStmt = db.prepare("SELECT id, created_at FROM capability_cards");
2209
+ const createdRows = createdStmt.all();
2210
+ const createdMap = new Map(createdRows.map((r) => [r.id, r.created_at]));
2211
+ cards = [...cards].sort((a, b) => {
2212
+ const aDate = createdMap.get(a.id) ?? "";
2213
+ const bDate = createdMap.get(b.id) ?? "";
2214
+ return bDate.localeCompare(aDate);
2215
+ });
1803
2216
  } else if (sort === "latency") {
1804
2217
  cards = [...cards].sort((a, b) => {
1805
2218
  const aLatency = a.metadata?.avg_latency_ms ?? Infinity;
@@ -1809,9 +2222,32 @@ function createRegistryServer(opts) {
1809
2222
  }
1810
2223
  const total = cards.length;
1811
2224
  const items = cards.slice(offset, offset + limit).map(stripInternal);
1812
- const result = { total, limit, offset, items };
2225
+ const usesThisWeek = {};
2226
+ for (const [key, count] of usesMap) {
2227
+ if (count > 0) usesThisWeek[key] = count;
2228
+ }
2229
+ const result = { total, limit, offset, items, uses_this_week: usesThisWeek };
1813
2230
  return reply.send(result);
1814
2231
  });
2232
+ server.get("/api/cards/trending", async (_request, reply) => {
2233
+ const trendingStmt = db.prepare(`
2234
+ SELECT rl.card_id, COUNT(*) as recent_requests
2235
+ FROM request_log rl
2236
+ WHERE rl.status = 'success'
2237
+ AND rl.created_at > datetime('now', '-7 days')
2238
+ AND (rl.action_type IS NULL OR rl.action_type = 'auto_share')
2239
+ GROUP BY rl.card_id
2240
+ ORDER BY recent_requests DESC
2241
+ LIMIT 10
2242
+ `);
2243
+ const trendingRows = trendingStmt.all();
2244
+ const items = trendingRows.map((row) => {
2245
+ const card = getCard(db, row.card_id);
2246
+ if (!card) return null;
2247
+ return { ...stripInternal(card), uses_this_week: row.recent_requests };
2248
+ }).filter((item) => item !== null);
2249
+ return reply.send({ items });
2250
+ });
1815
2251
  server.get("/cards/:id", async (request, reply) => {
1816
2252
  const { id } = request.params;
1817
2253
  const card = getCard(db, id);
@@ -1820,6 +2256,57 @@ function createRegistryServer(opts) {
1820
2256
  }
1821
2257
  return reply.send(stripInternal(card));
1822
2258
  });
2259
+ server.post("/cards", async (request, reply) => {
2260
+ const body = request.body;
2261
+ if (!body.spec_version) {
2262
+ body.spec_version = "1.0";
2263
+ }
2264
+ const result = AnyCardSchema.safeParse(body);
2265
+ if (!result.success) {
2266
+ return reply.code(400).send({
2267
+ error: "Card validation failed",
2268
+ issues: result.error.issues
2269
+ });
2270
+ }
2271
+ const card = result.data;
2272
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2273
+ if (card.spec_version === "2.0") {
2274
+ const cardWithTimestamps = {
2275
+ ...card,
2276
+ created_at: card.created_at ?? now,
2277
+ updated_at: now
2278
+ };
2279
+ db.prepare(
2280
+ `INSERT OR REPLACE INTO capability_cards (id, owner, data, created_at, updated_at)
2281
+ VALUES (?, ?, ?, ?, ?)`
2282
+ ).run(
2283
+ cardWithTimestamps.id,
2284
+ cardWithTimestamps.owner,
2285
+ JSON.stringify(cardWithTimestamps),
2286
+ cardWithTimestamps.created_at,
2287
+ cardWithTimestamps.updated_at
2288
+ );
2289
+ } else {
2290
+ try {
2291
+ insertCard(db, card);
2292
+ } catch (err) {
2293
+ if (err instanceof AgentBnBError && err.code === "VALIDATION_ERROR") {
2294
+ return reply.code(400).send({ error: err.message });
2295
+ }
2296
+ throw err;
2297
+ }
2298
+ }
2299
+ return reply.code(201).send({ ok: true, id: card.id });
2300
+ });
2301
+ server.delete("/cards/:id", async (request, reply) => {
2302
+ const { id } = request.params;
2303
+ const card = getCard(db, id);
2304
+ if (!card) {
2305
+ return reply.code(404).send({ error: "Not found" });
2306
+ }
2307
+ db.prepare("DELETE FROM capability_cards WHERE id = ?").run(id);
2308
+ return reply.send({ ok: true, id });
2309
+ });
1823
2310
  server.get("/api/agents", async (_request, reply) => {
1824
2311
  const allCards = listCards(db);
1825
2312
  const ownerMap = /* @__PURE__ */ new Map();
@@ -1930,10 +2417,67 @@ function createRegistryServer(opts) {
1930
2417
  const exchangeRow = exchangeStmt.get();
1931
2418
  return reply.send({
1932
2419
  agents_online: onlineOwners.size,
1933
- total_capabilities: allCards.length,
2420
+ total_capabilities: allCards.reduce((sum, card) => {
2421
+ const v2 = card;
2422
+ return sum + (v2.skills?.length ?? 1);
2423
+ }, 0),
1934
2424
  total_exchanges: exchangeRow.count
1935
2425
  });
1936
2426
  });
2427
+ server.post("/api/identity/register", async (request, reply) => {
2428
+ if (!opts.creditDb) {
2429
+ return reply.code(503).send({ error: "Credit database not configured" });
2430
+ }
2431
+ const body = request.body;
2432
+ const githubLogin = typeof body.github_login === "string" ? body.github_login.trim() : "";
2433
+ if (!githubLogin) {
2434
+ return reply.code(400).send({ error: "github_login is required" });
2435
+ }
2436
+ try {
2437
+ const record = registerGuarantor(opts.creditDb, githubLogin);
2438
+ const auth = initiateGithubAuth();
2439
+ return reply.code(201).send({ guarantor: record, oauth: auth });
2440
+ } catch (err) {
2441
+ if (err instanceof AgentBnBError && err.code === "GUARANTOR_EXISTS") {
2442
+ return reply.code(409).send({ error: err.message });
2443
+ }
2444
+ throw err;
2445
+ }
2446
+ });
2447
+ server.post("/api/identity/link", async (request, reply) => {
2448
+ if (!opts.creditDb) {
2449
+ return reply.code(503).send({ error: "Credit database not configured" });
2450
+ }
2451
+ const body = request.body;
2452
+ const agentId = typeof body.agent_id === "string" ? body.agent_id.trim() : "";
2453
+ const githubLogin = typeof body.github_login === "string" ? body.github_login.trim() : "";
2454
+ if (!agentId || !githubLogin) {
2455
+ return reply.code(400).send({ error: "agent_id and github_login are required" });
2456
+ }
2457
+ try {
2458
+ const record = linkAgentToGuarantor(opts.creditDb, agentId, githubLogin);
2459
+ return reply.send({ guarantor: record });
2460
+ } catch (err) {
2461
+ if (err instanceof AgentBnBError) {
2462
+ const statusMap = {
2463
+ GUARANTOR_NOT_FOUND: 404,
2464
+ MAX_AGENTS_EXCEEDED: 409,
2465
+ AGENT_ALREADY_LINKED: 409
2466
+ };
2467
+ const status = statusMap[err.code] ?? 400;
2468
+ return reply.code(status).send({ error: err.message });
2469
+ }
2470
+ throw err;
2471
+ }
2472
+ });
2473
+ server.get("/api/identity/:agent_id", async (request, reply) => {
2474
+ if (!opts.creditDb) {
2475
+ return reply.code(503).send({ error: "Credit database not configured" });
2476
+ }
2477
+ const { agent_id } = request.params;
2478
+ const guarantor = getAgentGuarantor(opts.creditDb, agent_id);
2479
+ return reply.send({ agent_id, guarantor });
2480
+ });
1937
2481
  if (opts.ownerApiKey && opts.ownerName) {
1938
2482
  const ownerApiKey = opts.ownerApiKey;
1939
2483
  const ownerName = opts.ownerName;
@@ -2114,10 +2658,10 @@ async function stopAnnouncement() {
2114
2658
  }
2115
2659
 
2116
2660
  // src/openclaw/soul-sync.ts
2117
- import { randomUUID as randomUUID5 } from "crypto";
2661
+ import { randomUUID as randomUUID7 } from "crypto";
2118
2662
 
2119
2663
  // src/skills/publish-capability.ts
2120
- import { randomUUID as randomUUID4 } from "crypto";
2664
+ import { randomUUID as randomUUID6 } from "crypto";
2121
2665
  function parseSoulMd(content) {
2122
2666
  const lines = content.split("\n");
2123
2667
  let name = "";
@@ -2188,7 +2732,7 @@ function parseSoulMdV2(content) {
2188
2732
  const parsed = parseSoulMd(content);
2189
2733
  const skills = parsed.capabilities.map((cap) => {
2190
2734
  const sanitizedId = cap.name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
2191
- const id = sanitizedId.length > 0 ? sanitizedId : randomUUID5();
2735
+ const id = sanitizedId.length > 0 ? sanitizedId : randomUUID7();
2192
2736
  return {
2193
2737
  id,
2194
2738
  name: cap.name,
@@ -2230,7 +2774,7 @@ function publishFromSoulV2(db, soulContent, owner) {
2230
2774
  (c) => c.spec_version === "2.0"
2231
2775
  );
2232
2776
  const now = (/* @__PURE__ */ new Date()).toISOString();
2233
- const cardId = existingV2?.id ?? randomUUID5();
2777
+ const cardId = existingV2?.id ?? randomUUID7();
2234
2778
  const card = {
2235
2779
  spec_version: "2.0",
2236
2780
  id: cardId,
@@ -2255,7 +2799,7 @@ function publishFromSoulV2(db, soulContent, owner) {
2255
2799
  }
2256
2800
 
2257
2801
  // src/openclaw/heartbeat-writer.ts
2258
- import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
2802
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync5 } from "fs";
2259
2803
  var HEARTBEAT_MARKER_START = "<!-- agentbnb:start -->";
2260
2804
  var HEARTBEAT_MARKER_END = "<!-- agentbnb:end -->";
2261
2805
  function generateHeartbeatSection(autonomy, budget) {
@@ -2291,11 +2835,11 @@ function generateHeartbeatSection(autonomy, budget) {
2291
2835
  ].join("\n");
2292
2836
  }
2293
2837
  function injectHeartbeatSection(heartbeatPath, section) {
2294
- if (!existsSync3(heartbeatPath)) {
2295
- writeFileSync(heartbeatPath, section + "\n", "utf-8");
2838
+ if (!existsSync5(heartbeatPath)) {
2839
+ writeFileSync2(heartbeatPath, section + "\n", "utf-8");
2296
2840
  return;
2297
2841
  }
2298
- let content = readFileSync2(heartbeatPath, "utf-8");
2842
+ let content = readFileSync4(heartbeatPath, "utf-8");
2299
2843
  const startIdx = content.indexOf(HEARTBEAT_MARKER_START);
2300
2844
  const endIdx = content.indexOf(HEARTBEAT_MARKER_END);
2301
2845
  if (startIdx !== -1 && endIdx !== -1) {
@@ -2303,7 +2847,7 @@ function injectHeartbeatSection(heartbeatPath, section) {
2303
2847
  } else {
2304
2848
  content = content + "\n" + section + "\n";
2305
2849
  }
2306
- writeFileSync(heartbeatPath, content, "utf-8");
2850
+ writeFileSync2(heartbeatPath, content, "utf-8");
2307
2851
  }
2308
2852
 
2309
2853
  // src/openclaw/skill.ts
@@ -2344,7 +2888,7 @@ function getOpenClawStatus(config, db, creditDb) {
2344
2888
  var require2 = createRequire(import.meta.url);
2345
2889
  var pkg = require2("../../package.json");
2346
2890
  async function confirm(question) {
2347
- const rl = createInterface({ input: process.stdin, output: process.stdout });
2891
+ const rl = createInterface2({ input: process.stdin, output: process.stdout });
2348
2892
  try {
2349
2893
  return await new Promise((resolve) => {
2350
2894
  rl.question(question, (answer) => {
@@ -2366,12 +2910,12 @@ function getLanIp() {
2366
2910
  }
2367
2911
  var program = new Command();
2368
2912
  program.name("agentbnb").description("P2P Agent Capability Sharing Protocol \u2014 Airbnb for AI agent pipelines").version(pkg.version);
2369
- program.command("init").description("Initialize AgentBnB config and create agent identity").option("--owner <name>", "Agent owner name").option("--port <port>", "Gateway port", "7700").option("--host <ip>", "Override gateway host IP (default: auto-detected LAN IP)").option("--yes", "Auto-confirm all draft cards (non-interactive)").option("--no-detect", "Skip API key detection").option("--json", "Output as JSON").action(async (opts) => {
2913
+ program.command("init").description("Initialize AgentBnB config and create agent identity").option("--owner <name>", "Agent owner name").option("--port <port>", "Gateway port", "7700").option("--host <ip>", "Override gateway host IP (default: auto-detected LAN IP)").option("--yes", "Auto-confirm all draft cards (non-interactive)").option("--no-detect", "Skip API key detection").option("--from <file>", "Parse a specific file for capability detection").option("--json", "Output as JSON").action(async (opts) => {
2370
2914
  const owner = opts.owner ?? `agent-${randomBytes(4).toString("hex")}`;
2371
2915
  const token = randomBytes(32).toString("hex");
2372
2916
  const configDir = getConfigDir();
2373
- const dbPath = join2(configDir, "registry.db");
2374
- const creditDbPath = join2(configDir, "credit.db");
2917
+ const dbPath = join4(configDir, "registry.db");
2918
+ const creditDbPath = join4(configDir, "credit.db");
2375
2919
  const port = parseInt(opts.port, 10);
2376
2920
  const ip = opts.host ?? getLanIp();
2377
2921
  const existingConfig = loadConfig();
@@ -2394,23 +2938,86 @@ program.command("init").description("Initialize AgentBnB config and create agent
2394
2938
  saveKeyPair(configDir, keys);
2395
2939
  keypairStatus = "generated";
2396
2940
  }
2941
+ const identity = ensureIdentity(configDir, owner);
2397
2942
  const creditDb = openCreditDb(creditDbPath);
2398
2943
  bootstrapAgent(creditDb, owner, 100);
2399
2944
  creditDb.close();
2400
2945
  const skipDetect = opts.detect === false;
2401
- let detectedKeys = [];
2402
- let detectedPorts = [];
2403
2946
  const publishedCards = [];
2947
+ let detectedSource = "none";
2404
2948
  if (!skipDetect) {
2405
- detectedKeys = detectApiKeys(KNOWN_API_KEYS);
2406
- detectedPorts = await detectOpenPorts([7700, 7701, 8080, 3e3, 8e3, 11434]);
2407
- if (detectedKeys.length > 0) {
2949
+ if (!opts.json) {
2950
+ console.log("\nDetecting capabilities...");
2951
+ }
2952
+ const result = detectCapabilities({ fromFile: opts.from, cwd: process.cwd() });
2953
+ detectedSource = result.source;
2954
+ if (result.source === "soul") {
2408
2955
  if (!opts.json) {
2409
- console.log(`
2410
- Detected ${detectedKeys.length} API key${detectedKeys.length > 1 ? "s" : ""}: ${detectedKeys.join(", ")}`);
2956
+ console.log(` Found SOUL.md \u2014 extracting capabilities...`);
2411
2957
  }
2958
+ const db = openDatabase(dbPath);
2959
+ try {
2960
+ const card = publishFromSoulV2(db, result.soulContent, owner);
2961
+ publishedCards.push({ id: card.id, name: card.agent_name });
2962
+ if (!opts.json) {
2963
+ console.log(` Published v2.0 card: ${card.agent_name} (${card.skills.length} skills)`);
2964
+ }
2965
+ } finally {
2966
+ db.close();
2967
+ }
2968
+ } else if (result.source === "docs") {
2969
+ if (!opts.json) {
2970
+ console.log(` Found ${result.sourceFile ?? "docs"} \u2014 detected ${result.capabilities.length} capabilities:`);
2971
+ for (const cap of result.capabilities) {
2972
+ console.log(` ${cap.name} (${cap.category}, cr ${cap.credits_per_call}/call)`);
2973
+ }
2974
+ }
2975
+ const card = capabilitiesToV2Card(result.capabilities, owner);
2976
+ if (opts.yes) {
2977
+ const db = openDatabase(dbPath);
2978
+ try {
2979
+ db.prepare(
2980
+ `INSERT OR REPLACE INTO capability_cards (id, owner, data, created_at, updated_at)
2981
+ VALUES (?, ?, ?, ?, ?)`
2982
+ ).run(card.id, card.owner, JSON.stringify(card), card.created_at, card.updated_at);
2983
+ publishedCards.push({ id: card.id, name: card.agent_name });
2984
+ if (!opts.json) {
2985
+ console.log(` Published v2.0 card: ${card.agent_name} (${card.skills.length} skills)`);
2986
+ }
2987
+ } finally {
2988
+ db.close();
2989
+ }
2990
+ } else if (process.stdout.isTTY) {
2991
+ const yes = await confirm(`
2992
+ Publish these ${card.skills.length} capabilities? [y/N] `);
2993
+ if (yes) {
2994
+ const db = openDatabase(dbPath);
2995
+ try {
2996
+ db.prepare(
2997
+ `INSERT OR REPLACE INTO capability_cards (id, owner, data, created_at, updated_at)
2998
+ VALUES (?, ?, ?, ?, ?)`
2999
+ ).run(card.id, card.owner, JSON.stringify(card), card.created_at, card.updated_at);
3000
+ publishedCards.push({ id: card.id, name: card.agent_name });
3001
+ console.log(` Published v2.0 card: ${card.agent_name} (${card.skills.length} skills)`);
3002
+ } finally {
3003
+ db.close();
3004
+ }
3005
+ } else {
3006
+ console.log(" Skipped publishing.");
3007
+ }
3008
+ } else {
3009
+ if (!opts.json) {
3010
+ console.log(" Non-interactive environment. Re-run with --yes to auto-publish.");
3011
+ }
3012
+ }
3013
+ } else if (result.source === "env") {
3014
+ const detectedKeys = result.envKeys ?? [];
3015
+ if (!opts.json) {
3016
+ console.log(` Detected ${detectedKeys.length} API key${detectedKeys.length > 1 ? "s" : ""}: ${detectedKeys.join(", ")}`);
3017
+ }
3018
+ const detectedPorts = await detectOpenPorts([7700, 7701, 8080, 3e3, 8e3, 11434]);
2412
3019
  if (detectedPorts.length > 0 && !opts.json) {
2413
- console.log(`Found services on ports: ${detectedPorts.join(", ")}`);
3020
+ console.log(` Found services on ports: ${detectedPorts.join(", ")}`);
2414
3021
  }
2415
3022
  const drafts = detectedKeys.map((key) => buildDraftCard(key, owner)).filter((card) => card !== null);
2416
3023
  if (opts.yes) {
@@ -2420,7 +3027,7 @@ Detected ${detectedKeys.length} API key${detectedKeys.length > 1 ? "s" : ""}: ${
2420
3027
  insertCard(db, card);
2421
3028
  publishedCards.push({ id: card.id, name: card.name });
2422
3029
  if (!opts.json) {
2423
- console.log(`Published: ${card.name} (${card.id})`);
3030
+ console.log(` Published: ${card.name} (${card.id})`);
2424
3031
  }
2425
3032
  }
2426
3033
  } finally {
@@ -2434,9 +3041,9 @@ Detected ${detectedKeys.length} API key${detectedKeys.length > 1 ? "s" : ""}: ${
2434
3041
  if (yes) {
2435
3042
  insertCard(db, card);
2436
3043
  publishedCards.push({ id: card.id, name: card.name });
2437
- console.log(`Published: ${card.name} (${card.id})`);
3044
+ console.log(` Published: ${card.name} (${card.id})`);
2438
3045
  } else {
2439
- console.log(`Skipped: ${card.name}`);
3046
+ console.log(` Skipped: ${card.name}`);
2440
3047
  }
2441
3048
  }
2442
3049
  } finally {
@@ -2444,12 +3051,29 @@ Detected ${detectedKeys.length} API key${detectedKeys.length > 1 ? "s" : ""}: ${
2444
3051
  }
2445
3052
  } else {
2446
3053
  if (!opts.json) {
2447
- console.log("Non-interactive environment detected. Re-run with --yes to auto-publish draft cards.");
3054
+ console.log(" Non-interactive environment. Re-run with --yes to auto-publish.");
2448
3055
  }
2449
3056
  }
2450
3057
  } else {
2451
- if (!opts.json) {
2452
- console.log("\nNo API keys detected. You can manually publish cards with `agentbnb publish`.");
3058
+ if (process.stdout.isTTY && !opts.yes && !opts.json) {
3059
+ const selected = await interactiveTemplateMenu();
3060
+ if (selected.length > 0) {
3061
+ const card = capabilitiesToV2Card(selected, owner);
3062
+ const db = openDatabase(dbPath);
3063
+ try {
3064
+ db.prepare(
3065
+ `INSERT OR REPLACE INTO capability_cards (id, owner, data, created_at, updated_at)
3066
+ VALUES (?, ?, ?, ?, ?)`
3067
+ ).run(card.id, card.owner, JSON.stringify(card), card.created_at, card.updated_at);
3068
+ publishedCards.push({ id: card.id, name: card.agent_name });
3069
+ console.log(`
3070
+ Published v2.0 card: ${card.agent_name} (${card.skills.length} skills)`);
3071
+ } finally {
3072
+ db.close();
3073
+ }
3074
+ }
3075
+ } else if (!opts.json) {
3076
+ console.log(" No capabilities detected. You can manually publish with `agentbnb publish`.");
2453
3077
  }
2454
3078
  }
2455
3079
  }
@@ -2460,10 +3084,11 @@ Detected ${detectedKeys.length} API key${detectedKeys.length > 1 ? "s" : ""}: ${
2460
3084
  config_dir: configDir,
2461
3085
  token,
2462
3086
  gateway_url: config.gateway_url,
2463
- keypair: keypairStatus
3087
+ keypair: keypairStatus,
3088
+ agent_id: identity.agent_id
2464
3089
  };
2465
3090
  if (!skipDetect) {
2466
- jsonOutput.detected_keys = detectedKeys;
3091
+ jsonOutput.detected_source = detectedSource;
2467
3092
  jsonOutput.published_cards = publishedCards;
2468
3093
  }
2469
3094
  console.log(JSON.stringify(jsonOutput, null, 2));
@@ -2474,10 +3099,11 @@ Detected ${detectedKeys.length} API key${detectedKeys.length > 1 ? "s" : ""}: ${
2474
3099
  console.log(` Config: ${configDir}/config.json`);
2475
3100
  console.log(` Credits: 100 (starter grant)`);
2476
3101
  console.log(` Keypair: ${keypairStatus === "generated" ? "generated (Ed25519)" : "preserved (existing)"}`);
3102
+ console.log(` Agent ID: ${identity.agent_id}`);
2477
3103
  console.log(` Gateway: http://${ip}:${port}`);
2478
3104
  }
2479
3105
  });
2480
- program.command("publish <card.json>").description("Publish a Capability Card to the registry").option("--json", "Output as JSON").action(async (cardPath, opts) => {
3106
+ program.command("publish <card.json>").description("Publish a Capability Card to the registry (v1.0 or v2.0)").option("--json", "Output as JSON").option("--registry <url>", "POST card to a remote registry URL instead of local DB").action(async (cardPath, opts) => {
2481
3107
  const config = loadConfig();
2482
3108
  if (!config) {
2483
3109
  console.error("Error: not initialized. Run `agentbnb init` first.");
@@ -2485,7 +3111,7 @@ program.command("publish <card.json>").description("Publish a Capability Card to
2485
3111
  }
2486
3112
  let raw;
2487
3113
  try {
2488
- raw = readFileSync3(cardPath, "utf-8");
3114
+ raw = readFileSync5(cardPath, "utf-8");
2489
3115
  } catch {
2490
3116
  console.error(`Error: cannot read file: ${cardPath}`);
2491
3117
  process.exit(1);
@@ -2497,7 +3123,10 @@ program.command("publish <card.json>").description("Publish a Capability Card to
2497
3123
  console.error("Error: invalid JSON in card file.");
2498
3124
  process.exit(1);
2499
3125
  }
2500
- const result = CapabilityCardSchema.safeParse(parsed);
3126
+ if (typeof parsed === "object" && parsed !== null && !("spec_version" in parsed)) {
3127
+ parsed.spec_version = "1.0";
3128
+ }
3129
+ const result = AnyCardSchema.safeParse(parsed);
2501
3130
  if (!result.success) {
2502
3131
  if (opts.json) {
2503
3132
  console.log(JSON.stringify({ success: false, errors: result.error.issues }, null, 2));
@@ -2509,16 +3138,128 @@ program.command("publish <card.json>").description("Publish a Capability Card to
2509
3138
  }
2510
3139
  process.exit(1);
2511
3140
  }
3141
+ const card = result.data;
3142
+ const cardName = card.spec_version === "2.0" ? card.agent_name : card.name;
3143
+ const db = openDatabase(config.db_path);
3144
+ try {
3145
+ if (card.spec_version === "2.0") {
3146
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3147
+ const cardWithTimestamps = { ...card, created_at: card.created_at ?? now, updated_at: now };
3148
+ db.prepare(
3149
+ "INSERT OR REPLACE INTO capability_cards (id, owner, data, created_at, updated_at) VALUES (?, ?, ?, ?, ?)"
3150
+ ).run(cardWithTimestamps.id, cardWithTimestamps.owner, JSON.stringify(cardWithTimestamps), cardWithTimestamps.created_at, cardWithTimestamps.updated_at);
3151
+ } else {
3152
+ insertCard(db, card);
3153
+ }
3154
+ } finally {
3155
+ db.close();
3156
+ }
3157
+ if (!opts.json) {
3158
+ console.log(`Published locally: ${cardName} (${card.id})`);
3159
+ }
3160
+ const registryUrl = opts.registry ?? config.registry;
3161
+ let remoteSuccess = false;
3162
+ if (registryUrl) {
3163
+ const url = `${registryUrl.replace(/\/$/, "")}/cards`;
3164
+ const remoteCard = { ...card, gateway_url: config.gateway_url };
3165
+ try {
3166
+ const response = await fetch(url, {
3167
+ method: "POST",
3168
+ headers: { "Content-Type": "application/json" },
3169
+ body: JSON.stringify(remoteCard)
3170
+ });
3171
+ if (!response.ok) {
3172
+ const body = await response.text();
3173
+ console.error(`Warning: remote registry returned ${response.status}: ${body}`);
3174
+ } else {
3175
+ remoteSuccess = true;
3176
+ if (!opts.json) {
3177
+ console.log(`Published to registry: ${url}`);
3178
+ }
3179
+ }
3180
+ } catch (err) {
3181
+ console.error(`Warning: cannot reach registry at ${url}: ${err.message}`);
3182
+ }
3183
+ }
3184
+ if (opts.json) {
3185
+ console.log(JSON.stringify({
3186
+ success: true,
3187
+ id: card.id,
3188
+ name: cardName,
3189
+ ...registryUrl ? { registry: registryUrl, remote_published: remoteSuccess } : {}
3190
+ }, null, 2));
3191
+ } else if (!registryUrl) {
3192
+ }
3193
+ });
3194
+ program.command("sync").description("Push all local capability cards to the configured remote registry").option("--registry <url>", "Remote registry URL (overrides config.registry)").option("--json", "Output as JSON").action(async (opts) => {
3195
+ const config = loadConfig();
3196
+ if (!config) {
3197
+ console.error("Error: not initialized. Run `agentbnb init` first.");
3198
+ process.exit(1);
3199
+ }
3200
+ const registryUrl = opts.registry ?? config.registry;
3201
+ if (!registryUrl) {
3202
+ console.error("Error: no remote registry configured.");
3203
+ console.error("Set one with: agentbnb config set registry <url>");
3204
+ process.exit(1);
3205
+ }
2512
3206
  const db = openDatabase(config.db_path);
3207
+ let localCards;
2513
3208
  try {
2514
- insertCard(db, result.data);
3209
+ localCards = listCards(db);
2515
3210
  } finally {
2516
3211
  db.close();
2517
3212
  }
3213
+ if (localCards.length === 0) {
3214
+ if (opts.json) {
3215
+ console.log(JSON.stringify({ synced: 0, failed: 0, registry: registryUrl }));
3216
+ } else {
3217
+ console.log("No local cards to sync.");
3218
+ }
3219
+ return;
3220
+ }
3221
+ const url = `${registryUrl.replace(/\/$/, "")}/cards`;
3222
+ let synced = 0;
3223
+ let failed = 0;
3224
+ const results = [];
3225
+ for (const card of localCards) {
3226
+ const { _internal: _, ...publicCard } = card;
3227
+ const remoteCard = { ...publicCard, gateway_url: config.gateway_url };
3228
+ const displayName = card.name ?? card.agent_name ?? card.id;
3229
+ try {
3230
+ const response = await fetch(url, {
3231
+ method: "POST",
3232
+ headers: { "Content-Type": "application/json" },
3233
+ body: JSON.stringify(remoteCard)
3234
+ });
3235
+ if (response.ok) {
3236
+ synced++;
3237
+ results.push({ id: card.id, name: displayName, ok: true });
3238
+ if (!opts.json) {
3239
+ console.log(` Synced: ${displayName} (${card.id.slice(0, 8)}...)`);
3240
+ }
3241
+ } else {
3242
+ const body = await response.text();
3243
+ failed++;
3244
+ results.push({ id: card.id, name: displayName, ok: false, error: `${response.status}: ${body}` });
3245
+ if (!opts.json) {
3246
+ console.error(` Failed: ${displayName} \u2014 ${response.status}`);
3247
+ }
3248
+ }
3249
+ } catch (err) {
3250
+ failed++;
3251
+ const msg = err instanceof Error ? err.message : String(err);
3252
+ results.push({ id: card.id, name: displayName, ok: false, error: msg });
3253
+ if (!opts.json) {
3254
+ console.error(` Failed: ${displayName} \u2014 ${msg}`);
3255
+ }
3256
+ }
3257
+ }
2518
3258
  if (opts.json) {
2519
- console.log(JSON.stringify({ success: true, id: result.data.id, name: result.data.name }, null, 2));
3259
+ console.log(JSON.stringify({ synced, failed, registry: registryUrl, results }, null, 2));
2520
3260
  } else {
2521
- console.log(`Published: ${result.data.name} (${result.data.id})`);
3261
+ console.log(`
3262
+ Synced ${synced}/${localCards.length} cards to ${registryUrl}${failed > 0 ? ` (${failed} failed)` : ""}`);
2522
3263
  }
2523
3264
  });
2524
3265
  program.command("discover [query]").description("Search available capabilities in the registry").option("--level <level>", "Filter by level (1, 2, or 3)").option("--online", "Only show online capabilities").option("--local", "Browse for agents on the local network via mDNS").option("--registry <url>", "Remote registry URL to query (e.g., http://host:7701)").option("--tag <tag>", "Filter by metadata tag").option("--json", "Output as JSON").action(async (query, opts) => {
@@ -2537,7 +3278,7 @@ program.command("discover [query]").description("Search available capabilities i
2537
3278
  console.log("No agents found on local network.");
2538
3279
  return;
2539
3280
  }
2540
- const col2 = (s, w) => s.slice(0, w).padEnd(w);
3281
+ const col2 = (s, w) => (s ?? "").slice(0, w).padEnd(w);
2541
3282
  console.log(col2("Name", 24) + " " + col2("URL", 32) + " " + col2("Owner", 20));
2542
3283
  console.log("-".repeat(80));
2543
3284
  for (const agent of discovered) {
@@ -2607,7 +3348,7 @@ ${discovered.length} agent(s) found on local network`);
2607
3348
  console.log("No capabilities found.");
2608
3349
  return;
2609
3350
  }
2610
- const col = (s, w) => s.slice(0, w).padEnd(w);
3351
+ const col = (s, w) => (s ?? "").slice(0, w).padEnd(w);
2611
3352
  if (hasRemote) {
2612
3353
  console.log(
2613
3354
  col("ID", 16) + " " + col("Name", 28) + " " + col("Lvl", 3) + " " + col("Credits", 7) + " " + col("Online", 6) + " " + col("Source", 8)
@@ -2615,10 +3356,11 @@ ${discovered.length} agent(s) found on local network`);
2615
3356
  console.log("-".repeat(80));
2616
3357
  for (const card of outputCards) {
2617
3358
  const shortId = card.id.slice(0, 8) + "...";
3359
+ const displayName = card.name ?? card.agent_name ?? "";
2618
3360
  const source = "source" in card ? card.source : "local";
2619
3361
  const sourceTag = source === "remote" ? "[remote]" : "[local]";
2620
3362
  console.log(
2621
- col(shortId, 16) + " " + col(card.name, 28) + " " + col(String(card.level), 3) + " " + col(String(card.pricing.credits_per_call), 7) + " " + col(card.availability.online ? "yes" : "no", 6) + " " + col(sourceTag, 8)
3363
+ col(shortId, 16) + " " + col(displayName, 28) + " " + col(String(card.level ?? ""), 3) + " " + col(String(card.pricing?.credits_per_call ?? ""), 7) + " " + col(card.availability?.online ? "yes" : "no", 6) + " " + col(sourceTag, 8)
2622
3364
  );
2623
3365
  }
2624
3366
  } else {
@@ -2628,8 +3370,9 @@ ${discovered.length} agent(s) found on local network`);
2628
3370
  console.log("-".repeat(72));
2629
3371
  for (const card of outputCards) {
2630
3372
  const shortId = card.id.slice(0, 8) + "...";
3373
+ const displayName = card.name ?? card.agent_name ?? "";
2631
3374
  console.log(
2632
- col(shortId, 16) + " " + col(card.name, 32) + " " + col(String(card.level), 3) + " " + col(String(card.pricing.credits_per_call), 7) + " " + col(card.availability.online ? "yes" : "no", 6)
3375
+ col(shortId, 16) + " " + col(displayName, 32) + " " + col(String(card.level ?? ""), 3) + " " + col(String(card.pricing?.credits_per_call ?? ""), 7) + " " + col(card.availability?.online ? "yes" : "no", 6)
2633
3376
  );
2634
3377
  }
2635
3378
  }
@@ -2652,8 +3395,8 @@ program.command("request [card-id]").description("Request a capability from anot
2652
3395
  process.exit(1);
2653
3396
  }
2654
3397
  }
2655
- const registryDb = openDatabase(join2(getConfigDir(), "registry.db"));
2656
- const creditDb = openCreditDb(join2(getConfigDir(), "credit.db"));
3398
+ const registryDb = openDatabase(join4(getConfigDir(), "registry.db"));
3399
+ const creditDb = openCreditDb(join4(getConfigDir(), "credit.db"));
2657
3400
  registryDb.pragma("busy_timeout = 5000");
2658
3401
  creditDb.pragma("busy_timeout = 5000");
2659
3402
  try {
@@ -2690,7 +3433,7 @@ program.command("request [card-id]").description("Request a capability from anot
2690
3433
  }
2691
3434
  let gatewayUrl;
2692
3435
  let token;
2693
- const isPeerRequest = !!opts.peer;
3436
+ let isRemoteRequest = false;
2694
3437
  if (opts.peer) {
2695
3438
  const peer = findPeer(opts.peer);
2696
3439
  if (!peer) {
@@ -2699,20 +3442,61 @@ program.command("request [card-id]").description("Request a capability from anot
2699
3442
  }
2700
3443
  gatewayUrl = peer.url;
2701
3444
  token = peer.token;
3445
+ isRemoteRequest = true;
2702
3446
  } else {
2703
- gatewayUrl = config.gateway_url;
2704
- token = config.token;
3447
+ const db = openDatabase(config.db_path);
3448
+ let localCard;
3449
+ try {
3450
+ localCard = db.prepare("SELECT data FROM capability_cards WHERE id = ?").get(cardId) ? JSON.parse(db.prepare("SELECT data FROM capability_cards WHERE id = ?").get(cardId).data) : void 0;
3451
+ } finally {
3452
+ db.close();
3453
+ }
3454
+ if (localCard) {
3455
+ gatewayUrl = config.gateway_url;
3456
+ token = config.token;
3457
+ } else {
3458
+ const registryUrl = config.registry;
3459
+ if (!registryUrl) {
3460
+ console.error("Error: card not found locally and no remote registry configured.");
3461
+ console.error("Set one with: agentbnb config set registry <url>");
3462
+ process.exit(1);
3463
+ }
3464
+ const cardUrl = `${registryUrl.replace(/\/$/, "")}/cards/${cardId}`;
3465
+ let remoteCard;
3466
+ try {
3467
+ const resp = await fetch(cardUrl);
3468
+ if (!resp.ok) {
3469
+ console.error(`Error: card ${cardId} not found on remote registry (${resp.status}).`);
3470
+ process.exit(1);
3471
+ }
3472
+ remoteCard = await resp.json();
3473
+ } catch (err) {
3474
+ console.error(`Error: cannot reach registry: ${err.message}`);
3475
+ process.exit(1);
3476
+ }
3477
+ if (!remoteCard.gateway_url || typeof remoteCard.gateway_url !== "string") {
3478
+ console.error("Error: remote card has no gateway_url. The provider needs to re-publish with `agentbnb sync`.");
3479
+ process.exit(1);
3480
+ }
3481
+ gatewayUrl = remoteCard.gateway_url;
3482
+ token = "";
3483
+ isRemoteRequest = true;
3484
+ if (!opts.json) {
3485
+ const displayName = remoteCard.name ?? remoteCard.agent_name ?? cardId;
3486
+ console.log(`Found remote card: ${displayName} @ ${gatewayUrl}`);
3487
+ }
3488
+ }
2705
3489
  }
2706
- const useReceipt = isPeerRequest && opts.receipt !== false;
3490
+ const useReceipt = isRemoteRequest && opts.receipt !== false;
2707
3491
  if (useReceipt && !opts.cost) {
2708
- console.error("Error: --cost <credits> is required for peer requests. Specify the credits to commit.");
3492
+ console.error("Error: --cost <credits> is required for remote requests. Specify the credits to commit.");
2709
3493
  process.exit(1);
2710
3494
  }
2711
3495
  let escrowId;
2712
3496
  let escrowReceipt;
2713
3497
  if (useReceipt) {
2714
3498
  const configDir = getConfigDir();
2715
- const creditDb = openCreditDb(join2(configDir, "credit.db"));
3499
+ const creditDb = openCreditDb(join4(configDir, "credit.db"));
2716
3500
  creditDb.pragma("busy_timeout = 5000");
2717
3501
  try {
2718
3502
  const keys = loadKeyPair(configDir);
@@ -2753,7 +3537,7 @@ program.command("request [card-id]").description("Request a capability from anot
2753
3537
  });
2754
3538
  if (useReceipt && escrowId) {
2755
3539
  const configDir = getConfigDir();
2756
- const creditDb = openCreditDb(join2(configDir, "credit.db"));
3540
+ const creditDb = openCreditDb(join4(configDir, "credit.db"));
2757
3541
  creditDb.pragma("busy_timeout = 5000");
2758
3542
  try {
2759
3543
  settleRequesterEscrow(creditDb, escrowId);
@@ -2773,7 +3557,7 @@ program.command("request [card-id]").description("Request a capability from anot
2773
3557
  } catch (err) {
2774
3558
  if (useReceipt && escrowId) {
2775
3559
  const configDir = getConfigDir();
2776
- const creditDb = openCreditDb(join2(configDir, "credit.db"));
3560
+ const creditDb = openCreditDb(join4(configDir, "credit.db"));
2777
3561
  creditDb.pragma("busy_timeout = 5000");
2778
3562
  try {
2779
3563
  releaseRequesterEscrow(creditDb, escrowId);
@@ -2841,7 +3625,7 @@ program.command("serve").description("Start the AgentBnB gateway server").option
2841
3625
  }
2842
3626
  const port = opts.port ? parseInt(opts.port, 10) : config.gateway_port;
2843
3627
  const registryPort = parseInt(opts.registryPort, 10);
2844
- const skillsYamlPath = opts.skillsYaml ?? join2(homedir(), ".agentbnb", "skills.yaml");
3628
+ const skillsYamlPath = opts.skillsYaml ?? join4(homedir(), ".agentbnb", "skills.yaml");
2845
3629
  const runtime = new AgentRuntime({
2846
3630
  registryDbPath: config.db_path,
2847
3631
  creditDbPath: config.credit_db_path,
@@ -2920,7 +3704,7 @@ program.command("serve").description("Start the AgentBnB gateway server").option
2920
3704
  }
2921
3705
  if (opts.registry) {
2922
3706
  const { RelayClient } = await import("../websocket-client-5TIQDYQ4.js");
2923
- const { executeCapabilityRequest: executeCapabilityRequest2 } = await import("../execute-3T5RF3DP.js");
3707
+ const { executeCapabilityRequest: executeCapabilityRequest2 } = await import("../execute-HM25IOG7.js");
2924
3708
  const cards = listCards(runtime.registryDb, config.owner);
2925
3709
  const card = cards[0] ?? {
2926
3710
  id: config.owner,
@@ -3004,7 +3788,7 @@ peersCommand.option("--json", "Output as JSON").action(async (opts) => {
3004
3788
  console.log("No peers registered. Use `agentbnb connect` to add one.");
3005
3789
  return;
3006
3790
  }
3007
- const col = (s, w) => s.slice(0, w).padEnd(w);
3791
+ const col = (s, w) => (s ?? "").slice(0, w).padEnd(w);
3008
3792
  console.log(col("Name", 20) + " " + col("URL", 36) + " " + col("Added", 20));
3009
3793
  console.log("-".repeat(80));
3010
3794
  for (const peer of peers) {
@@ -3128,7 +3912,7 @@ openclaw.command("sync").description("Read SOUL.md and publish/update a v2.0 cap
3128
3912
  }
3129
3913
  let content;
3130
3914
  try {
3131
- content = readFileSync3(opts.soulPath, "utf-8");
3915
+ content = readFileSync5(opts.soulPath, "utf-8");
3132
3916
  } catch {
3133
3917
  console.error(`Error: cannot read SOUL.md at ${opts.soulPath}`);
3134
3918
  process.exit(1);
@@ -3189,7 +3973,7 @@ openclaw.command("rules").description("Print HEARTBEAT.md rules block (or inject
3189
3973
  }
3190
3974
  });
3191
3975
  program.command("conduct <task>").description("Orchestrate a complex task across the AgentBnB network").option("--plan-only", "Show execution plan without executing").option("--max-budget <credits>", "Maximum credits to spend", "100").option("--json", "Output as JSON").action(async (task, opts) => {
3192
- const { conductAction } = await import("../conduct-M57F72RK.js");
3976
+ const { conductAction } = await import("../conduct-5XKKALNX.js");
3193
3977
  const result = await conductAction(task, opts);
3194
3978
  if (opts.json) {
3195
3979
  console.log(JSON.stringify(result, null, 2));