chainlesschain 0.66.0 → 0.132.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/bin/chainlesschain.js +0 -0
  2. package/package.json +1 -1
  3. package/src/commands/a2a.js +380 -0
  4. package/src/commands/agent-network.js +254 -1
  5. package/src/commands/audit.js +302 -0
  6. package/src/commands/automation.js +271 -1
  7. package/src/commands/bi.js +348 -0
  8. package/src/commands/codegen.js +224 -0
  9. package/src/commands/collab.js +341 -0
  10. package/src/commands/compliance.js +1035 -0
  11. package/src/commands/cowork.js +221 -0
  12. package/src/commands/crosschain.js +218 -0
  13. package/src/commands/dbevo.js +284 -0
  14. package/src/commands/dev.js +252 -0
  15. package/src/commands/did.js +358 -0
  16. package/src/commands/dlp.js +341 -0
  17. package/src/commands/encrypt.js +341 -0
  18. package/src/commands/evomap.js +394 -0
  19. package/src/commands/export.js +256 -1
  20. package/src/commands/federation.js +283 -0
  21. package/src/commands/fusion.js +258 -0
  22. package/src/commands/governance.js +325 -0
  23. package/src/commands/hardening.js +411 -0
  24. package/src/commands/hook.js +148 -0
  25. package/src/commands/import.js +252 -0
  26. package/src/commands/incentive.js +322 -0
  27. package/src/commands/inference.js +318 -0
  28. package/src/commands/infra.js +244 -0
  29. package/src/commands/instinct.js +260 -0
  30. package/src/commands/ipfs.js +318 -0
  31. package/src/commands/kg.js +387 -0
  32. package/src/commands/llm.js +263 -0
  33. package/src/commands/lowcode.js +356 -0
  34. package/src/commands/marketplace.js +256 -0
  35. package/src/commands/mcp.js +221 -0
  36. package/src/commands/memory.js +248 -0
  37. package/src/commands/multimodal.js +296 -0
  38. package/src/commands/nlprog.js +356 -0
  39. package/src/commands/note.js +244 -0
  40. package/src/commands/ops.js +354 -0
  41. package/src/commands/orchestrate.js +166 -0
  42. package/src/commands/org.js +277 -0
  43. package/src/commands/p2p.js +390 -0
  44. package/src/commands/perception.js +290 -0
  45. package/src/commands/permmem.js +251 -0
  46. package/src/commands/plugin-ecosystem.js +273 -0
  47. package/src/commands/pqc.js +393 -0
  48. package/src/commands/privacy.js +321 -0
  49. package/src/commands/quantization.js +351 -0
  50. package/src/commands/rcache.js +271 -0
  51. package/src/commands/recommend.js +340 -0
  52. package/src/commands/reputation.js +261 -0
  53. package/src/commands/runtime.js +307 -0
  54. package/src/commands/scim.js +262 -0
  55. package/src/commands/session.js +258 -0
  56. package/src/commands/siem.js +246 -0
  57. package/src/commands/skill.js +267 -1
  58. package/src/commands/sla.js +259 -0
  59. package/src/commands/social.js +256 -0
  60. package/src/commands/sso.js +186 -1
  61. package/src/commands/stress.js +230 -0
  62. package/src/commands/sync.js +256 -0
  63. package/src/commands/tech.js +338 -0
  64. package/src/commands/tenant.js +351 -0
  65. package/src/commands/terraform.js +245 -0
  66. package/src/commands/tokens.js +269 -0
  67. package/src/commands/trust.js +249 -0
  68. package/src/commands/wallet.js +277 -0
  69. package/src/commands/workflow.js +171 -0
  70. package/src/commands/zkp.js +335 -0
  71. package/src/index.js +4 -0
  72. package/src/lib/a2a-protocol.js +451 -0
  73. package/src/lib/agent-coordinator.js +325 -0
  74. package/src/lib/agent-network.js +387 -0
  75. package/src/lib/agent-router.js +395 -0
  76. package/src/lib/aiops.js +478 -0
  77. package/src/lib/app-builder.js +239 -0
  78. package/src/lib/audit-logger.js +379 -0
  79. package/src/lib/automation-engine.js +330 -0
  80. package/src/lib/autonomous-developer.js +350 -0
  81. package/src/lib/bi-engine.js +338 -0
  82. package/src/lib/code-agent.js +323 -0
  83. package/src/lib/collaboration-governance.js +364 -0
  84. package/src/lib/community-governance.js +436 -0
  85. package/src/lib/compliance-manager.js +434 -0
  86. package/src/lib/content-recommendation.js +469 -0
  87. package/src/lib/cross-chain.js +345 -0
  88. package/src/lib/crypto-manager.js +350 -0
  89. package/src/lib/dbevo.js +338 -0
  90. package/src/lib/decentral-infra.js +340 -0
  91. package/src/lib/did-manager.js +367 -0
  92. package/src/lib/dlp-engine.js +389 -0
  93. package/src/lib/evomap-federation.js +177 -0
  94. package/src/lib/evomap-governance.js +276 -0
  95. package/src/lib/federation-hardening.js +259 -0
  96. package/src/lib/hardening-manager.js +348 -0
  97. package/src/lib/hook-manager.js +380 -0
  98. package/src/lib/inference-network.js +330 -0
  99. package/src/lib/instinct-manager.js +332 -0
  100. package/src/lib/ipfs-storage.js +334 -0
  101. package/src/lib/knowledge-exporter.js +381 -0
  102. package/src/lib/knowledge-graph.js +432 -0
  103. package/src/lib/knowledge-importer.js +379 -0
  104. package/src/lib/llm-providers.js +391 -0
  105. package/src/lib/mcp-registry.js +333 -0
  106. package/src/lib/memory-manager.js +330 -0
  107. package/src/lib/multimodal.js +346 -0
  108. package/src/lib/nl-programming.js +343 -0
  109. package/src/lib/note-versioning.js +327 -0
  110. package/src/lib/org-manager.js +323 -0
  111. package/src/lib/p2p-manager.js +387 -0
  112. package/src/lib/perception.js +346 -0
  113. package/src/lib/perf-tuning.js +4 -1
  114. package/src/lib/permanent-memory.js +320 -0
  115. package/src/lib/plugin-ecosystem.js +377 -0
  116. package/src/lib/pqc-manager.js +368 -0
  117. package/src/lib/privacy-computing.js +427 -0
  118. package/src/lib/protocol-fusion.js +417 -0
  119. package/src/lib/quantization.js +325 -0
  120. package/src/lib/reputation-optimizer.js +299 -0
  121. package/src/lib/response-cache.js +327 -0
  122. package/src/lib/scim-manager.js +329 -0
  123. package/src/lib/session-manager.js +329 -0
  124. package/src/lib/siem-exporter.js +333 -0
  125. package/src/lib/skill-loader.js +377 -0
  126. package/src/lib/skill-marketplace.js +325 -0
  127. package/src/lib/sla-manager.js +275 -0
  128. package/src/lib/social-manager.js +326 -0
  129. package/src/lib/sso-manager.js +332 -0
  130. package/src/lib/stress-tester.js +330 -0
  131. package/src/lib/sync-manager.js +326 -0
  132. package/src/lib/tech-learning-engine.js +369 -0
  133. package/src/lib/tenant-saas.js +460 -0
  134. package/src/lib/terraform-manager.js +363 -0
  135. package/src/lib/threat-intel.js +335 -0
  136. package/src/lib/token-incentive.js +293 -0
  137. package/src/lib/token-tracker.js +329 -0
  138. package/src/lib/trust-security.js +390 -0
  139. package/src/lib/ueba.js +389 -0
  140. package/src/lib/universal-runtime.js +325 -0
  141. package/src/lib/wallet-manager.js +326 -0
  142. package/src/lib/workflow-engine.js +322 -0
  143. package/src/lib/zkp-engine.js +274 -0
@@ -666,4 +666,349 @@ export function _resetState() {
666
666
  _swaps.clear();
667
667
  _messages.clear();
668
668
  _chainConfigs.clear();
669
+ _maxActiveBridgesPerAddress = DEFAULT_MAX_ACTIVE_BRIDGES_PER_ADDRESS;
670
+ }
671
+
672
+ /* ══════════════════════════════════════════════════════════
673
+ * Phase 89 — Cross-Chain V2 surface (strictly additive)
674
+ *
675
+ * Adds canonical enums, per-address bridge concurrency cap,
676
+ * chain config CRUD, patch-merged state-machine setters,
677
+ * auto-expire swaps, and all-enum-key stats V2.
678
+ * ══════════════════════════════════════════════════════════ */
679
+
680
+ /* ── V2 Frozen Enums ────────────────────────────────────── */
681
+
682
+ export const BRIDGE_STATUS_V2 = Object.freeze({
683
+ PENDING: "pending",
684
+ LOCKED: "locked",
685
+ MINTED: "minted",
686
+ COMPLETED: "completed",
687
+ REFUNDED: "refunded",
688
+ FAILED: "failed",
689
+ });
690
+
691
+ export const SWAP_STATUS_V2 = Object.freeze({
692
+ INITIATED: "initiated",
693
+ HASH_LOCKED: "hash_locked",
694
+ CLAIMED: "claimed",
695
+ REFUNDED: "refunded",
696
+ EXPIRED: "expired",
697
+ });
698
+
699
+ export const MESSAGE_STATUS_V2 = Object.freeze({
700
+ PENDING: "pending",
701
+ SENT: "sent",
702
+ DELIVERED: "delivered",
703
+ FAILED: "failed",
704
+ });
705
+
706
+ export const CHAIN_ID_V2 = Object.freeze({
707
+ ETHEREUM: "ethereum",
708
+ POLYGON: "polygon",
709
+ BSC: "bsc",
710
+ ARBITRUM: "arbitrum",
711
+ SOLANA: "solana",
712
+ });
713
+
714
+ const DEFAULT_MAX_ACTIVE_BRIDGES_PER_ADDRESS = 3;
715
+ export const CROSSCHAIN_DEFAULT_MAX_ACTIVE_BRIDGES_PER_ADDRESS =
716
+ DEFAULT_MAX_ACTIVE_BRIDGES_PER_ADDRESS;
717
+
718
+ let _maxActiveBridgesPerAddress = DEFAULT_MAX_ACTIVE_BRIDGES_PER_ADDRESS;
719
+
720
+ /* ── State Machine Definitions ──────────────────────────── */
721
+
722
+ const BRIDGE_TRANSITIONS_V2 = new Map([
723
+ ["pending", new Set(["locked", "failed"])],
724
+ ["locked", new Set(["minted", "refunded", "failed"])],
725
+ ["minted", new Set(["completed", "failed"])],
726
+ ]);
727
+ const BRIDGE_TERMINALS_V2 = new Set(["completed", "refunded", "failed"]);
728
+
729
+ const SWAP_TRANSITIONS_V2 = new Map([
730
+ ["initiated", new Set(["hash_locked", "claimed", "refunded", "expired"])],
731
+ ["hash_locked", new Set(["claimed", "refunded", "expired"])],
732
+ ]);
733
+ const SWAP_TERMINALS_V2 = new Set(["claimed", "refunded", "expired"]);
734
+
735
+ const MESSAGE_TRANSITIONS_V2 = new Map([
736
+ ["pending", new Set(["sent", "failed"])],
737
+ ["sent", new Set(["delivered", "failed"])],
738
+ ["failed", new Set(["pending"])],
739
+ ]);
740
+ const MESSAGE_TERMINALS_V2 = new Set(["delivered"]);
741
+
742
+ /* ── Concurrency Cap ───────────────────────────────────── */
743
+
744
+ export function setMaxActiveBridgesPerAddress(n) {
745
+ if (typeof n !== "number" || Number.isNaN(n) || n < 1) {
746
+ throw new Error("Max active bridges must be a positive integer");
747
+ }
748
+ _maxActiveBridgesPerAddress = Math.floor(n);
749
+ }
750
+
751
+ export function getMaxActiveBridgesPerAddress() {
752
+ return _maxActiveBridgesPerAddress;
753
+ }
754
+
755
+ export function getActiveBridgeCount(address) {
756
+ let count = 0;
757
+ for (const b of _bridges.values()) {
758
+ if (BRIDGE_TERMINALS_V2.has(b.status)) continue;
759
+ if (address == null || b.sender_address === address) count += 1;
760
+ }
761
+ return count;
762
+ }
763
+
764
+ /* ── Chain Config V2 ───────────────────────────────────── */
765
+
766
+ export function configureChainV2({
767
+ chainId,
768
+ rpcUrl,
769
+ contractAddress,
770
+ enabled = true,
771
+ }) {
772
+ if (!_validateChain(chainId)) {
773
+ throw new Error(`Unsupported chain: ${chainId}`);
774
+ }
775
+ const cfg = {
776
+ chainId,
777
+ rpcUrl: rpcUrl || null,
778
+ contractAddress: contractAddress || null,
779
+ enabled: enabled !== false,
780
+ updatedAt: _now(),
781
+ };
782
+ _chainConfigs.set(chainId, cfg);
783
+ return { ...cfg };
784
+ }
785
+
786
+ export function getChainConfigV2(chainId) {
787
+ const cfg = _chainConfigs.get(chainId);
788
+ return cfg ? { ...cfg } : null;
789
+ }
790
+
791
+ export function listChainsV2() {
792
+ return Object.values(SUPPORTED_CHAINS).map((chain) => {
793
+ const cfg = _chainConfigs.get(chain.id);
794
+ return {
795
+ ...chain,
796
+ enabled: cfg ? cfg.enabled : false,
797
+ rpcUrl: cfg ? cfg.rpcUrl : null,
798
+ contractAddress: cfg ? cfg.contractAddress : null,
799
+ };
800
+ });
801
+ }
802
+
803
+ /* ── Bridge V2 (throws + cap enforcement) ──────────────── */
804
+
805
+ export function bridgeAssetV2(
806
+ db,
807
+ { fromChain, toChain, asset, amount, senderAddress, recipientAddress },
808
+ ) {
809
+ if (!_validateChain(fromChain)) {
810
+ throw new Error(`Unsupported source chain: ${fromChain}`);
811
+ }
812
+ if (!_validateChain(toChain)) {
813
+ throw new Error(`Unsupported destination chain: ${toChain}`);
814
+ }
815
+ if (fromChain === toChain) {
816
+ throw new Error("Source and destination chains must differ");
817
+ }
818
+ if (!amount || amount <= 0) {
819
+ throw new Error("Amount must be positive");
820
+ }
821
+ if (amount > DEFAULT_CONFIG.maxBridgeAmount) {
822
+ throw new Error(
823
+ `Amount ${amount} exceeds max ${DEFAULT_CONFIG.maxBridgeAmount}`,
824
+ );
825
+ }
826
+ if (senderAddress) {
827
+ const active = getActiveBridgeCount(senderAddress);
828
+ if (active >= _maxActiveBridgesPerAddress) {
829
+ throw new Error(
830
+ `Max active bridges per address reached (${active}/${_maxActiveBridgesPerAddress})`,
831
+ );
832
+ }
833
+ }
834
+
835
+ const result = bridgeAsset(db, {
836
+ fromChain,
837
+ toChain,
838
+ asset,
839
+ amount,
840
+ senderAddress,
841
+ recipientAddress,
842
+ });
843
+ return result;
844
+ }
845
+
846
+ /* ── Generic patch-merged state setters ────────────────── */
847
+
848
+ export function setBridgeStatusV2(db, bridgeId, newStatus, patch = {}) {
849
+ const b = _bridges.get(bridgeId);
850
+ if (!b) throw new Error(`Bridge not found: ${bridgeId}`);
851
+
852
+ const allowed = BRIDGE_TRANSITIONS_V2.get(b.status);
853
+ if (!allowed || !allowed.has(newStatus)) {
854
+ throw new Error(`Invalid bridge transition: ${b.status} → ${newStatus}`);
855
+ }
856
+ if (!Object.values(BRIDGE_STATUS_V2).includes(newStatus)) {
857
+ throw new Error(`Unknown bridge status: ${newStatus}`);
858
+ }
859
+
860
+ b.status = newStatus;
861
+ if (patch.lockTxHash !== undefined) b.lock_tx_hash = patch.lockTxHash;
862
+ if (patch.mintTxHash !== undefined) b.mint_tx_hash = patch.mintTxHash;
863
+ if (patch.errorMessage !== undefined) b.error_message = patch.errorMessage;
864
+ if (BRIDGE_TERMINALS_V2.has(newStatus) && !b.completed_at) {
865
+ b.completed_at = _now();
866
+ }
867
+
868
+ db.prepare(
869
+ `UPDATE cc_bridges SET status = ?, lock_tx_hash = ?, mint_tx_hash = ?,
870
+ completed_at = ?, error_message = ? WHERE id = ?`,
871
+ ).run(
872
+ b.status,
873
+ b.lock_tx_hash,
874
+ b.mint_tx_hash,
875
+ b.completed_at,
876
+ b.error_message,
877
+ bridgeId,
878
+ );
879
+
880
+ return { ...b };
881
+ }
882
+
883
+ export function setSwapStatusV2(db, swapId, newStatus, patch = {}) {
884
+ const s = _swaps.get(swapId);
885
+ if (!s) throw new Error(`Swap not found: ${swapId}`);
886
+
887
+ const allowed = SWAP_TRANSITIONS_V2.get(s.status);
888
+ if (!allowed || !allowed.has(newStatus)) {
889
+ throw new Error(`Invalid swap transition: ${s.status} → ${newStatus}`);
890
+ }
891
+ if (!Object.values(SWAP_STATUS_V2).includes(newStatus)) {
892
+ throw new Error(`Unknown swap status: ${newStatus}`);
893
+ }
894
+
895
+ s.status = newStatus;
896
+ if (patch.claimTxHash !== undefined) s.claim_tx_hash = patch.claimTxHash;
897
+ if (patch.refundTxHash !== undefined) s.refund_tx_hash = patch.refundTxHash;
898
+
899
+ db.prepare(
900
+ `UPDATE cc_swaps SET status = ?, claim_tx_hash = ?, refund_tx_hash = ?
901
+ WHERE id = ?`,
902
+ ).run(s.status, s.claim_tx_hash, s.refund_tx_hash, swapId);
903
+
904
+ const out = { ...s };
905
+ delete out.secret;
906
+ return out;
907
+ }
908
+
909
+ export function setMessageStatusV2(db, messageId, newStatus, patch = {}) {
910
+ const m = _messages.get(messageId);
911
+ if (!m) throw new Error(`Message not found: ${messageId}`);
912
+
913
+ const allowed = MESSAGE_TRANSITIONS_V2.get(m.status);
914
+ if (!allowed || !allowed.has(newStatus)) {
915
+ throw new Error(`Invalid message transition: ${m.status} → ${newStatus}`);
916
+ }
917
+ if (!Object.values(MESSAGE_STATUS_V2).includes(newStatus)) {
918
+ throw new Error(`Unknown message status: ${newStatus}`);
919
+ }
920
+
921
+ m.status = newStatus;
922
+ if (patch.sourceTxHash !== undefined) m.source_tx_hash = patch.sourceTxHash;
923
+ if (patch.destinationTxHash !== undefined)
924
+ m.destination_tx_hash = patch.destinationTxHash;
925
+ if (newStatus === "delivered" && !m.delivered_at) m.delivered_at = _now();
926
+ if (newStatus === "pending") m.retries += 1;
927
+
928
+ db.prepare(
929
+ `UPDATE cc_messages SET status = ?, source_tx_hash = ?, destination_tx_hash = ?,
930
+ delivered_at = ?, retries = ? WHERE id = ?`,
931
+ ).run(
932
+ m.status,
933
+ m.source_tx_hash,
934
+ m.destination_tx_hash,
935
+ m.delivered_at,
936
+ m.retries,
937
+ messageId,
938
+ );
939
+
940
+ return { ...m };
941
+ }
942
+
943
+ /* ── Auto-expire swaps ─────────────────────────────────── */
944
+
945
+ export function autoExpireSwapsV2(db) {
946
+ const now = _now();
947
+ const expired = [];
948
+ for (const s of _swaps.values()) {
949
+ if (s.status !== "initiated" && s.status !== "hash_locked") continue;
950
+ if (s.expires_at > now) continue;
951
+
952
+ s.status = "expired";
953
+ db.prepare("UPDATE cc_swaps SET status = ? WHERE id = ?").run(
954
+ "expired",
955
+ s.id,
956
+ );
957
+ const out = { ...s };
958
+ delete out.secret;
959
+ expired.push(out);
960
+ }
961
+ return expired;
962
+ }
963
+
964
+ /* ── Stats V2 ──────────────────────────────────────────── */
965
+
966
+ export function getCrossChainStatsV2() {
967
+ const bridgesByStatus = {};
968
+ for (const v of Object.values(BRIDGE_STATUS_V2)) bridgesByStatus[v] = 0;
969
+ const swapsByStatus = {};
970
+ for (const v of Object.values(SWAP_STATUS_V2)) swapsByStatus[v] = 0;
971
+ const messagesByStatus = {};
972
+ for (const v of Object.values(MESSAGE_STATUS_V2)) messagesByStatus[v] = 0;
973
+ const chainUsage = {};
974
+ for (const v of Object.values(CHAIN_ID_V2)) chainUsage[v] = 0;
975
+
976
+ let totalBridgeVolume = 0;
977
+ let totalFees = 0;
978
+ let activeBridges = 0;
979
+ for (const b of _bridges.values()) {
980
+ bridgesByStatus[b.status] = (bridgesByStatus[b.status] || 0) + 1;
981
+ chainUsage[b.from_chain] = (chainUsage[b.from_chain] || 0) + 1;
982
+ chainUsage[b.to_chain] = (chainUsage[b.to_chain] || 0) + 1;
983
+ totalBridgeVolume += b.amount;
984
+ totalFees += b.fee_amount;
985
+ if (!BRIDGE_TERMINALS_V2.has(b.status)) activeBridges += 1;
986
+ }
987
+
988
+ for (const s of _swaps.values()) {
989
+ swapsByStatus[s.status] = (swapsByStatus[s.status] || 0) + 1;
990
+ chainUsage[s.from_chain] = (chainUsage[s.from_chain] || 0) + 1;
991
+ chainUsage[s.to_chain] = (chainUsage[s.to_chain] || 0) + 1;
992
+ }
993
+
994
+ for (const m of _messages.values()) {
995
+ messagesByStatus[m.status] = (messagesByStatus[m.status] || 0) + 1;
996
+ chainUsage[m.from_chain] = (chainUsage[m.from_chain] || 0) + 1;
997
+ chainUsage[m.to_chain] = (chainUsage[m.to_chain] || 0) + 1;
998
+ }
999
+
1000
+ return {
1001
+ totalBridges: _bridges.size,
1002
+ totalSwaps: _swaps.size,
1003
+ totalMessages: _messages.size,
1004
+ activeBridges,
1005
+ maxActiveBridgesPerAddress: _maxActiveBridgesPerAddress,
1006
+ bridgesByStatus,
1007
+ swapsByStatus,
1008
+ messagesByStatus,
1009
+ chainUsage,
1010
+ totalBridgeVolume,
1011
+ totalFees: Math.round(totalFees * 1000) / 1000,
1012
+ configuredChains: _chainConfigs.size,
1013
+ };
669
1014
  }
@@ -244,3 +244,353 @@ export function getEncryptedFileInfo(filePath) {
244
244
  modified: stats.mtime.toISOString(),
245
245
  };
246
246
  }
247
+
248
+ /* ─────────────────────────────────────────────────────────────────────────
249
+ * V2 in-memory governance layer (independent of file/buffer crypto helpers)
250
+ * ───────────────────────────────────────────────────────────────────────── */
251
+
252
+ export const KEY_MATURITY_V2 = Object.freeze({
253
+ PENDING: "pending",
254
+ ACTIVE: "active",
255
+ ROTATED: "rotated",
256
+ RETIRED: "retired",
257
+ });
258
+
259
+ export const CRYPTO_JOB_LIFECYCLE_V2 = Object.freeze({
260
+ QUEUED: "queued",
261
+ RUNNING: "running",
262
+ COMPLETED: "completed",
263
+ FAILED: "failed",
264
+ CANCELLED: "cancelled",
265
+ });
266
+
267
+ const KEY_TERMINAL_V2 = new Set([KEY_MATURITY_V2.RETIRED]);
268
+ const JOB_TERMINAL_V2 = new Set([
269
+ CRYPTO_JOB_LIFECYCLE_V2.COMPLETED,
270
+ CRYPTO_JOB_LIFECYCLE_V2.FAILED,
271
+ CRYPTO_JOB_LIFECYCLE_V2.CANCELLED,
272
+ ]);
273
+
274
+ const KEY_TRANSITIONS_V2 = new Map([
275
+ [
276
+ KEY_MATURITY_V2.PENDING,
277
+ new Set([KEY_MATURITY_V2.ACTIVE, KEY_MATURITY_V2.RETIRED]),
278
+ ],
279
+ [
280
+ KEY_MATURITY_V2.ACTIVE,
281
+ new Set([KEY_MATURITY_V2.ROTATED, KEY_MATURITY_V2.RETIRED]),
282
+ ],
283
+ [
284
+ KEY_MATURITY_V2.ROTATED,
285
+ new Set([KEY_MATURITY_V2.ACTIVE, KEY_MATURITY_V2.RETIRED]),
286
+ ],
287
+ ]);
288
+
289
+ const JOB_TRANSITIONS_V2 = new Map([
290
+ [
291
+ CRYPTO_JOB_LIFECYCLE_V2.QUEUED,
292
+ new Set([
293
+ CRYPTO_JOB_LIFECYCLE_V2.RUNNING,
294
+ CRYPTO_JOB_LIFECYCLE_V2.CANCELLED,
295
+ ]),
296
+ ],
297
+ [
298
+ CRYPTO_JOB_LIFECYCLE_V2.RUNNING,
299
+ new Set([
300
+ CRYPTO_JOB_LIFECYCLE_V2.COMPLETED,
301
+ CRYPTO_JOB_LIFECYCLE_V2.FAILED,
302
+ CRYPTO_JOB_LIFECYCLE_V2.CANCELLED,
303
+ ]),
304
+ ],
305
+ ]);
306
+
307
+ export const CRYPTO_DEFAULT_MAX_ACTIVE_KEYS_PER_OWNER = 12;
308
+ export const CRYPTO_DEFAULT_MAX_PENDING_JOBS_PER_KEY = 16;
309
+ export const CRYPTO_DEFAULT_KEY_IDLE_MS = 14 * 24 * 60 * 60 * 1000;
310
+ export const CRYPTO_DEFAULT_JOB_STUCK_MS = 5 * 60 * 1000;
311
+
312
+ let _maxActiveKeysPerOwnerV2 = CRYPTO_DEFAULT_MAX_ACTIVE_KEYS_PER_OWNER;
313
+ let _maxPendingJobsPerKeyV2 = CRYPTO_DEFAULT_MAX_PENDING_JOBS_PER_KEY;
314
+ let _keyIdleMsV2 = CRYPTO_DEFAULT_KEY_IDLE_MS;
315
+ let _jobStuckMsV2 = CRYPTO_DEFAULT_JOB_STUCK_MS;
316
+
317
+ const _keysV2 = new Map();
318
+ const _jobsV2 = new Map();
319
+
320
+ function _posIntCryptoV2(n, label) {
321
+ if (typeof n !== "number" || !Number.isFinite(n) || n <= 0) {
322
+ throw new Error(`${label} must be a positive number`);
323
+ }
324
+ return Math.floor(n);
325
+ }
326
+
327
+ export function getMaxActiveKeysPerOwnerV2() {
328
+ return _maxActiveKeysPerOwnerV2;
329
+ }
330
+ export function setMaxActiveKeysPerOwnerV2(n) {
331
+ _maxActiveKeysPerOwnerV2 = _posIntCryptoV2(n, "maxActiveKeysPerOwner");
332
+ return _maxActiveKeysPerOwnerV2;
333
+ }
334
+ export function getMaxPendingJobsPerKeyV2() {
335
+ return _maxPendingJobsPerKeyV2;
336
+ }
337
+ export function setMaxPendingJobsPerKeyV2(n) {
338
+ _maxPendingJobsPerKeyV2 = _posIntCryptoV2(n, "maxPendingJobsPerKey");
339
+ return _maxPendingJobsPerKeyV2;
340
+ }
341
+ export function getKeyIdleMsV2() {
342
+ return _keyIdleMsV2;
343
+ }
344
+ export function setKeyIdleMsV2(ms) {
345
+ _keyIdleMsV2 = _posIntCryptoV2(ms, "keyIdleMs");
346
+ return _keyIdleMsV2;
347
+ }
348
+ export function getJobStuckMsV2() {
349
+ return _jobStuckMsV2;
350
+ }
351
+ export function setJobStuckMsV2(ms) {
352
+ _jobStuckMsV2 = _posIntCryptoV2(ms, "jobStuckMs");
353
+ return _jobStuckMsV2;
354
+ }
355
+
356
+ export function getActiveKeyCountV2(ownerId) {
357
+ let n = 0;
358
+ for (const k of _keysV2.values()) {
359
+ if (k.status !== KEY_MATURITY_V2.ACTIVE) continue;
360
+ if (ownerId && k.ownerId !== ownerId) continue;
361
+ n++;
362
+ }
363
+ return n;
364
+ }
365
+
366
+ export function getPendingJobCountV2(keyId) {
367
+ let n = 0;
368
+ for (const j of _jobsV2.values()) {
369
+ if (
370
+ j.status !== CRYPTO_JOB_LIFECYCLE_V2.QUEUED &&
371
+ j.status !== CRYPTO_JOB_LIFECYCLE_V2.RUNNING
372
+ )
373
+ continue;
374
+ if (keyId && j.keyId !== keyId) continue;
375
+ n++;
376
+ }
377
+ return n;
378
+ }
379
+
380
+ function _cloneKeyCryptoV2(k) {
381
+ return { ...k, metadata: { ...k.metadata } };
382
+ }
383
+ function _cloneJobCryptoV2(j) {
384
+ return { ...j, metadata: { ...j.metadata } };
385
+ }
386
+
387
+ export function registerKeyV2(
388
+ id,
389
+ { ownerId, algorithm, purpose, metadata } = {},
390
+ ) {
391
+ if (!id || typeof id !== "string") throw new Error("key id required");
392
+ if (!ownerId || typeof ownerId !== "string")
393
+ throw new Error("ownerId required");
394
+ if (!algorithm || typeof algorithm !== "string")
395
+ throw new Error("algorithm required");
396
+ if (_keysV2.has(id)) throw new Error(`key ${id} already exists`);
397
+ const now = Date.now();
398
+ const key = {
399
+ id,
400
+ ownerId,
401
+ algorithm,
402
+ purpose: purpose || "encryption",
403
+ status: KEY_MATURITY_V2.PENDING,
404
+ createdAt: now,
405
+ activatedAt: null,
406
+ retiredAt: null,
407
+ lastSeenAt: now,
408
+ metadata: metadata ? { ...metadata } : {},
409
+ };
410
+ _keysV2.set(id, key);
411
+ return _cloneKeyCryptoV2(key);
412
+ }
413
+
414
+ export function getKeyV2(id) {
415
+ const k = _keysV2.get(id);
416
+ return k ? _cloneKeyCryptoV2(k) : null;
417
+ }
418
+
419
+ export function listKeysV2({ ownerId, status, algorithm } = {}) {
420
+ const out = [];
421
+ for (const k of _keysV2.values()) {
422
+ if (ownerId && k.ownerId !== ownerId) continue;
423
+ if (status && k.status !== status) continue;
424
+ if (algorithm && k.algorithm !== algorithm) continue;
425
+ out.push(_cloneKeyCryptoV2(k));
426
+ }
427
+ return out;
428
+ }
429
+
430
+ export function setKeyStatusV2(id, next) {
431
+ const k = _keysV2.get(id);
432
+ if (!k) throw new Error(`unknown key ${id}`);
433
+ if (KEY_TERMINAL_V2.has(k.status)) {
434
+ throw new Error(`key ${id} is terminal (${k.status})`);
435
+ }
436
+ const allowed = KEY_TRANSITIONS_V2.get(k.status);
437
+ if (!allowed || !allowed.has(next)) {
438
+ throw new Error(`invalid key transition ${k.status} -> ${next}`);
439
+ }
440
+ if (next === KEY_MATURITY_V2.ACTIVE && k.status === KEY_MATURITY_V2.PENDING) {
441
+ const owned = getActiveKeyCountV2(k.ownerId);
442
+ if (owned >= _maxActiveKeysPerOwnerV2) {
443
+ throw new Error(
444
+ `owner ${k.ownerId} active key cap reached (${_maxActiveKeysPerOwnerV2})`,
445
+ );
446
+ }
447
+ }
448
+ const now = Date.now();
449
+ k.status = next;
450
+ if (next === KEY_MATURITY_V2.ACTIVE && !k.activatedAt) k.activatedAt = now;
451
+ if (next === KEY_MATURITY_V2.RETIRED && !k.retiredAt) k.retiredAt = now;
452
+ k.lastSeenAt = now;
453
+ return _cloneKeyCryptoV2(k);
454
+ }
455
+
456
+ export function activateKeyV2(id) {
457
+ return setKeyStatusV2(id, KEY_MATURITY_V2.ACTIVE);
458
+ }
459
+ export function rotateKeyV2(id) {
460
+ return setKeyStatusV2(id, KEY_MATURITY_V2.ROTATED);
461
+ }
462
+ export function retireKeyV2(id) {
463
+ return setKeyStatusV2(id, KEY_MATURITY_V2.RETIRED);
464
+ }
465
+
466
+ export function touchKeyV2(id) {
467
+ const k = _keysV2.get(id);
468
+ if (!k) throw new Error(`unknown key ${id}`);
469
+ k.lastSeenAt = Date.now();
470
+ return _cloneKeyCryptoV2(k);
471
+ }
472
+
473
+ export function createJobV2(id, { keyId, kind, metadata } = {}) {
474
+ if (!id || typeof id !== "string") throw new Error("job id required");
475
+ if (!keyId || typeof keyId !== "string") throw new Error("keyId required");
476
+ if (_jobsV2.has(id)) throw new Error(`job ${id} already exists`);
477
+ if (!_keysV2.has(keyId)) throw new Error(`unknown key ${keyId}`);
478
+ const pending = getPendingJobCountV2(keyId);
479
+ if (pending >= _maxPendingJobsPerKeyV2) {
480
+ throw new Error(
481
+ `key ${keyId} pending job cap reached (${_maxPendingJobsPerKeyV2})`,
482
+ );
483
+ }
484
+ const now = Date.now();
485
+ const job = {
486
+ id,
487
+ keyId,
488
+ kind: kind || "encrypt",
489
+ status: CRYPTO_JOB_LIFECYCLE_V2.QUEUED,
490
+ createdAt: now,
491
+ startedAt: null,
492
+ settledAt: null,
493
+ metadata: metadata ? { ...metadata } : {},
494
+ };
495
+ _jobsV2.set(id, job);
496
+ return _cloneJobCryptoV2(job);
497
+ }
498
+
499
+ export function getJobV2(id) {
500
+ const j = _jobsV2.get(id);
501
+ return j ? _cloneJobCryptoV2(j) : null;
502
+ }
503
+
504
+ export function listJobsV2({ keyId, status, kind } = {}) {
505
+ const out = [];
506
+ for (const j of _jobsV2.values()) {
507
+ if (keyId && j.keyId !== keyId) continue;
508
+ if (status && j.status !== status) continue;
509
+ if (kind && j.kind !== kind) continue;
510
+ out.push(_cloneJobCryptoV2(j));
511
+ }
512
+ return out;
513
+ }
514
+
515
+ export function setJobStatusV2(id, next) {
516
+ const j = _jobsV2.get(id);
517
+ if (!j) throw new Error(`unknown job ${id}`);
518
+ if (JOB_TERMINAL_V2.has(j.status)) {
519
+ throw new Error(`job ${id} is terminal (${j.status})`);
520
+ }
521
+ const allowed = JOB_TRANSITIONS_V2.get(j.status);
522
+ if (!allowed || !allowed.has(next)) {
523
+ throw new Error(`invalid job transition ${j.status} -> ${next}`);
524
+ }
525
+ const now = Date.now();
526
+ j.status = next;
527
+ if (next === CRYPTO_JOB_LIFECYCLE_V2.RUNNING && !j.startedAt)
528
+ j.startedAt = now;
529
+ if (JOB_TERMINAL_V2.has(next) && !j.settledAt) j.settledAt = now;
530
+ return _cloneJobCryptoV2(j);
531
+ }
532
+
533
+ export function startJobV2(id) {
534
+ return setJobStatusV2(id, CRYPTO_JOB_LIFECYCLE_V2.RUNNING);
535
+ }
536
+ export function completeJobV2(id) {
537
+ return setJobStatusV2(id, CRYPTO_JOB_LIFECYCLE_V2.COMPLETED);
538
+ }
539
+ export function failJobV2(id) {
540
+ return setJobStatusV2(id, CRYPTO_JOB_LIFECYCLE_V2.FAILED);
541
+ }
542
+ export function cancelJobV2(id) {
543
+ return setJobStatusV2(id, CRYPTO_JOB_LIFECYCLE_V2.CANCELLED);
544
+ }
545
+
546
+ export function autoRotateIdleKeysV2({ now = Date.now() } = {}) {
547
+ const out = [];
548
+ for (const k of _keysV2.values()) {
549
+ if (k.status !== KEY_MATURITY_V2.ACTIVE) continue;
550
+ if (now - k.lastSeenAt < _keyIdleMsV2) continue;
551
+ k.status = KEY_MATURITY_V2.ROTATED;
552
+ k.lastSeenAt = now;
553
+ out.push(_cloneKeyCryptoV2(k));
554
+ }
555
+ return out;
556
+ }
557
+
558
+ export function autoFailStuckJobsV2({ now = Date.now() } = {}) {
559
+ const out = [];
560
+ for (const j of _jobsV2.values()) {
561
+ if (j.status !== CRYPTO_JOB_LIFECYCLE_V2.RUNNING) continue;
562
+ if (!j.startedAt || now - j.startedAt < _jobStuckMsV2) continue;
563
+ j.status = CRYPTO_JOB_LIFECYCLE_V2.FAILED;
564
+ j.settledAt = now;
565
+ out.push(_cloneJobCryptoV2(j));
566
+ }
567
+ return out;
568
+ }
569
+
570
+ export function getCryptoManagerStatsV2() {
571
+ const keysByStatus = {};
572
+ for (const s of Object.values(KEY_MATURITY_V2)) keysByStatus[s] = 0;
573
+ for (const k of _keysV2.values()) keysByStatus[k.status]++;
574
+ const jobsByStatus = {};
575
+ for (const s of Object.values(CRYPTO_JOB_LIFECYCLE_V2)) jobsByStatus[s] = 0;
576
+ for (const j of _jobsV2.values()) jobsByStatus[j.status]++;
577
+ return {
578
+ totalKeysV2: _keysV2.size,
579
+ totalJobsV2: _jobsV2.size,
580
+ maxActiveKeysPerOwner: _maxActiveKeysPerOwnerV2,
581
+ maxPendingJobsPerKey: _maxPendingJobsPerKeyV2,
582
+ keyIdleMs: _keyIdleMsV2,
583
+ jobStuckMs: _jobStuckMsV2,
584
+ keysByStatus,
585
+ jobsByStatus,
586
+ };
587
+ }
588
+
589
+ export function _resetStateCryptoManagerV2() {
590
+ _keysV2.clear();
591
+ _jobsV2.clear();
592
+ _maxActiveKeysPerOwnerV2 = CRYPTO_DEFAULT_MAX_ACTIVE_KEYS_PER_OWNER;
593
+ _maxPendingJobsPerKeyV2 = CRYPTO_DEFAULT_MAX_PENDING_JOBS_PER_KEY;
594
+ _keyIdleMsV2 = CRYPTO_DEFAULT_KEY_IDLE_MS;
595
+ _jobStuckMsV2 = CRYPTO_DEFAULT_JOB_STUCK_MS;
596
+ }