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
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
executeCapabilityBatch,
|
|
3
3
|
executeCapabilityRequest
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-C537SFHV.js";
|
|
5
5
|
import {
|
|
6
6
|
StructuredFeedbackSchema
|
|
7
7
|
} from "./chunk-AUBHR7HH.js";
|
|
8
8
|
import {
|
|
9
9
|
RelayMessageSchema
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-SSK653A6.js";
|
|
11
11
|
import {
|
|
12
12
|
KNOWN_API_KEYS,
|
|
13
13
|
announceGateway,
|
|
@@ -15,15 +15,15 @@ import {
|
|
|
15
15
|
detectApiKeys,
|
|
16
16
|
getPricingStats,
|
|
17
17
|
stopAnnouncement
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-FTZTEHYG.js";
|
|
19
19
|
import {
|
|
20
20
|
deriveAgentId
|
|
21
|
-
} from "./chunk-
|
|
22
|
-
import "./chunk-
|
|
21
|
+
} from "./chunk-OH7BP5NP.js";
|
|
22
|
+
import "./chunk-X32NE6V4.js";
|
|
23
23
|
import {
|
|
24
24
|
createLedger,
|
|
25
25
|
identityAuthPlugin
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-5AAFG2V2.js";
|
|
27
27
|
import {
|
|
28
28
|
ApiSkillConfigSchema,
|
|
29
29
|
parseSkillsFile
|
|
@@ -37,13 +37,13 @@ import {
|
|
|
37
37
|
insertAuditEvent,
|
|
38
38
|
listPendingRequests,
|
|
39
39
|
resolvePendingRequest
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-GKVTD4EZ.js";
|
|
41
41
|
import {
|
|
42
42
|
buildReputationMap,
|
|
43
43
|
computeReputation,
|
|
44
44
|
filterCards,
|
|
45
45
|
searchCards
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-LJM7FHPM.js";
|
|
47
47
|
import {
|
|
48
48
|
bootstrapAgent,
|
|
49
49
|
getBalance,
|
|
@@ -53,12 +53,12 @@ import {
|
|
|
53
53
|
openCreditDb,
|
|
54
54
|
releaseEscrow,
|
|
55
55
|
settleEscrow
|
|
56
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-D6RKW2XG.js";
|
|
57
57
|
import "./chunk-NWIQJ2CL.js";
|
|
58
58
|
import {
|
|
59
59
|
generateKeyPair,
|
|
60
60
|
verifyEscrowReceipt
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-CUONY5TO.js";
|
|
62
62
|
import {
|
|
63
63
|
getConfigDir
|
|
64
64
|
} from "./chunk-75OC6E4F.js";
|
|
@@ -81,11 +81,11 @@ import {
|
|
|
81
81
|
updateCard,
|
|
82
82
|
updateSkillAvailability,
|
|
83
83
|
updateSkillIdleRate
|
|
84
|
-
} from "./chunk-
|
|
84
|
+
} from "./chunk-O2OYBAVR.js";
|
|
85
85
|
import {
|
|
86
86
|
AgentBnBError,
|
|
87
87
|
AnyCardSchema
|
|
88
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-WVY2W7AA.js";
|
|
89
89
|
|
|
90
90
|
// src/runtime/agent-runtime.ts
|
|
91
91
|
import { readFileSync, existsSync } from "fs";
|
|
@@ -604,7 +604,8 @@ var OpenClawBridge = class {
|
|
|
604
604
|
};
|
|
605
605
|
|
|
606
606
|
// src/skills/command-executor.ts
|
|
607
|
-
import {
|
|
607
|
+
import { spawn } from "child_process";
|
|
608
|
+
var KILL_GRACE_MS = 5e3;
|
|
608
609
|
function shellEscape2(value) {
|
|
609
610
|
return "'" + value.replace(/'/g, "'\\''") + "'";
|
|
610
611
|
}
|
|
@@ -630,29 +631,89 @@ function safeInterpolateCommand2(template, context) {
|
|
|
630
631
|
return shellEscape2(String(current));
|
|
631
632
|
});
|
|
632
633
|
}
|
|
633
|
-
function
|
|
634
|
+
function spawnWithKill(command, options, registry) {
|
|
634
635
|
return new Promise((resolve2, reject) => {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
636
|
+
const child = spawn("/bin/sh", ["-c", `${command} < /dev/null`], {
|
|
637
|
+
cwd: options.cwd,
|
|
638
|
+
env: options.env,
|
|
639
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
640
|
+
detached: true
|
|
641
|
+
});
|
|
642
|
+
registry.add(child);
|
|
643
|
+
let stdout = "";
|
|
644
|
+
let stderr = "";
|
|
645
|
+
let timedOut = false;
|
|
646
|
+
let killed = false;
|
|
647
|
+
let killTimer;
|
|
648
|
+
child.stdout.on("data", (chunk) => {
|
|
649
|
+
if (stdout.length < options.maxBuffer) {
|
|
650
|
+
stdout += chunk.toString();
|
|
651
|
+
}
|
|
652
|
+
});
|
|
653
|
+
child.stderr.on("data", (chunk) => {
|
|
654
|
+
if (stderr.length < options.maxBuffer) {
|
|
655
|
+
stderr += chunk.toString();
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
const killGroup = (signal) => {
|
|
659
|
+
try {
|
|
660
|
+
process.kill(-child.pid, signal);
|
|
661
|
+
} catch {
|
|
662
|
+
try {
|
|
663
|
+
child.kill(signal);
|
|
664
|
+
} catch {
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
const timeoutId = setTimeout(() => {
|
|
669
|
+
timedOut = true;
|
|
670
|
+
killGroup("SIGTERM");
|
|
671
|
+
killTimer = setTimeout(() => {
|
|
672
|
+
if (!killed) {
|
|
673
|
+
killGroup("SIGKILL");
|
|
674
|
+
}
|
|
675
|
+
}, KILL_GRACE_MS);
|
|
676
|
+
killTimer.unref();
|
|
677
|
+
}, options.timeout);
|
|
678
|
+
child.on("close", (_code, _signal) => {
|
|
679
|
+
killed = true;
|
|
680
|
+
clearTimeout(timeoutId);
|
|
681
|
+
if (killTimer) clearTimeout(killTimer);
|
|
682
|
+
registry.delete(child);
|
|
683
|
+
if (timedOut) {
|
|
684
|
+
const err = new Error(`Command timed out after ${options.timeout}ms`);
|
|
685
|
+
err.code = "ETIMEDOUT";
|
|
686
|
+
reject(err);
|
|
687
|
+
} else if (_code !== 0) {
|
|
688
|
+
const err = new Error(stderr.trim() || `Process exited with code ${_code}`);
|
|
689
|
+
Object.assign(err, { stderr: stderr.trim() });
|
|
690
|
+
reject(err);
|
|
641
691
|
} else {
|
|
642
|
-
resolve2({ stdout
|
|
692
|
+
resolve2({ stdout, stderr });
|
|
643
693
|
}
|
|
644
694
|
});
|
|
695
|
+
child.on("error", (err) => {
|
|
696
|
+
killed = true;
|
|
697
|
+
clearTimeout(timeoutId);
|
|
698
|
+
registry.delete(child);
|
|
699
|
+
reject(err);
|
|
700
|
+
});
|
|
645
701
|
});
|
|
646
702
|
}
|
|
647
703
|
var CommandExecutor = class {
|
|
704
|
+
/** Active child processes — killed on shutdown(). */
|
|
705
|
+
activeProcesses = /* @__PURE__ */ new Set();
|
|
706
|
+
/** In-flight execution count per skill ID for concurrency limiting. */
|
|
707
|
+
inflight = /* @__PURE__ */ new Map();
|
|
648
708
|
/**
|
|
649
709
|
* Execute a command skill with the provided parameters.
|
|
650
710
|
*
|
|
651
711
|
* Steps:
|
|
652
|
-
* 1.
|
|
653
|
-
* 2.
|
|
654
|
-
* 3.
|
|
655
|
-
* 4.
|
|
712
|
+
* 1. Concurrency check: reject if at capacity.max_concurrent limit.
|
|
713
|
+
* 2. Security check: base command must be in `allowed_commands` if set.
|
|
714
|
+
* 3. Interpolate `config.command` using `{ params }` context.
|
|
715
|
+
* 4. Run via spawn with SIGTERM→SIGKILL timeout handling.
|
|
716
|
+
* 5. Parse stdout based on `output_type`: text | json | file.
|
|
656
717
|
*
|
|
657
718
|
* @param config - Validated CommandSkillConfig.
|
|
658
719
|
* @param params - Input parameters passed by the caller.
|
|
@@ -660,6 +721,16 @@ var CommandExecutor = class {
|
|
|
660
721
|
*/
|
|
661
722
|
async execute(config, params) {
|
|
662
723
|
const cmdConfig = config;
|
|
724
|
+
const maxConcurrent = cmdConfig.capacity?.max_concurrent;
|
|
725
|
+
if (maxConcurrent !== void 0) {
|
|
726
|
+
const current = this.inflight.get(cmdConfig.id) ?? 0;
|
|
727
|
+
if (current >= maxConcurrent) {
|
|
728
|
+
return {
|
|
729
|
+
success: false,
|
|
730
|
+
error: `Skill "${cmdConfig.id}" at max concurrency (${maxConcurrent}). Try again later.`
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
}
|
|
663
734
|
const baseCommand = cmdConfig.command.trim().split(/\s+/)[0] ?? "";
|
|
664
735
|
if (cmdConfig.allowed_commands && cmdConfig.allowed_commands.length > 0) {
|
|
665
736
|
const effectiveAllowed = cmdConfig.claude_code ? [.../* @__PURE__ */ new Set([...cmdConfig.allowed_commands, "claude"])] : cmdConfig.allowed_commands;
|
|
@@ -690,31 +761,33 @@ var CommandExecutor = class {
|
|
|
690
761
|
}
|
|
691
762
|
const timeout = cmdConfig.timeout_ms ?? 3e4;
|
|
692
763
|
const cwd = cmdConfig.working_dir ?? process.cwd();
|
|
693
|
-
let stdout;
|
|
694
764
|
const env = { ...process.env };
|
|
695
765
|
delete env["CLAUDECODE"];
|
|
766
|
+
this.inflight.set(cmdConfig.id, (this.inflight.get(cmdConfig.id) ?? 0) + 1);
|
|
767
|
+
let stdout;
|
|
696
768
|
try {
|
|
697
|
-
const result = await
|
|
769
|
+
const result = await spawnWithKill(interpolatedCommand, {
|
|
698
770
|
timeout,
|
|
699
771
|
cwd,
|
|
700
772
|
env,
|
|
701
773
|
maxBuffer: 10 * 1024 * 1024
|
|
702
774
|
// 10 MB
|
|
703
|
-
});
|
|
775
|
+
}, this.activeProcesses);
|
|
704
776
|
stdout = result.stdout;
|
|
705
777
|
} catch (err) {
|
|
778
|
+
this.decrementInflight(cmdConfig.id);
|
|
706
779
|
if (err instanceof Error) {
|
|
707
|
-
const
|
|
708
|
-
|
|
709
|
-
if (message.includes("timed out") || message.includes("ETIMEDOUT") || err.code === "ETIMEDOUT") {
|
|
780
|
+
const code = err.code;
|
|
781
|
+
if (code === "ETIMEDOUT" || err.message.includes("timed out")) {
|
|
710
782
|
return {
|
|
711
783
|
success: false,
|
|
712
784
|
error: `Command timed out after ${timeout}ms`
|
|
713
785
|
};
|
|
714
786
|
}
|
|
787
|
+
const stderrContent = err.stderr ?? "";
|
|
715
788
|
return {
|
|
716
789
|
success: false,
|
|
717
|
-
error: stderrContent.trim() || message
|
|
790
|
+
error: stderrContent.trim() || err.message
|
|
718
791
|
};
|
|
719
792
|
}
|
|
720
793
|
return {
|
|
@@ -722,6 +795,7 @@ var CommandExecutor = class {
|
|
|
722
795
|
error: String(err)
|
|
723
796
|
};
|
|
724
797
|
}
|
|
798
|
+
this.decrementInflight(cmdConfig.id);
|
|
725
799
|
const rawOutput = stdout.trim();
|
|
726
800
|
switch (cmdConfig.output_type) {
|
|
727
801
|
case "text":
|
|
@@ -746,6 +820,48 @@ var CommandExecutor = class {
|
|
|
746
820
|
};
|
|
747
821
|
}
|
|
748
822
|
}
|
|
823
|
+
/**
|
|
824
|
+
* Kill all active child processes. Called during service shutdown
|
|
825
|
+
* to prevent zombie processes.
|
|
826
|
+
*/
|
|
827
|
+
shutdown() {
|
|
828
|
+
for (const child of this.activeProcesses) {
|
|
829
|
+
try {
|
|
830
|
+
process.kill(-child.pid, "SIGTERM");
|
|
831
|
+
} catch {
|
|
832
|
+
try {
|
|
833
|
+
child.kill("SIGTERM");
|
|
834
|
+
} catch {
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
const pid = child.pid;
|
|
838
|
+
const timer = setTimeout(() => {
|
|
839
|
+
try {
|
|
840
|
+
process.kill(-pid, "SIGKILL");
|
|
841
|
+
} catch {
|
|
842
|
+
}
|
|
843
|
+
}, KILL_GRACE_MS);
|
|
844
|
+
timer.unref();
|
|
845
|
+
}
|
|
846
|
+
this.activeProcesses.clear();
|
|
847
|
+
}
|
|
848
|
+
/** Returns the number of currently active child processes. */
|
|
849
|
+
get activeCount() {
|
|
850
|
+
return this.activeProcesses.size;
|
|
851
|
+
}
|
|
852
|
+
/** Returns the in-flight count for a specific skill ID. */
|
|
853
|
+
getInflight(skillId) {
|
|
854
|
+
return this.inflight.get(skillId) ?? 0;
|
|
855
|
+
}
|
|
856
|
+
/** Decrement the inflight counter for a skill ID. */
|
|
857
|
+
decrementInflight(skillId) {
|
|
858
|
+
const current = this.inflight.get(skillId) ?? 0;
|
|
859
|
+
if (current <= 1) {
|
|
860
|
+
this.inflight.delete(skillId);
|
|
861
|
+
} else {
|
|
862
|
+
this.inflight.set(skillId, current - 1);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
749
865
|
};
|
|
750
866
|
|
|
751
867
|
// src/runtime/agent-runtime.ts
|
|
@@ -763,6 +879,8 @@ var AgentRuntime = class {
|
|
|
763
879
|
* Undefined if no skills.yaml was provided or the file does not exist.
|
|
764
880
|
*/
|
|
765
881
|
skillExecutor;
|
|
882
|
+
/** Reference to CommandExecutor for shutdown cleanup of child processes. */
|
|
883
|
+
commandExecutor;
|
|
766
884
|
draining = false;
|
|
767
885
|
orphanedEscrowAgeMinutes;
|
|
768
886
|
skillsYamlPath;
|
|
@@ -828,8 +946,8 @@ var AgentRuntime = class {
|
|
|
828
946
|
}
|
|
829
947
|
const modes = /* @__PURE__ */ new Map();
|
|
830
948
|
if (this.conductorEnabled) {
|
|
831
|
-
const { ConductorMode } = await import("./conductor-mode-
|
|
832
|
-
const { registerConductorCard, CONDUCTOR_OWNER } = await import("./card-
|
|
949
|
+
const { ConductorMode } = await import("./conductor-mode-AKREGDIU.js");
|
|
950
|
+
const { registerConductorCard, CONDUCTOR_OWNER } = await import("./card-EX2EYGCZ.js");
|
|
833
951
|
const { loadPeers } = await import("./peers-K7FSHPN3.js");
|
|
834
952
|
registerConductorCard(this.registryDb);
|
|
835
953
|
const resolveAgentUrl = (owner) => {
|
|
@@ -876,10 +994,11 @@ var AgentRuntime = class {
|
|
|
876
994
|
const executor = createSkillExecutor(configs, modes);
|
|
877
995
|
if (hasSkillsYaml) {
|
|
878
996
|
const pipelineExecutor = new PipelineExecutor(executor);
|
|
997
|
+
this.commandExecutor = new CommandExecutor();
|
|
879
998
|
modes.set("api", new ApiExecutor());
|
|
880
999
|
modes.set("pipeline", pipelineExecutor);
|
|
881
1000
|
modes.set("openclaw", new OpenClawBridge());
|
|
882
|
-
modes.set("command",
|
|
1001
|
+
modes.set("command", this.commandExecutor);
|
|
883
1002
|
}
|
|
884
1003
|
this.skillExecutor = executor;
|
|
885
1004
|
}
|
|
@@ -912,6 +1031,9 @@ var AgentRuntime = class {
|
|
|
912
1031
|
return;
|
|
913
1032
|
}
|
|
914
1033
|
this.draining = true;
|
|
1034
|
+
if (this.commandExecutor) {
|
|
1035
|
+
this.commandExecutor.shutdown();
|
|
1036
|
+
}
|
|
915
1037
|
for (const job of this.jobs) {
|
|
916
1038
|
job.stop();
|
|
917
1039
|
}
|
|
@@ -1152,6 +1274,78 @@ function releaseForRelay(creditDb, escrowId) {
|
|
|
1152
1274
|
releaseEscrow(creditDb, escrowId);
|
|
1153
1275
|
}
|
|
1154
1276
|
|
|
1277
|
+
// src/relay/relay-escrow.ts
|
|
1278
|
+
function verifyRelaySignature(data, signature, publicKeyHex) {
|
|
1279
|
+
try {
|
|
1280
|
+
const publicKeyBuf = Buffer.from(publicKeyHex, "hex");
|
|
1281
|
+
return verifyEscrowReceipt(data, signature, publicKeyBuf);
|
|
1282
|
+
} catch {
|
|
1283
|
+
return false;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
function processEscrowHold(creditDb, consumerAgentId, providerAgentId, skillId, amount, requestId, signature, publicKeyHex) {
|
|
1287
|
+
if (signature && publicKeyHex) {
|
|
1288
|
+
const signData = {
|
|
1289
|
+
consumer_agent_id: consumerAgentId,
|
|
1290
|
+
provider_agent_id: providerAgentId,
|
|
1291
|
+
skill_id: skillId,
|
|
1292
|
+
amount,
|
|
1293
|
+
request_id: requestId
|
|
1294
|
+
};
|
|
1295
|
+
if (!verifyRelaySignature(signData, signature, publicKeyHex)) {
|
|
1296
|
+
throw new Error("Invalid consumer signature on escrow hold");
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
const escrowId = holdEscrow(creditDb, consumerAgentId, amount, `${providerAgentId}:${skillId}`);
|
|
1300
|
+
const remaining = getBalance(creditDb, consumerAgentId);
|
|
1301
|
+
return {
|
|
1302
|
+
escrow_id: escrowId,
|
|
1303
|
+
hold_amount: amount,
|
|
1304
|
+
consumer_remaining: remaining
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
function processEscrowSettle(creditDb, escrowId, success, providerAgentId, signature, publicKeyHex, consumerAgentId) {
|
|
1308
|
+
if (signature && publicKeyHex && consumerAgentId) {
|
|
1309
|
+
const signData = {
|
|
1310
|
+
escrow_id: escrowId,
|
|
1311
|
+
success,
|
|
1312
|
+
consumer_agent_id: consumerAgentId
|
|
1313
|
+
};
|
|
1314
|
+
if (!verifyRelaySignature(signData, signature, publicKeyHex)) {
|
|
1315
|
+
throw new Error("Invalid consumer signature on escrow settle");
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
const escrowRow = creditDb.prepare("SELECT amount, owner FROM credit_escrow WHERE id = ? AND status = ?").get(escrowId, "held");
|
|
1319
|
+
if (!escrowRow) {
|
|
1320
|
+
throw new Error(`Escrow not found or already settled: ${escrowId}`);
|
|
1321
|
+
}
|
|
1322
|
+
const NETWORK_FEE_RATE = 0.05;
|
|
1323
|
+
if (success) {
|
|
1324
|
+
settleEscrow(creditDb, escrowId, providerAgentId);
|
|
1325
|
+
const networkFee = Math.floor(escrowRow.amount * NETWORK_FEE_RATE);
|
|
1326
|
+
const providerAmount = escrowRow.amount - networkFee;
|
|
1327
|
+
return {
|
|
1328
|
+
escrow_id: escrowId,
|
|
1329
|
+
provider_earned: providerAmount,
|
|
1330
|
+
network_fee: networkFee,
|
|
1331
|
+
consumer_remaining: getBalance(creditDb, escrowRow.owner),
|
|
1332
|
+
provider_balance: getBalance(creditDb, providerAgentId)
|
|
1333
|
+
};
|
|
1334
|
+
} else {
|
|
1335
|
+
releaseEscrow(creditDb, escrowId);
|
|
1336
|
+
return {
|
|
1337
|
+
escrow_id: escrowId,
|
|
1338
|
+
provider_earned: 0,
|
|
1339
|
+
network_fee: 0,
|
|
1340
|
+
consumer_remaining: getBalance(creditDb, escrowRow.owner),
|
|
1341
|
+
provider_balance: getBalance(creditDb, providerAgentId)
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
function settleWithNetworkFee(creditDb, escrowId, providerOwner) {
|
|
1346
|
+
return processEscrowSettle(creditDb, escrowId, true, providerOwner);
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1155
1349
|
// src/hub-agent/relay-bridge.ts
|
|
1156
1350
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
1157
1351
|
|
|
@@ -1455,10 +1649,17 @@ var RATE_LIMIT_WINDOW_MS = 6e4;
|
|
|
1455
1649
|
var RELAY_TIMEOUT_MS = 3e5;
|
|
1456
1650
|
function registerWebSocketRelay(server, db, creditDb) {
|
|
1457
1651
|
const connections = /* @__PURE__ */ new Map();
|
|
1652
|
+
const agentIdToOwner = /* @__PURE__ */ new Map();
|
|
1458
1653
|
const pendingRequests = /* @__PURE__ */ new Map();
|
|
1459
1654
|
const rateLimits = /* @__PURE__ */ new Map();
|
|
1460
1655
|
const agentCapacities = /* @__PURE__ */ new Map();
|
|
1461
1656
|
let onAgentOnlineCallback;
|
|
1657
|
+
function resolveConnectionKey(target) {
|
|
1658
|
+
const ownerFromAgentId = agentIdToOwner.get(target);
|
|
1659
|
+
if (ownerFromAgentId && connections.has(ownerFromAgentId)) return ownerFromAgentId;
|
|
1660
|
+
if (connections.has(target)) return target;
|
|
1661
|
+
return void 0;
|
|
1662
|
+
}
|
|
1462
1663
|
function checkRateLimit(owner) {
|
|
1463
1664
|
const now = Date.now();
|
|
1464
1665
|
const entry = rateLimits.get(owner);
|
|
@@ -1562,6 +1763,20 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
1562
1763
|
}
|
|
1563
1764
|
}
|
|
1564
1765
|
connections.set(owner, ws);
|
|
1766
|
+
if (msg.agent_id) {
|
|
1767
|
+
agentIdToOwner.set(msg.agent_id, owner);
|
|
1768
|
+
}
|
|
1769
|
+
if (msg.agents && msg.agents.length > 0) {
|
|
1770
|
+
for (const agentEntry of msg.agents) {
|
|
1771
|
+
agentIdToOwner.set(agentEntry.agent_id, owner);
|
|
1772
|
+
for (const agentCard of agentEntry.cards) {
|
|
1773
|
+
try {
|
|
1774
|
+
upsertCard(agentCard, owner);
|
|
1775
|
+
} catch {
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1565
1780
|
const isEphemeral = owner.includes(":req:");
|
|
1566
1781
|
if (isEphemeral) {
|
|
1567
1782
|
const cardId2 = card.id ?? owner;
|
|
@@ -1605,12 +1820,13 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
1605
1820
|
});
|
|
1606
1821
|
return;
|
|
1607
1822
|
}
|
|
1608
|
-
const
|
|
1823
|
+
const targetKey = resolveConnectionKey(msg.target_agent_id ?? msg.target_owner);
|
|
1824
|
+
const targetWs = targetKey ? connections.get(targetKey) : void 0;
|
|
1609
1825
|
if (!targetWs || targetWs.readyState !== 1) {
|
|
1610
1826
|
sendMessage(ws, {
|
|
1611
1827
|
type: "response",
|
|
1612
1828
|
id: msg.id,
|
|
1613
|
-
error: { code: -32603, message: `Agent offline: ${msg.target_owner}` }
|
|
1829
|
+
error: { code: -32603, message: `Agent offline: ${msg.target_agent_id ?? msg.target_owner}` }
|
|
1614
1830
|
});
|
|
1615
1831
|
return;
|
|
1616
1832
|
}
|
|
@@ -1724,7 +1940,7 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
1724
1940
|
if (pending.escrowId && creditDb) {
|
|
1725
1941
|
try {
|
|
1726
1942
|
if (msg.error === void 0) {
|
|
1727
|
-
|
|
1943
|
+
settleWithNetworkFee(creditDb, pending.escrowId, pending.targetOwner);
|
|
1728
1944
|
} else {
|
|
1729
1945
|
releaseForRelay(creditDb, pending.escrowId);
|
|
1730
1946
|
}
|
|
@@ -1762,6 +1978,12 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
1762
1978
|
connections.delete(owner);
|
|
1763
1979
|
rateLimits.delete(owner);
|
|
1764
1980
|
agentCapacities.delete(owner);
|
|
1981
|
+
for (const [agentId, o] of agentIdToOwner) {
|
|
1982
|
+
if (o === owner) {
|
|
1983
|
+
agentIdToOwner.delete(agentId);
|
|
1984
|
+
break;
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1765
1987
|
markOwnerOffline(owner);
|
|
1766
1988
|
for (const [reqId, pending] of pendingRequests) {
|
|
1767
1989
|
if (pending.targetOwner === owner) {
|
|
@@ -1798,6 +2020,96 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
1798
2020
|
function handleHeartbeat(msg) {
|
|
1799
2021
|
agentCapacities.set(msg.owner, msg.capacity);
|
|
1800
2022
|
}
|
|
2023
|
+
function handleEscrowHold(ws, msg) {
|
|
2024
|
+
if (!creditDb) {
|
|
2025
|
+
sendMessage(ws, { type: "error", code: "no_credit_db", message: "Credit system not available" });
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
try {
|
|
2029
|
+
const result = processEscrowHold(
|
|
2030
|
+
creditDb,
|
|
2031
|
+
msg.consumer_agent_id,
|
|
2032
|
+
msg.provider_agent_id,
|
|
2033
|
+
msg.skill_id,
|
|
2034
|
+
msg.amount,
|
|
2035
|
+
msg.request_id,
|
|
2036
|
+
msg.signature,
|
|
2037
|
+
msg.public_key
|
|
2038
|
+
);
|
|
2039
|
+
sendMessage(ws, {
|
|
2040
|
+
type: "escrow_hold_confirmed",
|
|
2041
|
+
request_id: msg.request_id,
|
|
2042
|
+
escrow_id: result.escrow_id,
|
|
2043
|
+
hold_amount: result.hold_amount,
|
|
2044
|
+
consumer_remaining: result.consumer_remaining
|
|
2045
|
+
});
|
|
2046
|
+
} catch (err) {
|
|
2047
|
+
const errMsg = err instanceof Error ? err.message : "Escrow hold failed";
|
|
2048
|
+
const code = errMsg.includes("INSUFFICIENT_CREDITS") ? "insufficient_credits" : "escrow_hold_failed";
|
|
2049
|
+
sendMessage(ws, { type: "error", code, message: errMsg, request_id: msg.request_id });
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
function handleEscrowSettle(ws, msg) {
|
|
2053
|
+
if (!creditDb) {
|
|
2054
|
+
sendMessage(ws, { type: "error", code: "no_credit_db", message: "Credit system not available" });
|
|
2055
|
+
return;
|
|
2056
|
+
}
|
|
2057
|
+
try {
|
|
2058
|
+
const escrow = creditDb.prepare("SELECT card_id FROM credit_escrow WHERE id = ? AND status = ?").get(msg.escrow_id, "held");
|
|
2059
|
+
if (!escrow) {
|
|
2060
|
+
sendMessage(ws, { type: "error", code: "escrow_not_found", message: `Escrow not found: ${msg.escrow_id}`, request_id: msg.request_id });
|
|
2061
|
+
return;
|
|
2062
|
+
}
|
|
2063
|
+
const providerAgentId = escrow.card_id.split(":")[0];
|
|
2064
|
+
const result = processEscrowSettle(
|
|
2065
|
+
creditDb,
|
|
2066
|
+
msg.escrow_id,
|
|
2067
|
+
msg.success,
|
|
2068
|
+
providerAgentId,
|
|
2069
|
+
msg.signature,
|
|
2070
|
+
msg.public_key,
|
|
2071
|
+
msg.consumer_agent_id
|
|
2072
|
+
);
|
|
2073
|
+
sendMessage(ws, {
|
|
2074
|
+
type: "escrow_settled",
|
|
2075
|
+
escrow_id: result.escrow_id,
|
|
2076
|
+
request_id: msg.request_id,
|
|
2077
|
+
provider_earned: result.provider_earned,
|
|
2078
|
+
network_fee: result.network_fee,
|
|
2079
|
+
consumer_remaining: result.consumer_remaining,
|
|
2080
|
+
provider_balance: result.provider_balance
|
|
2081
|
+
});
|
|
2082
|
+
const providerKey = resolveConnectionKey(providerAgentId);
|
|
2083
|
+
if (providerKey) {
|
|
2084
|
+
const providerWs = connections.get(providerKey);
|
|
2085
|
+
if (providerWs && providerWs.readyState === 1) {
|
|
2086
|
+
sendMessage(providerWs, {
|
|
2087
|
+
type: "escrow_settled",
|
|
2088
|
+
escrow_id: result.escrow_id,
|
|
2089
|
+
request_id: msg.request_id,
|
|
2090
|
+
provider_earned: result.provider_earned,
|
|
2091
|
+
network_fee: result.network_fee,
|
|
2092
|
+
consumer_remaining: result.consumer_remaining,
|
|
2093
|
+
provider_balance: result.provider_balance
|
|
2094
|
+
});
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
} catch (err) {
|
|
2098
|
+
sendMessage(ws, { type: "error", code: "escrow_settle_failed", message: err instanceof Error ? err.message : "Settlement failed", request_id: msg.request_id });
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
function handleBalanceSync(ws, msg) {
|
|
2102
|
+
if (!creditDb) {
|
|
2103
|
+
sendMessage(ws, { type: "error", code: "no_credit_db", message: "Credit system not available" });
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
const balance = getBalance(creditDb, msg.agent_id);
|
|
2107
|
+
sendMessage(ws, {
|
|
2108
|
+
type: "balance_sync_response",
|
|
2109
|
+
agent_id: msg.agent_id,
|
|
2110
|
+
balance
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
1801
2113
|
void server.register(async (app) => {
|
|
1802
2114
|
app.get("/ws", { websocket: true }, (rawSocket, _request) => {
|
|
1803
2115
|
const socket = rawSocket;
|
|
@@ -1846,6 +2158,16 @@ function registerWebSocketRelay(server, db, creditDb) {
|
|
|
1846
2158
|
case "heartbeat":
|
|
1847
2159
|
handleHeartbeat(msg);
|
|
1848
2160
|
break;
|
|
2161
|
+
// V8 Phase 2: Explicit escrow messages
|
|
2162
|
+
case "escrow_hold":
|
|
2163
|
+
handleEscrowHold(socket, msg);
|
|
2164
|
+
break;
|
|
2165
|
+
case "escrow_settle":
|
|
2166
|
+
handleEscrowSettle(socket, msg);
|
|
2167
|
+
break;
|
|
2168
|
+
case "balance_sync":
|
|
2169
|
+
handleBalanceSync(socket, msg);
|
|
2170
|
+
break;
|
|
1849
2171
|
default:
|
|
1850
2172
|
break;
|
|
1851
2173
|
}
|
|
@@ -4499,7 +4821,7 @@ var IdleMonitor = class {
|
|
|
4499
4821
|
};
|
|
4500
4822
|
|
|
4501
4823
|
// src/runtime/service-coordinator.ts
|
|
4502
|
-
import { spawn } from "child_process";
|
|
4824
|
+
import { spawn as spawn2 } from "child_process";
|
|
4503
4825
|
import { createRequire } from "module";
|
|
4504
4826
|
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
4505
4827
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -4527,10 +4849,14 @@ var ServiceCoordinator = class {
|
|
|
4527
4849
|
if (health.ok && health.agentbnb) {
|
|
4528
4850
|
return "already_running";
|
|
4529
4851
|
}
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4852
|
+
if (opts?.foreground) {
|
|
4853
|
+
this.guard.release();
|
|
4854
|
+
} else {
|
|
4855
|
+
throw new AgentBnBError(
|
|
4856
|
+
`AgentBnB lock exists but health check failed (pid=${running.pid}, port=${running.port})`,
|
|
4857
|
+
"SERVICE_UNHEALTHY"
|
|
4858
|
+
);
|
|
4859
|
+
}
|
|
4534
4860
|
}
|
|
4535
4861
|
if (opts?.foreground) {
|
|
4536
4862
|
return this.startInProcess(opts);
|
|
@@ -4645,7 +4971,7 @@ var ServiceCoordinator = class {
|
|
|
4645
4971
|
console.log("Conductor mode enabled \u2014 orchestrate/plan skills available via gateway");
|
|
4646
4972
|
}
|
|
4647
4973
|
if (opts.conductorEnabled && this.config.conductor?.public) {
|
|
4648
|
-
const { buildConductorCard } = await import("./card-
|
|
4974
|
+
const { buildConductorCard } = await import("./card-EX2EYGCZ.js");
|
|
4649
4975
|
const conductorCard = buildConductorCard(this.config.owner);
|
|
4650
4976
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4651
4977
|
const existing = this.runtime.registryDb.prepare("SELECT id FROM capability_cards WHERE id = ?").get(conductorCard.id);
|
|
@@ -4702,8 +5028,8 @@ var ServiceCoordinator = class {
|
|
|
4702
5028
|
}
|
|
4703
5029
|
}
|
|
4704
5030
|
if (opts.registryUrl && opts.relay) {
|
|
4705
|
-
const { RelayClient } = await import("./websocket-client-
|
|
4706
|
-
const { executeCapabilityRequest: executeCapabilityRequest2 } = await import("./execute-
|
|
5031
|
+
const { RelayClient } = await import("./websocket-client-QOVARTRN.js");
|
|
5032
|
+
const { executeCapabilityRequest: executeCapabilityRequest2 } = await import("./execute-AYQWORVH.js");
|
|
4707
5033
|
const cards = listCards(this.runtime.registryDb, this.config.owner);
|
|
4708
5034
|
const card = cards[0] ?? {
|
|
4709
5035
|
id: randomUUID6(),
|
|
@@ -4719,7 +5045,7 @@ var ServiceCoordinator = class {
|
|
|
4719
5045
|
};
|
|
4720
5046
|
const additionalCards = [];
|
|
4721
5047
|
if (this.config.conductor?.public) {
|
|
4722
|
-
const { buildConductorCard } = await import("./card-
|
|
5048
|
+
const { buildConductorCard } = await import("./card-EX2EYGCZ.js");
|
|
4723
5049
|
additionalCards.push(
|
|
4724
5050
|
buildConductorCard(this.config.owner)
|
|
4725
5051
|
);
|
|
@@ -4728,6 +5054,7 @@ var ServiceCoordinator = class {
|
|
|
4728
5054
|
this.relayClient = new RelayClient({
|
|
4729
5055
|
registryUrl: opts.registryUrl,
|
|
4730
5056
|
owner: this.config.owner,
|
|
5057
|
+
agent_id: this.config.agent_id,
|
|
4731
5058
|
token: this.config.token,
|
|
4732
5059
|
card,
|
|
4733
5060
|
cards: additionalCards.length > 0 ? additionalCards : void 0,
|
|
@@ -4810,7 +5137,7 @@ var ServiceCoordinator = class {
|
|
|
4810
5137
|
const runtime = loadPersistedRuntime(getConfigDir());
|
|
4811
5138
|
const nodeExec = resolveNodeExecutable(runtime);
|
|
4812
5139
|
const cliArgs = resolveCliLaunchArgs(this.buildServeArgs(opts));
|
|
4813
|
-
const child =
|
|
5140
|
+
const child = spawn2(nodeExec, cliArgs, {
|
|
4814
5141
|
detached: true,
|
|
4815
5142
|
stdio: "ignore",
|
|
4816
5143
|
env: { ...process.env }
|