chainlesschain 0.66.0 → 0.132.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/chainlesschain.js +0 -0
- package/package.json +1 -1
- package/src/commands/a2a.js +380 -0
- package/src/commands/agent-network.js +254 -1
- package/src/commands/audit.js +302 -0
- package/src/commands/automation.js +271 -1
- package/src/commands/bi.js +348 -0
- package/src/commands/codegen.js +224 -0
- package/src/commands/collab.js +341 -0
- package/src/commands/compliance.js +1035 -0
- package/src/commands/cowork.js +221 -0
- package/src/commands/crosschain.js +218 -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 +341 -0
- package/src/commands/encrypt.js +341 -0
- package/src/commands/evomap.js +394 -0
- package/src/commands/export.js +256 -1
- package/src/commands/federation.js +283 -0
- package/src/commands/fusion.js +258 -0
- package/src/commands/governance.js +325 -0
- package/src/commands/hardening.js +411 -0
- package/src/commands/hook.js +148 -0
- package/src/commands/import.js +252 -0
- package/src/commands/incentive.js +322 -0
- package/src/commands/inference.js +318 -0
- package/src/commands/infra.js +244 -0
- package/src/commands/instinct.js +260 -0
- package/src/commands/ipfs.js +318 -0
- package/src/commands/kg.js +387 -0
- package/src/commands/llm.js +263 -0
- package/src/commands/lowcode.js +356 -0
- package/src/commands/marketplace.js +256 -0
- package/src/commands/mcp.js +221 -0
- package/src/commands/memory.js +248 -0
- package/src/commands/multimodal.js +296 -0
- package/src/commands/nlprog.js +356 -0
- package/src/commands/note.js +244 -0
- package/src/commands/ops.js +354 -0
- package/src/commands/orchestrate.js +166 -0
- package/src/commands/org.js +277 -0
- package/src/commands/p2p.js +390 -0
- package/src/commands/perception.js +290 -0
- package/src/commands/permmem.js +251 -0
- package/src/commands/plugin-ecosystem.js +273 -0
- package/src/commands/pqc.js +393 -0
- package/src/commands/privacy.js +321 -0
- package/src/commands/quantization.js +351 -0
- package/src/commands/rcache.js +271 -0
- package/src/commands/recommend.js +340 -0
- package/src/commands/reputation.js +261 -0
- package/src/commands/runtime.js +307 -0
- package/src/commands/scim.js +262 -0
- package/src/commands/session.js +258 -0
- package/src/commands/siem.js +246 -0
- package/src/commands/skill.js +267 -1
- package/src/commands/sla.js +259 -0
- package/src/commands/social.js +256 -0
- package/src/commands/sso.js +186 -1
- package/src/commands/stress.js +230 -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/terraform.js +245 -0
- package/src/commands/tokens.js +269 -0
- package/src/commands/trust.js +249 -0
- package/src/commands/wallet.js +277 -0
- package/src/commands/workflow.js +171 -0
- package/src/commands/zkp.js +335 -0
- package/src/index.js +4 -0
- package/src/lib/a2a-protocol.js +451 -0
- package/src/lib/agent-coordinator.js +325 -0
- package/src/lib/agent-network.js +387 -0
- package/src/lib/agent-router.js +395 -0
- package/src/lib/aiops.js +478 -0
- package/src/lib/app-builder.js +239 -0
- package/src/lib/audit-logger.js +379 -0
- package/src/lib/automation-engine.js +330 -0
- package/src/lib/autonomous-developer.js +350 -0
- package/src/lib/bi-engine.js +338 -0
- package/src/lib/code-agent.js +323 -0
- package/src/lib/collaboration-governance.js +364 -0
- package/src/lib/community-governance.js +436 -0
- package/src/lib/compliance-manager.js +434 -0
- package/src/lib/content-recommendation.js +469 -0
- package/src/lib/cross-chain.js +345 -0
- package/src/lib/crypto-manager.js +350 -0
- package/src/lib/dbevo.js +338 -0
- package/src/lib/decentral-infra.js +340 -0
- package/src/lib/did-manager.js +367 -0
- package/src/lib/dlp-engine.js +389 -0
- package/src/lib/evomap-federation.js +177 -0
- package/src/lib/evomap-governance.js +276 -0
- package/src/lib/federation-hardening.js +259 -0
- package/src/lib/hardening-manager.js +348 -0
- package/src/lib/hook-manager.js +380 -0
- package/src/lib/inference-network.js +330 -0
- package/src/lib/instinct-manager.js +332 -0
- package/src/lib/ipfs-storage.js +334 -0
- package/src/lib/knowledge-exporter.js +381 -0
- package/src/lib/knowledge-graph.js +432 -0
- package/src/lib/knowledge-importer.js +379 -0
- package/src/lib/llm-providers.js +391 -0
- package/src/lib/mcp-registry.js +333 -0
- package/src/lib/memory-manager.js +330 -0
- package/src/lib/multimodal.js +346 -0
- package/src/lib/nl-programming.js +343 -0
- package/src/lib/note-versioning.js +327 -0
- package/src/lib/org-manager.js +323 -0
- package/src/lib/p2p-manager.js +387 -0
- package/src/lib/perception.js +346 -0
- package/src/lib/perf-tuning.js +4 -1
- package/src/lib/permanent-memory.js +320 -0
- package/src/lib/plugin-ecosystem.js +377 -0
- package/src/lib/pqc-manager.js +368 -0
- package/src/lib/privacy-computing.js +427 -0
- package/src/lib/protocol-fusion.js +417 -0
- package/src/lib/quantization.js +325 -0
- package/src/lib/reputation-optimizer.js +299 -0
- package/src/lib/response-cache.js +327 -0
- package/src/lib/scim-manager.js +329 -0
- package/src/lib/session-manager.js +329 -0
- package/src/lib/siem-exporter.js +333 -0
- package/src/lib/skill-loader.js +377 -0
- package/src/lib/skill-marketplace.js +325 -0
- package/src/lib/sla-manager.js +275 -0
- package/src/lib/social-manager.js +326 -0
- package/src/lib/sso-manager.js +332 -0
- package/src/lib/stress-tester.js +330 -0
- package/src/lib/sync-manager.js +326 -0
- package/src/lib/tech-learning-engine.js +369 -0
- package/src/lib/tenant-saas.js +460 -0
- package/src/lib/terraform-manager.js +363 -0
- package/src/lib/threat-intel.js +335 -0
- package/src/lib/token-incentive.js +293 -0
- package/src/lib/token-tracker.js +329 -0
- package/src/lib/trust-security.js +390 -0
- package/src/lib/ueba.js +389 -0
- package/src/lib/universal-runtime.js +325 -0
- package/src/lib/wallet-manager.js +326 -0
- package/src/lib/workflow-engine.js +322 -0
- package/src/lib/zkp-engine.js +274 -0
package/src/lib/sso-manager.js
CHANGED
|
@@ -839,3 +839,335 @@ export function getStats(db) {
|
|
|
839
839
|
},
|
|
840
840
|
};
|
|
841
841
|
}
|
|
842
|
+
|
|
843
|
+
// ===== V2 Surface (cli 0.130.0) — in-memory governance =====
|
|
844
|
+
export const PROVIDER_MATURITY_V2 = Object.freeze({
|
|
845
|
+
PENDING: "pending",
|
|
846
|
+
ACTIVE: "active",
|
|
847
|
+
DEPRECATED: "deprecated",
|
|
848
|
+
RETIRED: "retired",
|
|
849
|
+
});
|
|
850
|
+
export const LOGIN_LIFECYCLE_V2 = Object.freeze({
|
|
851
|
+
QUEUED: "queued",
|
|
852
|
+
AUTHENTICATING: "authenticating",
|
|
853
|
+
AUTHENTICATED: "authenticated",
|
|
854
|
+
FAILED: "failed",
|
|
855
|
+
CANCELLED: "cancelled",
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
const _PM_V2 = PROVIDER_MATURITY_V2;
|
|
859
|
+
const _LL_V2 = LOGIN_LIFECYCLE_V2;
|
|
860
|
+
const _PM_TRANS_V2 = new Map([
|
|
861
|
+
[_PM_V2.PENDING, new Set([_PM_V2.ACTIVE, _PM_V2.RETIRED])],
|
|
862
|
+
[_PM_V2.ACTIVE, new Set([_PM_V2.DEPRECATED, _PM_V2.RETIRED])],
|
|
863
|
+
[_PM_V2.DEPRECATED, new Set([_PM_V2.ACTIVE, _PM_V2.RETIRED])],
|
|
864
|
+
[_PM_V2.RETIRED, new Set()],
|
|
865
|
+
]);
|
|
866
|
+
const _LL_TRANS_V2 = new Map([
|
|
867
|
+
[_LL_V2.QUEUED, new Set([_LL_V2.AUTHENTICATING, _LL_V2.CANCELLED])],
|
|
868
|
+
[
|
|
869
|
+
_LL_V2.AUTHENTICATING,
|
|
870
|
+
new Set([_LL_V2.AUTHENTICATED, _LL_V2.FAILED, _LL_V2.CANCELLED]),
|
|
871
|
+
],
|
|
872
|
+
[_LL_V2.AUTHENTICATED, new Set()],
|
|
873
|
+
[_LL_V2.FAILED, new Set()],
|
|
874
|
+
[_LL_V2.CANCELLED, new Set()],
|
|
875
|
+
]);
|
|
876
|
+
const _LL_TERM_V2 = new Set([
|
|
877
|
+
_LL_V2.AUTHENTICATED,
|
|
878
|
+
_LL_V2.FAILED,
|
|
879
|
+
_LL_V2.CANCELLED,
|
|
880
|
+
]);
|
|
881
|
+
|
|
882
|
+
const SSO_DEFAULT_MAX_ACTIVE_PROVIDERS_PER_OWNER = 8;
|
|
883
|
+
const SSO_DEFAULT_MAX_PENDING_LOGINS_PER_PROVIDER = 16;
|
|
884
|
+
const SSO_DEFAULT_PROVIDER_IDLE_MS = 30 * 24 * 60 * 60 * 1000;
|
|
885
|
+
const SSO_DEFAULT_LOGIN_STUCK_MS = 5 * 60 * 1000;
|
|
886
|
+
|
|
887
|
+
const _ssoProvidersV2 = new Map();
|
|
888
|
+
const _ssoLoginsV2 = new Map();
|
|
889
|
+
let _ssoConfigV2 = {
|
|
890
|
+
maxActiveProvidersPerOwner: SSO_DEFAULT_MAX_ACTIVE_PROVIDERS_PER_OWNER,
|
|
891
|
+
maxPendingLoginsPerProvider: SSO_DEFAULT_MAX_PENDING_LOGINS_PER_PROVIDER,
|
|
892
|
+
providerIdleMs: SSO_DEFAULT_PROVIDER_IDLE_MS,
|
|
893
|
+
loginStuckMs: SSO_DEFAULT_LOGIN_STUCK_MS,
|
|
894
|
+
};
|
|
895
|
+
|
|
896
|
+
function _ssoPosIntV2(n, label) {
|
|
897
|
+
if (typeof n !== "number" || !isFinite(n) || isNaN(n))
|
|
898
|
+
throw new Error(`${label} must be positive integer`);
|
|
899
|
+
const v = Math.floor(n);
|
|
900
|
+
if (v <= 0) throw new Error(`${label} must be positive integer`);
|
|
901
|
+
return v;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
export function _resetStateSsoManagerV2() {
|
|
905
|
+
_ssoProvidersV2.clear();
|
|
906
|
+
_ssoLoginsV2.clear();
|
|
907
|
+
_ssoConfigV2 = {
|
|
908
|
+
maxActiveProvidersPerOwner: SSO_DEFAULT_MAX_ACTIVE_PROVIDERS_PER_OWNER,
|
|
909
|
+
maxPendingLoginsPerProvider: SSO_DEFAULT_MAX_PENDING_LOGINS_PER_PROVIDER,
|
|
910
|
+
providerIdleMs: SSO_DEFAULT_PROVIDER_IDLE_MS,
|
|
911
|
+
loginStuckMs: SSO_DEFAULT_LOGIN_STUCK_MS,
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
export function setMaxActiveProvidersPerOwnerV2(n) {
|
|
916
|
+
_ssoConfigV2.maxActiveProvidersPerOwner = _ssoPosIntV2(
|
|
917
|
+
n,
|
|
918
|
+
"maxActiveProvidersPerOwner",
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
export function setMaxPendingLoginsPerProviderV2(n) {
|
|
922
|
+
_ssoConfigV2.maxPendingLoginsPerProvider = _ssoPosIntV2(
|
|
923
|
+
n,
|
|
924
|
+
"maxPendingLoginsPerProvider",
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
export function setProviderIdleMsV2(n) {
|
|
928
|
+
_ssoConfigV2.providerIdleMs = _ssoPosIntV2(n, "providerIdleMs");
|
|
929
|
+
}
|
|
930
|
+
export function setLoginStuckMsV2(n) {
|
|
931
|
+
_ssoConfigV2.loginStuckMs = _ssoPosIntV2(n, "loginStuckMs");
|
|
932
|
+
}
|
|
933
|
+
export function getMaxActiveProvidersPerOwnerV2() {
|
|
934
|
+
return _ssoConfigV2.maxActiveProvidersPerOwner;
|
|
935
|
+
}
|
|
936
|
+
export function getMaxPendingLoginsPerProviderV2() {
|
|
937
|
+
return _ssoConfigV2.maxPendingLoginsPerProvider;
|
|
938
|
+
}
|
|
939
|
+
export function getProviderIdleMsV2() {
|
|
940
|
+
return _ssoConfigV2.providerIdleMs;
|
|
941
|
+
}
|
|
942
|
+
export function getLoginStuckMsV2() {
|
|
943
|
+
return _ssoConfigV2.loginStuckMs;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
function _copyProviderV2(p) {
|
|
947
|
+
return { ...p, metadata: { ...(p.metadata || {}) } };
|
|
948
|
+
}
|
|
949
|
+
function _copyLoginV2(l) {
|
|
950
|
+
return { ...l, metadata: { ...(l.metadata || {}) } };
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
export function registerProviderV2({
|
|
954
|
+
id,
|
|
955
|
+
owner,
|
|
956
|
+
protocol,
|
|
957
|
+
displayName,
|
|
958
|
+
metadata,
|
|
959
|
+
} = {}) {
|
|
960
|
+
if (!id || typeof id !== "string") throw new Error("id required");
|
|
961
|
+
if (!owner || typeof owner !== "string") throw new Error("owner required");
|
|
962
|
+
if (!protocol || typeof protocol !== "string")
|
|
963
|
+
throw new Error("protocol required");
|
|
964
|
+
if (_ssoProvidersV2.has(id))
|
|
965
|
+
throw new Error(`provider ${id} already registered`);
|
|
966
|
+
const now = Date.now();
|
|
967
|
+
const p = {
|
|
968
|
+
id,
|
|
969
|
+
owner,
|
|
970
|
+
protocol,
|
|
971
|
+
displayName: displayName || id,
|
|
972
|
+
status: _PM_V2.PENDING,
|
|
973
|
+
activatedAt: null,
|
|
974
|
+
retiredAt: null,
|
|
975
|
+
lastSeenAt: now,
|
|
976
|
+
createdAt: now,
|
|
977
|
+
metadata: metadata && typeof metadata === "object" ? { ...metadata } : {},
|
|
978
|
+
};
|
|
979
|
+
_ssoProvidersV2.set(id, p);
|
|
980
|
+
return _copyProviderV2(p);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
function _activeProviderCountForOwnerV2(owner) {
|
|
984
|
+
let c = 0;
|
|
985
|
+
for (const p of _ssoProvidersV2.values())
|
|
986
|
+
if (p.owner === owner && p.status === _PM_V2.ACTIVE) c++;
|
|
987
|
+
return c;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
function _transitionProviderV2(id, next) {
|
|
991
|
+
const p = _ssoProvidersV2.get(id);
|
|
992
|
+
if (!p) throw new Error(`provider ${id} not found`);
|
|
993
|
+
const allowed = _PM_TRANS_V2.get(p.status);
|
|
994
|
+
if (!allowed || !allowed.has(next))
|
|
995
|
+
throw new Error(`invalid transition ${p.status} -> ${next}`);
|
|
996
|
+
if (next === _PM_V2.ACTIVE && p.status === _PM_V2.PENDING) {
|
|
997
|
+
if (
|
|
998
|
+
_activeProviderCountForOwnerV2(p.owner) >=
|
|
999
|
+
_ssoConfigV2.maxActiveProvidersPerOwner
|
|
1000
|
+
) {
|
|
1001
|
+
throw new Error(
|
|
1002
|
+
`owner ${p.owner} active-provider cap reached (${_ssoConfigV2.maxActiveProvidersPerOwner})`,
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
const now = Date.now();
|
|
1007
|
+
p.status = next;
|
|
1008
|
+
if (next === _PM_V2.ACTIVE && !p.activatedAt) p.activatedAt = now;
|
|
1009
|
+
if (next === _PM_V2.RETIRED && !p.retiredAt) p.retiredAt = now;
|
|
1010
|
+
p.lastSeenAt = now;
|
|
1011
|
+
return _copyProviderV2(p);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
export function activateProviderV2(id) {
|
|
1015
|
+
return _transitionProviderV2(id, _PM_V2.ACTIVE);
|
|
1016
|
+
}
|
|
1017
|
+
export function deprecateProviderV2(id) {
|
|
1018
|
+
return _transitionProviderV2(id, _PM_V2.DEPRECATED);
|
|
1019
|
+
}
|
|
1020
|
+
export function retireProviderV2(id) {
|
|
1021
|
+
return _transitionProviderV2(id, _PM_V2.RETIRED);
|
|
1022
|
+
}
|
|
1023
|
+
export function touchProviderV2(id) {
|
|
1024
|
+
const p = _ssoProvidersV2.get(id);
|
|
1025
|
+
if (!p) throw new Error(`provider ${id} not found`);
|
|
1026
|
+
p.lastSeenAt = Date.now();
|
|
1027
|
+
return _copyProviderV2(p);
|
|
1028
|
+
}
|
|
1029
|
+
export function getProviderV2(id) {
|
|
1030
|
+
const p = _ssoProvidersV2.get(id);
|
|
1031
|
+
return p ? _copyProviderV2(p) : null;
|
|
1032
|
+
}
|
|
1033
|
+
export function listProvidersV2({ owner, status, protocol } = {}) {
|
|
1034
|
+
const out = [];
|
|
1035
|
+
for (const p of _ssoProvidersV2.values()) {
|
|
1036
|
+
if (owner && p.owner !== owner) continue;
|
|
1037
|
+
if (status && p.status !== status) continue;
|
|
1038
|
+
if (protocol && p.protocol !== protocol) continue;
|
|
1039
|
+
out.push(_copyProviderV2(p));
|
|
1040
|
+
}
|
|
1041
|
+
return out;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
function _pendingLoginCountForProviderV2(providerId) {
|
|
1045
|
+
let c = 0;
|
|
1046
|
+
for (const l of _ssoLoginsV2.values()) {
|
|
1047
|
+
if (l.providerId !== providerId) continue;
|
|
1048
|
+
if (l.status === _LL_V2.QUEUED || l.status === _LL_V2.AUTHENTICATING) c++;
|
|
1049
|
+
}
|
|
1050
|
+
return c;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
export function createLoginV2({ id, providerId, subject, metadata } = {}) {
|
|
1054
|
+
if (!id || typeof id !== "string") throw new Error("id required");
|
|
1055
|
+
if (!providerId || typeof providerId !== "string")
|
|
1056
|
+
throw new Error("providerId required");
|
|
1057
|
+
if (_ssoLoginsV2.has(id)) throw new Error(`login ${id} already exists`);
|
|
1058
|
+
const provider = _ssoProvidersV2.get(providerId);
|
|
1059
|
+
if (!provider) throw new Error(`provider ${providerId} not found`);
|
|
1060
|
+
if (provider.status === _PM_V2.RETIRED)
|
|
1061
|
+
throw new Error(`provider ${providerId} retired`);
|
|
1062
|
+
if (
|
|
1063
|
+
_pendingLoginCountForProviderV2(providerId) >=
|
|
1064
|
+
_ssoConfigV2.maxPendingLoginsPerProvider
|
|
1065
|
+
) {
|
|
1066
|
+
throw new Error(
|
|
1067
|
+
`provider ${providerId} pending-login cap reached (${_ssoConfigV2.maxPendingLoginsPerProvider})`,
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
const now = Date.now();
|
|
1071
|
+
const l = {
|
|
1072
|
+
id,
|
|
1073
|
+
providerId,
|
|
1074
|
+
subject: subject || "anonymous",
|
|
1075
|
+
status: _LL_V2.QUEUED,
|
|
1076
|
+
startedAt: null,
|
|
1077
|
+
settledAt: null,
|
|
1078
|
+
createdAt: now,
|
|
1079
|
+
metadata: metadata && typeof metadata === "object" ? { ...metadata } : {},
|
|
1080
|
+
};
|
|
1081
|
+
_ssoLoginsV2.set(id, l);
|
|
1082
|
+
return _copyLoginV2(l);
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
function _transitionLoginV2(id, next, extra = {}) {
|
|
1086
|
+
const l = _ssoLoginsV2.get(id);
|
|
1087
|
+
if (!l) throw new Error(`login ${id} not found`);
|
|
1088
|
+
const allowed = _LL_TRANS_V2.get(l.status);
|
|
1089
|
+
if (!allowed || !allowed.has(next))
|
|
1090
|
+
throw new Error(`invalid transition ${l.status} -> ${next}`);
|
|
1091
|
+
const now = Date.now();
|
|
1092
|
+
l.status = next;
|
|
1093
|
+
if (next === _LL_V2.AUTHENTICATING && !l.startedAt) l.startedAt = now;
|
|
1094
|
+
if (_LL_TERM_V2.has(next) && !l.settledAt) l.settledAt = now;
|
|
1095
|
+
if (extra.error) l.metadata.error = extra.error;
|
|
1096
|
+
return _copyLoginV2(l);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
export function startLoginV2(id) {
|
|
1100
|
+
return _transitionLoginV2(id, _LL_V2.AUTHENTICATING);
|
|
1101
|
+
}
|
|
1102
|
+
export function completeLoginV2(id) {
|
|
1103
|
+
return _transitionLoginV2(id, _LL_V2.AUTHENTICATED);
|
|
1104
|
+
}
|
|
1105
|
+
export function failLoginV2(id, error) {
|
|
1106
|
+
return _transitionLoginV2(id, _LL_V2.FAILED, { error });
|
|
1107
|
+
}
|
|
1108
|
+
export function cancelLoginV2(id) {
|
|
1109
|
+
return _transitionLoginV2(id, _LL_V2.CANCELLED);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
export function getLoginV2(id) {
|
|
1113
|
+
const l = _ssoLoginsV2.get(id);
|
|
1114
|
+
return l ? _copyLoginV2(l) : null;
|
|
1115
|
+
}
|
|
1116
|
+
export function listLoginsV2({ providerId, status, subject } = {}) {
|
|
1117
|
+
const out = [];
|
|
1118
|
+
for (const l of _ssoLoginsV2.values()) {
|
|
1119
|
+
if (providerId && l.providerId !== providerId) continue;
|
|
1120
|
+
if (status && l.status !== status) continue;
|
|
1121
|
+
if (subject && l.subject !== subject) continue;
|
|
1122
|
+
out.push(_copyLoginV2(l));
|
|
1123
|
+
}
|
|
1124
|
+
return out;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
export function autoDeprecateIdleProvidersV2({ now } = {}) {
|
|
1128
|
+
const t = typeof now === "number" ? now : Date.now();
|
|
1129
|
+
const flipped = [];
|
|
1130
|
+
for (const p of _ssoProvidersV2.values()) {
|
|
1131
|
+
if (p.status !== _PM_V2.ACTIVE) continue;
|
|
1132
|
+
if (t - p.lastSeenAt > _ssoConfigV2.providerIdleMs) {
|
|
1133
|
+
p.status = _PM_V2.DEPRECATED;
|
|
1134
|
+
p.lastSeenAt = t;
|
|
1135
|
+
flipped.push(_copyProviderV2(p));
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
return flipped;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
export function autoFailStuckLoginsV2({ now } = {}) {
|
|
1142
|
+
const t = typeof now === "number" ? now : Date.now();
|
|
1143
|
+
const flipped = [];
|
|
1144
|
+
for (const l of _ssoLoginsV2.values()) {
|
|
1145
|
+
if (l.status !== _LL_V2.AUTHENTICATING) continue;
|
|
1146
|
+
if (l.startedAt && t - l.startedAt > _ssoConfigV2.loginStuckMs) {
|
|
1147
|
+
l.status = _LL_V2.FAILED;
|
|
1148
|
+
l.settledAt = t;
|
|
1149
|
+
l.metadata.error = "stuck-timeout";
|
|
1150
|
+
flipped.push(_copyLoginV2(l));
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
return flipped;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
export function getSsoManagerStatsV2() {
|
|
1157
|
+
const providersByStatus = {};
|
|
1158
|
+
for (const s of Object.values(_PM_V2)) providersByStatus[s] = 0;
|
|
1159
|
+
for (const p of _ssoProvidersV2.values()) providersByStatus[p.status]++;
|
|
1160
|
+
const loginsByStatus = {};
|
|
1161
|
+
for (const s of Object.values(_LL_V2)) loginsByStatus[s] = 0;
|
|
1162
|
+
for (const l of _ssoLoginsV2.values()) loginsByStatus[l.status]++;
|
|
1163
|
+
return {
|
|
1164
|
+
totalProvidersV2: _ssoProvidersV2.size,
|
|
1165
|
+
totalLoginsV2: _ssoLoginsV2.size,
|
|
1166
|
+
maxActiveProvidersPerOwner: _ssoConfigV2.maxActiveProvidersPerOwner,
|
|
1167
|
+
maxPendingLoginsPerProvider: _ssoConfigV2.maxPendingLoginsPerProvider,
|
|
1168
|
+
providerIdleMs: _ssoConfigV2.providerIdleMs,
|
|
1169
|
+
loginStuckMs: _ssoConfigV2.loginStuckMs,
|
|
1170
|
+
providersByStatus,
|
|
1171
|
+
loginsByStatus,
|
|
1172
|
+
};
|
|
1173
|
+
}
|
package/src/lib/stress-tester.js
CHANGED
|
@@ -380,4 +380,334 @@ export function _resetState() {
|
|
|
380
380
|
_runs.clear();
|
|
381
381
|
_results.clear();
|
|
382
382
|
_seq = 0;
|
|
383
|
+
_maxConcurrentTests = DEFAULT_MAX_CONCURRENT_TESTS;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
387
|
+
* V2 Canonical Surface (Phase 59 — Federation Stress Test)
|
|
388
|
+
* Strictly additive; legacy exports above remain unchanged.
|
|
389
|
+
* ═══════════════════════════════════════════════════════════════ */
|
|
390
|
+
|
|
391
|
+
export const RUN_STATUS_V2 = Object.freeze({
|
|
392
|
+
RUNNING: "running",
|
|
393
|
+
COMPLETE: "complete",
|
|
394
|
+
STOPPED: "stopped",
|
|
395
|
+
FAILED: "failed",
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
export const LEVEL_NAME_V2 = Object.freeze({
|
|
399
|
+
LIGHT: "light",
|
|
400
|
+
MEDIUM: "medium",
|
|
401
|
+
HEAVY: "heavy",
|
|
402
|
+
EXTREME: "extreme",
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
export const BOTTLENECK_KIND_V2 = Object.freeze({
|
|
406
|
+
ERROR_RATE: "error-rate",
|
|
407
|
+
TAIL_LATENCY: "tail-latency",
|
|
408
|
+
RESPONSE_TIME: "response-time",
|
|
409
|
+
THROUGHPUT: "throughput",
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
export const BOTTLENECK_SEVERITY_V2 = Object.freeze({
|
|
413
|
+
LOW: "low",
|
|
414
|
+
MEDIUM: "medium",
|
|
415
|
+
HIGH: "high",
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
const DEFAULT_MAX_CONCURRENT_TESTS = 3;
|
|
419
|
+
let _maxConcurrentTests = DEFAULT_MAX_CONCURRENT_TESTS;
|
|
420
|
+
|
|
421
|
+
export const STRESS_DEFAULT_MAX_CONCURRENT = DEFAULT_MAX_CONCURRENT_TESTS;
|
|
422
|
+
|
|
423
|
+
export function setMaxConcurrentTests(n) {
|
|
424
|
+
if (typeof n !== "number" || !Number.isFinite(n) || n < 1) {
|
|
425
|
+
throw new Error("maxConcurrentTests must be a positive integer");
|
|
426
|
+
}
|
|
427
|
+
_maxConcurrentTests = Math.floor(n);
|
|
428
|
+
return _maxConcurrentTests;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export function getMaxConcurrentTests() {
|
|
432
|
+
return _maxConcurrentTests;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const _terminalRunStatuses = new Set([
|
|
436
|
+
RUN_STATUS_V2.COMPLETE,
|
|
437
|
+
RUN_STATUS_V2.STOPPED,
|
|
438
|
+
RUN_STATUS_V2.FAILED,
|
|
439
|
+
]);
|
|
440
|
+
|
|
441
|
+
// Run state machine: running → { complete, stopped, failed }
|
|
442
|
+
const _allowedRunTransitions = new Map([
|
|
443
|
+
[
|
|
444
|
+
RUN_STATUS_V2.RUNNING,
|
|
445
|
+
new Set([
|
|
446
|
+
RUN_STATUS_V2.COMPLETE,
|
|
447
|
+
RUN_STATUS_V2.STOPPED,
|
|
448
|
+
RUN_STATUS_V2.FAILED,
|
|
449
|
+
]),
|
|
450
|
+
],
|
|
451
|
+
[RUN_STATUS_V2.COMPLETE, new Set([])],
|
|
452
|
+
[RUN_STATUS_V2.STOPPED, new Set([])],
|
|
453
|
+
[RUN_STATUS_V2.FAILED, new Set([])],
|
|
454
|
+
]);
|
|
455
|
+
|
|
456
|
+
export function getActiveTestCount() {
|
|
457
|
+
let count = 0;
|
|
458
|
+
for (const r of _runs.values()) {
|
|
459
|
+
if (r.status === RUN_STATUS_V2.RUNNING) count++;
|
|
460
|
+
}
|
|
461
|
+
return count;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* startStressTestV2 — asynchronous lifecycle variant. Creates the run row in
|
|
466
|
+
* RUNNING state without computing metrics; caller is expected to call
|
|
467
|
+
* completeStressTest, stopStressTestV2, or failStressTest afterwards.
|
|
468
|
+
*/
|
|
469
|
+
export function startStressTestV2(db, config = {}) {
|
|
470
|
+
const levelName = config.level || LEVEL_NAME_V2.MEDIUM;
|
|
471
|
+
const level = resolveLevel(levelName);
|
|
472
|
+
if (!level) {
|
|
473
|
+
throw new Error(
|
|
474
|
+
`Unknown load level: ${levelName} (known: ${Object.values(LEVEL_NAME_V2).join("/")})`,
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const concurrency = Number(config.concurrency ?? level.concurrency);
|
|
479
|
+
const requestsPerSecond = Number(
|
|
480
|
+
config.requestsPerSecond ?? level.requestsPerSecond,
|
|
481
|
+
);
|
|
482
|
+
const duration = Number(config.duration ?? level.duration);
|
|
483
|
+
if (concurrency <= 0 || duration <= 0 || requestsPerSecond <= 0) {
|
|
484
|
+
throw new Error("concurrency/duration/requestsPerSecond must all be > 0");
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const activeCount = getActiveTestCount();
|
|
488
|
+
if (activeCount >= _maxConcurrentTests) {
|
|
489
|
+
throw new Error(
|
|
490
|
+
`Max concurrent stress tests reached: ${activeCount}/${_maxConcurrentTests}`,
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const testId = crypto.randomUUID();
|
|
495
|
+
const now = Date.now();
|
|
496
|
+
|
|
497
|
+
const run = {
|
|
498
|
+
testId,
|
|
499
|
+
loadLevel: level.name,
|
|
500
|
+
concurrency,
|
|
501
|
+
requestsPerSecond,
|
|
502
|
+
duration,
|
|
503
|
+
status: RUN_STATUS_V2.RUNNING,
|
|
504
|
+
startedAt: now,
|
|
505
|
+
completedAt: null,
|
|
506
|
+
errorMessage: null,
|
|
507
|
+
_seq: ++_seq,
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
_runs.set(testId, run);
|
|
511
|
+
db.prepare(
|
|
512
|
+
`INSERT INTO stress_test_runs (test_id, load_level, concurrency, requests_per_second, duration, status, started_at, completed_at)
|
|
513
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
514
|
+
).run(
|
|
515
|
+
testId,
|
|
516
|
+
level.name,
|
|
517
|
+
concurrency,
|
|
518
|
+
requestsPerSecond,
|
|
519
|
+
duration,
|
|
520
|
+
run.status,
|
|
521
|
+
now,
|
|
522
|
+
null,
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
const { _seq: _omit, ...rest } = run;
|
|
526
|
+
void _omit;
|
|
527
|
+
return rest;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
export function completeStressTest(db, testId) {
|
|
531
|
+
const run = _runs.get(testId);
|
|
532
|
+
if (!run) throw new Error(`Stress test not found: ${testId}`);
|
|
533
|
+
|
|
534
|
+
const allowed = _allowedRunTransitions.get(run.status);
|
|
535
|
+
if (!allowed || !allowed.has(RUN_STATUS_V2.COMPLETE)) {
|
|
536
|
+
throw new Error(`Invalid run status transition: ${run.status} → complete`);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const seed = _hashSeed(testId);
|
|
540
|
+
const metrics = _synthesizeMetrics(
|
|
541
|
+
{
|
|
542
|
+
concurrency: run.concurrency,
|
|
543
|
+
requestsPerSecond: run.requestsPerSecond,
|
|
544
|
+
duration: run.duration,
|
|
545
|
+
},
|
|
546
|
+
seed,
|
|
547
|
+
);
|
|
548
|
+
const bottlenecks = _deriveBottlenecks(metrics, {
|
|
549
|
+
requestsPerSecond: run.requestsPerSecond,
|
|
550
|
+
});
|
|
551
|
+
const recommendations = _capacityRecommendations(
|
|
552
|
+
metrics,
|
|
553
|
+
{ requestsPerSecond: run.requestsPerSecond },
|
|
554
|
+
bottlenecks,
|
|
555
|
+
);
|
|
556
|
+
|
|
557
|
+
const now = Date.now();
|
|
558
|
+
const resultId = crypto.randomUUID();
|
|
559
|
+
const result = {
|
|
560
|
+
resultId,
|
|
561
|
+
testId,
|
|
562
|
+
...metrics,
|
|
563
|
+
bottlenecks,
|
|
564
|
+
capacityRecommendations: recommendations,
|
|
565
|
+
createdAt: now,
|
|
566
|
+
};
|
|
567
|
+
_results.set(testId, result);
|
|
568
|
+
|
|
569
|
+
db.prepare(
|
|
570
|
+
`INSERT INTO stress_test_results (result_id, test_id, tps, avg_response_time, p50_response_time, p95_response_time, p99_response_time, error_rate, bottlenecks, capacity_recommendations, created_at)
|
|
571
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
572
|
+
).run(
|
|
573
|
+
resultId,
|
|
574
|
+
testId,
|
|
575
|
+
metrics.tps,
|
|
576
|
+
metrics.avgResponseTime,
|
|
577
|
+
metrics.p50ResponseTime,
|
|
578
|
+
metrics.p95ResponseTime,
|
|
579
|
+
metrics.p99ResponseTime,
|
|
580
|
+
metrics.errorRate,
|
|
581
|
+
JSON.stringify(bottlenecks),
|
|
582
|
+
JSON.stringify(recommendations),
|
|
583
|
+
now,
|
|
584
|
+
);
|
|
585
|
+
|
|
586
|
+
run.status = RUN_STATUS_V2.COMPLETE;
|
|
587
|
+
run.completedAt = now;
|
|
588
|
+
db.prepare(
|
|
589
|
+
`UPDATE stress_test_runs SET status = ?, completed_at = ? WHERE test_id = ?`,
|
|
590
|
+
).run(RUN_STATUS_V2.COMPLETE, now, testId);
|
|
591
|
+
|
|
592
|
+
const { _seq: _omit, ...rest } = run;
|
|
593
|
+
void _omit;
|
|
594
|
+
return { ...rest, result };
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
export function stopStressTestV2(db, testId) {
|
|
598
|
+
return setRunStatus(db, testId, RUN_STATUS_V2.STOPPED);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
export function failStressTest(db, testId, errorMessage) {
|
|
602
|
+
return setRunStatus(db, testId, RUN_STATUS_V2.FAILED, { errorMessage });
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
export function setRunStatus(db, testId, newStatus, patch = {}) {
|
|
606
|
+
const run = _runs.get(testId);
|
|
607
|
+
if (!run) throw new Error(`Stress test not found: ${testId}`);
|
|
608
|
+
|
|
609
|
+
const validStatuses = Object.values(RUN_STATUS_V2);
|
|
610
|
+
if (!validStatuses.includes(newStatus)) {
|
|
611
|
+
throw new Error(`Unknown run status: ${newStatus}`);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const allowed = _allowedRunTransitions.get(run.status);
|
|
615
|
+
if (!allowed || !allowed.has(newStatus)) {
|
|
616
|
+
throw new Error(
|
|
617
|
+
`Invalid run status transition: ${run.status} → ${newStatus}`,
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
run.status = newStatus;
|
|
622
|
+
if (typeof patch.errorMessage === "string") {
|
|
623
|
+
run.errorMessage = patch.errorMessage;
|
|
624
|
+
}
|
|
625
|
+
if (_terminalRunStatuses.has(newStatus)) {
|
|
626
|
+
run.completedAt = Date.now();
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
db.prepare(
|
|
630
|
+
`UPDATE stress_test_runs SET status = ?, completed_at = ? WHERE test_id = ?`,
|
|
631
|
+
).run(newStatus, run.completedAt, testId);
|
|
632
|
+
|
|
633
|
+
const { _seq: _omit, ...rest } = run;
|
|
634
|
+
void _omit;
|
|
635
|
+
return rest;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* recommendLevelV2 — suggest the largest pre-defined level whose targetRps is
|
|
640
|
+
* still ≤ the caller's target. Returns `light` for any sub-light target,
|
|
641
|
+
* `extreme` for anything ≥ extreme.
|
|
642
|
+
*/
|
|
643
|
+
export function recommendLevelV2(targetRps) {
|
|
644
|
+
if (
|
|
645
|
+
typeof targetRps !== "number" ||
|
|
646
|
+
!Number.isFinite(targetRps) ||
|
|
647
|
+
targetRps <= 0
|
|
648
|
+
) {
|
|
649
|
+
throw new Error("targetRps must be a positive number");
|
|
650
|
+
}
|
|
651
|
+
const levels = Object.values(LOAD_LEVELS)
|
|
652
|
+
.slice()
|
|
653
|
+
.sort((a, b) => a.requestsPerSecond - b.requestsPerSecond);
|
|
654
|
+
let chosen = levels[0];
|
|
655
|
+
for (const level of levels) {
|
|
656
|
+
if (targetRps >= level.requestsPerSecond) chosen = level;
|
|
657
|
+
}
|
|
658
|
+
return { ...chosen };
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
export function getStressStatsV2() {
|
|
662
|
+
const runs = [..._runs.values()];
|
|
663
|
+
const results = [..._results.values()];
|
|
664
|
+
|
|
665
|
+
const byStatus = {};
|
|
666
|
+
for (const s of Object.values(RUN_STATUS_V2)) byStatus[s] = 0;
|
|
667
|
+
for (const r of runs) byStatus[r.status] = (byStatus[r.status] || 0) + 1;
|
|
668
|
+
|
|
669
|
+
const byLevel = {};
|
|
670
|
+
for (const l of Object.values(LEVEL_NAME_V2)) byLevel[l] = 0;
|
|
671
|
+
for (const r of runs) byLevel[r.loadLevel] = (byLevel[r.loadLevel] || 0) + 1;
|
|
672
|
+
|
|
673
|
+
const byKind = {};
|
|
674
|
+
for (const k of Object.values(BOTTLENECK_KIND_V2)) byKind[k] = 0;
|
|
675
|
+
const bySeverity = {};
|
|
676
|
+
for (const s of Object.values(BOTTLENECK_SEVERITY_V2)) bySeverity[s] = 0;
|
|
677
|
+
|
|
678
|
+
let totalTps = 0;
|
|
679
|
+
let totalP95 = 0;
|
|
680
|
+
let metricSamples = 0;
|
|
681
|
+
let totalBottlenecks = 0;
|
|
682
|
+
|
|
683
|
+
for (const result of results) {
|
|
684
|
+
totalTps += result.tps || 0;
|
|
685
|
+
totalP95 += result.p95ResponseTime || 0;
|
|
686
|
+
metricSamples++;
|
|
687
|
+
for (const b of result.bottlenecks || []) {
|
|
688
|
+
totalBottlenecks++;
|
|
689
|
+
if (byKind[b.kind] !== undefined) byKind[b.kind]++;
|
|
690
|
+
if (bySeverity[b.severity] !== undefined) bySeverity[b.severity]++;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
return {
|
|
695
|
+
totalTests: runs.length,
|
|
696
|
+
activeTests: getActiveTestCount(),
|
|
697
|
+
maxConcurrentTests: _maxConcurrentTests,
|
|
698
|
+
byStatus,
|
|
699
|
+
byLevel,
|
|
700
|
+
bottlenecks: {
|
|
701
|
+
total: totalBottlenecks,
|
|
702
|
+
byKind,
|
|
703
|
+
bySeverity,
|
|
704
|
+
},
|
|
705
|
+
aggregateMetrics: {
|
|
706
|
+
samples: metricSamples,
|
|
707
|
+
avgTps:
|
|
708
|
+
metricSamples > 0 ? Number((totalTps / metricSamples).toFixed(2)) : 0,
|
|
709
|
+
avgP95:
|
|
710
|
+
metricSamples > 0 ? Number((totalP95 / metricSamples).toFixed(2)) : 0,
|
|
711
|
+
},
|
|
712
|
+
};
|
|
383
713
|
}
|