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
|
@@ -154,3 +154,330 @@ export function getCacheStats(db) {
|
|
|
154
154
|
expired_entries: expired?.cnt || 0,
|
|
155
155
|
};
|
|
156
156
|
}
|
|
157
|
+
|
|
158
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
159
|
+
* V2 Surface — Response cache governance layer.
|
|
160
|
+
* Tracks per-owner cache-profile maturity + per-profile refresh-job
|
|
161
|
+
* lifecycle independent of SQLite llm_cache table.
|
|
162
|
+
* ═══════════════════════════════════════════════════════════════ */
|
|
163
|
+
|
|
164
|
+
export const PROFILE_MATURITY_V2 = Object.freeze({
|
|
165
|
+
PENDING: "pending",
|
|
166
|
+
ACTIVE: "active",
|
|
167
|
+
SUSPENDED: "suspended",
|
|
168
|
+
ARCHIVED: "archived",
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
export const REFRESH_JOB_LIFECYCLE_V2 = Object.freeze({
|
|
172
|
+
QUEUED: "queued",
|
|
173
|
+
RUNNING: "running",
|
|
174
|
+
COMPLETED: "completed",
|
|
175
|
+
FAILED: "failed",
|
|
176
|
+
CANCELLED: "cancelled",
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const PROFILE_TRANSITIONS_V2 = new Map([
|
|
180
|
+
["pending", new Set(["active", "archived"])],
|
|
181
|
+
["active", new Set(["suspended", "archived"])],
|
|
182
|
+
["suspended", new Set(["active", "archived"])],
|
|
183
|
+
["archived", new Set()],
|
|
184
|
+
]);
|
|
185
|
+
const PROFILE_TERMINALS_V2 = new Set(["archived"]);
|
|
186
|
+
|
|
187
|
+
const REFRESH_TRANSITIONS_V2 = new Map([
|
|
188
|
+
["queued", new Set(["running", "cancelled"])],
|
|
189
|
+
["running", new Set(["completed", "failed", "cancelled"])],
|
|
190
|
+
["completed", new Set()],
|
|
191
|
+
["failed", new Set()],
|
|
192
|
+
["cancelled", new Set()],
|
|
193
|
+
]);
|
|
194
|
+
const REFRESH_TERMINALS_V2 = new Set(["completed", "failed", "cancelled"]);
|
|
195
|
+
|
|
196
|
+
export const CACHE_DEFAULT_MAX_ACTIVE_PROFILES_PER_OWNER = 25;
|
|
197
|
+
export const CACHE_DEFAULT_MAX_PENDING_REFRESH_JOBS_PER_PROFILE = 4;
|
|
198
|
+
export const CACHE_DEFAULT_PROFILE_IDLE_MS = 1000 * 60 * 60 * 24 * 7; // 7 days
|
|
199
|
+
export const CACHE_DEFAULT_REFRESH_STUCK_MS = 1000 * 60 * 10; // 10 min
|
|
200
|
+
|
|
201
|
+
const _profilesV2 = new Map();
|
|
202
|
+
const _refreshJobsV2 = new Map();
|
|
203
|
+
let _maxActiveProfilesPerOwnerV2 = CACHE_DEFAULT_MAX_ACTIVE_PROFILES_PER_OWNER;
|
|
204
|
+
let _maxPendingRefreshJobsPerProfileV2 =
|
|
205
|
+
CACHE_DEFAULT_MAX_PENDING_REFRESH_JOBS_PER_PROFILE;
|
|
206
|
+
let _profileIdleMsV2 = CACHE_DEFAULT_PROFILE_IDLE_MS;
|
|
207
|
+
let _refreshStuckMsV2 = CACHE_DEFAULT_REFRESH_STUCK_MS;
|
|
208
|
+
|
|
209
|
+
function _posIntCacheV2(n, label) {
|
|
210
|
+
const v = Math.floor(Number(n));
|
|
211
|
+
if (!Number.isFinite(v) || v <= 0)
|
|
212
|
+
throw new Error(`${label} must be a positive integer`);
|
|
213
|
+
return v;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function getMaxActiveProfilesPerOwnerV2() {
|
|
217
|
+
return _maxActiveProfilesPerOwnerV2;
|
|
218
|
+
}
|
|
219
|
+
export function setMaxActiveProfilesPerOwnerV2(n) {
|
|
220
|
+
_maxActiveProfilesPerOwnerV2 = _posIntCacheV2(n, "maxActiveProfilesPerOwner");
|
|
221
|
+
}
|
|
222
|
+
export function getMaxPendingRefreshJobsPerProfileV2() {
|
|
223
|
+
return _maxPendingRefreshJobsPerProfileV2;
|
|
224
|
+
}
|
|
225
|
+
export function setMaxPendingRefreshJobsPerProfileV2(n) {
|
|
226
|
+
_maxPendingRefreshJobsPerProfileV2 = _posIntCacheV2(
|
|
227
|
+
n,
|
|
228
|
+
"maxPendingRefreshJobsPerProfile",
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
export function getProfileIdleMsV2() {
|
|
232
|
+
return _profileIdleMsV2;
|
|
233
|
+
}
|
|
234
|
+
export function setProfileIdleMsV2(n) {
|
|
235
|
+
_profileIdleMsV2 = _posIntCacheV2(n, "profileIdleMs");
|
|
236
|
+
}
|
|
237
|
+
export function getRefreshStuckMsV2() {
|
|
238
|
+
return _refreshStuckMsV2;
|
|
239
|
+
}
|
|
240
|
+
export function setRefreshStuckMsV2(n) {
|
|
241
|
+
_refreshStuckMsV2 = _posIntCacheV2(n, "refreshStuckMs");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function getActiveProfileCountV2(ownerId) {
|
|
245
|
+
let n = 0;
|
|
246
|
+
for (const p of _profilesV2.values()) {
|
|
247
|
+
if (p.ownerId === ownerId && p.status === "active") n += 1;
|
|
248
|
+
}
|
|
249
|
+
return n;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function getPendingRefreshJobCountV2(profileId) {
|
|
253
|
+
let n = 0;
|
|
254
|
+
for (const j of _refreshJobsV2.values()) {
|
|
255
|
+
if (
|
|
256
|
+
j.profileId === profileId &&
|
|
257
|
+
(j.status === "queued" || j.status === "running")
|
|
258
|
+
)
|
|
259
|
+
n += 1;
|
|
260
|
+
}
|
|
261
|
+
return n;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function _copyProfileV2(p) {
|
|
265
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
266
|
+
}
|
|
267
|
+
function _copyRefreshJobV2(j) {
|
|
268
|
+
return { ...j, metadata: { ...j.metadata } };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function registerProfileV2(
|
|
272
|
+
id,
|
|
273
|
+
{ ownerId, label, metadata = {}, now = Date.now() } = {},
|
|
274
|
+
) {
|
|
275
|
+
if (!id || typeof id !== "string") throw new Error("id must be a string");
|
|
276
|
+
if (!ownerId || typeof ownerId !== "string")
|
|
277
|
+
throw new Error("ownerId must be a string");
|
|
278
|
+
if (!label || typeof label !== "string")
|
|
279
|
+
throw new Error("label must be a string");
|
|
280
|
+
if (_profilesV2.has(id)) throw new Error(`profile ${id} already exists`);
|
|
281
|
+
const p = {
|
|
282
|
+
id,
|
|
283
|
+
ownerId,
|
|
284
|
+
label,
|
|
285
|
+
status: "pending",
|
|
286
|
+
createdAt: now,
|
|
287
|
+
lastSeenAt: now,
|
|
288
|
+
activatedAt: null,
|
|
289
|
+
archivedAt: null,
|
|
290
|
+
metadata: { ...metadata },
|
|
291
|
+
};
|
|
292
|
+
_profilesV2.set(id, p);
|
|
293
|
+
return _copyProfileV2(p);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function getProfileV2(id) {
|
|
297
|
+
const p = _profilesV2.get(id);
|
|
298
|
+
return p ? _copyProfileV2(p) : null;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function listProfilesV2({ ownerId, status } = {}) {
|
|
302
|
+
const out = [];
|
|
303
|
+
for (const p of _profilesV2.values()) {
|
|
304
|
+
if (ownerId && p.ownerId !== ownerId) continue;
|
|
305
|
+
if (status && p.status !== status) continue;
|
|
306
|
+
out.push(_copyProfileV2(p));
|
|
307
|
+
}
|
|
308
|
+
return out;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export function setProfileStatusV2(id, next, { now = Date.now() } = {}) {
|
|
312
|
+
const p = _profilesV2.get(id);
|
|
313
|
+
if (!p) throw new Error(`profile ${id} not found`);
|
|
314
|
+
if (!PROFILE_TRANSITIONS_V2.has(next))
|
|
315
|
+
throw new Error(`unknown profile status: ${next}`);
|
|
316
|
+
if (PROFILE_TERMINALS_V2.has(p.status))
|
|
317
|
+
throw new Error(`profile ${id} is in terminal state ${p.status}`);
|
|
318
|
+
const allowed = PROFILE_TRANSITIONS_V2.get(p.status);
|
|
319
|
+
if (!allowed.has(next))
|
|
320
|
+
throw new Error(`cannot transition profile from ${p.status} to ${next}`);
|
|
321
|
+
if (next === "active") {
|
|
322
|
+
if (p.status === "pending") {
|
|
323
|
+
const count = getActiveProfileCountV2(p.ownerId);
|
|
324
|
+
if (count >= _maxActiveProfilesPerOwnerV2)
|
|
325
|
+
throw new Error(
|
|
326
|
+
`owner ${p.ownerId} already at active-profile cap (${_maxActiveProfilesPerOwnerV2})`,
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
if (!p.activatedAt) p.activatedAt = now;
|
|
330
|
+
}
|
|
331
|
+
if (next === "archived" && !p.archivedAt) p.archivedAt = now;
|
|
332
|
+
p.status = next;
|
|
333
|
+
p.lastSeenAt = now;
|
|
334
|
+
return _copyProfileV2(p);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export function activateProfileV2(id, opts) {
|
|
338
|
+
return setProfileStatusV2(id, "active", opts);
|
|
339
|
+
}
|
|
340
|
+
export function suspendProfileV2(id, opts) {
|
|
341
|
+
return setProfileStatusV2(id, "suspended", opts);
|
|
342
|
+
}
|
|
343
|
+
export function archiveProfileV2(id, opts) {
|
|
344
|
+
return setProfileStatusV2(id, "archived", opts);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export function touchProfileV2(id, { now = Date.now() } = {}) {
|
|
348
|
+
const p = _profilesV2.get(id);
|
|
349
|
+
if (!p) throw new Error(`profile ${id} not found`);
|
|
350
|
+
p.lastSeenAt = now;
|
|
351
|
+
return _copyProfileV2(p);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function createRefreshJobV2(
|
|
355
|
+
id,
|
|
356
|
+
{ profileId, kind = "warm", metadata = {}, now = Date.now() } = {},
|
|
357
|
+
) {
|
|
358
|
+
if (!id || typeof id !== "string") throw new Error("id must be a string");
|
|
359
|
+
if (!profileId || typeof profileId !== "string")
|
|
360
|
+
throw new Error("profileId must be a string");
|
|
361
|
+
if (_refreshJobsV2.has(id)) throw new Error(`job ${id} already exists`);
|
|
362
|
+
const count = getPendingRefreshJobCountV2(profileId);
|
|
363
|
+
if (count >= _maxPendingRefreshJobsPerProfileV2)
|
|
364
|
+
throw new Error(
|
|
365
|
+
`profile ${profileId} already at pending-refresh-job cap (${_maxPendingRefreshJobsPerProfileV2})`,
|
|
366
|
+
);
|
|
367
|
+
const j = {
|
|
368
|
+
id,
|
|
369
|
+
profileId,
|
|
370
|
+
kind,
|
|
371
|
+
status: "queued",
|
|
372
|
+
createdAt: now,
|
|
373
|
+
lastSeenAt: now,
|
|
374
|
+
startedAt: null,
|
|
375
|
+
settledAt: null,
|
|
376
|
+
metadata: { ...metadata },
|
|
377
|
+
};
|
|
378
|
+
_refreshJobsV2.set(id, j);
|
|
379
|
+
return _copyRefreshJobV2(j);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export function getRefreshJobV2(id) {
|
|
383
|
+
const j = _refreshJobsV2.get(id);
|
|
384
|
+
return j ? _copyRefreshJobV2(j) : null;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export function listRefreshJobsV2({ profileId, status } = {}) {
|
|
388
|
+
const out = [];
|
|
389
|
+
for (const j of _refreshJobsV2.values()) {
|
|
390
|
+
if (profileId && j.profileId !== profileId) continue;
|
|
391
|
+
if (status && j.status !== status) continue;
|
|
392
|
+
out.push(_copyRefreshJobV2(j));
|
|
393
|
+
}
|
|
394
|
+
return out;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export function setRefreshJobStatusV2(id, next, { now = Date.now() } = {}) {
|
|
398
|
+
const j = _refreshJobsV2.get(id);
|
|
399
|
+
if (!j) throw new Error(`job ${id} not found`);
|
|
400
|
+
if (!REFRESH_TRANSITIONS_V2.has(next))
|
|
401
|
+
throw new Error(`unknown job status: ${next}`);
|
|
402
|
+
if (REFRESH_TERMINALS_V2.has(j.status))
|
|
403
|
+
throw new Error(`job ${id} is in terminal state ${j.status}`);
|
|
404
|
+
const allowed = REFRESH_TRANSITIONS_V2.get(j.status);
|
|
405
|
+
if (!allowed.has(next))
|
|
406
|
+
throw new Error(`cannot transition job from ${j.status} to ${next}`);
|
|
407
|
+
if (next === "running" && !j.startedAt) j.startedAt = now;
|
|
408
|
+
if (REFRESH_TERMINALS_V2.has(next) && !j.settledAt) j.settledAt = now;
|
|
409
|
+
j.status = next;
|
|
410
|
+
j.lastSeenAt = now;
|
|
411
|
+
return _copyRefreshJobV2(j);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export function startRefreshJobV2(id, opts) {
|
|
415
|
+
return setRefreshJobStatusV2(id, "running", opts);
|
|
416
|
+
}
|
|
417
|
+
export function completeRefreshJobV2(id, opts) {
|
|
418
|
+
return setRefreshJobStatusV2(id, "completed", opts);
|
|
419
|
+
}
|
|
420
|
+
export function failRefreshJobV2(id, opts) {
|
|
421
|
+
return setRefreshJobStatusV2(id, "failed", opts);
|
|
422
|
+
}
|
|
423
|
+
export function cancelRefreshJobV2(id, opts) {
|
|
424
|
+
return setRefreshJobStatusV2(id, "cancelled", opts);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export function autoSuspendIdleProfilesV2({ now = Date.now() } = {}) {
|
|
428
|
+
const flipped = [];
|
|
429
|
+
for (const p of _profilesV2.values()) {
|
|
430
|
+
if (p.status !== "active") continue;
|
|
431
|
+
if (now - p.lastSeenAt > _profileIdleMsV2) {
|
|
432
|
+
p.status = "suspended";
|
|
433
|
+
p.lastSeenAt = now;
|
|
434
|
+
flipped.push(_copyProfileV2(p));
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return flipped;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export function autoFailStuckRefreshJobsV2({ now = Date.now() } = {}) {
|
|
441
|
+
const flipped = [];
|
|
442
|
+
for (const j of _refreshJobsV2.values()) {
|
|
443
|
+
if (j.status !== "running") continue;
|
|
444
|
+
if (now - j.lastSeenAt > _refreshStuckMsV2) {
|
|
445
|
+
j.status = "failed";
|
|
446
|
+
j.lastSeenAt = now;
|
|
447
|
+
if (!j.settledAt) j.settledAt = now;
|
|
448
|
+
flipped.push(_copyRefreshJobV2(j));
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return flipped;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export function getResponseCacheStatsV2() {
|
|
455
|
+
const profilesByStatus = {};
|
|
456
|
+
for (const v of Object.values(PROFILE_MATURITY_V2)) profilesByStatus[v] = 0;
|
|
457
|
+
for (const p of _profilesV2.values()) profilesByStatus[p.status] += 1;
|
|
458
|
+
|
|
459
|
+
const jobsByStatus = {};
|
|
460
|
+
for (const v of Object.values(REFRESH_JOB_LIFECYCLE_V2)) jobsByStatus[v] = 0;
|
|
461
|
+
for (const j of _refreshJobsV2.values()) jobsByStatus[j.status] += 1;
|
|
462
|
+
|
|
463
|
+
return {
|
|
464
|
+
totalProfilesV2: _profilesV2.size,
|
|
465
|
+
totalRefreshJobsV2: _refreshJobsV2.size,
|
|
466
|
+
maxActiveProfilesPerOwner: _maxActiveProfilesPerOwnerV2,
|
|
467
|
+
maxPendingRefreshJobsPerProfile: _maxPendingRefreshJobsPerProfileV2,
|
|
468
|
+
profileIdleMs: _profileIdleMsV2,
|
|
469
|
+
refreshStuckMs: _refreshStuckMsV2,
|
|
470
|
+
profilesByStatus,
|
|
471
|
+
jobsByStatus,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
export function _resetStateResponseCacheV2() {
|
|
476
|
+
_profilesV2.clear();
|
|
477
|
+
_refreshJobsV2.clear();
|
|
478
|
+
_maxActiveProfilesPerOwnerV2 = CACHE_DEFAULT_MAX_ACTIVE_PROFILES_PER_OWNER;
|
|
479
|
+
_maxPendingRefreshJobsPerProfileV2 =
|
|
480
|
+
CACHE_DEFAULT_MAX_PENDING_REFRESH_JOBS_PER_PROFILE;
|
|
481
|
+
_profileIdleMsV2 = CACHE_DEFAULT_PROFILE_IDLE_MS;
|
|
482
|
+
_refreshStuckMsV2 = CACHE_DEFAULT_REFRESH_STUCK_MS;
|
|
483
|
+
}
|
package/src/lib/scim-manager.js
CHANGED
|
@@ -210,3 +210,332 @@ export function _resetState() {
|
|
|
210
210
|
_connectors.clear();
|
|
211
211
|
_syncLog.length = 0;
|
|
212
212
|
}
|
|
213
|
+
|
|
214
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
215
|
+
* V2 Surface — SCIM provisioning lifecycle layer.
|
|
216
|
+
* Tracks identities and sync-job lifecycle independent of legacy
|
|
217
|
+
* createUser/syncProvision flows above.
|
|
218
|
+
* ═══════════════════════════════════════════════════════════════ */
|
|
219
|
+
|
|
220
|
+
export const IDENTITY_LIFECYCLE_V2 = Object.freeze({
|
|
221
|
+
PENDING: "pending",
|
|
222
|
+
PROVISIONED: "provisioned",
|
|
223
|
+
SUSPENDED: "suspended",
|
|
224
|
+
DEPROVISIONED: "deprovisioned",
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
export const SYNC_JOB_V2 = Object.freeze({
|
|
228
|
+
QUEUED: "queued",
|
|
229
|
+
RUNNING: "running",
|
|
230
|
+
SUCCEEDED: "succeeded",
|
|
231
|
+
FAILED: "failed",
|
|
232
|
+
CANCELLED: "cancelled",
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const IDENTITY_TRANSITIONS_V2 = new Map([
|
|
236
|
+
["pending", new Set(["provisioned", "deprovisioned"])],
|
|
237
|
+
["provisioned", new Set(["suspended", "deprovisioned"])],
|
|
238
|
+
["suspended", new Set(["provisioned", "deprovisioned"])],
|
|
239
|
+
["deprovisioned", new Set()],
|
|
240
|
+
]);
|
|
241
|
+
const IDENTITY_TERMINALS_V2 = new Set(["deprovisioned"]);
|
|
242
|
+
|
|
243
|
+
const SYNC_TRANSITIONS_V2 = new Map([
|
|
244
|
+
["queued", new Set(["running", "cancelled"])],
|
|
245
|
+
["running", new Set(["succeeded", "failed", "cancelled"])],
|
|
246
|
+
["succeeded", new Set()],
|
|
247
|
+
["failed", new Set()],
|
|
248
|
+
["cancelled", new Set()],
|
|
249
|
+
]);
|
|
250
|
+
const SYNC_TERMINALS_V2 = new Set(["succeeded", "failed", "cancelled"]);
|
|
251
|
+
|
|
252
|
+
export const SCIM_DEFAULT_MAX_PROVISIONED_PER_CONNECTOR = 1000;
|
|
253
|
+
export const SCIM_DEFAULT_MAX_RUNNING_SYNC_PER_CONNECTOR = 2;
|
|
254
|
+
export const SCIM_DEFAULT_IDENTITY_IDLE_MS = 1000 * 60 * 60 * 24 * 90; // 90 days
|
|
255
|
+
export const SCIM_DEFAULT_SYNC_STUCK_MS = 1000 * 60 * 30; // 30 min
|
|
256
|
+
|
|
257
|
+
const _identitiesV2 = new Map();
|
|
258
|
+
const _syncJobsV2 = new Map();
|
|
259
|
+
let _maxProvisionedPerConnectorV2 = SCIM_DEFAULT_MAX_PROVISIONED_PER_CONNECTOR;
|
|
260
|
+
let _maxRunningSyncPerConnectorV2 = SCIM_DEFAULT_MAX_RUNNING_SYNC_PER_CONNECTOR;
|
|
261
|
+
let _identityIdleMsV2 = SCIM_DEFAULT_IDENTITY_IDLE_MS;
|
|
262
|
+
let _syncStuckMsV2 = SCIM_DEFAULT_SYNC_STUCK_MS;
|
|
263
|
+
|
|
264
|
+
function _posIntScimV2(n, label) {
|
|
265
|
+
const v = Math.floor(Number(n));
|
|
266
|
+
if (!Number.isFinite(v) || v <= 0)
|
|
267
|
+
throw new Error(`${label} must be a positive integer`);
|
|
268
|
+
return v;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function getMaxProvisionedPerConnectorV2() {
|
|
272
|
+
return _maxProvisionedPerConnectorV2;
|
|
273
|
+
}
|
|
274
|
+
export function setMaxProvisionedPerConnectorV2(n) {
|
|
275
|
+
_maxProvisionedPerConnectorV2 = _posIntScimV2(
|
|
276
|
+
n,
|
|
277
|
+
"maxProvisionedPerConnector",
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
export function getMaxRunningSyncPerConnectorV2() {
|
|
281
|
+
return _maxRunningSyncPerConnectorV2;
|
|
282
|
+
}
|
|
283
|
+
export function setMaxRunningSyncPerConnectorV2(n) {
|
|
284
|
+
_maxRunningSyncPerConnectorV2 = _posIntScimV2(
|
|
285
|
+
n,
|
|
286
|
+
"maxRunningSyncPerConnector",
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
export function getIdentityIdleMsV2() {
|
|
290
|
+
return _identityIdleMsV2;
|
|
291
|
+
}
|
|
292
|
+
export function setIdentityIdleMsV2(n) {
|
|
293
|
+
_identityIdleMsV2 = _posIntScimV2(n, "identityIdleMs");
|
|
294
|
+
}
|
|
295
|
+
export function getSyncStuckMsV2() {
|
|
296
|
+
return _syncStuckMsV2;
|
|
297
|
+
}
|
|
298
|
+
export function setSyncStuckMsV2(n) {
|
|
299
|
+
_syncStuckMsV2 = _posIntScimV2(n, "syncStuckMs");
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function getProvisionedCountV2(connectorId) {
|
|
303
|
+
let n = 0;
|
|
304
|
+
for (const i of _identitiesV2.values()) {
|
|
305
|
+
if (i.connectorId === connectorId && i.status === "provisioned") n += 1;
|
|
306
|
+
}
|
|
307
|
+
return n;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export function getRunningSyncCountV2(connectorId) {
|
|
311
|
+
let n = 0;
|
|
312
|
+
for (const j of _syncJobsV2.values()) {
|
|
313
|
+
if (j.connectorId === connectorId && j.status === "running") n += 1;
|
|
314
|
+
}
|
|
315
|
+
return n;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function _copyIdentityV2(i) {
|
|
319
|
+
return { ...i, metadata: { ...i.metadata } };
|
|
320
|
+
}
|
|
321
|
+
function _copySyncV2(j) {
|
|
322
|
+
return { ...j, metadata: { ...j.metadata } };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export function registerIdentityV2(
|
|
326
|
+
id,
|
|
327
|
+
{ connectorId, externalId, metadata = {}, now = Date.now() } = {},
|
|
328
|
+
) {
|
|
329
|
+
if (!id || typeof id !== "string") throw new Error("id must be a string");
|
|
330
|
+
if (!connectorId || typeof connectorId !== "string")
|
|
331
|
+
throw new Error("connectorId must be a string");
|
|
332
|
+
if (!externalId || typeof externalId !== "string")
|
|
333
|
+
throw new Error("externalId must be a string");
|
|
334
|
+
if (_identitiesV2.has(id)) throw new Error(`identity ${id} already exists`);
|
|
335
|
+
const i = {
|
|
336
|
+
id,
|
|
337
|
+
connectorId,
|
|
338
|
+
externalId,
|
|
339
|
+
status: "pending",
|
|
340
|
+
createdAt: now,
|
|
341
|
+
lastSeenAt: now,
|
|
342
|
+
provisionedAt: null,
|
|
343
|
+
deprovisionedAt: null,
|
|
344
|
+
metadata: { ...metadata },
|
|
345
|
+
};
|
|
346
|
+
_identitiesV2.set(id, i);
|
|
347
|
+
return _copyIdentityV2(i);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function getIdentityV2(id) {
|
|
351
|
+
const i = _identitiesV2.get(id);
|
|
352
|
+
return i ? _copyIdentityV2(i) : null;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export function listIdentitiesV2({ connectorId, status } = {}) {
|
|
356
|
+
const out = [];
|
|
357
|
+
for (const i of _identitiesV2.values()) {
|
|
358
|
+
if (connectorId && i.connectorId !== connectorId) continue;
|
|
359
|
+
if (status && i.status !== status) continue;
|
|
360
|
+
out.push(_copyIdentityV2(i));
|
|
361
|
+
}
|
|
362
|
+
return out;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export function setIdentityStatusV2(id, next, { now = Date.now() } = {}) {
|
|
366
|
+
const i = _identitiesV2.get(id);
|
|
367
|
+
if (!i) throw new Error(`identity ${id} not found`);
|
|
368
|
+
if (!IDENTITY_TRANSITIONS_V2.has(next))
|
|
369
|
+
throw new Error(`unknown identity status: ${next}`);
|
|
370
|
+
if (IDENTITY_TERMINALS_V2.has(i.status))
|
|
371
|
+
throw new Error(`identity ${id} is in terminal state ${i.status}`);
|
|
372
|
+
const allowed = IDENTITY_TRANSITIONS_V2.get(i.status);
|
|
373
|
+
if (!allowed.has(next))
|
|
374
|
+
throw new Error(`cannot transition identity from ${i.status} to ${next}`);
|
|
375
|
+
if (next === "provisioned") {
|
|
376
|
+
if (i.status === "pending") {
|
|
377
|
+
const count = getProvisionedCountV2(i.connectorId);
|
|
378
|
+
if (count >= _maxProvisionedPerConnectorV2)
|
|
379
|
+
throw new Error(
|
|
380
|
+
`connector ${i.connectorId} already at provisioned cap (${_maxProvisionedPerConnectorV2})`,
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
if (!i.provisionedAt) i.provisionedAt = now;
|
|
384
|
+
}
|
|
385
|
+
if (next === "deprovisioned" && !i.deprovisionedAt) i.deprovisionedAt = now;
|
|
386
|
+
i.status = next;
|
|
387
|
+
i.lastSeenAt = now;
|
|
388
|
+
return _copyIdentityV2(i);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export function provisionIdentityV2(id, opts) {
|
|
392
|
+
return setIdentityStatusV2(id, "provisioned", opts);
|
|
393
|
+
}
|
|
394
|
+
export function suspendIdentityV2(id, opts) {
|
|
395
|
+
return setIdentityStatusV2(id, "suspended", opts);
|
|
396
|
+
}
|
|
397
|
+
export function deprovisionIdentityV2(id, opts) {
|
|
398
|
+
return setIdentityStatusV2(id, "deprovisioned", opts);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export function touchIdentityV2(id, { now = Date.now() } = {}) {
|
|
402
|
+
const i = _identitiesV2.get(id);
|
|
403
|
+
if (!i) throw new Error(`identity ${id} not found`);
|
|
404
|
+
i.lastSeenAt = now;
|
|
405
|
+
return _copyIdentityV2(i);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export function createSyncJobV2(
|
|
409
|
+
id,
|
|
410
|
+
{ connectorId, kind = "full", metadata = {}, now = Date.now() } = {},
|
|
411
|
+
) {
|
|
412
|
+
if (!id || typeof id !== "string") throw new Error("id must be a string");
|
|
413
|
+
if (!connectorId || typeof connectorId !== "string")
|
|
414
|
+
throw new Error("connectorId must be a string");
|
|
415
|
+
if (_syncJobsV2.has(id)) throw new Error(`syncJob ${id} already exists`);
|
|
416
|
+
const j = {
|
|
417
|
+
id,
|
|
418
|
+
connectorId,
|
|
419
|
+
kind,
|
|
420
|
+
status: "queued",
|
|
421
|
+
createdAt: now,
|
|
422
|
+
lastSeenAt: now,
|
|
423
|
+
startedAt: null,
|
|
424
|
+
finishedAt: null,
|
|
425
|
+
metadata: { ...metadata },
|
|
426
|
+
};
|
|
427
|
+
_syncJobsV2.set(id, j);
|
|
428
|
+
return _copySyncV2(j);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export function getSyncJobV2(id) {
|
|
432
|
+
const j = _syncJobsV2.get(id);
|
|
433
|
+
return j ? _copySyncV2(j) : null;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export function listSyncJobsV2({ connectorId, status } = {}) {
|
|
437
|
+
const out = [];
|
|
438
|
+
for (const j of _syncJobsV2.values()) {
|
|
439
|
+
if (connectorId && j.connectorId !== connectorId) continue;
|
|
440
|
+
if (status && j.status !== status) continue;
|
|
441
|
+
out.push(_copySyncV2(j));
|
|
442
|
+
}
|
|
443
|
+
return out;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
export function setSyncJobStatusV2(id, next, { now = Date.now() } = {}) {
|
|
447
|
+
const j = _syncJobsV2.get(id);
|
|
448
|
+
if (!j) throw new Error(`syncJob ${id} not found`);
|
|
449
|
+
if (!SYNC_TRANSITIONS_V2.has(next))
|
|
450
|
+
throw new Error(`unknown syncJob status: ${next}`);
|
|
451
|
+
if (SYNC_TERMINALS_V2.has(j.status))
|
|
452
|
+
throw new Error(`syncJob ${id} is in terminal state ${j.status}`);
|
|
453
|
+
const allowed = SYNC_TRANSITIONS_V2.get(j.status);
|
|
454
|
+
if (!allowed.has(next))
|
|
455
|
+
throw new Error(`cannot transition syncJob from ${j.status} to ${next}`);
|
|
456
|
+
if (next === "running" && j.status === "queued") {
|
|
457
|
+
const count = getRunningSyncCountV2(j.connectorId);
|
|
458
|
+
if (count >= _maxRunningSyncPerConnectorV2)
|
|
459
|
+
throw new Error(
|
|
460
|
+
`connector ${j.connectorId} already at running-sync cap (${_maxRunningSyncPerConnectorV2})`,
|
|
461
|
+
);
|
|
462
|
+
if (!j.startedAt) j.startedAt = now;
|
|
463
|
+
}
|
|
464
|
+
if (SYNC_TERMINALS_V2.has(next) && !j.finishedAt) j.finishedAt = now;
|
|
465
|
+
j.status = next;
|
|
466
|
+
j.lastSeenAt = now;
|
|
467
|
+
return _copySyncV2(j);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export function startSyncJobV2(id, opts) {
|
|
471
|
+
return setSyncJobStatusV2(id, "running", opts);
|
|
472
|
+
}
|
|
473
|
+
export function succeedSyncJobV2(id, opts) {
|
|
474
|
+
return setSyncJobStatusV2(id, "succeeded", opts);
|
|
475
|
+
}
|
|
476
|
+
export function failSyncJobV2(id, opts) {
|
|
477
|
+
return setSyncJobStatusV2(id, "failed", opts);
|
|
478
|
+
}
|
|
479
|
+
export function cancelSyncJobV2(id, opts) {
|
|
480
|
+
return setSyncJobStatusV2(id, "cancelled", opts);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
export function autoDeprovisionIdleIdentitiesV2({ now = Date.now() } = {}) {
|
|
484
|
+
const flipped = [];
|
|
485
|
+
for (const i of _identitiesV2.values()) {
|
|
486
|
+
if (i.status === "deprovisioned" || i.status === "pending") continue;
|
|
487
|
+
if (now - i.lastSeenAt > _identityIdleMsV2) {
|
|
488
|
+
i.status = "deprovisioned";
|
|
489
|
+
i.lastSeenAt = now;
|
|
490
|
+
if (!i.deprovisionedAt) i.deprovisionedAt = now;
|
|
491
|
+
flipped.push(_copyIdentityV2(i));
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return flipped;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
export function autoFailStuckSyncJobsV2({ now = Date.now() } = {}) {
|
|
498
|
+
const flipped = [];
|
|
499
|
+
for (const j of _syncJobsV2.values()) {
|
|
500
|
+
if (j.status !== "running") continue;
|
|
501
|
+
const ref = j.startedAt ?? j.lastSeenAt;
|
|
502
|
+
if (now - ref > _syncStuckMsV2) {
|
|
503
|
+
j.status = "failed";
|
|
504
|
+
j.lastSeenAt = now;
|
|
505
|
+
if (!j.finishedAt) j.finishedAt = now;
|
|
506
|
+
flipped.push(_copySyncV2(j));
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return flipped;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
export function getScimManagerStatsV2() {
|
|
513
|
+
const identitiesByStatus = {};
|
|
514
|
+
for (const v of Object.values(IDENTITY_LIFECYCLE_V2))
|
|
515
|
+
identitiesByStatus[v] = 0;
|
|
516
|
+
for (const i of _identitiesV2.values()) identitiesByStatus[i.status] += 1;
|
|
517
|
+
|
|
518
|
+
const syncJobsByStatus = {};
|
|
519
|
+
for (const v of Object.values(SYNC_JOB_V2)) syncJobsByStatus[v] = 0;
|
|
520
|
+
for (const j of _syncJobsV2.values()) syncJobsByStatus[j.status] += 1;
|
|
521
|
+
|
|
522
|
+
return {
|
|
523
|
+
totalIdentitiesV2: _identitiesV2.size,
|
|
524
|
+
totalSyncJobsV2: _syncJobsV2.size,
|
|
525
|
+
maxProvisionedPerConnector: _maxProvisionedPerConnectorV2,
|
|
526
|
+
maxRunningSyncPerConnector: _maxRunningSyncPerConnectorV2,
|
|
527
|
+
identityIdleMs: _identityIdleMsV2,
|
|
528
|
+
syncStuckMs: _syncStuckMsV2,
|
|
529
|
+
identitiesByStatus,
|
|
530
|
+
syncJobsByStatus,
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
export function _resetStateScimManagerV2() {
|
|
535
|
+
_identitiesV2.clear();
|
|
536
|
+
_syncJobsV2.clear();
|
|
537
|
+
_maxProvisionedPerConnectorV2 = SCIM_DEFAULT_MAX_PROVISIONED_PER_CONNECTOR;
|
|
538
|
+
_maxRunningSyncPerConnectorV2 = SCIM_DEFAULT_MAX_RUNNING_SYNC_PER_CONNECTOR;
|
|
539
|
+
_identityIdleMsV2 = SCIM_DEFAULT_IDENTITY_IDLE_MS;
|
|
540
|
+
_syncStuckMsV2 = SCIM_DEFAULT_SYNC_STUCK_MS;
|
|
541
|
+
}
|