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