chainlesschain 0.81.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.
- package/bin/chainlesschain.js +0 -0
- package/package.json +1 -1
- package/src/commands/agent-network.js +254 -1
- package/src/commands/audit.js +302 -0
- package/src/commands/automation.js +271 -1
- package/src/commands/codegen.js +224 -0
- package/src/commands/collab.js +341 -0
- package/src/commands/compliance.js +1035 -0
- package/src/commands/cowork.js +221 -0
- package/src/commands/dbevo.js +284 -0
- package/src/commands/dev.js +252 -0
- package/src/commands/did.js +358 -0
- package/src/commands/encrypt.js +341 -0
- package/src/commands/export.js +256 -1
- package/src/commands/fusion.js +258 -0
- package/src/commands/governance.js +325 -0
- package/src/commands/hardening.js +411 -0
- package/src/commands/hook.js +148 -0
- package/src/commands/import.js +252 -0
- package/src/commands/incentive.js +322 -0
- package/src/commands/infra.js +244 -0
- package/src/commands/instinct.js +260 -0
- package/src/commands/ipfs.js +318 -0
- package/src/commands/kg.js +387 -0
- package/src/commands/llm.js +263 -0
- package/src/commands/mcp.js +221 -0
- package/src/commands/memory.js +248 -0
- package/src/commands/multimodal.js +296 -0
- package/src/commands/nlprog.js +356 -0
- package/src/commands/note.js +244 -0
- package/src/commands/ops.js +354 -0
- package/src/commands/orchestrate.js +166 -0
- package/src/commands/org.js +277 -0
- package/src/commands/p2p.js +390 -0
- package/src/commands/perception.js +290 -0
- package/src/commands/permmem.js +251 -0
- package/src/commands/plugin-ecosystem.js +273 -0
- package/src/commands/pqc.js +393 -0
- package/src/commands/quantization.js +351 -0
- package/src/commands/rcache.js +271 -0
- package/src/commands/recommend.js +340 -0
- package/src/commands/runtime.js +307 -0
- package/src/commands/scim.js +262 -0
- package/src/commands/session.js +258 -0
- package/src/commands/skill.js +267 -1
- package/src/commands/social.js +256 -0
- package/src/commands/sso.js +186 -1
- package/src/commands/sync.js +256 -0
- package/src/commands/tech.js +338 -0
- package/src/commands/tenant.js +351 -0
- package/src/commands/tokens.js +269 -0
- package/src/commands/trust.js +249 -0
- package/src/commands/wallet.js +277 -0
- package/src/commands/workflow.js +171 -0
- package/src/index.js +4 -0
- package/src/lib/agent-coordinator.js +325 -0
- package/src/lib/agent-network.js +387 -0
- package/src/lib/agent-router.js +395 -0
- package/src/lib/aiops.js +478 -0
- package/src/lib/audit-logger.js +379 -0
- package/src/lib/automation-engine.js +330 -0
- package/src/lib/autonomous-developer.js +350 -0
- package/src/lib/code-agent.js +323 -0
- package/src/lib/collaboration-governance.js +364 -0
- package/src/lib/community-governance.js +436 -0
- package/src/lib/compliance-manager.js +434 -0
- package/src/lib/content-recommendation.js +469 -0
- package/src/lib/crypto-manager.js +350 -0
- package/src/lib/dbevo.js +338 -0
- package/src/lib/decentral-infra.js +340 -0
- package/src/lib/did-manager.js +367 -0
- package/src/lib/hardening-manager.js +348 -0
- package/src/lib/hook-manager.js +380 -0
- package/src/lib/instinct-manager.js +332 -0
- package/src/lib/ipfs-storage.js +334 -0
- package/src/lib/knowledge-exporter.js +381 -0
- package/src/lib/knowledge-graph.js +432 -0
- package/src/lib/knowledge-importer.js +379 -0
- package/src/lib/llm-providers.js +391 -0
- package/src/lib/mcp-registry.js +333 -0
- package/src/lib/memory-manager.js +330 -0
- package/src/lib/multimodal.js +346 -0
- package/src/lib/nl-programming.js +343 -0
- package/src/lib/note-versioning.js +327 -0
- package/src/lib/org-manager.js +323 -0
- package/src/lib/p2p-manager.js +387 -0
- package/src/lib/perception.js +346 -0
- package/src/lib/perf-tuning.js +4 -1
- package/src/lib/permanent-memory.js +320 -0
- package/src/lib/plugin-ecosystem.js +377 -0
- package/src/lib/pqc-manager.js +368 -0
- package/src/lib/protocol-fusion.js +417 -0
- package/src/lib/quantization.js +325 -0
- package/src/lib/response-cache.js +327 -0
- package/src/lib/scim-manager.js +329 -0
- package/src/lib/session-manager.js +329 -0
- package/src/lib/skill-loader.js +377 -0
- package/src/lib/social-manager.js +326 -0
- package/src/lib/sso-manager.js +332 -0
- package/src/lib/sync-manager.js +326 -0
- package/src/lib/tech-learning-engine.js +369 -0
- package/src/lib/tenant-saas.js +460 -0
- package/src/lib/threat-intel.js +335 -0
- package/src/lib/token-incentive.js +293 -0
- package/src/lib/token-tracker.js +329 -0
- package/src/lib/trust-security.js +390 -0
- package/src/lib/ueba.js +389 -0
- package/src/lib/universal-runtime.js +325 -0
- package/src/lib/wallet-manager.js +326 -0
- package/src/lib/workflow-engine.js +322 -0
package/src/lib/threat-intel.js
CHANGED
|
@@ -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
|
+
}
|
|
@@ -511,3 +511,296 @@ export function _resetState() {
|
|
|
511
511
|
_contributions.clear();
|
|
512
512
|
_seq = 0;
|
|
513
513
|
}
|
|
514
|
+
|
|
515
|
+
// ═══════════════════════════════════════════════════════════════
|
|
516
|
+
// Phase 66 V2 — Account + Claim lifecycle, per-user claim cap
|
|
517
|
+
// ═══════════════════════════════════════════════════════════════
|
|
518
|
+
|
|
519
|
+
export const ACCOUNT_STATUS_V2 = Object.freeze({
|
|
520
|
+
ACTIVE: "active",
|
|
521
|
+
FROZEN: "frozen",
|
|
522
|
+
CLOSED: "closed",
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
export const CLAIM_STATUS_V2 = Object.freeze({
|
|
526
|
+
PENDING: "pending",
|
|
527
|
+
APPROVED: "approved",
|
|
528
|
+
PAID: "paid",
|
|
529
|
+
REJECTED: "rejected",
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
export const TOKEN_DEFAULT_MAX_PENDING_CLAIMS_PER_USER = 50;
|
|
533
|
+
export const TOKEN_DEFAULT_CLAIM_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
534
|
+
export const TOKEN_DEFAULT_MAX_CLAIM_AMOUNT = 10000;
|
|
535
|
+
|
|
536
|
+
let _maxPendingClaimsPerUser = TOKEN_DEFAULT_MAX_PENDING_CLAIMS_PER_USER;
|
|
537
|
+
let _claimExpiryMs = TOKEN_DEFAULT_CLAIM_EXPIRY_MS;
|
|
538
|
+
let _maxClaimAmount = TOKEN_DEFAULT_MAX_CLAIM_AMOUNT;
|
|
539
|
+
|
|
540
|
+
const _accountStatesV2 = new Map();
|
|
541
|
+
const _claimStatesV2 = new Map();
|
|
542
|
+
|
|
543
|
+
const ACCOUNT_TRANSITIONS_V2 = new Map([
|
|
544
|
+
["active", new Set(["frozen", "closed"])],
|
|
545
|
+
["frozen", new Set(["active", "closed"])],
|
|
546
|
+
]);
|
|
547
|
+
const ACCOUNT_TERMINALS_V2 = new Set(["closed"]);
|
|
548
|
+
|
|
549
|
+
const CLAIM_TRANSITIONS_V2 = new Map([
|
|
550
|
+
["pending", new Set(["approved", "rejected"])],
|
|
551
|
+
["approved", new Set(["paid", "rejected"])],
|
|
552
|
+
]);
|
|
553
|
+
const CLAIM_TERMINALS_V2 = new Set(["paid", "rejected"]);
|
|
554
|
+
|
|
555
|
+
function _positiveInt(n, label) {
|
|
556
|
+
const v = Number(n);
|
|
557
|
+
if (!Number.isFinite(v) || v <= 0) {
|
|
558
|
+
throw new Error(`${label} must be a positive integer`);
|
|
559
|
+
}
|
|
560
|
+
return Math.floor(v);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
export function setMaxPendingClaimsPerUser(n) {
|
|
564
|
+
_maxPendingClaimsPerUser = _positiveInt(n, "maxPendingClaimsPerUser");
|
|
565
|
+
return _maxPendingClaimsPerUser;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
export function setClaimExpiryMs(ms) {
|
|
569
|
+
_claimExpiryMs = _positiveInt(ms, "claimExpiryMs");
|
|
570
|
+
return _claimExpiryMs;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
export function setMaxClaimAmount(n) {
|
|
574
|
+
const v = Number(n);
|
|
575
|
+
if (!Number.isFinite(v) || v <= 0) {
|
|
576
|
+
throw new Error("maxClaimAmount must be a positive number");
|
|
577
|
+
}
|
|
578
|
+
_maxClaimAmount = v;
|
|
579
|
+
return _maxClaimAmount;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
export function getMaxPendingClaimsPerUser() {
|
|
583
|
+
return _maxPendingClaimsPerUser;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
export function getClaimExpiryMs() {
|
|
587
|
+
return _claimExpiryMs;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
export function getMaxClaimAmount() {
|
|
591
|
+
return _maxClaimAmount;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
export function getPendingClaimCount(userId) {
|
|
595
|
+
let count = 0;
|
|
596
|
+
for (const entry of _claimStatesV2.values()) {
|
|
597
|
+
if (entry.status === CLAIM_STATUS_V2.PENDING) {
|
|
598
|
+
if (!userId || entry.userId === userId) count += 1;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return count;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/* ── Account V2 ─────────────────────────────────────────────── */
|
|
605
|
+
|
|
606
|
+
export function registerAccountV2(db, { accountId, metadata } = {}) {
|
|
607
|
+
if (!accountId) throw new Error("accountId is required");
|
|
608
|
+
if (_accountStatesV2.has(accountId)) {
|
|
609
|
+
throw new Error(`Account already registered: ${accountId}`);
|
|
610
|
+
}
|
|
611
|
+
const now = Date.now();
|
|
612
|
+
const entry = {
|
|
613
|
+
accountId,
|
|
614
|
+
status: ACCOUNT_STATUS_V2.ACTIVE,
|
|
615
|
+
reason: null,
|
|
616
|
+
metadata: metadata ? { ...metadata } : {},
|
|
617
|
+
createdAt: now,
|
|
618
|
+
updatedAt: now,
|
|
619
|
+
};
|
|
620
|
+
_accountStatesV2.set(accountId, entry);
|
|
621
|
+
return { ...entry };
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
export function getAccountStatusV2(accountId) {
|
|
625
|
+
const entry = _accountStatesV2.get(accountId);
|
|
626
|
+
return entry ? { ...entry } : null;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
export function setAccountStatusV2(db, accountId, newStatus, patch = {}) {
|
|
630
|
+
const entry = _accountStatesV2.get(accountId);
|
|
631
|
+
if (!entry) throw new Error(`Account not found: ${accountId}`);
|
|
632
|
+
if (!Object.values(ACCOUNT_STATUS_V2).includes(newStatus)) {
|
|
633
|
+
throw new Error(`Invalid account status: ${newStatus}`);
|
|
634
|
+
}
|
|
635
|
+
if (ACCOUNT_TERMINALS_V2.has(entry.status)) {
|
|
636
|
+
throw new Error(`Account is terminal: ${entry.status}`);
|
|
637
|
+
}
|
|
638
|
+
const allowed = ACCOUNT_TRANSITIONS_V2.get(entry.status) || new Set();
|
|
639
|
+
if (!allowed.has(newStatus)) {
|
|
640
|
+
throw new Error(`Invalid transition: ${entry.status} → ${newStatus}`);
|
|
641
|
+
}
|
|
642
|
+
entry.status = newStatus;
|
|
643
|
+
entry.updatedAt = Date.now();
|
|
644
|
+
if (patch.reason !== undefined) entry.reason = patch.reason;
|
|
645
|
+
if (patch.metadata) entry.metadata = { ...entry.metadata, ...patch.metadata };
|
|
646
|
+
return { ...entry };
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
export function freezeAccount(db, accountId, reason) {
|
|
650
|
+
return setAccountStatusV2(db, accountId, ACCOUNT_STATUS_V2.FROZEN, {
|
|
651
|
+
reason,
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
export function unfreezeAccount(db, accountId, reason) {
|
|
656
|
+
return setAccountStatusV2(db, accountId, ACCOUNT_STATUS_V2.ACTIVE, {
|
|
657
|
+
reason,
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
export function closeAccount(db, accountId, reason) {
|
|
662
|
+
return setAccountStatusV2(db, accountId, ACCOUNT_STATUS_V2.CLOSED, {
|
|
663
|
+
reason,
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/* ── Claim V2 ───────────────────────────────────────────────── */
|
|
668
|
+
|
|
669
|
+
export function submitClaimV2(
|
|
670
|
+
db,
|
|
671
|
+
{ claimId, userId, amount, contributionId, metadata } = {},
|
|
672
|
+
) {
|
|
673
|
+
if (!claimId) throw new Error("claimId is required");
|
|
674
|
+
if (!userId) throw new Error("userId is required");
|
|
675
|
+
const v = Number(amount);
|
|
676
|
+
if (!Number.isFinite(v) || v <= 0) {
|
|
677
|
+
throw new Error(`Invalid amount: ${amount} (must be > 0)`);
|
|
678
|
+
}
|
|
679
|
+
if (v > _maxClaimAmount) {
|
|
680
|
+
throw new Error(`Amount exceeds maxClaimAmount: ${v} > ${_maxClaimAmount}`);
|
|
681
|
+
}
|
|
682
|
+
if (_claimStatesV2.has(claimId)) {
|
|
683
|
+
throw new Error(`Claim already registered: ${claimId}`);
|
|
684
|
+
}
|
|
685
|
+
// Reject if account exists in V2 state and is frozen/closed
|
|
686
|
+
const accountEntry = _accountStatesV2.get(userId);
|
|
687
|
+
if (accountEntry && accountEntry.status !== ACCOUNT_STATUS_V2.ACTIVE) {
|
|
688
|
+
throw new Error(`Account not active: ${accountEntry.status}`);
|
|
689
|
+
}
|
|
690
|
+
// Enforce per-user pending cap
|
|
691
|
+
const pendingCount = getPendingClaimCount(userId);
|
|
692
|
+
if (pendingCount >= _maxPendingClaimsPerUser) {
|
|
693
|
+
throw new Error(
|
|
694
|
+
`Max pending claims reached (${pendingCount}/${_maxPendingClaimsPerUser}) for user ${userId}`,
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
const now = Date.now();
|
|
698
|
+
const entry = {
|
|
699
|
+
claimId,
|
|
700
|
+
userId,
|
|
701
|
+
amount: v,
|
|
702
|
+
contributionId: contributionId || null,
|
|
703
|
+
status: CLAIM_STATUS_V2.PENDING,
|
|
704
|
+
reason: null,
|
|
705
|
+
metadata: metadata ? { ...metadata } : {},
|
|
706
|
+
createdAt: now,
|
|
707
|
+
updatedAt: now,
|
|
708
|
+
paidAt: null,
|
|
709
|
+
};
|
|
710
|
+
_claimStatesV2.set(claimId, entry);
|
|
711
|
+
return { ...entry };
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
export function getClaimStatusV2(claimId) {
|
|
715
|
+
const entry = _claimStatesV2.get(claimId);
|
|
716
|
+
return entry ? { ...entry } : null;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
export function setClaimStatusV2(db, claimId, newStatus, patch = {}) {
|
|
720
|
+
const entry = _claimStatesV2.get(claimId);
|
|
721
|
+
if (!entry) throw new Error(`Claim not found: ${claimId}`);
|
|
722
|
+
if (!Object.values(CLAIM_STATUS_V2).includes(newStatus)) {
|
|
723
|
+
throw new Error(`Invalid claim status: ${newStatus}`);
|
|
724
|
+
}
|
|
725
|
+
if (CLAIM_TERMINALS_V2.has(entry.status)) {
|
|
726
|
+
throw new Error(`Claim is terminal: ${entry.status}`);
|
|
727
|
+
}
|
|
728
|
+
const allowed = CLAIM_TRANSITIONS_V2.get(entry.status) || new Set();
|
|
729
|
+
if (!allowed.has(newStatus)) {
|
|
730
|
+
throw new Error(`Invalid transition: ${entry.status} → ${newStatus}`);
|
|
731
|
+
}
|
|
732
|
+
entry.status = newStatus;
|
|
733
|
+
entry.updatedAt = Date.now();
|
|
734
|
+
if (newStatus === CLAIM_STATUS_V2.PAID) entry.paidAt = entry.updatedAt;
|
|
735
|
+
if (patch.reason !== undefined) entry.reason = patch.reason;
|
|
736
|
+
if (patch.metadata) entry.metadata = { ...entry.metadata, ...patch.metadata };
|
|
737
|
+
return { ...entry };
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
export function approveClaim(db, claimId, reason) {
|
|
741
|
+
return setClaimStatusV2(db, claimId, CLAIM_STATUS_V2.APPROVED, { reason });
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
export function rejectClaim(db, claimId, reason) {
|
|
745
|
+
return setClaimStatusV2(db, claimId, CLAIM_STATUS_V2.REJECTED, { reason });
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
export function payClaim(db, claimId, reason) {
|
|
749
|
+
return setClaimStatusV2(db, claimId, CLAIM_STATUS_V2.PAID, { reason });
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
export function autoExpireUnclaimedClaims(db, nowMs = Date.now()) {
|
|
753
|
+
const expired = [];
|
|
754
|
+
for (const entry of _claimStatesV2.values()) {
|
|
755
|
+
if (entry.status !== CLAIM_STATUS_V2.PENDING) continue;
|
|
756
|
+
if (nowMs - entry.createdAt > _claimExpiryMs) {
|
|
757
|
+
entry.status = CLAIM_STATUS_V2.REJECTED;
|
|
758
|
+
entry.reason = "expired";
|
|
759
|
+
entry.updatedAt = nowMs;
|
|
760
|
+
expired.push({ ...entry });
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return expired;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/* ── Stats V2 ───────────────────────────────────────────────── */
|
|
767
|
+
|
|
768
|
+
export function getTokenStatsV2() {
|
|
769
|
+
const accountsByStatus = { active: 0, frozen: 0, closed: 0 };
|
|
770
|
+
const claimsByStatus = { pending: 0, approved: 0, paid: 0, rejected: 0 };
|
|
771
|
+
let totalClaimedAmount = 0;
|
|
772
|
+
let totalPaidAmount = 0;
|
|
773
|
+
|
|
774
|
+
for (const entry of _accountStatesV2.values()) {
|
|
775
|
+
if (accountsByStatus[entry.status] !== undefined)
|
|
776
|
+
accountsByStatus[entry.status] += 1;
|
|
777
|
+
}
|
|
778
|
+
for (const entry of _claimStatesV2.values()) {
|
|
779
|
+
if (claimsByStatus[entry.status] !== undefined)
|
|
780
|
+
claimsByStatus[entry.status] += 1;
|
|
781
|
+
totalClaimedAmount += entry.amount;
|
|
782
|
+
if (entry.status === CLAIM_STATUS_V2.PAID) totalPaidAmount += entry.amount;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
return {
|
|
786
|
+
totalAccounts: _accountStatesV2.size,
|
|
787
|
+
totalClaims: _claimStatesV2.size,
|
|
788
|
+
totalClaimedAmount: Number(totalClaimedAmount.toFixed(4)),
|
|
789
|
+
totalPaidAmount: Number(totalPaidAmount.toFixed(4)),
|
|
790
|
+
maxPendingClaimsPerUser: _maxPendingClaimsPerUser,
|
|
791
|
+
claimExpiryMs: _claimExpiryMs,
|
|
792
|
+
maxClaimAmount: _maxClaimAmount,
|
|
793
|
+
accountsByStatus,
|
|
794
|
+
claimsByStatus,
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/* ── Reset V2 (tests) ───────────────────────────────────────── */
|
|
799
|
+
|
|
800
|
+
export function _resetStateV2() {
|
|
801
|
+
_accountStatesV2.clear();
|
|
802
|
+
_claimStatesV2.clear();
|
|
803
|
+
_maxPendingClaimsPerUser = TOKEN_DEFAULT_MAX_PENDING_CLAIMS_PER_USER;
|
|
804
|
+
_claimExpiryMs = TOKEN_DEFAULT_CLAIM_EXPIRY_MS;
|
|
805
|
+
_maxClaimAmount = TOKEN_DEFAULT_MAX_CLAIM_AMOUNT;
|
|
806
|
+
}
|