chainlesschain 0.132.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.
- package/package.json +1 -1
- package/src/commands/a2a.js +62 -0
- package/src/commands/activitypub.js +61 -0
- package/src/commands/agent.js +117 -0
- package/src/commands/bi.js +61 -0
- package/src/commands/bm25.js +78 -0
- package/src/commands/browse.js +64 -0
- package/src/commands/ccron.js +78 -0
- package/src/commands/compliance.js +40 -0
- package/src/commands/compt.js +78 -0
- package/src/commands/consol.js +231 -0
- package/src/commands/cowork.js +42 -0
- package/src/commands/crosschain.js +62 -0
- package/src/commands/dao.js +62 -0
- package/src/commands/dlp.js +61 -0
- package/src/commands/economy.js +56 -0
- package/src/commands/evolution.js +56 -0
- package/src/commands/evomap.js +61 -0
- package/src/commands/fflag.js +178 -0
- package/src/commands/git.js +45 -0
- package/src/commands/hmemory.js +56 -0
- package/src/commands/inference.js +42 -0
- package/src/commands/itbudget.js +45 -0
- package/src/commands/lowcode.js +44 -0
- package/src/commands/matrix.js +62 -0
- package/src/commands/mcpscaf.js +41 -0
- package/src/commands/meminj.js +41 -0
- package/src/commands/nostr.js +62 -0
- package/src/commands/orchgov.js +45 -0
- package/src/commands/pdfp.js +78 -0
- package/src/commands/perf.js +39 -0
- package/src/commands/perm.js +45 -0
- package/src/commands/pipeline.js +57 -1
- package/src/commands/planmode.js +45 -0
- package/src/commands/promcomp.js +82 -0
- package/src/commands/recommend.js +42 -0
- package/src/commands/seshhook.js +41 -0
- package/src/commands/seshsearch.js +41 -0
- package/src/commands/seshtail.js +41 -0
- package/src/commands/seshu.js +41 -0
- package/src/commands/sganal.js +78 -0
- package/src/commands/siem.js +40 -0
- package/src/commands/slotfill.js +41 -0
- package/src/commands/social.js +34 -0
- package/src/commands/svccont.js +45 -0
- package/src/commands/tms.js +45 -0
- package/src/commands/topiccls.js +45 -0
- package/src/commands/uprof.js +45 -0
- package/src/commands/vcheck.js +78 -0
- package/src/commands/webfetch.js +41 -0
- package/src/commands/zkp.js +62 -0
- package/src/harness/prompt-compressor.js +331 -0
- package/src/index.js +61 -1
- package/src/lib/a2a-protocol.js +105 -0
- package/src/lib/activitypub-bridge.js +105 -0
- package/src/lib/agent-economy.js +105 -0
- package/src/lib/app-builder.js +105 -0
- package/src/lib/autonomous-agent.js +105 -0
- package/src/lib/bi-engine.js +105 -0
- package/src/lib/bm25-search.js +81 -0
- package/src/lib/browser-automation.js +105 -0
- package/src/lib/compliance-framework-reporter.js +105 -0
- package/src/lib/compression-telemetry.js +81 -0
- package/src/lib/content-recommender.js +105 -0
- package/src/lib/cowork-cron.js +81 -0
- package/src/lib/cowork-task-runner.js +105 -0
- package/src/lib/cross-chain.js +105 -0
- package/src/lib/dao-governance.js +105 -0
- package/src/lib/dlp-engine.js +105 -0
- package/src/lib/evolution-system.js +105 -0
- package/src/lib/evomap-manager.js +105 -0
- package/src/lib/execution-backend.js +105 -0
- package/src/lib/feature-flags.js +85 -0
- package/src/lib/git-integration.js +105 -0
- package/src/lib/hierarchical-memory.js +105 -0
- package/src/lib/inference-network.js +105 -0
- package/src/lib/iteration-budget.js +105 -0
- package/src/lib/matrix-bridge.js +105 -0
- package/src/lib/mcp-scaffold.js +81 -0
- package/src/lib/memory-injection.js +81 -0
- package/src/lib/nostr-bridge.js +105 -0
- package/src/lib/orchestrator.js +105 -0
- package/src/lib/pdf-parser.js +81 -0
- package/src/lib/perf-tuning.js +105 -0
- package/src/lib/permission-engine.js +81 -0
- package/src/lib/pipeline-orchestrator.js +105 -0
- package/src/lib/plan-mode.js +81 -0
- package/src/lib/prompt-compressor.js +1 -10
- package/src/lib/service-container.js +81 -0
- package/src/lib/session-consolidator.js +105 -0
- package/src/lib/session-hooks.js +81 -0
- package/src/lib/session-search.js +81 -0
- package/src/lib/session-tail.js +81 -0
- package/src/lib/session-usage.js +83 -0
- package/src/lib/siem-exporter.js +105 -0
- package/src/lib/slot-filler.js +81 -0
- package/src/lib/social-graph-analytics.js +81 -0
- package/src/lib/social-graph.js +81 -0
- package/src/lib/sub-agent-registry.js +110 -0
- package/src/lib/task-model-selector.js +81 -0
- package/src/lib/todo-manager.js +105 -0
- package/src/lib/topic-classifier.js +105 -0
- package/src/lib/user-profile.js +81 -0
- package/src/lib/version-checker.js +81 -0
- package/src/lib/web-fetch.js +81 -0
- package/src/lib/zkp-engine.js +105 -0
package/src/lib/nostr-bridge.js
CHANGED
|
@@ -369,3 +369,108 @@ export function _resetState() {
|
|
|
369
369
|
_keypairs.clear();
|
|
370
370
|
_didMappings.clear();
|
|
371
371
|
}
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
// ===== V2 Surface: Nostr Bridge governance overlay (CLI v0.134.0) =====
|
|
375
|
+
export const NOSTR_RELAY_MATURITY_V2 = Object.freeze({
|
|
376
|
+
PENDING: "pending", ACTIVE: "active", OFFLINE: "offline", RETIRED: "retired",
|
|
377
|
+
});
|
|
378
|
+
export const NOSTR_EVENT_LIFECYCLE_V2 = Object.freeze({
|
|
379
|
+
QUEUED: "queued", PUBLISHING: "publishing", PUBLISHED: "published", FAILED: "failed", CANCELLED: "cancelled",
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
const _nsRelayTrans = new Map([
|
|
383
|
+
[NOSTR_RELAY_MATURITY_V2.PENDING, new Set([NOSTR_RELAY_MATURITY_V2.ACTIVE, NOSTR_RELAY_MATURITY_V2.RETIRED])],
|
|
384
|
+
[NOSTR_RELAY_MATURITY_V2.ACTIVE, new Set([NOSTR_RELAY_MATURITY_V2.OFFLINE, NOSTR_RELAY_MATURITY_V2.RETIRED])],
|
|
385
|
+
[NOSTR_RELAY_MATURITY_V2.OFFLINE, new Set([NOSTR_RELAY_MATURITY_V2.ACTIVE, NOSTR_RELAY_MATURITY_V2.RETIRED])],
|
|
386
|
+
[NOSTR_RELAY_MATURITY_V2.RETIRED, new Set()],
|
|
387
|
+
]);
|
|
388
|
+
const _nsRelayTerminal = new Set([NOSTR_RELAY_MATURITY_V2.RETIRED]);
|
|
389
|
+
const _nsEventTrans = new Map([
|
|
390
|
+
[NOSTR_EVENT_LIFECYCLE_V2.QUEUED, new Set([NOSTR_EVENT_LIFECYCLE_V2.PUBLISHING, NOSTR_EVENT_LIFECYCLE_V2.CANCELLED])],
|
|
391
|
+
[NOSTR_EVENT_LIFECYCLE_V2.PUBLISHING, new Set([NOSTR_EVENT_LIFECYCLE_V2.PUBLISHED, NOSTR_EVENT_LIFECYCLE_V2.FAILED, NOSTR_EVENT_LIFECYCLE_V2.CANCELLED])],
|
|
392
|
+
[NOSTR_EVENT_LIFECYCLE_V2.PUBLISHED, new Set()],
|
|
393
|
+
[NOSTR_EVENT_LIFECYCLE_V2.FAILED, new Set()],
|
|
394
|
+
[NOSTR_EVENT_LIFECYCLE_V2.CANCELLED, new Set()],
|
|
395
|
+
]);
|
|
396
|
+
|
|
397
|
+
const _nsRelays = new Map();
|
|
398
|
+
const _nsEvents = new Map();
|
|
399
|
+
let _nsMaxActivePerOwner = 10;
|
|
400
|
+
let _nsMaxPendingPerRelay = 30;
|
|
401
|
+
let _nsRelayIdleMs = 60 * 60 * 1000;
|
|
402
|
+
let _nsEventStuckMs = 2 * 60 * 1000;
|
|
403
|
+
|
|
404
|
+
function _nsPos(n, lbl) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${lbl} must be positive integer`); return v; }
|
|
405
|
+
|
|
406
|
+
export function setMaxActiveNostrRelaysPerOwnerV2(n) { _nsMaxActivePerOwner = _nsPos(n, "maxActiveNostrRelaysPerOwner"); }
|
|
407
|
+
export function getMaxActiveNostrRelaysPerOwnerV2() { return _nsMaxActivePerOwner; }
|
|
408
|
+
export function setMaxPendingNostrEventsPerRelayV2(n) { _nsMaxPendingPerRelay = _nsPos(n, "maxPendingNostrEventsPerRelay"); }
|
|
409
|
+
export function getMaxPendingNostrEventsPerRelayV2() { return _nsMaxPendingPerRelay; }
|
|
410
|
+
export function setNostrRelayIdleMsV2(n) { _nsRelayIdleMs = _nsPos(n, "nostrRelayIdleMs"); }
|
|
411
|
+
export function getNostrRelayIdleMsV2() { return _nsRelayIdleMs; }
|
|
412
|
+
export function setNostrEventStuckMsV2(n) { _nsEventStuckMs = _nsPos(n, "nostrEventStuckMs"); }
|
|
413
|
+
export function getNostrEventStuckMsV2() { return _nsEventStuckMs; }
|
|
414
|
+
|
|
415
|
+
export function _resetStateNostrBridgeV2() {
|
|
416
|
+
_nsRelays.clear(); _nsEvents.clear();
|
|
417
|
+
_nsMaxActivePerOwner = 10; _nsMaxPendingPerRelay = 30;
|
|
418
|
+
_nsRelayIdleMs = 60 * 60 * 1000; _nsEventStuckMs = 2 * 60 * 1000;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export function registerNostrRelayV2({ id, owner, url, metadata } = {}) {
|
|
422
|
+
if (!id || typeof id !== "string") throw new Error("id is required");
|
|
423
|
+
if (!owner || typeof owner !== "string") throw new Error("owner is required");
|
|
424
|
+
if (_nsRelays.has(id)) throw new Error(`nostr relay ${id} already registered`);
|
|
425
|
+
const now = Date.now();
|
|
426
|
+
const r = { id, owner, url: url || "", status: NOSTR_RELAY_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, retiredAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
|
|
427
|
+
_nsRelays.set(id, r);
|
|
428
|
+
return { ...r, metadata: { ...r.metadata } };
|
|
429
|
+
}
|
|
430
|
+
function _nsCheckR(from, to) { const a = _nsRelayTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid nostr relay transition ${from} → ${to}`); }
|
|
431
|
+
function _nsCountActive(owner) { let n = 0; for (const r of _nsRelays.values()) if (r.owner === owner && r.status === NOSTR_RELAY_MATURITY_V2.ACTIVE) n++; return n; }
|
|
432
|
+
|
|
433
|
+
export function activateNostrRelayV2(id) {
|
|
434
|
+
const r = _nsRelays.get(id); if (!r) throw new Error(`nostr relay ${id} not found`);
|
|
435
|
+
_nsCheckR(r.status, NOSTR_RELAY_MATURITY_V2.ACTIVE);
|
|
436
|
+
const recovery = r.status === NOSTR_RELAY_MATURITY_V2.OFFLINE;
|
|
437
|
+
if (!recovery) { const a = _nsCountActive(r.owner); if (a >= _nsMaxActivePerOwner) throw new Error(`max active nostr relays per owner (${_nsMaxActivePerOwner}) reached for ${r.owner}`); }
|
|
438
|
+
const now = Date.now(); r.status = NOSTR_RELAY_MATURITY_V2.ACTIVE; r.updatedAt = now; r.lastTouchedAt = now; if (!r.activatedAt) r.activatedAt = now;
|
|
439
|
+
return { ...r, metadata: { ...r.metadata } };
|
|
440
|
+
}
|
|
441
|
+
export function offlineNostrRelayV2(id) { const r = _nsRelays.get(id); if (!r) throw new Error(`nostr relay ${id} not found`); _nsCheckR(r.status, NOSTR_RELAY_MATURITY_V2.OFFLINE); r.status = NOSTR_RELAY_MATURITY_V2.OFFLINE; r.updatedAt = Date.now(); return { ...r, metadata: { ...r.metadata } }; }
|
|
442
|
+
export function retireNostrRelayV2(id) { const r = _nsRelays.get(id); if (!r) throw new Error(`nostr relay ${id} not found`); _nsCheckR(r.status, NOSTR_RELAY_MATURITY_V2.RETIRED); const now = Date.now(); r.status = NOSTR_RELAY_MATURITY_V2.RETIRED; r.updatedAt = now; if (!r.retiredAt) r.retiredAt = now; return { ...r, metadata: { ...r.metadata } }; }
|
|
443
|
+
export function touchNostrRelayV2(id) { const r = _nsRelays.get(id); if (!r) throw new Error(`nostr relay ${id} not found`); if (_nsRelayTerminal.has(r.status)) throw new Error(`cannot touch terminal nostr relay ${id}`); const now = Date.now(); r.lastTouchedAt = now; r.updatedAt = now; return { ...r, metadata: { ...r.metadata } }; }
|
|
444
|
+
export function getNostrRelayV2(id) { const r = _nsRelays.get(id); if (!r) return null; return { ...r, metadata: { ...r.metadata } }; }
|
|
445
|
+
export function listNostrRelaysV2() { return [..._nsRelays.values()].map((r) => ({ ...r, metadata: { ...r.metadata } })); }
|
|
446
|
+
|
|
447
|
+
function _nsCountPending(rid) { let n = 0; for (const e of _nsEvents.values()) if (e.relayId === rid && (e.status === NOSTR_EVENT_LIFECYCLE_V2.QUEUED || e.status === NOSTR_EVENT_LIFECYCLE_V2.PUBLISHING)) n++; return n; }
|
|
448
|
+
|
|
449
|
+
export function createNostrEventV2({ id, relayId, kind, metadata } = {}) {
|
|
450
|
+
if (!id || typeof id !== "string") throw new Error("id is required");
|
|
451
|
+
if (!relayId || typeof relayId !== "string") throw new Error("relayId is required");
|
|
452
|
+
if (_nsEvents.has(id)) throw new Error(`nostr event ${id} already exists`);
|
|
453
|
+
if (!_nsRelays.has(relayId)) throw new Error(`nostr relay ${relayId} not found`);
|
|
454
|
+
const pending = _nsCountPending(relayId);
|
|
455
|
+
if (pending >= _nsMaxPendingPerRelay) throw new Error(`max pending nostr events per relay (${_nsMaxPendingPerRelay}) reached for ${relayId}`);
|
|
456
|
+
const now = Date.now();
|
|
457
|
+
const e = { id, relayId, kind: typeof kind === "number" ? kind : 1, status: NOSTR_EVENT_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
|
|
458
|
+
_nsEvents.set(id, e);
|
|
459
|
+
return { ...e, metadata: { ...e.metadata } };
|
|
460
|
+
}
|
|
461
|
+
function _nsCheckE(from, to) { const a = _nsEventTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid nostr event transition ${from} → ${to}`); }
|
|
462
|
+
export function startNostrEventV2(id) { const e = _nsEvents.get(id); if (!e) throw new Error(`nostr event ${id} not found`); _nsCheckE(e.status, NOSTR_EVENT_LIFECYCLE_V2.PUBLISHING); const now = Date.now(); e.status = NOSTR_EVENT_LIFECYCLE_V2.PUBLISHING; e.updatedAt = now; if (!e.startedAt) e.startedAt = now; return { ...e, metadata: { ...e.metadata } }; }
|
|
463
|
+
export function publishNostrEventV2(id) { const e = _nsEvents.get(id); if (!e) throw new Error(`nostr event ${id} not found`); _nsCheckE(e.status, NOSTR_EVENT_LIFECYCLE_V2.PUBLISHED); const now = Date.now(); e.status = NOSTR_EVENT_LIFECYCLE_V2.PUBLISHED; e.updatedAt = now; if (!e.settledAt) e.settledAt = now; return { ...e, metadata: { ...e.metadata } }; }
|
|
464
|
+
export function failNostrEventV2(id, reason) { const e = _nsEvents.get(id); if (!e) throw new Error(`nostr event ${id} not found`); _nsCheckE(e.status, NOSTR_EVENT_LIFECYCLE_V2.FAILED); const now = Date.now(); e.status = NOSTR_EVENT_LIFECYCLE_V2.FAILED; e.updatedAt = now; if (!e.settledAt) e.settledAt = now; if (reason) e.metadata.failReason = String(reason); return { ...e, metadata: { ...e.metadata } }; }
|
|
465
|
+
export function cancelNostrEventV2(id, reason) { const e = _nsEvents.get(id); if (!e) throw new Error(`nostr event ${id} not found`); _nsCheckE(e.status, NOSTR_EVENT_LIFECYCLE_V2.CANCELLED); const now = Date.now(); e.status = NOSTR_EVENT_LIFECYCLE_V2.CANCELLED; e.updatedAt = now; if (!e.settledAt) e.settledAt = now; if (reason) e.metadata.cancelReason = String(reason); return { ...e, metadata: { ...e.metadata } }; }
|
|
466
|
+
export function getNostrEventV2(id) { const e = _nsEvents.get(id); if (!e) return null; return { ...e, metadata: { ...e.metadata } }; }
|
|
467
|
+
export function listNostrEventsV2() { return [..._nsEvents.values()].map((e) => ({ ...e, metadata: { ...e.metadata } })); }
|
|
468
|
+
|
|
469
|
+
export function autoOfflineIdleNostrRelaysV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const r of _nsRelays.values()) if (r.status === NOSTR_RELAY_MATURITY_V2.ACTIVE && (t - r.lastTouchedAt) >= _nsRelayIdleMs) { r.status = NOSTR_RELAY_MATURITY_V2.OFFLINE; r.updatedAt = t; flipped.push(r.id); } return { flipped, count: flipped.length }; }
|
|
470
|
+
export function autoFailStuckNostrEventsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const e of _nsEvents.values()) if (e.status === NOSTR_EVENT_LIFECYCLE_V2.PUBLISHING && e.startedAt != null && (t - e.startedAt) >= _nsEventStuckMs) { e.status = NOSTR_EVENT_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 }; }
|
|
471
|
+
|
|
472
|
+
export function getNostrBridgeStatsV2() {
|
|
473
|
+
const relaysByStatus = {}; for (const s of Object.values(NOSTR_RELAY_MATURITY_V2)) relaysByStatus[s] = 0; for (const r of _nsRelays.values()) relaysByStatus[r.status]++;
|
|
474
|
+
const eventsByStatus = {}; for (const s of Object.values(NOSTR_EVENT_LIFECYCLE_V2)) eventsByStatus[s] = 0; for (const e of _nsEvents.values()) eventsByStatus[e.status]++;
|
|
475
|
+
return { totalRelaysV2: _nsRelays.size, totalEventsV2: _nsEvents.size, maxActiveNostrRelaysPerOwner: _nsMaxActivePerOwner, maxPendingNostrEventsPerRelay: _nsMaxPendingPerRelay, nostrRelayIdleMs: _nsRelayIdleMs, nostrEventStuckMs: _nsEventStuckMs, relaysByStatus, eventsByStatus };
|
|
476
|
+
}
|
package/src/lib/orchestrator.js
CHANGED
|
@@ -436,3 +436,108 @@ function _extractChangedFiles(output) {
|
|
|
436
436
|
const matches = output.match(/(?:modified|created|deleted):\s+(\S+)/g) || [];
|
|
437
437
|
return matches.map((m) => m.split(/:\s+/)[1]).filter(Boolean);
|
|
438
438
|
}
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
// ===== V2 Surface: Orchestrator governance overlay (CLI v0.140.0) =====
|
|
442
|
+
export const ORCH_PROFILE_MATURITY_V2 = Object.freeze({
|
|
443
|
+
PENDING: "pending", ACTIVE: "active", PAUSED: "paused", RETIRED: "retired",
|
|
444
|
+
});
|
|
445
|
+
export const ORCH_TASK_LIFECYCLE_V2 = Object.freeze({
|
|
446
|
+
QUEUED: "queued", DISPATCHING: "dispatching", COMPLETED: "completed", FAILED: "failed", CANCELLED: "cancelled",
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
const _opTrans = new Map([
|
|
450
|
+
[ORCH_PROFILE_MATURITY_V2.PENDING, new Set([ORCH_PROFILE_MATURITY_V2.ACTIVE, ORCH_PROFILE_MATURITY_V2.RETIRED])],
|
|
451
|
+
[ORCH_PROFILE_MATURITY_V2.ACTIVE, new Set([ORCH_PROFILE_MATURITY_V2.PAUSED, ORCH_PROFILE_MATURITY_V2.RETIRED])],
|
|
452
|
+
[ORCH_PROFILE_MATURITY_V2.PAUSED, new Set([ORCH_PROFILE_MATURITY_V2.ACTIVE, ORCH_PROFILE_MATURITY_V2.RETIRED])],
|
|
453
|
+
[ORCH_PROFILE_MATURITY_V2.RETIRED, new Set()],
|
|
454
|
+
]);
|
|
455
|
+
const _opTerminal = new Set([ORCH_PROFILE_MATURITY_V2.RETIRED]);
|
|
456
|
+
const _otTrans = new Map([
|
|
457
|
+
[ORCH_TASK_LIFECYCLE_V2.QUEUED, new Set([ORCH_TASK_LIFECYCLE_V2.DISPATCHING, ORCH_TASK_LIFECYCLE_V2.CANCELLED])],
|
|
458
|
+
[ORCH_TASK_LIFECYCLE_V2.DISPATCHING, new Set([ORCH_TASK_LIFECYCLE_V2.COMPLETED, ORCH_TASK_LIFECYCLE_V2.FAILED, ORCH_TASK_LIFECYCLE_V2.CANCELLED])],
|
|
459
|
+
[ORCH_TASK_LIFECYCLE_V2.COMPLETED, new Set()],
|
|
460
|
+
[ORCH_TASK_LIFECYCLE_V2.FAILED, new Set()],
|
|
461
|
+
[ORCH_TASK_LIFECYCLE_V2.CANCELLED, new Set()],
|
|
462
|
+
]);
|
|
463
|
+
|
|
464
|
+
const _opsV2 = new Map();
|
|
465
|
+
const _otsV2 = new Map();
|
|
466
|
+
let _opMaxActivePerOwner = 6;
|
|
467
|
+
let _opMaxPendingTasksPerProfile = 12;
|
|
468
|
+
let _opIdleMs = 14 * 24 * 60 * 60 * 1000;
|
|
469
|
+
let _otStuckMs = 15 * 60 * 1000;
|
|
470
|
+
|
|
471
|
+
function _opPos(n, lbl) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${lbl} must be positive integer`); return v; }
|
|
472
|
+
|
|
473
|
+
export function setMaxActiveOrchProfilesPerOwnerV2(n) { _opMaxActivePerOwner = _opPos(n, "maxActiveOrchProfilesPerOwner"); }
|
|
474
|
+
export function getMaxActiveOrchProfilesPerOwnerV2() { return _opMaxActivePerOwner; }
|
|
475
|
+
export function setMaxPendingOrchTasksPerProfileV2(n) { _opMaxPendingTasksPerProfile = _opPos(n, "maxPendingOrchTasksPerProfile"); }
|
|
476
|
+
export function getMaxPendingOrchTasksPerProfileV2() { return _opMaxPendingTasksPerProfile; }
|
|
477
|
+
export function setOrchProfileIdleMsV2(n) { _opIdleMs = _opPos(n, "orchProfileIdleMs"); }
|
|
478
|
+
export function getOrchProfileIdleMsV2() { return _opIdleMs; }
|
|
479
|
+
export function setOrchTaskStuckMsV2(n) { _otStuckMs = _opPos(n, "orchTaskStuckMs"); }
|
|
480
|
+
export function getOrchTaskStuckMsV2() { return _otStuckMs; }
|
|
481
|
+
|
|
482
|
+
export function _resetStateOrchestratorV2() {
|
|
483
|
+
_opsV2.clear(); _otsV2.clear();
|
|
484
|
+
_opMaxActivePerOwner = 6; _opMaxPendingTasksPerProfile = 12;
|
|
485
|
+
_opIdleMs = 14 * 24 * 60 * 60 * 1000; _otStuckMs = 15 * 60 * 1000;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
export function registerOrchProfileV2({ id, owner, source, metadata } = {}) {
|
|
489
|
+
if (!id || typeof id !== "string") throw new Error("id is required");
|
|
490
|
+
if (!owner || typeof owner !== "string") throw new Error("owner is required");
|
|
491
|
+
if (_opsV2.has(id)) throw new Error(`orch profile ${id} already registered`);
|
|
492
|
+
const now = Date.now();
|
|
493
|
+
const p = { id, owner, source: source || "cli", status: ORCH_PROFILE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, retiredAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
|
|
494
|
+
_opsV2.set(id, p);
|
|
495
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
496
|
+
}
|
|
497
|
+
function _opCheckP(from, to) { const a = _opTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid orch profile transition ${from} → ${to}`); }
|
|
498
|
+
function _opCountActive(owner) { let n = 0; for (const p of _opsV2.values()) if (p.owner === owner && p.status === ORCH_PROFILE_MATURITY_V2.ACTIVE) n++; return n; }
|
|
499
|
+
|
|
500
|
+
export function activateOrchProfileV2(id) {
|
|
501
|
+
const p = _opsV2.get(id); if (!p) throw new Error(`orch profile ${id} not found`);
|
|
502
|
+
_opCheckP(p.status, ORCH_PROFILE_MATURITY_V2.ACTIVE);
|
|
503
|
+
const recovery = p.status === ORCH_PROFILE_MATURITY_V2.PAUSED;
|
|
504
|
+
if (!recovery) { const c = _opCountActive(p.owner); if (c >= _opMaxActivePerOwner) throw new Error(`max active orch profiles per owner (${_opMaxActivePerOwner}) reached for ${p.owner}`); }
|
|
505
|
+
const now = Date.now(); p.status = ORCH_PROFILE_MATURITY_V2.ACTIVE; p.updatedAt = now; p.lastTouchedAt = now; if (!p.activatedAt) p.activatedAt = now;
|
|
506
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
507
|
+
}
|
|
508
|
+
export function pauseOrchProfileV2(id) { const p = _opsV2.get(id); if (!p) throw new Error(`orch profile ${id} not found`); _opCheckP(p.status, ORCH_PROFILE_MATURITY_V2.PAUSED); p.status = ORCH_PROFILE_MATURITY_V2.PAUSED; p.updatedAt = Date.now(); return { ...p, metadata: { ...p.metadata } }; }
|
|
509
|
+
export function retireOrchProfileV2(id) { const p = _opsV2.get(id); if (!p) throw new Error(`orch profile ${id} not found`); _opCheckP(p.status, ORCH_PROFILE_MATURITY_V2.RETIRED); const now = Date.now(); p.status = ORCH_PROFILE_MATURITY_V2.RETIRED; p.updatedAt = now; if (!p.retiredAt) p.retiredAt = now; return { ...p, metadata: { ...p.metadata } }; }
|
|
510
|
+
export function touchOrchProfileV2(id) { const p = _opsV2.get(id); if (!p) throw new Error(`orch profile ${id} not found`); if (_opTerminal.has(p.status)) throw new Error(`cannot touch terminal orch profile ${id}`); const now = Date.now(); p.lastTouchedAt = now; p.updatedAt = now; return { ...p, metadata: { ...p.metadata } }; }
|
|
511
|
+
export function getOrchProfileV2(id) { const p = _opsV2.get(id); if (!p) return null; return { ...p, metadata: { ...p.metadata } }; }
|
|
512
|
+
export function listOrchProfilesV2() { return [..._opsV2.values()].map((p) => ({ ...p, metadata: { ...p.metadata } })); }
|
|
513
|
+
|
|
514
|
+
function _otCountPending(profileId) { let n = 0; for (const t of _otsV2.values()) if (t.profileId === profileId && (t.status === ORCH_TASK_LIFECYCLE_V2.QUEUED || t.status === ORCH_TASK_LIFECYCLE_V2.DISPATCHING)) n++; return n; }
|
|
515
|
+
|
|
516
|
+
export function createOrchTaskV2({ id, profileId, prompt, metadata } = {}) {
|
|
517
|
+
if (!id || typeof id !== "string") throw new Error("id is required");
|
|
518
|
+
if (!profileId || typeof profileId !== "string") throw new Error("profileId is required");
|
|
519
|
+
if (_otsV2.has(id)) throw new Error(`orch task ${id} already exists`);
|
|
520
|
+
if (!_opsV2.has(profileId)) throw new Error(`orch profile ${profileId} not found`);
|
|
521
|
+
const pending = _otCountPending(profileId);
|
|
522
|
+
if (pending >= _opMaxPendingTasksPerProfile) throw new Error(`max pending orch tasks per profile (${_opMaxPendingTasksPerProfile}) reached for ${profileId}`);
|
|
523
|
+
const now = Date.now();
|
|
524
|
+
const t = { id, profileId, prompt: prompt || "", status: ORCH_TASK_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
|
|
525
|
+
_otsV2.set(id, t);
|
|
526
|
+
return { ...t, metadata: { ...t.metadata } };
|
|
527
|
+
}
|
|
528
|
+
function _otCheckT(from, to) { const a = _otTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid orch task transition ${from} → ${to}`); }
|
|
529
|
+
export function dispatchOrchTaskV2(id) { const t = _otsV2.get(id); if (!t) throw new Error(`orch task ${id} not found`); _otCheckT(t.status, ORCH_TASK_LIFECYCLE_V2.DISPATCHING); const now = Date.now(); t.status = ORCH_TASK_LIFECYCLE_V2.DISPATCHING; t.updatedAt = now; if (!t.startedAt) t.startedAt = now; return { ...t, metadata: { ...t.metadata } }; }
|
|
530
|
+
export function completeOrchTaskV2(id) { const t = _otsV2.get(id); if (!t) throw new Error(`orch task ${id} not found`); _otCheckT(t.status, ORCH_TASK_LIFECYCLE_V2.COMPLETED); const now = Date.now(); t.status = ORCH_TASK_LIFECYCLE_V2.COMPLETED; t.updatedAt = now; if (!t.settledAt) t.settledAt = now; return { ...t, metadata: { ...t.metadata } }; }
|
|
531
|
+
export function failOrchTaskV2(id, reason) { const t = _otsV2.get(id); if (!t) throw new Error(`orch task ${id} not found`); _otCheckT(t.status, ORCH_TASK_LIFECYCLE_V2.FAILED); const now = Date.now(); t.status = ORCH_TASK_LIFECYCLE_V2.FAILED; t.updatedAt = now; if (!t.settledAt) t.settledAt = now; if (reason) t.metadata.failReason = String(reason); return { ...t, metadata: { ...t.metadata } }; }
|
|
532
|
+
export function cancelOrchTaskV2(id, reason) { const t = _otsV2.get(id); if (!t) throw new Error(`orch task ${id} not found`); _otCheckT(t.status, ORCH_TASK_LIFECYCLE_V2.CANCELLED); const now = Date.now(); t.status = ORCH_TASK_LIFECYCLE_V2.CANCELLED; t.updatedAt = now; if (!t.settledAt) t.settledAt = now; if (reason) t.metadata.cancelReason = String(reason); return { ...t, metadata: { ...t.metadata } }; }
|
|
533
|
+
export function getOrchTaskV2(id) { const t = _otsV2.get(id); if (!t) return null; return { ...t, metadata: { ...t.metadata } }; }
|
|
534
|
+
export function listOrchTasksV2() { return [..._otsV2.values()].map((t) => ({ ...t, metadata: { ...t.metadata } })); }
|
|
535
|
+
|
|
536
|
+
export function autoPauseIdleOrchProfilesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const p of _opsV2.values()) if (p.status === ORCH_PROFILE_MATURITY_V2.ACTIVE && (t - p.lastTouchedAt) >= _opIdleMs) { p.status = ORCH_PROFILE_MATURITY_V2.PAUSED; p.updatedAt = t; flipped.push(p.id); } return { flipped, count: flipped.length }; }
|
|
537
|
+
export function autoFailStuckOrchTasksV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const x of _otsV2.values()) if (x.status === ORCH_TASK_LIFECYCLE_V2.DISPATCHING && x.startedAt != null && (t - x.startedAt) >= _otStuckMs) { x.status = ORCH_TASK_LIFECYCLE_V2.FAILED; x.updatedAt = t; if (!x.settledAt) x.settledAt = t; x.metadata.failReason = "auto-fail-stuck"; flipped.push(x.id); } return { flipped, count: flipped.length }; }
|
|
538
|
+
|
|
539
|
+
export function getOrchestratorGovStatsV2() {
|
|
540
|
+
const profilesByStatus = {}; for (const s of Object.values(ORCH_PROFILE_MATURITY_V2)) profilesByStatus[s] = 0; for (const p of _opsV2.values()) profilesByStatus[p.status]++;
|
|
541
|
+
const tasksByStatus = {}; for (const s of Object.values(ORCH_TASK_LIFECYCLE_V2)) tasksByStatus[s] = 0; for (const t of _otsV2.values()) tasksByStatus[t.status]++;
|
|
542
|
+
return { totalOrchProfilesV2: _opsV2.size, totalOrchTasksV2: _otsV2.size, maxActiveOrchProfilesPerOwner: _opMaxActivePerOwner, maxPendingOrchTasksPerProfile: _opMaxPendingTasksPerProfile, orchProfileIdleMs: _opIdleMs, orchTaskStuckMs: _otStuckMs, profilesByStatus, tasksByStatus };
|
|
543
|
+
}
|
package/src/lib/pdf-parser.js
CHANGED
|
@@ -94,3 +94,84 @@ function decodePdfString(str) {
|
|
|
94
94
|
.replace(/\\\)/g, ")")
|
|
95
95
|
.replace(/\\\\/g, "\\");
|
|
96
96
|
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
// =====================================================================
|
|
100
|
+
// PDF Parser V2 governance overlay
|
|
101
|
+
// =====================================================================
|
|
102
|
+
export const PDFP_PROFILE_MATURITY_V2 = Object.freeze({ PENDING: "pending", ACTIVE: "active", STALE: "stale", ARCHIVED: "archived" });
|
|
103
|
+
export const PDFP_PARSE_LIFECYCLE_V2 = Object.freeze({ QUEUED: "queued", PARSING: "parsing", PARSED: "parsed", FAILED: "failed", CANCELLED: "cancelled" });
|
|
104
|
+
const _pdfpPTrans = new Map([
|
|
105
|
+
[PDFP_PROFILE_MATURITY_V2.PENDING, new Set([PDFP_PROFILE_MATURITY_V2.ACTIVE, PDFP_PROFILE_MATURITY_V2.ARCHIVED])],
|
|
106
|
+
[PDFP_PROFILE_MATURITY_V2.ACTIVE, new Set([PDFP_PROFILE_MATURITY_V2.STALE, PDFP_PROFILE_MATURITY_V2.ARCHIVED])],
|
|
107
|
+
[PDFP_PROFILE_MATURITY_V2.STALE, new Set([PDFP_PROFILE_MATURITY_V2.ACTIVE, PDFP_PROFILE_MATURITY_V2.ARCHIVED])],
|
|
108
|
+
[PDFP_PROFILE_MATURITY_V2.ARCHIVED, new Set()],
|
|
109
|
+
]);
|
|
110
|
+
const _pdfpPTerminal = new Set([PDFP_PROFILE_MATURITY_V2.ARCHIVED]);
|
|
111
|
+
const _pdfpJTrans = new Map([
|
|
112
|
+
[PDFP_PARSE_LIFECYCLE_V2.QUEUED, new Set([PDFP_PARSE_LIFECYCLE_V2.PARSING, PDFP_PARSE_LIFECYCLE_V2.CANCELLED])],
|
|
113
|
+
[PDFP_PARSE_LIFECYCLE_V2.PARSING, new Set([PDFP_PARSE_LIFECYCLE_V2.PARSED, PDFP_PARSE_LIFECYCLE_V2.FAILED, PDFP_PARSE_LIFECYCLE_V2.CANCELLED])],
|
|
114
|
+
[PDFP_PARSE_LIFECYCLE_V2.PARSED, new Set()],
|
|
115
|
+
[PDFP_PARSE_LIFECYCLE_V2.FAILED, new Set()],
|
|
116
|
+
[PDFP_PARSE_LIFECYCLE_V2.CANCELLED, new Set()],
|
|
117
|
+
]);
|
|
118
|
+
const _pdfpPsV2 = new Map();
|
|
119
|
+
const _pdfpJsV2 = new Map();
|
|
120
|
+
let _pdfpMaxActive = 6, _pdfpMaxPending = 12, _pdfpIdleMs = 30 * 24 * 60 * 60 * 1000, _pdfpStuckMs = 60 * 1000;
|
|
121
|
+
function _pdfpPos(n, label) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${label} must be positive integer`); return v; }
|
|
122
|
+
function _pdfpCheckP(from, to) { const a = _pdfpPTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid pdfp profile transition ${from} → ${to}`); }
|
|
123
|
+
function _pdfpCheckJ(from, to) { const a = _pdfpJTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid pdfp parse transition ${from} → ${to}`); }
|
|
124
|
+
function _pdfpCountActive(owner) { let c = 0; for (const p of _pdfpPsV2.values()) if (p.owner === owner && p.status === PDFP_PROFILE_MATURITY_V2.ACTIVE) c++; return c; }
|
|
125
|
+
function _pdfpCountPending(profileId) { let c = 0; for (const j of _pdfpJsV2.values()) if (j.profileId === profileId && (j.status === PDFP_PARSE_LIFECYCLE_V2.QUEUED || j.status === PDFP_PARSE_LIFECYCLE_V2.PARSING)) c++; return c; }
|
|
126
|
+
export function setMaxActivePdfpProfilesPerOwnerV2(n) { _pdfpMaxActive = _pdfpPos(n, "maxActivePdfpProfilesPerOwner"); }
|
|
127
|
+
export function getMaxActivePdfpProfilesPerOwnerV2() { return _pdfpMaxActive; }
|
|
128
|
+
export function setMaxPendingPdfpParsesPerProfileV2(n) { _pdfpMaxPending = _pdfpPos(n, "maxPendingPdfpParsesPerProfile"); }
|
|
129
|
+
export function getMaxPendingPdfpParsesPerProfileV2() { return _pdfpMaxPending; }
|
|
130
|
+
export function setPdfpProfileIdleMsV2(n) { _pdfpIdleMs = _pdfpPos(n, "pdfpProfileIdleMs"); }
|
|
131
|
+
export function getPdfpProfileIdleMsV2() { return _pdfpIdleMs; }
|
|
132
|
+
export function setPdfpParseStuckMsV2(n) { _pdfpStuckMs = _pdfpPos(n, "pdfpParseStuckMs"); }
|
|
133
|
+
export function getPdfpParseStuckMsV2() { return _pdfpStuckMs; }
|
|
134
|
+
export function _resetStatePdfParserV2() { _pdfpPsV2.clear(); _pdfpJsV2.clear(); _pdfpMaxActive = 6; _pdfpMaxPending = 12; _pdfpIdleMs = 30 * 24 * 60 * 60 * 1000; _pdfpStuckMs = 60 * 1000; }
|
|
135
|
+
export function registerPdfpProfileV2({ id, owner, encoding, metadata } = {}) {
|
|
136
|
+
if (!id || !owner) throw new Error("id and owner required");
|
|
137
|
+
if (_pdfpPsV2.has(id)) throw new Error(`pdfp profile ${id} already exists`);
|
|
138
|
+
const now = Date.now();
|
|
139
|
+
const p = { id, owner, encoding: encoding || "utf-8", status: PDFP_PROFILE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, lastTouchedAt: now, activatedAt: null, archivedAt: null, metadata: { ...(metadata || {}) } };
|
|
140
|
+
_pdfpPsV2.set(id, p); return { ...p, metadata: { ...p.metadata } };
|
|
141
|
+
}
|
|
142
|
+
export function activatePdfpProfileV2(id) {
|
|
143
|
+
const p = _pdfpPsV2.get(id); if (!p) throw new Error(`pdfp profile ${id} not found`);
|
|
144
|
+
const isInitial = p.status === PDFP_PROFILE_MATURITY_V2.PENDING;
|
|
145
|
+
_pdfpCheckP(p.status, PDFP_PROFILE_MATURITY_V2.ACTIVE);
|
|
146
|
+
if (isInitial && _pdfpCountActive(p.owner) >= _pdfpMaxActive) throw new Error(`max active pdfp profiles for owner ${p.owner} reached`);
|
|
147
|
+
const now = Date.now(); p.status = PDFP_PROFILE_MATURITY_V2.ACTIVE; p.updatedAt = now; p.lastTouchedAt = now;
|
|
148
|
+
if (!p.activatedAt) p.activatedAt = now;
|
|
149
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
150
|
+
}
|
|
151
|
+
export function stalePdfpProfileV2(id) { const p = _pdfpPsV2.get(id); if (!p) throw new Error(`pdfp profile ${id} not found`); _pdfpCheckP(p.status, PDFP_PROFILE_MATURITY_V2.STALE); p.status = PDFP_PROFILE_MATURITY_V2.STALE; p.updatedAt = Date.now(); return { ...p, metadata: { ...p.metadata } }; }
|
|
152
|
+
export function archivePdfpProfileV2(id) { const p = _pdfpPsV2.get(id); if (!p) throw new Error(`pdfp profile ${id} not found`); _pdfpCheckP(p.status, PDFP_PROFILE_MATURITY_V2.ARCHIVED); const now = Date.now(); p.status = PDFP_PROFILE_MATURITY_V2.ARCHIVED; p.updatedAt = now; if (!p.archivedAt) p.archivedAt = now; return { ...p, metadata: { ...p.metadata } }; }
|
|
153
|
+
export function touchPdfpProfileV2(id) { const p = _pdfpPsV2.get(id); if (!p) throw new Error(`pdfp profile ${id} not found`); if (_pdfpPTerminal.has(p.status)) throw new Error(`cannot touch terminal pdfp profile ${id}`); const now = Date.now(); p.lastTouchedAt = now; p.updatedAt = now; return { ...p, metadata: { ...p.metadata } }; }
|
|
154
|
+
export function getPdfpProfileV2(id) { const p = _pdfpPsV2.get(id); if (!p) return null; return { ...p, metadata: { ...p.metadata } }; }
|
|
155
|
+
export function listPdfpProfilesV2() { return [..._pdfpPsV2.values()].map((p) => ({ ...p, metadata: { ...p.metadata } })); }
|
|
156
|
+
export function createPdfpParseV2({ id, profileId, path, metadata } = {}) {
|
|
157
|
+
if (!id || !profileId) throw new Error("id and profileId required");
|
|
158
|
+
if (_pdfpJsV2.has(id)) throw new Error(`pdfp parse ${id} already exists`);
|
|
159
|
+
if (!_pdfpPsV2.has(profileId)) throw new Error(`pdfp profile ${profileId} not found`);
|
|
160
|
+
if (_pdfpCountPending(profileId) >= _pdfpMaxPending) throw new Error(`max pending pdfp parses for profile ${profileId} reached`);
|
|
161
|
+
const now = Date.now();
|
|
162
|
+
const j = { id, profileId, path: path || "", status: PDFP_PARSE_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
|
|
163
|
+
_pdfpJsV2.set(id, j); return { ...j, metadata: { ...j.metadata } };
|
|
164
|
+
}
|
|
165
|
+
export function parsingPdfpParseV2(id) { const j = _pdfpJsV2.get(id); if (!j) throw new Error(`pdfp parse ${id} not found`); _pdfpCheckJ(j.status, PDFP_PARSE_LIFECYCLE_V2.PARSING); const now = Date.now(); j.status = PDFP_PARSE_LIFECYCLE_V2.PARSING; j.updatedAt = now; if (!j.startedAt) j.startedAt = now; return { ...j, metadata: { ...j.metadata } }; }
|
|
166
|
+
export function parsePdfpParseV2(id) { const j = _pdfpJsV2.get(id); if (!j) throw new Error(`pdfp parse ${id} not found`); _pdfpCheckJ(j.status, PDFP_PARSE_LIFECYCLE_V2.PARSED); const now = Date.now(); j.status = PDFP_PARSE_LIFECYCLE_V2.PARSED; j.updatedAt = now; if (!j.settledAt) j.settledAt = now; return { ...j, metadata: { ...j.metadata } }; }
|
|
167
|
+
export function failPdfpParseV2(id, reason) { const j = _pdfpJsV2.get(id); if (!j) throw new Error(`pdfp parse ${id} not found`); _pdfpCheckJ(j.status, PDFP_PARSE_LIFECYCLE_V2.FAILED); const now = Date.now(); j.status = PDFP_PARSE_LIFECYCLE_V2.FAILED; j.updatedAt = now; if (!j.settledAt) j.settledAt = now; if (reason) j.metadata.failReason = String(reason); return { ...j, metadata: { ...j.metadata } }; }
|
|
168
|
+
export function cancelPdfpParseV2(id, reason) { const j = _pdfpJsV2.get(id); if (!j) throw new Error(`pdfp parse ${id} not found`); _pdfpCheckJ(j.status, PDFP_PARSE_LIFECYCLE_V2.CANCELLED); const now = Date.now(); j.status = PDFP_PARSE_LIFECYCLE_V2.CANCELLED; j.updatedAt = now; if (!j.settledAt) j.settledAt = now; if (reason) j.metadata.cancelReason = String(reason); return { ...j, metadata: { ...j.metadata } }; }
|
|
169
|
+
export function getPdfpParseV2(id) { const j = _pdfpJsV2.get(id); if (!j) return null; return { ...j, metadata: { ...j.metadata } }; }
|
|
170
|
+
export function listPdfpParsesV2() { return [..._pdfpJsV2.values()].map((j) => ({ ...j, metadata: { ...j.metadata } })); }
|
|
171
|
+
export function autoStaleIdlePdfpProfilesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const p of _pdfpPsV2.values()) if (p.status === PDFP_PROFILE_MATURITY_V2.ACTIVE && (t - p.lastTouchedAt) >= _pdfpIdleMs) { p.status = PDFP_PROFILE_MATURITY_V2.STALE; p.updatedAt = t; flipped.push(p.id); } return { flipped, count: flipped.length }; }
|
|
172
|
+
export function autoFailStuckPdfpParsesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const j of _pdfpJsV2.values()) if (j.status === PDFP_PARSE_LIFECYCLE_V2.PARSING && j.startedAt != null && (t - j.startedAt) >= _pdfpStuckMs) { j.status = PDFP_PARSE_LIFECYCLE_V2.FAILED; j.updatedAt = t; if (!j.settledAt) j.settledAt = t; j.metadata.failReason = "auto-fail-stuck"; flipped.push(j.id); } return { flipped, count: flipped.length }; }
|
|
173
|
+
export function getPdfParserGovStatsV2() {
|
|
174
|
+
const profilesByStatus = {}; for (const v of Object.values(PDFP_PROFILE_MATURITY_V2)) profilesByStatus[v] = 0; for (const p of _pdfpPsV2.values()) profilesByStatus[p.status]++;
|
|
175
|
+
const parsesByStatus = {}; for (const v of Object.values(PDFP_PARSE_LIFECYCLE_V2)) parsesByStatus[v] = 0; for (const j of _pdfpJsV2.values()) parsesByStatus[j.status]++;
|
|
176
|
+
return { totalPdfpProfilesV2: _pdfpPsV2.size, totalPdfpParsesV2: _pdfpJsV2.size, maxActivePdfpProfilesPerOwner: _pdfpMaxActive, maxPendingPdfpParsesPerProfile: _pdfpMaxPending, pdfpProfileIdleMs: _pdfpIdleMs, pdfpParseStuckMs: _pdfpStuckMs, profilesByStatus, parsesByStatus };
|
|
177
|
+
}
|
package/src/lib/perf-tuning.js
CHANGED
|
@@ -735,3 +735,108 @@ export function getPerformanceReport(db) {
|
|
|
735
735
|
export function _resetState() {
|
|
736
736
|
/* CLI is stateless; helper exists for parity with other libs */
|
|
737
737
|
}
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
// ===== V2 Surface: Perf Tuning governance overlay (CLI v0.140.0) =====
|
|
741
|
+
export const PERF_TUNING_PROFILE_MATURITY_V2 = Object.freeze({
|
|
742
|
+
PENDING: "pending", ACTIVE: "active", STALE: "stale", DECOMMISSIONED: "decommissioned",
|
|
743
|
+
});
|
|
744
|
+
export const PERF_BENCH_LIFECYCLE_V2 = Object.freeze({
|
|
745
|
+
QUEUED: "queued", RUNNING: "running", COMPLETED: "completed", FAILED: "failed", CANCELLED: "cancelled",
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
const _ptpTrans = new Map([
|
|
749
|
+
[PERF_TUNING_PROFILE_MATURITY_V2.PENDING, new Set([PERF_TUNING_PROFILE_MATURITY_V2.ACTIVE, PERF_TUNING_PROFILE_MATURITY_V2.DECOMMISSIONED])],
|
|
750
|
+
[PERF_TUNING_PROFILE_MATURITY_V2.ACTIVE, new Set([PERF_TUNING_PROFILE_MATURITY_V2.STALE, PERF_TUNING_PROFILE_MATURITY_V2.DECOMMISSIONED])],
|
|
751
|
+
[PERF_TUNING_PROFILE_MATURITY_V2.STALE, new Set([PERF_TUNING_PROFILE_MATURITY_V2.ACTIVE, PERF_TUNING_PROFILE_MATURITY_V2.DECOMMISSIONED])],
|
|
752
|
+
[PERF_TUNING_PROFILE_MATURITY_V2.DECOMMISSIONED, new Set()],
|
|
753
|
+
]);
|
|
754
|
+
const _ptpTerminal = new Set([PERF_TUNING_PROFILE_MATURITY_V2.DECOMMISSIONED]);
|
|
755
|
+
const _pbTrans = new Map([
|
|
756
|
+
[PERF_BENCH_LIFECYCLE_V2.QUEUED, new Set([PERF_BENCH_LIFECYCLE_V2.RUNNING, PERF_BENCH_LIFECYCLE_V2.CANCELLED])],
|
|
757
|
+
[PERF_BENCH_LIFECYCLE_V2.RUNNING, new Set([PERF_BENCH_LIFECYCLE_V2.COMPLETED, PERF_BENCH_LIFECYCLE_V2.FAILED, PERF_BENCH_LIFECYCLE_V2.CANCELLED])],
|
|
758
|
+
[PERF_BENCH_LIFECYCLE_V2.COMPLETED, new Set()],
|
|
759
|
+
[PERF_BENCH_LIFECYCLE_V2.FAILED, new Set()],
|
|
760
|
+
[PERF_BENCH_LIFECYCLE_V2.CANCELLED, new Set()],
|
|
761
|
+
]);
|
|
762
|
+
|
|
763
|
+
const _ptpsV2 = new Map();
|
|
764
|
+
const _pbsV2 = new Map();
|
|
765
|
+
let _ptpMaxActivePerOwner = 6;
|
|
766
|
+
let _ptpMaxPendingBenchesPerProfile = 10;
|
|
767
|
+
let _ptpIdleMs = 7 * 24 * 60 * 60 * 1000;
|
|
768
|
+
let _pbStuckMs = 30 * 60 * 1000;
|
|
769
|
+
|
|
770
|
+
function _ptpPos(n, lbl) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${lbl} must be positive integer`); return v; }
|
|
771
|
+
|
|
772
|
+
export function setMaxActivePerfTuningProfilesPerOwnerV2(n) { _ptpMaxActivePerOwner = _ptpPos(n, "maxActivePerfTuningProfilesPerOwner"); }
|
|
773
|
+
export function getMaxActivePerfTuningProfilesPerOwnerV2() { return _ptpMaxActivePerOwner; }
|
|
774
|
+
export function setMaxPendingPerfBenchesPerProfileV2(n) { _ptpMaxPendingBenchesPerProfile = _ptpPos(n, "maxPendingPerfBenchesPerProfile"); }
|
|
775
|
+
export function getMaxPendingPerfBenchesPerProfileV2() { return _ptpMaxPendingBenchesPerProfile; }
|
|
776
|
+
export function setPerfTuningProfileIdleMsV2(n) { _ptpIdleMs = _ptpPos(n, "perfTuningProfileIdleMs"); }
|
|
777
|
+
export function getPerfTuningProfileIdleMsV2() { return _ptpIdleMs; }
|
|
778
|
+
export function setPerfBenchStuckMsV2(n) { _pbStuckMs = _ptpPos(n, "perfBenchStuckMs"); }
|
|
779
|
+
export function getPerfBenchStuckMsV2() { return _pbStuckMs; }
|
|
780
|
+
|
|
781
|
+
export function _resetStatePerfTuningV2() {
|
|
782
|
+
_ptpsV2.clear(); _pbsV2.clear();
|
|
783
|
+
_ptpMaxActivePerOwner = 6; _ptpMaxPendingBenchesPerProfile = 10;
|
|
784
|
+
_ptpIdleMs = 7 * 24 * 60 * 60 * 1000; _pbStuckMs = 30 * 60 * 1000;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
export function registerPerfTuningProfileV2({ id, owner, target, metadata } = {}) {
|
|
788
|
+
if (!id || typeof id !== "string") throw new Error("id is required");
|
|
789
|
+
if (!owner || typeof owner !== "string") throw new Error("owner is required");
|
|
790
|
+
if (_ptpsV2.has(id)) throw new Error(`perf tuning profile ${id} already registered`);
|
|
791
|
+
const now = Date.now();
|
|
792
|
+
const p = { id, owner, target: target || "default", status: PERF_TUNING_PROFILE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, decommissionedAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
|
|
793
|
+
_ptpsV2.set(id, p);
|
|
794
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
795
|
+
}
|
|
796
|
+
function _ptpCheckP(from, to) { const a = _ptpTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid perf tuning profile transition ${from} → ${to}`); }
|
|
797
|
+
function _ptpCountActive(owner) { let n = 0; for (const p of _ptpsV2.values()) if (p.owner === owner && p.status === PERF_TUNING_PROFILE_MATURITY_V2.ACTIVE) n++; return n; }
|
|
798
|
+
|
|
799
|
+
export function activatePerfTuningProfileV2(id) {
|
|
800
|
+
const p = _ptpsV2.get(id); if (!p) throw new Error(`perf tuning profile ${id} not found`);
|
|
801
|
+
_ptpCheckP(p.status, PERF_TUNING_PROFILE_MATURITY_V2.ACTIVE);
|
|
802
|
+
const recovery = p.status === PERF_TUNING_PROFILE_MATURITY_V2.STALE;
|
|
803
|
+
if (!recovery) { const c = _ptpCountActive(p.owner); if (c >= _ptpMaxActivePerOwner) throw new Error(`max active perf tuning profiles per owner (${_ptpMaxActivePerOwner}) reached for ${p.owner}`); }
|
|
804
|
+
const now = Date.now(); p.status = PERF_TUNING_PROFILE_MATURITY_V2.ACTIVE; p.updatedAt = now; p.lastTouchedAt = now; if (!p.activatedAt) p.activatedAt = now;
|
|
805
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
806
|
+
}
|
|
807
|
+
export function stalePerfTuningProfileV2(id) { const p = _ptpsV2.get(id); if (!p) throw new Error(`perf tuning profile ${id} not found`); _ptpCheckP(p.status, PERF_TUNING_PROFILE_MATURITY_V2.STALE); p.status = PERF_TUNING_PROFILE_MATURITY_V2.STALE; p.updatedAt = Date.now(); return { ...p, metadata: { ...p.metadata } }; }
|
|
808
|
+
export function decommissionPerfTuningProfileV2(id) { const p = _ptpsV2.get(id); if (!p) throw new Error(`perf tuning profile ${id} not found`); _ptpCheckP(p.status, PERF_TUNING_PROFILE_MATURITY_V2.DECOMMISSIONED); const now = Date.now(); p.status = PERF_TUNING_PROFILE_MATURITY_V2.DECOMMISSIONED; p.updatedAt = now; if (!p.decommissionedAt) p.decommissionedAt = now; return { ...p, metadata: { ...p.metadata } }; }
|
|
809
|
+
export function touchPerfTuningProfileV2(id) { const p = _ptpsV2.get(id); if (!p) throw new Error(`perf tuning profile ${id} not found`); if (_ptpTerminal.has(p.status)) throw new Error(`cannot touch terminal perf tuning profile ${id}`); const now = Date.now(); p.lastTouchedAt = now; p.updatedAt = now; return { ...p, metadata: { ...p.metadata } }; }
|
|
810
|
+
export function getPerfTuningProfileV2(id) { const p = _ptpsV2.get(id); if (!p) return null; return { ...p, metadata: { ...p.metadata } }; }
|
|
811
|
+
export function listPerfTuningProfilesV2() { return [..._ptpsV2.values()].map((p) => ({ ...p, metadata: { ...p.metadata } })); }
|
|
812
|
+
|
|
813
|
+
function _pbCountPending(profileId) { let n = 0; for (const b of _pbsV2.values()) if (b.profileId === profileId && (b.status === PERF_BENCH_LIFECYCLE_V2.QUEUED || b.status === PERF_BENCH_LIFECYCLE_V2.RUNNING)) n++; return n; }
|
|
814
|
+
|
|
815
|
+
export function createPerfBenchV2({ id, profileId, scenario, metadata } = {}) {
|
|
816
|
+
if (!id || typeof id !== "string") throw new Error("id is required");
|
|
817
|
+
if (!profileId || typeof profileId !== "string") throw new Error("profileId is required");
|
|
818
|
+
if (_pbsV2.has(id)) throw new Error(`perf bench ${id} already exists`);
|
|
819
|
+
if (!_ptpsV2.has(profileId)) throw new Error(`perf tuning profile ${profileId} not found`);
|
|
820
|
+
const pending = _pbCountPending(profileId);
|
|
821
|
+
if (pending >= _ptpMaxPendingBenchesPerProfile) throw new Error(`max pending perf benches per profile (${_ptpMaxPendingBenchesPerProfile}) reached for ${profileId}`);
|
|
822
|
+
const now = Date.now();
|
|
823
|
+
const b = { id, profileId, scenario: scenario || "", status: PERF_BENCH_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
|
|
824
|
+
_pbsV2.set(id, b);
|
|
825
|
+
return { ...b, metadata: { ...b.metadata } };
|
|
826
|
+
}
|
|
827
|
+
function _pbCheckB(from, to) { const a = _pbTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid perf bench transition ${from} → ${to}`); }
|
|
828
|
+
export function startPerfBenchV2(id) { const b = _pbsV2.get(id); if (!b) throw new Error(`perf bench ${id} not found`); _pbCheckB(b.status, PERF_BENCH_LIFECYCLE_V2.RUNNING); const now = Date.now(); b.status = PERF_BENCH_LIFECYCLE_V2.RUNNING; b.updatedAt = now; if (!b.startedAt) b.startedAt = now; return { ...b, metadata: { ...b.metadata } }; }
|
|
829
|
+
export function completePerfBenchV2(id) { const b = _pbsV2.get(id); if (!b) throw new Error(`perf bench ${id} not found`); _pbCheckB(b.status, PERF_BENCH_LIFECYCLE_V2.COMPLETED); const now = Date.now(); b.status = PERF_BENCH_LIFECYCLE_V2.COMPLETED; b.updatedAt = now; if (!b.settledAt) b.settledAt = now; return { ...b, metadata: { ...b.metadata } }; }
|
|
830
|
+
export function failPerfBenchV2(id, reason) { const b = _pbsV2.get(id); if (!b) throw new Error(`perf bench ${id} not found`); _pbCheckB(b.status, PERF_BENCH_LIFECYCLE_V2.FAILED); const now = Date.now(); b.status = PERF_BENCH_LIFECYCLE_V2.FAILED; b.updatedAt = now; if (!b.settledAt) b.settledAt = now; if (reason) b.metadata.failReason = String(reason); return { ...b, metadata: { ...b.metadata } }; }
|
|
831
|
+
export function cancelPerfBenchV2(id, reason) { const b = _pbsV2.get(id); if (!b) throw new Error(`perf bench ${id} not found`); _pbCheckB(b.status, PERF_BENCH_LIFECYCLE_V2.CANCELLED); const now = Date.now(); b.status = PERF_BENCH_LIFECYCLE_V2.CANCELLED; b.updatedAt = now; if (!b.settledAt) b.settledAt = now; if (reason) b.metadata.cancelReason = String(reason); return { ...b, metadata: { ...b.metadata } }; }
|
|
832
|
+
export function getPerfBenchV2(id) { const b = _pbsV2.get(id); if (!b) return null; return { ...b, metadata: { ...b.metadata } }; }
|
|
833
|
+
export function listPerfBenchesV2() { return [..._pbsV2.values()].map((b) => ({ ...b, metadata: { ...b.metadata } })); }
|
|
834
|
+
|
|
835
|
+
export function autoStaleIdlePerfTuningProfilesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const p of _ptpsV2.values()) if (p.status === PERF_TUNING_PROFILE_MATURITY_V2.ACTIVE && (t - p.lastTouchedAt) >= _ptpIdleMs) { p.status = PERF_TUNING_PROFILE_MATURITY_V2.STALE; p.updatedAt = t; flipped.push(p.id); } return { flipped, count: flipped.length }; }
|
|
836
|
+
export function autoFailStuckPerfBenchesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const b of _pbsV2.values()) if (b.status === PERF_BENCH_LIFECYCLE_V2.RUNNING && b.startedAt != null && (t - b.startedAt) >= _pbStuckMs) { b.status = PERF_BENCH_LIFECYCLE_V2.FAILED; b.updatedAt = t; if (!b.settledAt) b.settledAt = t; b.metadata.failReason = "auto-fail-stuck"; flipped.push(b.id); } return { flipped, count: flipped.length }; }
|
|
837
|
+
|
|
838
|
+
export function getPerfTuningGovStatsV2() {
|
|
839
|
+
const profilesByStatus = {}; for (const s of Object.values(PERF_TUNING_PROFILE_MATURITY_V2)) profilesByStatus[s] = 0; for (const p of _ptpsV2.values()) profilesByStatus[p.status]++;
|
|
840
|
+
const benchesByStatus = {}; for (const s of Object.values(PERF_BENCH_LIFECYCLE_V2)) benchesByStatus[s] = 0; for (const b of _pbsV2.values()) benchesByStatus[b.status]++;
|
|
841
|
+
return { totalPerfTuningProfilesV2: _ptpsV2.size, totalPerfBenchesV2: _pbsV2.size, maxActivePerfTuningProfilesPerOwner: _ptpMaxActivePerOwner, maxPendingPerfBenchesPerProfile: _ptpMaxPendingBenchesPerProfile, perfTuningProfileIdleMs: _ptpIdleMs, perfBenchStuckMs: _pbStuckMs, profilesByStatus, benchesByStatus };
|
|
842
|
+
}
|
|
@@ -372,3 +372,84 @@ export function listUserRoles(db) {
|
|
|
372
372
|
|
|
373
373
|
return [...userMap.entries()].map(([userDid, roles]) => ({ userDid, roles }));
|
|
374
374
|
}
|
|
375
|
+
|
|
376
|
+
// ===== V2 Surface: Permission Engine governance overlay (CLI v0.141.0) =====
|
|
377
|
+
export const PERM_RULE_MATURITY_V2 = Object.freeze({
|
|
378
|
+
PENDING: "pending", ACTIVE: "active", DISABLED: "disabled", RETIRED: "retired",
|
|
379
|
+
});
|
|
380
|
+
export const PERM_CHECK_LIFECYCLE_V2 = Object.freeze({
|
|
381
|
+
QUEUED: "queued", EVALUATING: "evaluating", ALLOWED: "allowed", DENIED: "denied", CANCELLED: "cancelled",
|
|
382
|
+
});
|
|
383
|
+
const _permRTrans = new Map([
|
|
384
|
+
[PERM_RULE_MATURITY_V2.PENDING, new Set([PERM_RULE_MATURITY_V2.ACTIVE, PERM_RULE_MATURITY_V2.RETIRED])],
|
|
385
|
+
[PERM_RULE_MATURITY_V2.ACTIVE, new Set([PERM_RULE_MATURITY_V2.DISABLED, PERM_RULE_MATURITY_V2.RETIRED])],
|
|
386
|
+
[PERM_RULE_MATURITY_V2.DISABLED, new Set([PERM_RULE_MATURITY_V2.ACTIVE, PERM_RULE_MATURITY_V2.RETIRED])],
|
|
387
|
+
[PERM_RULE_MATURITY_V2.RETIRED, new Set()],
|
|
388
|
+
]);
|
|
389
|
+
const _permRTerminal = new Set([PERM_RULE_MATURITY_V2.RETIRED]);
|
|
390
|
+
const _permCTrans = new Map([
|
|
391
|
+
[PERM_CHECK_LIFECYCLE_V2.QUEUED, new Set([PERM_CHECK_LIFECYCLE_V2.EVALUATING, PERM_CHECK_LIFECYCLE_V2.CANCELLED])],
|
|
392
|
+
[PERM_CHECK_LIFECYCLE_V2.EVALUATING, new Set([PERM_CHECK_LIFECYCLE_V2.ALLOWED, PERM_CHECK_LIFECYCLE_V2.DENIED, PERM_CHECK_LIFECYCLE_V2.CANCELLED])],
|
|
393
|
+
[PERM_CHECK_LIFECYCLE_V2.ALLOWED, new Set()],
|
|
394
|
+
[PERM_CHECK_LIFECYCLE_V2.DENIED, new Set()],
|
|
395
|
+
[PERM_CHECK_LIFECYCLE_V2.CANCELLED, new Set()],
|
|
396
|
+
]);
|
|
397
|
+
const _permRsV2 = new Map();
|
|
398
|
+
const _permCsV2 = new Map();
|
|
399
|
+
let _permMaxActivePerOwner = 10, _permMaxPendingChecksPerRule = 30, _permIdleMs = 30 * 24 * 60 * 60 * 1000, _permStuckMs = 60 * 1000;
|
|
400
|
+
function _permPos(n, label) { const v = Math.floor(Number(n)); if (!Number.isFinite(v) || v <= 0) throw new Error(`${label} must be positive integer`); return v; }
|
|
401
|
+
function _permCheckR(from, to) { const a = _permRTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid perm rule transition ${from} → ${to}`); }
|
|
402
|
+
function _permCheckC(from, to) { const a = _permCTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid perm check transition ${from} → ${to}`); }
|
|
403
|
+
export function setMaxActivePermRulesPerOwnerV2(n) { _permMaxActivePerOwner = _permPos(n, "maxActivePermRulesPerOwner"); }
|
|
404
|
+
export function getMaxActivePermRulesPerOwnerV2() { return _permMaxActivePerOwner; }
|
|
405
|
+
export function setMaxPendingPermChecksPerRuleV2(n) { _permMaxPendingChecksPerRule = _permPos(n, "maxPendingPermChecksPerRule"); }
|
|
406
|
+
export function getMaxPendingPermChecksPerRuleV2() { return _permMaxPendingChecksPerRule; }
|
|
407
|
+
export function setPermRuleIdleMsV2(n) { _permIdleMs = _permPos(n, "permRuleIdleMs"); }
|
|
408
|
+
export function getPermRuleIdleMsV2() { return _permIdleMs; }
|
|
409
|
+
export function setPermCheckStuckMsV2(n) { _permStuckMs = _permPos(n, "permCheckStuckMs"); }
|
|
410
|
+
export function getPermCheckStuckMsV2() { return _permStuckMs; }
|
|
411
|
+
export function _resetStatePermissionEngineV2() { _permRsV2.clear(); _permCsV2.clear(); _permMaxActivePerOwner = 10; _permMaxPendingChecksPerRule = 30; _permIdleMs = 30 * 24 * 60 * 60 * 1000; _permStuckMs = 60 * 1000; }
|
|
412
|
+
export function registerPermRuleV2({ id, owner, scope, metadata } = {}) {
|
|
413
|
+
if (!id) throw new Error("perm rule id required"); if (!owner) throw new Error("perm rule owner required");
|
|
414
|
+
if (_permRsV2.has(id)) throw new Error(`perm rule ${id} already registered`);
|
|
415
|
+
const now = Date.now();
|
|
416
|
+
const r = { id, owner, scope: scope || "*", status: PERM_RULE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, retiredAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
|
|
417
|
+
_permRsV2.set(id, r); return { ...r, metadata: { ...r.metadata } };
|
|
418
|
+
}
|
|
419
|
+
function _permCountActive(owner) { let n = 0; for (const r of _permRsV2.values()) if (r.owner === owner && r.status === PERM_RULE_MATURITY_V2.ACTIVE) n++; return n; }
|
|
420
|
+
export function activatePermRuleV2(id) {
|
|
421
|
+
const r = _permRsV2.get(id); if (!r) throw new Error(`perm rule ${id} not found`);
|
|
422
|
+
_permCheckR(r.status, PERM_RULE_MATURITY_V2.ACTIVE);
|
|
423
|
+
const recovery = r.status === PERM_RULE_MATURITY_V2.DISABLED;
|
|
424
|
+
if (!recovery && _permCountActive(r.owner) >= _permMaxActivePerOwner) throw new Error(`max active perm rules for owner ${r.owner} reached`);
|
|
425
|
+
const now = Date.now(); r.status = PERM_RULE_MATURITY_V2.ACTIVE; r.updatedAt = now; r.lastTouchedAt = now; if (!r.activatedAt) r.activatedAt = now;
|
|
426
|
+
return { ...r, metadata: { ...r.metadata } };
|
|
427
|
+
}
|
|
428
|
+
export function disablePermRuleV2(id) { const r = _permRsV2.get(id); if (!r) throw new Error(`perm rule ${id} not found`); _permCheckR(r.status, PERM_RULE_MATURITY_V2.DISABLED); r.status = PERM_RULE_MATURITY_V2.DISABLED; r.updatedAt = Date.now(); return { ...r, metadata: { ...r.metadata } }; }
|
|
429
|
+
export function retirePermRuleV2(id) { const r = _permRsV2.get(id); if (!r) throw new Error(`perm rule ${id} not found`); _permCheckR(r.status, PERM_RULE_MATURITY_V2.RETIRED); const now = Date.now(); r.status = PERM_RULE_MATURITY_V2.RETIRED; r.updatedAt = now; if (!r.retiredAt) r.retiredAt = now; return { ...r, metadata: { ...r.metadata } }; }
|
|
430
|
+
export function touchPermRuleV2(id) { const r = _permRsV2.get(id); if (!r) throw new Error(`perm rule ${id} not found`); if (_permRTerminal.has(r.status)) throw new Error(`cannot touch terminal perm rule ${id}`); const now = Date.now(); r.lastTouchedAt = now; r.updatedAt = now; return { ...r, metadata: { ...r.metadata } }; }
|
|
431
|
+
export function getPermRuleV2(id) { const r = _permRsV2.get(id); if (!r) return null; return { ...r, metadata: { ...r.metadata } }; }
|
|
432
|
+
export function listPermRulesV2() { return [..._permRsV2.values()].map((r) => ({ ...r, metadata: { ...r.metadata } })); }
|
|
433
|
+
function _permCountPending(ruleId) { let n = 0; for (const c of _permCsV2.values()) if (c.ruleId === ruleId && (c.status === PERM_CHECK_LIFECYCLE_V2.QUEUED || c.status === PERM_CHECK_LIFECYCLE_V2.EVALUATING)) n++; return n; }
|
|
434
|
+
export function createPermCheckV2({ id, ruleId, subject, metadata } = {}) {
|
|
435
|
+
if (!id) throw new Error("perm check id required"); if (!ruleId) throw new Error("perm check ruleId required");
|
|
436
|
+
if (_permCsV2.has(id)) throw new Error(`perm check ${id} already exists`);
|
|
437
|
+
if (!_permRsV2.has(ruleId)) throw new Error(`perm rule ${ruleId} not found`);
|
|
438
|
+
if (_permCountPending(ruleId) >= _permMaxPendingChecksPerRule) throw new Error(`max pending perm checks for rule ${ruleId} reached`);
|
|
439
|
+
const now = Date.now();
|
|
440
|
+
const c = { id, ruleId, subject: subject || "", status: PERM_CHECK_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
|
|
441
|
+
_permCsV2.set(id, c); return { ...c, metadata: { ...c.metadata } };
|
|
442
|
+
}
|
|
443
|
+
export function evaluatePermCheckV2(id) { const c = _permCsV2.get(id); if (!c) throw new Error(`perm check ${id} not found`); _permCheckC(c.status, PERM_CHECK_LIFECYCLE_V2.EVALUATING); const now = Date.now(); c.status = PERM_CHECK_LIFECYCLE_V2.EVALUATING; c.updatedAt = now; if (!c.startedAt) c.startedAt = now; return { ...c, metadata: { ...c.metadata } }; }
|
|
444
|
+
export function allowPermCheckV2(id) { const c = _permCsV2.get(id); if (!c) throw new Error(`perm check ${id} not found`); _permCheckC(c.status, PERM_CHECK_LIFECYCLE_V2.ALLOWED); const now = Date.now(); c.status = PERM_CHECK_LIFECYCLE_V2.ALLOWED; c.updatedAt = now; if (!c.settledAt) c.settledAt = now; return { ...c, metadata: { ...c.metadata } }; }
|
|
445
|
+
export function denyPermCheckV2(id, reason) { const c = _permCsV2.get(id); if (!c) throw new Error(`perm check ${id} not found`); _permCheckC(c.status, PERM_CHECK_LIFECYCLE_V2.DENIED); const now = Date.now(); c.status = PERM_CHECK_LIFECYCLE_V2.DENIED; c.updatedAt = now; if (!c.settledAt) c.settledAt = now; if (reason) c.metadata.denyReason = String(reason); return { ...c, metadata: { ...c.metadata } }; }
|
|
446
|
+
export function cancelPermCheckV2(id, reason) { const c = _permCsV2.get(id); if (!c) throw new Error(`perm check ${id} not found`); _permCheckC(c.status, PERM_CHECK_LIFECYCLE_V2.CANCELLED); const now = Date.now(); c.status = PERM_CHECK_LIFECYCLE_V2.CANCELLED; c.updatedAt = now; if (!c.settledAt) c.settledAt = now; if (reason) c.metadata.cancelReason = String(reason); return { ...c, metadata: { ...c.metadata } }; }
|
|
447
|
+
export function getPermCheckV2(id) { const c = _permCsV2.get(id); if (!c) return null; return { ...c, metadata: { ...c.metadata } }; }
|
|
448
|
+
export function listPermChecksV2() { return [..._permCsV2.values()].map((c) => ({ ...c, metadata: { ...c.metadata } })); }
|
|
449
|
+
export function autoDisableIdlePermRulesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const r of _permRsV2.values()) if (r.status === PERM_RULE_MATURITY_V2.ACTIVE && (t - r.lastTouchedAt) >= _permIdleMs) { r.status = PERM_RULE_MATURITY_V2.DISABLED; r.updatedAt = t; flipped.push(r.id); } return { flipped, count: flipped.length }; }
|
|
450
|
+
export function autoDenyStuckPermChecksV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const c of _permCsV2.values()) if (c.status === PERM_CHECK_LIFECYCLE_V2.EVALUATING && c.startedAt != null && (t - c.startedAt) >= _permStuckMs) { c.status = PERM_CHECK_LIFECYCLE_V2.DENIED; c.updatedAt = t; if (!c.settledAt) c.settledAt = t; c.metadata.denyReason = "auto-deny-stuck"; flipped.push(c.id); } return { flipped, count: flipped.length }; }
|
|
451
|
+
export function getPermissionEngineGovStatsV2() {
|
|
452
|
+
const rulesByStatus = {}; for (const v of Object.values(PERM_RULE_MATURITY_V2)) rulesByStatus[v] = 0; for (const r of _permRsV2.values()) rulesByStatus[r.status]++;
|
|
453
|
+
const checksByStatus = {}; for (const v of Object.values(PERM_CHECK_LIFECYCLE_V2)) checksByStatus[v] = 0; for (const c of _permCsV2.values()) checksByStatus[c.status]++;
|
|
454
|
+
return { totalPermRulesV2: _permRsV2.size, totalPermChecksV2: _permCsV2.size, maxActivePermRulesPerOwner: _permMaxActivePerOwner, maxPendingPermChecksPerRule: _permMaxPendingChecksPerRule, permRuleIdleMs: _permIdleMs, permCheckStuckMs: _permStuckMs, rulesByStatus, checksByStatus };
|
|
455
|
+
}
|