agentbnb 3.1.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{card-P5C36VBD.js → card-EWIXC377.js} +1 -1
- package/dist/{chunk-QVIGMCHA.js → chunk-2ETVQXP7.js} +2 -2
- package/dist/{chunk-TQMI73LL.js → chunk-7RU5INZI.js} +1 -0
- package/dist/{chunk-4Q7D24DP.js → chunk-MGHI67GR.js} +3 -3
- package/dist/{chunk-ZJCIBK6O.js → chunk-MZCNJ5PY.js} +1 -1
- package/dist/{chunk-2LLXUKMY.js → chunk-QAY6XTT7.js} +1 -1
- package/dist/{chunk-T7ZJPQHD.js → chunk-VCW7IDJM.js} +1 -1
- package/dist/cli/index.js +822 -151
- package/dist/{conduct-M57F72RK.js → conduct-5T3LGXMF.js} +5 -5
- package/dist/{conductor-mode-CF6PSRRA.js → conductor-mode-GPLAM2XO.js} +4 -4
- package/dist/execute-NZXTSSVV.js +9 -0
- package/dist/index.d.ts +3772 -0
- package/dist/index.js +735 -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-MGHI67GR.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-2ETVQXP7.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-QAY6XTT7.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-MZCNJ5PY.js";
|
|
58
58
|
import {
|
|
59
59
|
AgentBnBError,
|
|
60
|
-
|
|
60
|
+
AnyCardSchema,
|
|
61
61
|
CapabilityCardV2Schema
|
|
62
|
-
} from "../chunk-
|
|
62
|
+
} from "../chunk-7RU5INZI.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-GPLAM2XO.js");
|
|
1529
|
+
const { registerConductorCard, CONDUCTOR_OWNER } = await import("../card-EWIXC377.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") {
|
|
2955
|
+
if (!opts.json) {
|
|
2956
|
+
console.log(` Found SOUL.md \u2014 extracting capabilities...`);
|
|
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 ?? [];
|
|
2408
3015
|
if (!opts.json) {
|
|
2409
|
-
console.log(`
|
|
2410
|
-
Detected ${detectedKeys.length} API key${detectedKeys.length > 1 ? "s" : ""}: ${detectedKeys.join(", ")}`);
|
|
3016
|
+
console.log(` Detected ${detectedKeys.length} API key${detectedKeys.length > 1 ? "s" : ""}: ${detectedKeys.join(", ")}`);
|
|
2411
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,56 @@ 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;
|
|
2512
3143
|
const db = openDatabase(config.db_path);
|
|
2513
3144
|
try {
|
|
2514
|
-
|
|
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
|
+
}
|
|
2515
3154
|
} finally {
|
|
2516
3155
|
db.close();
|
|
2517
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
|
+
try {
|
|
3165
|
+
const response = await fetch(url, {
|
|
3166
|
+
method: "POST",
|
|
3167
|
+
headers: { "Content-Type": "application/json" },
|
|
3168
|
+
body: JSON.stringify(card)
|
|
3169
|
+
});
|
|
3170
|
+
if (!response.ok) {
|
|
3171
|
+
const body = await response.text();
|
|
3172
|
+
console.error(`Warning: remote registry returned ${response.status}: ${body}`);
|
|
3173
|
+
} else {
|
|
3174
|
+
remoteSuccess = true;
|
|
3175
|
+
if (!opts.json) {
|
|
3176
|
+
console.log(`Published to registry: ${url}`);
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
} catch (err) {
|
|
3180
|
+
console.error(`Warning: cannot reach registry at ${url}: ${err.message}`);
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
2518
3183
|
if (opts.json) {
|
|
2519
|
-
console.log(JSON.stringify({
|
|
2520
|
-
|
|
2521
|
-
|
|
3184
|
+
console.log(JSON.stringify({
|
|
3185
|
+
success: true,
|
|
3186
|
+
id: card.id,
|
|
3187
|
+
name: cardName,
|
|
3188
|
+
...registryUrl ? { registry: registryUrl, remote_published: remoteSuccess } : {}
|
|
3189
|
+
}, null, 2));
|
|
3190
|
+
} else if (!registryUrl) {
|
|
2522
3191
|
}
|
|
2523
3192
|
});
|
|
2524
3193
|
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 +3206,7 @@ program.command("discover [query]").description("Search available capabilities i
|
|
|
2537
3206
|
console.log("No agents found on local network.");
|
|
2538
3207
|
return;
|
|
2539
3208
|
}
|
|
2540
|
-
const col2 = (s, w) => s.slice(0, w).padEnd(w);
|
|
3209
|
+
const col2 = (s, w) => (s ?? "").slice(0, w).padEnd(w);
|
|
2541
3210
|
console.log(col2("Name", 24) + " " + col2("URL", 32) + " " + col2("Owner", 20));
|
|
2542
3211
|
console.log("-".repeat(80));
|
|
2543
3212
|
for (const agent of discovered) {
|
|
@@ -2607,7 +3276,7 @@ ${discovered.length} agent(s) found on local network`);
|
|
|
2607
3276
|
console.log("No capabilities found.");
|
|
2608
3277
|
return;
|
|
2609
3278
|
}
|
|
2610
|
-
const col = (s, w) => s.slice(0, w).padEnd(w);
|
|
3279
|
+
const col = (s, w) => (s ?? "").slice(0, w).padEnd(w);
|
|
2611
3280
|
if (hasRemote) {
|
|
2612
3281
|
console.log(
|
|
2613
3282
|
col("ID", 16) + " " + col("Name", 28) + " " + col("Lvl", 3) + " " + col("Credits", 7) + " " + col("Online", 6) + " " + col("Source", 8)
|
|
@@ -2615,10 +3284,11 @@ ${discovered.length} agent(s) found on local network`);
|
|
|
2615
3284
|
console.log("-".repeat(80));
|
|
2616
3285
|
for (const card of outputCards) {
|
|
2617
3286
|
const shortId = card.id.slice(0, 8) + "...";
|
|
3287
|
+
const displayName = card.name ?? card.agent_name ?? "";
|
|
2618
3288
|
const source = "source" in card ? card.source : "local";
|
|
2619
3289
|
const sourceTag = source === "remote" ? "[remote]" : "[local]";
|
|
2620
3290
|
console.log(
|
|
2621
|
-
col(shortId, 16) + " " + col(
|
|
3291
|
+
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
3292
|
);
|
|
2623
3293
|
}
|
|
2624
3294
|
} else {
|
|
@@ -2628,8 +3298,9 @@ ${discovered.length} agent(s) found on local network`);
|
|
|
2628
3298
|
console.log("-".repeat(72));
|
|
2629
3299
|
for (const card of outputCards) {
|
|
2630
3300
|
const shortId = card.id.slice(0, 8) + "...";
|
|
3301
|
+
const displayName = card.name ?? card.agent_name ?? "";
|
|
2631
3302
|
console.log(
|
|
2632
|
-
col(shortId, 16) + " " + col(
|
|
3303
|
+
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
3304
|
);
|
|
2634
3305
|
}
|
|
2635
3306
|
}
|
|
@@ -2652,8 +3323,8 @@ program.command("request [card-id]").description("Request a capability from anot
|
|
|
2652
3323
|
process.exit(1);
|
|
2653
3324
|
}
|
|
2654
3325
|
}
|
|
2655
|
-
const registryDb = openDatabase(
|
|
2656
|
-
const creditDb = openCreditDb(
|
|
3326
|
+
const registryDb = openDatabase(join4(getConfigDir(), "registry.db"));
|
|
3327
|
+
const creditDb = openCreditDb(join4(getConfigDir(), "credit.db"));
|
|
2657
3328
|
registryDb.pragma("busy_timeout = 5000");
|
|
2658
3329
|
creditDb.pragma("busy_timeout = 5000");
|
|
2659
3330
|
try {
|
|
@@ -2712,7 +3383,7 @@ program.command("request [card-id]").description("Request a capability from anot
|
|
|
2712
3383
|
let escrowReceipt;
|
|
2713
3384
|
if (useReceipt) {
|
|
2714
3385
|
const configDir = getConfigDir();
|
|
2715
|
-
const creditDb = openCreditDb(
|
|
3386
|
+
const creditDb = openCreditDb(join4(configDir, "credit.db"));
|
|
2716
3387
|
creditDb.pragma("busy_timeout = 5000");
|
|
2717
3388
|
try {
|
|
2718
3389
|
const keys = loadKeyPair(configDir);
|
|
@@ -2753,7 +3424,7 @@ program.command("request [card-id]").description("Request a capability from anot
|
|
|
2753
3424
|
});
|
|
2754
3425
|
if (useReceipt && escrowId) {
|
|
2755
3426
|
const configDir = getConfigDir();
|
|
2756
|
-
const creditDb = openCreditDb(
|
|
3427
|
+
const creditDb = openCreditDb(join4(configDir, "credit.db"));
|
|
2757
3428
|
creditDb.pragma("busy_timeout = 5000");
|
|
2758
3429
|
try {
|
|
2759
3430
|
settleRequesterEscrow(creditDb, escrowId);
|
|
@@ -2773,7 +3444,7 @@ program.command("request [card-id]").description("Request a capability from anot
|
|
|
2773
3444
|
} catch (err) {
|
|
2774
3445
|
if (useReceipt && escrowId) {
|
|
2775
3446
|
const configDir = getConfigDir();
|
|
2776
|
-
const creditDb = openCreditDb(
|
|
3447
|
+
const creditDb = openCreditDb(join4(configDir, "credit.db"));
|
|
2777
3448
|
creditDb.pragma("busy_timeout = 5000");
|
|
2778
3449
|
try {
|
|
2779
3450
|
releaseRequesterEscrow(creditDb, escrowId);
|
|
@@ -2841,7 +3512,7 @@ program.command("serve").description("Start the AgentBnB gateway server").option
|
|
|
2841
3512
|
}
|
|
2842
3513
|
const port = opts.port ? parseInt(opts.port, 10) : config.gateway_port;
|
|
2843
3514
|
const registryPort = parseInt(opts.registryPort, 10);
|
|
2844
|
-
const skillsYamlPath = opts.skillsYaml ??
|
|
3515
|
+
const skillsYamlPath = opts.skillsYaml ?? join4(homedir(), ".agentbnb", "skills.yaml");
|
|
2845
3516
|
const runtime = new AgentRuntime({
|
|
2846
3517
|
registryDbPath: config.db_path,
|
|
2847
3518
|
creditDbPath: config.credit_db_path,
|
|
@@ -2920,7 +3591,7 @@ program.command("serve").description("Start the AgentBnB gateway server").option
|
|
|
2920
3591
|
}
|
|
2921
3592
|
if (opts.registry) {
|
|
2922
3593
|
const { RelayClient } = await import("../websocket-client-5TIQDYQ4.js");
|
|
2923
|
-
const { executeCapabilityRequest: executeCapabilityRequest2 } = await import("../execute-
|
|
3594
|
+
const { executeCapabilityRequest: executeCapabilityRequest2 } = await import("../execute-NZXTSSVV.js");
|
|
2924
3595
|
const cards = listCards(runtime.registryDb, config.owner);
|
|
2925
3596
|
const card = cards[0] ?? {
|
|
2926
3597
|
id: config.owner,
|
|
@@ -3004,7 +3675,7 @@ peersCommand.option("--json", "Output as JSON").action(async (opts) => {
|
|
|
3004
3675
|
console.log("No peers registered. Use `agentbnb connect` to add one.");
|
|
3005
3676
|
return;
|
|
3006
3677
|
}
|
|
3007
|
-
const col = (s, w) => s.slice(0, w).padEnd(w);
|
|
3678
|
+
const col = (s, w) => (s ?? "").slice(0, w).padEnd(w);
|
|
3008
3679
|
console.log(col("Name", 20) + " " + col("URL", 36) + " " + col("Added", 20));
|
|
3009
3680
|
console.log("-".repeat(80));
|
|
3010
3681
|
for (const peer of peers) {
|
|
@@ -3128,7 +3799,7 @@ openclaw.command("sync").description("Read SOUL.md and publish/update a v2.0 cap
|
|
|
3128
3799
|
}
|
|
3129
3800
|
let content;
|
|
3130
3801
|
try {
|
|
3131
|
-
content =
|
|
3802
|
+
content = readFileSync5(opts.soulPath, "utf-8");
|
|
3132
3803
|
} catch {
|
|
3133
3804
|
console.error(`Error: cannot read SOUL.md at ${opts.soulPath}`);
|
|
3134
3805
|
process.exit(1);
|
|
@@ -3189,7 +3860,7 @@ openclaw.command("rules").description("Print HEARTBEAT.md rules block (or inject
|
|
|
3189
3860
|
}
|
|
3190
3861
|
});
|
|
3191
3862
|
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-
|
|
3863
|
+
const { conductAction } = await import("../conduct-5T3LGXMF.js");
|
|
3193
3864
|
const result = await conductAction(task, opts);
|
|
3194
3865
|
if (opts.json) {
|
|
3195
3866
|
console.log(JSON.stringify(result, null, 2));
|