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/{card-P5C36VBD.js → card-IE5UV5QX.js} +1 -1
- package/dist/{chunk-QVIGMCHA.js → chunk-KTHJ5F3X.js} +2 -2
- package/dist/{chunk-T7ZJPQHD.js → chunk-P35546JW.js} +1 -1
- package/dist/{chunk-ZJCIBK6O.js → chunk-QVV2P3FN.js} +1 -1
- package/dist/{chunk-4Q7D24DP.js → chunk-SVEZBIGE.js} +3 -3
- package/dist/{chunk-2LLXUKMY.js → chunk-UOGDK2S2.js} +1 -1
- package/dist/{chunk-TQMI73LL.js → chunk-XA63SD4T.js} +5 -0
- package/dist/cli/index.js +939 -155
- package/dist/{conduct-M57F72RK.js → conduct-5XKKALNX.js} +5 -5
- package/dist/{conductor-mode-CF6PSRRA.js → conductor-mode-6MIVMFBC.js} +4 -4
- package/dist/execute-HM25IOG7.js +9 -0
- package/dist/index.d.ts +3780 -0
- package/dist/index.js +739 -51
- package/package.json +13 -1
- package/dist/execute-3T5RF3DP.js +0 -9
package/dist/cli/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
saveKeyPair,
|
|
8
8
|
settleRequesterEscrow,
|
|
9
9
|
signEscrowReceipt
|
|
10
|
-
} from "../chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
57
|
+
} from "../chunk-QVV2P3FN.js";
|
|
58
58
|
import {
|
|
59
59
|
AgentBnBError,
|
|
60
|
-
|
|
60
|
+
AnyCardSchema,
|
|
61
61
|
CapabilityCardV2Schema
|
|
62
|
-
} from "../chunk-
|
|
62
|
+
} from "../chunk-XA63SD4T.js";
|
|
63
63
|
|
|
64
64
|
// src/cli/index.ts
|
|
65
65
|
import { Command } from "commander";
|
|
66
|
-
import { readFileSync as
|
|
66
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
67
67
|
import { createRequire } from "module";
|
|
68
68
|
import { randomBytes } from "crypto";
|
|
69
|
-
import { join as
|
|
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/
|
|
73
|
+
// src/identity/identity.ts
|
|
74
74
|
import { z } from "zod";
|
|
75
|
-
import {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
789
|
+
import { z as z3 } from "zod";
|
|
535
790
|
import yaml from "js-yaml";
|
|
536
|
-
var PricingSchema =
|
|
537
|
-
credits_per_call:
|
|
538
|
-
credits_per_minute:
|
|
539
|
-
free_tier:
|
|
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 =
|
|
542
|
-
|
|
543
|
-
type:
|
|
544
|
-
token:
|
|
796
|
+
var ApiAuthSchema = z3.discriminatedUnion("type", [
|
|
797
|
+
z3.object({
|
|
798
|
+
type: z3.literal("bearer"),
|
|
799
|
+
token: z3.string()
|
|
545
800
|
}),
|
|
546
|
-
|
|
547
|
-
type:
|
|
548
|
-
header:
|
|
549
|
-
key:
|
|
801
|
+
z3.object({
|
|
802
|
+
type: z3.literal("apikey"),
|
|
803
|
+
header: z3.string().default("X-API-Key"),
|
|
804
|
+
key: z3.string()
|
|
550
805
|
}),
|
|
551
|
-
|
|
552
|
-
type:
|
|
553
|
-
username:
|
|
554
|
-
password:
|
|
806
|
+
z3.object({
|
|
807
|
+
type: z3.literal("basic"),
|
|
808
|
+
username: z3.string(),
|
|
809
|
+
password: z3.string()
|
|
555
810
|
})
|
|
556
811
|
]);
|
|
557
|
-
var ApiSkillConfigSchema =
|
|
558
|
-
id:
|
|
559
|
-
type:
|
|
560
|
-
name:
|
|
561
|
-
endpoint:
|
|
562
|
-
method:
|
|
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:
|
|
565
|
-
output_mapping:
|
|
819
|
+
input_mapping: z3.record(z3.string()).default({}),
|
|
820
|
+
output_mapping: z3.record(z3.string()).default({}),
|
|
566
821
|
pricing: PricingSchema,
|
|
567
|
-
timeout_ms:
|
|
568
|
-
retries:
|
|
569
|
-
provider:
|
|
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 =
|
|
572
|
-
|
|
573
|
-
skill_id:
|
|
574
|
-
input_mapping:
|
|
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
|
-
|
|
577
|
-
command:
|
|
578
|
-
input_mapping:
|
|
831
|
+
z3.object({
|
|
832
|
+
command: z3.string().min(1),
|
|
833
|
+
input_mapping: z3.record(z3.string()).default({})
|
|
579
834
|
})
|
|
580
835
|
]);
|
|
581
|
-
var PipelineSkillConfigSchema =
|
|
582
|
-
id:
|
|
583
|
-
type:
|
|
584
|
-
name:
|
|
585
|
-
steps:
|
|
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:
|
|
842
|
+
timeout_ms: z3.number().positive().optional()
|
|
588
843
|
});
|
|
589
|
-
var OpenClawSkillConfigSchema =
|
|
590
|
-
id:
|
|
591
|
-
type:
|
|
592
|
-
name:
|
|
593
|
-
agent_name:
|
|
594
|
-
channel:
|
|
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:
|
|
851
|
+
timeout_ms: z3.number().positive().optional()
|
|
597
852
|
});
|
|
598
|
-
var CommandSkillConfigSchema =
|
|
599
|
-
id:
|
|
600
|
-
type:
|
|
601
|
-
name:
|
|
602
|
-
command:
|
|
603
|
-
output_type:
|
|
604
|
-
allowed_commands:
|
|
605
|
-
working_dir:
|
|
606
|
-
timeout_ms:
|
|
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 =
|
|
610
|
-
id:
|
|
611
|
-
type:
|
|
612
|
-
name:
|
|
613
|
-
conductor_skill:
|
|
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:
|
|
870
|
+
timeout_ms: z3.number().positive().optional()
|
|
616
871
|
});
|
|
617
|
-
var SkillConfigSchema =
|
|
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 =
|
|
625
|
-
skills:
|
|
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 &&
|
|
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 =
|
|
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-
|
|
1274
|
-
const { registerConductorCard, CONDUCTOR_OWNER } = await import("../card-
|
|
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
|
|
1728
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1474
1729
|
|
|
1475
1730
|
// src/relay/websocket-relay.ts
|
|
1476
|
-
import { randomUUID as
|
|
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:
|
|
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 }, (
|
|
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
|
-
|
|
2114
|
+
join3(__dirname, "../../hub/dist"),
|
|
1740
2115
|
// When running from dist/registry/server.js
|
|
1741
|
-
|
|
2116
|
+
join3(__dirname, "../../../hub/dist")
|
|
1742
2117
|
// Fallback for alternative layouts
|
|
1743
2118
|
];
|
|
1744
|
-
const hubDistDir = hubDistCandidates.find((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
|
-
|
|
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
|
|
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.
|
|
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
|
|
2661
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
2118
2662
|
|
|
2119
2663
|
// src/skills/publish-capability.ts
|
|
2120
|
-
import { randomUUID as
|
|
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 :
|
|
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 ??
|
|
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
|
|
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 (!
|
|
2295
|
-
|
|
2838
|
+
if (!existsSync5(heartbeatPath)) {
|
|
2839
|
+
writeFileSync2(heartbeatPath, section + "\n", "utf-8");
|
|
2296
2840
|
return;
|
|
2297
2841
|
}
|
|
2298
|
-
let content =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
2374
|
-
const creditDbPath =
|
|
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
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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({
|
|
3259
|
+
console.log(JSON.stringify({ synced, failed, registry: registryUrl, results }, null, 2));
|
|
2520
3260
|
} else {
|
|
2521
|
-
console.log(`
|
|
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(
|
|
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(
|
|
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(
|
|
2656
|
-
const creditDb = openCreditDb(
|
|
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
|
-
|
|
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
|
-
|
|
2704
|
-
|
|
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 =
|
|
3490
|
+
const useReceipt = isRemoteRequest && opts.receipt !== false;
|
|
2707
3491
|
if (useReceipt && !opts.cost) {
|
|
2708
|
-
console.error("Error: --cost <credits> is required for
|
|
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(
|
|
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(
|
|
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(
|
|
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 ??
|
|
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-
|
|
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 =
|
|
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-
|
|
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));
|