chainlesschain 0.81.0 → 0.143.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/chainlesschain.js +0 -0
- package/package.json +1 -1
- package/src/commands/a2a.js +62 -0
- package/src/commands/activitypub.js +61 -0
- package/src/commands/agent-network.js +254 -1
- package/src/commands/agent.js +117 -0
- package/src/commands/audit.js +302 -0
- package/src/commands/automation.js +271 -1
- 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/codegen.js +224 -0
- package/src/commands/collab.js +341 -0
- package/src/commands/compliance.js +1075 -0
- package/src/commands/compt.js +78 -0
- package/src/commands/consol.js +231 -0
- package/src/commands/cowork.js +263 -0
- package/src/commands/crosschain.js +62 -0
- package/src/commands/dao.js +62 -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/dlp.js +61 -0
- package/src/commands/economy.js +56 -0
- package/src/commands/encrypt.js +341 -0
- package/src/commands/evolution.js +56 -0
- package/src/commands/evomap.js +61 -0
- package/src/commands/export.js +256 -1
- package/src/commands/fflag.js +178 -0
- package/src/commands/fusion.js +258 -0
- package/src/commands/git.js +45 -0
- package/src/commands/governance.js +325 -0
- package/src/commands/hardening.js +411 -0
- package/src/commands/hmemory.js +56 -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/inference.js +42 -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/itbudget.js +45 -0
- package/src/commands/kg.js +387 -0
- package/src/commands/llm.js +263 -0
- package/src/commands/lowcode.js +44 -0
- package/src/commands/matrix.js +62 -0
- package/src/commands/mcp.js +221 -0
- package/src/commands/mcpscaf.js +41 -0
- package/src/commands/meminj.js +41 -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/nostr.js +62 -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/orchgov.js +45 -0
- package/src/commands/org.js +277 -0
- package/src/commands/p2p.js +390 -0
- package/src/commands/pdfp.js +78 -0
- package/src/commands/perception.js +290 -0
- package/src/commands/perf.js +39 -0
- package/src/commands/perm.js +45 -0
- package/src/commands/permmem.js +251 -0
- package/src/commands/pipeline.js +57 -1
- package/src/commands/planmode.js +45 -0
- package/src/commands/plugin-ecosystem.js +273 -0
- package/src/commands/pqc.js +393 -0
- package/src/commands/promcomp.js +82 -0
- package/src/commands/quantization.js +351 -0
- package/src/commands/rcache.js +271 -0
- package/src/commands/recommend.js +382 -0
- package/src/commands/runtime.js +307 -0
- package/src/commands/scim.js +262 -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/session.js +258 -0
- package/src/commands/sganal.js +78 -0
- package/src/commands/siem.js +40 -0
- package/src/commands/skill.js +267 -1
- package/src/commands/slotfill.js +41 -0
- package/src/commands/social.js +290 -0
- package/src/commands/sso.js +186 -1
- package/src/commands/svccont.js +45 -0
- package/src/commands/sync.js +256 -0
- package/src/commands/tech.js +338 -0
- package/src/commands/tenant.js +351 -0
- package/src/commands/tms.js +45 -0
- package/src/commands/tokens.js +269 -0
- package/src/commands/topiccls.js +45 -0
- package/src/commands/trust.js +249 -0
- package/src/commands/uprof.js +45 -0
- package/src/commands/vcheck.js +78 -0
- package/src/commands/wallet.js +277 -0
- package/src/commands/webfetch.js +41 -0
- package/src/commands/workflow.js +171 -0
- package/src/commands/zkp.js +62 -0
- package/src/harness/prompt-compressor.js +331 -0
- package/src/index.js +65 -1
- package/src/lib/a2a-protocol.js +105 -0
- package/src/lib/activitypub-bridge.js +105 -0
- package/src/lib/agent-coordinator.js +325 -0
- package/src/lib/agent-economy.js +105 -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/app-builder.js +105 -0
- package/src/lib/audit-logger.js +379 -0
- package/src/lib/automation-engine.js +330 -0
- package/src/lib/autonomous-agent.js +105 -0
- package/src/lib/autonomous-developer.js +350 -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/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-framework-reporter.js +105 -0
- package/src/lib/compliance-manager.js +434 -0
- package/src/lib/compression-telemetry.js +81 -0
- package/src/lib/content-recommendation.js +469 -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/crypto-manager.js +350 -0
- package/src/lib/dao-governance.js +105 -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/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/hardening-manager.js +348 -0
- package/src/lib/hierarchical-memory.js +105 -0
- package/src/lib/hook-manager.js +380 -0
- package/src/lib/inference-network.js +105 -0
- package/src/lib/instinct-manager.js +332 -0
- package/src/lib/ipfs-storage.js +334 -0
- package/src/lib/iteration-budget.js +105 -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/matrix-bridge.js +105 -0
- package/src/lib/mcp-registry.js +333 -0
- package/src/lib/mcp-scaffold.js +81 -0
- package/src/lib/memory-injection.js +81 -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/nostr-bridge.js +105 -0
- package/src/lib/note-versioning.js +327 -0
- package/src/lib/orchestrator.js +105 -0
- package/src/lib/org-manager.js +323 -0
- package/src/lib/p2p-manager.js +387 -0
- package/src/lib/pdf-parser.js +81 -0
- package/src/lib/perception.js +346 -0
- package/src/lib/perf-tuning.js +109 -1
- package/src/lib/permanent-memory.js +320 -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/plugin-ecosystem.js +377 -0
- package/src/lib/pqc-manager.js +368 -0
- package/src/lib/prompt-compressor.js +1 -10
- 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/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-manager.js +329 -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/skill-loader.js +377 -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/social-manager.js +326 -0
- package/src/lib/sso-manager.js +332 -0
- package/src/lib/sub-agent-registry.js +110 -0
- package/src/lib/sync-manager.js +326 -0
- package/src/lib/task-model-selector.js +81 -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/todo-manager.js +105 -0
- package/src/lib/token-incentive.js +293 -0
- package/src/lib/token-tracker.js +329 -0
- package/src/lib/topic-classifier.js +105 -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/user-profile.js +81 -0
- package/src/lib/version-checker.js +81 -0
- package/src/lib/wallet-manager.js +326 -0
- package/src/lib/web-fetch.js +81 -0
- package/src/lib/workflow-engine.js +322 -0
- package/src/lib/zkp-engine.js +105 -0
package/src/lib/hook-manager.js
CHANGED
|
@@ -387,3 +387,383 @@ export function updateHookStats(
|
|
|
387
387
|
"UPDATE hooks SET execution_count = ?, error_count = ?, total_execution_time = ?, updated_at = datetime('now') WHERE id = ?",
|
|
388
388
|
).run(newCount, newErrorCount, newTotalTime, hookId);
|
|
389
389
|
}
|
|
390
|
+
|
|
391
|
+
// ===== V2 Surface: Hook Manager governance overlay (CLI v0.132.0) =====
|
|
392
|
+
// In-memory governance for hook profiles + execution lifecycle, independent of
|
|
393
|
+
// the legacy registerHook/executeHooks SQLite-backed path above.
|
|
394
|
+
|
|
395
|
+
export const HOOK_PROFILE_MATURITY_V2 = Object.freeze({
|
|
396
|
+
PENDING: "pending",
|
|
397
|
+
ACTIVE: "active",
|
|
398
|
+
DISABLED: "disabled",
|
|
399
|
+
RETIRED: "retired",
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
export const HOOK_EXEC_LIFECYCLE_V2 = Object.freeze({
|
|
403
|
+
QUEUED: "queued",
|
|
404
|
+
RUNNING: "running",
|
|
405
|
+
COMPLETED: "completed",
|
|
406
|
+
FAILED: "failed",
|
|
407
|
+
CANCELLED: "cancelled",
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
const _hookProfileTransitionsV2 = new Map([
|
|
411
|
+
[
|
|
412
|
+
HOOK_PROFILE_MATURITY_V2.PENDING,
|
|
413
|
+
new Set([
|
|
414
|
+
HOOK_PROFILE_MATURITY_V2.ACTIVE,
|
|
415
|
+
HOOK_PROFILE_MATURITY_V2.RETIRED,
|
|
416
|
+
]),
|
|
417
|
+
],
|
|
418
|
+
[
|
|
419
|
+
HOOK_PROFILE_MATURITY_V2.ACTIVE,
|
|
420
|
+
new Set([
|
|
421
|
+
HOOK_PROFILE_MATURITY_V2.DISABLED,
|
|
422
|
+
HOOK_PROFILE_MATURITY_V2.RETIRED,
|
|
423
|
+
]),
|
|
424
|
+
],
|
|
425
|
+
[
|
|
426
|
+
HOOK_PROFILE_MATURITY_V2.DISABLED,
|
|
427
|
+
new Set([
|
|
428
|
+
HOOK_PROFILE_MATURITY_V2.ACTIVE,
|
|
429
|
+
HOOK_PROFILE_MATURITY_V2.RETIRED,
|
|
430
|
+
]),
|
|
431
|
+
],
|
|
432
|
+
[HOOK_PROFILE_MATURITY_V2.RETIRED, new Set()],
|
|
433
|
+
]);
|
|
434
|
+
const _hookProfileTerminalV2 = new Set([HOOK_PROFILE_MATURITY_V2.RETIRED]);
|
|
435
|
+
|
|
436
|
+
const _hookExecTransitionsV2 = new Map([
|
|
437
|
+
[
|
|
438
|
+
HOOK_EXEC_LIFECYCLE_V2.QUEUED,
|
|
439
|
+
new Set([HOOK_EXEC_LIFECYCLE_V2.RUNNING, HOOK_EXEC_LIFECYCLE_V2.CANCELLED]),
|
|
440
|
+
],
|
|
441
|
+
[
|
|
442
|
+
HOOK_EXEC_LIFECYCLE_V2.RUNNING,
|
|
443
|
+
new Set([
|
|
444
|
+
HOOK_EXEC_LIFECYCLE_V2.COMPLETED,
|
|
445
|
+
HOOK_EXEC_LIFECYCLE_V2.FAILED,
|
|
446
|
+
HOOK_EXEC_LIFECYCLE_V2.CANCELLED,
|
|
447
|
+
]),
|
|
448
|
+
],
|
|
449
|
+
[HOOK_EXEC_LIFECYCLE_V2.COMPLETED, new Set()],
|
|
450
|
+
[HOOK_EXEC_LIFECYCLE_V2.FAILED, new Set()],
|
|
451
|
+
[HOOK_EXEC_LIFECYCLE_V2.CANCELLED, new Set()],
|
|
452
|
+
]);
|
|
453
|
+
const _hookExecTerminalV2 = new Set([
|
|
454
|
+
HOOK_EXEC_LIFECYCLE_V2.COMPLETED,
|
|
455
|
+
HOOK_EXEC_LIFECYCLE_V2.FAILED,
|
|
456
|
+
HOOK_EXEC_LIFECYCLE_V2.CANCELLED,
|
|
457
|
+
]);
|
|
458
|
+
|
|
459
|
+
const _hookProfilesV2 = new Map();
|
|
460
|
+
const _hookExecsV2 = new Map();
|
|
461
|
+
let _maxActiveHooksPerOwnerV2 = 20;
|
|
462
|
+
let _maxPendingExecsPerHookV2 = 32;
|
|
463
|
+
let _hookIdleMsV2 = 24 * 60 * 60 * 1000;
|
|
464
|
+
let _hookExecStuckMsV2 = 60 * 1000;
|
|
465
|
+
|
|
466
|
+
function _hookPosIntV2(n, label) {
|
|
467
|
+
const v = Math.floor(Number(n));
|
|
468
|
+
if (!Number.isFinite(v) || v <= 0)
|
|
469
|
+
throw new Error(`${label} must be positive integer`);
|
|
470
|
+
return v;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
export function setMaxActiveHooksPerOwnerV2(n) {
|
|
474
|
+
_maxActiveHooksPerOwnerV2 = _hookPosIntV2(n, "maxActiveHooksPerOwner");
|
|
475
|
+
}
|
|
476
|
+
export function getMaxActiveHooksPerOwnerV2() {
|
|
477
|
+
return _maxActiveHooksPerOwnerV2;
|
|
478
|
+
}
|
|
479
|
+
export function setMaxPendingExecsPerHookV2(n) {
|
|
480
|
+
_maxPendingExecsPerHookV2 = _hookPosIntV2(n, "maxPendingExecsPerHook");
|
|
481
|
+
}
|
|
482
|
+
export function getMaxPendingExecsPerHookV2() {
|
|
483
|
+
return _maxPendingExecsPerHookV2;
|
|
484
|
+
}
|
|
485
|
+
export function setHookIdleMsV2(n) {
|
|
486
|
+
_hookIdleMsV2 = _hookPosIntV2(n, "hookIdleMs");
|
|
487
|
+
}
|
|
488
|
+
export function getHookIdleMsV2() {
|
|
489
|
+
return _hookIdleMsV2;
|
|
490
|
+
}
|
|
491
|
+
export function setHookExecStuckMsV2(n) {
|
|
492
|
+
_hookExecStuckMsV2 = _hookPosIntV2(n, "hookExecStuckMs");
|
|
493
|
+
}
|
|
494
|
+
export function getHookExecStuckMsV2() {
|
|
495
|
+
return _hookExecStuckMsV2;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
export function _resetStateHookManagerV2() {
|
|
499
|
+
_hookProfilesV2.clear();
|
|
500
|
+
_hookExecsV2.clear();
|
|
501
|
+
_maxActiveHooksPerOwnerV2 = 20;
|
|
502
|
+
_maxPendingExecsPerHookV2 = 32;
|
|
503
|
+
_hookIdleMsV2 = 24 * 60 * 60 * 1000;
|
|
504
|
+
_hookExecStuckMsV2 = 60 * 1000;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
export function registerHookProfileV2({ id, owner, event, metadata } = {}) {
|
|
508
|
+
if (!id || typeof id !== "string") throw new Error("id is required");
|
|
509
|
+
if (!owner || typeof owner !== "string") throw new Error("owner is required");
|
|
510
|
+
if (_hookProfilesV2.has(id))
|
|
511
|
+
throw new Error(`hook profile ${id} already registered`);
|
|
512
|
+
const now = Date.now();
|
|
513
|
+
const p = {
|
|
514
|
+
id,
|
|
515
|
+
owner,
|
|
516
|
+
event: event || "*",
|
|
517
|
+
status: HOOK_PROFILE_MATURITY_V2.PENDING,
|
|
518
|
+
createdAt: now,
|
|
519
|
+
updatedAt: now,
|
|
520
|
+
activatedAt: null,
|
|
521
|
+
retiredAt: null,
|
|
522
|
+
lastTouchedAt: now,
|
|
523
|
+
metadata: { ...(metadata || {}) },
|
|
524
|
+
};
|
|
525
|
+
_hookProfilesV2.set(id, p);
|
|
526
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function _hookProfileCheck(from, to) {
|
|
530
|
+
const allowed = _hookProfileTransitionsV2.get(from);
|
|
531
|
+
if (!allowed || !allowed.has(to))
|
|
532
|
+
throw new Error(`invalid hook profile transition ${from} → ${to}`);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function _countActiveHooksByOwner(owner) {
|
|
536
|
+
let n = 0;
|
|
537
|
+
for (const p of _hookProfilesV2.values()) {
|
|
538
|
+
if (p.owner === owner && p.status === HOOK_PROFILE_MATURITY_V2.ACTIVE) n++;
|
|
539
|
+
}
|
|
540
|
+
return n;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
export function activateHookProfileV2(id) {
|
|
544
|
+
const p = _hookProfilesV2.get(id);
|
|
545
|
+
if (!p) throw new Error(`hook profile ${id} not found`);
|
|
546
|
+
_hookProfileCheck(p.status, HOOK_PROFILE_MATURITY_V2.ACTIVE);
|
|
547
|
+
const isRecovery = p.status === HOOK_PROFILE_MATURITY_V2.DISABLED;
|
|
548
|
+
if (!isRecovery) {
|
|
549
|
+
const active = _countActiveHooksByOwner(p.owner);
|
|
550
|
+
if (active >= _maxActiveHooksPerOwnerV2) {
|
|
551
|
+
throw new Error(
|
|
552
|
+
`max active hooks per owner (${_maxActiveHooksPerOwnerV2}) reached for ${p.owner}`,
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
const now = Date.now();
|
|
557
|
+
p.status = HOOK_PROFILE_MATURITY_V2.ACTIVE;
|
|
558
|
+
p.updatedAt = now;
|
|
559
|
+
p.lastTouchedAt = now;
|
|
560
|
+
if (!p.activatedAt) p.activatedAt = now;
|
|
561
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
export function disableHookProfileV2(id) {
|
|
565
|
+
const p = _hookProfilesV2.get(id);
|
|
566
|
+
if (!p) throw new Error(`hook profile ${id} not found`);
|
|
567
|
+
_hookProfileCheck(p.status, HOOK_PROFILE_MATURITY_V2.DISABLED);
|
|
568
|
+
const now = Date.now();
|
|
569
|
+
p.status = HOOK_PROFILE_MATURITY_V2.DISABLED;
|
|
570
|
+
p.updatedAt = now;
|
|
571
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
export function retireHookProfileV2(id) {
|
|
575
|
+
const p = _hookProfilesV2.get(id);
|
|
576
|
+
if (!p) throw new Error(`hook profile ${id} not found`);
|
|
577
|
+
_hookProfileCheck(p.status, HOOK_PROFILE_MATURITY_V2.RETIRED);
|
|
578
|
+
const now = Date.now();
|
|
579
|
+
p.status = HOOK_PROFILE_MATURITY_V2.RETIRED;
|
|
580
|
+
p.updatedAt = now;
|
|
581
|
+
if (!p.retiredAt) p.retiredAt = now;
|
|
582
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
export function touchHookProfileV2(id) {
|
|
586
|
+
const p = _hookProfilesV2.get(id);
|
|
587
|
+
if (!p) throw new Error(`hook profile ${id} not found`);
|
|
588
|
+
if (_hookProfileTerminalV2.has(p.status))
|
|
589
|
+
throw new Error(`cannot touch terminal hook profile ${id}`);
|
|
590
|
+
const now = Date.now();
|
|
591
|
+
p.lastTouchedAt = now;
|
|
592
|
+
p.updatedAt = now;
|
|
593
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
export function getHookProfileV2(id) {
|
|
597
|
+
const p = _hookProfilesV2.get(id);
|
|
598
|
+
if (!p) return null;
|
|
599
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
export function listHookProfilesV2() {
|
|
603
|
+
return [..._hookProfilesV2.values()].map((p) => ({
|
|
604
|
+
...p,
|
|
605
|
+
metadata: { ...p.metadata },
|
|
606
|
+
}));
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function _countPendingExecsByHook(hookId) {
|
|
610
|
+
let n = 0;
|
|
611
|
+
for (const e of _hookExecsV2.values()) {
|
|
612
|
+
if (
|
|
613
|
+
e.hookId === hookId &&
|
|
614
|
+
(e.status === HOOK_EXEC_LIFECYCLE_V2.QUEUED ||
|
|
615
|
+
e.status === HOOK_EXEC_LIFECYCLE_V2.RUNNING)
|
|
616
|
+
)
|
|
617
|
+
n++;
|
|
618
|
+
}
|
|
619
|
+
return n;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
export function createHookExecV2({ id, hookId, payload, metadata } = {}) {
|
|
623
|
+
if (!id || typeof id !== "string") throw new Error("id is required");
|
|
624
|
+
if (!hookId || typeof hookId !== "string")
|
|
625
|
+
throw new Error("hookId is required");
|
|
626
|
+
if (_hookExecsV2.has(id)) throw new Error(`hook exec ${id} already exists`);
|
|
627
|
+
const hook = _hookProfilesV2.get(hookId);
|
|
628
|
+
if (!hook) throw new Error(`hook profile ${hookId} not found`);
|
|
629
|
+
const pending = _countPendingExecsByHook(hookId);
|
|
630
|
+
if (pending >= _maxPendingExecsPerHookV2) {
|
|
631
|
+
throw new Error(
|
|
632
|
+
`max pending execs per hook (${_maxPendingExecsPerHookV2}) reached for ${hookId}`,
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
const now = Date.now();
|
|
636
|
+
const e = {
|
|
637
|
+
id,
|
|
638
|
+
hookId,
|
|
639
|
+
payload: payload || null,
|
|
640
|
+
status: HOOK_EXEC_LIFECYCLE_V2.QUEUED,
|
|
641
|
+
createdAt: now,
|
|
642
|
+
updatedAt: now,
|
|
643
|
+
startedAt: null,
|
|
644
|
+
settledAt: null,
|
|
645
|
+
metadata: { ...(metadata || {}) },
|
|
646
|
+
};
|
|
647
|
+
_hookExecsV2.set(id, e);
|
|
648
|
+
return { ...e, metadata: { ...e.metadata } };
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function _hookExecCheck(from, to) {
|
|
652
|
+
const allowed = _hookExecTransitionsV2.get(from);
|
|
653
|
+
if (!allowed || !allowed.has(to))
|
|
654
|
+
throw new Error(`invalid hook exec transition ${from} → ${to}`);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
export function startHookExecV2(id) {
|
|
658
|
+
const e = _hookExecsV2.get(id);
|
|
659
|
+
if (!e) throw new Error(`hook exec ${id} not found`);
|
|
660
|
+
_hookExecCheck(e.status, HOOK_EXEC_LIFECYCLE_V2.RUNNING);
|
|
661
|
+
const now = Date.now();
|
|
662
|
+
e.status = HOOK_EXEC_LIFECYCLE_V2.RUNNING;
|
|
663
|
+
e.updatedAt = now;
|
|
664
|
+
if (!e.startedAt) e.startedAt = now;
|
|
665
|
+
return { ...e, metadata: { ...e.metadata } };
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
export function completeHookExecV2(id) {
|
|
669
|
+
const e = _hookExecsV2.get(id);
|
|
670
|
+
if (!e) throw new Error(`hook exec ${id} not found`);
|
|
671
|
+
_hookExecCheck(e.status, HOOK_EXEC_LIFECYCLE_V2.COMPLETED);
|
|
672
|
+
const now = Date.now();
|
|
673
|
+
e.status = HOOK_EXEC_LIFECYCLE_V2.COMPLETED;
|
|
674
|
+
e.updatedAt = now;
|
|
675
|
+
if (!e.settledAt) e.settledAt = now;
|
|
676
|
+
return { ...e, metadata: { ...e.metadata } };
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
export function failHookExecV2(id, reason) {
|
|
680
|
+
const e = _hookExecsV2.get(id);
|
|
681
|
+
if (!e) throw new Error(`hook exec ${id} not found`);
|
|
682
|
+
_hookExecCheck(e.status, HOOK_EXEC_LIFECYCLE_V2.FAILED);
|
|
683
|
+
const now = Date.now();
|
|
684
|
+
e.status = HOOK_EXEC_LIFECYCLE_V2.FAILED;
|
|
685
|
+
e.updatedAt = now;
|
|
686
|
+
if (!e.settledAt) e.settledAt = now;
|
|
687
|
+
if (reason) e.metadata.failReason = String(reason);
|
|
688
|
+
return { ...e, metadata: { ...e.metadata } };
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
export function cancelHookExecV2(id, reason) {
|
|
692
|
+
const e = _hookExecsV2.get(id);
|
|
693
|
+
if (!e) throw new Error(`hook exec ${id} not found`);
|
|
694
|
+
_hookExecCheck(e.status, HOOK_EXEC_LIFECYCLE_V2.CANCELLED);
|
|
695
|
+
const now = Date.now();
|
|
696
|
+
e.status = HOOK_EXEC_LIFECYCLE_V2.CANCELLED;
|
|
697
|
+
e.updatedAt = now;
|
|
698
|
+
if (!e.settledAt) e.settledAt = now;
|
|
699
|
+
if (reason) e.metadata.cancelReason = String(reason);
|
|
700
|
+
return { ...e, metadata: { ...e.metadata } };
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
export function getHookExecV2(id) {
|
|
704
|
+
const e = _hookExecsV2.get(id);
|
|
705
|
+
if (!e) return null;
|
|
706
|
+
return { ...e, metadata: { ...e.metadata } };
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
export function listHookExecsV2() {
|
|
710
|
+
return [..._hookExecsV2.values()].map((e) => ({
|
|
711
|
+
...e,
|
|
712
|
+
metadata: { ...e.metadata },
|
|
713
|
+
}));
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
export function autoDisableIdleHooksV2({ now } = {}) {
|
|
717
|
+
const t = now ?? Date.now();
|
|
718
|
+
const flipped = [];
|
|
719
|
+
for (const p of _hookProfilesV2.values()) {
|
|
720
|
+
if (
|
|
721
|
+
p.status === HOOK_PROFILE_MATURITY_V2.ACTIVE &&
|
|
722
|
+
t - p.lastTouchedAt >= _hookIdleMsV2
|
|
723
|
+
) {
|
|
724
|
+
p.status = HOOK_PROFILE_MATURITY_V2.DISABLED;
|
|
725
|
+
p.updatedAt = t;
|
|
726
|
+
flipped.push(p.id);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return { flipped, count: flipped.length };
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
export function autoFailStuckHookExecsV2({ now } = {}) {
|
|
733
|
+
const t = now ?? Date.now();
|
|
734
|
+
const flipped = [];
|
|
735
|
+
for (const e of _hookExecsV2.values()) {
|
|
736
|
+
if (
|
|
737
|
+
e.status === HOOK_EXEC_LIFECYCLE_V2.RUNNING &&
|
|
738
|
+
e.startedAt != null &&
|
|
739
|
+
t - e.startedAt >= _hookExecStuckMsV2
|
|
740
|
+
) {
|
|
741
|
+
e.status = HOOK_EXEC_LIFECYCLE_V2.FAILED;
|
|
742
|
+
e.updatedAt = t;
|
|
743
|
+
if (!e.settledAt) e.settledAt = t;
|
|
744
|
+
e.metadata.failReason = "auto-fail-stuck";
|
|
745
|
+
flipped.push(e.id);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
return { flipped, count: flipped.length };
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
export function getHookManagerStatsV2() {
|
|
752
|
+
const profilesByStatus = {};
|
|
753
|
+
for (const s of Object.values(HOOK_PROFILE_MATURITY_V2))
|
|
754
|
+
profilesByStatus[s] = 0;
|
|
755
|
+
for (const p of _hookProfilesV2.values()) profilesByStatus[p.status]++;
|
|
756
|
+
const execsByStatus = {};
|
|
757
|
+
for (const s of Object.values(HOOK_EXEC_LIFECYCLE_V2)) execsByStatus[s] = 0;
|
|
758
|
+
for (const e of _hookExecsV2.values()) execsByStatus[e.status]++;
|
|
759
|
+
return {
|
|
760
|
+
totalProfilesV2: _hookProfilesV2.size,
|
|
761
|
+
totalExecsV2: _hookExecsV2.size,
|
|
762
|
+
maxActiveHooksPerOwner: _maxActiveHooksPerOwnerV2,
|
|
763
|
+
maxPendingExecsPerHook: _maxPendingExecsPerHookV2,
|
|
764
|
+
hookIdleMs: _hookIdleMsV2,
|
|
765
|
+
hookExecStuckMs: _hookExecStuckMsV2,
|
|
766
|
+
profilesByStatus,
|
|
767
|
+
execsByStatus,
|
|
768
|
+
};
|
|
769
|
+
}
|
|
@@ -735,3 +735,108 @@ export function getInferenceStatsV2() {
|
|
|
735
735
|
avgDurationMs: durCount > 0 ? Math.round(durSum / durCount) : 0,
|
|
736
736
|
};
|
|
737
737
|
}
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
// ===== V2 Surface: Inference Network governance overlay (CLI v0.139.0) =====
|
|
741
|
+
export const INFERENCE_NODE_MATURITY_V2 = Object.freeze({
|
|
742
|
+
PENDING: "pending", ACTIVE: "active", DEGRADED: "degraded", DECOMMISSIONED: "decommissioned",
|
|
743
|
+
});
|
|
744
|
+
export const INFERENCE_JOB_LIFECYCLE_V2 = Object.freeze({
|
|
745
|
+
QUEUED: "queued", RUNNING: "running", COMPLETED: "completed", FAILED: "failed", CANCELLED: "cancelled",
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
const _inTrans = new Map([
|
|
749
|
+
[INFERENCE_NODE_MATURITY_V2.PENDING, new Set([INFERENCE_NODE_MATURITY_V2.ACTIVE, INFERENCE_NODE_MATURITY_V2.DECOMMISSIONED])],
|
|
750
|
+
[INFERENCE_NODE_MATURITY_V2.ACTIVE, new Set([INFERENCE_NODE_MATURITY_V2.DEGRADED, INFERENCE_NODE_MATURITY_V2.DECOMMISSIONED])],
|
|
751
|
+
[INFERENCE_NODE_MATURITY_V2.DEGRADED, new Set([INFERENCE_NODE_MATURITY_V2.ACTIVE, INFERENCE_NODE_MATURITY_V2.DECOMMISSIONED])],
|
|
752
|
+
[INFERENCE_NODE_MATURITY_V2.DECOMMISSIONED, new Set()],
|
|
753
|
+
]);
|
|
754
|
+
const _inTerminal = new Set([INFERENCE_NODE_MATURITY_V2.DECOMMISSIONED]);
|
|
755
|
+
const _ijTrans = new Map([
|
|
756
|
+
[INFERENCE_JOB_LIFECYCLE_V2.QUEUED, new Set([INFERENCE_JOB_LIFECYCLE_V2.RUNNING, INFERENCE_JOB_LIFECYCLE_V2.CANCELLED])],
|
|
757
|
+
[INFERENCE_JOB_LIFECYCLE_V2.RUNNING, new Set([INFERENCE_JOB_LIFECYCLE_V2.COMPLETED, INFERENCE_JOB_LIFECYCLE_V2.FAILED, INFERENCE_JOB_LIFECYCLE_V2.CANCELLED])],
|
|
758
|
+
[INFERENCE_JOB_LIFECYCLE_V2.COMPLETED, new Set()],
|
|
759
|
+
[INFERENCE_JOB_LIFECYCLE_V2.FAILED, new Set()],
|
|
760
|
+
[INFERENCE_JOB_LIFECYCLE_V2.CANCELLED, new Set()],
|
|
761
|
+
]);
|
|
762
|
+
|
|
763
|
+
const _innV2 = new Map();
|
|
764
|
+
const _ijsV2 = new Map();
|
|
765
|
+
let _inMaxActivePerOperator = 12;
|
|
766
|
+
let _inMaxPendingJobsPerNode = 25;
|
|
767
|
+
let _inIdleMs = 24 * 60 * 60 * 1000;
|
|
768
|
+
let _ijStuckMs = 10 * 60 * 1000;
|
|
769
|
+
|
|
770
|
+
function _inPos(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 setMaxActiveInferenceNodesPerOperatorV2(n) { _inMaxActivePerOperator = _inPos(n, "maxActiveInferenceNodesPerOperator"); }
|
|
773
|
+
export function getMaxActiveInferenceNodesPerOperatorV2() { return _inMaxActivePerOperator; }
|
|
774
|
+
export function setMaxPendingInferenceJobsPerNodeV2(n) { _inMaxPendingJobsPerNode = _inPos(n, "maxPendingInferenceJobsPerNode"); }
|
|
775
|
+
export function getMaxPendingInferenceJobsPerNodeV2() { return _inMaxPendingJobsPerNode; }
|
|
776
|
+
export function setInferenceNodeIdleMsV2(n) { _inIdleMs = _inPos(n, "inferenceNodeIdleMs"); }
|
|
777
|
+
export function getInferenceNodeIdleMsV2() { return _inIdleMs; }
|
|
778
|
+
export function setInferenceJobStuckMsV2(n) { _ijStuckMs = _inPos(n, "inferenceJobStuckMs"); }
|
|
779
|
+
export function getInferenceJobStuckMsV2() { return _ijStuckMs; }
|
|
780
|
+
|
|
781
|
+
export function _resetStateInferenceNetworkV2() {
|
|
782
|
+
_innV2.clear(); _ijsV2.clear();
|
|
783
|
+
_inMaxActivePerOperator = 12; _inMaxPendingJobsPerNode = 25;
|
|
784
|
+
_inIdleMs = 24 * 60 * 60 * 1000; _ijStuckMs = 10 * 60 * 1000;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
export function registerInferenceNodeV2({ id, operator, model, metadata } = {}) {
|
|
788
|
+
if (!id || typeof id !== "string") throw new Error("id is required");
|
|
789
|
+
if (!operator || typeof operator !== "string") throw new Error("operator is required");
|
|
790
|
+
if (_innV2.has(id)) throw new Error(`inference node ${id} already registered`);
|
|
791
|
+
const now = Date.now();
|
|
792
|
+
const n = { id, operator, model: model || "default", status: INFERENCE_NODE_MATURITY_V2.PENDING, createdAt: now, updatedAt: now, activatedAt: null, decommissionedAt: null, lastTouchedAt: now, metadata: { ...(metadata || {}) } };
|
|
793
|
+
_innV2.set(id, n);
|
|
794
|
+
return { ...n, metadata: { ...n.metadata } };
|
|
795
|
+
}
|
|
796
|
+
function _inCheckN(from, to) { const a = _inTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid inference node transition ${from} → ${to}`); }
|
|
797
|
+
function _inCountActive(operator) { let n = 0; for (const x of _innV2.values()) if (x.operator === operator && x.status === INFERENCE_NODE_MATURITY_V2.ACTIVE) n++; return n; }
|
|
798
|
+
|
|
799
|
+
export function activateInferenceNodeV2(id) {
|
|
800
|
+
const n = _innV2.get(id); if (!n) throw new Error(`inference node ${id} not found`);
|
|
801
|
+
_inCheckN(n.status, INFERENCE_NODE_MATURITY_V2.ACTIVE);
|
|
802
|
+
const recovery = n.status === INFERENCE_NODE_MATURITY_V2.DEGRADED;
|
|
803
|
+
if (!recovery) { const c = _inCountActive(n.operator); if (c >= _inMaxActivePerOperator) throw new Error(`max active inference nodes per operator (${_inMaxActivePerOperator}) reached for ${n.operator}`); }
|
|
804
|
+
const now = Date.now(); n.status = INFERENCE_NODE_MATURITY_V2.ACTIVE; n.updatedAt = now; n.lastTouchedAt = now; if (!n.activatedAt) n.activatedAt = now;
|
|
805
|
+
return { ...n, metadata: { ...n.metadata } };
|
|
806
|
+
}
|
|
807
|
+
export function degradeInferenceNodeV2(id) { const n = _innV2.get(id); if (!n) throw new Error(`inference node ${id} not found`); _inCheckN(n.status, INFERENCE_NODE_MATURITY_V2.DEGRADED); n.status = INFERENCE_NODE_MATURITY_V2.DEGRADED; n.updatedAt = Date.now(); return { ...n, metadata: { ...n.metadata } }; }
|
|
808
|
+
export function decommissionInferenceNodeV2(id) { const n = _innV2.get(id); if (!n) throw new Error(`inference node ${id} not found`); _inCheckN(n.status, INFERENCE_NODE_MATURITY_V2.DECOMMISSIONED); const now = Date.now(); n.status = INFERENCE_NODE_MATURITY_V2.DECOMMISSIONED; n.updatedAt = now; if (!n.decommissionedAt) n.decommissionedAt = now; return { ...n, metadata: { ...n.metadata } }; }
|
|
809
|
+
export function touchInferenceNodeV2(id) { const n = _innV2.get(id); if (!n) throw new Error(`inference node ${id} not found`); if (_inTerminal.has(n.status)) throw new Error(`cannot touch terminal inference node ${id}`); const now = Date.now(); n.lastTouchedAt = now; n.updatedAt = now; return { ...n, metadata: { ...n.metadata } }; }
|
|
810
|
+
export function getInferenceNodeV2(id) { const n = _innV2.get(id); if (!n) return null; return { ...n, metadata: { ...n.metadata } }; }
|
|
811
|
+
export function listInferenceNodesV2() { return [..._innV2.values()].map((n) => ({ ...n, metadata: { ...n.metadata } })); }
|
|
812
|
+
|
|
813
|
+
function _ijCountPending(nodeId) { let n = 0; for (const j of _ijsV2.values()) if (j.nodeId === nodeId && (j.status === INFERENCE_JOB_LIFECYCLE_V2.QUEUED || j.status === INFERENCE_JOB_LIFECYCLE_V2.RUNNING)) n++; return n; }
|
|
814
|
+
|
|
815
|
+
export function createInferenceJobV2({ id, nodeId, prompt, metadata } = {}) {
|
|
816
|
+
if (!id || typeof id !== "string") throw new Error("id is required");
|
|
817
|
+
if (!nodeId || typeof nodeId !== "string") throw new Error("nodeId is required");
|
|
818
|
+
if (_ijsV2.has(id)) throw new Error(`inference job ${id} already exists`);
|
|
819
|
+
if (!_innV2.has(nodeId)) throw new Error(`inference node ${nodeId} not found`);
|
|
820
|
+
const pending = _ijCountPending(nodeId);
|
|
821
|
+
if (pending >= _inMaxPendingJobsPerNode) throw new Error(`max pending inference jobs per node (${_inMaxPendingJobsPerNode}) reached for ${nodeId}`);
|
|
822
|
+
const now = Date.now();
|
|
823
|
+
const j = { id, nodeId, prompt: prompt || "", status: INFERENCE_JOB_LIFECYCLE_V2.QUEUED, createdAt: now, updatedAt: now, startedAt: null, settledAt: null, metadata: { ...(metadata || {}) } };
|
|
824
|
+
_ijsV2.set(id, j);
|
|
825
|
+
return { ...j, metadata: { ...j.metadata } };
|
|
826
|
+
}
|
|
827
|
+
function _ijCheckJ(from, to) { const a = _ijTrans.get(from); if (!a || !a.has(to)) throw new Error(`invalid inference job transition ${from} → ${to}`); }
|
|
828
|
+
export function startInferenceJobV2(id) { const j = _ijsV2.get(id); if (!j) throw new Error(`inference job ${id} not found`); _ijCheckJ(j.status, INFERENCE_JOB_LIFECYCLE_V2.RUNNING); const now = Date.now(); j.status = INFERENCE_JOB_LIFECYCLE_V2.RUNNING; j.updatedAt = now; if (!j.startedAt) j.startedAt = now; return { ...j, metadata: { ...j.metadata } }; }
|
|
829
|
+
export function completeInferenceJobV2(id) { const j = _ijsV2.get(id); if (!j) throw new Error(`inference job ${id} not found`); _ijCheckJ(j.status, INFERENCE_JOB_LIFECYCLE_V2.COMPLETED); const now = Date.now(); j.status = INFERENCE_JOB_LIFECYCLE_V2.COMPLETED; j.updatedAt = now; if (!j.settledAt) j.settledAt = now; return { ...j, metadata: { ...j.metadata } }; }
|
|
830
|
+
export function failInferenceJobV2(id, reason) { const j = _ijsV2.get(id); if (!j) throw new Error(`inference job ${id} not found`); _ijCheckJ(j.status, INFERENCE_JOB_LIFECYCLE_V2.FAILED); const now = Date.now(); j.status = INFERENCE_JOB_LIFECYCLE_V2.FAILED; j.updatedAt = now; if (!j.settledAt) j.settledAt = now; if (reason) j.metadata.failReason = String(reason); return { ...j, metadata: { ...j.metadata } }; }
|
|
831
|
+
export function cancelInferenceJobV2(id, reason) { const j = _ijsV2.get(id); if (!j) throw new Error(`inference job ${id} not found`); _ijCheckJ(j.status, INFERENCE_JOB_LIFECYCLE_V2.CANCELLED); const now = Date.now(); j.status = INFERENCE_JOB_LIFECYCLE_V2.CANCELLED; j.updatedAt = now; if (!j.settledAt) j.settledAt = now; if (reason) j.metadata.cancelReason = String(reason); return { ...j, metadata: { ...j.metadata } }; }
|
|
832
|
+
export function getInferenceJobV2(id) { const j = _ijsV2.get(id); if (!j) return null; return { ...j, metadata: { ...j.metadata } }; }
|
|
833
|
+
export function listInferenceJobsV2() { return [..._ijsV2.values()].map((j) => ({ ...j, metadata: { ...j.metadata } })); }
|
|
834
|
+
|
|
835
|
+
export function autoDegradeIdleInferenceNodesV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const n of _innV2.values()) if (n.status === INFERENCE_NODE_MATURITY_V2.ACTIVE && (t - n.lastTouchedAt) >= _inIdleMs) { n.status = INFERENCE_NODE_MATURITY_V2.DEGRADED; n.updatedAt = t; flipped.push(n.id); } return { flipped, count: flipped.length }; }
|
|
836
|
+
export function autoFailStuckInferenceJobsV2({ now } = {}) { const t = now ?? Date.now(); const flipped = []; for (const j of _ijsV2.values()) if (j.status === INFERENCE_JOB_LIFECYCLE_V2.RUNNING && j.startedAt != null && (t - j.startedAt) >= _ijStuckMs) { j.status = INFERENCE_JOB_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 }; }
|
|
837
|
+
|
|
838
|
+
export function getInferenceNetworkGovStatsV2() {
|
|
839
|
+
const nodesByStatus = {}; for (const s of Object.values(INFERENCE_NODE_MATURITY_V2)) nodesByStatus[s] = 0; for (const n of _innV2.values()) nodesByStatus[n.status]++;
|
|
840
|
+
const jobsByStatus = {}; for (const s of Object.values(INFERENCE_JOB_LIFECYCLE_V2)) jobsByStatus[s] = 0; for (const j of _ijsV2.values()) jobsByStatus[j.status]++;
|
|
841
|
+
return { totalInferenceNodesV2: _innV2.size, totalInferenceJobsV2: _ijsV2.size, maxActiveInferenceNodesPerOperator: _inMaxActivePerOperator, maxPendingInferenceJobsPerNode: _inMaxPendingJobsPerNode, inferenceNodeIdleMs: _inIdleMs, inferenceJobStuckMs: _ijStuckMs, nodesByStatus, jobsByStatus };
|
|
842
|
+
}
|