agentbnb 7.0.0-beta.1 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{card-REW7BSWW.js → card-EX2EYGCZ.js} +1 -1
- package/dist/{chunk-PU7LXOQ3.js → chunk-3LWBH7P3.js} +72 -3
- package/dist/{chunk-2HSUPCBT.js → chunk-5AAFG2V2.js} +3 -3
- package/dist/{chunk-GO4FVRVN.js → chunk-5GME4KJZ.js} +5 -5
- package/dist/{chunk-VPQ44XKE.js → chunk-64AK4FJM.js} +2 -2
- package/dist/{chunk-K5FO42YF.js → chunk-7EF3HYVZ.js} +24 -1
- package/dist/{chunk-EAD4A4KG.js → chunk-ALX4WS3A.js} +2 -2
- package/dist/{chunk-ETGOKDFR.js → chunk-B2VJTKO5.js} +2 -2
- package/dist/{chunk-PGDBUUGR.js → chunk-C537SFHV.js} +5 -5
- package/dist/{chunk-F53QQIM2.js → chunk-CUONY5TO.js} +1 -1
- package/dist/{chunk-J2K5S5MX.js → chunk-D6RKW2XG.js} +67 -1
- package/dist/{chunk-APEG4QIN.js → chunk-E2OKP5CY.js} +4 -4
- package/dist/{chunk-FK2MDNTB.js → chunk-FTZTEHYG.js} +1 -1
- package/dist/{chunk-Y7T6IMM3.js → chunk-GKVTD4EZ.js} +1 -1
- package/dist/{chunk-VMH2YS2I.js → chunk-KF3TZHA5.js} +1 -1
- package/dist/{chunk-574W3HHE.js → chunk-LJM7FHPM.js} +1 -1
- package/dist/{chunk-KA2VIEGM.js → chunk-O2OYBAVR.js} +1 -1
- package/dist/{chunk-PSQHUZ7X.js → chunk-OH7BP5NP.js} +1 -1
- package/dist/{chunk-EHSHB7TY.js → chunk-SSK653A6.js} +67 -2
- package/dist/{chunk-BP3L2TET.js → chunk-TBJ3FZKZ.js} +2 -2
- package/dist/{chunk-3CIMVISQ.js → chunk-WVY2W7AA.js} +4 -0
- package/dist/{chunk-DUW6RX6I.js → chunk-X32NE6V4.js} +1 -1
- package/dist/{chunk-CWYPTQRQ.js → chunk-YHY7OG6S.js} +5 -5
- package/dist/{chunk-TW65F5EU.js → chunk-Z4MCGKTL.js} +6 -2
- package/dist/cli/index.js +44 -23
- package/dist/{client-HRYRJKSA.js → client-HKV3QWZ3.js} +3 -3
- package/dist/{conduct-JNYJCDHQ.js → conduct-W6XF6DJW.js} +13 -13
- package/dist/conduct-YB64OHI6.js +22 -0
- package/dist/{conductor-mode-2VVFMKVE.js → conductor-mode-2GSLHVN6.js} +3 -3
- package/dist/{conductor-mode-VGUU54QI.js → conductor-mode-AKREGDIU.js} +10 -10
- package/dist/{execute-MOXSSA3Q.js → execute-AYQWORVH.js} +6 -6
- package/dist/{execute-I4PKSNJM.js → execute-EPE6MZLT.js} +3 -3
- package/dist/index.d.ts +262 -10
- package/dist/index.js +438 -26
- package/dist/{process-guard-QCCBGILS.js → process-guard-GH5LRNWO.js} +1 -1
- package/dist/{publish-capability-TS6CNR5G.js → publish-capability-AH2HDW54.js} +3 -3
- package/dist/{request-E7TA7COA.js → request-HCCXSKAY.js} +12 -12
- package/dist/{serve-skill-HIOWYKRU.js → serve-skill-SZAQT5T5.js} +8 -8
- package/dist/{server-I63CXFX3.js → server-MHMAYXWZ.js} +11 -11
- package/dist/{service-coordinator-XBNT3SMU.js → service-coordinator-WGH6B2VT.js} +375 -48
- package/dist/skills/agentbnb/bootstrap.js +393 -58
- package/dist/{websocket-client-PFGVTXNE.js → websocket-client-4Z5P54RU.js} +1 -1
- package/dist/websocket-client-QOVARTRN.js +7 -0
- package/package.json +17 -11
- package/skills/agentbnb/bootstrap.test.ts +9 -0
- package/skills/agentbnb/bootstrap.ts +51 -26
- package/skills/agentbnb/install.sh +0 -0
- package/dist/conduct-KJUD2RTB.js +0 -22
- package/dist/websocket-client-5MH6QRJK.js +0 -7
|
@@ -12,13 +12,13 @@ import {
|
|
|
12
12
|
requestViaRelay,
|
|
13
13
|
resolvePendingRequest,
|
|
14
14
|
searchCards
|
|
15
|
-
} from "../../chunk-
|
|
15
|
+
} from "../../chunk-B2VJTKO5.js";
|
|
16
16
|
import {
|
|
17
17
|
executeCapabilityBatch,
|
|
18
18
|
executeCapabilityRequest,
|
|
19
19
|
releaseRequesterEscrow,
|
|
20
20
|
settleRequesterEscrow
|
|
21
|
-
} from "../../chunk-
|
|
21
|
+
} from "../../chunk-ALX4WS3A.js";
|
|
22
22
|
import {
|
|
23
23
|
bootstrapAgent,
|
|
24
24
|
generateKeyPair,
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
updateSkillAvailability,
|
|
52
52
|
updateSkillIdleRate,
|
|
53
53
|
verifyEscrowReceipt
|
|
54
|
-
} from "../../chunk-
|
|
54
|
+
} from "../../chunk-7EF3HYVZ.js";
|
|
55
55
|
import "../../chunk-NWIQJ2CL.js";
|
|
56
56
|
import {
|
|
57
57
|
getConfigDir,
|
|
@@ -60,16 +60,14 @@ import {
|
|
|
60
60
|
import {
|
|
61
61
|
AgentBnBError,
|
|
62
62
|
AnyCardSchema
|
|
63
|
-
} from "../../chunk-
|
|
63
|
+
} from "../../chunk-WVY2W7AA.js";
|
|
64
64
|
import {
|
|
65
65
|
RelayClient,
|
|
66
66
|
RelayMessageSchema
|
|
67
|
-
} from "../../chunk-
|
|
67
|
+
} from "../../chunk-3LWBH7P3.js";
|
|
68
68
|
|
|
69
69
|
// skills/agentbnb/bootstrap.ts
|
|
70
70
|
import { join as join5 } from "path";
|
|
71
|
-
import { dirname as dirname4, basename } from "path";
|
|
72
|
-
import { existsSync as existsSync6 } from "fs";
|
|
73
71
|
import { homedir as homedir2 } from "os";
|
|
74
72
|
import { spawnSync } from "child_process";
|
|
75
73
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
@@ -918,7 +916,8 @@ var OpenClawBridge = class {
|
|
|
918
916
|
};
|
|
919
917
|
|
|
920
918
|
// src/skills/command-executor.ts
|
|
921
|
-
import {
|
|
919
|
+
import { spawn } from "child_process";
|
|
920
|
+
var KILL_GRACE_MS = 5e3;
|
|
922
921
|
function shellEscape2(value) {
|
|
923
922
|
return "'" + value.replace(/'/g, "'\\''") + "'";
|
|
924
923
|
}
|
|
@@ -944,29 +943,89 @@ function safeInterpolateCommand2(template, context) {
|
|
|
944
943
|
return shellEscape2(String(current));
|
|
945
944
|
});
|
|
946
945
|
}
|
|
947
|
-
function
|
|
946
|
+
function spawnWithKill(command, options, registry) {
|
|
948
947
|
return new Promise((resolve2, reject) => {
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
948
|
+
const child = spawn("/bin/sh", ["-c", `${command} < /dev/null`], {
|
|
949
|
+
cwd: options.cwd,
|
|
950
|
+
env: options.env,
|
|
951
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
952
|
+
detached: true
|
|
953
|
+
});
|
|
954
|
+
registry.add(child);
|
|
955
|
+
let stdout = "";
|
|
956
|
+
let stderr = "";
|
|
957
|
+
let timedOut = false;
|
|
958
|
+
let killed = false;
|
|
959
|
+
let killTimer;
|
|
960
|
+
child.stdout.on("data", (chunk) => {
|
|
961
|
+
if (stdout.length < options.maxBuffer) {
|
|
962
|
+
stdout += chunk.toString();
|
|
963
|
+
}
|
|
964
|
+
});
|
|
965
|
+
child.stderr.on("data", (chunk) => {
|
|
966
|
+
if (stderr.length < options.maxBuffer) {
|
|
967
|
+
stderr += chunk.toString();
|
|
968
|
+
}
|
|
969
|
+
});
|
|
970
|
+
const killGroup = (signal) => {
|
|
971
|
+
try {
|
|
972
|
+
process.kill(-child.pid, signal);
|
|
973
|
+
} catch {
|
|
974
|
+
try {
|
|
975
|
+
child.kill(signal);
|
|
976
|
+
} catch {
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
};
|
|
980
|
+
const timeoutId = setTimeout(() => {
|
|
981
|
+
timedOut = true;
|
|
982
|
+
killGroup("SIGTERM");
|
|
983
|
+
killTimer = setTimeout(() => {
|
|
984
|
+
if (!killed) {
|
|
985
|
+
killGroup("SIGKILL");
|
|
986
|
+
}
|
|
987
|
+
}, KILL_GRACE_MS);
|
|
988
|
+
killTimer.unref();
|
|
989
|
+
}, options.timeout);
|
|
990
|
+
child.on("close", (_code, _signal) => {
|
|
991
|
+
killed = true;
|
|
992
|
+
clearTimeout(timeoutId);
|
|
993
|
+
if (killTimer) clearTimeout(killTimer);
|
|
994
|
+
registry.delete(child);
|
|
995
|
+
if (timedOut) {
|
|
996
|
+
const err = new Error(`Command timed out after ${options.timeout}ms`);
|
|
997
|
+
err.code = "ETIMEDOUT";
|
|
998
|
+
reject(err);
|
|
999
|
+
} else if (_code !== 0) {
|
|
1000
|
+
const err = new Error(stderr.trim() || `Process exited with code ${_code}`);
|
|
1001
|
+
Object.assign(err, { stderr: stderr.trim() });
|
|
1002
|
+
reject(err);
|
|
955
1003
|
} else {
|
|
956
|
-
resolve2({ stdout
|
|
1004
|
+
resolve2({ stdout, stderr });
|
|
957
1005
|
}
|
|
958
1006
|
});
|
|
1007
|
+
child.on("error", (err) => {
|
|
1008
|
+
killed = true;
|
|
1009
|
+
clearTimeout(timeoutId);
|
|
1010
|
+
registry.delete(child);
|
|
1011
|
+
reject(err);
|
|
1012
|
+
});
|
|
959
1013
|
});
|
|
960
1014
|
}
|
|
961
1015
|
var CommandExecutor = class {
|
|
1016
|
+
/** Active child processes — killed on shutdown(). */
|
|
1017
|
+
activeProcesses = /* @__PURE__ */ new Set();
|
|
1018
|
+
/** In-flight execution count per skill ID for concurrency limiting. */
|
|
1019
|
+
inflight = /* @__PURE__ */ new Map();
|
|
962
1020
|
/**
|
|
963
1021
|
* Execute a command skill with the provided parameters.
|
|
964
1022
|
*
|
|
965
1023
|
* Steps:
|
|
966
|
-
* 1.
|
|
967
|
-
* 2.
|
|
968
|
-
* 3.
|
|
969
|
-
* 4.
|
|
1024
|
+
* 1. Concurrency check: reject if at capacity.max_concurrent limit.
|
|
1025
|
+
* 2. Security check: base command must be in `allowed_commands` if set.
|
|
1026
|
+
* 3. Interpolate `config.command` using `{ params }` context.
|
|
1027
|
+
* 4. Run via spawn with SIGTERM→SIGKILL timeout handling.
|
|
1028
|
+
* 5. Parse stdout based on `output_type`: text | json | file.
|
|
970
1029
|
*
|
|
971
1030
|
* @param config - Validated CommandSkillConfig.
|
|
972
1031
|
* @param params - Input parameters passed by the caller.
|
|
@@ -974,6 +1033,16 @@ var CommandExecutor = class {
|
|
|
974
1033
|
*/
|
|
975
1034
|
async execute(config, params) {
|
|
976
1035
|
const cmdConfig = config;
|
|
1036
|
+
const maxConcurrent = cmdConfig.capacity?.max_concurrent;
|
|
1037
|
+
if (maxConcurrent !== void 0) {
|
|
1038
|
+
const current = this.inflight.get(cmdConfig.id) ?? 0;
|
|
1039
|
+
if (current >= maxConcurrent) {
|
|
1040
|
+
return {
|
|
1041
|
+
success: false,
|
|
1042
|
+
error: `Skill "${cmdConfig.id}" at max concurrency (${maxConcurrent}). Try again later.`
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
977
1046
|
const baseCommand = cmdConfig.command.trim().split(/\s+/)[0] ?? "";
|
|
978
1047
|
if (cmdConfig.allowed_commands && cmdConfig.allowed_commands.length > 0) {
|
|
979
1048
|
const effectiveAllowed = cmdConfig.claude_code ? [.../* @__PURE__ */ new Set([...cmdConfig.allowed_commands, "claude"])] : cmdConfig.allowed_commands;
|
|
@@ -1004,31 +1073,33 @@ var CommandExecutor = class {
|
|
|
1004
1073
|
}
|
|
1005
1074
|
const timeout = cmdConfig.timeout_ms ?? 3e4;
|
|
1006
1075
|
const cwd = cmdConfig.working_dir ?? process.cwd();
|
|
1007
|
-
let stdout;
|
|
1008
1076
|
const env = { ...process.env };
|
|
1009
1077
|
delete env["CLAUDECODE"];
|
|
1078
|
+
this.inflight.set(cmdConfig.id, (this.inflight.get(cmdConfig.id) ?? 0) + 1);
|
|
1079
|
+
let stdout;
|
|
1010
1080
|
try {
|
|
1011
|
-
const result = await
|
|
1081
|
+
const result = await spawnWithKill(interpolatedCommand, {
|
|
1012
1082
|
timeout,
|
|
1013
1083
|
cwd,
|
|
1014
1084
|
env,
|
|
1015
1085
|
maxBuffer: 10 * 1024 * 1024
|
|
1016
1086
|
// 10 MB
|
|
1017
|
-
});
|
|
1087
|
+
}, this.activeProcesses);
|
|
1018
1088
|
stdout = result.stdout;
|
|
1019
1089
|
} catch (err) {
|
|
1090
|
+
this.decrementInflight(cmdConfig.id);
|
|
1020
1091
|
if (err instanceof Error) {
|
|
1021
|
-
const
|
|
1022
|
-
|
|
1023
|
-
if (message.includes("timed out") || message.includes("ETIMEDOUT") || err.code === "ETIMEDOUT") {
|
|
1092
|
+
const code = err.code;
|
|
1093
|
+
if (code === "ETIMEDOUT" || err.message.includes("timed out")) {
|
|
1024
1094
|
return {
|
|
1025
1095
|
success: false,
|
|
1026
1096
|
error: `Command timed out after ${timeout}ms`
|
|
1027
1097
|
};
|
|
1028
1098
|
}
|
|
1099
|
+
const stderrContent = err.stderr ?? "";
|
|
1029
1100
|
return {
|
|
1030
1101
|
success: false,
|
|
1031
|
-
error: stderrContent.trim() || message
|
|
1102
|
+
error: stderrContent.trim() || err.message
|
|
1032
1103
|
};
|
|
1033
1104
|
}
|
|
1034
1105
|
return {
|
|
@@ -1036,6 +1107,7 @@ var CommandExecutor = class {
|
|
|
1036
1107
|
error: String(err)
|
|
1037
1108
|
};
|
|
1038
1109
|
}
|
|
1110
|
+
this.decrementInflight(cmdConfig.id);
|
|
1039
1111
|
const rawOutput = stdout.trim();
|
|
1040
1112
|
switch (cmdConfig.output_type) {
|
|
1041
1113
|
case "text":
|
|
@@ -1060,6 +1132,48 @@ var CommandExecutor = class {
|
|
|
1060
1132
|
};
|
|
1061
1133
|
}
|
|
1062
1134
|
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Kill all active child processes. Called during service shutdown
|
|
1137
|
+
* to prevent zombie processes.
|
|
1138
|
+
*/
|
|
1139
|
+
shutdown() {
|
|
1140
|
+
for (const child of this.activeProcesses) {
|
|
1141
|
+
try {
|
|
1142
|
+
process.kill(-child.pid, "SIGTERM");
|
|
1143
|
+
} catch {
|
|
1144
|
+
try {
|
|
1145
|
+
child.kill("SIGTERM");
|
|
1146
|
+
} catch {
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
const pid = child.pid;
|
|
1150
|
+
const timer = setTimeout(() => {
|
|
1151
|
+
try {
|
|
1152
|
+
process.kill(-pid, "SIGKILL");
|
|
1153
|
+
} catch {
|
|
1154
|
+
}
|
|
1155
|
+
}, KILL_GRACE_MS);
|
|
1156
|
+
timer.unref();
|
|
1157
|
+
}
|
|
1158
|
+
this.activeProcesses.clear();
|
|
1159
|
+
}
|
|
1160
|
+
/** Returns the number of currently active child processes. */
|
|
1161
|
+
get activeCount() {
|
|
1162
|
+
return this.activeProcesses.size;
|
|
1163
|
+
}
|
|
1164
|
+
/** Returns the in-flight count for a specific skill ID. */
|
|
1165
|
+
getInflight(skillId) {
|
|
1166
|
+
return this.inflight.get(skillId) ?? 0;
|
|
1167
|
+
}
|
|
1168
|
+
/** Decrement the inflight counter for a skill ID. */
|
|
1169
|
+
decrementInflight(skillId) {
|
|
1170
|
+
const current = this.inflight.get(skillId) ?? 0;
|
|
1171
|
+
if (current <= 1) {
|
|
1172
|
+
this.inflight.delete(skillId);
|
|
1173
|
+
} else {
|
|
1174
|
+
this.inflight.set(skillId, current - 1);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1063
1177
|
};
|
|
1064
1178
|
|
|
1065
1179
|
// src/runtime/agent-runtime.ts
|
|
@@ -1077,6 +1191,8 @@ var AgentRuntime = class {
|
|
|
1077
1191
|
* Undefined if no skills.yaml was provided or the file does not exist.
|
|
1078
1192
|
*/
|
|
1079
1193
|
skillExecutor;
|
|
1194
|
+
/** Reference to CommandExecutor for shutdown cleanup of child processes. */
|
|
1195
|
+
commandExecutor;
|
|
1080
1196
|
draining = false;
|
|
1081
1197
|
orphanedEscrowAgeMinutes;
|
|
1082
1198
|
skillsYamlPath;
|
|
@@ -1142,8 +1258,8 @@ var AgentRuntime = class {
|
|
|
1142
1258
|
}
|
|
1143
1259
|
const modes = /* @__PURE__ */ new Map();
|
|
1144
1260
|
if (this.conductorEnabled) {
|
|
1145
|
-
const { ConductorMode } = await import("../../conductor-mode-
|
|
1146
|
-
const { registerConductorCard, CONDUCTOR_OWNER } = await import("../../card-
|
|
1261
|
+
const { ConductorMode } = await import("../../conductor-mode-2GSLHVN6.js");
|
|
1262
|
+
const { registerConductorCard, CONDUCTOR_OWNER } = await import("../../card-EX2EYGCZ.js");
|
|
1147
1263
|
const { loadPeers } = await import("../../peers-CJ7T4RJO.js");
|
|
1148
1264
|
registerConductorCard(this.registryDb);
|
|
1149
1265
|
const resolveAgentUrl = (owner) => {
|
|
@@ -1190,10 +1306,11 @@ var AgentRuntime = class {
|
|
|
1190
1306
|
const executor = createSkillExecutor(configs, modes);
|
|
1191
1307
|
if (hasSkillsYaml) {
|
|
1192
1308
|
const pipelineExecutor = new PipelineExecutor(executor);
|
|
1309
|
+
this.commandExecutor = new CommandExecutor();
|
|
1193
1310
|
modes.set("api", new ApiExecutor());
|
|
1194
1311
|
modes.set("pipeline", pipelineExecutor);
|
|
1195
1312
|
modes.set("openclaw", new OpenClawBridge());
|
|
1196
|
-
modes.set("command",
|
|
1313
|
+
modes.set("command", this.commandExecutor);
|
|
1197
1314
|
}
|
|
1198
1315
|
this.skillExecutor = executor;
|
|
1199
1316
|
}
|
|
@@ -1226,6 +1343,9 @@ var AgentRuntime = class {
|
|
|
1226
1343
|
return;
|
|
1227
1344
|
}
|
|
1228
1345
|
this.draining = true;
|
|
1346
|
+
if (this.commandExecutor) {
|
|
1347
|
+
this.commandExecutor.shutdown();
|
|
1348
|
+
}
|
|
1229
1349
|
for (const job of this.jobs) {
|
|
1230
1350
|
job.stop();
|
|
1231
1351
|
}
|
|
@@ -2003,6 +2123,78 @@ function releaseForRelay(creditDb, escrowId) {
|
|
|
2003
2123
|
releaseEscrow(creditDb, escrowId);
|
|
2004
2124
|
}
|
|
2005
2125
|
|
|
2126
|
+
// src/relay/relay-escrow.ts
|
|
2127
|
+
function verifyRelaySignature(data, signature, publicKeyHex) {
|
|
2128
|
+
try {
|
|
2129
|
+
const publicKeyBuf = Buffer.from(publicKeyHex, "hex");
|
|
2130
|
+
return verifyEscrowReceipt(data, signature, publicKeyBuf);
|
|
2131
|
+
} catch {
|
|
2132
|
+
return false;
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
function processEscrowHold(creditDb, consumerAgentId, providerAgentId, skillId, amount, requestId, signature, publicKeyHex) {
|
|
2136
|
+
if (signature && publicKeyHex) {
|
|
2137
|
+
const signData = {
|
|
2138
|
+
consumer_agent_id: consumerAgentId,
|
|
2139
|
+
provider_agent_id: providerAgentId,
|
|
2140
|
+
skill_id: skillId,
|
|
2141
|
+
amount,
|
|
2142
|
+
request_id: requestId
|
|
2143
|
+
};
|
|
2144
|
+
if (!verifyRelaySignature(signData, signature, publicKeyHex)) {
|
|
2145
|
+
throw new Error("Invalid consumer signature on escrow hold");
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
const escrowId = holdEscrow(creditDb, consumerAgentId, amount, `${providerAgentId}:${skillId}`);
|
|
2149
|
+
const remaining = getBalance(creditDb, consumerAgentId);
|
|
2150
|
+
return {
|
|
2151
|
+
escrow_id: escrowId,
|
|
2152
|
+
hold_amount: amount,
|
|
2153
|
+
consumer_remaining: remaining
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
function processEscrowSettle(creditDb, escrowId, success, providerAgentId, signature, publicKeyHex, consumerAgentId) {
|
|
2157
|
+
if (signature && publicKeyHex && consumerAgentId) {
|
|
2158
|
+
const signData = {
|
|
2159
|
+
escrow_id: escrowId,
|
|
2160
|
+
success,
|
|
2161
|
+
consumer_agent_id: consumerAgentId
|
|
2162
|
+
};
|
|
2163
|
+
if (!verifyRelaySignature(signData, signature, publicKeyHex)) {
|
|
2164
|
+
throw new Error("Invalid consumer signature on escrow settle");
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
const escrowRow = creditDb.prepare("SELECT amount, owner FROM credit_escrow WHERE id = ? AND status = ?").get(escrowId, "held");
|
|
2168
|
+
if (!escrowRow) {
|
|
2169
|
+
throw new Error(`Escrow not found or already settled: ${escrowId}`);
|
|
2170
|
+
}
|
|
2171
|
+
const NETWORK_FEE_RATE = 0.05;
|
|
2172
|
+
if (success) {
|
|
2173
|
+
settleEscrow(creditDb, escrowId, providerAgentId);
|
|
2174
|
+
const networkFee = Math.floor(escrowRow.amount * NETWORK_FEE_RATE);
|
|
2175
|
+
const providerAmount = escrowRow.amount - networkFee;
|
|
2176
|
+
return {
|
|
2177
|
+
escrow_id: escrowId,
|
|
2178
|
+
provider_earned: providerAmount,
|
|
2179
|
+
network_fee: networkFee,
|
|
2180
|
+
consumer_remaining: getBalance(creditDb, escrowRow.owner),
|
|
2181
|
+
provider_balance: getBalance(creditDb, providerAgentId)
|
|
2182
|
+
};
|
|
2183
|
+
} else {
|
|
2184
|
+
releaseEscrow(creditDb, escrowId);
|
|
2185
|
+
return {
|
|
2186
|
+
escrow_id: escrowId,
|
|
2187
|
+
provider_earned: 0,
|
|
2188
|
+
network_fee: 0,
|
|
2189
|
+
consumer_remaining: getBalance(creditDb, escrowRow.owner),
|
|
2190
|
+
provider_balance: getBalance(creditDb, providerAgentId)
|
|
2191
|
+
};
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
function settleWithNetworkFee(creditDb, escrowId, providerOwner) {
|
|
2195
|
+
return processEscrowSettle(creditDb, escrowId, true, providerOwner);
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2006
2198
|
// src/hub-agent/relay-bridge.ts
|
|
2007
2199
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
2008
2200
|
|
|
@@ -2392,10 +2584,17 @@ var RATE_LIMIT_WINDOW_MS = 6e4;
|
|
|
2392
2584
|
var RELAY_TIMEOUT_MS = 3e5;
|
|
2393
2585
|
function registerWebSocketRelay(server, db, creditDb) {
|
|
2394
2586
|
const connections = /* @__PURE__ */ new Map();
|
|
2587
|
+
const agentIdToOwner = /* @__PURE__ */ new Map();
|
|
2395
2588
|
const pendingRequests = /* @__PURE__ */ new Map();
|
|
2396
2589
|
const rateLimits = /* @__PURE__ */ new Map();
|
|
2397
2590
|
const agentCapacities = /* @__PURE__ */ new Map();
|
|
2398
2591
|
let onAgentOnlineCallback;
|
|
2592
|
+
function resolveConnectionKey(target) {
|
|
2593
|
+
const ownerFromAgentId = agentIdToOwner.get(target);
|
|
2594
|
+
if (ownerFromAgentId && connections.has(ownerFromAgentId)) return ownerFromAgentId;
|
|
2595
|
+
if (connections.has(target)) return target;
|
|
2596
|
+
return void 0;
|
|
2597
|
+
}
|
|
2399
2598
|
function checkRateLimit(owner) {
|
|
2400
2599
|
const now = Date.now();
|
|
2401
2600
|
const entry = rateLimits.get(owner);
|
|
@@ -2499,6 +2698,20 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
2499
2698
|
}
|
|
2500
2699
|
}
|
|
2501
2700
|
connections.set(owner, ws);
|
|
2701
|
+
if (msg.agent_id) {
|
|
2702
|
+
agentIdToOwner.set(msg.agent_id, owner);
|
|
2703
|
+
}
|
|
2704
|
+
if (msg.agents && msg.agents.length > 0) {
|
|
2705
|
+
for (const agentEntry of msg.agents) {
|
|
2706
|
+
agentIdToOwner.set(agentEntry.agent_id, owner);
|
|
2707
|
+
for (const agentCard of agentEntry.cards) {
|
|
2708
|
+
try {
|
|
2709
|
+
upsertCard(agentCard, owner);
|
|
2710
|
+
} catch {
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2502
2715
|
const isEphemeral = owner.includes(":req:");
|
|
2503
2716
|
if (isEphemeral) {
|
|
2504
2717
|
const cardId2 = card.id ?? owner;
|
|
@@ -2542,12 +2755,13 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
2542
2755
|
});
|
|
2543
2756
|
return;
|
|
2544
2757
|
}
|
|
2545
|
-
const
|
|
2758
|
+
const targetKey = resolveConnectionKey(msg.target_agent_id ?? msg.target_owner);
|
|
2759
|
+
const targetWs = targetKey ? connections.get(targetKey) : void 0;
|
|
2546
2760
|
if (!targetWs || targetWs.readyState !== 1) {
|
|
2547
2761
|
sendMessage(ws, {
|
|
2548
2762
|
type: "response",
|
|
2549
2763
|
id: msg.id,
|
|
2550
|
-
error: { code: -32603, message: `Agent offline: ${msg.target_owner}` }
|
|
2764
|
+
error: { code: -32603, message: `Agent offline: ${msg.target_agent_id ?? msg.target_owner}` }
|
|
2551
2765
|
});
|
|
2552
2766
|
return;
|
|
2553
2767
|
}
|
|
@@ -2661,7 +2875,7 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
2661
2875
|
if (pending.escrowId && creditDb) {
|
|
2662
2876
|
try {
|
|
2663
2877
|
if (msg.error === void 0) {
|
|
2664
|
-
|
|
2878
|
+
settleWithNetworkFee(creditDb, pending.escrowId, pending.targetOwner);
|
|
2665
2879
|
} else {
|
|
2666
2880
|
releaseForRelay(creditDb, pending.escrowId);
|
|
2667
2881
|
}
|
|
@@ -2699,6 +2913,12 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
2699
2913
|
connections.delete(owner);
|
|
2700
2914
|
rateLimits.delete(owner);
|
|
2701
2915
|
agentCapacities.delete(owner);
|
|
2916
|
+
for (const [agentId, o] of agentIdToOwner) {
|
|
2917
|
+
if (o === owner) {
|
|
2918
|
+
agentIdToOwner.delete(agentId);
|
|
2919
|
+
break;
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2702
2922
|
markOwnerOffline(owner);
|
|
2703
2923
|
for (const [reqId, pending] of pendingRequests) {
|
|
2704
2924
|
if (pending.targetOwner === owner) {
|
|
@@ -2735,6 +2955,96 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
2735
2955
|
function handleHeartbeat(msg) {
|
|
2736
2956
|
agentCapacities.set(msg.owner, msg.capacity);
|
|
2737
2957
|
}
|
|
2958
|
+
function handleEscrowHold(ws, msg) {
|
|
2959
|
+
if (!creditDb) {
|
|
2960
|
+
sendMessage(ws, { type: "error", code: "no_credit_db", message: "Credit system not available" });
|
|
2961
|
+
return;
|
|
2962
|
+
}
|
|
2963
|
+
try {
|
|
2964
|
+
const result = processEscrowHold(
|
|
2965
|
+
creditDb,
|
|
2966
|
+
msg.consumer_agent_id,
|
|
2967
|
+
msg.provider_agent_id,
|
|
2968
|
+
msg.skill_id,
|
|
2969
|
+
msg.amount,
|
|
2970
|
+
msg.request_id,
|
|
2971
|
+
msg.signature,
|
|
2972
|
+
msg.public_key
|
|
2973
|
+
);
|
|
2974
|
+
sendMessage(ws, {
|
|
2975
|
+
type: "escrow_hold_confirmed",
|
|
2976
|
+
request_id: msg.request_id,
|
|
2977
|
+
escrow_id: result.escrow_id,
|
|
2978
|
+
hold_amount: result.hold_amount,
|
|
2979
|
+
consumer_remaining: result.consumer_remaining
|
|
2980
|
+
});
|
|
2981
|
+
} catch (err) {
|
|
2982
|
+
const errMsg = err instanceof Error ? err.message : "Escrow hold failed";
|
|
2983
|
+
const code = errMsg.includes("INSUFFICIENT_CREDITS") ? "insufficient_credits" : "escrow_hold_failed";
|
|
2984
|
+
sendMessage(ws, { type: "error", code, message: errMsg, request_id: msg.request_id });
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
function handleEscrowSettle(ws, msg) {
|
|
2988
|
+
if (!creditDb) {
|
|
2989
|
+
sendMessage(ws, { type: "error", code: "no_credit_db", message: "Credit system not available" });
|
|
2990
|
+
return;
|
|
2991
|
+
}
|
|
2992
|
+
try {
|
|
2993
|
+
const escrow = creditDb.prepare("SELECT card_id FROM credit_escrow WHERE id = ? AND status = ?").get(msg.escrow_id, "held");
|
|
2994
|
+
if (!escrow) {
|
|
2995
|
+
sendMessage(ws, { type: "error", code: "escrow_not_found", message: `Escrow not found: ${msg.escrow_id}`, request_id: msg.request_id });
|
|
2996
|
+
return;
|
|
2997
|
+
}
|
|
2998
|
+
const providerAgentId = escrow.card_id.split(":")[0];
|
|
2999
|
+
const result = processEscrowSettle(
|
|
3000
|
+
creditDb,
|
|
3001
|
+
msg.escrow_id,
|
|
3002
|
+
msg.success,
|
|
3003
|
+
providerAgentId,
|
|
3004
|
+
msg.signature,
|
|
3005
|
+
msg.public_key,
|
|
3006
|
+
msg.consumer_agent_id
|
|
3007
|
+
);
|
|
3008
|
+
sendMessage(ws, {
|
|
3009
|
+
type: "escrow_settled",
|
|
3010
|
+
escrow_id: result.escrow_id,
|
|
3011
|
+
request_id: msg.request_id,
|
|
3012
|
+
provider_earned: result.provider_earned,
|
|
3013
|
+
network_fee: result.network_fee,
|
|
3014
|
+
consumer_remaining: result.consumer_remaining,
|
|
3015
|
+
provider_balance: result.provider_balance
|
|
3016
|
+
});
|
|
3017
|
+
const providerKey = resolveConnectionKey(providerAgentId);
|
|
3018
|
+
if (providerKey) {
|
|
3019
|
+
const providerWs = connections.get(providerKey);
|
|
3020
|
+
if (providerWs && providerWs.readyState === 1) {
|
|
3021
|
+
sendMessage(providerWs, {
|
|
3022
|
+
type: "escrow_settled",
|
|
3023
|
+
escrow_id: result.escrow_id,
|
|
3024
|
+
request_id: msg.request_id,
|
|
3025
|
+
provider_earned: result.provider_earned,
|
|
3026
|
+
network_fee: result.network_fee,
|
|
3027
|
+
consumer_remaining: result.consumer_remaining,
|
|
3028
|
+
provider_balance: result.provider_balance
|
|
3029
|
+
});
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
} catch (err) {
|
|
3033
|
+
sendMessage(ws, { type: "error", code: "escrow_settle_failed", message: err instanceof Error ? err.message : "Settlement failed", request_id: msg.request_id });
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
function handleBalanceSync(ws, msg) {
|
|
3037
|
+
if (!creditDb) {
|
|
3038
|
+
sendMessage(ws, { type: "error", code: "no_credit_db", message: "Credit system not available" });
|
|
3039
|
+
return;
|
|
3040
|
+
}
|
|
3041
|
+
const balance = getBalance(creditDb, msg.agent_id);
|
|
3042
|
+
sendMessage(ws, {
|
|
3043
|
+
type: "balance_sync_response",
|
|
3044
|
+
agent_id: msg.agent_id,
|
|
3045
|
+
balance
|
|
3046
|
+
});
|
|
3047
|
+
}
|
|
2738
3048
|
void server.register(async (app) => {
|
|
2739
3049
|
app.get("/ws", { websocket: true }, (rawSocket, _request) => {
|
|
2740
3050
|
const socket = rawSocket;
|
|
@@ -2783,6 +3093,16 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
2783
3093
|
case "heartbeat":
|
|
2784
3094
|
handleHeartbeat(msg);
|
|
2785
3095
|
break;
|
|
3096
|
+
// V8 Phase 2: Explicit escrow messages
|
|
3097
|
+
case "escrow_hold":
|
|
3098
|
+
handleEscrowHold(socket, msg);
|
|
3099
|
+
break;
|
|
3100
|
+
case "escrow_settle":
|
|
3101
|
+
handleEscrowSettle(socket, msg);
|
|
3102
|
+
break;
|
|
3103
|
+
case "balance_sync":
|
|
3104
|
+
handleBalanceSync(socket, msg);
|
|
3105
|
+
break;
|
|
2786
3106
|
default:
|
|
2787
3107
|
break;
|
|
2788
3108
|
}
|
|
@@ -5494,7 +5814,7 @@ async function stopAnnouncement() {
|
|
|
5494
5814
|
}
|
|
5495
5815
|
|
|
5496
5816
|
// src/runtime/service-coordinator.ts
|
|
5497
|
-
import { spawn } from "child_process";
|
|
5817
|
+
import { spawn as spawn2 } from "child_process";
|
|
5498
5818
|
import { createRequire } from "module";
|
|
5499
5819
|
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
5500
5820
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -5522,10 +5842,14 @@ var ServiceCoordinator = class {
|
|
|
5522
5842
|
if (health.ok && health.agentbnb) {
|
|
5523
5843
|
return "already_running";
|
|
5524
5844
|
}
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5845
|
+
if (opts?.foreground) {
|
|
5846
|
+
this.guard.release();
|
|
5847
|
+
} else {
|
|
5848
|
+
throw new AgentBnBError(
|
|
5849
|
+
`AgentBnB lock exists but health check failed (pid=${running.pid}, port=${running.port})`,
|
|
5850
|
+
"SERVICE_UNHEALTHY"
|
|
5851
|
+
);
|
|
5852
|
+
}
|
|
5529
5853
|
}
|
|
5530
5854
|
if (opts?.foreground) {
|
|
5531
5855
|
return this.startInProcess(opts);
|
|
@@ -5640,7 +5964,7 @@ var ServiceCoordinator = class {
|
|
|
5640
5964
|
console.log("Conductor mode enabled \u2014 orchestrate/plan skills available via gateway");
|
|
5641
5965
|
}
|
|
5642
5966
|
if (opts.conductorEnabled && this.config.conductor?.public) {
|
|
5643
|
-
const { buildConductorCard } = await import("../../card-
|
|
5967
|
+
const { buildConductorCard } = await import("../../card-EX2EYGCZ.js");
|
|
5644
5968
|
const conductorCard = buildConductorCard(this.config.owner);
|
|
5645
5969
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5646
5970
|
const existing = this.runtime.registryDb.prepare("SELECT id FROM capability_cards WHERE id = ?").get(conductorCard.id);
|
|
@@ -5697,8 +6021,8 @@ var ServiceCoordinator = class {
|
|
|
5697
6021
|
}
|
|
5698
6022
|
}
|
|
5699
6023
|
if (opts.registryUrl && opts.relay) {
|
|
5700
|
-
const { RelayClient: RelayClient2 } = await import("../../websocket-client-
|
|
5701
|
-
const { executeCapabilityRequest: executeCapabilityRequest2 } = await import("../../execute-
|
|
6024
|
+
const { RelayClient: RelayClient2 } = await import("../../websocket-client-4Z5P54RU.js");
|
|
6025
|
+
const { executeCapabilityRequest: executeCapabilityRequest2 } = await import("../../execute-EPE6MZLT.js");
|
|
5702
6026
|
const cards = listCards(this.runtime.registryDb, this.config.owner);
|
|
5703
6027
|
const card = cards[0] ?? {
|
|
5704
6028
|
id: randomUUID7(),
|
|
@@ -5714,7 +6038,7 @@ var ServiceCoordinator = class {
|
|
|
5714
6038
|
};
|
|
5715
6039
|
const additionalCards = [];
|
|
5716
6040
|
if (this.config.conductor?.public) {
|
|
5717
|
-
const { buildConductorCard } = await import("../../card-
|
|
6041
|
+
const { buildConductorCard } = await import("../../card-EX2EYGCZ.js");
|
|
5718
6042
|
additionalCards.push(
|
|
5719
6043
|
buildConductorCard(this.config.owner)
|
|
5720
6044
|
);
|
|
@@ -5723,6 +6047,7 @@ var ServiceCoordinator = class {
|
|
|
5723
6047
|
this.relayClient = new RelayClient2({
|
|
5724
6048
|
registryUrl: opts.registryUrl,
|
|
5725
6049
|
owner: this.config.owner,
|
|
6050
|
+
agent_id: this.config.agent_id,
|
|
5726
6051
|
token: this.config.token,
|
|
5727
6052
|
card,
|
|
5728
6053
|
cards: additionalCards.length > 0 ? additionalCards : void 0,
|
|
@@ -5805,7 +6130,7 @@ var ServiceCoordinator = class {
|
|
|
5805
6130
|
const runtime = loadPersistedRuntime(getConfigDir());
|
|
5806
6131
|
const nodeExec = resolveNodeExecutable(runtime);
|
|
5807
6132
|
const cliArgs = resolveCliLaunchArgs(this.buildServeArgs(opts));
|
|
5808
|
-
const child =
|
|
6133
|
+
const child = spawn2(nodeExec, cliArgs, {
|
|
5809
6134
|
detached: true,
|
|
5810
6135
|
stdio: "ignore",
|
|
5811
6136
|
env: { ...process.env }
|
|
@@ -6034,6 +6359,7 @@ import { z as z8 } from "zod";
|
|
|
6034
6359
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
6035
6360
|
var EscrowReceiptSchema = z8.object({
|
|
6036
6361
|
requester_owner: z8.string().min(1),
|
|
6362
|
+
requester_agent_id: z8.string().optional(),
|
|
6037
6363
|
requester_public_key: z8.string().min(1),
|
|
6038
6364
|
amount: z8.number().positive(),
|
|
6039
6365
|
card_id: z8.string().min(1),
|
|
@@ -6046,6 +6372,7 @@ function createSignedEscrowReceipt(db, privateKey, publicKey, opts) {
|
|
|
6046
6372
|
const escrowId = holdEscrow(db, opts.owner, opts.amount, opts.cardId);
|
|
6047
6373
|
const receiptData = {
|
|
6048
6374
|
requester_owner: opts.owner,
|
|
6375
|
+
...opts.agent_id ? { requester_agent_id: opts.agent_id } : {},
|
|
6049
6376
|
requester_public_key: publicKey.toString("hex"),
|
|
6050
6377
|
amount: opts.amount,
|
|
6051
6378
|
card_id: opts.cardId,
|
|
@@ -6408,21 +6735,16 @@ function isNetworkError(err) {
|
|
|
6408
6735
|
}
|
|
6409
6736
|
|
|
6410
6737
|
// skills/agentbnb/bootstrap.ts
|
|
6411
|
-
function findSoulMd(startDir) {
|
|
6412
|
-
let dir = startDir;
|
|
6413
|
-
while (true) {
|
|
6414
|
-
const candidate = join5(dir, "SOUL.md");
|
|
6415
|
-
if (existsSync6(candidate)) return candidate;
|
|
6416
|
-
const parent = dirname4(dir);
|
|
6417
|
-
if (parent === dir) return null;
|
|
6418
|
-
dir = parent;
|
|
6419
|
-
}
|
|
6420
|
-
}
|
|
6421
6738
|
function resolveWorkspaceDir() {
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6739
|
+
if (process.env["AGENTBNB_DIR"]) {
|
|
6740
|
+
return process.env["AGENTBNB_DIR"];
|
|
6741
|
+
}
|
|
6742
|
+
const openclawAgentsDir = join5(homedir2(), ".openclaw", "agents");
|
|
6743
|
+
const cwd = process.cwd();
|
|
6744
|
+
if (cwd.startsWith(openclawAgentsDir + "/")) {
|
|
6745
|
+
const relative = cwd.slice(openclawAgentsDir.length + 1);
|
|
6746
|
+
const agentName = relative.split("/")[0];
|
|
6747
|
+
return join5(openclawAgentsDir, agentName, ".agentbnb");
|
|
6426
6748
|
}
|
|
6427
6749
|
return join5(homedir2(), ".agentbnb");
|
|
6428
6750
|
}
|
|
@@ -6486,7 +6808,20 @@ function registerDecomposerCard(configDir, owner) {
|
|
|
6486
6808
|
}
|
|
6487
6809
|
}
|
|
6488
6810
|
async function activate(config = {}) {
|
|
6489
|
-
if (
|
|
6811
|
+
if (config.agentDir) {
|
|
6812
|
+
process.env["AGENTBNB_DIR"] = config.agentDir;
|
|
6813
|
+
process.stderr.write(
|
|
6814
|
+
`[agentbnb] AGENTBNB_DIR set from config.agentDir: ${config.agentDir}
|
|
6815
|
+
`
|
|
6816
|
+
);
|
|
6817
|
+
} else if (config.workspaceDir) {
|
|
6818
|
+
const derived = join5(config.workspaceDir, ".agentbnb");
|
|
6819
|
+
process.env["AGENTBNB_DIR"] = derived;
|
|
6820
|
+
process.stderr.write(
|
|
6821
|
+
`[agentbnb] AGENTBNB_DIR derived from config.workspaceDir: ${derived}
|
|
6822
|
+
`
|
|
6823
|
+
);
|
|
6824
|
+
} else if (!process.env["AGENTBNB_DIR"]) {
|
|
6490
6825
|
const workspaceDir = resolveWorkspaceDir();
|
|
6491
6826
|
process.env["AGENTBNB_DIR"] = workspaceDir;
|
|
6492
6827
|
process.stderr.write(
|