@peac/adapter-openclaw 0.10.11 → 0.10.13

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.mjs CHANGED
@@ -1,5 +1,10 @@
1
- import { canonicalize, base64urlDecode, sign } from '@peac/crypto';
2
- import { toInteractionEvidence, createHasher, createCaptureSession } from '@peac/capture-core';
1
+ import * as crypto from '@peac/crypto';
2
+ import { canonicalize, generateKeypair, base64urlEncode as base64urlEncode$1 } from '@peac/crypto';
3
+ import * as captureCore from '@peac/capture-core';
4
+ import { toInteractionEvidence } from '@peac/capture-core';
5
+ import * as path from 'path';
6
+ import * as fs2 from 'fs/promises';
7
+ import { createFsSpoolStore, createFsDedupeIndex, getFsSpoolDiagnostics } from '@peac/capture-node';
3
8
 
4
9
  // src/types.ts
5
10
  var OPENCLAW_EXTENSION_KEYS = {
@@ -483,10 +488,10 @@ function createJwkSigner(jwk, issuer, audience) {
483
488
  if (!jwk.d) {
484
489
  throw new Error("JWK must include private key (d parameter)");
485
490
  }
486
- const privateKeyBytes = base64urlDecode(jwk.d);
491
+ const privateKeyBytes = crypto.base64urlDecode(jwk.d);
487
492
  return {
488
493
  async sign(payload) {
489
- return sign(payload, privateKeyBytes, kid);
494
+ return crypto.sign(payload, privateKeyBytes, kid);
490
495
  },
491
496
  getKeyId() {
492
497
  return kid;
@@ -516,8 +521,8 @@ async function resolveSigner(keyRef, issuer, audience) {
516
521
  return createJwkSigner(jwk, issuer, audience);
517
522
  }
518
523
  case "file": {
519
- const fs = await import('fs');
520
- const keyData = await fs.promises.readFile(value, "utf-8");
524
+ const fs3 = await import('fs');
525
+ const keyData = await fs3.promises.readFile(value, "utf-8");
521
526
  const jwk = JSON.parse(keyData);
522
527
  return createJwkSigner(jwk, issuer, audience);
523
528
  }
@@ -540,32 +545,36 @@ function generateKeyId(jwk) {
540
545
  return `k_${Math.abs(hash).toString(16).padStart(8, "0")}`;
541
546
  }
542
547
  async function createFileReceiptWriter(outputDir) {
543
- const fs = await import('fs');
548
+ const fs3 = await import('fs');
544
549
  const pathModule = await import('path');
545
550
  const resolvedOutputDir = pathModule.resolve(outputDir);
546
- await fs.promises.mkdir(resolvedOutputDir, { recursive: true });
551
+ await fs3.promises.mkdir(resolvedOutputDir, { recursive: true });
547
552
  return {
548
553
  async write(receipt) {
549
554
  const filename = `${receipt.rid}.peac.json`;
550
555
  const filepath = pathModule.join(resolvedOutputDir, filename);
551
556
  const tempPath = `${filepath}.tmp`;
552
- const content = JSON.stringify(
553
- {
554
- rid: receipt.rid,
555
- interaction_id: receipt.interaction_id,
556
- entry_digest: receipt.entry_digest,
557
- _jws: receipt.jws
558
- },
559
- null,
560
- 2
561
- );
562
- await fs.promises.writeFile(tempPath, content, "utf-8");
563
- const fd = await fs.promises.open(tempPath, "r");
557
+ let content;
558
+ const jwsParts = receipt.jws.split(".");
559
+ if (jwsParts.length !== 3) {
560
+ content = JSON.stringify({ _jws: receipt.jws }, null, 2);
561
+ } else {
562
+ try {
563
+ const payloadJson = Buffer.from(jwsParts[1], "base64url").toString("utf-8");
564
+ const payload = JSON.parse(payloadJson);
565
+ const { evidence, ...auth } = payload;
566
+ content = JSON.stringify({ auth, evidence, _jws: receipt.jws }, null, 2);
567
+ } catch {
568
+ content = JSON.stringify({ _jws: receipt.jws }, null, 2);
569
+ }
570
+ }
571
+ await fs3.promises.writeFile(tempPath, content, "utf-8");
572
+ const fd = await fs3.promises.open(tempPath, "r");
564
573
  await fd.sync();
565
574
  await fd.close();
566
- await fs.promises.rename(tempPath, filepath);
575
+ await fs3.promises.rename(tempPath, filepath);
567
576
  try {
568
- const dirFd = await fs.promises.open(resolvedOutputDir, "r");
577
+ const dirFd = await fs3.promises.open(resolvedOutputDir, "r");
569
578
  await dirFd.sync();
570
579
  await dirFd.close();
571
580
  } catch {
@@ -587,8 +596,8 @@ async function createPluginInstance(options) {
587
596
  batchSize = 100,
588
597
  onError
589
598
  } = options;
590
- const hasher = await createHasher();
591
- const session = createCaptureSession({
599
+ const hasher = await captureCore.createHasher();
600
+ const session = captureCore.createCaptureSession({
592
601
  store,
593
602
  dedupe,
594
603
  hasher
@@ -689,6 +698,20 @@ var MAX_STATUS_FILE_SCAN = 1e3;
689
698
  var MAX_QUERY_FILE_PARSE = 1e4;
690
699
  var MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024;
691
700
  var ALLOWED_ALGORITHMS = ["EdDSA", "ES256", "ES384", "ES512", "RS256", "RS384", "RS512"];
701
+ function decodeReceiptPayload(receipt) {
702
+ const jws = receipt._jws;
703
+ if (typeof jws !== "string") return null;
704
+ const parts = jws.split(".");
705
+ if (parts.length !== 3) return null;
706
+ try {
707
+ const payloadJson = Buffer.from(parts[1], "base64url").toString("utf-8");
708
+ const payload = JSON.parse(payloadJson);
709
+ const { evidence, ...auth } = payload;
710
+ return { auth: auth ?? {}, evidence: evidence ?? {} };
711
+ } catch {
712
+ return null;
713
+ }
714
+ }
692
715
  var ALG_KEY_TYPE_MAP = {
693
716
  EdDSA: { kty: "OKP", crv: ["Ed25519", "Ed448"] },
694
717
  ES256: { kty: "EC", crv: ["P-256"] },
@@ -698,13 +721,14 @@ var ALG_KEY_TYPE_MAP = {
698
721
  RS384: { kty: "RSA" },
699
722
  RS512: { kty: "RSA" }
700
723
  };
701
- function createStatusTool(stats, outputDir) {
724
+ function createStatusTool(getStats, outputDir) {
702
725
  return {
703
726
  name: "peac_receipts.status",
704
727
  description: "Show PEAC receipts status: spool size, last receipt time, config summary",
705
728
  parameters: {},
706
729
  async execute() {
707
- const fs = await import('fs');
730
+ const stats = typeof getStats === "function" ? getStats() : getStats;
731
+ const fs3 = await import('fs');
708
732
  const pathModule = await import('path');
709
733
  const resolvedOutputDir = pathModule.resolve(outputDir);
710
734
  let receiptCount = 0;
@@ -712,7 +736,7 @@ function createStatusTool(stats, outputDir) {
712
736
  let oldestReceiptTime = null;
713
737
  let countApproximate = false;
714
738
  try {
715
- const files = await fs.promises.readdir(resolvedOutputDir);
739
+ const files = await fs3.promises.readdir(resolvedOutputDir);
716
740
  const receiptFiles = files.filter((f) => f.endsWith(".peac.json"));
717
741
  receiptCount = receiptFiles.length;
718
742
  const filesToScan = receiptFiles.slice(0, MAX_STATUS_FILE_SCAN);
@@ -720,7 +744,7 @@ function createStatusTool(stats, outputDir) {
720
744
  if (filesToScan.length > 0) {
721
745
  const fileStats = await Promise.all(
722
746
  filesToScan.map(async (f) => {
723
- const stat = await fs.promises.stat(pathModule.join(resolvedOutputDir, f));
747
+ const stat = await fs3.promises.stat(pathModule.join(resolvedOutputDir, f));
724
748
  return { file: f, mtime: stat.mtime };
725
749
  })
726
750
  );
@@ -780,11 +804,11 @@ function createExportBundleTool(outputDir, logger) {
780
804
  }
781
805
  },
782
806
  async execute(params) {
783
- const fs = await import('fs');
807
+ const fs3 = await import('fs');
784
808
  const pathModule = await import('path');
785
809
  const resolvedOutputDir = pathModule.resolve(outputDir);
786
810
  try {
787
- const files = await fs.promises.readdir(resolvedOutputDir);
811
+ const files = await fs3.promises.readdir(resolvedOutputDir);
788
812
  const receiptFiles = files.filter((f) => f.endsWith(".peac.json"));
789
813
  if (receiptFiles.length === 0) {
790
814
  return {
@@ -794,30 +818,48 @@ function createExportBundleTool(outputDir, logger) {
794
818
  };
795
819
  }
796
820
  const receipts = [];
821
+ const skippedReasons = { stat_error: 0, invalid_json: 0, malformed_jws: 0, filtered: 0 };
822
+ const skippedFiles = [];
797
823
  for (const file of receiptFiles) {
798
824
  const filePath = pathModule.join(resolvedOutputDir, file);
799
825
  let stat;
800
826
  try {
801
- stat = await fs.promises.stat(filePath);
827
+ stat = await fs3.promises.stat(filePath);
802
828
  } catch {
829
+ skippedReasons.stat_error++;
830
+ skippedFiles.push(file);
803
831
  continue;
804
832
  }
805
833
  if (params.since && stat.mtime < new Date(params.since)) {
834
+ skippedReasons.filtered++;
806
835
  continue;
807
836
  }
808
837
  if (params.until && stat.mtime > new Date(params.until)) {
838
+ skippedReasons.filtered++;
809
839
  continue;
810
840
  }
811
841
  let content;
812
842
  try {
813
- content = JSON.parse(await fs.promises.readFile(filePath, "utf-8"));
843
+ content = JSON.parse(await fs3.promises.readFile(filePath, "utf-8"));
814
844
  } catch {
815
845
  logger.warn(`Skipping invalid JSON in ${file}`);
846
+ skippedReasons.invalid_json++;
847
+ skippedFiles.push(file);
816
848
  continue;
817
849
  }
818
850
  if (params.workflow_id) {
819
- const workflowExt = content?.auth?.extensions?.["org.peacprotocol/workflow"];
851
+ const decoded = content._jws ? decodeReceiptPayload(content) : null;
852
+ if (content._jws && !decoded) {
853
+ logger.warn(`Skipping ${file}: _jws present but malformed`);
854
+ skippedReasons.malformed_jws++;
855
+ skippedFiles.push(file);
856
+ continue;
857
+ }
858
+ const authBlock = decoded?.auth ?? content?.auth;
859
+ const extensions = authBlock?.extensions;
860
+ const workflowExt = extensions?.["org.peacprotocol/workflow"];
820
861
  if (workflowExt?.workflow_id !== params.workflow_id) {
862
+ skippedReasons.filtered++;
821
863
  continue;
822
864
  }
823
865
  }
@@ -827,7 +869,12 @@ function createExportBundleTool(outputDir, logger) {
827
869
  return {
828
870
  status: "ok",
829
871
  message: "No receipts match the filter criteria",
830
- receipt_count: 0
872
+ receipt_count: 0,
873
+ scanned_count: receiptFiles.length,
874
+ exported_count: 0,
875
+ skipped_count: skippedReasons.stat_error + skippedReasons.invalid_json + skippedReasons.malformed_jws + skippedReasons.filtered,
876
+ skipped_reasons: skippedReasons,
877
+ skipped_files: skippedFiles.length > 0 ? skippedFiles.slice(0, 100) : void 0
831
878
  };
832
879
  }
833
880
  const manifest = {
@@ -847,15 +894,15 @@ function createExportBundleTool(outputDir, logger) {
847
894
  };
848
895
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
849
896
  const bundleDir = params.output_path ? pathModule.resolve(params.output_path) : pathModule.join(resolvedOutputDir, `peac-bundle-${timestamp}`);
850
- await fs.promises.mkdir(bundleDir, { recursive: true });
851
- await fs.promises.writeFile(
897
+ await fs3.promises.mkdir(bundleDir, { recursive: true });
898
+ await fs3.promises.writeFile(
852
899
  pathModule.join(bundleDir, "manifest.json"),
853
900
  JSON.stringify(manifest, null, 2)
854
901
  );
855
902
  const receiptsDir = pathModule.join(bundleDir, "receipts");
856
- await fs.promises.mkdir(receiptsDir, { recursive: true });
903
+ await fs3.promises.mkdir(receiptsDir, { recursive: true });
857
904
  for (const receipt of receipts) {
858
- await fs.promises.writeFile(
905
+ await fs3.promises.writeFile(
859
906
  pathModule.join(receiptsDir, receipt.file),
860
907
  JSON.stringify(receipt.content, null, 2)
861
908
  );
@@ -865,7 +912,12 @@ function createExportBundleTool(outputDir, logger) {
865
912
  status: "ok",
866
913
  message: `Exported ${receipts.length} receipts to directory`,
867
914
  receipt_count: receipts.length,
868
- bundle_path: bundleDir
915
+ bundle_path: bundleDir,
916
+ scanned_count: receiptFiles.length,
917
+ exported_count: receipts.length,
918
+ skipped_count: skippedReasons.stat_error + skippedReasons.invalid_json + skippedReasons.malformed_jws + skippedReasons.filtered,
919
+ skipped_reasons: skippedReasons,
920
+ skipped_files: skippedFiles.length > 0 ? skippedFiles.slice(0, 100) : void 0
869
921
  };
870
922
  } catch (error) {
871
923
  logger.error("Export bundle failed:", error);
@@ -898,20 +950,20 @@ function createVerifyTool(logger) {
898
950
  },
899
951
  async execute(params) {
900
952
  const verifyParams = params;
901
- const fs = await import('fs');
953
+ const fs3 = await import('fs');
902
954
  const pathModule = await import('path');
903
955
  try {
904
- const stat = await fs.promises.stat(verifyParams.path);
956
+ const stat = await fs3.promises.stat(verifyParams.path);
905
957
  if (stat.isDirectory()) {
906
958
  return await verifyBundle(
907
959
  verifyParams.path,
908
960
  verifyParams.jwks_path,
909
- fs,
961
+ fs3,
910
962
  pathModule,
911
963
  logger
912
964
  );
913
965
  } else {
914
- return await verifySingleReceipt(verifyParams.path, verifyParams.jwks_path, fs, logger);
966
+ return await verifySingleReceipt(verifyParams.path, verifyParams.jwks_path, fs3, logger);
915
967
  }
916
968
  } catch (error) {
917
969
  logger.error("Verification failed:", error);
@@ -925,8 +977,8 @@ function createVerifyTool(logger) {
925
977
  }
926
978
  };
927
979
  }
928
- async function verifySingleReceipt(receiptPath, jwksPath, fs, logger) {
929
- const stat = await fs.promises.stat(receiptPath);
980
+ async function verifySingleReceipt(receiptPath, jwksPath, fs3, logger) {
981
+ const stat = await fs3.promises.stat(receiptPath);
930
982
  if (stat.size > MAX_FILE_SIZE_BYTES) {
931
983
  return {
932
984
  status: "error",
@@ -935,17 +987,47 @@ async function verifySingleReceipt(receiptPath, jwksPath, fs, logger) {
935
987
  errors: [`File size ${stat.size} bytes exceeds ${MAX_FILE_SIZE_BYTES} byte limit`]
936
988
  };
937
989
  }
938
- const content = await fs.promises.readFile(receiptPath, "utf-8");
990
+ const content = await fs3.promises.readFile(receiptPath, "utf-8");
939
991
  const receipt = JSON.parse(content);
940
992
  const errors = [];
941
993
  const warnings = [];
942
- if (!receipt.auth) {
994
+ let auth;
995
+ let evidence;
996
+ if (!receipt._jws) {
997
+ auth = receipt.auth;
998
+ evidence = receipt.evidence;
999
+ warnings.push("Missing _jws field -- using unsigned top-level fields");
1000
+ } else {
1001
+ const decoded = decodeReceiptPayload(receipt);
1002
+ if (!decoded) {
1003
+ errors.push("_jws field present but payload could not be decoded (malformed JWS)");
1004
+ auth = void 0;
1005
+ evidence = void 0;
1006
+ } else {
1007
+ auth = decoded.auth;
1008
+ evidence = decoded.evidence;
1009
+ if (receipt.auth !== void 0 || receipt.evidence !== void 0) {
1010
+ const topAuth = receipt.auth;
1011
+ const topEvidence = receipt.evidence;
1012
+ const authMatch = topAuth === void 0 || JSON.stringify(topAuth) === JSON.stringify(decoded.auth);
1013
+ const evidenceMatch = topEvidence === void 0 || JSON.stringify(topEvidence) === JSON.stringify(decoded.evidence);
1014
+ if (!authMatch || !evidenceMatch) {
1015
+ errors.push(
1016
+ "Dual-representation mismatch: top-level auth/evidence differs from _jws payload"
1017
+ );
1018
+ }
1019
+ }
1020
+ }
1021
+ }
1022
+ if (!auth) {
943
1023
  errors.push("Missing auth block");
944
1024
  }
945
- if (!receipt.evidence) {
1025
+ if (!evidence) {
946
1026
  errors.push("Missing evidence block");
947
1027
  }
948
- const interaction = receipt.evidence?.extensions?.["org.peacprotocol/interaction@0.1"];
1028
+ const evidenceObj = evidence;
1029
+ const extensions = evidenceObj?.extensions;
1030
+ const interaction = extensions?.["org.peacprotocol/interaction@0.1"];
949
1031
  if (interaction) {
950
1032
  if (!interaction.interaction_id) {
951
1033
  errors.push("Missing interaction_id");
@@ -953,7 +1035,8 @@ async function verifySingleReceipt(receiptPath, jwksPath, fs, logger) {
953
1035
  if (!interaction.kind) {
954
1036
  errors.push("Missing kind");
955
1037
  }
956
- if (!interaction.executor?.platform) {
1038
+ const executor = interaction.executor;
1039
+ if (!executor?.platform) {
957
1040
  errors.push("Missing executor.platform");
958
1041
  }
959
1042
  if (!interaction.started_at) {
@@ -964,7 +1047,8 @@ async function verifySingleReceipt(receiptPath, jwksPath, fs, logger) {
964
1047
  errors.push("completed_at is before started_at");
965
1048
  }
966
1049
  }
967
- if (interaction.output && !interaction.result?.status) {
1050
+ const result = interaction.result;
1051
+ if (interaction.output && !result?.status) {
968
1052
  errors.push("output present but result.status missing");
969
1053
  }
970
1054
  } else {
@@ -973,7 +1057,7 @@ async function verifySingleReceipt(receiptPath, jwksPath, fs, logger) {
973
1057
  if (jwksPath && receipt._jws) {
974
1058
  try {
975
1059
  const { verify, base64urlDecode: base64urlDecode2, base64urlDecodeString } = await import('@peac/crypto');
976
- const jwks = JSON.parse(await fs.promises.readFile(jwksPath, "utf-8"));
1060
+ const jwks = JSON.parse(await fs3.promises.readFile(jwksPath, "utf-8"));
977
1061
  const jwsParts = receipt._jws.split(".");
978
1062
  if (jwsParts.length !== 3) {
979
1063
  errors.push("Invalid JWS format (expected 3 parts)");
@@ -1058,15 +1142,15 @@ async function verifySingleReceipt(receiptPath, jwksPath, fs, logger) {
1058
1142
  message: errors.length === 0 ? "Receipt is valid" : "Receipt has validation errors",
1059
1143
  errors: errors.length > 0 ? errors : void 0,
1060
1144
  warnings: warnings.length > 0 ? warnings : void 0,
1061
- receipt_id: receipt.auth?.rid,
1145
+ receipt_id: auth?.rid,
1062
1146
  interaction_id: interaction?.interaction_id
1063
1147
  };
1064
1148
  }
1065
- async function verifyBundle(bundlePath, jwksPath, fs, pathModule, logger) {
1149
+ async function verifyBundle(bundlePath, jwksPath, fs3, pathModule, logger) {
1066
1150
  const manifestPath = pathModule.join(bundlePath, "manifest.json");
1067
- const manifest = JSON.parse(await fs.promises.readFile(manifestPath, "utf-8"));
1151
+ const manifest = JSON.parse(await fs3.promises.readFile(manifestPath, "utf-8"));
1068
1152
  const receiptsDir = pathModule.join(bundlePath, "receipts");
1069
- const files = await fs.promises.readdir(receiptsDir);
1153
+ const files = await fs3.promises.readdir(receiptsDir);
1070
1154
  let validCount = 0;
1071
1155
  let invalidCount = 0;
1072
1156
  const allErrors = [];
@@ -1076,7 +1160,7 @@ async function verifyBundle(bundlePath, jwksPath, fs, pathModule, logger) {
1076
1160
  const result = await verifySingleReceipt(
1077
1161
  pathModule.join(receiptsDir, file),
1078
1162
  jwksPath,
1079
- fs,
1163
+ fs3,
1080
1164
  logger
1081
1165
  );
1082
1166
  if (result.valid) {
@@ -1144,11 +1228,11 @@ function createQueryTool(outputDir, logger) {
1144
1228
  }
1145
1229
  },
1146
1230
  async execute(params) {
1147
- const fs = await import('fs');
1231
+ const fs3 = await import('fs');
1148
1232
  const pathModule = await import('path');
1149
1233
  const resolvedOutputDir = pathModule.resolve(outputDir);
1150
1234
  try {
1151
- const files = await fs.promises.readdir(resolvedOutputDir);
1235
+ const files = await fs3.promises.readdir(resolvedOutputDir);
1152
1236
  const receiptFiles = files.filter((f) => f.endsWith(".peac.json"));
1153
1237
  const limit = Math.min(params.limit || 100, MAX_QUERY_FILE_PARSE);
1154
1238
  const offset = params.offset || 0;
@@ -1156,7 +1240,7 @@ function createQueryTool(outputDir, logger) {
1156
1240
  for (const file of receiptFiles.slice(0, MAX_QUERY_FILE_PARSE)) {
1157
1241
  try {
1158
1242
  const filePath = pathModule.join(resolvedOutputDir, file);
1159
- const stat = await fs.promises.stat(filePath);
1243
+ const stat = await fs3.promises.stat(filePath);
1160
1244
  if (params.since && stat.mtime < new Date(params.since)) {
1161
1245
  continue;
1162
1246
  }
@@ -1173,10 +1257,11 @@ function createQueryTool(outputDir, logger) {
1173
1257
  let skippedForFilters = 0;
1174
1258
  let filesSkippedForSize = 0;
1175
1259
  let filesSkippedForInvalidJson = 0;
1260
+ let filesSkippedForMalformedJws = 0;
1176
1261
  for (const { file } of fileInfos) {
1177
1262
  const filePath = pathModule.join(resolvedOutputDir, file);
1178
1263
  try {
1179
- const fileStat = await fs.promises.stat(filePath);
1264
+ const fileStat = await fs3.promises.stat(filePath);
1180
1265
  if (fileStat.size > MAX_FILE_SIZE_BYTES) {
1181
1266
  filesSkippedForSize++;
1182
1267
  continue;
@@ -1186,32 +1271,43 @@ function createQueryTool(outputDir, logger) {
1186
1271
  }
1187
1272
  let content;
1188
1273
  try {
1189
- content = JSON.parse(await fs.promises.readFile(filePath, "utf-8"));
1274
+ content = JSON.parse(await fs3.promises.readFile(filePath, "utf-8"));
1190
1275
  } catch {
1191
1276
  filesSkippedForInvalidJson++;
1192
1277
  continue;
1193
1278
  }
1194
- const interaction = content?.evidence?.extensions?.["org.peacprotocol/interaction@0.1"];
1195
- const workflow = content?.auth?.extensions?.["org.peacprotocol/workflow"];
1279
+ const decoded = content._jws ? decodeReceiptPayload(content) : null;
1280
+ if (content._jws && !decoded) {
1281
+ filesSkippedForMalformedJws++;
1282
+ continue;
1283
+ }
1284
+ const authBlock = decoded?.auth ?? content?.auth;
1285
+ const evidenceBlock = decoded?.evidence ?? content?.evidence;
1286
+ const evidenceExtensions = evidenceBlock?.extensions;
1287
+ const interaction = evidenceExtensions?.["org.peacprotocol/interaction@0.1"];
1288
+ const authExtensions = authBlock?.extensions;
1289
+ const workflow = authExtensions?.["org.peacprotocol/workflow"];
1196
1290
  if (params.workflow_id && workflow?.workflow_id !== params.workflow_id) {
1197
1291
  skippedForFilters++;
1198
1292
  continue;
1199
1293
  }
1200
- if (params.tool_name && interaction?.tool?.name !== params.tool_name) {
1294
+ const tool = interaction?.tool;
1295
+ if (params.tool_name && tool?.name !== params.tool_name) {
1201
1296
  skippedForFilters++;
1202
1297
  continue;
1203
1298
  }
1204
- if (params.status && interaction?.result?.status !== params.status) {
1299
+ const result = interaction?.result;
1300
+ if (params.status && result?.status !== params.status) {
1205
1301
  skippedForFilters++;
1206
1302
  continue;
1207
1303
  }
1208
1304
  matches.push({
1209
1305
  file,
1210
- receipt_id: content.auth?.rid,
1306
+ receipt_id: authBlock?.rid,
1211
1307
  interaction_id: interaction?.interaction_id,
1212
1308
  workflow_id: workflow?.workflow_id,
1213
- tool_name: interaction?.tool?.name,
1214
- status: interaction?.result?.status,
1309
+ tool_name: tool?.name,
1310
+ status: result?.status,
1215
1311
  started_at: interaction?.started_at,
1216
1312
  completed_at: interaction?.completed_at
1217
1313
  });
@@ -1238,17 +1334,25 @@ function createQueryTool(outputDir, logger) {
1238
1334
  if (filesSkippedForInvalidJson > 0) {
1239
1335
  warnings.push(`${filesSkippedForInvalidJson} files skipped (invalid JSON)`);
1240
1336
  }
1241
- const hasTruncation = receiptFiles.length > MAX_QUERY_FILE_PARSE || filesSkippedForSize > 0 || filesSkippedForInvalidJson > 0;
1337
+ if (filesSkippedForMalformedJws > 0) {
1338
+ warnings.push(`${filesSkippedForMalformedJws} files skipped (malformed JWS)`);
1339
+ }
1340
+ const totalSkipped = filesSkippedForSize + filesSkippedForInvalidJson + filesSkippedForMalformedJws;
1341
+ const hasTruncation = receiptFiles.length > MAX_QUERY_FILE_PARSE || totalSkipped > 0;
1242
1342
  return {
1243
1343
  status: "ok",
1244
1344
  total: matches.length,
1245
1345
  offset,
1246
1346
  limit,
1247
1347
  results: paginated,
1348
+ scanned_count: fileInfos.length,
1349
+ matched_count: matches.length,
1248
1350
  truncated: hasTruncation,
1249
- skipped: hasTruncation ? {
1351
+ skipped: hasTruncation || skippedForFilters > 0 ? {
1250
1352
  too_large: filesSkippedForSize,
1251
1353
  invalid_json: filesSkippedForInvalidJson,
1354
+ malformed_jws: filesSkippedForMalformedJws,
1355
+ filtered: skippedForFilters,
1252
1356
  capped: receiptFiles.length > MAX_QUERY_FILE_PARSE ? receiptFiles.length - MAX_QUERY_FILE_PARSE : 0
1253
1357
  } : void 0,
1254
1358
  ...warnings.length > 0 && { warning: warnings.join("; ") }
@@ -1267,7 +1371,157 @@ function createQueryTool(outputDir, logger) {
1267
1371
  }
1268
1372
  };
1269
1373
  }
1374
+ var DEFAULT_DATA_DIR_NAME = ".openclaw/peac";
1375
+ var SPOOL_FILENAME = "spool.jsonl";
1376
+ var DEDUPE_FILENAME = "dedupe.idx";
1377
+ function defaultDataDir() {
1378
+ const home = process.env.HOME || process.env.USERPROFILE || ".";
1379
+ return path.join(home, DEFAULT_DATA_DIR_NAME);
1380
+ }
1381
+ function noopLogger() {
1382
+ const noop = () => {
1383
+ };
1384
+ return { debug: noop, info: noop, warn: noop, error: noop };
1385
+ }
1386
+ async function activate(options) {
1387
+ const { config, spoolOptions } = options;
1388
+ const logger = options.logger ?? noopLogger();
1389
+ const dataDir = options.dataDir ?? defaultDataDir();
1390
+ await fs2.mkdir(dataDir, { recursive: true });
1391
+ try {
1392
+ await fs2.access(dataDir, (await import('constants')).W_OK);
1393
+ } catch {
1394
+ throw new Error(`Data directory is not writable: ${dataDir}`);
1395
+ }
1396
+ logger.info(`Resolving signing key from: ${config.signing.key_ref.split(":")[0]}:***`);
1397
+ const signer = await resolveSigner(
1398
+ config.signing.key_ref,
1399
+ config.signing.issuer,
1400
+ config.signing.audience
1401
+ );
1402
+ logger.info(`Signing key resolved. kid: ${signer.getKeyId()}`);
1403
+ const spoolPath = path.join(dataDir, SPOOL_FILENAME);
1404
+ const dedupePath = path.join(dataDir, DEDUPE_FILENAME);
1405
+ const store = await createFsSpoolStore({
1406
+ filePath: spoolPath,
1407
+ maxEntries: spoolOptions?.maxEntries,
1408
+ maxFileBytes: spoolOptions?.maxFileBytes,
1409
+ autoCommitIntervalMs: spoolOptions?.autoCommitIntervalMs,
1410
+ lockOptions: spoolOptions?.lockOptions,
1411
+ onWarning: (msg) => logger.warn(`spool: ${msg}`)
1412
+ });
1413
+ const dedupe = await createFsDedupeIndex({
1414
+ filePath: dedupePath
1415
+ });
1416
+ const outputDir = config.output_dir ?? path.join(dataDir, "receipts");
1417
+ const writer = await createFileReceiptWriter(outputDir);
1418
+ const instance = await createPluginInstance({
1419
+ signer,
1420
+ writer,
1421
+ adapterConfig: config,
1422
+ store,
1423
+ dedupe,
1424
+ drainIntervalMs: config.background?.drain_interval_ms,
1425
+ batchSize: config.background?.batch_size,
1426
+ onError: (err) => logger.error(`plugin error: ${err.message}`)
1427
+ });
1428
+ const tools = [
1429
+ createStatusTool(() => instance.getStats(), outputDir),
1430
+ createExportBundleTool(outputDir, logger),
1431
+ createVerifyTool(logger),
1432
+ createQueryTool(outputDir, logger)
1433
+ ];
1434
+ const diag = getFsSpoolDiagnostics(store);
1435
+ if (diag) {
1436
+ logger.info(
1437
+ `PEAC evidence export active. Spool: ${diag.entryCount} entries, ${diag.fileBytes} bytes. Receipts: ${outputDir}`
1438
+ );
1439
+ } else {
1440
+ logger.info(`PEAC evidence export active. Receipts: ${outputDir}`);
1441
+ }
1442
+ async function shutdown() {
1443
+ logger.info("Shutting down PEAC evidence export...");
1444
+ await instance.stop();
1445
+ await store.close();
1446
+ await dedupe.close();
1447
+ logger.info("PEAC evidence export stopped.");
1448
+ }
1449
+ return {
1450
+ instance,
1451
+ tools,
1452
+ hookHandler: instance.hookHandler,
1453
+ dataDir,
1454
+ flush: () => instance.backgroundService.drain(),
1455
+ shutdown
1456
+ };
1457
+ }
1458
+ var DEFAULT_KEY_FILENAME = "signing-key.jwk";
1459
+ async function generateSigningKey(options) {
1460
+ const outputDir = options?.outputDir ?? ".";
1461
+ const filename = options?.filename ?? DEFAULT_KEY_FILENAME;
1462
+ const { privateKey: privateKeyBytes, publicKey: publicKeyBytes } = await generateKeypair();
1463
+ const d = base64urlEncode$1(privateKeyBytes);
1464
+ const x = base64urlEncode$1(publicKeyBytes);
1465
+ const privateJwk = {
1466
+ kty: "OKP",
1467
+ crv: "Ed25519",
1468
+ x,
1469
+ d,
1470
+ alg: "EdDSA",
1471
+ use: "sig"
1472
+ };
1473
+ const kid = generateKeyId(privateJwk);
1474
+ privateJwk.kid = kid;
1475
+ const publicJwk = {
1476
+ kty: "OKP",
1477
+ crv: "Ed25519",
1478
+ x,
1479
+ kid,
1480
+ alg: "EdDSA",
1481
+ use: "sig"
1482
+ };
1483
+ await fs2.mkdir(outputDir, { recursive: true });
1484
+ const keyPath = path.join(outputDir, filename);
1485
+ await fs2.writeFile(keyPath, JSON.stringify(privateJwk, null, 2) + "\n", "utf-8");
1486
+ try {
1487
+ await fs2.chmod(keyPath, 384);
1488
+ } catch {
1489
+ process.stderr.write(
1490
+ `Warning: Could not set file permissions on ${keyPath}. Ensure the file is not readable by other users.
1491
+ `
1492
+ );
1493
+ }
1494
+ return { kid, keyPath, publicJwk, privateJwk };
1495
+ }
1496
+ async function keygenCli(args) {
1497
+ let outputDir;
1498
+ for (let i = 0; i < args.length; i++) {
1499
+ if (args[i] === "--output-dir" && i + 1 < args.length) {
1500
+ outputDir = args[i + 1];
1501
+ i++;
1502
+ } else if (args[i] === "--help" || args[i] === "-h") {
1503
+ process.stdout.write(
1504
+ "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"
1505
+ );
1506
+ return;
1507
+ }
1508
+ }
1509
+ const result = await generateSigningKey({ outputDir });
1510
+ process.stdout.write(`Key generated successfully.
1511
+ `);
1512
+ process.stdout.write(` kid: ${result.kid}
1513
+ `);
1514
+ process.stdout.write(` key file: ${result.keyPath}
1515
+ `);
1516
+ process.stdout.write(` public x: ${result.publicJwk.x}
1517
+ `);
1518
+ process.stdout.write(`
1519
+ To use with the OpenClaw adapter:
1520
+ `);
1521
+ process.stdout.write(` signing.key_ref: "file:${path.resolve(result.keyPath)}"
1522
+ `);
1523
+ }
1270
1524
 
1271
- export { OPENCLAW_ERROR_CODES, OPENCLAW_EXTENSION_KEYS, OPENCLAW_WARNING_CODES, buildWorkflowContext, captureBatch, captureParallel, createBackgroundService, createExportBundleTool, createFileReceiptWriter, createHookHandler, createJwkSigner, createPluginInstance, createQueryTool, createReceiptEmitter, createSessionHistoryTailer, createStatusTool, createVerifyTool, extractWorkflowId, generateKeyId, mapToolCallEvent, mapToolCallEventBatch, resolveSigner };
1525
+ export { OPENCLAW_ERROR_CODES, OPENCLAW_EXTENSION_KEYS, OPENCLAW_WARNING_CODES, activate, buildWorkflowContext, captureBatch, captureParallel, createBackgroundService, createExportBundleTool, createFileReceiptWriter, createHookHandler, createJwkSigner, createPluginInstance, createQueryTool, createReceiptEmitter, createSessionHistoryTailer, createStatusTool, createVerifyTool, extractWorkflowId, generateKeyId, generateSigningKey, keygenCli, mapToolCallEvent, mapToolCallEventBatch, resolveSigner };
1272
1526
  //# sourceMappingURL=index.mjs.map
1273
1527
  //# sourceMappingURL=index.mjs.map