agentbnb 2.2.0 → 3.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/index.js CHANGED
@@ -458,7 +458,7 @@ function updateReputation(db, cardId, success, latencyMs) {
458
458
 
459
459
  // src/registry/matcher.ts
460
460
  function searchCards(db, query, filters = {}) {
461
- const words = query.trim().split(/\s+/).map((w) => w.replace(/"/g, "")).filter((w) => w.length > 0);
461
+ const words = query.trim().split(/\s+/).map((w) => w.replace(/["*^{}():]/g, "")).filter((w) => w.length > 0);
462
462
  if (words.length === 0) return [];
463
463
  const ftsQuery = words.map((w) => `"${w}"`).join(" OR ");
464
464
  const conditions = [];
@@ -537,6 +537,24 @@ function getBalance(db, owner) {
537
537
  const row = db.prepare("SELECT balance FROM credit_balances WHERE owner = ?").get(owner);
538
538
  return row?.balance ?? 0;
539
539
  }
540
+ function recordEarning(db, owner, amount, _cardId, receiptNonce) {
541
+ const now = (/* @__PURE__ */ new Date()).toISOString();
542
+ db.transaction(() => {
543
+ const existing = db.prepare(
544
+ "SELECT id FROM credit_transactions WHERE reference_id = ? AND reason = 'remote_earning'"
545
+ ).get(receiptNonce);
546
+ if (existing) return;
547
+ db.prepare(
548
+ "INSERT OR IGNORE INTO credit_balances (owner, balance, updated_at) VALUES (?, 0, ?)"
549
+ ).run(owner, now);
550
+ db.prepare(
551
+ "UPDATE credit_balances SET balance = balance + ?, updated_at = ? WHERE owner = ?"
552
+ ).run(amount, now, owner);
553
+ db.prepare(
554
+ "INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
555
+ ).run(randomUUID(), owner, amount, "remote_earning", receiptNonce, now);
556
+ })();
557
+ }
540
558
 
541
559
  // src/gateway/server.ts
542
560
  import Fastify from "fastify";
@@ -618,6 +636,98 @@ function releaseEscrow(db, escrowId) {
618
636
  });
619
637
  release();
620
638
  }
639
+ function confirmEscrowDebit(db, escrowId) {
640
+ const now = (/* @__PURE__ */ new Date()).toISOString();
641
+ const confirm = db.transaction(() => {
642
+ const escrow = db.prepare("SELECT id, owner, amount, status FROM credit_escrow WHERE id = ?").get(escrowId);
643
+ if (!escrow) {
644
+ throw new AgentBnBError(`Escrow not found: ${escrowId}`, "ESCROW_NOT_FOUND");
645
+ }
646
+ if (escrow.status !== "held") {
647
+ throw new AgentBnBError(
648
+ `Escrow ${escrowId} is already ${escrow.status}`,
649
+ "ESCROW_ALREADY_SETTLED"
650
+ );
651
+ }
652
+ db.prepare(
653
+ "UPDATE credit_escrow SET status = ?, settled_at = ? WHERE id = ?"
654
+ ).run("settled", now, escrowId);
655
+ db.prepare(
656
+ "INSERT INTO credit_transactions (id, owner, amount, reason, reference_id, created_at) VALUES (?, ?, ?, ?, ?, ?)"
657
+ ).run(randomUUID2(), escrow.owner, 0, "remote_settlement_confirmed", escrowId, now);
658
+ });
659
+ confirm();
660
+ }
661
+
662
+ // src/credit/signing.ts
663
+ import { generateKeyPairSync, sign, verify, createPublicKey, createPrivateKey } from "crypto";
664
+ import { writeFileSync, readFileSync, existsSync, chmodSync } from "fs";
665
+ import { join } from "path";
666
+ function generateKeyPair() {
667
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
668
+ publicKeyEncoding: { type: "spki", format: "der" },
669
+ privateKeyEncoding: { type: "pkcs8", format: "der" }
670
+ });
671
+ return {
672
+ publicKey: Buffer.from(publicKey),
673
+ privateKey: Buffer.from(privateKey)
674
+ };
675
+ }
676
+ function saveKeyPair(configDir, keys) {
677
+ const privatePath = join(configDir, "private.key");
678
+ const publicPath = join(configDir, "public.key");
679
+ writeFileSync(privatePath, keys.privateKey);
680
+ chmodSync(privatePath, 384);
681
+ writeFileSync(publicPath, keys.publicKey);
682
+ }
683
+ function loadKeyPair(configDir) {
684
+ const privatePath = join(configDir, "private.key");
685
+ const publicPath = join(configDir, "public.key");
686
+ if (!existsSync(privatePath) || !existsSync(publicPath)) {
687
+ throw new AgentBnBError("Keypair not found. Run `agentbnb init` to generate one.", "KEYPAIR_NOT_FOUND");
688
+ }
689
+ return {
690
+ publicKey: readFileSync(publicPath),
691
+ privateKey: readFileSync(privatePath)
692
+ };
693
+ }
694
+ function canonicalJson(data) {
695
+ return JSON.stringify(data, Object.keys(data).sort());
696
+ }
697
+ function signEscrowReceipt(data, privateKey) {
698
+ const message = Buffer.from(canonicalJson(data), "utf-8");
699
+ const keyObject = createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" });
700
+ const signature = sign(null, message, keyObject);
701
+ return signature.toString("base64url");
702
+ }
703
+ function verifyEscrowReceipt(data, signature, publicKey) {
704
+ try {
705
+ const message = Buffer.from(canonicalJson(data), "utf-8");
706
+ const keyObject = createPublicKey({ key: publicKey, format: "der", type: "spki" });
707
+ const sigBuffer = Buffer.from(signature, "base64url");
708
+ return verify(null, message, keyObject, sigBuffer);
709
+ } catch {
710
+ return false;
711
+ }
712
+ }
713
+
714
+ // src/credit/settlement.ts
715
+ function settleProviderEarning(providerDb, providerOwner, receipt) {
716
+ recordEarning(
717
+ providerDb,
718
+ providerOwner,
719
+ receipt.amount,
720
+ receipt.card_id,
721
+ receipt.nonce
722
+ );
723
+ return { settled: true };
724
+ }
725
+ function settleRequesterEscrow(requesterDb, escrowId) {
726
+ confirmEscrowDebit(requesterDb, escrowId);
727
+ }
728
+ function releaseRequesterEscrow(requesterDb, escrowId) {
729
+ releaseEscrow(requesterDb, escrowId);
730
+ }
621
731
 
622
732
  // src/gateway/server.ts
623
733
  var VERSION = "0.0.1";
@@ -713,24 +823,55 @@ function createGatewayServer(opts) {
713
823
  creditsNeeded = card.pricing.credits_per_call;
714
824
  cardName = card.name;
715
825
  }
716
- let escrowId;
717
- try {
718
- const balance = getBalance(creditDb, requester);
719
- if (balance < creditsNeeded) {
826
+ const receipt = params.escrow_receipt;
827
+ let escrowId = null;
828
+ let isRemoteEscrow = false;
829
+ if (receipt) {
830
+ const { signature, ...receiptData } = receipt;
831
+ const publicKeyBuf = Buffer.from(receipt.requester_public_key, "hex");
832
+ const valid = verifyEscrowReceipt(receiptData, signature, publicKeyBuf);
833
+ if (!valid) {
720
834
  return reply.send({
721
835
  jsonrpc: "2.0",
722
836
  id,
723
- error: { code: -32603, message: "Insufficient credits" }
837
+ error: { code: -32603, message: "Invalid escrow receipt signature" }
838
+ });
839
+ }
840
+ if (receipt.amount < creditsNeeded) {
841
+ return reply.send({
842
+ jsonrpc: "2.0",
843
+ id,
844
+ error: { code: -32603, message: "Insufficient escrow amount" }
845
+ });
846
+ }
847
+ const receiptAge = Date.now() - new Date(receipt.timestamp).getTime();
848
+ if (receiptAge > 5 * 60 * 1e3) {
849
+ return reply.send({
850
+ jsonrpc: "2.0",
851
+ id,
852
+ error: { code: -32603, message: "Escrow receipt expired" }
853
+ });
854
+ }
855
+ isRemoteEscrow = true;
856
+ } else {
857
+ try {
858
+ const balance = getBalance(creditDb, requester);
859
+ if (balance < creditsNeeded) {
860
+ return reply.send({
861
+ jsonrpc: "2.0",
862
+ id,
863
+ error: { code: -32603, message: "Insufficient credits" }
864
+ });
865
+ }
866
+ escrowId = holdEscrow(creditDb, requester, creditsNeeded, cardId);
867
+ } catch (err) {
868
+ const msg = err instanceof AgentBnBError ? err.message : "Failed to hold escrow";
869
+ return reply.send({
870
+ jsonrpc: "2.0",
871
+ id,
872
+ error: { code: -32603, message: msg }
724
873
  });
725
874
  }
726
- escrowId = holdEscrow(creditDb, requester, creditsNeeded, cardId);
727
- } catch (err) {
728
- const msg = err instanceof AgentBnBError ? err.message : "Failed to hold escrow";
729
- return reply.send({
730
- jsonrpc: "2.0",
731
- id,
732
- error: { code: -32603, message: msg }
733
- });
734
875
  }
735
876
  const startMs = Date.now();
736
877
  if (skillExecutor) {
@@ -739,7 +880,7 @@ function createGatewayServer(opts) {
739
880
  try {
740
881
  execResult = await skillExecutor.execute(targetSkillId, params);
741
882
  } catch (err) {
742
- releaseEscrow(creditDb, escrowId);
883
+ if (!isRemoteEscrow && escrowId) releaseEscrow(creditDb, escrowId);
743
884
  updateReputation(registryDb, cardId, false, Date.now() - startMs);
744
885
  try {
745
886
  insertRequestLog(registryDb, {
@@ -759,11 +900,15 @@ function createGatewayServer(opts) {
759
900
  return reply.send({
760
901
  jsonrpc: "2.0",
761
902
  id,
762
- error: { code: -32603, message }
903
+ error: {
904
+ code: -32603,
905
+ message,
906
+ ...isRemoteEscrow ? { data: { receipt_released: true } } : {}
907
+ }
763
908
  });
764
909
  }
765
910
  if (!execResult.success) {
766
- releaseEscrow(creditDb, escrowId);
911
+ if (!isRemoteEscrow && escrowId) releaseEscrow(creditDb, escrowId);
767
912
  updateReputation(registryDb, cardId, false, execResult.latency_ms);
768
913
  try {
769
914
  insertRequestLog(registryDb, {
@@ -782,10 +927,18 @@ function createGatewayServer(opts) {
782
927
  return reply.send({
783
928
  jsonrpc: "2.0",
784
929
  id,
785
- error: { code: -32603, message: execResult.error ?? "Execution failed" }
930
+ error: {
931
+ code: -32603,
932
+ message: execResult.error ?? "Execution failed",
933
+ ...isRemoteEscrow ? { data: { receipt_released: true } } : {}
934
+ }
786
935
  });
787
936
  }
788
- settleEscrow(creditDb, escrowId, card.owner);
937
+ if (isRemoteEscrow && receipt) {
938
+ settleProviderEarning(creditDb, card.owner, receipt);
939
+ } else if (escrowId) {
940
+ settleEscrow(creditDb, escrowId, card.owner);
941
+ }
789
942
  updateReputation(registryDb, cardId, true, execResult.latency_ms);
790
943
  try {
791
944
  insertRequestLog(registryDb, {
@@ -801,7 +954,8 @@ function createGatewayServer(opts) {
801
954
  });
802
955
  } catch {
803
956
  }
804
- return reply.send({ jsonrpc: "2.0", id, result: execResult.result });
957
+ const successResult = isRemoteEscrow ? { ...typeof execResult.result === "object" && execResult.result !== null ? execResult.result : { data: execResult.result }, receipt_settled: true, receipt_nonce: receipt.nonce } : execResult.result;
958
+ return reply.send({ jsonrpc: "2.0", id, result: successResult });
805
959
  }
806
960
  const controller = new AbortController();
807
961
  const timer = setTimeout(() => controller.abort(), timeoutMs);
@@ -814,7 +968,7 @@ function createGatewayServer(opts) {
814
968
  });
815
969
  clearTimeout(timer);
816
970
  if (!response.ok) {
817
- releaseEscrow(creditDb, escrowId);
971
+ if (!isRemoteEscrow && escrowId) releaseEscrow(creditDb, escrowId);
818
972
  updateReputation(registryDb, cardId, false, Date.now() - startMs);
819
973
  try {
820
974
  insertRequestLog(registryDb, {
@@ -833,11 +987,19 @@ function createGatewayServer(opts) {
833
987
  return reply.send({
834
988
  jsonrpc: "2.0",
835
989
  id,
836
- error: { code: -32603, message: `Handler returned ${response.status}` }
990
+ error: {
991
+ code: -32603,
992
+ message: `Handler returned ${response.status}`,
993
+ ...isRemoteEscrow ? { data: { receipt_released: true } } : {}
994
+ }
837
995
  });
838
996
  }
839
997
  const result = await response.json();
840
- settleEscrow(creditDb, escrowId, card.owner);
998
+ if (isRemoteEscrow && receipt) {
999
+ settleProviderEarning(creditDb, card.owner, receipt);
1000
+ } else if (escrowId) {
1001
+ settleEscrow(creditDb, escrowId, card.owner);
1002
+ }
841
1003
  updateReputation(registryDb, cardId, true, Date.now() - startMs);
842
1004
  try {
843
1005
  insertRequestLog(registryDb, {
@@ -853,10 +1015,11 @@ function createGatewayServer(opts) {
853
1015
  });
854
1016
  } catch {
855
1017
  }
856
- return reply.send({ jsonrpc: "2.0", id, result });
1018
+ const successResult = isRemoteEscrow ? { ...typeof result === "object" && result !== null ? result : { data: result }, receipt_settled: true, receipt_nonce: receipt.nonce } : result;
1019
+ return reply.send({ jsonrpc: "2.0", id, result: successResult });
857
1020
  } catch (err) {
858
1021
  clearTimeout(timer);
859
- releaseEscrow(creditDb, escrowId);
1022
+ if (!isRemoteEscrow && escrowId) releaseEscrow(creditDb, escrowId);
860
1023
  updateReputation(registryDb, cardId, false, Date.now() - startMs);
861
1024
  const isTimeout = err instanceof Error && err.name === "AbortError";
862
1025
  try {
@@ -876,19 +1039,1550 @@ function createGatewayServer(opts) {
876
1039
  return reply.send({
877
1040
  jsonrpc: "2.0",
878
1041
  id,
879
- error: { code: -32603, message: isTimeout ? "Execution timeout" : "Handler error" }
1042
+ error: {
1043
+ code: -32603,
1044
+ message: isTimeout ? "Execution timeout" : "Handler error",
1045
+ ...isRemoteEscrow ? { data: { receipt_released: true } } : {}
1046
+ }
880
1047
  });
881
1048
  }
882
1049
  });
883
1050
  return fastify;
884
1051
  }
1052
+
1053
+ // src/skills/executor.ts
1054
+ var SkillExecutor = class {
1055
+ skillMap;
1056
+ modeMap;
1057
+ /**
1058
+ * @param configs - Parsed SkillConfig array (from parseSkillsFile).
1059
+ * @param modes - Map from skill type string to its executor implementation.
1060
+ */
1061
+ constructor(configs, modes) {
1062
+ this.skillMap = new Map(configs.map((c) => [c.id, c]));
1063
+ this.modeMap = modes;
1064
+ }
1065
+ /**
1066
+ * Execute a skill by ID with the given input parameters.
1067
+ *
1068
+ * Dispatch order:
1069
+ * 1. Look up skill config by skillId.
1070
+ * 2. Find executor mode by config.type.
1071
+ * 3. Invoke mode.execute(), wrap with latency timing.
1072
+ * 4. Catch any thrown errors and return as ExecutionResult with success:false.
1073
+ *
1074
+ * @param skillId - The ID of the skill to execute.
1075
+ * @param params - Input parameters for the skill.
1076
+ * @returns ExecutionResult including success, result/error, and latency_ms.
1077
+ */
1078
+ async execute(skillId, params) {
1079
+ const startTime = Date.now();
1080
+ const config = this.skillMap.get(skillId);
1081
+ if (!config) {
1082
+ return {
1083
+ success: false,
1084
+ error: `Skill not found: "${skillId}"`,
1085
+ latency_ms: Date.now() - startTime
1086
+ };
1087
+ }
1088
+ const mode = this.modeMap.get(config.type);
1089
+ if (!mode) {
1090
+ return {
1091
+ success: false,
1092
+ error: `No executor registered for skill type "${config.type}" (skill: "${skillId}")`,
1093
+ latency_ms: Date.now() - startTime
1094
+ };
1095
+ }
1096
+ try {
1097
+ const modeResult = await mode.execute(config, params);
1098
+ return {
1099
+ ...modeResult,
1100
+ latency_ms: Date.now() - startTime
1101
+ };
1102
+ } catch (err) {
1103
+ const message = err instanceof Error ? err.message : String(err);
1104
+ return {
1105
+ success: false,
1106
+ error: message,
1107
+ latency_ms: Date.now() - startTime
1108
+ };
1109
+ }
1110
+ }
1111
+ /**
1112
+ * Returns the IDs of all registered skills.
1113
+ *
1114
+ * @returns Array of skill ID strings.
1115
+ */
1116
+ listSkills() {
1117
+ return Array.from(this.skillMap.keys());
1118
+ }
1119
+ /**
1120
+ * Returns the SkillConfig for a given skill ID, or undefined if not found.
1121
+ *
1122
+ * @param skillId - The skill ID to look up.
1123
+ * @returns The SkillConfig or undefined.
1124
+ */
1125
+ getSkillConfig(skillId) {
1126
+ return this.skillMap.get(skillId);
1127
+ }
1128
+ };
1129
+ function createSkillExecutor(configs, modes) {
1130
+ return new SkillExecutor(configs, modes);
1131
+ }
1132
+
1133
+ // src/skills/skill-config.ts
1134
+ import { z as z2 } from "zod";
1135
+ import yaml from "js-yaml";
1136
+ var PricingSchema = z2.object({
1137
+ credits_per_call: z2.number().nonnegative(),
1138
+ credits_per_minute: z2.number().nonnegative().optional(),
1139
+ free_tier: z2.number().nonnegative().optional()
1140
+ });
1141
+ var ApiAuthSchema = z2.discriminatedUnion("type", [
1142
+ z2.object({
1143
+ type: z2.literal("bearer"),
1144
+ token: z2.string()
1145
+ }),
1146
+ z2.object({
1147
+ type: z2.literal("apikey"),
1148
+ header: z2.string().default("X-API-Key"),
1149
+ key: z2.string()
1150
+ }),
1151
+ z2.object({
1152
+ type: z2.literal("basic"),
1153
+ username: z2.string(),
1154
+ password: z2.string()
1155
+ })
1156
+ ]);
1157
+ var ApiSkillConfigSchema = z2.object({
1158
+ id: z2.string().min(1),
1159
+ type: z2.literal("api"),
1160
+ name: z2.string().min(1),
1161
+ endpoint: z2.string().min(1),
1162
+ method: z2.enum(["GET", "POST", "PUT", "DELETE"]),
1163
+ auth: ApiAuthSchema.optional(),
1164
+ input_mapping: z2.record(z2.string()).default({}),
1165
+ output_mapping: z2.record(z2.string()).default({}),
1166
+ pricing: PricingSchema,
1167
+ timeout_ms: z2.number().positive().default(3e4),
1168
+ retries: z2.number().nonnegative().int().default(0),
1169
+ provider: z2.string().optional()
1170
+ });
1171
+ var PipelineStepSchema = z2.union([
1172
+ z2.object({
1173
+ skill_id: z2.string().min(1),
1174
+ input_mapping: z2.record(z2.string()).default({})
1175
+ }),
1176
+ z2.object({
1177
+ command: z2.string().min(1),
1178
+ input_mapping: z2.record(z2.string()).default({})
1179
+ })
1180
+ ]);
1181
+ var PipelineSkillConfigSchema = z2.object({
1182
+ id: z2.string().min(1),
1183
+ type: z2.literal("pipeline"),
1184
+ name: z2.string().min(1),
1185
+ steps: z2.array(PipelineStepSchema).min(1),
1186
+ pricing: PricingSchema,
1187
+ timeout_ms: z2.number().positive().optional()
1188
+ });
1189
+ var OpenClawSkillConfigSchema = z2.object({
1190
+ id: z2.string().min(1),
1191
+ type: z2.literal("openclaw"),
1192
+ name: z2.string().min(1),
1193
+ agent_name: z2.string().min(1),
1194
+ channel: z2.enum(["telegram", "webhook", "process"]),
1195
+ pricing: PricingSchema,
1196
+ timeout_ms: z2.number().positive().optional()
1197
+ });
1198
+ var CommandSkillConfigSchema = z2.object({
1199
+ id: z2.string().min(1),
1200
+ type: z2.literal("command"),
1201
+ name: z2.string().min(1),
1202
+ command: z2.string().min(1),
1203
+ output_type: z2.enum(["json", "text", "file"]),
1204
+ allowed_commands: z2.array(z2.string()).optional(),
1205
+ working_dir: z2.string().optional(),
1206
+ timeout_ms: z2.number().positive().default(3e4),
1207
+ pricing: PricingSchema
1208
+ });
1209
+ var ConductorSkillConfigSchema = z2.object({
1210
+ id: z2.string().min(1),
1211
+ type: z2.literal("conductor"),
1212
+ name: z2.string().min(1),
1213
+ conductor_skill: z2.enum(["orchestrate", "plan"]),
1214
+ pricing: PricingSchema,
1215
+ timeout_ms: z2.number().positive().optional()
1216
+ });
1217
+ var SkillConfigSchema = z2.discriminatedUnion("type", [
1218
+ ApiSkillConfigSchema,
1219
+ PipelineSkillConfigSchema,
1220
+ OpenClawSkillConfigSchema,
1221
+ CommandSkillConfigSchema,
1222
+ ConductorSkillConfigSchema
1223
+ ]);
1224
+ var SkillsFileSchema = z2.object({
1225
+ skills: z2.array(SkillConfigSchema)
1226
+ });
1227
+ function expandEnvVars(value) {
1228
+ return value.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
1229
+ if (/[.a-z]/.test(varName)) {
1230
+ return _match;
1231
+ }
1232
+ const envValue = process.env[varName];
1233
+ if (envValue === void 0) {
1234
+ throw new Error(`Environment variable "${varName}" is not defined`);
1235
+ }
1236
+ return envValue;
1237
+ });
1238
+ }
1239
+ function expandEnvVarsDeep(value) {
1240
+ if (typeof value === "string") {
1241
+ return expandEnvVars(value);
1242
+ }
1243
+ if (Array.isArray(value)) {
1244
+ return value.map(expandEnvVarsDeep);
1245
+ }
1246
+ if (value !== null && typeof value === "object") {
1247
+ const result = {};
1248
+ for (const [k, v] of Object.entries(value)) {
1249
+ result[k] = expandEnvVarsDeep(v);
1250
+ }
1251
+ return result;
1252
+ }
1253
+ return value;
1254
+ }
1255
+ function parseSkillsFile(yamlContent) {
1256
+ const raw = yaml.load(yamlContent);
1257
+ const expanded = expandEnvVarsDeep(raw);
1258
+ const result = SkillsFileSchema.parse(expanded);
1259
+ return result.skills;
1260
+ }
1261
+
1262
+ // src/skills/api-executor.ts
1263
+ function parseMappingTarget(mapping) {
1264
+ const dotIndex = mapping.indexOf(".");
1265
+ if (dotIndex < 0) {
1266
+ throw new Error(`Invalid input_mapping format: "${mapping}" (expected "target.key")`);
1267
+ }
1268
+ const target = mapping.slice(0, dotIndex);
1269
+ const key = mapping.slice(dotIndex + 1);
1270
+ if (!["body", "query", "path", "header"].includes(target)) {
1271
+ throw new Error(
1272
+ `Invalid mapping target "${target}" in "${mapping}" (must be body|query|path|header)`
1273
+ );
1274
+ }
1275
+ return { target, key };
1276
+ }
1277
+ function extractByPath(obj, dotPath) {
1278
+ const normalizedPath = dotPath.startsWith("response.") ? dotPath.slice("response.".length) : dotPath;
1279
+ const parts = normalizedPath.split(".");
1280
+ let current = obj;
1281
+ for (const part of parts) {
1282
+ if (current === null || typeof current !== "object") {
1283
+ return void 0;
1284
+ }
1285
+ current = current[part];
1286
+ }
1287
+ return current;
1288
+ }
1289
+ function buildAuthHeaders(auth) {
1290
+ if (!auth) return {};
1291
+ switch (auth.type) {
1292
+ case "bearer":
1293
+ return { Authorization: `Bearer ${auth.token}` };
1294
+ case "apikey":
1295
+ return { [auth.header]: auth.key };
1296
+ case "basic": {
1297
+ const encoded = Buffer.from(`${auth.username}:${auth.password}`).toString("base64");
1298
+ return { Authorization: `Basic ${encoded}` };
1299
+ }
1300
+ }
1301
+ }
1302
+ function applyInputMapping(params, mapping) {
1303
+ const body = {};
1304
+ const query = {};
1305
+ const pathParams = {};
1306
+ const headers = {};
1307
+ for (const [paramName, mappingValue] of Object.entries(mapping)) {
1308
+ const value = params[paramName];
1309
+ if (value === void 0) continue;
1310
+ const { target, key } = parseMappingTarget(mappingValue);
1311
+ switch (target) {
1312
+ case "body":
1313
+ body[key] = value;
1314
+ break;
1315
+ case "query":
1316
+ query[key] = String(value);
1317
+ break;
1318
+ case "path":
1319
+ pathParams[key] = String(value);
1320
+ break;
1321
+ case "header":
1322
+ headers[key] = String(value).replace(/[\r\n]/g, "");
1323
+ break;
1324
+ }
1325
+ }
1326
+ return { body, query, pathParams, headers };
1327
+ }
1328
+ function sleep(ms) {
1329
+ return new Promise((resolve) => setTimeout(resolve, ms));
1330
+ }
1331
+ var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 500, 503]);
1332
+ var ApiExecutor = class {
1333
+ /**
1334
+ * Execute an API call described by the given skill config.
1335
+ *
1336
+ * @param config - The validated SkillConfig (must be ApiSkillConfig).
1337
+ * @param params - Input parameters to map to the HTTP request.
1338
+ * @returns Partial ExecutionResult (without latency_ms — added by SkillExecutor).
1339
+ */
1340
+ async execute(config, params) {
1341
+ const apiConfig = config;
1342
+ const { body, query, pathParams, headers: mappedHeaders } = applyInputMapping(
1343
+ params,
1344
+ apiConfig.input_mapping
1345
+ );
1346
+ let url = apiConfig.endpoint;
1347
+ for (const [key, value] of Object.entries(pathParams)) {
1348
+ url = url.replace(`{${key}}`, encodeURIComponent(value));
1349
+ }
1350
+ if (Object.keys(query).length > 0) {
1351
+ const qs = new URLSearchParams(query).toString();
1352
+ url = `${url}?${qs}`;
1353
+ }
1354
+ const authHeaders = buildAuthHeaders(apiConfig.auth);
1355
+ const requestHeaders = {
1356
+ ...authHeaders,
1357
+ ...mappedHeaders
1358
+ };
1359
+ const hasBody = ["POST", "PUT"].includes(apiConfig.method);
1360
+ if (hasBody) {
1361
+ requestHeaders["Content-Type"] = "application/json";
1362
+ }
1363
+ const maxAttempts = (apiConfig.retries ?? 0) + 1;
1364
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
1365
+ if (attempt > 0) {
1366
+ await sleep(100 * Math.pow(2, attempt - 1));
1367
+ }
1368
+ const controller = new AbortController();
1369
+ const timeoutId = setTimeout(
1370
+ () => controller.abort(),
1371
+ apiConfig.timeout_ms ?? 3e4
1372
+ );
1373
+ let response;
1374
+ try {
1375
+ response = await fetch(url, {
1376
+ method: apiConfig.method,
1377
+ headers: requestHeaders,
1378
+ body: hasBody ? JSON.stringify(body) : void 0,
1379
+ signal: controller.signal
1380
+ });
1381
+ } catch (err) {
1382
+ clearTimeout(timeoutId);
1383
+ const message = err instanceof Error ? err.message : String(err);
1384
+ const isAbort = err instanceof Error && err.name === "AbortError" || message.toLowerCase().includes("abort");
1385
+ if (isAbort) {
1386
+ return { success: false, error: `Request timeout after ${apiConfig.timeout_ms}ms` };
1387
+ }
1388
+ return { success: false, error: message };
1389
+ } finally {
1390
+ clearTimeout(timeoutId);
1391
+ }
1392
+ if (!response.ok && RETRYABLE_STATUSES.has(response.status) && attempt < maxAttempts - 1) {
1393
+ continue;
1394
+ }
1395
+ if (!response.ok) {
1396
+ return {
1397
+ success: false,
1398
+ error: `HTTP ${response.status} from ${apiConfig.endpoint}`
1399
+ };
1400
+ }
1401
+ const responseBody = await response.json();
1402
+ const outputMapping = apiConfig.output_mapping;
1403
+ if (Object.keys(outputMapping).length === 0) {
1404
+ return { success: true, result: responseBody };
1405
+ }
1406
+ const mappedOutput = {};
1407
+ for (const [outputKey, path] of Object.entries(outputMapping)) {
1408
+ mappedOutput[outputKey] = extractByPath(responseBody, path);
1409
+ }
1410
+ return { success: true, result: mappedOutput };
1411
+ }
1412
+ return { success: false, error: "Unexpected: retry loop exhausted" };
1413
+ }
1414
+ };
1415
+
1416
+ // src/skills/pipeline-executor.ts
1417
+ import { execFile } from "child_process";
1418
+ import { promisify } from "util";
1419
+
1420
+ // src/utils/interpolation.ts
1421
+ function resolvePath(obj, path) {
1422
+ const segments = path.replace(/\[(\d+)\]/g, ".$1").split(".").filter((s) => s.length > 0);
1423
+ let current = obj;
1424
+ for (const segment of segments) {
1425
+ if (current === null || current === void 0) {
1426
+ return void 0;
1427
+ }
1428
+ if (typeof current !== "object") {
1429
+ return void 0;
1430
+ }
1431
+ current = current[segment];
1432
+ }
1433
+ return current;
1434
+ }
1435
+ function interpolate(template, context) {
1436
+ return template.replace(/\$\{([^}]+)\}/g, (_match, expression) => {
1437
+ const resolved = resolvePath(context, expression.trim());
1438
+ if (resolved === void 0 || resolved === null) {
1439
+ return "";
1440
+ }
1441
+ if (typeof resolved === "object") {
1442
+ return JSON.stringify(resolved);
1443
+ }
1444
+ return String(resolved);
1445
+ });
1446
+ }
1447
+ function interpolateObject(obj, context) {
1448
+ const result = {};
1449
+ for (const [key, value] of Object.entries(obj)) {
1450
+ result[key] = interpolateValue(value, context);
1451
+ }
1452
+ return result;
1453
+ }
1454
+ function interpolateValue(value, context) {
1455
+ if (typeof value === "string") {
1456
+ return interpolate(value, context);
1457
+ }
1458
+ if (Array.isArray(value)) {
1459
+ return value.map((item) => interpolateValue(item, context));
1460
+ }
1461
+ if (value !== null && typeof value === "object") {
1462
+ return interpolateObject(value, context);
1463
+ }
1464
+ return value;
1465
+ }
1466
+
1467
+ // src/skills/pipeline-executor.ts
1468
+ var execFileAsync = promisify(execFile);
1469
+ function shellEscape(value) {
1470
+ return "'" + value.replace(/'/g, "'\\''") + "'";
1471
+ }
1472
+ function safeInterpolateCommand(template, context) {
1473
+ return template.replace(/\$\{([^}]+)\}/g, (_match, expr) => {
1474
+ const parts = expr.split(".");
1475
+ let current = context;
1476
+ for (const part of parts) {
1477
+ if (current === null || typeof current !== "object") return "";
1478
+ const bracketMatch = part.match(/^(\w+)\[(\d+)\]$/);
1479
+ if (bracketMatch) {
1480
+ current = current[bracketMatch[1]];
1481
+ if (Array.isArray(current)) {
1482
+ current = current[parseInt(bracketMatch[2], 10)];
1483
+ } else {
1484
+ return "";
1485
+ }
1486
+ } else {
1487
+ current = current[part];
1488
+ }
1489
+ }
1490
+ if (current === void 0 || current === null) return "";
1491
+ return shellEscape(String(current));
1492
+ });
1493
+ }
1494
+ var PipelineExecutor = class {
1495
+ /**
1496
+ * @param skillExecutor - The parent SkillExecutor used to dispatch sub-skill calls.
1497
+ */
1498
+ constructor(skillExecutor) {
1499
+ this.skillExecutor = skillExecutor;
1500
+ }
1501
+ /**
1502
+ * Execute a pipeline skill config sequentially.
1503
+ *
1504
+ * Algorithm:
1505
+ * 1. Initialise context: { params, steps: [], prev: { result: null } }
1506
+ * 2. For each step:
1507
+ * a. Resolve input_mapping keys against current context via interpolateObject.
1508
+ * b. If step has `skill_id`: dispatch via skillExecutor.execute(). On failure → stop.
1509
+ * c. If step has `command`: interpolate command string, run via exec(). On non-zero exit → stop.
1510
+ * d. Store step result in context.steps[i] and context.prev.
1511
+ * 3. Return success with final step result (or null for empty pipeline).
1512
+ *
1513
+ * @param config - The PipelineSkillConfig for this skill.
1514
+ * @param params - Input parameters from the caller.
1515
+ * @returns Partial ExecutionResult (without latency_ms — added by SkillExecutor wrapper).
1516
+ */
1517
+ async execute(config, params) {
1518
+ const pipelineConfig = config;
1519
+ const steps = pipelineConfig.steps ?? [];
1520
+ if (steps.length === 0) {
1521
+ return { success: true, result: null };
1522
+ }
1523
+ const context = {
1524
+ params,
1525
+ steps: [],
1526
+ prev: { result: null }
1527
+ };
1528
+ for (let i = 0; i < steps.length; i++) {
1529
+ const step = steps[i];
1530
+ if (step === void 0) {
1531
+ return {
1532
+ success: false,
1533
+ error: `Step ${i} failed: step definition is undefined`
1534
+ };
1535
+ }
1536
+ const resolvedInputs = interpolateObject(
1537
+ step.input_mapping,
1538
+ context
1539
+ );
1540
+ let stepResult;
1541
+ if ("skill_id" in step && step.skill_id) {
1542
+ const subResult = await this.skillExecutor.execute(
1543
+ step.skill_id,
1544
+ resolvedInputs
1545
+ );
1546
+ if (!subResult.success) {
1547
+ return {
1548
+ success: false,
1549
+ error: `Step ${i} failed: ${subResult.error ?? "unknown error"}`
1550
+ };
1551
+ }
1552
+ stepResult = subResult.result;
1553
+ } else if ("command" in step && step.command) {
1554
+ const interpolatedCommand = safeInterpolateCommand(
1555
+ step.command,
1556
+ context
1557
+ );
1558
+ try {
1559
+ const { stdout } = await execFileAsync("/bin/sh", ["-c", interpolatedCommand], { timeout: 3e4 });
1560
+ stepResult = stdout.trim();
1561
+ } catch (err) {
1562
+ const message = err instanceof Error ? err.message : String(err);
1563
+ return {
1564
+ success: false,
1565
+ error: `Step ${i} failed: ${message}`
1566
+ };
1567
+ }
1568
+ } else {
1569
+ return {
1570
+ success: false,
1571
+ error: `Step ${i} failed: step must have either "skill_id" or "command"`
1572
+ };
1573
+ }
1574
+ context.steps.push({ result: stepResult });
1575
+ context.prev = { result: stepResult };
1576
+ }
1577
+ const lastStep = context.steps[context.steps.length - 1];
1578
+ return {
1579
+ success: true,
1580
+ result: lastStep !== void 0 ? lastStep.result : null
1581
+ };
1582
+ }
1583
+ };
1584
+
1585
+ // src/skills/openclaw-bridge.ts
1586
+ import { execFileSync } from "child_process";
1587
+ var DEFAULT_BASE_URL = "http://localhost:3000";
1588
+ var DEFAULT_TIMEOUT_MS = 6e4;
1589
+ function buildPayload(config, params) {
1590
+ return {
1591
+ task: config.name,
1592
+ params,
1593
+ source: "agentbnb",
1594
+ skill_id: config.id
1595
+ };
1596
+ }
1597
+ async function executeWebhook(config, payload) {
1598
+ const baseUrl = process.env["OPENCLAW_BASE_URL"] ?? DEFAULT_BASE_URL;
1599
+ const url = `${baseUrl}/openclaw/${config.agent_name}/task`;
1600
+ const timeoutMs = config.timeout_ms ?? DEFAULT_TIMEOUT_MS;
1601
+ const controller = new AbortController();
1602
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
1603
+ try {
1604
+ const response = await fetch(url, {
1605
+ method: "POST",
1606
+ headers: { "Content-Type": "application/json" },
1607
+ body: JSON.stringify(payload),
1608
+ signal: controller.signal
1609
+ });
1610
+ if (!response.ok) {
1611
+ return {
1612
+ success: false,
1613
+ error: `Webhook returned HTTP ${response.status}: ${response.statusText}`
1614
+ };
1615
+ }
1616
+ const result = await response.json();
1617
+ return { success: true, result };
1618
+ } catch (err) {
1619
+ if (err instanceof Error && err.name === "AbortError") {
1620
+ return {
1621
+ success: false,
1622
+ error: `OpenClaw webhook timed out after ${timeoutMs}ms`
1623
+ };
1624
+ }
1625
+ const message = err instanceof Error ? err.message : String(err);
1626
+ return { success: false, error: message };
1627
+ } finally {
1628
+ clearTimeout(timer);
1629
+ }
1630
+ }
1631
+ function validateAgentName(name) {
1632
+ return /^[a-zA-Z0-9._-]+$/.test(name);
1633
+ }
1634
+ function executeProcess(config, payload) {
1635
+ const timeoutMs = config.timeout_ms ?? DEFAULT_TIMEOUT_MS;
1636
+ if (!validateAgentName(config.agent_name)) {
1637
+ return {
1638
+ success: false,
1639
+ error: `Invalid agent name: "${config.agent_name}" (only alphanumeric, hyphens, underscores, dots allowed)`
1640
+ };
1641
+ }
1642
+ const inputJson = JSON.stringify(payload);
1643
+ try {
1644
+ const stdout = execFileSync("openclaw", ["run", config.agent_name, "--input", inputJson], {
1645
+ timeout: timeoutMs
1646
+ });
1647
+ const text = stdout.toString().trim();
1648
+ const result = JSON.parse(text);
1649
+ return { success: true, result };
1650
+ } catch (err) {
1651
+ const message = err instanceof Error ? err.message : String(err);
1652
+ return { success: false, error: message };
1653
+ }
1654
+ }
1655
+ async function executeTelegram(config, payload) {
1656
+ const token = process.env["TELEGRAM_BOT_TOKEN"];
1657
+ if (!token) {
1658
+ return {
1659
+ success: false,
1660
+ error: "TELEGRAM_BOT_TOKEN environment variable is not set"
1661
+ };
1662
+ }
1663
+ const chatId = process.env["TELEGRAM_CHAT_ID"];
1664
+ if (!chatId) {
1665
+ return {
1666
+ success: false,
1667
+ error: "TELEGRAM_CHAT_ID environment variable is not set"
1668
+ };
1669
+ }
1670
+ const text = `[AgentBnB] Skill: ${config.name} (${config.id})
1671
+ Agent: ${config.agent_name}
1672
+ Params: ${JSON.stringify(payload.params ?? {})}`;
1673
+ const url = `https://api.telegram.org/bot${token}/sendMessage`;
1674
+ try {
1675
+ await fetch(url, {
1676
+ method: "POST",
1677
+ headers: { "Content-Type": "application/json" },
1678
+ body: JSON.stringify({ chat_id: chatId, text })
1679
+ });
1680
+ return {
1681
+ success: true,
1682
+ result: { sent: true, channel: "telegram" }
1683
+ };
1684
+ } catch (err) {
1685
+ const message = err instanceof Error ? err.message : String(err);
1686
+ return { success: false, error: message };
1687
+ }
1688
+ }
1689
+ var OpenClawBridge = class {
1690
+ /**
1691
+ * Execute a skill with the given config and input parameters.
1692
+ *
1693
+ * @param config - The SkillConfig for this skill (must be type 'openclaw').
1694
+ * @param params - Input parameters passed by the caller.
1695
+ * @returns Partial ExecutionResult without latency_ms.
1696
+ */
1697
+ async execute(config, params) {
1698
+ const ocConfig = config;
1699
+ const payload = buildPayload(ocConfig, params);
1700
+ switch (ocConfig.channel) {
1701
+ case "webhook":
1702
+ return executeWebhook(ocConfig, payload);
1703
+ case "process":
1704
+ return executeProcess(ocConfig, payload);
1705
+ case "telegram":
1706
+ return executeTelegram(ocConfig, payload);
1707
+ default: {
1708
+ const unknownChannel = ocConfig.channel;
1709
+ return {
1710
+ success: false,
1711
+ error: `Unknown OpenClaw channel: "${String(unknownChannel)}"`
1712
+ };
1713
+ }
1714
+ }
1715
+ }
1716
+ };
1717
+
1718
+ // src/skills/command-executor.ts
1719
+ import { execFile as execFile2 } from "child_process";
1720
+ function shellEscape2(value) {
1721
+ return "'" + value.replace(/'/g, "'\\''") + "'";
1722
+ }
1723
+ function safeInterpolateCommand2(template, context) {
1724
+ return template.replace(/\$\{([^}]+)\}/g, (_match, expr) => {
1725
+ const parts = expr.split(".");
1726
+ let current = context;
1727
+ for (const part of parts) {
1728
+ if (current === null || typeof current !== "object") return "";
1729
+ const bracketMatch = part.match(/^(\w+)\[(\d+)\]$/);
1730
+ if (bracketMatch) {
1731
+ current = current[bracketMatch[1]];
1732
+ if (Array.isArray(current)) {
1733
+ current = current[parseInt(bracketMatch[2], 10)];
1734
+ } else {
1735
+ return "";
1736
+ }
1737
+ } else {
1738
+ current = current[part];
1739
+ }
1740
+ }
1741
+ if (current === void 0 || current === null) return "";
1742
+ return shellEscape2(String(current));
1743
+ });
1744
+ }
1745
+ function execFileAsync2(file, args, options) {
1746
+ return new Promise((resolve, reject) => {
1747
+ execFile2(file, args, options, (error, stdout, stderr) => {
1748
+ const stdoutStr = typeof stdout === "string" ? stdout : stdout.toString();
1749
+ const stderrStr = typeof stderr === "string" ? stderr : stderr.toString();
1750
+ if (error) {
1751
+ const enriched = Object.assign(error, { stderr: stderrStr });
1752
+ reject(enriched);
1753
+ } else {
1754
+ resolve({ stdout: stdoutStr, stderr: stderrStr });
1755
+ }
1756
+ });
1757
+ });
1758
+ }
1759
+ var CommandExecutor = class {
1760
+ /**
1761
+ * Execute a command skill with the provided parameters.
1762
+ *
1763
+ * Steps:
1764
+ * 1. Security check: base command must be in `allowed_commands` if set.
1765
+ * 2. Interpolate `config.command` using `{ params }` context.
1766
+ * 3. Run via `child_process.exec` with timeout and cwd.
1767
+ * 4. Parse stdout based on `output_type`: text | json | file.
1768
+ *
1769
+ * @param config - Validated CommandSkillConfig.
1770
+ * @param params - Input parameters passed by the caller.
1771
+ * @returns Partial ExecutionResult (without latency_ms).
1772
+ */
1773
+ async execute(config, params) {
1774
+ const cmdConfig = config;
1775
+ const baseCommand = cmdConfig.command.trim().split(/\s+/)[0] ?? "";
1776
+ if (cmdConfig.allowed_commands && cmdConfig.allowed_commands.length > 0) {
1777
+ if (!cmdConfig.allowed_commands.includes(baseCommand)) {
1778
+ return {
1779
+ success: false,
1780
+ error: `Command not allowed: "${baseCommand}". Allowed: ${cmdConfig.allowed_commands.join(", ")}`
1781
+ };
1782
+ }
1783
+ }
1784
+ const interpolatedCommand = safeInterpolateCommand2(cmdConfig.command, { params });
1785
+ const timeout = cmdConfig.timeout_ms ?? 3e4;
1786
+ const cwd = cmdConfig.working_dir ?? process.cwd();
1787
+ let stdout;
1788
+ try {
1789
+ const result = await execFileAsync2("/bin/sh", ["-c", interpolatedCommand], {
1790
+ timeout,
1791
+ cwd,
1792
+ maxBuffer: 10 * 1024 * 1024
1793
+ // 10 MB
1794
+ });
1795
+ stdout = result.stdout;
1796
+ } catch (err) {
1797
+ if (err instanceof Error) {
1798
+ const message = err.message;
1799
+ const stderrContent = err.stderr ?? "";
1800
+ if (message.includes("timed out") || message.includes("ETIMEDOUT") || err.code === "ETIMEDOUT") {
1801
+ return {
1802
+ success: false,
1803
+ error: `Command timed out after ${timeout}ms`
1804
+ };
1805
+ }
1806
+ return {
1807
+ success: false,
1808
+ error: stderrContent.trim() || message
1809
+ };
1810
+ }
1811
+ return {
1812
+ success: false,
1813
+ error: String(err)
1814
+ };
1815
+ }
1816
+ const rawOutput = stdout.trim();
1817
+ switch (cmdConfig.output_type) {
1818
+ case "text":
1819
+ return { success: true, result: rawOutput };
1820
+ case "json": {
1821
+ try {
1822
+ const parsed = JSON.parse(rawOutput);
1823
+ return { success: true, result: parsed };
1824
+ } catch {
1825
+ return {
1826
+ success: false,
1827
+ error: `Failed to parse JSON output: ${rawOutput.slice(0, 100)}`
1828
+ };
1829
+ }
1830
+ }
1831
+ case "file":
1832
+ return { success: true, result: { file_path: rawOutput } };
1833
+ default:
1834
+ return {
1835
+ success: false,
1836
+ error: `Unknown output_type: ${String(cmdConfig.output_type)}`
1837
+ };
1838
+ }
1839
+ }
1840
+ };
1841
+
1842
+ // src/conductor/task-decomposer.ts
1843
+ import { randomUUID as randomUUID4 } from "crypto";
1844
+ var TEMPLATES = {
1845
+ "video-production": {
1846
+ keywords: ["video", "demo", "clip", "animation"],
1847
+ steps: [
1848
+ {
1849
+ description: "Generate script from task description",
1850
+ required_capability: "text_gen",
1851
+ estimated_credits: 2,
1852
+ depends_on_indices: []
1853
+ },
1854
+ {
1855
+ description: "Generate voiceover from script",
1856
+ required_capability: "tts",
1857
+ estimated_credits: 3,
1858
+ depends_on_indices: [0]
1859
+ },
1860
+ {
1861
+ description: "Generate video visuals from script",
1862
+ required_capability: "video_gen",
1863
+ estimated_credits: 5,
1864
+ depends_on_indices: [0]
1865
+ },
1866
+ {
1867
+ description: "Composite voiceover and video into final output",
1868
+ required_capability: "video_edit",
1869
+ estimated_credits: 3,
1870
+ depends_on_indices: [1, 2]
1871
+ }
1872
+ ]
1873
+ },
1874
+ "deep-analysis": {
1875
+ keywords: ["analyze", "analysis", "research", "report", "evaluate"],
1876
+ steps: [
1877
+ {
1878
+ description: "Research and gather relevant data",
1879
+ required_capability: "web_search",
1880
+ estimated_credits: 2,
1881
+ depends_on_indices: []
1882
+ },
1883
+ {
1884
+ description: "Analyze gathered data",
1885
+ required_capability: "text_gen",
1886
+ estimated_credits: 3,
1887
+ depends_on_indices: [0]
1888
+ },
1889
+ {
1890
+ description: "Summarize analysis findings",
1891
+ required_capability: "text_gen",
1892
+ estimated_credits: 2,
1893
+ depends_on_indices: [1]
1894
+ },
1895
+ {
1896
+ description: "Format into final report",
1897
+ required_capability: "text_gen",
1898
+ estimated_credits: 1,
1899
+ depends_on_indices: [2]
1900
+ }
1901
+ ]
1902
+ },
1903
+ "content-generation": {
1904
+ keywords: ["write", "blog", "article", "content", "post", "essay"],
1905
+ steps: [
1906
+ {
1907
+ description: "Create content outline",
1908
+ required_capability: "text_gen",
1909
+ estimated_credits: 1,
1910
+ depends_on_indices: []
1911
+ },
1912
+ {
1913
+ description: "Draft content from outline",
1914
+ required_capability: "text_gen",
1915
+ estimated_credits: 3,
1916
+ depends_on_indices: [0]
1917
+ },
1918
+ {
1919
+ description: "Review and refine draft",
1920
+ required_capability: "text_gen",
1921
+ estimated_credits: 2,
1922
+ depends_on_indices: [1]
1923
+ },
1924
+ {
1925
+ description: "Finalize and polish content",
1926
+ required_capability: "text_gen",
1927
+ estimated_credits: 1,
1928
+ depends_on_indices: [2]
1929
+ }
1930
+ ]
1931
+ }
1932
+ };
1933
+ function decompose(task, _availableCapabilities) {
1934
+ const lower = task.toLowerCase();
1935
+ for (const template of Object.values(TEMPLATES)) {
1936
+ const matched = template.keywords.some((kw) => lower.includes(kw));
1937
+ if (!matched) continue;
1938
+ const ids = template.steps.map(() => randomUUID4());
1939
+ return template.steps.map((step, i) => ({
1940
+ id: ids[i],
1941
+ description: step.description,
1942
+ required_capability: step.required_capability,
1943
+ params: {},
1944
+ depends_on: step.depends_on_indices.map((idx) => ids[idx]),
1945
+ estimated_credits: step.estimated_credits
1946
+ }));
1947
+ }
1948
+ return [];
1949
+ }
1950
+
1951
+ // src/gateway/client.ts
1952
+ import { randomUUID as randomUUID5 } from "crypto";
1953
+ async function requestCapability(opts) {
1954
+ const { gatewayUrl, token, cardId, params = {}, timeoutMs = 3e4, escrowReceipt } = opts;
1955
+ const id = randomUUID5();
1956
+ const payload = {
1957
+ jsonrpc: "2.0",
1958
+ id,
1959
+ method: "capability.execute",
1960
+ params: {
1961
+ card_id: cardId,
1962
+ ...params,
1963
+ ...escrowReceipt ? { escrow_receipt: escrowReceipt } : {}
1964
+ }
1965
+ };
1966
+ const controller = new AbortController();
1967
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
1968
+ let response;
1969
+ try {
1970
+ response = await fetch(`${gatewayUrl}/rpc`, {
1971
+ method: "POST",
1972
+ headers: {
1973
+ "Content-Type": "application/json",
1974
+ Authorization: `Bearer ${token}`
1975
+ },
1976
+ body: JSON.stringify(payload),
1977
+ signal: controller.signal
1978
+ });
1979
+ } catch (err) {
1980
+ clearTimeout(timer);
1981
+ const isTimeout = err instanceof Error && err.name === "AbortError";
1982
+ throw new AgentBnBError(
1983
+ isTimeout ? "Request timed out" : `Network error: ${String(err)}`,
1984
+ isTimeout ? "TIMEOUT" : "NETWORK_ERROR"
1985
+ );
1986
+ } finally {
1987
+ clearTimeout(timer);
1988
+ }
1989
+ const body = await response.json();
1990
+ if (body.error) {
1991
+ throw new AgentBnBError(body.error.message, `RPC_ERROR_${body.error.code}`);
1992
+ }
1993
+ return body.result;
1994
+ }
1995
+
1996
+ // src/autonomy/tiers.ts
1997
+ import { randomUUID as randomUUID6 } from "crypto";
1998
+
1999
+ // src/autonomy/pending-requests.ts
2000
+ import { randomUUID as randomUUID7 } from "crypto";
2001
+
2002
+ // src/cli/peers.ts
2003
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
2004
+ import { join as join3 } from "path";
2005
+
2006
+ // src/cli/config.ts
2007
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync2 } from "fs";
2008
+ import { homedir } from "os";
2009
+ import { join as join2 } from "path";
2010
+
2011
+ // src/autonomy/auto-request.ts
2012
+ function minMaxNormalize(values) {
2013
+ if (values.length === 0) return [];
2014
+ if (values.length === 1) return [1];
2015
+ const min = Math.min(...values);
2016
+ const max = Math.max(...values);
2017
+ if (max === min) {
2018
+ return values.map(() => 1);
2019
+ }
2020
+ return values.map((v) => (v - min) / (max - min));
2021
+ }
2022
+ function scorePeers(candidates, selfOwner) {
2023
+ const eligible = candidates.filter((c) => c.card.owner !== selfOwner);
2024
+ if (eligible.length === 0) return [];
2025
+ const successRates = eligible.map((c) => c.card.metadata?.success_rate ?? 0.5);
2026
+ const costEfficiencies = eligible.map((c) => c.cost === 0 ? 1 : 1 / c.cost);
2027
+ const idleRates = eligible.map((c) => {
2028
+ const internal = c.card._internal;
2029
+ const idleRate = internal?.idle_rate;
2030
+ return typeof idleRate === "number" ? idleRate : 1;
2031
+ });
2032
+ const normSuccess = minMaxNormalize(successRates);
2033
+ const normCost = minMaxNormalize(costEfficiencies);
2034
+ const normIdle = minMaxNormalize(idleRates);
2035
+ const scored = eligible.map((c, i) => ({
2036
+ ...c,
2037
+ rawScore: (normSuccess[i] ?? 0) * (normCost[i] ?? 0) * (normIdle[i] ?? 0)
2038
+ }));
2039
+ scored.sort((a, b) => b.rawScore - a.rawScore);
2040
+ return scored;
2041
+ }
2042
+
2043
+ // src/conductor/capability-matcher.ts
2044
+ var MAX_ALTERNATIVES = 2;
2045
+ function matchSubTasks(opts) {
2046
+ const { db, subtasks, conductorOwner } = opts;
2047
+ return subtasks.map((subtask) => {
2048
+ const cards = searchCards(db, subtask.required_capability, { online: true });
2049
+ const candidates = [];
2050
+ for (const card of cards) {
2051
+ const cardAsV2 = card;
2052
+ if (Array.isArray(cardAsV2.skills)) {
2053
+ for (const skill of cardAsV2.skills) {
2054
+ candidates.push({
2055
+ card,
2056
+ cost: skill.pricing.credits_per_call,
2057
+ skillId: skill.id
2058
+ });
2059
+ }
2060
+ } else {
2061
+ candidates.push({
2062
+ card,
2063
+ cost: card.pricing.credits_per_call,
2064
+ skillId: void 0
2065
+ });
2066
+ }
2067
+ }
2068
+ const scored = scorePeers(candidates, conductorOwner);
2069
+ if (scored.length === 0) {
2070
+ return {
2071
+ subtask_id: subtask.id,
2072
+ selected_agent: "",
2073
+ selected_skill: "",
2074
+ score: 0,
2075
+ credits: 0,
2076
+ alternatives: []
2077
+ };
2078
+ }
2079
+ const top = scored[0];
2080
+ const alternatives = scored.slice(1, 1 + MAX_ALTERNATIVES).map((s) => ({
2081
+ agent: s.card.owner,
2082
+ skill: s.skillId ?? "",
2083
+ score: s.rawScore,
2084
+ credits: s.cost
2085
+ }));
2086
+ return {
2087
+ subtask_id: subtask.id,
2088
+ selected_agent: top.card.owner,
2089
+ selected_skill: top.skillId ?? "",
2090
+ score: top.rawScore,
2091
+ credits: top.cost,
2092
+ alternatives
2093
+ };
2094
+ });
2095
+ }
2096
+
2097
+ // src/conductor/budget-controller.ts
2098
+ var ORCHESTRATION_FEE = 5;
2099
+ var BudgetController = class {
2100
+ /**
2101
+ * Creates a new BudgetController.
2102
+ *
2103
+ * @param budgetManager - Underlying BudgetManager for reserve floor enforcement.
2104
+ * @param maxBudget - Hard ceiling for the orchestration run.
2105
+ */
2106
+ constructor(budgetManager, maxBudget) {
2107
+ this.budgetManager = budgetManager;
2108
+ this.maxBudget = maxBudget;
2109
+ }
2110
+ /**
2111
+ * Pre-calculates the total budget for an orchestration run.
2112
+ *
2113
+ * Sums all matched sub-task credits, adds the orchestration fee,
2114
+ * and determines whether approval is required (estimated > max).
2115
+ *
2116
+ * @param matches - MatchResult[] from the CapabilityMatcher.
2117
+ * @returns An ExecutionBudget with cost breakdown and approval status.
2118
+ */
2119
+ calculateBudget(matches) {
2120
+ const perTaskSpending = /* @__PURE__ */ new Map();
2121
+ let subTotal = 0;
2122
+ for (const match of matches) {
2123
+ perTaskSpending.set(match.subtask_id, match.credits);
2124
+ subTotal += match.credits;
2125
+ }
2126
+ const estimatedTotal = subTotal + ORCHESTRATION_FEE;
2127
+ return {
2128
+ estimated_total: estimatedTotal,
2129
+ max_budget: this.maxBudget,
2130
+ orchestration_fee: ORCHESTRATION_FEE,
2131
+ per_task_spending: perTaskSpending,
2132
+ requires_approval: estimatedTotal > this.maxBudget
2133
+ };
2134
+ }
2135
+ /**
2136
+ * Checks whether orchestration can proceed without explicit approval.
2137
+ *
2138
+ * Returns true only when:
2139
+ * 1. The budget does NOT require approval (estimated_total <= max_budget)
2140
+ * 2. The BudgetManager confirms sufficient credits (respecting reserve floor)
2141
+ *
2142
+ * @param budget - ExecutionBudget from calculateBudget().
2143
+ * @returns true if execution can proceed autonomously.
2144
+ */
2145
+ canExecute(budget) {
2146
+ if (budget.requires_approval) return false;
2147
+ return this.budgetManager.canSpend(budget.estimated_total);
2148
+ }
2149
+ /**
2150
+ * Checks budget after explicit user/agent approval.
2151
+ *
2152
+ * Ignores the requires_approval flag — used when the caller has already
2153
+ * obtained explicit approval for the over-budget orchestration.
2154
+ * Still enforces the reserve floor via BudgetManager.canSpend().
2155
+ *
2156
+ * @param budget - ExecutionBudget from calculateBudget().
2157
+ * @returns true if the agent has sufficient credits (reserve floor check only).
2158
+ */
2159
+ approveAndCheck(budget) {
2160
+ return this.budgetManager.canSpend(budget.estimated_total);
2161
+ }
2162
+ };
2163
+
2164
+ // src/conductor/card.ts
2165
+ var CONDUCTOR_OWNER = "agentbnb-conductor";
2166
+ var CONDUCTOR_CARD_ID = "00000000-0000-4000-8000-000000000001";
2167
+ function buildConductorCard() {
2168
+ const card = {
2169
+ spec_version: "2.0",
2170
+ id: CONDUCTOR_CARD_ID,
2171
+ owner: CONDUCTOR_OWNER,
2172
+ agent_name: "AgentBnB Conductor",
2173
+ skills: [
2174
+ {
2175
+ id: "orchestrate",
2176
+ name: "Task Orchestration",
2177
+ description: "Decomposes complex tasks and coordinates multi-agent execution",
2178
+ level: 3,
2179
+ inputs: [
2180
+ {
2181
+ name: "task",
2182
+ type: "text",
2183
+ description: "Natural language task description"
2184
+ }
2185
+ ],
2186
+ outputs: [
2187
+ {
2188
+ name: "result",
2189
+ type: "json",
2190
+ description: "Aggregated execution results"
2191
+ }
2192
+ ],
2193
+ pricing: { credits_per_call: 5 }
2194
+ },
2195
+ {
2196
+ id: "plan",
2197
+ name: "Execution Planning",
2198
+ description: "Returns an execution plan with cost estimate without executing",
2199
+ level: 1,
2200
+ inputs: [
2201
+ {
2202
+ name: "task",
2203
+ type: "text",
2204
+ description: "Natural language task description"
2205
+ }
2206
+ ],
2207
+ outputs: [
2208
+ {
2209
+ name: "plan",
2210
+ type: "json",
2211
+ description: "Execution plan with cost breakdown"
2212
+ }
2213
+ ],
2214
+ pricing: { credits_per_call: 1 }
2215
+ }
2216
+ ],
2217
+ availability: { online: true }
2218
+ };
2219
+ return CapabilityCardV2Schema.parse(card);
2220
+ }
2221
+ function registerConductorCard(db) {
2222
+ const card = buildConductorCard();
2223
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2224
+ const existing = db.prepare("SELECT id FROM capability_cards WHERE id = ?").get(card.id);
2225
+ if (existing) {
2226
+ db.prepare(
2227
+ "UPDATE capability_cards SET data = ?, updated_at = ? WHERE id = ?"
2228
+ ).run(JSON.stringify(card), now, card.id);
2229
+ } else {
2230
+ db.prepare(
2231
+ "INSERT INTO capability_cards (id, owner, data, created_at, updated_at) VALUES (?, ?, ?, ?, ?)"
2232
+ ).run(card.id, card.owner, JSON.stringify(card), now, now);
2233
+ }
2234
+ return card;
2235
+ }
2236
+
2237
+ // src/conductor/pipeline-orchestrator.ts
2238
+ function computeWaves(subtasks) {
2239
+ const waves = [];
2240
+ const completed = /* @__PURE__ */ new Set();
2241
+ const remaining = new Map(subtasks.map((s) => [s.id, s]));
2242
+ while (remaining.size > 0) {
2243
+ const wave = [];
2244
+ for (const [id, task] of remaining) {
2245
+ const depsResolved = task.depends_on.every((dep) => completed.has(dep));
2246
+ if (depsResolved) {
2247
+ wave.push(id);
2248
+ }
2249
+ }
2250
+ if (wave.length === 0) {
2251
+ break;
2252
+ }
2253
+ for (const id of wave) {
2254
+ remaining.delete(id);
2255
+ completed.add(id);
2256
+ }
2257
+ waves.push(wave);
2258
+ }
2259
+ return waves;
2260
+ }
2261
+ async function orchestrate(opts) {
2262
+ const { subtasks, matches, gatewayToken, resolveAgentUrl, timeoutMs = 3e4, maxBudget } = opts;
2263
+ const startTime = Date.now();
2264
+ if (subtasks.length === 0) {
2265
+ return {
2266
+ success: true,
2267
+ results: /* @__PURE__ */ new Map(),
2268
+ total_credits: 0,
2269
+ latency_ms: Date.now() - startTime
2270
+ };
2271
+ }
2272
+ const results = /* @__PURE__ */ new Map();
2273
+ const errors = [];
2274
+ let totalCredits = 0;
2275
+ const waves = computeWaves(subtasks);
2276
+ const subtaskMap = new Map(subtasks.map((s) => [s.id, s]));
2277
+ for (const wave of waves) {
2278
+ if (maxBudget !== void 0 && totalCredits >= maxBudget) {
2279
+ errors.push(`Budget exceeded: spent ${totalCredits} cr, max ${maxBudget} cr`);
2280
+ break;
2281
+ }
2282
+ const executableIds = [];
2283
+ for (const taskId of wave) {
2284
+ const m = matches.get(taskId);
2285
+ if (maxBudget !== void 0 && m && totalCredits + m.credits > maxBudget) {
2286
+ errors.push(`Skipping task ${taskId}: would exceed budget (${totalCredits} + ${m.credits} > ${maxBudget})`);
2287
+ continue;
2288
+ }
2289
+ executableIds.push(taskId);
2290
+ }
2291
+ const waveResults = await Promise.allSettled(
2292
+ executableIds.map(async (taskId) => {
2293
+ const subtask = subtaskMap.get(taskId);
2294
+ const m = matches.get(taskId);
2295
+ if (!m) {
2296
+ throw new Error(`No match found for subtask ${taskId}`);
2297
+ }
2298
+ const stepsContext = {};
2299
+ for (const [id, val] of results) {
2300
+ stepsContext[id] = val;
2301
+ }
2302
+ const interpContext = { steps: stepsContext, prev: void 0 };
2303
+ if (subtask.depends_on.length > 0) {
2304
+ const lastDep = subtask.depends_on[subtask.depends_on.length - 1];
2305
+ interpContext.prev = results.get(lastDep);
2306
+ }
2307
+ const interpolatedParams = interpolateObject(
2308
+ subtask.params,
2309
+ interpContext
2310
+ );
2311
+ const primary = resolveAgentUrl(m.selected_agent);
2312
+ try {
2313
+ const res = await requestCapability({
2314
+ gatewayUrl: primary.url,
2315
+ token: gatewayToken,
2316
+ cardId: primary.cardId,
2317
+ params: interpolatedParams,
2318
+ timeoutMs
2319
+ });
2320
+ return { taskId, result: res, credits: m.credits };
2321
+ } catch (primaryErr) {
2322
+ if (m.alternatives.length > 0) {
2323
+ const alt = m.alternatives[0];
2324
+ const altAgent = resolveAgentUrl(alt.agent);
2325
+ try {
2326
+ const altRes = await requestCapability({
2327
+ gatewayUrl: altAgent.url,
2328
+ token: gatewayToken,
2329
+ cardId: altAgent.cardId,
2330
+ params: interpolatedParams,
2331
+ timeoutMs
2332
+ });
2333
+ return { taskId, result: altRes, credits: alt.credits };
2334
+ } catch (altErr) {
2335
+ throw new Error(
2336
+ `Task ${taskId}: primary (${m.selected_agent}) failed: ${primaryErr instanceof Error ? primaryErr.message : String(primaryErr)}; alternative (${alt.agent}) failed: ${altErr instanceof Error ? altErr.message : String(altErr)}`
2337
+ );
2338
+ }
2339
+ }
2340
+ throw new Error(
2341
+ `Task ${taskId}: ${primaryErr instanceof Error ? primaryErr.message : String(primaryErr)}`
2342
+ );
2343
+ }
2344
+ })
2345
+ );
2346
+ for (const settlement of waveResults) {
2347
+ if (settlement.status === "fulfilled") {
2348
+ const { taskId, result, credits } = settlement.value;
2349
+ results.set(taskId, result);
2350
+ totalCredits += credits;
2351
+ } else {
2352
+ errors.push(settlement.reason instanceof Error ? settlement.reason.message : String(settlement.reason));
2353
+ }
2354
+ }
2355
+ }
2356
+ return {
2357
+ success: errors.length === 0,
2358
+ results,
2359
+ total_credits: totalCredits,
2360
+ latency_ms: Date.now() - startTime,
2361
+ errors: errors.length > 0 ? errors : void 0
2362
+ };
2363
+ }
2364
+
2365
+ // src/credit/budget.ts
2366
+ var DEFAULT_BUDGET_CONFIG = {
2367
+ reserve_credits: 20
2368
+ };
2369
+ var BudgetManager = class {
2370
+ /**
2371
+ * Creates a new BudgetManager.
2372
+ *
2373
+ * @param creditDb - The credit SQLite database instance.
2374
+ * @param owner - Agent owner identifier.
2375
+ * @param config - Budget configuration. Defaults to DEFAULT_BUDGET_CONFIG (20 credit reserve).
2376
+ */
2377
+ constructor(creditDb, owner, config = DEFAULT_BUDGET_CONFIG) {
2378
+ this.creditDb = creditDb;
2379
+ this.owner = owner;
2380
+ this.config = config;
2381
+ }
2382
+ /**
2383
+ * Returns the number of credits available for spending.
2384
+ * Computed as: max(0, balance - reserve_credits).
2385
+ * Always returns a non-negative number — never goes below zero.
2386
+ *
2387
+ * @returns Available credits (balance minus reserve, floored at 0).
2388
+ */
2389
+ availableCredits() {
2390
+ const balance = getBalance(this.creditDb, this.owner);
2391
+ return Math.max(0, balance - this.config.reserve_credits);
2392
+ }
2393
+ /**
2394
+ * Returns true if spending `amount` credits is permitted by budget rules.
2395
+ *
2396
+ * Rules:
2397
+ * - Zero-cost calls (amount <= 0) always return true.
2398
+ * - Any positive amount requires availableCredits() >= amount.
2399
+ * - If balance is at or below the reserve floor, all positive-cost calls return false.
2400
+ *
2401
+ * @param amount - Number of credits to spend.
2402
+ * @returns true if the spend is allowed, false if it would breach the reserve floor.
2403
+ */
2404
+ canSpend(amount) {
2405
+ if (amount <= 0) return true;
2406
+ return this.availableCredits() >= amount;
2407
+ }
2408
+ };
2409
+
2410
+ // src/conductor/conductor-mode.ts
2411
+ var ConductorMode = class {
2412
+ db;
2413
+ creditDb;
2414
+ conductorOwner;
2415
+ gatewayToken;
2416
+ resolveAgentUrl;
2417
+ maxBudget;
2418
+ constructor(opts) {
2419
+ this.db = opts.db;
2420
+ this.creditDb = opts.creditDb;
2421
+ this.conductorOwner = opts.conductorOwner;
2422
+ this.gatewayToken = opts.gatewayToken;
2423
+ this.resolveAgentUrl = opts.resolveAgentUrl;
2424
+ this.maxBudget = opts.maxBudget ?? 100;
2425
+ }
2426
+ /**
2427
+ * Execute a conductor skill with the given config and params.
2428
+ *
2429
+ * @param config - SkillConfig with type 'conductor' and conductor_skill field.
2430
+ * @param params - Must include `task` string.
2431
+ * @returns Execution result without latency_ms (added by SkillExecutor).
2432
+ */
2433
+ async execute(config, params) {
2434
+ const conductorSkill = config.conductor_skill;
2435
+ if (conductorSkill !== "orchestrate" && conductorSkill !== "plan") {
2436
+ return {
2437
+ success: false,
2438
+ error: `Unknown conductor skill: "${conductorSkill}"`
2439
+ };
2440
+ }
2441
+ const task = params.task;
2442
+ if (typeof task !== "string" || task.length === 0) {
2443
+ return {
2444
+ success: false,
2445
+ error: 'Missing or empty "task" parameter'
2446
+ };
2447
+ }
2448
+ const subtasks = decompose(task);
2449
+ if (subtasks.length === 0) {
2450
+ return {
2451
+ success: false,
2452
+ error: "No template matches task"
2453
+ };
2454
+ }
2455
+ const matchResults = matchSubTasks({
2456
+ db: this.db,
2457
+ subtasks,
2458
+ conductorOwner: this.conductorOwner
2459
+ });
2460
+ const budgetManager = new BudgetManager(this.creditDb, this.conductorOwner);
2461
+ const budgetController = new BudgetController(budgetManager, this.maxBudget);
2462
+ const executionBudget = budgetController.calculateBudget(matchResults);
2463
+ if (!budgetController.canExecute(executionBudget)) {
2464
+ return {
2465
+ success: false,
2466
+ error: `Budget exceeded: estimated ${executionBudget.estimated_total} cr, max ${this.maxBudget} cr`
2467
+ };
2468
+ }
2469
+ if (conductorSkill === "plan") {
2470
+ return {
2471
+ success: true,
2472
+ result: {
2473
+ subtasks,
2474
+ matches: matchResults,
2475
+ budget: executionBudget
2476
+ }
2477
+ };
2478
+ }
2479
+ const matchMap = new Map(
2480
+ matchResults.map((m) => [m.subtask_id, m])
2481
+ );
2482
+ const orchResult = await orchestrate({
2483
+ subtasks,
2484
+ matches: matchMap,
2485
+ gatewayToken: this.gatewayToken,
2486
+ resolveAgentUrl: this.resolveAgentUrl,
2487
+ maxBudget: this.maxBudget
2488
+ });
2489
+ const resultObj = {};
2490
+ for (const [key, value] of orchResult.results) {
2491
+ resultObj[key] = value;
2492
+ }
2493
+ return {
2494
+ success: orchResult.success,
2495
+ result: {
2496
+ plan: subtasks,
2497
+ execution: resultObj,
2498
+ total_credits: orchResult.total_credits,
2499
+ latency_ms: orchResult.latency_ms,
2500
+ errors: orchResult.errors
2501
+ },
2502
+ error: orchResult.success ? void 0 : orchResult.errors?.join("; ")
2503
+ };
2504
+ }
2505
+ };
2506
+
2507
+ // src/credit/escrow-receipt.ts
2508
+ import { z as z3 } from "zod";
2509
+ import { randomUUID as randomUUID8 } from "crypto";
2510
+ var EscrowReceiptSchema = z3.object({
2511
+ requester_owner: z3.string().min(1),
2512
+ requester_public_key: z3.string().min(1),
2513
+ amount: z3.number().positive(),
2514
+ card_id: z3.string().min(1),
2515
+ skill_id: z3.string().optional(),
2516
+ timestamp: z3.string(),
2517
+ nonce: z3.string().uuid(),
2518
+ signature: z3.string().min(1)
2519
+ });
2520
+ function createSignedEscrowReceipt(db, privateKey, publicKey, opts) {
2521
+ const escrowId = holdEscrow(db, opts.owner, opts.amount, opts.cardId);
2522
+ const receiptData = {
2523
+ requester_owner: opts.owner,
2524
+ requester_public_key: publicKey.toString("hex"),
2525
+ amount: opts.amount,
2526
+ card_id: opts.cardId,
2527
+ ...opts.skillId ? { skill_id: opts.skillId } : {},
2528
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2529
+ nonce: randomUUID8()
2530
+ };
2531
+ const signature = signEscrowReceipt(receiptData, privateKey);
2532
+ const receipt = {
2533
+ ...receiptData,
2534
+ signature
2535
+ };
2536
+ return { escrowId, receipt };
2537
+ }
885
2538
  export {
2539
+ ApiExecutor,
2540
+ ApiSkillConfigSchema,
2541
+ BudgetController,
2542
+ CONDUCTOR_OWNER,
886
2543
  CapabilityCardSchema,
2544
+ CommandExecutor,
2545
+ CommandSkillConfigSchema,
2546
+ ConductorMode,
2547
+ ConductorSkillConfigSchema,
2548
+ EscrowReceiptSchema,
2549
+ ORCHESTRATION_FEE,
2550
+ OpenClawBridge,
2551
+ OpenClawSkillConfigSchema,
2552
+ PipelineExecutor,
2553
+ PipelineSkillConfigSchema,
2554
+ SkillConfigSchema,
2555
+ SkillExecutor,
2556
+ SkillsFileSchema,
2557
+ TEMPLATES,
2558
+ applyInputMapping,
2559
+ buildAuthHeaders,
2560
+ buildConductorCard,
887
2561
  createGatewayServer,
2562
+ createSignedEscrowReceipt,
2563
+ createSkillExecutor,
2564
+ decompose,
2565
+ expandEnvVars,
2566
+ extractByPath,
2567
+ generateKeyPair,
888
2568
  getBalance,
889
2569
  getCard,
890
2570
  insertCard,
2571
+ interpolate,
2572
+ interpolateObject,
2573
+ loadKeyPair,
2574
+ matchSubTasks,
891
2575
  openCreditDb,
892
2576
  openDatabase,
893
- searchCards
2577
+ orchestrate,
2578
+ parseSkillsFile,
2579
+ registerConductorCard,
2580
+ releaseRequesterEscrow,
2581
+ resolvePath,
2582
+ saveKeyPair,
2583
+ searchCards,
2584
+ settleProviderEarning,
2585
+ settleRequesterEscrow,
2586
+ signEscrowReceipt,
2587
+ verifyEscrowReceipt
894
2588
  };