chainlesschain 0.81.0 → 0.143.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 (209) hide show
  1. package/bin/chainlesschain.js +0 -0
  2. package/package.json +1 -1
  3. package/src/commands/a2a.js +62 -0
  4. package/src/commands/activitypub.js +61 -0
  5. package/src/commands/agent-network.js +254 -1
  6. package/src/commands/agent.js +117 -0
  7. package/src/commands/audit.js +302 -0
  8. package/src/commands/automation.js +271 -1
  9. package/src/commands/bi.js +61 -0
  10. package/src/commands/bm25.js +78 -0
  11. package/src/commands/browse.js +64 -0
  12. package/src/commands/ccron.js +78 -0
  13. package/src/commands/codegen.js +224 -0
  14. package/src/commands/collab.js +341 -0
  15. package/src/commands/compliance.js +1075 -0
  16. package/src/commands/compt.js +78 -0
  17. package/src/commands/consol.js +231 -0
  18. package/src/commands/cowork.js +263 -0
  19. package/src/commands/crosschain.js +62 -0
  20. package/src/commands/dao.js +62 -0
  21. package/src/commands/dbevo.js +284 -0
  22. package/src/commands/dev.js +252 -0
  23. package/src/commands/did.js +358 -0
  24. package/src/commands/dlp.js +61 -0
  25. package/src/commands/economy.js +56 -0
  26. package/src/commands/encrypt.js +341 -0
  27. package/src/commands/evolution.js +56 -0
  28. package/src/commands/evomap.js +61 -0
  29. package/src/commands/export.js +256 -1
  30. package/src/commands/fflag.js +178 -0
  31. package/src/commands/fusion.js +258 -0
  32. package/src/commands/git.js +45 -0
  33. package/src/commands/governance.js +325 -0
  34. package/src/commands/hardening.js +411 -0
  35. package/src/commands/hmemory.js +56 -0
  36. package/src/commands/hook.js +148 -0
  37. package/src/commands/import.js +252 -0
  38. package/src/commands/incentive.js +322 -0
  39. package/src/commands/inference.js +42 -0
  40. package/src/commands/infra.js +244 -0
  41. package/src/commands/instinct.js +260 -0
  42. package/src/commands/ipfs.js +318 -0
  43. package/src/commands/itbudget.js +45 -0
  44. package/src/commands/kg.js +387 -0
  45. package/src/commands/llm.js +263 -0
  46. package/src/commands/lowcode.js +44 -0
  47. package/src/commands/matrix.js +62 -0
  48. package/src/commands/mcp.js +221 -0
  49. package/src/commands/mcpscaf.js +41 -0
  50. package/src/commands/meminj.js +41 -0
  51. package/src/commands/memory.js +248 -0
  52. package/src/commands/multimodal.js +296 -0
  53. package/src/commands/nlprog.js +356 -0
  54. package/src/commands/nostr.js +62 -0
  55. package/src/commands/note.js +244 -0
  56. package/src/commands/ops.js +354 -0
  57. package/src/commands/orchestrate.js +166 -0
  58. package/src/commands/orchgov.js +45 -0
  59. package/src/commands/org.js +277 -0
  60. package/src/commands/p2p.js +390 -0
  61. package/src/commands/pdfp.js +78 -0
  62. package/src/commands/perception.js +290 -0
  63. package/src/commands/perf.js +39 -0
  64. package/src/commands/perm.js +45 -0
  65. package/src/commands/permmem.js +251 -0
  66. package/src/commands/pipeline.js +57 -1
  67. package/src/commands/planmode.js +45 -0
  68. package/src/commands/plugin-ecosystem.js +273 -0
  69. package/src/commands/pqc.js +393 -0
  70. package/src/commands/promcomp.js +82 -0
  71. package/src/commands/quantization.js +351 -0
  72. package/src/commands/rcache.js +271 -0
  73. package/src/commands/recommend.js +382 -0
  74. package/src/commands/runtime.js +307 -0
  75. package/src/commands/scim.js +262 -0
  76. package/src/commands/seshhook.js +41 -0
  77. package/src/commands/seshsearch.js +41 -0
  78. package/src/commands/seshtail.js +41 -0
  79. package/src/commands/seshu.js +41 -0
  80. package/src/commands/session.js +258 -0
  81. package/src/commands/sganal.js +78 -0
  82. package/src/commands/siem.js +40 -0
  83. package/src/commands/skill.js +267 -1
  84. package/src/commands/slotfill.js +41 -0
  85. package/src/commands/social.js +290 -0
  86. package/src/commands/sso.js +186 -1
  87. package/src/commands/svccont.js +45 -0
  88. package/src/commands/sync.js +256 -0
  89. package/src/commands/tech.js +338 -0
  90. package/src/commands/tenant.js +351 -0
  91. package/src/commands/tms.js +45 -0
  92. package/src/commands/tokens.js +269 -0
  93. package/src/commands/topiccls.js +45 -0
  94. package/src/commands/trust.js +249 -0
  95. package/src/commands/uprof.js +45 -0
  96. package/src/commands/vcheck.js +78 -0
  97. package/src/commands/wallet.js +277 -0
  98. package/src/commands/webfetch.js +41 -0
  99. package/src/commands/workflow.js +171 -0
  100. package/src/commands/zkp.js +62 -0
  101. package/src/harness/prompt-compressor.js +331 -0
  102. package/src/index.js +65 -1
  103. package/src/lib/a2a-protocol.js +105 -0
  104. package/src/lib/activitypub-bridge.js +105 -0
  105. package/src/lib/agent-coordinator.js +325 -0
  106. package/src/lib/agent-economy.js +105 -0
  107. package/src/lib/agent-network.js +387 -0
  108. package/src/lib/agent-router.js +395 -0
  109. package/src/lib/aiops.js +478 -0
  110. package/src/lib/app-builder.js +105 -0
  111. package/src/lib/audit-logger.js +379 -0
  112. package/src/lib/automation-engine.js +330 -0
  113. package/src/lib/autonomous-agent.js +105 -0
  114. package/src/lib/autonomous-developer.js +350 -0
  115. package/src/lib/bi-engine.js +105 -0
  116. package/src/lib/bm25-search.js +81 -0
  117. package/src/lib/browser-automation.js +105 -0
  118. package/src/lib/code-agent.js +323 -0
  119. package/src/lib/collaboration-governance.js +364 -0
  120. package/src/lib/community-governance.js +436 -0
  121. package/src/lib/compliance-framework-reporter.js +105 -0
  122. package/src/lib/compliance-manager.js +434 -0
  123. package/src/lib/compression-telemetry.js +81 -0
  124. package/src/lib/content-recommendation.js +469 -0
  125. package/src/lib/content-recommender.js +105 -0
  126. package/src/lib/cowork-cron.js +81 -0
  127. package/src/lib/cowork-task-runner.js +105 -0
  128. package/src/lib/cross-chain.js +105 -0
  129. package/src/lib/crypto-manager.js +350 -0
  130. package/src/lib/dao-governance.js +105 -0
  131. package/src/lib/dbevo.js +338 -0
  132. package/src/lib/decentral-infra.js +340 -0
  133. package/src/lib/did-manager.js +367 -0
  134. package/src/lib/dlp-engine.js +105 -0
  135. package/src/lib/evolution-system.js +105 -0
  136. package/src/lib/evomap-manager.js +105 -0
  137. package/src/lib/execution-backend.js +105 -0
  138. package/src/lib/feature-flags.js +85 -0
  139. package/src/lib/git-integration.js +105 -0
  140. package/src/lib/hardening-manager.js +348 -0
  141. package/src/lib/hierarchical-memory.js +105 -0
  142. package/src/lib/hook-manager.js +380 -0
  143. package/src/lib/inference-network.js +105 -0
  144. package/src/lib/instinct-manager.js +332 -0
  145. package/src/lib/ipfs-storage.js +334 -0
  146. package/src/lib/iteration-budget.js +105 -0
  147. package/src/lib/knowledge-exporter.js +381 -0
  148. package/src/lib/knowledge-graph.js +432 -0
  149. package/src/lib/knowledge-importer.js +379 -0
  150. package/src/lib/llm-providers.js +391 -0
  151. package/src/lib/matrix-bridge.js +105 -0
  152. package/src/lib/mcp-registry.js +333 -0
  153. package/src/lib/mcp-scaffold.js +81 -0
  154. package/src/lib/memory-injection.js +81 -0
  155. package/src/lib/memory-manager.js +330 -0
  156. package/src/lib/multimodal.js +346 -0
  157. package/src/lib/nl-programming.js +343 -0
  158. package/src/lib/nostr-bridge.js +105 -0
  159. package/src/lib/note-versioning.js +327 -0
  160. package/src/lib/orchestrator.js +105 -0
  161. package/src/lib/org-manager.js +323 -0
  162. package/src/lib/p2p-manager.js +387 -0
  163. package/src/lib/pdf-parser.js +81 -0
  164. package/src/lib/perception.js +346 -0
  165. package/src/lib/perf-tuning.js +109 -1
  166. package/src/lib/permanent-memory.js +320 -0
  167. package/src/lib/permission-engine.js +81 -0
  168. package/src/lib/pipeline-orchestrator.js +105 -0
  169. package/src/lib/plan-mode.js +81 -0
  170. package/src/lib/plugin-ecosystem.js +377 -0
  171. package/src/lib/pqc-manager.js +368 -0
  172. package/src/lib/prompt-compressor.js +1 -10
  173. package/src/lib/protocol-fusion.js +417 -0
  174. package/src/lib/quantization.js +325 -0
  175. package/src/lib/response-cache.js +327 -0
  176. package/src/lib/scim-manager.js +329 -0
  177. package/src/lib/service-container.js +81 -0
  178. package/src/lib/session-consolidator.js +105 -0
  179. package/src/lib/session-hooks.js +81 -0
  180. package/src/lib/session-manager.js +329 -0
  181. package/src/lib/session-search.js +81 -0
  182. package/src/lib/session-tail.js +81 -0
  183. package/src/lib/session-usage.js +83 -0
  184. package/src/lib/siem-exporter.js +105 -0
  185. package/src/lib/skill-loader.js +377 -0
  186. package/src/lib/slot-filler.js +81 -0
  187. package/src/lib/social-graph-analytics.js +81 -0
  188. package/src/lib/social-graph.js +81 -0
  189. package/src/lib/social-manager.js +326 -0
  190. package/src/lib/sso-manager.js +332 -0
  191. package/src/lib/sub-agent-registry.js +110 -0
  192. package/src/lib/sync-manager.js +326 -0
  193. package/src/lib/task-model-selector.js +81 -0
  194. package/src/lib/tech-learning-engine.js +369 -0
  195. package/src/lib/tenant-saas.js +460 -0
  196. package/src/lib/threat-intel.js +335 -0
  197. package/src/lib/todo-manager.js +105 -0
  198. package/src/lib/token-incentive.js +293 -0
  199. package/src/lib/token-tracker.js +329 -0
  200. package/src/lib/topic-classifier.js +105 -0
  201. package/src/lib/trust-security.js +390 -0
  202. package/src/lib/ueba.js +389 -0
  203. package/src/lib/universal-runtime.js +325 -0
  204. package/src/lib/user-profile.js +81 -0
  205. package/src/lib/version-checker.js +81 -0
  206. package/src/lib/wallet-manager.js +326 -0
  207. package/src/lib/web-fetch.js +81 -0
  208. package/src/lib/workflow-engine.js +322 -0
  209. package/src/lib/zkp-engine.js +105 -0
@@ -465,3 +465,108 @@ function _appendHistory(cwd, entry, userMessage) {
465
465
  // Best-effort — don't fail the task for history write errors
466
466
  }
467
467
  }
468
+
469
+
470
+ // ===== V2 Surface: Cowork Task Runner governance overlay (CLI v0.139.0) =====
471
+ export const RUNNER_PROFILE_MATURITY_V2 = Object.freeze({
472
+ PENDING: "pending", ACTIVE: "active", PAUSED: "paused", RETIRED: "retired",
473
+ });
474
+ export const RUNNER_EXEC_LIFECYCLE_V2 = Object.freeze({
475
+ QUEUED: "queued", RUNNING: "running", SUCCEEDED: "succeeded", FAILED: "failed", CANCELLED: "cancelled",
476
+ });
477
+
478
+ const _rpTrans = new Map([
479
+ [RUNNER_PROFILE_MATURITY_V2.PENDING, new Set([RUNNER_PROFILE_MATURITY_V2.ACTIVE, RUNNER_PROFILE_MATURITY_V2.RETIRED])],
480
+ [RUNNER_PROFILE_MATURITY_V2.ACTIVE, new Set([RUNNER_PROFILE_MATURITY_V2.PAUSED, RUNNER_PROFILE_MATURITY_V2.RETIRED])],
481
+ [RUNNER_PROFILE_MATURITY_V2.PAUSED, new Set([RUNNER_PROFILE_MATURITY_V2.ACTIVE, RUNNER_PROFILE_MATURITY_V2.RETIRED])],
482
+ [RUNNER_PROFILE_MATURITY_V2.RETIRED, new Set()],
483
+ ]);
484
+ const _rpTerminal = new Set([RUNNER_PROFILE_MATURITY_V2.RETIRED]);
485
+ const _reTrans = new Map([
486
+ [RUNNER_EXEC_LIFECYCLE_V2.QUEUED, new Set([RUNNER_EXEC_LIFECYCLE_V2.RUNNING, RUNNER_EXEC_LIFECYCLE_V2.CANCELLED])],
487
+ [RUNNER_EXEC_LIFECYCLE_V2.RUNNING, new Set([RUNNER_EXEC_LIFECYCLE_V2.SUCCEEDED, RUNNER_EXEC_LIFECYCLE_V2.FAILED, RUNNER_EXEC_LIFECYCLE_V2.CANCELLED])],
488
+ [RUNNER_EXEC_LIFECYCLE_V2.SUCCEEDED, new Set()],
489
+ [RUNNER_EXEC_LIFECYCLE_V2.FAILED, new Set()],
490
+ [RUNNER_EXEC_LIFECYCLE_V2.CANCELLED, new Set()],
491
+ ]);
492
+
493
+ const _rpsV2 = new Map();
494
+ const _resV2 = new Map();
495
+ let _rpMaxActivePerOwner = 8;
496
+ let _rpMaxPendingExecsPerProfile = 15;
497
+ let _rpIdleMs = 14 * 24 * 60 * 60 * 1000;
498
+ let _reStuckMs = 20 * 60 * 1000;
499
+
500
+ function _rpPos(n, lbl) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${lbl} must be positive integer`); return v; }
501
+
502
+ export function setMaxActiveRunnerProfilesPerOwnerV2(n) { _rpMaxActivePerOwner = _rpPos(n, "maxActiveRunnerProfilesPerOwner"); }
503
+ export function getMaxActiveRunnerProfilesPerOwnerV2() { return _rpMaxActivePerOwner; }
504
+ export function setMaxPendingRunnerExecsPerProfileV2(n) { _rpMaxPendingExecsPerProfile = _rpPos(n, "maxPendingRunnerExecsPerProfile"); }
505
+ export function getMaxPendingRunnerExecsPerProfileV2() { return _rpMaxPendingExecsPerProfile; }
506
+ export function setRunnerProfileIdleMsV2(n) { _rpIdleMs = _rpPos(n, "runnerProfileIdleMs"); }
507
+ export function getRunnerProfileIdleMsV2() { return _rpIdleMs; }
508
+ export function setRunnerExecStuckMsV2(n) { _reStuckMs = _rpPos(n, "runnerExecStuckMs"); }
509
+ export function getRunnerExecStuckMsV2() { return _reStuckMs; }
510
+
511
+ export function _resetStateRunnerV2() {
512
+ _rpsV2.clear(); _resV2.clear();
513
+ _rpMaxActivePerOwner = 8; _rpMaxPendingExecsPerProfile = 15;
514
+ _rpIdleMs = 14 * 24 * 60 * 60 * 1000; _reStuckMs = 20 * 60 * 1000;
515
+ }
516
+
517
+ export function registerRunnerProfileV2({ id, owner, template, metadata } = {}) {
518
+ if (!id || typeof id !== "string") throw new Error("id is required");
519
+ if (!owner || typeof owner !== "string") throw new Error("owner is required");
520
+ if (_rpsV2.has(id)) throw new Error(`runner profile ${id} already registered`);
521
+ const now = Date.now();
522
+ const p = { id, owner, template: template || "default", status: RUNNER_PROFILE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, retiredAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
523
+ _rpsV2.set(id, p);
524
+ return { ...p, metadata: { ...p.metadata } };
525
+ }
526
+ function _rpCheckP(from, to) { const a = _rpTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid runner profile transition ${from} → ${to}`); }
527
+ function _rpCountActive(owner) { let n = 0; for (const p of _rpsV2.values()) if (p.owner === owner && p.status === RUNNER_PROFILE_MATURITY_V2.ACTIVE) n++; return n; }
528
+
529
+ export function activateRunnerProfileV2(id) {
530
+ const p = _rpsV2.get(id); if (!p) throw new Error(`runner profile ${id} not found`);
531
+ _rpCheckP(p.status, RUNNER_PROFILE_MATURITY_V2.ACTIVE);
532
+ const recovery = p.status === RUNNER_PROFILE_MATURITY_V2.PAUSED;
533
+ if (!recovery) { const c = _rpCountActive(p.owner); if (c >= _rpMaxActivePerOwner) throw new Error(`max active runner profiles per owner (${_rpMaxActivePerOwner}) reached for ${p.owner}`); }
534
+ const now = Date.now(); p.status = RUNNER_PROFILE_MATURITY_V2.ACTIVE; p.updatedAt = now; p.lastTouchedAt = now; if (!p.activatedAt) p.activatedAt = now;
535
+ return { ...p, metadata: { ...p.metadata } };
536
+ }
537
+ export function pauseRunnerProfileV2(id) { const p = _rpsV2.get(id); if (!p) throw new Error(`runner profile ${id} not found`); _rpCheckP(p.status, RUNNER_PROFILE_MATURITY_V2.PAUSED); p.status = RUNNER_PROFILE_MATURITY_V2.PAUSED; p.updatedAt = Date.now(); return { ...p, metadata: { ...p.metadata } }; }
538
+ export function retireRunnerProfileV2(id) { const p = _rpsV2.get(id); if (!p) throw new Error(`runner profile ${id} not found`); _rpCheckP(p.status, RUNNER_PROFILE_MATURITY_V2.RETIRED); const now = Date.now(); p.status = RUNNER_PROFILE_MATURITY_V2.RETIRED; p.updatedAt = now; if (!p.retiredAt) p.retiredAt = now; return { ...p, metadata: { ...p.metadata } }; }
539
+ export function touchRunnerProfileV2(id) { const p = _rpsV2.get(id); if (!p) throw new Error(`runner profile ${id} not found`); if (_rpTerminal.has(p.status)) throw new Error(`cannot touch terminal runner profile ${id}`); const now = Date.now(); p.lastTouchedAt = now; p.updatedAt = now; return { ...p, metadata: { ...p.metadata } }; }
540
+ export function getRunnerProfileV2(id) { const p = _rpsV2.get(id); if (!p) return null; return { ...p, metadata: { ...p.metadata } }; }
541
+ export function listRunnerProfilesV2() { return [..._rpsV2.values()].map((p) => ({ ...p, metadata: { ...p.metadata } })); }
542
+
543
+ function _reCountPending(profileId) { let n = 0; for (const e of _resV2.values()) if (e.profileId === profileId && (e.status === RUNNER_EXEC_LIFECYCLE_V2.QUEUED || e.status === RUNNER_EXEC_LIFECYCLE_V2.RUNNING)) n++; return n; }
544
+
545
+ export function createRunnerExecV2({ id, profileId, taskInput, metadata } = {}) {
546
+ if (!id || typeof id !== "string") throw new Error("id is required");
547
+ if (!profileId || typeof profileId !== "string") throw new Error("profileId is required");
548
+ if (_resV2.has(id)) throw new Error(`runner exec ${id} already exists`);
549
+ if (!_rpsV2.has(profileId)) throw new Error(`runner profile ${profileId} not found`);
550
+ const pending = _reCountPending(profileId);
551
+ if (pending >= _rpMaxPendingExecsPerProfile) throw new Error(`max pending runner execs per profile (${_rpMaxPendingExecsPerProfile}) reached for ${profileId}`);
552
+ const now = Date.now();
553
+ const e = { id, profileId, taskInput: taskInput || "", status: RUNNER_EXEC_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
554
+ _resV2.set(id, e);
555
+ return { ...e, metadata: { ...e.metadata } };
556
+ }
557
+ function _reCheckE(from, to) { const a = _reTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid runner exec transition ${from} → ${to}`); }
558
+ export function startRunnerExecV2(id) { const e = _resV2.get(id); if (!e) throw new Error(`runner exec ${id} not found`); _reCheckE(e.status, RUNNER_EXEC_LIFECYCLE_V2.RUNNING); const now = Date.now(); e.status = RUNNER_EXEC_LIFECYCLE_V2.RUNNING; e.updatedAt = now; if (!e.startedAt) e.startedAt = now; return { ...e, metadata: { ...e.metadata } }; }
559
+ export function succeedRunnerExecV2(id) { const e = _resV2.get(id); if (!e) throw new Error(`runner exec ${id} not found`); _reCheckE(e.status, RUNNER_EXEC_LIFECYCLE_V2.SUCCEEDED); const now = Date.now(); e.status = RUNNER_EXEC_LIFECYCLE_V2.SUCCEEDED; e.updatedAt = now; if (!e.settledAt) e.settledAt = now; return { ...e, metadata: { ...e.metadata } }; }
560
+ export function failRunnerExecV2(id, reason) { const e = _resV2.get(id); if (!e) throw new Error(`runner exec ${id} not found`); _reCheckE(e.status, RUNNER_EXEC_LIFECYCLE_V2.FAILED); const now = Date.now(); e.status = RUNNER_EXEC_LIFECYCLE_V2.FAILED; e.updatedAt = now; if (!e.settledAt) e.settledAt = now; if (reason) e.metadata.failReason = String(reason); return { ...e, metadata: { ...e.metadata } }; }
561
+ export function cancelRunnerExecV2(id, reason) { const e = _resV2.get(id); if (!e) throw new Error(`runner exec ${id} not found`); _reCheckE(e.status, RUNNER_EXEC_LIFECYCLE_V2.CANCELLED); const now = Date.now(); e.status = RUNNER_EXEC_LIFECYCLE_V2.CANCELLED; e.updatedAt = now; if (!e.settledAt) e.settledAt = now; if (reason) e.metadata.cancelReason = String(reason); return { ...e, metadata: { ...e.metadata } }; }
562
+ export function getRunnerExecV2(id) { const e = _resV2.get(id); if (!e) return null; return { ...e, metadata: { ...e.metadata } }; }
563
+ export function listRunnerExecsV2() { return [..._resV2.values()].map((e) => ({ ...e, metadata: { ...e.metadata } })); }
564
+
565
+ export function autoPauseIdleRunnerProfilesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const p of _rpsV2.values()) if (p.status === RUNNER_PROFILE_MATURITY_V2.ACTIVE && (t - p.lastTouchedAt) >= _rpIdleMs) { p.status = RUNNER_PROFILE_MATURITY_V2.PAUSED; p.updatedAt = t; flipped.push(p.id); } return { flipped, count: flipped.length }; }
566
+ export function autoFailStuckRunnerExecsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const e of _resV2.values()) if (e.status === RUNNER_EXEC_LIFECYCLE_V2.RUNNING && e.startedAt != null && (t - e.startedAt) >= _reStuckMs) { e.status = RUNNER_EXEC_LIFECYCLE_V2.FAILED; e.updatedAt = t; if (!e.settledAt) e.settledAt = t; e.metadata.failReason = "auto-fail-stuck"; flipped.push(e.id); } return { flipped, count: flipped.length }; }
567
+
568
+ export function getRunnerGovStatsV2() {
569
+ const profilesByStatus = {}; for (const s of Object.values(RUNNER_PROFILE_MATURITY_V2)) profilesByStatus[s] = 0; for (const p of _rpsV2.values()) profilesByStatus[p.status]++;
570
+ const execsByStatus = {}; for (const s of Object.values(RUNNER_EXEC_LIFECYCLE_V2)) execsByStatus[s] = 0; for (const e of _resV2.values()) execsByStatus[e.status]++;
571
+ return { totalRunnerProfilesV2: _rpsV2.size, totalRunnerExecsV2: _resV2.size, maxActiveRunnerProfilesPerOwner: _rpMaxActivePerOwner, maxPendingRunnerExecsPerProfile: _rpMaxPendingExecsPerProfile, runnerProfileIdleMs: _rpIdleMs, runnerExecStuckMs: _reStuckMs, profilesByStatus, execsByStatus };
572
+ }
@@ -1012,3 +1012,108 @@ export function getCrossChainStatsV2() {
1012
1012
  configuredChains: _chainConfigs.size,
1013
1013
  };
1014
1014
  }
1015
+
1016
+
1017
+ // ===== V2 Surface: Cross-Chain governance overlay (CLI v0.136.0) =====
1018
+ export const XCHAIN_CHANNEL_MATURITY_V2 = Object.freeze({
1019
+ PENDING: "pending", ACTIVE: "active", PAUSED: "paused", DECOMMISSIONED: "decommissioned",
1020
+ });
1021
+ export const XCHAIN_TRANSFER_LIFECYCLE_V2 = Object.freeze({
1022
+ QUEUED: "queued", RELAYING: "relaying", CONFIRMED: "confirmed", FAILED: "failed", CANCELLED: "cancelled",
1023
+ });
1024
+
1025
+ const _xcChanTrans = new Map([
1026
+ [XCHAIN_CHANNEL_MATURITY_V2.PENDING, new Set([XCHAIN_CHANNEL_MATURITY_V2.ACTIVE, XCHAIN_CHANNEL_MATURITY_V2.DECOMMISSIONED])],
1027
+ [XCHAIN_CHANNEL_MATURITY_V2.ACTIVE, new Set([XCHAIN_CHANNEL_MATURITY_V2.PAUSED, XCHAIN_CHANNEL_MATURITY_V2.DECOMMISSIONED])],
1028
+ [XCHAIN_CHANNEL_MATURITY_V2.PAUSED, new Set([XCHAIN_CHANNEL_MATURITY_V2.ACTIVE, XCHAIN_CHANNEL_MATURITY_V2.DECOMMISSIONED])],
1029
+ [XCHAIN_CHANNEL_MATURITY_V2.DECOMMISSIONED, new Set()],
1030
+ ]);
1031
+ const _xcChanTerminal = new Set([XCHAIN_CHANNEL_MATURITY_V2.DECOMMISSIONED]);
1032
+ const _xcTxTrans = new Map([
1033
+ [XCHAIN_TRANSFER_LIFECYCLE_V2.QUEUED, new Set([XCHAIN_TRANSFER_LIFECYCLE_V2.RELAYING, XCHAIN_TRANSFER_LIFECYCLE_V2.CANCELLED])],
1034
+ [XCHAIN_TRANSFER_LIFECYCLE_V2.RELAYING, new Set([XCHAIN_TRANSFER_LIFECYCLE_V2.CONFIRMED, XCHAIN_TRANSFER_LIFECYCLE_V2.FAILED, XCHAIN_TRANSFER_LIFECYCLE_V2.CANCELLED])],
1035
+ [XCHAIN_TRANSFER_LIFECYCLE_V2.CONFIRMED, new Set()],
1036
+ [XCHAIN_TRANSFER_LIFECYCLE_V2.FAILED, new Set()],
1037
+ [XCHAIN_TRANSFER_LIFECYCLE_V2.CANCELLED, new Set()],
1038
+ ]);
1039
+
1040
+ const _xcChans = new Map();
1041
+ const _xcTxs = new Map();
1042
+ let _xcMaxActivePerOwner = 10;
1043
+ let _xcMaxPendingPerChan = 20;
1044
+ let _xcChanIdleMs = 24 * 60 * 60 * 1000;
1045
+ let _xcTxStuckMs = 15 * 60 * 1000;
1046
+
1047
+ function _xcPos(n, lbl) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${lbl} must be positive integer`); return v; }
1048
+
1049
+ export function setMaxActiveXchainChannelsPerOwnerV2(n) { _xcMaxActivePerOwner = _xcPos(n, "maxActiveXchainChannelsPerOwner"); }
1050
+ export function getMaxActiveXchainChannelsPerOwnerV2() { return _xcMaxActivePerOwner; }
1051
+ export function setMaxPendingXchainTransfersPerChannelV2(n) { _xcMaxPendingPerChan = _xcPos(n, "maxPendingXchainTransfersPerChannel"); }
1052
+ export function getMaxPendingXchainTransfersPerChannelV2() { return _xcMaxPendingPerChan; }
1053
+ export function setXchainChannelIdleMsV2(n) { _xcChanIdleMs = _xcPos(n, "xchainChannelIdleMs"); }
1054
+ export function getXchainChannelIdleMsV2() { return _xcChanIdleMs; }
1055
+ export function setXchainTransferStuckMsV2(n) { _xcTxStuckMs = _xcPos(n, "xchainTransferStuckMs"); }
1056
+ export function getXchainTransferStuckMsV2() { return _xcTxStuckMs; }
1057
+
1058
+ export function _resetStateCrossChainV2() {
1059
+ _xcChans.clear(); _xcTxs.clear();
1060
+ _xcMaxActivePerOwner = 10; _xcMaxPendingPerChan = 20;
1061
+ _xcChanIdleMs = 24 * 60 * 60 * 1000; _xcTxStuckMs = 15 * 60 * 1000;
1062
+ }
1063
+
1064
+ export function registerXchainChannelV2({ id, owner, fromChain, toChain, metadata } = {}) {
1065
+ if (!id || typeof id !== "string") throw new Error("id is required");
1066
+ if (!owner || typeof owner !== "string") throw new Error("owner is required");
1067
+ if (_xcChans.has(id)) throw new Error(`xchain channel ${id} already registered`);
1068
+ const now = Date.now();
1069
+ const c = { id, owner, fromChain: fromChain || "", toChain: toChain || "", status: XCHAIN_CHANNEL_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, decommissionedAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
1070
+ _xcChans.set(id, c);
1071
+ return { ...c, metadata: { ...c.metadata } };
1072
+ }
1073
+ function _xcCheckC(from, to) { const a = _xcChanTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid xchain channel transition ${from} → ${to}`); }
1074
+ function _xcCountActive(owner) { let n = 0; for (const c of _xcChans.values()) if (c.owner === owner && c.status === XCHAIN_CHANNEL_MATURITY_V2.ACTIVE) n++; return n; }
1075
+
1076
+ export function activateXchainChannelV2(id) {
1077
+ const c = _xcChans.get(id); if (!c) throw new Error(`xchain channel ${id} not found`);
1078
+ _xcCheckC(c.status, XCHAIN_CHANNEL_MATURITY_V2.ACTIVE);
1079
+ const recovery = c.status === XCHAIN_CHANNEL_MATURITY_V2.PAUSED;
1080
+ if (!recovery) { const a = _xcCountActive(c.owner); if (a >= _xcMaxActivePerOwner) throw new Error(`max active xchain channels per owner (${_xcMaxActivePerOwner}) reached for ${c.owner}`); }
1081
+ const now = Date.now(); c.status = XCHAIN_CHANNEL_MATURITY_V2.ACTIVE; c.updatedAt = now; c.lastTouchedAt = now; if (!c.activatedAt) c.activatedAt = now;
1082
+ return { ...c, metadata: { ...c.metadata } };
1083
+ }
1084
+ export function pauseXchainChannelV2(id) { const c = _xcChans.get(id); if (!c) throw new Error(`xchain channel ${id} not found`); _xcCheckC(c.status, XCHAIN_CHANNEL_MATURITY_V2.PAUSED); c.status = XCHAIN_CHANNEL_MATURITY_V2.PAUSED; c.updatedAt = Date.now(); return { ...c, metadata: { ...c.metadata } }; }
1085
+ export function decommissionXchainChannelV2(id) { const c = _xcChans.get(id); if (!c) throw new Error(`xchain channel ${id} not found`); _xcCheckC(c.status, XCHAIN_CHANNEL_MATURITY_V2.DECOMMISSIONED); const now = Date.now(); c.status = XCHAIN_CHANNEL_MATURITY_V2.DECOMMISSIONED; c.updatedAt = now; if (!c.decommissionedAt) c.decommissionedAt = now; return { ...c, metadata: { ...c.metadata } }; }
1086
+ export function touchXchainChannelV2(id) { const c = _xcChans.get(id); if (!c) throw new Error(`xchain channel ${id} not found`); if (_xcChanTerminal.has(c.status)) throw new Error(`cannot touch terminal xchain channel ${id}`); const now = Date.now(); c.lastTouchedAt = now; c.updatedAt = now; return { ...c, metadata: { ...c.metadata } }; }
1087
+ export function getXchainChannelV2(id) { const c = _xcChans.get(id); if (!c) return null; return { ...c, metadata: { ...c.metadata } }; }
1088
+ export function listXchainChannelsV2() { return [..._xcChans.values()].map((c) => ({ ...c, metadata: { ...c.metadata } })); }
1089
+
1090
+ function _xcCountPending(cid) { let n = 0; for (const t of _xcTxs.values()) if (t.channelId === cid && (t.status === XCHAIN_TRANSFER_LIFECYCLE_V2.QUEUED || t.status === XCHAIN_TRANSFER_LIFECYCLE_V2.RELAYING)) n++; return n; }
1091
+
1092
+ export function createXchainTransferV2({ id, channelId, amount, metadata } = {}) {
1093
+ if (!id || typeof id !== "string") throw new Error("id is required");
1094
+ if (!channelId || typeof channelId !== "string") throw new Error("channelId is required");
1095
+ if (_xcTxs.has(id)) throw new Error(`xchain transfer ${id} already exists`);
1096
+ if (!_xcChans.has(channelId)) throw new Error(`xchain channel ${channelId} not found`);
1097
+ const pending = _xcCountPending(channelId);
1098
+ if (pending >= _xcMaxPendingPerChan) throw new Error(`max pending xchain transfers per channel (${_xcMaxPendingPerChan}) reached for ${channelId}`);
1099
+ const now = Date.now();
1100
+ const t = { id, channelId, amount: amount || "0", status: XCHAIN_TRANSFER_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
1101
+ _xcTxs.set(id, t);
1102
+ return { ...t, metadata: { ...t.metadata } };
1103
+ }
1104
+ function _xcCheckT(from, to) { const a = _xcTxTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid xchain transfer transition ${from} → ${to}`); }
1105
+ export function startXchainTransferV2(id) { const t = _xcTxs.get(id); if (!t) throw new Error(`xchain transfer ${id} not found`); _xcCheckT(t.status, XCHAIN_TRANSFER_LIFECYCLE_V2.RELAYING); const now = Date.now(); t.status = XCHAIN_TRANSFER_LIFECYCLE_V2.RELAYING; t.updatedAt = now; if (!t.startedAt) t.startedAt = now; return { ...t, metadata: { ...t.metadata } }; }
1106
+ export function confirmXchainTransferV2(id) { const t = _xcTxs.get(id); if (!t) throw new Error(`xchain transfer ${id} not found`); _xcCheckT(t.status, XCHAIN_TRANSFER_LIFECYCLE_V2.CONFIRMED); const now = Date.now(); t.status = XCHAIN_TRANSFER_LIFECYCLE_V2.CONFIRMED; t.updatedAt = now; if (!t.settledAt) t.settledAt = now; return { ...t, metadata: { ...t.metadata } }; }
1107
+ export function failXchainTransferV2(id, reason) { const t = _xcTxs.get(id); if (!t) throw new Error(`xchain transfer ${id} not found`); _xcCheckT(t.status, XCHAIN_TRANSFER_LIFECYCLE_V2.FAILED); const now = Date.now(); t.status = XCHAIN_TRANSFER_LIFECYCLE_V2.FAILED; t.updatedAt = now; if (!t.settledAt) t.settledAt = now; if (reason) t.metadata.failReason = String(reason); return { ...t, metadata: { ...t.metadata } }; }
1108
+ export function cancelXchainTransferV2(id, reason) { const t = _xcTxs.get(id); if (!t) throw new Error(`xchain transfer ${id} not found`); _xcCheckT(t.status, XCHAIN_TRANSFER_LIFECYCLE_V2.CANCELLED); const now = Date.now(); t.status = XCHAIN_TRANSFER_LIFECYCLE_V2.CANCELLED; t.updatedAt = now; if (!t.settledAt) t.settledAt = now; if (reason) t.metadata.cancelReason = String(reason); return { ...t, metadata: { ...t.metadata } }; }
1109
+ export function getXchainTransferV2(id) { const t = _xcTxs.get(id); if (!t) return null; return { ...t, metadata: { ...t.metadata } }; }
1110
+ export function listXchainTransfersV2() { return [..._xcTxs.values()].map((t) => ({ ...t, metadata: { ...t.metadata } })); }
1111
+
1112
+ export function autoPauseIdleXchainChannelsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const c of _xcChans.values()) if (c.status === XCHAIN_CHANNEL_MATURITY_V2.ACTIVE && (t - c.lastTouchedAt) >= _xcChanIdleMs) { c.status = XCHAIN_CHANNEL_MATURITY_V2.PAUSED; c.updatedAt = t; flipped.push(c.id); } return { flipped, count: flipped.length }; }
1113
+ export function autoFailStuckXchainTransfersV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const tx of _xcTxs.values()) if (tx.status === XCHAIN_TRANSFER_LIFECYCLE_V2.RELAYING && tx.startedAt != null && (t - tx.startedAt) >= _xcTxStuckMs) { tx.status = XCHAIN_TRANSFER_LIFECYCLE_V2.FAILED; tx.updatedAt = t; if (!tx.settledAt) tx.settledAt = t; tx.metadata.failReason = "auto-fail-stuck"; flipped.push(tx.id); } return { flipped, count: flipped.length }; }
1114
+
1115
+ export function getCrossChainGovStatsV2() {
1116
+ const channelsByStatus = {}; for (const s of Object.values(XCHAIN_CHANNEL_MATURITY_V2)) channelsByStatus[s] = 0; for (const c of _xcChans.values()) channelsByStatus[c.status]++;
1117
+ const transfersByStatus = {}; for (const s of Object.values(XCHAIN_TRANSFER_LIFECYCLE_V2)) transfersByStatus[s] = 0; for (const t of _xcTxs.values()) transfersByStatus[t.status]++;
1118
+ return { totalChannelsV2: _xcChans.size, totalTransfersV2: _xcTxs.size, maxActiveXchainChannelsPerOwner: _xcMaxActivePerOwner, maxPendingXchainTransfersPerChannel: _xcMaxPendingPerChan, xchainChannelIdleMs: _xcChanIdleMs, xchainTransferStuckMs: _xcTxStuckMs, channelsByStatus, transfersByStatus };
1119
+ }
@@ -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
+ }