agentbnb 8.3.2 → 8.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/{card-T2XJZA5A.js → card-HYTD2BJQ.js} +1 -1
  2. package/dist/{chunk-S7DZHKCG.js → chunk-2PP5MQPD.js} +5 -3
  3. package/dist/{chunk-75OC6E4F.js → chunk-3XPBFF6H.js} +1 -0
  4. package/dist/{chunk-5AH3CMOX.js → chunk-3YQ73ZM6.js} +1 -1
  5. package/dist/{chunk-FELGHDCA.js → chunk-6FZ4WYQL.js} +38 -4
  6. package/dist/{chunk-D242QZCR.js → chunk-6XCN62YU.js} +10 -54
  7. package/dist/chunk-AZKVGC5T.js +53 -0
  8. package/dist/{chunk-NHVHLUYS.js → chunk-CFHCG5FE.js} +6 -217
  9. package/dist/{chunk-5AIYALBX.js → chunk-COA2D7QM.js} +4 -3
  10. package/dist/chunk-G5WKW3ED.js +41 -0
  11. package/dist/{chunk-F3KIEVJ2.js → chunk-HU46M4JA.js} +2 -87
  12. package/dist/{chunk-77KGEDH4.js → chunk-MZSVVG55.js} +2 -41
  13. package/dist/chunk-PCQEHIGF.js +750 -0
  14. package/dist/chunk-PIPCGRCR.js +91 -0
  15. package/dist/{chunk-QFPXZITP.js → chunk-PMRTQ2RL.js} +34 -0
  16. package/dist/{chunk-7IQE34QK.js → chunk-UF6R2RVN.js} +5 -3
  17. package/dist/{chunk-IGQNP3ZO.js → chunk-VAAEBCMU.js} +1 -1
  18. package/dist/chunk-VRPLSK34.js +214 -0
  19. package/dist/{chunk-BARHNIKG.js → chunk-WK2QSO4E.js} +1 -1
  20. package/dist/chunk-WPB5LFGI.js +132 -0
  21. package/dist/chunk-XGOA5J2K.js +173 -0
  22. package/dist/{chunk-VJ7XBEY6.js → chunk-Z5726VPY.js} +6 -6
  23. package/dist/cli/index.js +118 -789
  24. package/dist/conduct-FZPUMPCI.js +25 -0
  25. package/dist/{conduct-VYYBCPHA.js → conduct-J2NXU6RM.js} +12 -9
  26. package/dist/{conductor-mode-SBDCRIX6.js → conductor-mode-HNNMWZIH.js} +11 -8
  27. package/dist/config-IRWLG6IW.js +12 -0
  28. package/dist/{execute-FZLQGIXB.js → execute-UAD5T3BQ.js} +1 -1
  29. package/dist/{execute-TEZPQ5WP.js → execute-UP46R7KS.js} +6 -5
  30. package/dist/index.js +34 -0
  31. package/dist/openclaw-setup-KA72IIEW.js +296 -0
  32. package/dist/openclaw-skills-CT673RBL.js +370 -0
  33. package/dist/{peers-K7FSHPN3.js → peers-F2EWUMVQ.js} +2 -2
  34. package/dist/{publish-capability-HVYILTPR.js → publish-capability-GNH5FHKG.js} +2 -2
  35. package/dist/{request-KJNKR27T.js → request-IM3ZLAOA.js} +13 -9
  36. package/dist/scanner-F5VNV5FP.js +8 -0
  37. package/dist/{serve-skill-GC6NIQ5T.js → serve-skill-6RKMVDMK.js} +6 -5
  38. package/dist/{server-6I7GU2OZ.js → server-MH7FTZFN.js} +11 -9
  39. package/dist/{service-coordinator-LZJCAQSJ.js → service-coordinator-R5LZVM6A.js} +31 -26
  40. package/dist/skills/agentbnb/bootstrap.js +34 -2
  41. package/dist/store-4Z446745.js +32 -0
  42. package/dist/writer-4QJ3U3WE.js +16 -0
  43. package/package.json +1 -1
  44. package/skills/agentbnb/bootstrap.ts +45 -2
  45. package/dist/conduct-4JDMWBQD.js +0 -22
@@ -0,0 +1,91 @@
1
+ import {
2
+ AgentBnBError
3
+ } from "./chunk-I7KWA7OB.js";
4
+
5
+ // src/cli/remote-registry.ts
6
+ var RegistryTimeoutError = class extends AgentBnBError {
7
+ constructor(url) {
8
+ super(
9
+ `Registry at ${url} did not respond within 5s. Showing local results only.`,
10
+ "REGISTRY_TIMEOUT"
11
+ );
12
+ this.name = "RegistryTimeoutError";
13
+ }
14
+ };
15
+ var RegistryConnectionError = class extends AgentBnBError {
16
+ constructor(url) {
17
+ super(
18
+ `Cannot reach ${url}. Is the registry running? Showing local results only.`,
19
+ "REGISTRY_CONNECTION"
20
+ );
21
+ this.name = "RegistryConnectionError";
22
+ }
23
+ };
24
+ var RegistryAuthError = class extends AgentBnBError {
25
+ constructor(url) {
26
+ super(
27
+ `Authentication failed for ${url}. Run \`agentbnb config set token <your-token>\`.`,
28
+ "REGISTRY_AUTH"
29
+ );
30
+ this.name = "RegistryAuthError";
31
+ }
32
+ };
33
+ async function fetchRemoteCards(registryUrl, params, timeoutMs = 5e3) {
34
+ let cardsUrl;
35
+ try {
36
+ cardsUrl = new URL("/cards", registryUrl);
37
+ } catch {
38
+ throw new AgentBnBError(`Invalid registry URL: ${registryUrl}`, "INVALID_REGISTRY_URL");
39
+ }
40
+ const searchParams = new URLSearchParams();
41
+ if (params.q !== void 0) searchParams.set("q", params.q);
42
+ if (params.level !== void 0) searchParams.set("level", String(params.level));
43
+ if (params.online !== void 0) searchParams.set("online", String(params.online));
44
+ if (params.tag !== void 0) searchParams.set("tag", params.tag);
45
+ searchParams.set("limit", "100");
46
+ cardsUrl.search = searchParams.toString();
47
+ const controller = new AbortController();
48
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
49
+ let response;
50
+ try {
51
+ response = await fetch(cardsUrl.toString(), { signal: controller.signal });
52
+ } catch (err) {
53
+ clearTimeout(timer);
54
+ const isTimeout = err instanceof Error && err.name === "AbortError";
55
+ if (isTimeout) {
56
+ throw new RegistryTimeoutError(registryUrl);
57
+ }
58
+ throw new RegistryConnectionError(registryUrl);
59
+ } finally {
60
+ clearTimeout(timer);
61
+ }
62
+ if (response.status === 401 || response.status === 403) {
63
+ throw new RegistryAuthError(registryUrl);
64
+ }
65
+ if (!response.ok) {
66
+ throw new RegistryConnectionError(registryUrl);
67
+ }
68
+ const body = await response.json();
69
+ return body.items;
70
+ }
71
+ function mergeResults(localCards, remoteCards, hasQuery) {
72
+ const taggedLocal = localCards.map((c) => ({ ...c, source: "local" }));
73
+ const taggedRemote = remoteCards.map((c) => ({ ...c, source: "remote" }));
74
+ const localIds = new Set(localCards.map((c) => c.id));
75
+ const dedupedRemote = taggedRemote.filter((c) => !localIds.has(c.id));
76
+ if (!hasQuery) {
77
+ return [...taggedLocal, ...dedupedRemote];
78
+ }
79
+ const result = [];
80
+ const maxLen = Math.max(taggedLocal.length, dedupedRemote.length);
81
+ for (let i = 0; i < maxLen; i++) {
82
+ if (i < taggedLocal.length) result.push(taggedLocal[i]);
83
+ if (i < dedupedRemote.length) result.push(dedupedRemote[i]);
84
+ }
85
+ return result;
86
+ }
87
+
88
+ export {
89
+ fetchRemoteCards,
90
+ mergeResults
91
+ };
@@ -41,6 +41,29 @@ async function notifyTelegramSkillExecuted(opts) {
41
41
  body: JSON.stringify({ chat_id: chatId, text })
42
42
  });
43
43
  }
44
+ async function notifyTelegramSkillFailed(opts) {
45
+ const cfg = loadConfig();
46
+ if (!cfg?.telegram_notifications) return;
47
+ const token = cfg.telegram_bot_token ?? process.env["TELEGRAM_BOT_TOKEN"];
48
+ const chatId = cfg.telegram_chat_id ?? process.env["TELEGRAM_CHAT_ID"];
49
+ if (!token || !chatId) return;
50
+ const balance = getBalance(opts.creditDb, opts.owner);
51
+ const skillLabel = opts.skillId ? `${opts.skillName} (${opts.skillId})` : opts.skillName;
52
+ const text = [
53
+ "[AgentBnB] Skill failed",
54
+ `Skill: ${skillLabel}`,
55
+ `Requester: ${opts.requester}`,
56
+ `Reason: ${opts.failureReason}`,
57
+ `Error: ${opts.message}`,
58
+ `Balance: ${balance} credits`,
59
+ `Latency: ${opts.latencyMs}ms`
60
+ ].join("\n");
61
+ await fetch(`https://api.telegram.org/bot${token}/sendMessage`, {
62
+ method: "POST",
63
+ headers: { "Content-Type": "application/json" },
64
+ body: JSON.stringify({ chat_id: chatId, text })
65
+ });
66
+ }
44
67
  async function executeCapabilityRequest(opts) {
45
68
  const {
46
69
  registryDb,
@@ -141,6 +164,17 @@ async function executeCapabilityRequest(opts) {
141
164
  });
142
165
  } catch {
143
166
  }
167
+ notifyTelegramSkillFailed({
168
+ creditDb,
169
+ owner: card.owner,
170
+ skillName: cardName,
171
+ skillId: resolvedSkillId ?? null,
172
+ requester,
173
+ latencyMs,
174
+ failureReason,
175
+ message
176
+ }).catch(() => {
177
+ });
144
178
  return { success: false, error: { code: -32603, message } };
145
179
  };
146
180
  const handleSuccess = (result, latencyMs) => {
@@ -3,11 +3,13 @@ import {
3
3
  } from "./chunk-3MJT4PZG.js";
4
4
  import {
5
5
  scorePeers
6
- } from "./chunk-D242QZCR.js";
6
+ } from "./chunk-6XCN62YU.js";
7
+ import {
8
+ fetchRemoteCards
9
+ } from "./chunk-PIPCGRCR.js";
7
10
  import {
8
- fetchRemoteCards,
9
11
  searchCards
10
- } from "./chunk-F3KIEVJ2.js";
12
+ } from "./chunk-HU46M4JA.js";
11
13
  import {
12
14
  requestCapability,
13
15
  requestCapabilityBatch
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  insertCard
3
- } from "./chunk-5AIYALBX.js";
3
+ } from "./chunk-COA2D7QM.js";
4
4
  import {
5
5
  CapabilityCardSchema
6
6
  } from "./chunk-I7KWA7OB.js";
@@ -0,0 +1,214 @@
1
+ import {
2
+ AgentBnBError
3
+ } from "./chunk-I7KWA7OB.js";
4
+
5
+ // src/discovery/mdns.ts
6
+ import { Bonjour } from "bonjour-service";
7
+ var bonjourInstance = null;
8
+ function getBonjour() {
9
+ if (bonjourInstance === null) {
10
+ bonjourInstance = new Bonjour();
11
+ }
12
+ return bonjourInstance;
13
+ }
14
+ function announceGateway(owner, port, metadata) {
15
+ const bonjour = getBonjour();
16
+ const txt = {
17
+ owner,
18
+ version: "1.0",
19
+ ...metadata
20
+ };
21
+ bonjour.publish({
22
+ name: owner,
23
+ type: "agentbnb",
24
+ port,
25
+ txt
26
+ });
27
+ }
28
+ function discoverLocalAgents(onFound, onDown) {
29
+ const bonjour = getBonjour();
30
+ const browser = bonjour.find({ type: "agentbnb" });
31
+ browser.on("up", (service) => {
32
+ const addresses = service.addresses ?? [];
33
+ const ipv4Addresses = addresses.filter((addr) => !addr.includes(":"));
34
+ const host = ipv4Addresses.length > 0 ? ipv4Addresses[0] : service.host;
35
+ const url = `http://${host}:${service.port}`;
36
+ const owner = service.txt?.owner ?? service.name;
37
+ onFound({
38
+ name: service.name,
39
+ url,
40
+ owner
41
+ });
42
+ });
43
+ if (onDown) {
44
+ browser.on("down", (service) => {
45
+ const addresses = service.addresses ?? [];
46
+ const ipv4Addresses = addresses.filter((addr) => !addr.includes(":"));
47
+ const host = ipv4Addresses.length > 0 ? ipv4Addresses[0] : service.host;
48
+ const url = `http://${host}:${service.port}`;
49
+ const owner = service.txt?.owner ?? service.name;
50
+ onDown({
51
+ name: service.name,
52
+ url,
53
+ owner
54
+ });
55
+ });
56
+ }
57
+ return {
58
+ stop: () => browser.stop()
59
+ };
60
+ }
61
+ async function stopAnnouncement() {
62
+ if (bonjourInstance === null) {
63
+ return;
64
+ }
65
+ const instance = bonjourInstance;
66
+ bonjourInstance = null;
67
+ await new Promise((resolve2) => {
68
+ instance.unpublishAll(() => {
69
+ instance.destroy();
70
+ resolve2();
71
+ });
72
+ });
73
+ }
74
+
75
+ // src/runtime/resolve-self-cli.ts
76
+ import { execFileSync } from "child_process";
77
+ import { existsSync, realpathSync } from "fs";
78
+ import { createRequire } from "module";
79
+ import { homedir } from "os";
80
+ import { basename, dirname, isAbsolute, join, resolve } from "path";
81
+ function resolveSelfCli() {
82
+ const require2 = createRequire(import.meta.url);
83
+ return resolveSelfCliWithDeps({
84
+ argv1: process.argv[1],
85
+ cwd: process.cwd(),
86
+ platform: process.platform,
87
+ homeDir: homedir(),
88
+ envPath: process.env["PATH"],
89
+ nodeExecDir: dirname(process.execPath),
90
+ exists: existsSync,
91
+ realpath: realpathSync,
92
+ runWhich: (pathEnv) => execFileSync("which", ["agentbnb"], {
93
+ encoding: "utf8",
94
+ env: { ...process.env, PATH: pathEnv }
95
+ }).trim(),
96
+ requireResolve: (id) => require2.resolve(id)
97
+ });
98
+ }
99
+ function resolveSelfCliWithDeps(deps) {
100
+ const tried = [];
101
+ const tryCandidate = (rawPath, label, requireCliShape = false) => {
102
+ if (!rawPath || rawPath.trim().length === 0) {
103
+ tried.push(`${label}: <empty>`);
104
+ return null;
105
+ }
106
+ const maybeAbsolute = isAbsolute(rawPath) ? rawPath : resolve(deps.cwd, rawPath);
107
+ tried.push(`${label}: ${maybeAbsolute}`);
108
+ if (!deps.exists(maybeAbsolute)) return null;
109
+ const resolvedPath = safeRealpath(deps.realpath, maybeAbsolute);
110
+ if (requireCliShape && !looksLikeAgentbnbCli(resolvedPath)) return null;
111
+ return resolvedPath;
112
+ };
113
+ const argvPath = tryCandidate(deps.argv1, "process.argv[1]", true);
114
+ if (argvPath) return argvPath;
115
+ const fullPathEnv = buildFullPathEnv(deps.envPath, deps.homeDir, deps.nodeExecDir);
116
+ tried.push(`which agentbnb PATH=${fullPathEnv}`);
117
+ try {
118
+ const whichPath = tryCandidate(deps.runWhich(fullPathEnv), "which agentbnb");
119
+ if (whichPath) return whichPath;
120
+ } catch (err) {
121
+ tried.push(`which agentbnb failed: ${extractErrorMessage(err)}`);
122
+ }
123
+ const npmGlobalCandidates = ["/usr/local/bin/agentbnb", "/opt/homebrew/bin/agentbnb"];
124
+ for (const candidate of npmGlobalCandidates) {
125
+ const resolvedPath = tryCandidate(candidate, "npm-global");
126
+ if (resolvedPath) return resolvedPath;
127
+ }
128
+ const pnpmCandidates = getPnpmGlobalCandidates(deps.platform, deps.homeDir);
129
+ for (const candidate of pnpmCandidates) {
130
+ const resolvedPath = tryCandidate(candidate, "pnpm-global");
131
+ if (resolvedPath) return resolvedPath;
132
+ }
133
+ if (deps.nodeExecDir) {
134
+ const execDirCandidate = tryCandidate(
135
+ join(deps.nodeExecDir, "agentbnb"),
136
+ "node-execpath-dir"
137
+ );
138
+ if (execDirCandidate) return execDirCandidate;
139
+ }
140
+ try {
141
+ const requireResolved = deps.requireResolve("agentbnb/dist/cli/index.js");
142
+ const resolvedPath = tryCandidate(requireResolved, "require.resolve(agentbnb/dist/cli/index.js)");
143
+ if (resolvedPath) return resolvedPath;
144
+ } catch (err) {
145
+ tried.push(`require.resolve(agentbnb/dist/cli/index.js) failed: ${extractErrorMessage(err)}`);
146
+ }
147
+ throw new AgentBnBError(
148
+ `Unable to resolve absolute path to agentbnb CLI.
149
+ Paths tried:
150
+ ${tried.map((item) => `- ${item}`).join("\n")}`,
151
+ "CLI_ENTRY_NOT_FOUND"
152
+ );
153
+ }
154
+ function buildFullPathEnv(pathEnv, homeDir, nodeExecDir) {
155
+ const values = /* @__PURE__ */ new Set();
156
+ for (const item of (pathEnv ?? "").split(":")) {
157
+ if (item.trim()) values.add(item.trim());
158
+ }
159
+ for (const extra of [
160
+ "/usr/local/bin",
161
+ "/opt/homebrew/bin",
162
+ "/usr/bin",
163
+ "/bin",
164
+ "/usr/sbin",
165
+ "/sbin",
166
+ join(homeDir, ".local", "bin"),
167
+ join(homeDir, "Library", "pnpm"),
168
+ join(homeDir, ".local", "share", "pnpm")
169
+ ]) {
170
+ values.add(extra);
171
+ }
172
+ if (nodeExecDir) {
173
+ values.add(nodeExecDir);
174
+ }
175
+ return [...values].join(":");
176
+ }
177
+ function safeRealpath(realpath, path) {
178
+ try {
179
+ return realpath(path);
180
+ } catch {
181
+ return path;
182
+ }
183
+ }
184
+ function getPnpmGlobalCandidates(platform, homeDir) {
185
+ const candidates = /* @__PURE__ */ new Set();
186
+ if (platform === "darwin") {
187
+ candidates.add(join(homeDir, "Library", "pnpm", "agentbnb"));
188
+ }
189
+ if (platform === "linux") {
190
+ candidates.add(join(homeDir, ".local", "share", "pnpm", "agentbnb"));
191
+ }
192
+ candidates.add(join(homeDir, "Library", "pnpm", "agentbnb"));
193
+ candidates.add(join(homeDir, ".local", "share", "pnpm", "agentbnb"));
194
+ return [...candidates];
195
+ }
196
+ function looksLikeAgentbnbCli(path) {
197
+ const normalized = path.replace(/\\/g, "/").toLowerCase();
198
+ const fileName = basename(normalized);
199
+ if (fileName === "agentbnb" || fileName === "agentbnb.cmd" || fileName === "agentbnb.exe") {
200
+ return true;
201
+ }
202
+ return normalized.includes("/agentbnb/dist/cli/index.") || normalized.endsWith("/dist/cli/index.js") || normalized.endsWith("/dist/cli/index.mjs") || normalized.endsWith("/dist/cli/index.cjs");
203
+ }
204
+ function extractErrorMessage(err) {
205
+ if (err instanceof Error && err.message) return err.message;
206
+ return String(err);
207
+ }
208
+
209
+ export {
210
+ announceGateway,
211
+ discoverLocalAgents,
212
+ stopAnnouncement,
213
+ resolveSelfCli
214
+ };
@@ -7,7 +7,7 @@ import {
7
7
  openCreditDb,
8
8
  releaseEscrow,
9
9
  settleEscrow
10
- } from "./chunk-F3KIEVJ2.js";
10
+ } from "./chunk-HU46M4JA.js";
11
11
  import {
12
12
  generateKeyPair,
13
13
  loadKeyPair,
@@ -0,0 +1,132 @@
1
+ // src/workspace/writer.ts
2
+ import { existsSync, readFileSync, writeFileSync } from "fs";
3
+ import { join } from "path";
4
+ var SOUL_SECTION_HEADER = "## AgentBnB Network Trading";
5
+ var HEARTBEAT_SECTION_HEADER = "<!-- agentbnb-trading-rules -->";
6
+ var TOOLS_SECTION_HEADER = "<!-- agentbnb-cli -->";
7
+ function renderSkillsTable(skills) {
8
+ const rows = skills.map((s) => {
9
+ const price = s.pricing?.credits_per_call ?? 3;
10
+ const desc = (s.description ?? "").split("\n")[0]?.trim().slice(0, 60) ?? "";
11
+ return `| ${s.id} | ${price} cr | ${desc} |`;
12
+ }).join("\n");
13
+ return ["| Skill | Price | Description |", "|-------|-------|-------------|", rows].join("\n");
14
+ }
15
+ function appendSoulMdTradingSection(soulPath, skills, agentbnbDir) {
16
+ const content = readFileSync(soulPath, "utf-8");
17
+ if (content.includes(SOUL_SECTION_HEADER)) return;
18
+ const skillsTable = renderSkillsTable(skills);
19
+ const section = [
20
+ "",
21
+ "---",
22
+ SOUL_SECTION_HEADER,
23
+ "",
24
+ `This agent is connected to the [AgentBnB](https://agentbnb.dev) P2P capability-sharing network.`,
25
+ "",
26
+ "### Shared Skills",
27
+ skillsTable,
28
+ "",
29
+ "### Trading Rules",
30
+ `- AGENTBNB_DIR: ${agentbnbDir}`,
31
+ "- Reserve floor: 20 credits (never auto-spend below this)",
32
+ "- Auto-trade limit: 10 credits per transaction (above \u2192 ask owner)",
33
+ `- Request capabilities: \`AGENTBNB_DIR=${agentbnbDir} agentbnb request <card_id> --skill <skill> --params '<json>'\``,
34
+ `- Check balance: \`AGENTBNB_DIR=${agentbnbDir} agentbnb status\``,
35
+ `- Browse network: \`AGENTBNB_DIR=${agentbnbDir} agentbnb discover\``,
36
+ "",
37
+ "### GEP Evolution",
38
+ "AgentBnB tracks capability evolution in the `gep/` directory:",
39
+ "- `gep/genes.json` \u2014 capability traits earned through successful executions",
40
+ "- `gep/capsules.json` \u2014 packaged skill bundles for sharing",
41
+ "- `gep/events.jsonl` \u2014 append-only evolution event log",
42
+ "",
43
+ "### How to Earn",
44
+ "Credits are escrowed when another agent hires your skill and settled on success (5% network fee applies)."
45
+ ].join("\n");
46
+ writeFileSync(soulPath, content + section, "utf-8");
47
+ }
48
+ function updateSoulMdSkillsTable(soulPath, skills) {
49
+ const content = readFileSync(soulPath, "utf-8");
50
+ if (!content.includes(SOUL_SECTION_HEADER)) return;
51
+ const newTable = renderSkillsTable(skills);
52
+ const updated = content.replace(
53
+ /(### Shared Skills\n)\|.*?\|[\s\S]*?(?=\n###|\n---|\n$)/,
54
+ `$1${newTable}
55
+ `
56
+ );
57
+ if (updated !== content) {
58
+ writeFileSync(soulPath, updated, "utf-8");
59
+ }
60
+ }
61
+ function appendHeartbeatTradingSection(heartbeatPath, agentbnbDir) {
62
+ if (!existsSync(heartbeatPath)) return;
63
+ const content = readFileSync(heartbeatPath, "utf-8");
64
+ if (content.includes(HEARTBEAT_SECTION_HEADER)) return;
65
+ const section = [
66
+ "",
67
+ HEARTBEAT_SECTION_HEADER,
68
+ "## AgentBnB Trading Cycle",
69
+ "",
70
+ "- Every idle period (>10 min): check `AGENTBNB_DIR=" + agentbnbDir + " agentbnb status`",
71
+ "- If balance < 20 credits: pause auto-requests, notify owner",
72
+ "- If idle_rate > 70%: auto-share idle skills to network",
73
+ "- Settle completed escrows automatically (no action needed)",
74
+ "<!-- /agentbnb-trading-rules -->"
75
+ ].join("\n");
76
+ writeFileSync(heartbeatPath, content + section, "utf-8");
77
+ }
78
+ function appendToolsMdAgentBnB(toolsPath, agentbnbDir) {
79
+ if (!existsSync(toolsPath)) return;
80
+ const content = readFileSync(toolsPath, "utf-8");
81
+ if (content.includes(TOOLS_SECTION_HEADER)) return;
82
+ const envPrefix = `AGENTBNB_DIR=${agentbnbDir}`;
83
+ const section = [
84
+ "",
85
+ TOOLS_SECTION_HEADER,
86
+ "## AgentBnB CLI",
87
+ "",
88
+ "```bash",
89
+ `# Check status & balance`,
90
+ `${envPrefix} agentbnb status`,
91
+ "",
92
+ `# Discover agents on the network`,
93
+ `${envPrefix} agentbnb discover`,
94
+ "",
95
+ `# Request a capability`,
96
+ `${envPrefix} agentbnb request <card_id> --skill <skill_id> --params '{"key":"value"}'`,
97
+ "",
98
+ `# Manage shared skills`,
99
+ `${envPrefix} agentbnb openclaw skills list`,
100
+ `${envPrefix} agentbnb openclaw skills stats`,
101
+ "```",
102
+ "<!-- /agentbnb-cli -->"
103
+ ].join("\n");
104
+ writeFileSync(toolsPath, content + section, "utf-8");
105
+ }
106
+ function writeBootstrapMd(bootstrapPath, agentbnbDir) {
107
+ const content = [
108
+ "# AgentBnB First-Run Setup",
109
+ "",
110
+ "Run this command to connect to the AgentBnB network:",
111
+ "",
112
+ "```bash",
113
+ `AGENTBNB_DIR=${agentbnbDir} agentbnb openclaw setup`,
114
+ "```",
115
+ "",
116
+ "After setup completes, delete this file and tell your owner:",
117
+ `"AgentBnB setup complete! I'm now connected to the network."`,
118
+ "",
119
+ "---",
120
+ "_Generated by AgentBnB bootstrap on first install._"
121
+ ].join("\n");
122
+ writeFileSync(bootstrapPath, content, "utf-8");
123
+ }
124
+
125
+ export {
126
+ join,
127
+ appendSoulMdTradingSection,
128
+ updateSoulMdSkillsTable,
129
+ appendHeartbeatTradingSection,
130
+ appendToolsMdAgentBnB,
131
+ writeBootstrapMd
132
+ };
@@ -0,0 +1,173 @@
1
+ // src/workspace/scanner.ts
2
+ import { existsSync, readdirSync, readFileSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ function heuristicPrice(skillName) {
6
+ const lower = skillName.toLowerCase();
7
+ if (/voice|tts|elevenlabs/.test(lower)) return 4;
8
+ if (/crawl|browser|cf/.test(lower)) return 3;
9
+ if (/scrape|stealth/.test(lower)) return 5;
10
+ if (/search|kb|knowledge/.test(lower)) return 2;
11
+ return 3;
12
+ }
13
+ function firstParagraph(md) {
14
+ const lines = md.split("\n");
15
+ const paragraphLines = [];
16
+ let inParagraph = false;
17
+ for (const line of lines) {
18
+ const trimmed = line.trim();
19
+ if (trimmed.startsWith("#")) continue;
20
+ if (/^---+$/.test(trimmed)) continue;
21
+ if (trimmed.length > 0) {
22
+ inParagraph = true;
23
+ paragraphLines.push(trimmed);
24
+ } else if (inParagraph) {
25
+ break;
26
+ }
27
+ }
28
+ return paragraphLines.join(" ");
29
+ }
30
+ function readChannel(agentsDir, agentName) {
31
+ const openclawJsonPath = join(agentsDir, agentName, "openclaw.json");
32
+ if (!existsSync(openclawJsonPath)) return "unknown";
33
+ try {
34
+ const raw = readFileSync(openclawJsonPath, "utf-8");
35
+ const parsed = JSON.parse(raw);
36
+ const channel = parsed["channel"] ?? parsed["type"] ?? "unknown";
37
+ return typeof channel === "string" ? channel : "unknown";
38
+ } catch {
39
+ return "unknown";
40
+ }
41
+ }
42
+ function countSkills(brainDir) {
43
+ const skillsDir = join(brainDir, "skills");
44
+ if (!existsSync(skillsDir)) return 0;
45
+ try {
46
+ return readdirSync(skillsDir, { withFileTypes: true }).filter((e) => e.isFile() || e.isDirectory()).length;
47
+ } catch {
48
+ return 0;
49
+ }
50
+ }
51
+ function scanAgents() {
52
+ const openclawDir = join(homedir(), ".openclaw");
53
+ const brainsDir = join(openclawDir, "workspace", "brains");
54
+ const agentsDir = join(openclawDir, "agents");
55
+ const results = [];
56
+ const seenNames = /* @__PURE__ */ new Set();
57
+ if (existsSync(brainsDir)) {
58
+ const entries = readdirSync(brainsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
59
+ for (const name of entries) {
60
+ const brainDir = join(brainsDir, name);
61
+ const soulPath = join(brainDir, "SOUL.md");
62
+ const description = existsSync(soulPath) ? firstParagraph(readFileSync(soulPath, "utf-8")) : "";
63
+ results.push({
64
+ name,
65
+ description,
66
+ skillCount: countSkills(brainDir),
67
+ channel: readChannel(agentsDir, name),
68
+ brainDir,
69
+ agentbnbDir: join(agentsDir, name, ".agentbnb")
70
+ });
71
+ seenNames.add(name);
72
+ }
73
+ }
74
+ if (existsSync(agentsDir)) {
75
+ const entries = readdirSync(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).filter((name) => !seenNames.has(name));
76
+ for (const name of entries) {
77
+ const agentDir = join(agentsDir, name);
78
+ const soulPath = join(agentDir, "SOUL.md");
79
+ const description = existsSync(soulPath) ? firstParagraph(readFileSync(soulPath, "utf-8")) : "";
80
+ results.push({
81
+ name,
82
+ description,
83
+ skillCount: 0,
84
+ channel: readChannel(agentsDir, name),
85
+ brainDir: "",
86
+ agentbnbDir: join(agentDir, ".agentbnb")
87
+ });
88
+ seenNames.add(name);
89
+ }
90
+ }
91
+ return results.sort((a, b) => a.name.localeCompare(b.name));
92
+ }
93
+ function parseCapabilitiesFromSoul(soulContent) {
94
+ const results = [];
95
+ const lines = soulContent.split("\n");
96
+ let currentName = null;
97
+ const descLines = [];
98
+ for (const line of lines) {
99
+ const h2Match = /^## (.+)$/.exec(line);
100
+ if (h2Match) {
101
+ if (currentName) {
102
+ results.push({ name: currentName, description: descLines.join(" ").trim() });
103
+ }
104
+ currentName = h2Match[1].trim();
105
+ if (currentName === "AgentBnB Network Trading" || currentName.startsWith("Trading") || currentName === "Overview" || currentName === "About") {
106
+ currentName = null;
107
+ }
108
+ descLines.length = 0;
109
+ } else if (currentName && descLines.length === 0 && line.trim().length > 0) {
110
+ descLines.push(line.trim());
111
+ }
112
+ }
113
+ if (currentName) {
114
+ results.push({ name: currentName, description: descLines.join(" ").trim() });
115
+ }
116
+ return results;
117
+ }
118
+ function scanCapabilities(brainDir) {
119
+ const results = [];
120
+ const seen = /* @__PURE__ */ new Set();
121
+ const soulPath = join(brainDir, "SOUL.md");
122
+ if (existsSync(soulPath)) {
123
+ const soulContent = readFileSync(soulPath, "utf-8");
124
+ for (const { name, description } of parseCapabilitiesFromSoul(soulContent)) {
125
+ if (!seen.has(name)) {
126
+ seen.add(name);
127
+ results.push({
128
+ name,
129
+ description,
130
+ source: "soul_md",
131
+ suggestedPrice: heuristicPrice(name)
132
+ });
133
+ }
134
+ }
135
+ }
136
+ const skillsDir = join(brainDir, "skills");
137
+ if (existsSync(skillsDir)) {
138
+ const entries = readdirSync(skillsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
139
+ for (const skillDirName of entries) {
140
+ const skillMdPath = join(skillsDir, skillDirName, "SKILL.md");
141
+ if (!existsSync(skillMdPath)) continue;
142
+ try {
143
+ const content = readFileSync(skillMdPath, "utf-8");
144
+ const fmMatch = /^---\n([\s\S]*?)\n---/.exec(content);
145
+ let name = skillDirName;
146
+ let description = "";
147
+ if (fmMatch) {
148
+ const fm = fmMatch[1];
149
+ const nameMatch = /^name:\s*(.+)$/m.exec(fm);
150
+ const descMatch = /^description:\s*(.+)$/m.exec(fm);
151
+ if (nameMatch) name = nameMatch[1].trim();
152
+ if (descMatch) description = descMatch[1].trim();
153
+ }
154
+ if (!seen.has(name)) {
155
+ seen.add(name);
156
+ results.push({
157
+ name,
158
+ description,
159
+ source: "skill_md",
160
+ suggestedPrice: heuristicPrice(name)
161
+ });
162
+ }
163
+ } catch {
164
+ }
165
+ }
166
+ }
167
+ return results;
168
+ }
169
+
170
+ export {
171
+ scanAgents,
172
+ scanCapabilities
173
+ };