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
@@ -198,4 +198,367 @@ export function listRuns(filter = {}) {
198
198
  export function _resetState() {
199
199
  _workspaces.clear();
200
200
  _runs.clear();
201
+ _maxConcurrentRuns = DEFAULT_MAX_CONCURRENT_RUNS;
202
+ }
203
+
204
+ /* ═══════════════════════════════════════════════════════════════
205
+ * V2 Canonical Surface (Phase 56 — Terraform Manager)
206
+ * Strictly additive; legacy exports above remain unchanged.
207
+ * ═══════════════════════════════════════════════════════════════ */
208
+
209
+ export const RUN_STATUS_V2 = Object.freeze({
210
+ PENDING: "pending",
211
+ PLANNING: "planning",
212
+ PLANNED: "planned",
213
+ APPLYING: "applying",
214
+ APPLIED: "applied",
215
+ DESTROYING: "destroying",
216
+ DESTROYED: "destroyed",
217
+ ERRORED: "errored",
218
+ CANCELLED: "cancelled",
219
+ });
220
+
221
+ export const RUN_TYPE_V2 = Object.freeze({
222
+ PLAN: "plan",
223
+ APPLY: "apply",
224
+ DESTROY: "destroy",
225
+ });
226
+
227
+ export const WORKSPACE_STATUS_V2 = Object.freeze({
228
+ ACTIVE: "active",
229
+ LOCKED: "locked",
230
+ ARCHIVED: "archived",
231
+ });
232
+
233
+ const DEFAULT_MAX_CONCURRENT_RUNS = 5;
234
+ let _maxConcurrentRuns = DEFAULT_MAX_CONCURRENT_RUNS;
235
+
236
+ export const TERRAFORM_DEFAULT_MAX_CONCURRENT = DEFAULT_MAX_CONCURRENT_RUNS;
237
+
238
+ export function setMaxConcurrentRuns(n) {
239
+ if (typeof n !== "number" || !Number.isFinite(n) || n < 1) {
240
+ throw new Error("maxConcurrentRuns must be a positive integer");
241
+ }
242
+ _maxConcurrentRuns = Math.floor(n);
243
+ return _maxConcurrentRuns;
244
+ }
245
+
246
+ export function getMaxConcurrentRuns() {
247
+ return _maxConcurrentRuns;
248
+ }
249
+
250
+ const _activeRunStatuses = new Set([
251
+ RUN_STATUS_V2.PENDING,
252
+ RUN_STATUS_V2.PLANNING,
253
+ RUN_STATUS_V2.APPLYING,
254
+ RUN_STATUS_V2.DESTROYING,
255
+ ]);
256
+
257
+ const _terminalRunStatuses = new Set([
258
+ RUN_STATUS_V2.APPLIED,
259
+ RUN_STATUS_V2.DESTROYED,
260
+ RUN_STATUS_V2.ERRORED,
261
+ RUN_STATUS_V2.CANCELLED,
262
+ ]);
263
+
264
+ // run-status state machine
265
+ const _allowedRunTransitions = new Map([
266
+ [
267
+ RUN_STATUS_V2.PENDING,
268
+ new Set([
269
+ RUN_STATUS_V2.PLANNING,
270
+ RUN_STATUS_V2.CANCELLED,
271
+ RUN_STATUS_V2.ERRORED,
272
+ ]),
273
+ ],
274
+ [
275
+ RUN_STATUS_V2.PLANNING,
276
+ new Set([
277
+ RUN_STATUS_V2.PLANNED,
278
+ RUN_STATUS_V2.ERRORED,
279
+ RUN_STATUS_V2.CANCELLED,
280
+ ]),
281
+ ],
282
+ [
283
+ RUN_STATUS_V2.PLANNED,
284
+ new Set([
285
+ RUN_STATUS_V2.APPLYING,
286
+ RUN_STATUS_V2.DESTROYING,
287
+ RUN_STATUS_V2.CANCELLED,
288
+ RUN_STATUS_V2.ERRORED,
289
+ ]),
290
+ ],
291
+ [
292
+ RUN_STATUS_V2.APPLYING,
293
+ new Set([RUN_STATUS_V2.APPLIED, RUN_STATUS_V2.ERRORED]),
294
+ ],
295
+ [
296
+ RUN_STATUS_V2.DESTROYING,
297
+ new Set([RUN_STATUS_V2.DESTROYED, RUN_STATUS_V2.ERRORED]),
298
+ ],
299
+ [RUN_STATUS_V2.APPLIED, new Set([])],
300
+ [RUN_STATUS_V2.DESTROYED, new Set([])],
301
+ [RUN_STATUS_V2.ERRORED, new Set([])],
302
+ [RUN_STATUS_V2.CANCELLED, new Set([])],
303
+ ]);
304
+
305
+ // workspace-status state machine
306
+ const _allowedWorkspaceTransitions = new Map([
307
+ [
308
+ WORKSPACE_STATUS_V2.ACTIVE,
309
+ new Set([WORKSPACE_STATUS_V2.LOCKED, WORKSPACE_STATUS_V2.ARCHIVED]),
310
+ ],
311
+ [
312
+ WORKSPACE_STATUS_V2.LOCKED,
313
+ new Set([WORKSPACE_STATUS_V2.ACTIVE, WORKSPACE_STATUS_V2.ARCHIVED]),
314
+ ],
315
+ [WORKSPACE_STATUS_V2.ARCHIVED, new Set([WORKSPACE_STATUS_V2.ACTIVE])],
316
+ ]);
317
+
318
+ export function createWorkspaceV2(db, options = {}) {
319
+ const {
320
+ name,
321
+ description,
322
+ terraformVersion,
323
+ workingDirectory,
324
+ autoApply,
325
+ variables,
326
+ providers,
327
+ } = options;
328
+
329
+ if (!name) throw new Error("Workspace name is required");
330
+
331
+ // Unique name check
332
+ for (const existing of _workspaces.values()) {
333
+ if (existing.name === name) {
334
+ throw new Error(`Workspace name already exists: ${name}`);
335
+ }
336
+ }
337
+
338
+ return createWorkspace(db, name, {
339
+ description,
340
+ terraformVersion,
341
+ workingDirectory,
342
+ autoApply,
343
+ variables,
344
+ providers,
345
+ });
346
+ }
347
+
348
+ export function setWorkspaceStatus(db, workspaceId, newStatus) {
349
+ const workspace = _workspaces.get(workspaceId);
350
+ if (!workspace) throw new Error(`Workspace not found: ${workspaceId}`);
351
+
352
+ const validStatuses = Object.values(WORKSPACE_STATUS_V2);
353
+ if (!validStatuses.includes(newStatus)) {
354
+ throw new Error(`Unknown workspace status: ${newStatus}`);
355
+ }
356
+
357
+ const allowed = _allowedWorkspaceTransitions.get(workspace.status);
358
+ if (!allowed || !allowed.has(newStatus)) {
359
+ throw new Error(
360
+ `Invalid workspace status transition: ${workspace.status} → ${newStatus}`,
361
+ );
362
+ }
363
+
364
+ workspace.status = newStatus;
365
+ db.prepare(`UPDATE terraform_workspaces SET status = ? WHERE id = ?`).run(
366
+ newStatus,
367
+ workspaceId,
368
+ );
369
+
370
+ return { workspaceId, status: newStatus };
371
+ }
372
+
373
+ export function archiveWorkspace(db, workspaceId) {
374
+ return setWorkspaceStatus(db, workspaceId, WORKSPACE_STATUS_V2.ARCHIVED);
375
+ }
376
+
377
+ function _countActiveRuns() {
378
+ let count = 0;
379
+ for (const r of _runs.values()) {
380
+ if (_activeRunStatuses.has(r.status)) count++;
381
+ }
382
+ return count;
383
+ }
384
+
385
+ export function planRunV2(db, options = {}) {
386
+ const { workspaceId, runType, triggeredBy } = options;
387
+ const workspace = _workspaces.get(workspaceId);
388
+ if (!workspace) throw new Error(`Workspace not found: ${workspaceId}`);
389
+
390
+ const validRunTypes = Object.values(RUN_TYPE_V2);
391
+ const effectiveType = runType || RUN_TYPE_V2.PLAN;
392
+ if (!validRunTypes.includes(effectiveType)) {
393
+ throw new Error(`Unknown run type: ${effectiveType}`);
394
+ }
395
+
396
+ if (workspace.status === WORKSPACE_STATUS_V2.ARCHIVED) {
397
+ throw new Error(`Cannot plan run on archived workspace: ${workspaceId}`);
398
+ }
399
+
400
+ const activeCount = _countActiveRuns();
401
+ if (activeCount >= _maxConcurrentRuns) {
402
+ throw new Error(
403
+ `Max concurrent runs reached: ${activeCount}/${_maxConcurrentRuns}`,
404
+ );
405
+ }
406
+
407
+ const id = crypto.randomUUID();
408
+ const now = new Date().toISOString();
409
+
410
+ const run = {
411
+ id,
412
+ workspaceId,
413
+ runType: effectiveType,
414
+ status: RUN_STATUS_V2.PENDING,
415
+ planOutput: null,
416
+ applyOutput: null,
417
+ resourcesAdded: 0,
418
+ resourcesChanged: 0,
419
+ resourcesDestroyed: 0,
420
+ triggeredBy: triggeredBy || "manual",
421
+ startedAt: now,
422
+ completedAt: null,
423
+ errorMessage: null,
424
+ createdAt: now,
425
+ };
426
+
427
+ _runs.set(id, run);
428
+
429
+ db.prepare(
430
+ `INSERT INTO terraform_runs (id, workspace_id, run_type, status, plan_output, apply_output, resources_added, resources_changed, resources_destroyed, triggered_by, started_at, completed_at, error_message, created_at)
431
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
432
+ ).run(
433
+ id,
434
+ workspaceId,
435
+ effectiveType,
436
+ run.status,
437
+ null,
438
+ null,
439
+ 0,
440
+ 0,
441
+ 0,
442
+ run.triggeredBy,
443
+ now,
444
+ null,
445
+ null,
446
+ now,
447
+ );
448
+
449
+ return run;
450
+ }
451
+
452
+ export function setRunStatus(db, runId, newStatus, patch = {}) {
453
+ const run = _runs.get(runId);
454
+ if (!run) throw new Error(`Run not found: ${runId}`);
455
+
456
+ const validStatuses = Object.values(RUN_STATUS_V2);
457
+ if (!validStatuses.includes(newStatus)) {
458
+ throw new Error(`Unknown run status: ${newStatus}`);
459
+ }
460
+
461
+ const allowed = _allowedRunTransitions.get(run.status);
462
+ if (!allowed || !allowed.has(newStatus)) {
463
+ throw new Error(
464
+ `Invalid run status transition: ${run.status} → ${newStatus}`,
465
+ );
466
+ }
467
+
468
+ run.status = newStatus;
469
+ if (typeof patch.planOutput === "string") run.planOutput = patch.planOutput;
470
+ if (typeof patch.applyOutput === "string")
471
+ run.applyOutput = patch.applyOutput;
472
+ if (typeof patch.resourcesAdded === "number")
473
+ run.resourcesAdded = patch.resourcesAdded;
474
+ if (typeof patch.resourcesChanged === "number")
475
+ run.resourcesChanged = patch.resourcesChanged;
476
+ if (typeof patch.resourcesDestroyed === "number")
477
+ run.resourcesDestroyed = patch.resourcesDestroyed;
478
+ if (typeof patch.errorMessage === "string")
479
+ run.errorMessage = patch.errorMessage;
480
+ if (_terminalRunStatuses.has(newStatus)) {
481
+ run.completedAt = new Date().toISOString();
482
+ }
483
+
484
+ db.prepare(
485
+ `UPDATE terraform_runs SET status = ?, plan_output = ?, apply_output = ?, resources_added = ?, resources_changed = ?, resources_destroyed = ?, error_message = ?, completed_at = ? WHERE id = ?`,
486
+ ).run(
487
+ run.status,
488
+ run.planOutput,
489
+ run.applyOutput,
490
+ run.resourcesAdded,
491
+ run.resourcesChanged,
492
+ run.resourcesDestroyed,
493
+ run.errorMessage,
494
+ run.completedAt,
495
+ runId,
496
+ );
497
+
498
+ // Bump workspace state version on terminal apply/destroy
499
+ if (
500
+ newStatus === RUN_STATUS_V2.APPLIED ||
501
+ newStatus === RUN_STATUS_V2.DESTROYED
502
+ ) {
503
+ const workspace = _workspaces.get(run.workspaceId);
504
+ if (workspace) {
505
+ workspace.stateVersion++;
506
+ workspace.lastRunId = runId;
507
+ workspace.lastRunAt = new Date().toISOString();
508
+ }
509
+ }
510
+
511
+ return run;
512
+ }
513
+
514
+ export function cancelRun(db, runId) {
515
+ return setRunStatus(db, runId, RUN_STATUS_V2.CANCELLED);
516
+ }
517
+
518
+ export function failRun(db, runId, errorMessage) {
519
+ return setRunStatus(db, runId, RUN_STATUS_V2.ERRORED, { errorMessage });
520
+ }
521
+
522
+ export function getActiveRunCount() {
523
+ return _countActiveRuns();
524
+ }
525
+
526
+ export function getTerraformStatsV2() {
527
+ const workspaces = [..._workspaces.values()];
528
+ const runs = [..._runs.values()];
529
+
530
+ const wsByStatus = {};
531
+ for (const s of Object.values(WORKSPACE_STATUS_V2)) wsByStatus[s] = 0;
532
+ for (const w of workspaces)
533
+ wsByStatus[w.status] = (wsByStatus[w.status] || 0) + 1;
534
+
535
+ const runsByStatus = {};
536
+ for (const s of Object.values(RUN_STATUS_V2)) runsByStatus[s] = 0;
537
+ for (const r of runs)
538
+ runsByStatus[r.status] = (runsByStatus[r.status] || 0) + 1;
539
+
540
+ const runsByType = {};
541
+ for (const t of Object.values(RUN_TYPE_V2)) runsByType[t] = 0;
542
+ for (const r of runs)
543
+ runsByType[r.runType] = (runsByType[r.runType] || 0) + 1;
544
+
545
+ const totalResources = runs.reduce(
546
+ (acc, r) => ({
547
+ added: acc.added + (r.resourcesAdded || 0),
548
+ changed: acc.changed + (r.resourcesChanged || 0),
549
+ destroyed: acc.destroyed + (r.resourcesDestroyed || 0),
550
+ }),
551
+ { added: 0, changed: 0, destroyed: 0 },
552
+ );
553
+
554
+ return {
555
+ totalWorkspaces: workspaces.length,
556
+ totalRuns: runs.length,
557
+ activeRuns: _countActiveRuns(),
558
+ maxConcurrentRuns: _maxConcurrentRuns,
559
+ workspacesByStatus: wsByStatus,
560
+ runsByStatus,
561
+ runsByType,
562
+ totalResources,
563
+ };
201
564
  }
@@ -266,3 +266,338 @@ export function clearAll(db) {
266
266
  const info = db.prepare(`DELETE FROM threat_intel_indicators`).run();
267
267
  return info.changes;
268
268
  }
269
+
270
+ /* ═══════════════════════════════════════════════════════════════
271
+ * V2 Surface — In-memory feed-maturity + indicator-lifecycle layer.
272
+ * Independent of the SQLite IoC catalog above; tracks feed sources
273
+ * and indicator lifecycle transitions with caps and auto-flip.
274
+ * ═══════════════════════════════════════════════════════════════ */
275
+
276
+ export const FEED_MATURITY_V2 = Object.freeze({
277
+ PENDING: "pending",
278
+ TRUSTED: "trusted",
279
+ DEPRECATED: "deprecated",
280
+ RETIRED: "retired",
281
+ });
282
+
283
+ export const INDICATOR_LIFECYCLE_V2 = Object.freeze({
284
+ PENDING: "pending",
285
+ ACTIVE: "active",
286
+ EXPIRED: "expired",
287
+ REVOKED: "revoked",
288
+ SUPERSEDED: "superseded",
289
+ });
290
+
291
+ const FEED_TRANSITIONS_V2 = new Map([
292
+ ["pending", new Set(["trusted", "retired"])],
293
+ ["trusted", new Set(["deprecated", "retired"])],
294
+ ["deprecated", new Set(["trusted", "retired"])],
295
+ ["retired", new Set()],
296
+ ]);
297
+ const FEED_TERMINALS_V2 = new Set(["retired"]);
298
+
299
+ const INDICATOR_TRANSITIONS_V2 = new Map([
300
+ ["pending", new Set(["active", "revoked", "superseded"])],
301
+ ["active", new Set(["expired", "revoked", "superseded"])],
302
+ ["expired", new Set()],
303
+ ["revoked", new Set()],
304
+ ["superseded", new Set()],
305
+ ]);
306
+ const INDICATOR_TERMINALS_V2 = new Set(["expired", "revoked", "superseded"]);
307
+
308
+ export const TI_DEFAULT_MAX_ACTIVE_FEEDS_PER_OWNER = 15;
309
+ export const TI_DEFAULT_MAX_ACTIVE_INDICATORS_PER_FEED = 500;
310
+ export const TI_DEFAULT_FEED_IDLE_MS = 1000 * 60 * 60 * 24 * 60; // 60 days
311
+ export const TI_DEFAULT_INDICATOR_STALE_MS = 1000 * 60 * 60 * 24 * 90; // 90 days
312
+
313
+ const _feedsV2 = new Map();
314
+ const _indicatorsV2 = new Map();
315
+ let _maxActiveFeedsPerOwnerV2 = TI_DEFAULT_MAX_ACTIVE_FEEDS_PER_OWNER;
316
+ let _maxActiveIndicatorsPerFeedV2 = TI_DEFAULT_MAX_ACTIVE_INDICATORS_PER_FEED;
317
+ let _feedIdleMsV2 = TI_DEFAULT_FEED_IDLE_MS;
318
+ let _indicatorStaleMsV2 = TI_DEFAULT_INDICATOR_STALE_MS;
319
+
320
+ function _posIntV2(n, label) {
321
+ const v = Math.floor(Number(n));
322
+ if (!Number.isFinite(v) || v <= 0)
323
+ throw new Error(`${label} must be a positive integer`);
324
+ return v;
325
+ }
326
+
327
+ export function getMaxActiveFeedsPerOwnerV2() {
328
+ return _maxActiveFeedsPerOwnerV2;
329
+ }
330
+ export function setMaxActiveFeedsPerOwnerV2(n) {
331
+ _maxActiveFeedsPerOwnerV2 = _posIntV2(n, "maxActiveFeedsPerOwner");
332
+ }
333
+ export function getMaxActiveIndicatorsPerFeedV2() {
334
+ return _maxActiveIndicatorsPerFeedV2;
335
+ }
336
+ export function setMaxActiveIndicatorsPerFeedV2(n) {
337
+ _maxActiveIndicatorsPerFeedV2 = _posIntV2(n, "maxActiveIndicatorsPerFeed");
338
+ }
339
+ export function getFeedIdleMsV2() {
340
+ return _feedIdleMsV2;
341
+ }
342
+ export function setFeedIdleMsV2(n) {
343
+ _feedIdleMsV2 = _posIntV2(n, "feedIdleMs");
344
+ }
345
+ export function getIndicatorStaleMsV2() {
346
+ return _indicatorStaleMsV2;
347
+ }
348
+ export function setIndicatorStaleMsV2(n) {
349
+ _indicatorStaleMsV2 = _posIntV2(n, "indicatorStaleMs");
350
+ }
351
+
352
+ export function getActiveFeedCountV2(owner) {
353
+ let n = 0;
354
+ for (const f of _feedsV2.values()) {
355
+ if (f.owner === owner && f.maturity === "trusted") n += 1;
356
+ }
357
+ return n;
358
+ }
359
+
360
+ export function getActiveIndicatorCountV2(feedId) {
361
+ let n = 0;
362
+ for (const i of _indicatorsV2.values()) {
363
+ if (i.feedId === feedId && i.status === "active") n += 1;
364
+ }
365
+ return n;
366
+ }
367
+
368
+ function _copyFeedV2(f) {
369
+ return { ...f, metadata: { ...f.metadata } };
370
+ }
371
+ function _copyIndicatorV2(i) {
372
+ return { ...i, metadata: { ...i.metadata } };
373
+ }
374
+
375
+ export function registerFeedV2(id, { owner, name, metadata = {} } = {}) {
376
+ if (!id || typeof id !== "string") throw new Error("id must be a string");
377
+ if (!owner || typeof owner !== "string")
378
+ throw new Error("owner must be a string");
379
+ if (!name || typeof name !== "string")
380
+ throw new Error("name must be a string");
381
+ if (_feedsV2.has(id)) throw new Error(`feed ${id} already exists`);
382
+ const now = Date.now();
383
+ const feed = {
384
+ id,
385
+ owner,
386
+ name,
387
+ maturity: "pending",
388
+ createdAt: now,
389
+ lastSeenAt: now,
390
+ activatedAt: null,
391
+ metadata: { ...metadata },
392
+ };
393
+ _feedsV2.set(id, feed);
394
+ return _copyFeedV2(feed);
395
+ }
396
+
397
+ export function getFeedV2(id) {
398
+ const f = _feedsV2.get(id);
399
+ return f ? _copyFeedV2(f) : null;
400
+ }
401
+
402
+ export function listFeedsV2({ owner, maturity } = {}) {
403
+ const out = [];
404
+ for (const f of _feedsV2.values()) {
405
+ if (owner && f.owner !== owner) continue;
406
+ if (maturity && f.maturity !== maturity) continue;
407
+ out.push(_copyFeedV2(f));
408
+ }
409
+ return out;
410
+ }
411
+
412
+ export function setFeedMaturityV2(id, next, { now = Date.now() } = {}) {
413
+ const f = _feedsV2.get(id);
414
+ if (!f) throw new Error(`feed ${id} not found`);
415
+ if (!FEED_TRANSITIONS_V2.has(next))
416
+ throw new Error(`unknown feed maturity: ${next}`);
417
+ if (FEED_TERMINALS_V2.has(f.maturity))
418
+ throw new Error(`feed ${id} is in terminal state ${f.maturity}`);
419
+ const allowed = FEED_TRANSITIONS_V2.get(f.maturity);
420
+ if (!allowed.has(next))
421
+ throw new Error(`cannot transition feed from ${f.maturity} to ${next}`);
422
+ if (next === "trusted") {
423
+ if (f.maturity === "pending") {
424
+ const count = getActiveFeedCountV2(f.owner);
425
+ if (count >= _maxActiveFeedsPerOwnerV2)
426
+ throw new Error(
427
+ `owner ${f.owner} already at active-feed cap (${_maxActiveFeedsPerOwnerV2})`,
428
+ );
429
+ }
430
+ if (!f.activatedAt) f.activatedAt = now;
431
+ }
432
+ f.maturity = next;
433
+ f.lastSeenAt = now;
434
+ return _copyFeedV2(f);
435
+ }
436
+
437
+ export function trustFeedV2(id, opts) {
438
+ return setFeedMaturityV2(id, "trusted", opts);
439
+ }
440
+ export function deprecateFeedV2(id, opts) {
441
+ return setFeedMaturityV2(id, "deprecated", opts);
442
+ }
443
+ export function retireFeedV2(id, opts) {
444
+ return setFeedMaturityV2(id, "retired", opts);
445
+ }
446
+
447
+ export function touchFeedV2(id, { now = Date.now() } = {}) {
448
+ const f = _feedsV2.get(id);
449
+ if (!f) throw new Error(`feed ${id} not found`);
450
+ f.lastSeenAt = now;
451
+ return _copyFeedV2(f);
452
+ }
453
+
454
+ export function createIndicatorV2(
455
+ id,
456
+ { feedId, iocType, value, metadata = {} } = {},
457
+ ) {
458
+ if (!id || typeof id !== "string") throw new Error("id must be a string");
459
+ if (!feedId || typeof feedId !== "string")
460
+ throw new Error("feedId must be a string");
461
+ if (!iocType || typeof iocType !== "string")
462
+ throw new Error("iocType must be a string");
463
+ if (!value || typeof value !== "string")
464
+ throw new Error("value must be a string");
465
+ if (!_feedsV2.has(feedId)) throw new Error(`feed ${feedId} not found`);
466
+ if (_indicatorsV2.has(id)) throw new Error(`indicator ${id} already exists`);
467
+ const now = Date.now();
468
+ const indicator = {
469
+ id,
470
+ feedId,
471
+ iocType,
472
+ value,
473
+ status: "pending",
474
+ createdAt: now,
475
+ lastSeenAt: now,
476
+ activatedAt: null,
477
+ resolvedAt: null,
478
+ metadata: { ...metadata },
479
+ };
480
+ _indicatorsV2.set(id, indicator);
481
+ return _copyIndicatorV2(indicator);
482
+ }
483
+
484
+ export function getIndicatorV2(id) {
485
+ const i = _indicatorsV2.get(id);
486
+ return i ? _copyIndicatorV2(i) : null;
487
+ }
488
+
489
+ export function listIndicatorsV2({ feedId, status } = {}) {
490
+ const out = [];
491
+ for (const i of _indicatorsV2.values()) {
492
+ if (feedId && i.feedId !== feedId) continue;
493
+ if (status && i.status !== status) continue;
494
+ out.push(_copyIndicatorV2(i));
495
+ }
496
+ return out;
497
+ }
498
+
499
+ export function setIndicatorStatusV2(id, next, { now = Date.now() } = {}) {
500
+ const i = _indicatorsV2.get(id);
501
+ if (!i) throw new Error(`indicator ${id} not found`);
502
+ if (!INDICATOR_TRANSITIONS_V2.has(next))
503
+ throw new Error(`unknown indicator status: ${next}`);
504
+ if (INDICATOR_TERMINALS_V2.has(i.status))
505
+ throw new Error(`indicator ${id} is in terminal state ${i.status}`);
506
+ const allowed = INDICATOR_TRANSITIONS_V2.get(i.status);
507
+ if (!allowed.has(next))
508
+ throw new Error(`cannot transition indicator from ${i.status} to ${next}`);
509
+ if (next === "active" && i.status === "pending") {
510
+ const count = getActiveIndicatorCountV2(i.feedId);
511
+ if (count >= _maxActiveIndicatorsPerFeedV2)
512
+ throw new Error(
513
+ `feed ${i.feedId} already at active-indicator cap (${_maxActiveIndicatorsPerFeedV2})`,
514
+ );
515
+ if (!i.activatedAt) i.activatedAt = now;
516
+ }
517
+ if (INDICATOR_TERMINALS_V2.has(next) && !i.resolvedAt) {
518
+ i.resolvedAt = now;
519
+ }
520
+ i.status = next;
521
+ i.lastSeenAt = now;
522
+ return _copyIndicatorV2(i);
523
+ }
524
+
525
+ export function activateIndicatorV2(id, opts) {
526
+ return setIndicatorStatusV2(id, "active", opts);
527
+ }
528
+ export function expireIndicatorV2(id, opts) {
529
+ return setIndicatorStatusV2(id, "expired", opts);
530
+ }
531
+ export function revokeIndicatorV2(id, opts) {
532
+ return setIndicatorStatusV2(id, "revoked", opts);
533
+ }
534
+ export function supersedeIndicatorV2(id, opts) {
535
+ return setIndicatorStatusV2(id, "superseded", opts);
536
+ }
537
+
538
+ export function refreshIndicatorV2(id, { now = Date.now() } = {}) {
539
+ const i = _indicatorsV2.get(id);
540
+ if (!i) throw new Error(`indicator ${id} not found`);
541
+ if (INDICATOR_TERMINALS_V2.has(i.status))
542
+ throw new Error(`indicator ${id} is in terminal state ${i.status}`);
543
+ i.lastSeenAt = now;
544
+ return _copyIndicatorV2(i);
545
+ }
546
+
547
+ export function autoDeprecateIdleFeedsV2({ now = Date.now() } = {}) {
548
+ const flipped = [];
549
+ for (const f of _feedsV2.values()) {
550
+ if (f.maturity !== "trusted") continue;
551
+ if (now - f.lastSeenAt > _feedIdleMsV2) {
552
+ f.maturity = "deprecated";
553
+ f.lastSeenAt = now;
554
+ flipped.push(_copyFeedV2(f));
555
+ }
556
+ }
557
+ return flipped;
558
+ }
559
+
560
+ export function autoExpireStaleIndicatorsV2({ now = Date.now() } = {}) {
561
+ const flipped = [];
562
+ for (const i of _indicatorsV2.values()) {
563
+ if (i.status !== "active") continue;
564
+ if (now - i.lastSeenAt > _indicatorStaleMsV2) {
565
+ i.status = "expired";
566
+ i.lastSeenAt = now;
567
+ if (!i.resolvedAt) i.resolvedAt = now;
568
+ flipped.push(_copyIndicatorV2(i));
569
+ }
570
+ }
571
+ return flipped;
572
+ }
573
+
574
+ export function getThreatIntelStatsV2() {
575
+ const feedsByMaturity = {};
576
+ for (const m of Object.values(FEED_MATURITY_V2)) feedsByMaturity[m] = 0;
577
+ for (const f of _feedsV2.values()) feedsByMaturity[f.maturity] += 1;
578
+
579
+ const indicatorsByStatus = {};
580
+ for (const s of Object.values(INDICATOR_LIFECYCLE_V2))
581
+ indicatorsByStatus[s] = 0;
582
+ for (const i of _indicatorsV2.values()) indicatorsByStatus[i.status] += 1;
583
+
584
+ return {
585
+ totalFeedsV2: _feedsV2.size,
586
+ totalIndicatorsV2: _indicatorsV2.size,
587
+ maxActiveFeedsPerOwner: _maxActiveFeedsPerOwnerV2,
588
+ maxActiveIndicatorsPerFeed: _maxActiveIndicatorsPerFeedV2,
589
+ feedIdleMs: _feedIdleMsV2,
590
+ indicatorStaleMs: _indicatorStaleMsV2,
591
+ feedsByMaturity,
592
+ indicatorsByStatus,
593
+ };
594
+ }
595
+
596
+ export function _resetStateThreatIntelV2() {
597
+ _feedsV2.clear();
598
+ _indicatorsV2.clear();
599
+ _maxActiveFeedsPerOwnerV2 = TI_DEFAULT_MAX_ACTIVE_FEEDS_PER_OWNER;
600
+ _maxActiveIndicatorsPerFeedV2 = TI_DEFAULT_MAX_ACTIVE_INDICATORS_PER_FEED;
601
+ _feedIdleMsV2 = TI_DEFAULT_FEED_IDLE_MS;
602
+ _indicatorStaleMsV2 = TI_DEFAULT_INDICATOR_STALE_MS;
603
+ }