agentbnb 5.1.11 → 7.0.0-beta.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/README.md +245 -39
- package/dist/{card-RSGDCHCV.js → card-REW7BSWW.js} +1 -1
- package/dist/{chunk-FLY3WIQR.js → chunk-2HSUPCBT.js} +3 -3
- package/dist/{chunk-WGZ5AGOX.js → chunk-3CIMVISQ.js} +24 -1
- package/dist/{chunk-NH2FIERR.js → chunk-574W3HHE.js} +1 -1
- package/dist/{chunk-WTXRY7R2.js → chunk-APEG4QIN.js} +157 -9
- package/dist/chunk-BP3L2TET.js +148 -0
- package/dist/{chunk-NLAWT4DT.js → chunk-CWYPTQRQ.js} +7 -7
- package/dist/{chunk-UKT6H7YT.js → chunk-DUW6RX6I.js} +5 -2
- package/dist/chunk-EAD4A4KG.js +430 -0
- package/dist/{chunk-QT7TEVNV.js → chunk-EHSHB7TY.js} +23 -1
- package/dist/{chunk-B5FTAGFN.js → chunk-ETGOKDFR.js} +75 -75
- package/dist/{chunk-5KFI5X7B.js → chunk-F53QQIM2.js} +1 -1
- package/dist/{chunk-MLS6IGGG.js → chunk-FK2MDNTB.js} +117 -117
- package/dist/{chunk-EGUOAHCW.js → chunk-GO4FVRVN.js} +15 -13
- package/dist/{chunk-CRFCWD6V.js → chunk-J2K5S5MX.js} +136 -173
- package/dist/chunk-K5FO42YF.js +1136 -0
- package/dist/{chunk-DFBX3BBD.js → chunk-KA2VIEGM.js} +211 -16
- package/dist/chunk-NWIQJ2CL.js +108 -0
- package/dist/chunk-OCSU2S6W.js +168 -0
- package/dist/{chunk-QQFBFV4V.js → chunk-PGDBUUGR.js} +60 -19
- package/dist/{chunk-QITOPASZ.js → chunk-PSQHUZ7X.js} +1 -1
- package/dist/{chunk-C6KPAFCC.js → chunk-PU7LXOQ3.js} +23 -1
- package/dist/{chunk-JOY533UH.js → chunk-TW65F5EU.js} +1 -1
- package/dist/{chunk-ZX5623ER.js → chunk-VMH2YS2I.js} +1 -1
- package/dist/{chunk-XND2DWTZ.js → chunk-VPQ44XKE.js} +2 -2
- package/dist/{chunk-CSATDXZC.js → chunk-Y7T6IMM3.js} +1 -1
- package/dist/cli/index.js +755 -379
- package/dist/{client-T5MTY3CS.js → client-HRYRJKSA.js} +3 -3
- package/dist/{conduct-WU3VEXB6.js → conduct-JNYJCDHQ.js} +14 -13
- package/dist/conduct-KJUD2RTB.js +22 -0
- package/dist/{conductor-mode-ZMTFZGJP.js → conductor-mode-2VVFMKVE.js} +313 -14
- package/dist/conductor-mode-VGUU54QI.js +276 -0
- package/dist/execute-I4PKSNJM.js +12 -0
- package/dist/execute-MOXSSA3Q.js +15 -0
- package/dist/index.d.ts +795 -2
- package/dist/index.js +861 -111
- package/dist/{process-guard-CC7CNRQJ.js → process-guard-QCCBGILS.js} +1 -1
- package/dist/publish-capability-TS6CNR5G.js +12 -0
- package/dist/reliability-metrics-QG7WC5QK.js +18 -0
- package/dist/{request-VOXBFUOG.js → request-E7TA7COA.js} +19 -18
- package/dist/{serve-skill-IH7UAJNR.js → serve-skill-HIOWYKRU.js} +13 -11
- package/dist/{server-JVQW2TID.js → server-I63CXFX3.js} +17 -16
- package/dist/{service-coordinator-EYRDTHL5.js → service-coordinator-XBNT3SMU.js} +369 -260
- package/dist/skill-config-FETXPNVP.js +22 -0
- package/dist/skills/agentbnb/bootstrap.js +430 -84
- package/dist/websocket-client-5MH6QRJK.js +7 -0
- package/dist/{websocket-client-WRN3HO73.js → websocket-client-PFGVTXNE.js} +1 -1
- package/openclaw.plugin.json +2 -2
- package/package.json +2 -1
- package/skills/agentbnb/SKILL.md +35 -0
- package/skills/agentbnb/bootstrap.ts +126 -8
- package/skills/agentbnb/install.sh +49 -9
- package/dist/chunk-EANI2N2V.js +0 -309
- package/dist/chunk-EPIWHNB2.js +0 -946
- package/dist/conduct-6LKIJJKQ.js +0 -21
- package/dist/conductor-mode-Q4IIDY5E.js +0 -123
- package/dist/execute-4D4ITQCL.js +0 -10
- package/dist/execute-T7Y6RKSW.js +0 -13
- package/dist/websocket-client-6IIDGXKB.js +0 -7
package/dist/cli/index.js
CHANGED
|
@@ -6,32 +6,54 @@ import {
|
|
|
6
6
|
detectOpenPorts,
|
|
7
7
|
discoverLocalAgents,
|
|
8
8
|
getPricingStats
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-FK2MDNTB.js";
|
|
10
10
|
import {
|
|
11
11
|
ensureIdentity
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-PSQHUZ7X.js";
|
|
13
|
+
import {
|
|
14
|
+
releaseRequesterEscrow,
|
|
15
|
+
settleRequesterEscrow
|
|
16
|
+
} from "../chunk-DUW6RX6I.js";
|
|
13
17
|
import {
|
|
14
18
|
createLedger
|
|
15
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-2HSUPCBT.js";
|
|
20
|
+
import {
|
|
21
|
+
parseSoulMd
|
|
22
|
+
} from "../chunk-BP3L2TET.js";
|
|
16
23
|
import {
|
|
17
24
|
AutoRequestor,
|
|
18
25
|
BudgetManager,
|
|
19
26
|
DEFAULT_BUDGET_CONFIG
|
|
20
|
-
} from "../chunk-
|
|
21
|
-
import {
|
|
22
|
-
DEFAULT_AUTONOMY_CONFIG
|
|
23
|
-
} from "../chunk-CSATDXZC.js";
|
|
27
|
+
} from "../chunk-GO4FVRVN.js";
|
|
24
28
|
import {
|
|
25
29
|
fetchRemoteCards,
|
|
26
30
|
mergeResults
|
|
27
|
-
} from "../chunk-
|
|
31
|
+
} from "../chunk-VMH2YS2I.js";
|
|
32
|
+
import {
|
|
33
|
+
DEFAULT_AUTONOMY_CONFIG
|
|
34
|
+
} from "../chunk-Y7T6IMM3.js";
|
|
28
35
|
import {
|
|
29
36
|
filterCards,
|
|
30
37
|
searchCards
|
|
31
|
-
} from "../chunk-
|
|
38
|
+
} from "../chunk-574W3HHE.js";
|
|
39
|
+
import {
|
|
40
|
+
bootstrapAgent,
|
|
41
|
+
getBalance,
|
|
42
|
+
getTransactions,
|
|
43
|
+
holdEscrow,
|
|
44
|
+
migrateOwner,
|
|
45
|
+
openCreditDb
|
|
46
|
+
} from "../chunk-J2K5S5MX.js";
|
|
47
|
+
import "../chunk-NWIQJ2CL.js";
|
|
32
48
|
import {
|
|
33
49
|
requestCapability
|
|
34
|
-
} from "../chunk-
|
|
50
|
+
} from "../chunk-VPQ44XKE.js";
|
|
51
|
+
import {
|
|
52
|
+
generateKeyPair,
|
|
53
|
+
loadKeyPair,
|
|
54
|
+
saveKeyPair,
|
|
55
|
+
signEscrowReceipt
|
|
56
|
+
} from "../chunk-F53QQIM2.js";
|
|
35
57
|
import {
|
|
36
58
|
findPeer,
|
|
37
59
|
loadPeers,
|
|
@@ -44,42 +66,26 @@ import {
|
|
|
44
66
|
saveConfig
|
|
45
67
|
} from "../chunk-75OC6E4F.js";
|
|
46
68
|
import {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
} from "../chunk-UKT6H7YT.js";
|
|
50
|
-
import {
|
|
69
|
+
deleteCard,
|
|
70
|
+
getCard,
|
|
51
71
|
insertCard,
|
|
52
72
|
listCards,
|
|
53
73
|
openDatabase
|
|
54
|
-
} from "../chunk-
|
|
55
|
-
import {
|
|
56
|
-
bootstrapAgent,
|
|
57
|
-
getBalance,
|
|
58
|
-
getTransactions,
|
|
59
|
-
holdEscrow,
|
|
60
|
-
migrateOwner,
|
|
61
|
-
openCreditDb
|
|
62
|
-
} from "../chunk-EANI2N2V.js";
|
|
63
|
-
import {
|
|
64
|
-
generateKeyPair,
|
|
65
|
-
loadKeyPair,
|
|
66
|
-
saveKeyPair,
|
|
67
|
-
signEscrowReceipt
|
|
68
|
-
} from "../chunk-5KFI5X7B.js";
|
|
74
|
+
} from "../chunk-KA2VIEGM.js";
|
|
69
75
|
import {
|
|
70
76
|
AgentBnBError,
|
|
71
77
|
AnyCardSchema,
|
|
72
78
|
CapabilityCardV2Schema
|
|
73
|
-
} from "../chunk-
|
|
79
|
+
} from "../chunk-3CIMVISQ.js";
|
|
74
80
|
|
|
75
81
|
// src/cli/index.ts
|
|
76
82
|
import { Command } from "commander";
|
|
77
|
-
import { readFileSync as
|
|
83
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
78
84
|
import { createRequire } from "module";
|
|
79
|
-
import {
|
|
80
|
-
import { join as
|
|
81
|
-
import { networkInterfaces } from "os";
|
|
82
|
-
import { createInterface as
|
|
85
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
86
|
+
import { join as join4 } from "path";
|
|
87
|
+
import { networkInterfaces as networkInterfaces2 } from "os";
|
|
88
|
+
import { createInterface as createInterface3 } from "readline";
|
|
83
89
|
|
|
84
90
|
// src/credit/escrow-receipt.ts
|
|
85
91
|
import { z } from "zod";
|
|
@@ -113,9 +119,214 @@ function createSignedEscrowReceipt(db, privateKey, publicKey, opts) {
|
|
|
113
119
|
return { escrowId, receipt };
|
|
114
120
|
}
|
|
115
121
|
|
|
116
|
-
// src/
|
|
122
|
+
// src/openclaw/soul-sync.ts
|
|
117
123
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
118
|
-
|
|
124
|
+
var SKILL_META_GLOBAL_RE = /(?:^|\s)-\s*(capability_types|requires(?:_capabilities)?|visibility)\s*:\s*([^-][^]*?)(?=\s+-\s+(?:capability_types|requires(?:_capabilities)?|visibility)\s*:|$)/gi;
|
|
125
|
+
function extractSkillMeta(raw) {
|
|
126
|
+
let capability_types;
|
|
127
|
+
let requires_capabilities;
|
|
128
|
+
let visibility;
|
|
129
|
+
const removedRanges = [];
|
|
130
|
+
SKILL_META_GLOBAL_RE.lastIndex = 0;
|
|
131
|
+
let m;
|
|
132
|
+
while ((m = SKILL_META_GLOBAL_RE.exec(raw)) !== null) {
|
|
133
|
+
const key = m[1].toLowerCase();
|
|
134
|
+
const val = m[2].trim();
|
|
135
|
+
if (key === "capability_types") {
|
|
136
|
+
capability_types = val.split(",").map((v) => v.trim()).filter(Boolean);
|
|
137
|
+
} else if (key === "requires" || key === "requires_capabilities") {
|
|
138
|
+
requires_capabilities = val.split(",").map((v) => v.trim()).filter(Boolean);
|
|
139
|
+
} else if (key === "visibility") {
|
|
140
|
+
const vis = val.toLowerCase();
|
|
141
|
+
if (vis === "public" || vis === "private") {
|
|
142
|
+
visibility = vis;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
removedRanges.push({ start: m.index, end: m.index + m[0].length });
|
|
146
|
+
}
|
|
147
|
+
let description = raw;
|
|
148
|
+
for (const { start, end } of removedRanges.slice().reverse()) {
|
|
149
|
+
description = description.slice(0, start) + description.slice(end);
|
|
150
|
+
}
|
|
151
|
+
description = description.trim();
|
|
152
|
+
return { description, capability_types, requires_capabilities, visibility };
|
|
153
|
+
}
|
|
154
|
+
function parseSoulMdV2(content) {
|
|
155
|
+
const parsed = parseSoulMd(content);
|
|
156
|
+
const skills = parsed.capabilities.map((cap) => {
|
|
157
|
+
const sanitizedId = cap.name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
158
|
+
const id = sanitizedId.length > 0 ? sanitizedId : randomUUID2();
|
|
159
|
+
const { description: cleanDesc, capability_types, requires_capabilities, visibility } = extractSkillMeta(cap.description);
|
|
160
|
+
const finalDescription = (cleanDesc || cap.name).slice(0, 500);
|
|
161
|
+
const skill = {
|
|
162
|
+
id,
|
|
163
|
+
name: cap.name,
|
|
164
|
+
description: finalDescription,
|
|
165
|
+
level: 2,
|
|
166
|
+
inputs: [
|
|
167
|
+
{
|
|
168
|
+
name: "input",
|
|
169
|
+
type: "text",
|
|
170
|
+
description: "Input for the skill",
|
|
171
|
+
required: true
|
|
172
|
+
}
|
|
173
|
+
],
|
|
174
|
+
outputs: [
|
|
175
|
+
{
|
|
176
|
+
name: "output",
|
|
177
|
+
type: "text",
|
|
178
|
+
description: "Output from the skill",
|
|
179
|
+
required: true
|
|
180
|
+
}
|
|
181
|
+
],
|
|
182
|
+
pricing: { credits_per_call: cap.pricing !== void 0 ? cap.pricing : 10 },
|
|
183
|
+
availability: { online: true }
|
|
184
|
+
};
|
|
185
|
+
if (capability_types !== void 0) skill.capability_types = capability_types;
|
|
186
|
+
if (requires_capabilities !== void 0) skill.requires_capabilities = requires_capabilities;
|
|
187
|
+
if (visibility !== void 0) skill.visibility = visibility;
|
|
188
|
+
return skill;
|
|
189
|
+
});
|
|
190
|
+
return {
|
|
191
|
+
agentName: parsed.name || "Unknown Agent",
|
|
192
|
+
description: parsed.description,
|
|
193
|
+
skills
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function publishFromSoulV2(db, soulContent, owner, sharedSkills) {
|
|
197
|
+
const { agentName, skills: allSkills } = parseSoulMdV2(soulContent);
|
|
198
|
+
const skills = allSkills.filter((skill) => {
|
|
199
|
+
if (sharedSkills && sharedSkills.length > 0) {
|
|
200
|
+
return sharedSkills.includes(skill.id);
|
|
201
|
+
}
|
|
202
|
+
return skill.visibility !== "private";
|
|
203
|
+
});
|
|
204
|
+
if (skills.length === 0) {
|
|
205
|
+
throw new AgentBnBError("SOUL.md has no H2 sections", "VALIDATION_ERROR");
|
|
206
|
+
}
|
|
207
|
+
const existingCards = listCards(db, owner);
|
|
208
|
+
const existingV2 = existingCards.find(
|
|
209
|
+
(c) => c.spec_version === "2.0"
|
|
210
|
+
);
|
|
211
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
212
|
+
const cardId = existingV2?.id ?? randomUUID2();
|
|
213
|
+
const card = {
|
|
214
|
+
spec_version: "2.0",
|
|
215
|
+
id: cardId,
|
|
216
|
+
owner,
|
|
217
|
+
agent_name: agentName,
|
|
218
|
+
skills,
|
|
219
|
+
availability: { online: true },
|
|
220
|
+
created_at: existingV2?.created_at ?? now,
|
|
221
|
+
updated_at: now
|
|
222
|
+
};
|
|
223
|
+
CapabilityCardV2Schema.parse(card);
|
|
224
|
+
if (existingV2) {
|
|
225
|
+
db.prepare(
|
|
226
|
+
"UPDATE capability_cards SET data = ?, updated_at = ? WHERE id = ?"
|
|
227
|
+
).run(JSON.stringify(card), now, cardId);
|
|
228
|
+
} else {
|
|
229
|
+
db.prepare(
|
|
230
|
+
"INSERT INTO capability_cards (id, owner, data, created_at, updated_at) VALUES (?, ?, ?, ?, ?)"
|
|
231
|
+
).run(cardId, owner, JSON.stringify(card), now, now);
|
|
232
|
+
}
|
|
233
|
+
return card;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// src/openclaw/heartbeat-writer.ts
|
|
237
|
+
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
238
|
+
var HEARTBEAT_MARKER_START = "<!-- agentbnb:start -->";
|
|
239
|
+
var HEARTBEAT_MARKER_END = "<!-- agentbnb:end -->";
|
|
240
|
+
function generateHeartbeatSection(autonomy, budget) {
|
|
241
|
+
const t1 = autonomy.tier1_max_credits;
|
|
242
|
+
const t2 = autonomy.tier2_max_credits;
|
|
243
|
+
const reserve = budget.reserve_credits;
|
|
244
|
+
return [
|
|
245
|
+
HEARTBEAT_MARKER_START,
|
|
246
|
+
"## AgentBnB Protocol",
|
|
247
|
+
"",
|
|
248
|
+
"You are a participant in the AgentBnB capability sharing network.",
|
|
249
|
+
"",
|
|
250
|
+
"### Sharing Rules",
|
|
251
|
+
"- When your idle_rate exceeds 70%, automatically share idle capabilities",
|
|
252
|
+
"- Accept incoming requests that match your published Capability Card",
|
|
253
|
+
"- Track credit earnings and report to owner weekly",
|
|
254
|
+
"",
|
|
255
|
+
"### Requesting Rules",
|
|
256
|
+
"- When you encounter a task you cannot complete with local skills:",
|
|
257
|
+
" 1. Query AgentBnB network for matching capabilities",
|
|
258
|
+
" 2. If found and credit sufficient, automatically request",
|
|
259
|
+
" 3. Integrate result into your current workflow",
|
|
260
|
+
"- Budget limits:",
|
|
261
|
+
` - < ${t1} credits: auto-execute, no notification`,
|
|
262
|
+
` - ${t1}-${t2} credits: execute, notify owner after`,
|
|
263
|
+
` - > ${t2} credits: ask owner before executing`,
|
|
264
|
+
"",
|
|
265
|
+
"### Credit Management",
|
|
266
|
+
`- Maintain minimum balance of ${reserve} credits (reserve for emergencies)`,
|
|
267
|
+
`- If balance drops below ${reserve}, increase sharing priority`,
|
|
268
|
+
"- If balance exceeds 500, notify owner of surplus",
|
|
269
|
+
HEARTBEAT_MARKER_END
|
|
270
|
+
].join("\n");
|
|
271
|
+
}
|
|
272
|
+
function injectHeartbeatSection(heartbeatPath, section) {
|
|
273
|
+
if (!existsSync(heartbeatPath)) {
|
|
274
|
+
writeFileSync(heartbeatPath, section + "\n", "utf-8");
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
let content = readFileSync(heartbeatPath, "utf-8");
|
|
278
|
+
const startIdx = content.indexOf(HEARTBEAT_MARKER_START);
|
|
279
|
+
const endIdx = content.indexOf(HEARTBEAT_MARKER_END);
|
|
280
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
281
|
+
content = content.slice(0, startIdx) + section + content.slice(endIdx + HEARTBEAT_MARKER_END.length);
|
|
282
|
+
} else {
|
|
283
|
+
content = content + "\n" + section + "\n";
|
|
284
|
+
}
|
|
285
|
+
writeFileSync(heartbeatPath, content, "utf-8");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// src/openclaw/skill.ts
|
|
289
|
+
function getOpenClawStatus(config, db, creditDb) {
|
|
290
|
+
const autonomy = config.autonomy ?? DEFAULT_AUTONOMY_CONFIG;
|
|
291
|
+
const budget = config.budget ?? DEFAULT_BUDGET_CONFIG;
|
|
292
|
+
const balance = getBalance(creditDb, config.owner);
|
|
293
|
+
const allCards = listCards(db, config.owner);
|
|
294
|
+
const skills = [];
|
|
295
|
+
for (const card of allCards) {
|
|
296
|
+
const anyCard = card;
|
|
297
|
+
if (anyCard.spec_version !== "2.0" || !Array.isArray(anyCard.skills)) continue;
|
|
298
|
+
for (const skill of anyCard.skills) {
|
|
299
|
+
const internal = skill["_internal"] ?? {};
|
|
300
|
+
const idleRate = typeof internal["idle_rate"] === "number" ? internal["idle_rate"] : null;
|
|
301
|
+
const availability = skill["availability"];
|
|
302
|
+
const online = availability?.online ?? false;
|
|
303
|
+
skills.push({
|
|
304
|
+
id: String(skill["id"] ?? ""),
|
|
305
|
+
name: String(skill["name"] ?? ""),
|
|
306
|
+
idle_rate: idleRate,
|
|
307
|
+
online
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
installed: true,
|
|
313
|
+
owner: config.owner,
|
|
314
|
+
gateway_url: config.gateway_url,
|
|
315
|
+
tier: autonomy,
|
|
316
|
+
balance,
|
|
317
|
+
reserve: budget.reserve_credits,
|
|
318
|
+
skills
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// src/cli/init-action.ts
|
|
323
|
+
import { randomBytes } from "crypto";
|
|
324
|
+
import { join as join2 } from "path";
|
|
325
|
+
import { networkInterfaces } from "os";
|
|
326
|
+
|
|
327
|
+
// src/onboarding/index.ts
|
|
328
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
329
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
119
330
|
import { join } from "path";
|
|
120
331
|
|
|
121
332
|
// src/onboarding/capability-templates.ts
|
|
@@ -232,8 +443,8 @@ function detectCapabilities(opts = {}) {
|
|
|
232
443
|
const cwd = opts.cwd ?? process.cwd();
|
|
233
444
|
if (opts.fromFile) {
|
|
234
445
|
const filePath = opts.fromFile.startsWith("/") ? opts.fromFile : join(cwd, opts.fromFile);
|
|
235
|
-
if (
|
|
236
|
-
const content =
|
|
446
|
+
if (existsSync2(filePath)) {
|
|
447
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
237
448
|
const capabilities = detectFromDocs(content);
|
|
238
449
|
if (capabilities.length > 0) {
|
|
239
450
|
return { source: "docs", capabilities, sourceFile: filePath };
|
|
@@ -243,8 +454,8 @@ function detectCapabilities(opts = {}) {
|
|
|
243
454
|
}
|
|
244
455
|
for (const fileName of DOC_FILES) {
|
|
245
456
|
const filePath = join(cwd, fileName);
|
|
246
|
-
if (!
|
|
247
|
-
const content =
|
|
457
|
+
if (!existsSync2(filePath)) continue;
|
|
458
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
248
459
|
if (fileName === "SOUL.md") {
|
|
249
460
|
return { source: "soul", capabilities: [], soulContent: content, sourceFile: filePath };
|
|
250
461
|
}
|
|
@@ -253,285 +464,43 @@ function detectCapabilities(opts = {}) {
|
|
|
253
464
|
return { source: "docs", capabilities, sourceFile: filePath };
|
|
254
465
|
}
|
|
255
466
|
}
|
|
256
|
-
const envKeys = detectApiKeys(KNOWN_API_KEYS);
|
|
257
|
-
if (envKeys.length > 0) {
|
|
258
|
-
return { source: "env", capabilities: [], envKeys };
|
|
259
|
-
}
|
|
260
|
-
return { source: "none", capabilities: [] };
|
|
261
|
-
}
|
|
262
|
-
function capabilitiesToV2Card(capabilities, owner, agentName) {
|
|
263
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
264
|
-
const skills = capabilities.map((cap) => ({
|
|
265
|
-
id: cap.key,
|
|
266
|
-
name: cap.name,
|
|
267
|
-
description: `${cap.name} capability \u2014 ${cap.category}`,
|
|
268
|
-
level: 1,
|
|
269
|
-
category: cap.category.toLowerCase().replace(/\s+/g, "_"),
|
|
270
|
-
inputs: [{ name: "input", type: "text", required: true }],
|
|
271
|
-
outputs: [{ name: "output", type: "text", required: true }],
|
|
272
|
-
pricing: { credits_per_call: cap.credits_per_call },
|
|
273
|
-
availability: { online: true },
|
|
274
|
-
metadata: {
|
|
275
|
-
tags: cap.tags
|
|
276
|
-
}
|
|
277
|
-
}));
|
|
278
|
-
const card = {
|
|
279
|
-
spec_version: "2.0",
|
|
280
|
-
id: randomUUID2(),
|
|
281
|
-
owner,
|
|
282
|
-
agent_name: agentName ?? owner,
|
|
283
|
-
skills,
|
|
284
|
-
availability: { online: true },
|
|
285
|
-
created_at: now,
|
|
286
|
-
updated_at: now
|
|
287
|
-
};
|
|
288
|
-
return CapabilityCardV2Schema.parse(card);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// src/openclaw/soul-sync.ts
|
|
292
|
-
import { randomUUID as randomUUID4 } from "crypto";
|
|
293
|
-
|
|
294
|
-
// src/skills/publish-capability.ts
|
|
295
|
-
import { randomUUID as randomUUID3 } from "crypto";
|
|
296
|
-
function parseSoulMd(content) {
|
|
297
|
-
const lines = content.split("\n");
|
|
298
|
-
let name = "";
|
|
299
|
-
let description = "";
|
|
300
|
-
const capabilities = [];
|
|
301
|
-
const unknownSections = [];
|
|
302
|
-
let currentSection = null;
|
|
303
|
-
let currentCapabilityName = "";
|
|
304
|
-
let currentCapabilityLines = [];
|
|
305
|
-
let currentCapabilityPricing = void 0;
|
|
306
|
-
let descriptionLines = [];
|
|
307
|
-
let pastFirstH1 = false;
|
|
308
|
-
let pastFirstH2 = false;
|
|
309
|
-
const flushCapability = () => {
|
|
310
|
-
if (currentCapabilityName) {
|
|
311
|
-
const cap = {
|
|
312
|
-
name: currentCapabilityName,
|
|
313
|
-
description: currentCapabilityLines.join(" ").trim()
|
|
314
|
-
};
|
|
315
|
-
if (currentCapabilityPricing !== void 0) {
|
|
316
|
-
cap.pricing = currentCapabilityPricing;
|
|
317
|
-
}
|
|
318
|
-
capabilities.push(cap);
|
|
319
|
-
currentCapabilityName = "";
|
|
320
|
-
currentCapabilityLines = [];
|
|
321
|
-
currentCapabilityPricing = void 0;
|
|
322
|
-
}
|
|
323
|
-
};
|
|
324
|
-
for (const line of lines) {
|
|
325
|
-
const trimmed = line.trim();
|
|
326
|
-
if (/^# /.test(trimmed) && !pastFirstH1) {
|
|
327
|
-
name = trimmed.slice(2).trim();
|
|
328
|
-
pastFirstH1 = true;
|
|
329
|
-
currentSection = "preamble";
|
|
330
|
-
continue;
|
|
331
|
-
}
|
|
332
|
-
if (/^## /.test(trimmed)) {
|
|
333
|
-
flushCapability();
|
|
334
|
-
const capName = trimmed.slice(3).trim();
|
|
335
|
-
currentCapabilityName = capName;
|
|
336
|
-
currentSection = "capability";
|
|
337
|
-
pastFirstH2 = true;
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
if (/^#{3,} /.test(trimmed)) {
|
|
341
|
-
const sectionName = trimmed.replace(/^#+\s*/, "");
|
|
342
|
-
if (!unknownSections.includes(sectionName)) {
|
|
343
|
-
unknownSections.push(sectionName);
|
|
344
|
-
}
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
if (trimmed === "") continue;
|
|
348
|
-
if (currentSection === "preamble" && !pastFirstH2) {
|
|
349
|
-
descriptionLines.push(trimmed);
|
|
350
|
-
} else if (currentSection === "capability") {
|
|
351
|
-
const pricingMatch = trimmed.match(/^pricing:\s*(\d+(?:\.\d+)?)$/i);
|
|
352
|
-
if (pricingMatch) {
|
|
353
|
-
const val = parseFloat(pricingMatch[1]);
|
|
354
|
-
if (!isNaN(val) && val >= 0) {
|
|
355
|
-
currentCapabilityPricing = val;
|
|
356
|
-
}
|
|
357
|
-
} else {
|
|
358
|
-
currentCapabilityLines.push(trimmed);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
flushCapability();
|
|
363
|
-
if (descriptionLines.length > 0) {
|
|
364
|
-
description = descriptionLines[0] ?? "";
|
|
365
|
-
}
|
|
366
|
-
return {
|
|
367
|
-
name,
|
|
368
|
-
description,
|
|
369
|
-
level: 2,
|
|
370
|
-
capabilities,
|
|
371
|
-
unknownSections
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// src/openclaw/soul-sync.ts
|
|
376
|
-
function parseSoulMdV2(content) {
|
|
377
|
-
const parsed = parseSoulMd(content);
|
|
378
|
-
const skills = parsed.capabilities.map((cap) => {
|
|
379
|
-
const sanitizedId = cap.name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
380
|
-
const id = sanitizedId.length > 0 ? sanitizedId : randomUUID4();
|
|
381
|
-
return {
|
|
382
|
-
id,
|
|
383
|
-
name: cap.name,
|
|
384
|
-
description: (cap.description.slice(0, 500) || cap.name).slice(0, 500),
|
|
385
|
-
level: 2,
|
|
386
|
-
inputs: [
|
|
387
|
-
{
|
|
388
|
-
name: "input",
|
|
389
|
-
type: "text",
|
|
390
|
-
description: "Input for the skill",
|
|
391
|
-
required: true
|
|
392
|
-
}
|
|
393
|
-
],
|
|
394
|
-
outputs: [
|
|
395
|
-
{
|
|
396
|
-
name: "output",
|
|
397
|
-
type: "text",
|
|
398
|
-
description: "Output from the skill",
|
|
399
|
-
required: true
|
|
400
|
-
}
|
|
401
|
-
],
|
|
402
|
-
pricing: { credits_per_call: cap.pricing !== void 0 ? cap.pricing : 10 },
|
|
403
|
-
availability: { online: true }
|
|
404
|
-
};
|
|
405
|
-
});
|
|
406
|
-
return {
|
|
407
|
-
agentName: parsed.name || "Unknown Agent",
|
|
408
|
-
description: parsed.description,
|
|
409
|
-
skills
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
function publishFromSoulV2(db, soulContent, owner) {
|
|
413
|
-
const { agentName, skills } = parseSoulMdV2(soulContent);
|
|
414
|
-
if (skills.length === 0) {
|
|
415
|
-
throw new AgentBnBError("SOUL.md has no H2 sections", "VALIDATION_ERROR");
|
|
416
|
-
}
|
|
417
|
-
const existingCards = listCards(db, owner);
|
|
418
|
-
const existingV2 = existingCards.find(
|
|
419
|
-
(c) => c.spec_version === "2.0"
|
|
420
|
-
);
|
|
421
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
422
|
-
const cardId = existingV2?.id ?? randomUUID4();
|
|
423
|
-
const card = {
|
|
424
|
-
spec_version: "2.0",
|
|
425
|
-
id: cardId,
|
|
426
|
-
owner,
|
|
427
|
-
agent_name: agentName,
|
|
428
|
-
skills,
|
|
429
|
-
availability: { online: true },
|
|
430
|
-
created_at: existingV2?.created_at ?? now,
|
|
431
|
-
updated_at: now
|
|
432
|
-
};
|
|
433
|
-
CapabilityCardV2Schema.parse(card);
|
|
434
|
-
if (existingV2) {
|
|
435
|
-
db.prepare(
|
|
436
|
-
"UPDATE capability_cards SET data = ?, updated_at = ? WHERE id = ?"
|
|
437
|
-
).run(JSON.stringify(card), now, cardId);
|
|
438
|
-
} else {
|
|
439
|
-
db.prepare(
|
|
440
|
-
"INSERT INTO capability_cards (id, owner, data, created_at, updated_at) VALUES (?, ?, ?, ?, ?)"
|
|
441
|
-
).run(cardId, owner, JSON.stringify(card), now, now);
|
|
442
|
-
}
|
|
443
|
-
return card;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// src/openclaw/heartbeat-writer.ts
|
|
447
|
-
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2 } from "fs";
|
|
448
|
-
var HEARTBEAT_MARKER_START = "<!-- agentbnb:start -->";
|
|
449
|
-
var HEARTBEAT_MARKER_END = "<!-- agentbnb:end -->";
|
|
450
|
-
function generateHeartbeatSection(autonomy, budget) {
|
|
451
|
-
const t1 = autonomy.tier1_max_credits;
|
|
452
|
-
const t2 = autonomy.tier2_max_credits;
|
|
453
|
-
const reserve = budget.reserve_credits;
|
|
454
|
-
return [
|
|
455
|
-
HEARTBEAT_MARKER_START,
|
|
456
|
-
"## AgentBnB Protocol",
|
|
457
|
-
"",
|
|
458
|
-
"You are a participant in the AgentBnB capability sharing network.",
|
|
459
|
-
"",
|
|
460
|
-
"### Sharing Rules",
|
|
461
|
-
"- When your idle_rate exceeds 70%, automatically share idle capabilities",
|
|
462
|
-
"- Accept incoming requests that match your published Capability Card",
|
|
463
|
-
"- Track credit earnings and report to owner weekly",
|
|
464
|
-
"",
|
|
465
|
-
"### Requesting Rules",
|
|
466
|
-
"- When you encounter a task you cannot complete with local skills:",
|
|
467
|
-
" 1. Query AgentBnB network for matching capabilities",
|
|
468
|
-
" 2. If found and credit sufficient, automatically request",
|
|
469
|
-
" 3. Integrate result into your current workflow",
|
|
470
|
-
"- Budget limits:",
|
|
471
|
-
` - < ${t1} credits: auto-execute, no notification`,
|
|
472
|
-
` - ${t1}-${t2} credits: execute, notify owner after`,
|
|
473
|
-
` - > ${t2} credits: ask owner before executing`,
|
|
474
|
-
"",
|
|
475
|
-
"### Credit Management",
|
|
476
|
-
`- Maintain minimum balance of ${reserve} credits (reserve for emergencies)`,
|
|
477
|
-
`- If balance drops below ${reserve}, increase sharing priority`,
|
|
478
|
-
"- If balance exceeds 500, notify owner of surplus",
|
|
479
|
-
HEARTBEAT_MARKER_END
|
|
480
|
-
].join("\n");
|
|
481
|
-
}
|
|
482
|
-
function injectHeartbeatSection(heartbeatPath, section) {
|
|
483
|
-
if (!existsSync2(heartbeatPath)) {
|
|
484
|
-
writeFileSync(heartbeatPath, section + "\n", "utf-8");
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
487
|
-
let content = readFileSync2(heartbeatPath, "utf-8");
|
|
488
|
-
const startIdx = content.indexOf(HEARTBEAT_MARKER_START);
|
|
489
|
-
const endIdx = content.indexOf(HEARTBEAT_MARKER_END);
|
|
490
|
-
if (startIdx !== -1 && endIdx !== -1) {
|
|
491
|
-
content = content.slice(0, startIdx) + section + content.slice(endIdx + HEARTBEAT_MARKER_END.length);
|
|
492
|
-
} else {
|
|
493
|
-
content = content + "\n" + section + "\n";
|
|
467
|
+
const envKeys = detectApiKeys(KNOWN_API_KEYS);
|
|
468
|
+
if (envKeys.length > 0) {
|
|
469
|
+
return { source: "env", capabilities: [], envKeys };
|
|
494
470
|
}
|
|
495
|
-
|
|
471
|
+
return { source: "none", capabilities: [] };
|
|
496
472
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
const availability = skill["availability"];
|
|
512
|
-
const online = availability?.online ?? false;
|
|
513
|
-
skills.push({
|
|
514
|
-
id: String(skill["id"] ?? ""),
|
|
515
|
-
name: String(skill["name"] ?? ""),
|
|
516
|
-
idle_rate: idleRate,
|
|
517
|
-
online
|
|
518
|
-
});
|
|
473
|
+
function capabilitiesToV2Card(capabilities, owner, agentName) {
|
|
474
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
475
|
+
const skills = capabilities.map((cap) => ({
|
|
476
|
+
id: cap.key,
|
|
477
|
+
name: cap.name,
|
|
478
|
+
description: `${cap.name} capability \u2014 ${cap.category}`,
|
|
479
|
+
level: 1,
|
|
480
|
+
category: cap.category.toLowerCase().replace(/\s+/g, "_"),
|
|
481
|
+
inputs: [{ name: "input", type: "text", required: true }],
|
|
482
|
+
outputs: [{ name: "output", type: "text", required: true }],
|
|
483
|
+
pricing: { credits_per_call: cap.credits_per_call },
|
|
484
|
+
availability: { online: true },
|
|
485
|
+
metadata: {
|
|
486
|
+
tags: cap.tags
|
|
519
487
|
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
488
|
+
}));
|
|
489
|
+
const card = {
|
|
490
|
+
spec_version: "2.0",
|
|
491
|
+
id: randomUUID3(),
|
|
492
|
+
owner,
|
|
493
|
+
agent_name: agentName ?? owner,
|
|
494
|
+
skills,
|
|
495
|
+
availability: { online: true },
|
|
496
|
+
created_at: now,
|
|
497
|
+
updated_at: now
|
|
529
498
|
};
|
|
499
|
+
return CapabilityCardV2Schema.parse(card);
|
|
530
500
|
}
|
|
531
501
|
|
|
532
|
-
// src/cli/
|
|
533
|
-
|
|
534
|
-
var pkg = require2("../../package.json");
|
|
502
|
+
// src/cli/init-action.ts
|
|
503
|
+
import { createInterface as createInterface2 } from "readline";
|
|
535
504
|
async function confirm(question) {
|
|
536
505
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
537
506
|
try {
|
|
@@ -544,6 +513,15 @@ async function confirm(question) {
|
|
|
544
513
|
rl.close();
|
|
545
514
|
}
|
|
546
515
|
}
|
|
516
|
+
function getLanIp() {
|
|
517
|
+
const nets = networkInterfaces();
|
|
518
|
+
for (const ifaces of Object.values(nets)) {
|
|
519
|
+
for (const iface of ifaces ?? []) {
|
|
520
|
+
if (iface.family === "IPv4" && !iface.internal) return iface.address;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return "localhost";
|
|
524
|
+
}
|
|
547
525
|
function loadIdentityAuth(owner) {
|
|
548
526
|
const configDir = getConfigDir();
|
|
549
527
|
let keys;
|
|
@@ -560,41 +538,25 @@ function loadIdentityAuth(owner) {
|
|
|
560
538
|
privateKey: keys.privateKey
|
|
561
539
|
};
|
|
562
540
|
}
|
|
563
|
-
function
|
|
564
|
-
const nets = networkInterfaces();
|
|
565
|
-
for (const ifaces of Object.values(nets)) {
|
|
566
|
-
for (const iface of ifaces ?? []) {
|
|
567
|
-
if (iface.family === "IPv4" && !iface.internal) return iface.address;
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
return "localhost";
|
|
571
|
-
}
|
|
572
|
-
var program = new Command();
|
|
573
|
-
program.name("agentbnb").description("P2P Agent Capability Sharing Protocol \u2014 Airbnb for AI agent pipelines").version(pkg.version);
|
|
574
|
-
program.command("init").description("Initialize AgentBnB config and create agent identity").option("--owner <name>", "Agent owner name").option("--agent-id <id>", "Agent identity (alias for --owner, for genesis-template compat)").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("--non-interactive", "Non-interactive mode (alias for --yes)").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) => {
|
|
541
|
+
async function performInit(opts) {
|
|
575
542
|
const configDir = getConfigDir();
|
|
576
543
|
const dbPath = join2(configDir, "registry.db");
|
|
577
544
|
const creditDbPath = join2(configDir, "credit.db");
|
|
578
545
|
const port = parseInt(opts.port, 10);
|
|
579
546
|
const ip = opts.host ?? getLanIp();
|
|
580
547
|
const yesMode = opts.yes ?? opts.nonInteractive ?? false;
|
|
581
|
-
opts.yes = yesMode;
|
|
582
548
|
const existingConfig = loadConfig();
|
|
583
549
|
const owner = opts.agentId ?? opts.owner ?? existingConfig?.owner ?? `agent-${randomBytes(4).toString("hex")}`;
|
|
584
550
|
const config = {
|
|
585
551
|
...existingConfig,
|
|
586
|
-
// Preserve all existing keys (registry, autonomy, budget, etc.)
|
|
587
552
|
owner,
|
|
588
553
|
gateway_url: `http://${ip}:${port}`,
|
|
589
554
|
gateway_port: port,
|
|
590
555
|
db_path: dbPath,
|
|
591
556
|
credit_db_path: creditDbPath,
|
|
592
557
|
token: existingConfig?.token ?? randomBytes(32).toString("hex"),
|
|
593
|
-
// Preserve existing token
|
|
594
558
|
api_key: existingConfig?.api_key ?? randomBytes(32).toString("hex"),
|
|
595
|
-
|
|
596
|
-
// Interactive init leaves registry unset so users can configure it explicitly.
|
|
597
|
-
...existingConfig?.registry ? { registry: existingConfig.registry } : opts.yes ? { registry: "https://agentbnb.fly.dev" } : {}
|
|
559
|
+
...existingConfig?.registry ? { registry: existingConfig.registry } : yesMode ? { registry: "https://agentbnb.fly.dev" } : {}
|
|
598
560
|
};
|
|
599
561
|
saveConfig(config);
|
|
600
562
|
let keypairStatus = "existing";
|
|
@@ -700,12 +662,12 @@ program.command("init").description("Initialize AgentBnB config and create agent
|
|
|
700
662
|
}
|
|
701
663
|
}
|
|
702
664
|
const card = capabilitiesToV2Card(result.capabilities, owner);
|
|
703
|
-
if (
|
|
665
|
+
if (yesMode) {
|
|
704
666
|
const db = openDatabase(dbPath);
|
|
705
667
|
try {
|
|
706
668
|
db.prepare(
|
|
707
669
|
`INSERT OR REPLACE INTO capability_cards (id, owner, data, created_at, updated_at)
|
|
708
|
-
|
|
670
|
+
VALUES (?, ?, ?, ?, ?)`
|
|
709
671
|
).run(card.id, card.owner, JSON.stringify(card), card.created_at, card.updated_at);
|
|
710
672
|
publishedCards.push({ id: card.id, name: card.agent_name });
|
|
711
673
|
if (!opts.json) {
|
|
@@ -722,7 +684,7 @@ Publish these ${card.skills.length} capabilities? [y/N] `);
|
|
|
722
684
|
try {
|
|
723
685
|
db.prepare(
|
|
724
686
|
`INSERT OR REPLACE INTO capability_cards (id, owner, data, created_at, updated_at)
|
|
725
|
-
|
|
687
|
+
VALUES (?, ?, ?, ?, ?)`
|
|
726
688
|
).run(card.id, card.owner, JSON.stringify(card), card.created_at, card.updated_at);
|
|
727
689
|
publishedCards.push({ id: card.id, name: card.agent_name });
|
|
728
690
|
console.log(` Published v2.0 card: ${card.agent_name} (${card.skills.length} skills)`);
|
|
@@ -747,7 +709,7 @@ Publish these ${card.skills.length} capabilities? [y/N] `);
|
|
|
747
709
|
console.log(` Found services on ports: ${detectedPorts.join(", ")}`);
|
|
748
710
|
}
|
|
749
711
|
const drafts = detectedKeys.map((key) => buildDraftCard(key, owner)).filter((card) => card !== null);
|
|
750
|
-
if (
|
|
712
|
+
if (yesMode) {
|
|
751
713
|
const db = openDatabase(dbPath);
|
|
752
714
|
try {
|
|
753
715
|
for (const card of drafts) {
|
|
@@ -782,7 +744,7 @@ Publish these ${card.skills.length} capabilities? [y/N] `);
|
|
|
782
744
|
}
|
|
783
745
|
}
|
|
784
746
|
} else {
|
|
785
|
-
if (process.stdout.isTTY && !
|
|
747
|
+
if (process.stdout.isTTY && !yesMode && !opts.json) {
|
|
786
748
|
const selected = await interactiveTemplateMenu();
|
|
787
749
|
if (selected.length > 0) {
|
|
788
750
|
const card = capabilitiesToV2Card(selected, owner);
|
|
@@ -790,7 +752,7 @@ Publish these ${card.skills.length} capabilities? [y/N] `);
|
|
|
790
752
|
try {
|
|
791
753
|
db.prepare(
|
|
792
754
|
`INSERT OR REPLACE INTO capability_cards (id, owner, data, created_at, updated_at)
|
|
793
|
-
|
|
755
|
+
VALUES (?, ?, ?, ?, ?)`
|
|
794
756
|
).run(card.id, card.owner, JSON.stringify(card), card.created_at, card.updated_at);
|
|
795
757
|
publishedCards.push({ id: card.id, name: card.agent_name });
|
|
796
758
|
console.log(`
|
|
@@ -804,36 +766,277 @@ Publish these ${card.skills.length} capabilities? [y/N] `);
|
|
|
804
766
|
}
|
|
805
767
|
}
|
|
806
768
|
}
|
|
769
|
+
return {
|
|
770
|
+
config,
|
|
771
|
+
owner,
|
|
772
|
+
configDir,
|
|
773
|
+
publishedCards,
|
|
774
|
+
registryBalance,
|
|
775
|
+
identity: { agent_id: identity.agent_id },
|
|
776
|
+
keypairStatus,
|
|
777
|
+
detectedSource
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// src/cli/quickstart.ts
|
|
782
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
783
|
+
import { join as join3 } from "path";
|
|
784
|
+
import { homedir } from "os";
|
|
785
|
+
import { execSync } from "child_process";
|
|
786
|
+
var SKILLS_YAML_TEMPLATE = `skills:
|
|
787
|
+
# General-purpose AI task execution via Claude Code CLI
|
|
788
|
+
- id: claude-code-run
|
|
789
|
+
type: command
|
|
790
|
+
name: Claude Code Task Runner
|
|
791
|
+
description: "Execute any text-based AI task via Claude Code"
|
|
792
|
+
command: claude -p "\${params.prompt}"
|
|
793
|
+
output_type: text
|
|
794
|
+
allowed_commands:
|
|
795
|
+
- claude
|
|
796
|
+
timeout_ms: 180000
|
|
797
|
+
pricing:
|
|
798
|
+
credits_per_call: 5
|
|
799
|
+
|
|
800
|
+
# Code review skill
|
|
801
|
+
- id: claude-code-review
|
|
802
|
+
type: command
|
|
803
|
+
name: Code Review
|
|
804
|
+
description: "Review code for bugs, style, and improvements"
|
|
805
|
+
command: claude -p "Review this code for bugs, style issues, and improvements:\\n\\n\${params.code}"
|
|
806
|
+
output_type: text
|
|
807
|
+
allowed_commands:
|
|
808
|
+
- claude
|
|
809
|
+
timeout_ms: 120000
|
|
810
|
+
pricing:
|
|
811
|
+
credits_per_call: 3
|
|
812
|
+
|
|
813
|
+
# Text summarization skill
|
|
814
|
+
- id: claude-code-summarize
|
|
815
|
+
type: command
|
|
816
|
+
name: Text Summarizer
|
|
817
|
+
description: "Summarize long text into concise key points"
|
|
818
|
+
command: claude -p "Summarize the following text into concise key points:\\n\\n\${params.text}"
|
|
819
|
+
output_type: text
|
|
820
|
+
allowed_commands:
|
|
821
|
+
- claude
|
|
822
|
+
timeout_ms: 120000
|
|
823
|
+
pricing:
|
|
824
|
+
credits_per_call: 2
|
|
825
|
+
`;
|
|
826
|
+
function generateSkillsYaml(configDir) {
|
|
827
|
+
const skillsPath = join3(configDir, "skills.yaml");
|
|
828
|
+
if (existsSync3(skillsPath)) {
|
|
829
|
+
const content = readFileSync3(skillsPath, "utf-8");
|
|
830
|
+
const matches = content.match(/^\s+-\s+id:/gm);
|
|
831
|
+
return { generated: false, path: skillsPath, skillCount: matches?.length ?? 0 };
|
|
832
|
+
}
|
|
833
|
+
writeFileSync2(skillsPath, SKILLS_YAML_TEMPLATE, "utf-8");
|
|
834
|
+
return { generated: true, path: skillsPath, skillCount: 3 };
|
|
835
|
+
}
|
|
836
|
+
function registerMcpWithClaudeCode() {
|
|
837
|
+
const claudeDir = join3(homedir(), ".claude");
|
|
838
|
+
if (!existsSync3(claudeDir)) {
|
|
839
|
+
return {
|
|
840
|
+
registered: false,
|
|
841
|
+
reason: "Claude Code not detected. Add MCP manually: claude mcp add agentbnb -- agentbnb mcp-server"
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
const settingsPath = join3(claudeDir, "settings.json");
|
|
845
|
+
let agentbnbCommand = "agentbnb";
|
|
846
|
+
try {
|
|
847
|
+
const resolved = execSync("which agentbnb", { encoding: "utf-8" }).trim();
|
|
848
|
+
if (resolved) agentbnbCommand = resolved;
|
|
849
|
+
} catch {
|
|
850
|
+
}
|
|
851
|
+
let settings = {};
|
|
852
|
+
if (existsSync3(settingsPath)) {
|
|
853
|
+
try {
|
|
854
|
+
settings = JSON.parse(readFileSync3(settingsPath, "utf-8"));
|
|
855
|
+
} catch {
|
|
856
|
+
try {
|
|
857
|
+
writeFileSync2(`${settingsPath}.bak`, readFileSync3(settingsPath, "utf-8"), "utf-8");
|
|
858
|
+
} catch {
|
|
859
|
+
}
|
|
860
|
+
settings = {};
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
const mcpServers = settings.mcpServers ?? {};
|
|
864
|
+
if (mcpServers.agentbnb) {
|
|
865
|
+
return { registered: false, path: settingsPath, reason: "already registered" };
|
|
866
|
+
}
|
|
867
|
+
mcpServers.agentbnb = {
|
|
868
|
+
command: agentbnbCommand,
|
|
869
|
+
args: ["mcp-server"]
|
|
870
|
+
};
|
|
871
|
+
settings.mcpServers = mcpServers;
|
|
872
|
+
writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
873
|
+
return { registered: true, path: settingsPath };
|
|
874
|
+
}
|
|
875
|
+
async function runQuickstart(opts) {
|
|
876
|
+
const jsonMode = opts.json ?? false;
|
|
877
|
+
const skipServe = opts.serve === false;
|
|
878
|
+
const skipMcp = opts.mcp === false;
|
|
879
|
+
if (!jsonMode) console.log("Initializing AgentBnB...");
|
|
880
|
+
const initResult = await performInit({
|
|
881
|
+
owner: opts.owner,
|
|
882
|
+
port: opts.port,
|
|
883
|
+
yes: true,
|
|
884
|
+
detect: true,
|
|
885
|
+
json: false
|
|
886
|
+
// We handle output ourselves
|
|
887
|
+
});
|
|
888
|
+
const skills = generateSkillsYaml(initResult.configDir);
|
|
889
|
+
if (!jsonMode) {
|
|
890
|
+
if (skills.generated) {
|
|
891
|
+
console.log(`
|
|
892
|
+
Generated skills.yaml with ${skills.skillCount} Claude Code skills`);
|
|
893
|
+
} else {
|
|
894
|
+
console.log(`
|
|
895
|
+
Skills: ${skills.skillCount} skill(s) in ${skills.path}`);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
let mcpResult = { registered: false, reason: "skipped" };
|
|
899
|
+
if (!skipMcp) {
|
|
900
|
+
mcpResult = registerMcpWithClaudeCode();
|
|
901
|
+
if (!jsonMode) {
|
|
902
|
+
if (mcpResult.registered) {
|
|
903
|
+
console.log(`MCP: registered in ${mcpResult.path}`);
|
|
904
|
+
} else if (mcpResult.reason === "already registered") {
|
|
905
|
+
console.log(`MCP: already registered in ${mcpResult.path}`);
|
|
906
|
+
} else {
|
|
907
|
+
console.log(`MCP: ${mcpResult.reason}`);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
let daemonStatus = { running: false, reason: "skipped" };
|
|
912
|
+
if (!skipServe) {
|
|
913
|
+
try {
|
|
914
|
+
const { ProcessGuard } = await import("../process-guard-QCCBGILS.js");
|
|
915
|
+
const { ServiceCoordinator } = await import("../service-coordinator-XBNT3SMU.js");
|
|
916
|
+
const guard = new ProcessGuard(join3(initResult.configDir, ".pid"));
|
|
917
|
+
const coordinator = new ServiceCoordinator(initResult.config, guard);
|
|
918
|
+
const result = await coordinator.ensureRunning({
|
|
919
|
+
port: initResult.config.gateway_port,
|
|
920
|
+
skillsYamlPath: join3(initResult.configDir, "skills.yaml"),
|
|
921
|
+
registryUrl: initResult.config.registry,
|
|
922
|
+
relay: true
|
|
923
|
+
});
|
|
924
|
+
if (result === "already_running") {
|
|
925
|
+
daemonStatus = { running: true, reason: "already running" };
|
|
926
|
+
} else {
|
|
927
|
+
const meta = guard.getRunningMeta();
|
|
928
|
+
daemonStatus = { running: true, pid: meta?.pid };
|
|
929
|
+
}
|
|
930
|
+
if (!jsonMode) {
|
|
931
|
+
if (result === "already_running") {
|
|
932
|
+
console.log(`Daemon: already running`);
|
|
933
|
+
} else {
|
|
934
|
+
console.log(`Daemon: started (pid ${daemonStatus.pid ?? "unknown"})`);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
} catch (err) {
|
|
938
|
+
daemonStatus = { running: false, reason: err.message };
|
|
939
|
+
if (!jsonMode) {
|
|
940
|
+
console.warn(`Daemon: failed to start \u2014 ${err.message}`);
|
|
941
|
+
console.warn(` Start manually with: agentbnb serve`);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
if (jsonMode) {
|
|
946
|
+
console.log(JSON.stringify({
|
|
947
|
+
success: true,
|
|
948
|
+
owner: initResult.owner,
|
|
949
|
+
config_dir: initResult.configDir,
|
|
950
|
+
gateway_url: initResult.config.gateway_url,
|
|
951
|
+
registry: initResult.config.registry,
|
|
952
|
+
credits: initResult.registryBalance ?? 100,
|
|
953
|
+
skills: { count: skills.skillCount, generated: skills.generated, path: skills.path },
|
|
954
|
+
mcp: mcpResult,
|
|
955
|
+
daemon: daemonStatus,
|
|
956
|
+
published_cards: initResult.publishedCards
|
|
957
|
+
}, null, 2));
|
|
958
|
+
} else {
|
|
959
|
+
console.log("\n--- AgentBnB quickstart complete! ---\n");
|
|
960
|
+
console.log(` Owner: ${initResult.owner}`);
|
|
961
|
+
console.log(` Gateway: ${initResult.config.gateway_url}`);
|
|
962
|
+
console.log(` Credits: ${initResult.registryBalance ?? 100}`);
|
|
963
|
+
console.log(` Registry: ${initResult.config.registry ?? "not configured"}`);
|
|
964
|
+
console.log(` Skills: ${skills.skillCount} loaded`);
|
|
965
|
+
console.log(` MCP: ${mcpResult.registered ? "registered" : mcpResult.reason}`);
|
|
966
|
+
console.log(` Daemon: ${daemonStatus.running ? `running${daemonStatus.pid ? ` (pid ${daemonStatus.pid})` : ""}` : `not running \u2014 ${daemonStatus.reason}`}`);
|
|
967
|
+
console.log("");
|
|
968
|
+
console.log(" You are now both a consumer and provider on AgentBnB.");
|
|
969
|
+
console.log("");
|
|
970
|
+
console.log(" Consumer: In Claude Code, use agentbnb_discover and agentbnb_request");
|
|
971
|
+
console.log(" Provider: Your claude -p skills are live and accepting requests");
|
|
972
|
+
console.log(` Dashboard: http://localhost:7701/hub/#/myagent`);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
// src/cli/index.ts
|
|
977
|
+
var require2 = createRequire(import.meta.url);
|
|
978
|
+
var pkg = require2("../../package.json");
|
|
979
|
+
function loadIdentityAuth2(owner) {
|
|
980
|
+
const configDir = getConfigDir();
|
|
981
|
+
let keys;
|
|
982
|
+
try {
|
|
983
|
+
keys = loadKeyPair(configDir);
|
|
984
|
+
} catch {
|
|
985
|
+
keys = generateKeyPair();
|
|
986
|
+
saveKeyPair(configDir, keys);
|
|
987
|
+
}
|
|
988
|
+
const identity = ensureIdentity(configDir, owner);
|
|
989
|
+
return {
|
|
990
|
+
agentId: identity.agent_id,
|
|
991
|
+
publicKey: identity.public_key,
|
|
992
|
+
privateKey: keys.privateKey
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
function getLanIp2() {
|
|
996
|
+
const nets = networkInterfaces2();
|
|
997
|
+
for (const ifaces of Object.values(nets)) {
|
|
998
|
+
for (const iface of ifaces ?? []) {
|
|
999
|
+
if (iface.family === "IPv4" && !iface.internal) return iface.address;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
return "localhost";
|
|
1003
|
+
}
|
|
1004
|
+
var program = new Command();
|
|
1005
|
+
program.name("agentbnb").description("P2P Agent Capability Sharing Protocol \u2014 Airbnb for AI agent pipelines").version(pkg.version);
|
|
1006
|
+
program.command("init").description("Initialize AgentBnB config and create agent identity").option("--owner <name>", "Agent owner name").option("--agent-id <id>", "Agent identity (alias for --owner, for genesis-template compat)").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("--non-interactive", "Non-interactive mode (alias for --yes)").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) => {
|
|
1007
|
+
const result = await performInit(opts);
|
|
807
1008
|
if (opts.json) {
|
|
808
1009
|
const jsonOutput = {
|
|
809
1010
|
success: true,
|
|
810
|
-
owner,
|
|
811
|
-
config_dir: configDir,
|
|
812
|
-
token: config.token,
|
|
813
|
-
gateway_url: config.gateway_url,
|
|
814
|
-
keypair: keypairStatus,
|
|
815
|
-
agent_id: identity.agent_id
|
|
1011
|
+
owner: result.owner,
|
|
1012
|
+
config_dir: result.configDir,
|
|
1013
|
+
token: result.config.token,
|
|
1014
|
+
gateway_url: result.config.gateway_url,
|
|
1015
|
+
keypair: result.keypairStatus,
|
|
1016
|
+
agent_id: result.identity.agent_id
|
|
816
1017
|
};
|
|
817
|
-
if (registryBalance !== void 0) {
|
|
818
|
-
jsonOutput.registry_balance = registryBalance;
|
|
1018
|
+
if (result.registryBalance !== void 0) {
|
|
1019
|
+
jsonOutput.registry_balance = result.registryBalance;
|
|
819
1020
|
}
|
|
820
|
-
if (
|
|
821
|
-
jsonOutput.detected_source = detectedSource;
|
|
822
|
-
jsonOutput.published_cards = publishedCards;
|
|
1021
|
+
if (opts.detect !== false) {
|
|
1022
|
+
jsonOutput.detected_source = result.detectedSource;
|
|
1023
|
+
jsonOutput.published_cards = result.publishedCards;
|
|
823
1024
|
}
|
|
824
1025
|
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
825
1026
|
} else {
|
|
1027
|
+
const ip = opts.host ?? getLanIp2();
|
|
1028
|
+
const port = parseInt(opts.port, 10);
|
|
826
1029
|
console.log(`AgentBnB initialized.`);
|
|
827
|
-
console.log(` Owner: ${owner}`);
|
|
828
|
-
console.log(` Token: ${config.token}`);
|
|
829
|
-
console.log(` Config: ${configDir}/config.json`);
|
|
830
|
-
if (registryBalance !== void 0) {
|
|
831
|
-
console.log(` Registry balance: ${registryBalance} credits`);
|
|
1030
|
+
console.log(` Owner: ${result.owner}`);
|
|
1031
|
+
console.log(` Token: ${result.config.token}`);
|
|
1032
|
+
console.log(` Config: ${result.configDir}/config.json`);
|
|
1033
|
+
if (result.registryBalance !== void 0) {
|
|
1034
|
+
console.log(` Registry balance: ${result.registryBalance} credits`);
|
|
832
1035
|
} else {
|
|
833
1036
|
console.log(` Credits: 100 (starter grant)`);
|
|
834
1037
|
}
|
|
835
|
-
console.log(` Keypair: ${keypairStatus === "generated" ? "generated (Ed25519)" : "preserved (existing)"}`);
|
|
836
|
-
console.log(` Agent ID: ${identity.agent_id}`);
|
|
1038
|
+
console.log(` Keypair: ${result.keypairStatus === "generated" ? "generated (Ed25519)" : "preserved (existing)"}`);
|
|
1039
|
+
console.log(` Agent ID: ${result.identity.agent_id}`);
|
|
837
1040
|
console.log(` Gateway: http://${ip}:${port}`);
|
|
838
1041
|
}
|
|
839
1042
|
});
|
|
@@ -845,7 +1048,7 @@ program.command("publish <card.json>").description("Publish a Capability Card to
|
|
|
845
1048
|
}
|
|
846
1049
|
let raw;
|
|
847
1050
|
try {
|
|
848
|
-
raw =
|
|
1051
|
+
raw = readFileSync4(cardPath, "utf-8");
|
|
849
1052
|
} catch {
|
|
850
1053
|
console.error(`Error: cannot read file: ${cardPath}`);
|
|
851
1054
|
process.exit(1);
|
|
@@ -946,6 +1149,62 @@ program.command("publish <card.json>").description("Publish a Capability Card to
|
|
|
946
1149
|
} else if (!registryUrl) {
|
|
947
1150
|
}
|
|
948
1151
|
});
|
|
1152
|
+
program.command("publish-skills").description("Publish capabilities from skills.yaml to the local registry").option("--from-skills [path]", "Path to skills.yaml", "./skills.yaml").action(async (opts) => {
|
|
1153
|
+
const config = loadConfig();
|
|
1154
|
+
if (!config) {
|
|
1155
|
+
console.error("Error: not initialized. Run `agentbnb init` first.");
|
|
1156
|
+
process.exit(1);
|
|
1157
|
+
}
|
|
1158
|
+
const { parseSkillsFile } = await import("../skill-config-FETXPNVP.js");
|
|
1159
|
+
const { skillConfigToSkill } = await import("../publish-capability-TS6CNR5G.js");
|
|
1160
|
+
const skillsPath = typeof opts.fromSkills === "string" ? opts.fromSkills : "./skills.yaml";
|
|
1161
|
+
let yamlContent;
|
|
1162
|
+
try {
|
|
1163
|
+
yamlContent = readFileSync4(skillsPath, "utf-8");
|
|
1164
|
+
} catch {
|
|
1165
|
+
console.error(`Error: cannot read skills.yaml at ${skillsPath}`);
|
|
1166
|
+
process.exit(1);
|
|
1167
|
+
}
|
|
1168
|
+
let allSkillConfigs;
|
|
1169
|
+
try {
|
|
1170
|
+
allSkillConfigs = parseSkillsFile(yamlContent);
|
|
1171
|
+
} catch (err) {
|
|
1172
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1173
|
+
console.error(`Error: failed to parse skills.yaml \u2014 ${msg}`);
|
|
1174
|
+
process.exit(1);
|
|
1175
|
+
}
|
|
1176
|
+
const publicSkillConfigs = allSkillConfigs.filter(
|
|
1177
|
+
(sc) => sc.visibility !== "private"
|
|
1178
|
+
);
|
|
1179
|
+
if (publicSkillConfigs.length === 0) {
|
|
1180
|
+
console.log("No public skills to publish (all skills have visibility: private or no skills found).");
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
const skills = publicSkillConfigs.map((sc) => skillConfigToSkill(sc));
|
|
1184
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1185
|
+
const card = {
|
|
1186
|
+
spec_version: "2.0",
|
|
1187
|
+
id: randomUUID4(),
|
|
1188
|
+
owner: config.owner,
|
|
1189
|
+
agent_name: config.owner,
|
|
1190
|
+
skills,
|
|
1191
|
+
availability: { online: true },
|
|
1192
|
+
created_at: now,
|
|
1193
|
+
updated_at: now
|
|
1194
|
+
};
|
|
1195
|
+
const db = openDatabase(config.db_path);
|
|
1196
|
+
try {
|
|
1197
|
+
db.prepare(
|
|
1198
|
+
"INSERT OR REPLACE INTO capability_cards (id, owner, data, created_at, updated_at) VALUES (?, ?, ?, ?, ?)"
|
|
1199
|
+
).run(card.id, card.owner, JSON.stringify(card), now, now);
|
|
1200
|
+
} finally {
|
|
1201
|
+
db.close();
|
|
1202
|
+
}
|
|
1203
|
+
console.log(`Published ${skills.length} skill(s) to local registry`);
|
|
1204
|
+
for (const skill of skills) {
|
|
1205
|
+
console.log(` - ${skill.id}: ${skill.name} (${skill.pricing.credits_per_call} cr/call)`);
|
|
1206
|
+
}
|
|
1207
|
+
});
|
|
949
1208
|
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) => {
|
|
950
1209
|
const config = loadConfig();
|
|
951
1210
|
if (!config) {
|
|
@@ -1148,7 +1407,7 @@ program.command("request [card-id]").description("Request a capability from anot
|
|
|
1148
1407
|
}
|
|
1149
1408
|
let batchPayload;
|
|
1150
1409
|
try {
|
|
1151
|
-
const raw =
|
|
1410
|
+
const raw = readFileSync4(opts.batch, "utf-8");
|
|
1152
1411
|
batchPayload = JSON.parse(raw);
|
|
1153
1412
|
} catch (err) {
|
|
1154
1413
|
console.error(`Error: could not read batch file: ${err.message}`);
|
|
@@ -1209,8 +1468,8 @@ Batch Results (${res.results.length} items):`);
|
|
|
1209
1468
|
process.exit(1);
|
|
1210
1469
|
}
|
|
1211
1470
|
}
|
|
1212
|
-
const registryDb = openDatabase(
|
|
1213
|
-
const creditDb = openCreditDb(
|
|
1471
|
+
const registryDb = openDatabase(join4(getConfigDir(), "registry.db"));
|
|
1472
|
+
const creditDb = openCreditDb(join4(getConfigDir(), "credit.db"));
|
|
1214
1473
|
registryDb.pragma("busy_timeout = 5000");
|
|
1215
1474
|
creditDb.pragma("busy_timeout = 5000");
|
|
1216
1475
|
try {
|
|
@@ -1250,7 +1509,7 @@ Batch Results (${res.results.length} items):`);
|
|
|
1250
1509
|
let token;
|
|
1251
1510
|
let isRemoteRequest = false;
|
|
1252
1511
|
let targetOwner;
|
|
1253
|
-
const identityAuth =
|
|
1512
|
+
const identityAuth = loadIdentityAuth2(config.owner);
|
|
1254
1513
|
if (opts.peer) {
|
|
1255
1514
|
const peer = findPeer(opts.peer);
|
|
1256
1515
|
if (!peer) {
|
|
@@ -1330,7 +1589,7 @@ Batch Results (${res.results.length} items):`);
|
|
|
1330
1589
|
process.exit(1);
|
|
1331
1590
|
}
|
|
1332
1591
|
if (useRegistryLedger) {
|
|
1333
|
-
const reqIdentityAuth =
|
|
1592
|
+
const reqIdentityAuth = loadIdentityAuth2(config.owner);
|
|
1334
1593
|
requestLedger = createLedger({
|
|
1335
1594
|
registryUrl: config.registry,
|
|
1336
1595
|
ownerPublicKey: reqIdentityAuth.publicKey,
|
|
@@ -1353,7 +1612,7 @@ Batch Results (${res.results.length} items):`);
|
|
|
1353
1612
|
}
|
|
1354
1613
|
} else if (gatewayUrl) {
|
|
1355
1614
|
const configDir = getConfigDir();
|
|
1356
|
-
const creditDb = openCreditDb(
|
|
1615
|
+
const creditDb = openCreditDb(join4(configDir, "credit.db"));
|
|
1357
1616
|
creditDb.pragma("busy_timeout = 5000");
|
|
1358
1617
|
try {
|
|
1359
1618
|
const keys = loadKeyPair(configDir);
|
|
@@ -1388,7 +1647,7 @@ Batch Results (${res.results.length} items):`);
|
|
|
1388
1647
|
if (!opts.json) console.log(`Escrow settled: ${opts.cost} credits deducted.`);
|
|
1389
1648
|
} else if (escrowReceipt) {
|
|
1390
1649
|
const configDir = getConfigDir();
|
|
1391
|
-
const creditDb = openCreditDb(
|
|
1650
|
+
const creditDb = openCreditDb(join4(configDir, "credit.db"));
|
|
1392
1651
|
creditDb.pragma("busy_timeout = 5000");
|
|
1393
1652
|
try {
|
|
1394
1653
|
settleRequesterEscrow(creditDb, escrowId);
|
|
@@ -1406,7 +1665,7 @@ Batch Results (${res.results.length} items):`);
|
|
|
1406
1665
|
if (!opts.json) console.log("Escrow released: credits refunded.");
|
|
1407
1666
|
} else if (escrowReceipt) {
|
|
1408
1667
|
const configDir = getConfigDir();
|
|
1409
|
-
const creditDb = openCreditDb(
|
|
1668
|
+
const creditDb = openCreditDb(join4(configDir, "credit.db"));
|
|
1410
1669
|
creditDb.pragma("busy_timeout = 5000");
|
|
1411
1670
|
try {
|
|
1412
1671
|
releaseRequesterEscrow(creditDb, escrowId);
|
|
@@ -1430,14 +1689,14 @@ Batch Results (${res.results.length} items):`);
|
|
|
1430
1689
|
return msg.includes("NETWORK_ERROR") || msg.includes("ECONNREFUSED") || msg.includes("fetch failed") || msg.includes("Network error");
|
|
1431
1690
|
};
|
|
1432
1691
|
const tryViaRelay = async () => {
|
|
1433
|
-
const { RelayClient } = await import("../websocket-client-
|
|
1434
|
-
const { requestViaRelay } = await import("../client-
|
|
1435
|
-
const requesterId = `${config.owner}:req:${
|
|
1692
|
+
const { RelayClient } = await import("../websocket-client-5MH6QRJK.js");
|
|
1693
|
+
const { requestViaRelay } = await import("../client-HRYRJKSA.js");
|
|
1694
|
+
const requesterId = `${config.owner}:req:${randomUUID4()}`;
|
|
1436
1695
|
const tempRelay = new RelayClient({
|
|
1437
1696
|
registryUrl: config.registry,
|
|
1438
1697
|
owner: requesterId,
|
|
1439
1698
|
token: config.token,
|
|
1440
|
-
card: { id:
|
|
1699
|
+
card: { id: randomUUID4(), owner: requesterId, name: requesterId, description: "Requester", level: 1, spec_version: "1.0", inputs: [], outputs: [], pricing: { credits_per_call: 1 }, availability: { online: false } },
|
|
1441
1700
|
onRequest: async () => ({ error: { code: -32601, message: "Not serving" } }),
|
|
1442
1701
|
silent: true
|
|
1443
1702
|
});
|
|
@@ -1509,7 +1768,7 @@ program.command("status").description("Show credit balance and recent transactio
|
|
|
1509
1768
|
let transactions;
|
|
1510
1769
|
let heldEscrows;
|
|
1511
1770
|
if (config.registry) {
|
|
1512
|
-
const statusIdentityAuth =
|
|
1771
|
+
const statusIdentityAuth = loadIdentityAuth2(config.owner);
|
|
1513
1772
|
const statusLedger = createLedger({
|
|
1514
1773
|
registryUrl: config.registry,
|
|
1515
1774
|
ownerPublicKey: statusIdentityAuth.publicKey,
|
|
@@ -1560,15 +1819,15 @@ program.command("serve").description("Start the AgentBnB gateway server").option
|
|
|
1560
1819
|
console.error("Error: not initialized. Run `agentbnb init` first.");
|
|
1561
1820
|
process.exit(1);
|
|
1562
1821
|
}
|
|
1563
|
-
const { ProcessGuard } = await import("../process-guard-
|
|
1564
|
-
const { ServiceCoordinator } = await import("../service-coordinator-
|
|
1822
|
+
const { ProcessGuard } = await import("../process-guard-QCCBGILS.js");
|
|
1823
|
+
const { ServiceCoordinator } = await import("../service-coordinator-XBNT3SMU.js");
|
|
1565
1824
|
const port = opts.port ? parseInt(opts.port, 10) : config.gateway_port;
|
|
1566
1825
|
const registryPort = parseInt(opts.registryPort, 10);
|
|
1567
1826
|
if (!Number.isFinite(port) || !Number.isFinite(registryPort)) {
|
|
1568
1827
|
console.error("Error: --port and --registry-port must be valid numbers.");
|
|
1569
1828
|
process.exit(1);
|
|
1570
1829
|
}
|
|
1571
|
-
const guard = new ProcessGuard(
|
|
1830
|
+
const guard = new ProcessGuard(join4(getConfigDir(), ".pid"));
|
|
1572
1831
|
const coordinator = new ServiceCoordinator(config, guard);
|
|
1573
1832
|
try {
|
|
1574
1833
|
await coordinator.ensureRunning({
|
|
@@ -1631,7 +1890,7 @@ peersCommand.command("remove <name>").description("Remove a registered peer").ac
|
|
|
1631
1890
|
});
|
|
1632
1891
|
var configCmd = program.command("config").description("Get or set AgentBnB configuration values");
|
|
1633
1892
|
configCmd.command("set <key> <value>").description("Set a configuration value").action((key, value) => {
|
|
1634
|
-
const allowedKeys = ["registry", "tier1", "tier2", "reserve", "idle-threshold", "conductor-public"];
|
|
1893
|
+
const allowedKeys = ["registry", "tier1", "tier2", "reserve", "idle-threshold", "conductor-public", "telegram-notifications", "telegram-bot-token", "telegram-chat-id", "shared-skills"];
|
|
1635
1894
|
if (!allowedKeys.includes(key)) {
|
|
1636
1895
|
console.error(`Unknown config key: ${key}. Valid keys: ${allowedKeys.join(", ")}`);
|
|
1637
1896
|
process.exit(1);
|
|
@@ -1707,6 +1966,35 @@ configCmd.command("set <key> <value>").description("Set a configuration value").
|
|
|
1707
1966
|
console.log(`Set conductor-public = ${boolVal} (conductor card ${boolVal ? "will be" : "will NOT be"} published to registry)`);
|
|
1708
1967
|
return;
|
|
1709
1968
|
}
|
|
1969
|
+
if (key === "telegram-notifications") {
|
|
1970
|
+
if (value !== "true" && value !== "false") {
|
|
1971
|
+
console.error('Error: telegram-notifications must be "true" or "false"');
|
|
1972
|
+
process.exit(1);
|
|
1973
|
+
}
|
|
1974
|
+
config.telegram_notifications = value === "true";
|
|
1975
|
+
saveConfig(config);
|
|
1976
|
+
console.log(`Set telegram-notifications = ${config.telegram_notifications}`);
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1979
|
+
if (key === "telegram-bot-token") {
|
|
1980
|
+
config.telegram_bot_token = value;
|
|
1981
|
+
saveConfig(config);
|
|
1982
|
+
console.log("Set telegram-bot-token");
|
|
1983
|
+
return;
|
|
1984
|
+
}
|
|
1985
|
+
if (key === "telegram-chat-id") {
|
|
1986
|
+
config.telegram_chat_id = value;
|
|
1987
|
+
saveConfig(config);
|
|
1988
|
+
console.log(`Set telegram-chat-id = ${value}`);
|
|
1989
|
+
return;
|
|
1990
|
+
}
|
|
1991
|
+
if (key === "shared-skills") {
|
|
1992
|
+
config.shared_skills = value.trim() === "" ? [] : value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1993
|
+
saveConfig(config);
|
|
1994
|
+
const display = config.shared_skills.length > 0 ? config.shared_skills.join(", ") : "(all skills published)";
|
|
1995
|
+
console.log(`Set shared-skills: ${display}`);
|
|
1996
|
+
return;
|
|
1997
|
+
}
|
|
1710
1998
|
config[key] = value;
|
|
1711
1999
|
saveConfig(config);
|
|
1712
2000
|
console.log(`Set ${key} = ${value}`);
|
|
@@ -1738,11 +2026,97 @@ configCmd.command("get <key>").description("Get a configuration value").action((
|
|
|
1738
2026
|
console.log(String(config.conductor?.public ?? false));
|
|
1739
2027
|
return;
|
|
1740
2028
|
}
|
|
2029
|
+
if (key === "telegram-notifications") {
|
|
2030
|
+
console.log(String(config.telegram_notifications ?? false));
|
|
2031
|
+
return;
|
|
2032
|
+
}
|
|
2033
|
+
if (key === "telegram-bot-token") {
|
|
2034
|
+
console.log(config.telegram_bot_token ?? "(not set)");
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
if (key === "telegram-chat-id") {
|
|
2038
|
+
console.log(config.telegram_chat_id ?? "(not set)");
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
if (key === "shared-skills") {
|
|
2042
|
+
const skills = config.shared_skills ?? [];
|
|
2043
|
+
console.log(skills.length > 0 ? skills.join(", ") : "(all skills published)");
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
1741
2046
|
const value = config[key];
|
|
1742
2047
|
console.log(value !== void 0 ? String(value) : "(not set)");
|
|
1743
2048
|
});
|
|
2049
|
+
var cardsCmd = program.command("cards").description("Manage published capability cards");
|
|
2050
|
+
cardsCmd.command("list").description("List all published capability cards in the local registry").action(() => {
|
|
2051
|
+
const config = loadConfig();
|
|
2052
|
+
if (!config) {
|
|
2053
|
+
console.error("Error: not initialized. Run `agentbnb init` first.");
|
|
2054
|
+
process.exit(1);
|
|
2055
|
+
}
|
|
2056
|
+
const db = openDatabase(config.db_path);
|
|
2057
|
+
try {
|
|
2058
|
+
const cards = listCards(db);
|
|
2059
|
+
if (cards.length === 0) {
|
|
2060
|
+
console.log("No published cards found.");
|
|
2061
|
+
return;
|
|
2062
|
+
}
|
|
2063
|
+
console.log(`Published cards (${cards.length}):`);
|
|
2064
|
+
for (const card of cards) {
|
|
2065
|
+
const v2 = card;
|
|
2066
|
+
const name = String(v2["agent_name"] ?? v2["name"] ?? "(unnamed)");
|
|
2067
|
+
const owner = String(v2["owner"] ?? "");
|
|
2068
|
+
const skills = Array.isArray(v2["skills"]) ? v2["skills"].length : 0;
|
|
2069
|
+
const skillsLabel = skills > 0 ? ` (${skills} skill${skills !== 1 ? "s" : ""})` : "";
|
|
2070
|
+
console.log(` ${card.id} ${name} owner: ${owner}${skillsLabel}`);
|
|
2071
|
+
}
|
|
2072
|
+
} finally {
|
|
2073
|
+
db.close();
|
|
2074
|
+
}
|
|
2075
|
+
});
|
|
2076
|
+
cardsCmd.command("delete <card-id>").description("Delete a published capability card from the local registry").option("--force", "Skip confirmation prompt").action(async (cardId, opts) => {
|
|
2077
|
+
const config = loadConfig();
|
|
2078
|
+
if (!config) {
|
|
2079
|
+
console.error("Error: not initialized. Run `agentbnb init` first.");
|
|
2080
|
+
process.exit(1);
|
|
2081
|
+
}
|
|
2082
|
+
const db = openDatabase(config.db_path);
|
|
2083
|
+
try {
|
|
2084
|
+
const card = getCard(db, cardId);
|
|
2085
|
+
if (!card) {
|
|
2086
|
+
console.error(`Error: card not found: ${cardId}`);
|
|
2087
|
+
process.exit(1);
|
|
2088
|
+
}
|
|
2089
|
+
const v2 = card;
|
|
2090
|
+
const cardName = String(v2["agent_name"] ?? v2["name"] ?? "(unnamed)");
|
|
2091
|
+
const skillCount = Array.isArray(v2["skills"]) ? v2["skills"].length : 0;
|
|
2092
|
+
console.log("Deleting card:");
|
|
2093
|
+
console.log(` Name: "${cardName}"`);
|
|
2094
|
+
console.log(` Owner: ${card.owner}`);
|
|
2095
|
+
console.log(` Skills: ${skillCount}`);
|
|
2096
|
+
console.log(` ID: ${card.id}`);
|
|
2097
|
+
if (!opts.force && process.stdin.isTTY) {
|
|
2098
|
+
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
2099
|
+
const answer = await new Promise((resolve) => {
|
|
2100
|
+
rl.question("Confirm deletion? [y/N] ", resolve);
|
|
2101
|
+
});
|
|
2102
|
+
rl.close();
|
|
2103
|
+
if (answer.toLowerCase() !== "y") {
|
|
2104
|
+
console.log("Cancelled.");
|
|
2105
|
+
process.exit(0);
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
deleteCard(db, cardId, card.owner);
|
|
2109
|
+
console.log(`Deleted: "${cardName}" (${skillCount} skill${skillCount !== 1 ? "s" : ""})`);
|
|
2110
|
+
} catch (err) {
|
|
2111
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2112
|
+
console.error(`Error: ${msg}`);
|
|
2113
|
+
process.exit(1);
|
|
2114
|
+
} finally {
|
|
2115
|
+
db.close();
|
|
2116
|
+
}
|
|
2117
|
+
});
|
|
1744
2118
|
var openclaw = program.command("openclaw").description("OpenClaw integration commands");
|
|
1745
|
-
openclaw.command("sync").description("Read SOUL.md and publish/update a v2.0 capability card").option("--soul-path <path>", "Path to SOUL.md", "./SOUL.md").action(async (opts) => {
|
|
2119
|
+
openclaw.command("sync").description("Read SOUL.md and publish/update a v2.0 capability card").option("--soul-path <path>", "Path to SOUL.md", "./SOUL.md").option("--skills <ids>", "Comma-separated skill IDs to publish (overrides shared-skills config and skill visibility)").action(async (opts) => {
|
|
1746
2120
|
const config = loadConfig();
|
|
1747
2121
|
if (!config) {
|
|
1748
2122
|
console.error("Error: not initialized. Run `agentbnb init` first.");
|
|
@@ -1750,14 +2124,15 @@ openclaw.command("sync").description("Read SOUL.md and publish/update a v2.0 cap
|
|
|
1750
2124
|
}
|
|
1751
2125
|
let content;
|
|
1752
2126
|
try {
|
|
1753
|
-
content =
|
|
2127
|
+
content = readFileSync4(opts.soulPath, "utf-8");
|
|
1754
2128
|
} catch {
|
|
1755
2129
|
console.error(`Error: cannot read SOUL.md at ${opts.soulPath}`);
|
|
1756
2130
|
process.exit(1);
|
|
1757
2131
|
}
|
|
2132
|
+
const sharedSkills = opts.skills ? opts.skills.split(",").map((s) => s.trim()).filter(Boolean) : config.shared_skills && config.shared_skills.length > 0 ? config.shared_skills : void 0;
|
|
1758
2133
|
const db = openDatabase(config.db_path);
|
|
1759
2134
|
try {
|
|
1760
|
-
const card = publishFromSoulV2(db, content, config.owner);
|
|
2135
|
+
const card = publishFromSoulV2(db, content, config.owner, sharedSkills);
|
|
1761
2136
|
console.log(`Published card ${card.id} with ${card.skills.length} skill(s)`);
|
|
1762
2137
|
for (const skill of card.skills) {
|
|
1763
2138
|
const stats = getPricingStats(db, skill.name);
|
|
@@ -1819,7 +2194,7 @@ openclaw.command("rules").description("Print HEARTBEAT.md rules block (or inject
|
|
|
1819
2194
|
}
|
|
1820
2195
|
});
|
|
1821
2196
|
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) => {
|
|
1822
|
-
const { conductAction } = await import("../conduct-
|
|
2197
|
+
const { conductAction } = await import("../conduct-KJUD2RTB.js");
|
|
1823
2198
|
const result = await conductAction(task, opts);
|
|
1824
2199
|
if (opts.json) {
|
|
1825
2200
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -1929,8 +2304,9 @@ Feedback for skill: ${opts.skill} (${feedbacks.length} entries)
|
|
|
1929
2304
|
}
|
|
1930
2305
|
}
|
|
1931
2306
|
});
|
|
2307
|
+
program.command("quickstart").alias("qs").description("One-command setup: init + skills.yaml + MCP registration + serve daemon").option("--owner <name>", "Agent owner name").option("--port <port>", "Gateway port", "7700").option("--no-serve", "Skip starting background daemon").option("--no-mcp", "Skip MCP registration with Claude Code").option("--json", "Output as JSON").action(runQuickstart);
|
|
1932
2308
|
program.command("mcp-server").description("Start an MCP (Model Context Protocol) server for IDE integration").action(async () => {
|
|
1933
|
-
const { startMcpServer } = await import("../server-
|
|
2309
|
+
const { startMcpServer } = await import("../server-I63CXFX3.js");
|
|
1934
2310
|
await startMcpServer();
|
|
1935
2311
|
});
|
|
1936
2312
|
await program.parseAsync(process.argv);
|