@peac/adapter-openclaw 0.10.11 → 0.10.12

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.cjs CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  var crypto = require('@peac/crypto');
4
4
  var captureCore = require('@peac/capture-core');
5
+ var path = require('path');
6
+ var fs2 = require('fs/promises');
7
+ var captureNode = require('@peac/capture-node');
8
+
9
+ function _interopNamespace(e) {
10
+ if (e && e.__esModule) return e;
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n.default = e;
24
+ return Object.freeze(n);
25
+ }
26
+
27
+ var path__namespace = /*#__PURE__*/_interopNamespace(path);
28
+ var fs2__namespace = /*#__PURE__*/_interopNamespace(fs2);
5
29
 
6
30
  // src/types.ts
7
31
  var OPENCLAW_EXTENSION_KEYS = {
@@ -518,8 +542,8 @@ async function resolveSigner(keyRef, issuer, audience) {
518
542
  return createJwkSigner(jwk, issuer, audience);
519
543
  }
520
544
  case "file": {
521
- const fs = await import('fs');
522
- const keyData = await fs.promises.readFile(value, "utf-8");
545
+ const fs3 = await import('fs');
546
+ const keyData = await fs3.promises.readFile(value, "utf-8");
523
547
  const jwk = JSON.parse(keyData);
524
548
  return createJwkSigner(jwk, issuer, audience);
525
549
  }
@@ -542,32 +566,36 @@ function generateKeyId(jwk) {
542
566
  return `k_${Math.abs(hash).toString(16).padStart(8, "0")}`;
543
567
  }
544
568
  async function createFileReceiptWriter(outputDir) {
545
- const fs = await import('fs');
569
+ const fs3 = await import('fs');
546
570
  const pathModule = await import('path');
547
571
  const resolvedOutputDir = pathModule.resolve(outputDir);
548
- await fs.promises.mkdir(resolvedOutputDir, { recursive: true });
572
+ await fs3.promises.mkdir(resolvedOutputDir, { recursive: true });
549
573
  return {
550
574
  async write(receipt) {
551
575
  const filename = `${receipt.rid}.peac.json`;
552
576
  const filepath = pathModule.join(resolvedOutputDir, filename);
553
577
  const tempPath = `${filepath}.tmp`;
554
- const content = JSON.stringify(
555
- {
556
- rid: receipt.rid,
557
- interaction_id: receipt.interaction_id,
558
- entry_digest: receipt.entry_digest,
559
- _jws: receipt.jws
560
- },
561
- null,
562
- 2
563
- );
564
- await fs.promises.writeFile(tempPath, content, "utf-8");
565
- const fd = await fs.promises.open(tempPath, "r");
578
+ let content;
579
+ const jwsParts = receipt.jws.split(".");
580
+ if (jwsParts.length !== 3) {
581
+ content = JSON.stringify({ _jws: receipt.jws }, null, 2);
582
+ } else {
583
+ try {
584
+ const payloadJson = Buffer.from(jwsParts[1], "base64url").toString("utf-8");
585
+ const payload = JSON.parse(payloadJson);
586
+ const { evidence, ...auth } = payload;
587
+ content = JSON.stringify({ auth, evidence, _jws: receipt.jws }, null, 2);
588
+ } catch {
589
+ content = JSON.stringify({ _jws: receipt.jws }, null, 2);
590
+ }
591
+ }
592
+ await fs3.promises.writeFile(tempPath, content, "utf-8");
593
+ const fd = await fs3.promises.open(tempPath, "r");
566
594
  await fd.sync();
567
595
  await fd.close();
568
- await fs.promises.rename(tempPath, filepath);
596
+ await fs3.promises.rename(tempPath, filepath);
569
597
  try {
570
- const dirFd = await fs.promises.open(resolvedOutputDir, "r");
598
+ const dirFd = await fs3.promises.open(resolvedOutputDir, "r");
571
599
  await dirFd.sync();
572
600
  await dirFd.close();
573
601
  } catch {
@@ -691,6 +719,20 @@ var MAX_STATUS_FILE_SCAN = 1e3;
691
719
  var MAX_QUERY_FILE_PARSE = 1e4;
692
720
  var MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024;
693
721
  var ALLOWED_ALGORITHMS = ["EdDSA", "ES256", "ES384", "ES512", "RS256", "RS384", "RS512"];
722
+ function decodeReceiptPayload(receipt) {
723
+ const jws = receipt._jws;
724
+ if (typeof jws !== "string") return null;
725
+ const parts = jws.split(".");
726
+ if (parts.length !== 3) return null;
727
+ try {
728
+ const payloadJson = Buffer.from(parts[1], "base64url").toString("utf-8");
729
+ const payload = JSON.parse(payloadJson);
730
+ const { evidence, ...auth } = payload;
731
+ return { auth: auth ?? {}, evidence: evidence ?? {} };
732
+ } catch {
733
+ return null;
734
+ }
735
+ }
694
736
  var ALG_KEY_TYPE_MAP = {
695
737
  EdDSA: { kty: "OKP", crv: ["Ed25519", "Ed448"] },
696
738
  ES256: { kty: "EC", crv: ["P-256"] },
@@ -700,13 +742,14 @@ var ALG_KEY_TYPE_MAP = {
700
742
  RS384: { kty: "RSA" },
701
743
  RS512: { kty: "RSA" }
702
744
  };
703
- function createStatusTool(stats, outputDir) {
745
+ function createStatusTool(getStats, outputDir) {
704
746
  return {
705
747
  name: "peac_receipts.status",
706
748
  description: "Show PEAC receipts status: spool size, last receipt time, config summary",
707
749
  parameters: {},
708
750
  async execute() {
709
- const fs = await import('fs');
751
+ const stats = typeof getStats === "function" ? getStats() : getStats;
752
+ const fs3 = await import('fs');
710
753
  const pathModule = await import('path');
711
754
  const resolvedOutputDir = pathModule.resolve(outputDir);
712
755
  let receiptCount = 0;
@@ -714,7 +757,7 @@ function createStatusTool(stats, outputDir) {
714
757
  let oldestReceiptTime = null;
715
758
  let countApproximate = false;
716
759
  try {
717
- const files = await fs.promises.readdir(resolvedOutputDir);
760
+ const files = await fs3.promises.readdir(resolvedOutputDir);
718
761
  const receiptFiles = files.filter((f) => f.endsWith(".peac.json"));
719
762
  receiptCount = receiptFiles.length;
720
763
  const filesToScan = receiptFiles.slice(0, MAX_STATUS_FILE_SCAN);
@@ -722,7 +765,7 @@ function createStatusTool(stats, outputDir) {
722
765
  if (filesToScan.length > 0) {
723
766
  const fileStats = await Promise.all(
724
767
  filesToScan.map(async (f) => {
725
- const stat = await fs.promises.stat(pathModule.join(resolvedOutputDir, f));
768
+ const stat = await fs3.promises.stat(pathModule.join(resolvedOutputDir, f));
726
769
  return { file: f, mtime: stat.mtime };
727
770
  })
728
771
  );
@@ -782,11 +825,11 @@ function createExportBundleTool(outputDir, logger) {
782
825
  }
783
826
  },
784
827
  async execute(params) {
785
- const fs = await import('fs');
828
+ const fs3 = await import('fs');
786
829
  const pathModule = await import('path');
787
830
  const resolvedOutputDir = pathModule.resolve(outputDir);
788
831
  try {
789
- const files = await fs.promises.readdir(resolvedOutputDir);
832
+ const files = await fs3.promises.readdir(resolvedOutputDir);
790
833
  const receiptFiles = files.filter((f) => f.endsWith(".peac.json"));
791
834
  if (receiptFiles.length === 0) {
792
835
  return {
@@ -796,30 +839,48 @@ function createExportBundleTool(outputDir, logger) {
796
839
  };
797
840
  }
798
841
  const receipts = [];
842
+ const skippedReasons = { stat_error: 0, invalid_json: 0, malformed_jws: 0, filtered: 0 };
843
+ const skippedFiles = [];
799
844
  for (const file of receiptFiles) {
800
845
  const filePath = pathModule.join(resolvedOutputDir, file);
801
846
  let stat;
802
847
  try {
803
- stat = await fs.promises.stat(filePath);
848
+ stat = await fs3.promises.stat(filePath);
804
849
  } catch {
850
+ skippedReasons.stat_error++;
851
+ skippedFiles.push(file);
805
852
  continue;
806
853
  }
807
854
  if (params.since && stat.mtime < new Date(params.since)) {
855
+ skippedReasons.filtered++;
808
856
  continue;
809
857
  }
810
858
  if (params.until && stat.mtime > new Date(params.until)) {
859
+ skippedReasons.filtered++;
811
860
  continue;
812
861
  }
813
862
  let content;
814
863
  try {
815
- content = JSON.parse(await fs.promises.readFile(filePath, "utf-8"));
864
+ content = JSON.parse(await fs3.promises.readFile(filePath, "utf-8"));
816
865
  } catch {
817
866
  logger.warn(`Skipping invalid JSON in ${file}`);
867
+ skippedReasons.invalid_json++;
868
+ skippedFiles.push(file);
818
869
  continue;
819
870
  }
820
871
  if (params.workflow_id) {
821
- const workflowExt = content?.auth?.extensions?.["org.peacprotocol/workflow"];
872
+ const decoded = content._jws ? decodeReceiptPayload(content) : null;
873
+ if (content._jws && !decoded) {
874
+ logger.warn(`Skipping ${file}: _jws present but malformed`);
875
+ skippedReasons.malformed_jws++;
876
+ skippedFiles.push(file);
877
+ continue;
878
+ }
879
+ const authBlock = decoded?.auth ?? content?.auth;
880
+ const extensions = authBlock?.extensions;
881
+ const workflowExt = extensions?.["org.peacprotocol/workflow"];
822
882
  if (workflowExt?.workflow_id !== params.workflow_id) {
883
+ skippedReasons.filtered++;
823
884
  continue;
824
885
  }
825
886
  }
@@ -829,7 +890,12 @@ function createExportBundleTool(outputDir, logger) {
829
890
  return {
830
891
  status: "ok",
831
892
  message: "No receipts match the filter criteria",
832
- receipt_count: 0
893
+ receipt_count: 0,
894
+ scanned_count: receiptFiles.length,
895
+ exported_count: 0,
896
+ skipped_count: skippedReasons.stat_error + skippedReasons.invalid_json + skippedReasons.malformed_jws + skippedReasons.filtered,
897
+ skipped_reasons: skippedReasons,
898
+ skipped_files: skippedFiles.length > 0 ? skippedFiles.slice(0, 100) : void 0
833
899
  };
834
900
  }
835
901
  const manifest = {
@@ -849,15 +915,15 @@ function createExportBundleTool(outputDir, logger) {
849
915
  };
850
916
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
851
917
  const bundleDir = params.output_path ? pathModule.resolve(params.output_path) : pathModule.join(resolvedOutputDir, `peac-bundle-${timestamp}`);
852
- await fs.promises.mkdir(bundleDir, { recursive: true });
853
- await fs.promises.writeFile(
918
+ await fs3.promises.mkdir(bundleDir, { recursive: true });
919
+ await fs3.promises.writeFile(
854
920
  pathModule.join(bundleDir, "manifest.json"),
855
921
  JSON.stringify(manifest, null, 2)
856
922
  );
857
923
  const receiptsDir = pathModule.join(bundleDir, "receipts");
858
- await fs.promises.mkdir(receiptsDir, { recursive: true });
924
+ await fs3.promises.mkdir(receiptsDir, { recursive: true });
859
925
  for (const receipt of receipts) {
860
- await fs.promises.writeFile(
926
+ await fs3.promises.writeFile(
861
927
  pathModule.join(receiptsDir, receipt.file),
862
928
  JSON.stringify(receipt.content, null, 2)
863
929
  );
@@ -867,7 +933,12 @@ function createExportBundleTool(outputDir, logger) {
867
933
  status: "ok",
868
934
  message: `Exported ${receipts.length} receipts to directory`,
869
935
  receipt_count: receipts.length,
870
- bundle_path: bundleDir
936
+ bundle_path: bundleDir,
937
+ scanned_count: receiptFiles.length,
938
+ exported_count: receipts.length,
939
+ skipped_count: skippedReasons.stat_error + skippedReasons.invalid_json + skippedReasons.malformed_jws + skippedReasons.filtered,
940
+ skipped_reasons: skippedReasons,
941
+ skipped_files: skippedFiles.length > 0 ? skippedFiles.slice(0, 100) : void 0
871
942
  };
872
943
  } catch (error) {
873
944
  logger.error("Export bundle failed:", error);
@@ -900,20 +971,20 @@ function createVerifyTool(logger) {
900
971
  },
901
972
  async execute(params) {
902
973
  const verifyParams = params;
903
- const fs = await import('fs');
974
+ const fs3 = await import('fs');
904
975
  const pathModule = await import('path');
905
976
  try {
906
- const stat = await fs.promises.stat(verifyParams.path);
977
+ const stat = await fs3.promises.stat(verifyParams.path);
907
978
  if (stat.isDirectory()) {
908
979
  return await verifyBundle(
909
980
  verifyParams.path,
910
981
  verifyParams.jwks_path,
911
- fs,
982
+ fs3,
912
983
  pathModule,
913
984
  logger
914
985
  );
915
986
  } else {
916
- return await verifySingleReceipt(verifyParams.path, verifyParams.jwks_path, fs, logger);
987
+ return await verifySingleReceipt(verifyParams.path, verifyParams.jwks_path, fs3, logger);
917
988
  }
918
989
  } catch (error) {
919
990
  logger.error("Verification failed:", error);
@@ -927,8 +998,8 @@ function createVerifyTool(logger) {
927
998
  }
928
999
  };
929
1000
  }
930
- async function verifySingleReceipt(receiptPath, jwksPath, fs, logger) {
931
- const stat = await fs.promises.stat(receiptPath);
1001
+ async function verifySingleReceipt(receiptPath, jwksPath, fs3, logger) {
1002
+ const stat = await fs3.promises.stat(receiptPath);
932
1003
  if (stat.size > MAX_FILE_SIZE_BYTES) {
933
1004
  return {
934
1005
  status: "error",
@@ -937,17 +1008,47 @@ async function verifySingleReceipt(receiptPath, jwksPath, fs, logger) {
937
1008
  errors: [`File size ${stat.size} bytes exceeds ${MAX_FILE_SIZE_BYTES} byte limit`]
938
1009
  };
939
1010
  }
940
- const content = await fs.promises.readFile(receiptPath, "utf-8");
1011
+ const content = await fs3.promises.readFile(receiptPath, "utf-8");
941
1012
  const receipt = JSON.parse(content);
942
1013
  const errors = [];
943
1014
  const warnings = [];
944
- if (!receipt.auth) {
1015
+ let auth;
1016
+ let evidence;
1017
+ if (!receipt._jws) {
1018
+ auth = receipt.auth;
1019
+ evidence = receipt.evidence;
1020
+ warnings.push("Missing _jws field -- using unsigned top-level fields");
1021
+ } else {
1022
+ const decoded = decodeReceiptPayload(receipt);
1023
+ if (!decoded) {
1024
+ errors.push("_jws field present but payload could not be decoded (malformed JWS)");
1025
+ auth = void 0;
1026
+ evidence = void 0;
1027
+ } else {
1028
+ auth = decoded.auth;
1029
+ evidence = decoded.evidence;
1030
+ if (receipt.auth !== void 0 || receipt.evidence !== void 0) {
1031
+ const topAuth = receipt.auth;
1032
+ const topEvidence = receipt.evidence;
1033
+ const authMatch = topAuth === void 0 || JSON.stringify(topAuth) === JSON.stringify(decoded.auth);
1034
+ const evidenceMatch = topEvidence === void 0 || JSON.stringify(topEvidence) === JSON.stringify(decoded.evidence);
1035
+ if (!authMatch || !evidenceMatch) {
1036
+ errors.push(
1037
+ "Dual-representation mismatch: top-level auth/evidence differs from _jws payload"
1038
+ );
1039
+ }
1040
+ }
1041
+ }
1042
+ }
1043
+ if (!auth) {
945
1044
  errors.push("Missing auth block");
946
1045
  }
947
- if (!receipt.evidence) {
1046
+ if (!evidence) {
948
1047
  errors.push("Missing evidence block");
949
1048
  }
950
- const interaction = receipt.evidence?.extensions?.["org.peacprotocol/interaction@0.1"];
1049
+ const evidenceObj = evidence;
1050
+ const extensions = evidenceObj?.extensions;
1051
+ const interaction = extensions?.["org.peacprotocol/interaction@0.1"];
951
1052
  if (interaction) {
952
1053
  if (!interaction.interaction_id) {
953
1054
  errors.push("Missing interaction_id");
@@ -955,7 +1056,8 @@ async function verifySingleReceipt(receiptPath, jwksPath, fs, logger) {
955
1056
  if (!interaction.kind) {
956
1057
  errors.push("Missing kind");
957
1058
  }
958
- if (!interaction.executor?.platform) {
1059
+ const executor = interaction.executor;
1060
+ if (!executor?.platform) {
959
1061
  errors.push("Missing executor.platform");
960
1062
  }
961
1063
  if (!interaction.started_at) {
@@ -966,7 +1068,8 @@ async function verifySingleReceipt(receiptPath, jwksPath, fs, logger) {
966
1068
  errors.push("completed_at is before started_at");
967
1069
  }
968
1070
  }
969
- if (interaction.output && !interaction.result?.status) {
1071
+ const result = interaction.result;
1072
+ if (interaction.output && !result?.status) {
970
1073
  errors.push("output present but result.status missing");
971
1074
  }
972
1075
  } else {
@@ -975,7 +1078,7 @@ async function verifySingleReceipt(receiptPath, jwksPath, fs, logger) {
975
1078
  if (jwksPath && receipt._jws) {
976
1079
  try {
977
1080
  const { verify, base64urlDecode: base64urlDecode2, base64urlDecodeString } = await import('@peac/crypto');
978
- const jwks = JSON.parse(await fs.promises.readFile(jwksPath, "utf-8"));
1081
+ const jwks = JSON.parse(await fs3.promises.readFile(jwksPath, "utf-8"));
979
1082
  const jwsParts = receipt._jws.split(".");
980
1083
  if (jwsParts.length !== 3) {
981
1084
  errors.push("Invalid JWS format (expected 3 parts)");
@@ -1060,15 +1163,15 @@ async function verifySingleReceipt(receiptPath, jwksPath, fs, logger) {
1060
1163
  message: errors.length === 0 ? "Receipt is valid" : "Receipt has validation errors",
1061
1164
  errors: errors.length > 0 ? errors : void 0,
1062
1165
  warnings: warnings.length > 0 ? warnings : void 0,
1063
- receipt_id: receipt.auth?.rid,
1166
+ receipt_id: auth?.rid,
1064
1167
  interaction_id: interaction?.interaction_id
1065
1168
  };
1066
1169
  }
1067
- async function verifyBundle(bundlePath, jwksPath, fs, pathModule, logger) {
1170
+ async function verifyBundle(bundlePath, jwksPath, fs3, pathModule, logger) {
1068
1171
  const manifestPath = pathModule.join(bundlePath, "manifest.json");
1069
- const manifest = JSON.parse(await fs.promises.readFile(manifestPath, "utf-8"));
1172
+ const manifest = JSON.parse(await fs3.promises.readFile(manifestPath, "utf-8"));
1070
1173
  const receiptsDir = pathModule.join(bundlePath, "receipts");
1071
- const files = await fs.promises.readdir(receiptsDir);
1174
+ const files = await fs3.promises.readdir(receiptsDir);
1072
1175
  let validCount = 0;
1073
1176
  let invalidCount = 0;
1074
1177
  const allErrors = [];
@@ -1078,7 +1181,7 @@ async function verifyBundle(bundlePath, jwksPath, fs, pathModule, logger) {
1078
1181
  const result = await verifySingleReceipt(
1079
1182
  pathModule.join(receiptsDir, file),
1080
1183
  jwksPath,
1081
- fs,
1184
+ fs3,
1082
1185
  logger
1083
1186
  );
1084
1187
  if (result.valid) {
@@ -1146,11 +1249,11 @@ function createQueryTool(outputDir, logger) {
1146
1249
  }
1147
1250
  },
1148
1251
  async execute(params) {
1149
- const fs = await import('fs');
1252
+ const fs3 = await import('fs');
1150
1253
  const pathModule = await import('path');
1151
1254
  const resolvedOutputDir = pathModule.resolve(outputDir);
1152
1255
  try {
1153
- const files = await fs.promises.readdir(resolvedOutputDir);
1256
+ const files = await fs3.promises.readdir(resolvedOutputDir);
1154
1257
  const receiptFiles = files.filter((f) => f.endsWith(".peac.json"));
1155
1258
  const limit = Math.min(params.limit || 100, MAX_QUERY_FILE_PARSE);
1156
1259
  const offset = params.offset || 0;
@@ -1158,7 +1261,7 @@ function createQueryTool(outputDir, logger) {
1158
1261
  for (const file of receiptFiles.slice(0, MAX_QUERY_FILE_PARSE)) {
1159
1262
  try {
1160
1263
  const filePath = pathModule.join(resolvedOutputDir, file);
1161
- const stat = await fs.promises.stat(filePath);
1264
+ const stat = await fs3.promises.stat(filePath);
1162
1265
  if (params.since && stat.mtime < new Date(params.since)) {
1163
1266
  continue;
1164
1267
  }
@@ -1175,10 +1278,11 @@ function createQueryTool(outputDir, logger) {
1175
1278
  let skippedForFilters = 0;
1176
1279
  let filesSkippedForSize = 0;
1177
1280
  let filesSkippedForInvalidJson = 0;
1281
+ let filesSkippedForMalformedJws = 0;
1178
1282
  for (const { file } of fileInfos) {
1179
1283
  const filePath = pathModule.join(resolvedOutputDir, file);
1180
1284
  try {
1181
- const fileStat = await fs.promises.stat(filePath);
1285
+ const fileStat = await fs3.promises.stat(filePath);
1182
1286
  if (fileStat.size > MAX_FILE_SIZE_BYTES) {
1183
1287
  filesSkippedForSize++;
1184
1288
  continue;
@@ -1188,32 +1292,43 @@ function createQueryTool(outputDir, logger) {
1188
1292
  }
1189
1293
  let content;
1190
1294
  try {
1191
- content = JSON.parse(await fs.promises.readFile(filePath, "utf-8"));
1295
+ content = JSON.parse(await fs3.promises.readFile(filePath, "utf-8"));
1192
1296
  } catch {
1193
1297
  filesSkippedForInvalidJson++;
1194
1298
  continue;
1195
1299
  }
1196
- const interaction = content?.evidence?.extensions?.["org.peacprotocol/interaction@0.1"];
1197
- const workflow = content?.auth?.extensions?.["org.peacprotocol/workflow"];
1300
+ const decoded = content._jws ? decodeReceiptPayload(content) : null;
1301
+ if (content._jws && !decoded) {
1302
+ filesSkippedForMalformedJws++;
1303
+ continue;
1304
+ }
1305
+ const authBlock = decoded?.auth ?? content?.auth;
1306
+ const evidenceBlock = decoded?.evidence ?? content?.evidence;
1307
+ const evidenceExtensions = evidenceBlock?.extensions;
1308
+ const interaction = evidenceExtensions?.["org.peacprotocol/interaction@0.1"];
1309
+ const authExtensions = authBlock?.extensions;
1310
+ const workflow = authExtensions?.["org.peacprotocol/workflow"];
1198
1311
  if (params.workflow_id && workflow?.workflow_id !== params.workflow_id) {
1199
1312
  skippedForFilters++;
1200
1313
  continue;
1201
1314
  }
1202
- if (params.tool_name && interaction?.tool?.name !== params.tool_name) {
1315
+ const tool = interaction?.tool;
1316
+ if (params.tool_name && tool?.name !== params.tool_name) {
1203
1317
  skippedForFilters++;
1204
1318
  continue;
1205
1319
  }
1206
- if (params.status && interaction?.result?.status !== params.status) {
1320
+ const result = interaction?.result;
1321
+ if (params.status && result?.status !== params.status) {
1207
1322
  skippedForFilters++;
1208
1323
  continue;
1209
1324
  }
1210
1325
  matches.push({
1211
1326
  file,
1212
- receipt_id: content.auth?.rid,
1327
+ receipt_id: authBlock?.rid,
1213
1328
  interaction_id: interaction?.interaction_id,
1214
1329
  workflow_id: workflow?.workflow_id,
1215
- tool_name: interaction?.tool?.name,
1216
- status: interaction?.result?.status,
1330
+ tool_name: tool?.name,
1331
+ status: result?.status,
1217
1332
  started_at: interaction?.started_at,
1218
1333
  completed_at: interaction?.completed_at
1219
1334
  });
@@ -1240,17 +1355,25 @@ function createQueryTool(outputDir, logger) {
1240
1355
  if (filesSkippedForInvalidJson > 0) {
1241
1356
  warnings.push(`${filesSkippedForInvalidJson} files skipped (invalid JSON)`);
1242
1357
  }
1243
- const hasTruncation = receiptFiles.length > MAX_QUERY_FILE_PARSE || filesSkippedForSize > 0 || filesSkippedForInvalidJson > 0;
1358
+ if (filesSkippedForMalformedJws > 0) {
1359
+ warnings.push(`${filesSkippedForMalformedJws} files skipped (malformed JWS)`);
1360
+ }
1361
+ const totalSkipped = filesSkippedForSize + filesSkippedForInvalidJson + filesSkippedForMalformedJws;
1362
+ const hasTruncation = receiptFiles.length > MAX_QUERY_FILE_PARSE || totalSkipped > 0;
1244
1363
  return {
1245
1364
  status: "ok",
1246
1365
  total: matches.length,
1247
1366
  offset,
1248
1367
  limit,
1249
1368
  results: paginated,
1369
+ scanned_count: fileInfos.length,
1370
+ matched_count: matches.length,
1250
1371
  truncated: hasTruncation,
1251
- skipped: hasTruncation ? {
1372
+ skipped: hasTruncation || skippedForFilters > 0 ? {
1252
1373
  too_large: filesSkippedForSize,
1253
1374
  invalid_json: filesSkippedForInvalidJson,
1375
+ malformed_jws: filesSkippedForMalformedJws,
1376
+ filtered: skippedForFilters,
1254
1377
  capped: receiptFiles.length > MAX_QUERY_FILE_PARSE ? receiptFiles.length - MAX_QUERY_FILE_PARSE : 0
1255
1378
  } : void 0,
1256
1379
  ...warnings.length > 0 && { warning: warnings.join("; ") }
@@ -1269,10 +1392,161 @@ function createQueryTool(outputDir, logger) {
1269
1392
  }
1270
1393
  };
1271
1394
  }
1395
+ var DEFAULT_DATA_DIR_NAME = ".openclaw/peac";
1396
+ var SPOOL_FILENAME = "spool.jsonl";
1397
+ var DEDUPE_FILENAME = "dedupe.idx";
1398
+ function defaultDataDir() {
1399
+ const home = process.env.HOME || process.env.USERPROFILE || ".";
1400
+ return path__namespace.join(home, DEFAULT_DATA_DIR_NAME);
1401
+ }
1402
+ function noopLogger() {
1403
+ const noop = () => {
1404
+ };
1405
+ return { debug: noop, info: noop, warn: noop, error: noop };
1406
+ }
1407
+ async function activate(options) {
1408
+ const { config, spoolOptions } = options;
1409
+ const logger = options.logger ?? noopLogger();
1410
+ const dataDir = options.dataDir ?? defaultDataDir();
1411
+ await fs2__namespace.mkdir(dataDir, { recursive: true });
1412
+ try {
1413
+ await fs2__namespace.access(dataDir, (await import('constants')).W_OK);
1414
+ } catch {
1415
+ throw new Error(`Data directory is not writable: ${dataDir}`);
1416
+ }
1417
+ logger.info(`Resolving signing key from: ${config.signing.key_ref.split(":")[0]}:***`);
1418
+ const signer = await resolveSigner(
1419
+ config.signing.key_ref,
1420
+ config.signing.issuer,
1421
+ config.signing.audience
1422
+ );
1423
+ logger.info(`Signing key resolved. kid: ${signer.getKeyId()}`);
1424
+ const spoolPath = path__namespace.join(dataDir, SPOOL_FILENAME);
1425
+ const dedupePath = path__namespace.join(dataDir, DEDUPE_FILENAME);
1426
+ const store = await captureNode.createFsSpoolStore({
1427
+ filePath: spoolPath,
1428
+ maxEntries: spoolOptions?.maxEntries,
1429
+ maxFileBytes: spoolOptions?.maxFileBytes,
1430
+ autoCommitIntervalMs: spoolOptions?.autoCommitIntervalMs,
1431
+ lockOptions: spoolOptions?.lockOptions,
1432
+ onWarning: (msg) => logger.warn(`spool: ${msg}`)
1433
+ });
1434
+ const dedupe = await captureNode.createFsDedupeIndex({
1435
+ filePath: dedupePath
1436
+ });
1437
+ const outputDir = config.output_dir ?? path__namespace.join(dataDir, "receipts");
1438
+ const writer = await createFileReceiptWriter(outputDir);
1439
+ const instance = await createPluginInstance({
1440
+ signer,
1441
+ writer,
1442
+ adapterConfig: config,
1443
+ store,
1444
+ dedupe,
1445
+ drainIntervalMs: config.background?.drain_interval_ms,
1446
+ batchSize: config.background?.batch_size,
1447
+ onError: (err) => logger.error(`plugin error: ${err.message}`)
1448
+ });
1449
+ const tools = [
1450
+ createStatusTool(() => instance.getStats(), outputDir),
1451
+ createExportBundleTool(outputDir, logger),
1452
+ createVerifyTool(logger),
1453
+ createQueryTool(outputDir, logger)
1454
+ ];
1455
+ const diag = captureNode.getFsSpoolDiagnostics(store);
1456
+ if (diag) {
1457
+ logger.info(
1458
+ `PEAC evidence export active. Spool: ${diag.entryCount} entries, ${diag.fileBytes} bytes. Receipts: ${outputDir}`
1459
+ );
1460
+ } else {
1461
+ logger.info(`PEAC evidence export active. Receipts: ${outputDir}`);
1462
+ }
1463
+ async function shutdown() {
1464
+ logger.info("Shutting down PEAC evidence export...");
1465
+ await instance.stop();
1466
+ await store.close();
1467
+ await dedupe.close();
1468
+ logger.info("PEAC evidence export stopped.");
1469
+ }
1470
+ return {
1471
+ instance,
1472
+ tools,
1473
+ hookHandler: instance.hookHandler,
1474
+ dataDir,
1475
+ flush: () => instance.backgroundService.drain(),
1476
+ shutdown
1477
+ };
1478
+ }
1479
+ var DEFAULT_KEY_FILENAME = "signing-key.jwk";
1480
+ async function generateSigningKey(options) {
1481
+ const outputDir = options?.outputDir ?? ".";
1482
+ const filename = options?.filename ?? DEFAULT_KEY_FILENAME;
1483
+ const { privateKey: privateKeyBytes, publicKey: publicKeyBytes } = await crypto.generateKeypair();
1484
+ const d = crypto.base64urlEncode(privateKeyBytes);
1485
+ const x = crypto.base64urlEncode(publicKeyBytes);
1486
+ const privateJwk = {
1487
+ kty: "OKP",
1488
+ crv: "Ed25519",
1489
+ x,
1490
+ d,
1491
+ alg: "EdDSA",
1492
+ use: "sig"
1493
+ };
1494
+ const kid = generateKeyId(privateJwk);
1495
+ privateJwk.kid = kid;
1496
+ const publicJwk = {
1497
+ kty: "OKP",
1498
+ crv: "Ed25519",
1499
+ x,
1500
+ kid,
1501
+ alg: "EdDSA",
1502
+ use: "sig"
1503
+ };
1504
+ await fs2__namespace.mkdir(outputDir, { recursive: true });
1505
+ const keyPath = path__namespace.join(outputDir, filename);
1506
+ await fs2__namespace.writeFile(keyPath, JSON.stringify(privateJwk, null, 2) + "\n", "utf-8");
1507
+ try {
1508
+ await fs2__namespace.chmod(keyPath, 384);
1509
+ } catch {
1510
+ process.stderr.write(
1511
+ `Warning: Could not set file permissions on ${keyPath}. Ensure the file is not readable by other users.
1512
+ `
1513
+ );
1514
+ }
1515
+ return { kid, keyPath, publicJwk, privateJwk };
1516
+ }
1517
+ async function keygenCli(args) {
1518
+ let outputDir;
1519
+ for (let i = 0; i < args.length; i++) {
1520
+ if (args[i] === "--output-dir" && i + 1 < args.length) {
1521
+ outputDir = args[i + 1];
1522
+ i++;
1523
+ } else if (args[i] === "--help" || args[i] === "-h") {
1524
+ process.stdout.write(
1525
+ "Usage: peac-keygen [--output-dir <dir>]\n\nGenerate an Ed25519 signing key for PEAC receipt signing.\n\nOptions:\n --output-dir <dir> Directory for key file (default: current directory)\n --help, -h Show this help message\n"
1526
+ );
1527
+ return;
1528
+ }
1529
+ }
1530
+ const result = await generateSigningKey({ outputDir });
1531
+ process.stdout.write(`Key generated successfully.
1532
+ `);
1533
+ process.stdout.write(` kid: ${result.kid}
1534
+ `);
1535
+ process.stdout.write(` key file: ${result.keyPath}
1536
+ `);
1537
+ process.stdout.write(` public x: ${result.publicJwk.x}
1538
+ `);
1539
+ process.stdout.write(`
1540
+ To use with the OpenClaw adapter:
1541
+ `);
1542
+ process.stdout.write(` signing.key_ref: "file:${path__namespace.resolve(result.keyPath)}"
1543
+ `);
1544
+ }
1272
1545
 
1273
1546
  exports.OPENCLAW_ERROR_CODES = OPENCLAW_ERROR_CODES;
1274
1547
  exports.OPENCLAW_EXTENSION_KEYS = OPENCLAW_EXTENSION_KEYS;
1275
1548
  exports.OPENCLAW_WARNING_CODES = OPENCLAW_WARNING_CODES;
1549
+ exports.activate = activate;
1276
1550
  exports.buildWorkflowContext = buildWorkflowContext;
1277
1551
  exports.captureBatch = captureBatch;
1278
1552
  exports.captureParallel = captureParallel;
@@ -1289,6 +1563,8 @@ exports.createStatusTool = createStatusTool;
1289
1563
  exports.createVerifyTool = createVerifyTool;
1290
1564
  exports.extractWorkflowId = extractWorkflowId;
1291
1565
  exports.generateKeyId = generateKeyId;
1566
+ exports.generateSigningKey = generateSigningKey;
1567
+ exports.keygenCli = keygenCli;
1292
1568
  exports.mapToolCallEvent = mapToolCallEvent;
1293
1569
  exports.mapToolCallEventBatch = mapToolCallEventBatch;
1294
1570
  exports.resolveSigner = resolveSigner;