chainlesschain 0.132.0 → 0.145.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/commands/a2a.js +230 -0
- package/src/commands/activitypub.js +191 -0
- package/src/commands/agent.js +601 -0
- package/src/commands/audit.js +206 -0
- package/src/commands/bi.js +186 -0
- package/src/commands/bm25.js +162 -0
- package/src/commands/browse.js +225 -0
- package/src/commands/ccron.js +178 -0
- package/src/commands/chat.js +207 -0
- package/src/commands/compliance.js +420 -0
- package/src/commands/compt.js +176 -0
- package/src/commands/consol.js +237 -0
- package/src/commands/cowork.js +588 -0
- package/src/commands/crosschain.js +216 -0
- package/src/commands/dao.js +216 -0
- package/src/commands/dlp.js +206 -0
- package/src/commands/economy.js +211 -0
- package/src/commands/evolution.js +209 -0
- package/src/commands/evomap.js +216 -0
- package/src/commands/fflag.js +230 -0
- package/src/commands/git.js +185 -0
- package/src/commands/hardening.js +209 -0
- package/src/commands/hmemory.js +210 -0
- package/src/commands/incentive.js +209 -0
- package/src/commands/inference.js +178 -0
- package/src/commands/itbudget.js +161 -0
- package/src/commands/kg.js +206 -0
- package/src/commands/lowcode.js +201 -0
- package/src/commands/marketplace.js +206 -0
- package/src/commands/matrix.js +214 -0
- package/src/commands/mcpscaf.js +153 -0
- package/src/commands/meminj.js +153 -0
- package/src/commands/nostr.js +213 -0
- package/src/commands/orchestrate.js +217 -0
- package/src/commands/orchgov.js +156 -0
- package/src/commands/pdfp.js +160 -0
- package/src/commands/perf.js +176 -0
- package/src/commands/perm.js +156 -0
- package/src/commands/pipeline.js +211 -0
- package/src/commands/planmode.js +154 -0
- package/src/commands/privacy.js +203 -0
- package/src/commands/promcomp.js +166 -0
- package/src/commands/recommend.js +185 -0
- package/src/commands/reputation.js +208 -0
- package/src/commands/sandbox.js +206 -0
- package/src/commands/seshhook.js +153 -0
- package/src/commands/seshsearch.js +149 -0
- package/src/commands/seshtail.js +152 -0
- package/src/commands/seshu.js +160 -0
- package/src/commands/sganal.js +172 -0
- package/src/commands/siem.js +207 -0
- package/src/commands/sla.js +212 -0
- package/src/commands/slotfill.js +154 -0
- package/src/commands/social.js +159 -0
- package/src/commands/stress.js +206 -0
- package/src/commands/svccont.js +157 -0
- package/src/commands/terraform.js +206 -0
- package/src/commands/tms.js +183 -0
- package/src/commands/topiccls.js +158 -0
- package/src/commands/uprof.js +154 -0
- package/src/commands/vcheck.js +172 -0
- package/src/commands/webfetch.js +150 -0
- package/src/commands/zkp.js +218 -0
- package/src/harness/prompt-compressor.js +331 -0
- package/src/index.js +101 -1
- package/src/lib/a2a-protocol.js +373 -0
- package/src/lib/activitypub-bridge.js +343 -0
- package/src/lib/agent-economy.js +358 -0
- package/src/lib/app-builder.js +338 -0
- package/src/lib/audit-logger.js +321 -0
- package/src/lib/autonomous-agent.js +341 -0
- package/src/lib/bi-engine.js +339 -0
- package/src/lib/bm25-search.js +333 -0
- package/src/lib/browser-automation.js +352 -0
- package/src/lib/chat-core.js +336 -0
- package/src/lib/claude-code-bridge.js +341 -0
- package/src/lib/compliance-framework-reporter.js +359 -0
- package/src/lib/compliance-manager.js +330 -0
- package/src/lib/compression-telemetry.js +333 -0
- package/src/lib/content-recommender.js +370 -0
- package/src/lib/cowork-cron.js +330 -0
- package/src/lib/cowork-learning.js +333 -0
- package/src/lib/cowork-task-runner.js +362 -0
- package/src/lib/cowork-workflow.js +327 -0
- package/src/lib/cross-chain.js +365 -0
- package/src/lib/dao-governance.js +339 -0
- package/src/lib/dlp-engine.js +343 -0
- package/src/lib/evolution-system.js +336 -0
- package/src/lib/evomap-manager.js +339 -0
- package/src/lib/execution-backend.js +351 -0
- package/src/lib/feature-flags.js +330 -0
- package/src/lib/git-integration.js +343 -0
- package/src/lib/hardening-manager.js +341 -0
- package/src/lib/hierarchical-memory.js +341 -0
- package/src/lib/inference-network.js +362 -0
- package/src/lib/iteration-budget.js +357 -0
- package/src/lib/knowledge-graph.js +333 -0
- package/src/lib/matrix-bridge.js +339 -0
- package/src/lib/mcp-scaffold.js +345 -0
- package/src/lib/memory-injection.js +320 -0
- package/src/lib/nostr-bridge.js +342 -0
- package/src/lib/orchestrator.js +350 -0
- package/src/lib/pdf-parser.js +330 -0
- package/src/lib/perf-tuning.js +364 -0
- package/src/lib/permission-engine.js +319 -0
- package/src/lib/pipeline-orchestrator.js +345 -0
- package/src/lib/plan-mode.js +328 -0
- package/src/lib/privacy-computing.js +335 -0
- package/src/lib/prompt-compressor.js +1 -10
- package/src/lib/reputation-optimizer.js +340 -0
- package/src/lib/sandbox-v2.js +327 -0
- package/src/lib/service-container.js +342 -0
- package/src/lib/session-consolidator.js +352 -0
- package/src/lib/session-hooks.js +340 -0
- package/src/lib/session-search.js +334 -0
- package/src/lib/session-tail.js +320 -0
- package/src/lib/session-usage.js +329 -0
- package/src/lib/siem-exporter.js +352 -0
- package/src/lib/skill-marketplace.js +345 -0
- package/src/lib/sla-manager.js +341 -0
- package/src/lib/slot-filler.js +333 -0
- package/src/lib/social-graph-analytics.js +327 -0
- package/src/lib/social-graph.js +304 -0
- package/src/lib/stress-tester.js +342 -0
- package/src/lib/sub-agent-registry.js +359 -0
- package/src/lib/task-model-selector.js +333 -0
- package/src/lib/terraform-manager.js +333 -0
- package/src/lib/todo-manager.js +339 -0
- package/src/lib/token-incentive.js +341 -0
- package/src/lib/topic-classifier.js +353 -0
- package/src/lib/user-profile.js +325 -0
- package/src/lib/version-checker.js +335 -0
- package/src/lib/web-fetch.js +322 -0
- package/src/lib/zkp-engine.js +342 -0
package/src/lib/feature-flags.js
CHANGED
|
@@ -5,3 +5,333 @@ export {
|
|
|
5
5
|
setFeature,
|
|
6
6
|
getFlagInfo,
|
|
7
7
|
} from "../harness/feature-flags.js";
|
|
8
|
+
|
|
9
|
+
// =====================================================================
|
|
10
|
+
// Feature Flags V2 governance overlay (in-memory, atop legacy flags)
|
|
11
|
+
// =====================================================================
|
|
12
|
+
export const FFLAG_PROFILE_MATURITY_V2 = Object.freeze({
|
|
13
|
+
PENDING: "pending",
|
|
14
|
+
ACTIVE: "active",
|
|
15
|
+
PAUSED: "paused",
|
|
16
|
+
ARCHIVED: "archived",
|
|
17
|
+
});
|
|
18
|
+
export const FFLAG_EVAL_LIFECYCLE_V2 = Object.freeze({
|
|
19
|
+
QUEUED: "queued",
|
|
20
|
+
EVALUATING: "evaluating",
|
|
21
|
+
EVALUATED: "evaluated",
|
|
22
|
+
FAILED: "failed",
|
|
23
|
+
CANCELLED: "cancelled",
|
|
24
|
+
});
|
|
25
|
+
const _fflagPTrans = new Map([
|
|
26
|
+
[
|
|
27
|
+
FFLAG_PROFILE_MATURITY_V2.PENDING,
|
|
28
|
+
new Set([
|
|
29
|
+
FFLAG_PROFILE_MATURITY_V2.ACTIVE,
|
|
30
|
+
FFLAG_PROFILE_MATURITY_V2.ARCHIVED,
|
|
31
|
+
]),
|
|
32
|
+
],
|
|
33
|
+
[
|
|
34
|
+
FFLAG_PROFILE_MATURITY_V2.ACTIVE,
|
|
35
|
+
new Set([
|
|
36
|
+
FFLAG_PROFILE_MATURITY_V2.PAUSED,
|
|
37
|
+
FFLAG_PROFILE_MATURITY_V2.ARCHIVED,
|
|
38
|
+
]),
|
|
39
|
+
],
|
|
40
|
+
[
|
|
41
|
+
FFLAG_PROFILE_MATURITY_V2.PAUSED,
|
|
42
|
+
new Set([
|
|
43
|
+
FFLAG_PROFILE_MATURITY_V2.ACTIVE,
|
|
44
|
+
FFLAG_PROFILE_MATURITY_V2.ARCHIVED,
|
|
45
|
+
]),
|
|
46
|
+
],
|
|
47
|
+
[FFLAG_PROFILE_MATURITY_V2.ARCHIVED, new Set()],
|
|
48
|
+
]);
|
|
49
|
+
const _fflagPTerminal = new Set([FFLAG_PROFILE_MATURITY_V2.ARCHIVED]);
|
|
50
|
+
const _fflagETrans = new Map([
|
|
51
|
+
[
|
|
52
|
+
FFLAG_EVAL_LIFECYCLE_V2.QUEUED,
|
|
53
|
+
new Set([
|
|
54
|
+
FFLAG_EVAL_LIFECYCLE_V2.EVALUATING,
|
|
55
|
+
FFLAG_EVAL_LIFECYCLE_V2.CANCELLED,
|
|
56
|
+
]),
|
|
57
|
+
],
|
|
58
|
+
[
|
|
59
|
+
FFLAG_EVAL_LIFECYCLE_V2.EVALUATING,
|
|
60
|
+
new Set([
|
|
61
|
+
FFLAG_EVAL_LIFECYCLE_V2.EVALUATED,
|
|
62
|
+
FFLAG_EVAL_LIFECYCLE_V2.FAILED,
|
|
63
|
+
FFLAG_EVAL_LIFECYCLE_V2.CANCELLED,
|
|
64
|
+
]),
|
|
65
|
+
],
|
|
66
|
+
[FFLAG_EVAL_LIFECYCLE_V2.EVALUATED, new Set()],
|
|
67
|
+
[FFLAG_EVAL_LIFECYCLE_V2.FAILED, new Set()],
|
|
68
|
+
[FFLAG_EVAL_LIFECYCLE_V2.CANCELLED, new Set()],
|
|
69
|
+
]);
|
|
70
|
+
const _fflagPsV2 = new Map();
|
|
71
|
+
const _fflagEsV2 = new Map();
|
|
72
|
+
let _fflagMaxActive = 15,
|
|
73
|
+
_fflagMaxPending = 30,
|
|
74
|
+
_fflagIdleMs = 30 * 24 * 60 * 60 * 1000,
|
|
75
|
+
_fflagStuckMs = 30 * 1000;
|
|
76
|
+
function _fflagPos(n, label) {
|
|
77
|
+
const v = Math.floor(Number(n));
|
|
78
|
+
if (!Number.isFinite(v) || v <= 0)
|
|
79
|
+
throw new Error(`${label} must be positive integer`);
|
|
80
|
+
return v;
|
|
81
|
+
}
|
|
82
|
+
function _fflagCheckP(from, to) {
|
|
83
|
+
const a = _fflagPTrans.get(from);
|
|
84
|
+
if (!a || !a.has(to))
|
|
85
|
+
throw new Error(`invalid fflag profile transition ${from} → ${to}`);
|
|
86
|
+
}
|
|
87
|
+
function _fflagCheckE(from, to) {
|
|
88
|
+
const a = _fflagETrans.get(from);
|
|
89
|
+
if (!a || !a.has(to))
|
|
90
|
+
throw new Error(`invalid fflag eval transition ${from} → ${to}`);
|
|
91
|
+
}
|
|
92
|
+
function _fflagCountActive(owner) {
|
|
93
|
+
let c = 0;
|
|
94
|
+
for (const p of _fflagPsV2.values())
|
|
95
|
+
if (p.owner === owner && p.status === FFLAG_PROFILE_MATURITY_V2.ACTIVE) c++;
|
|
96
|
+
return c;
|
|
97
|
+
}
|
|
98
|
+
function _fflagCountPending(profileId) {
|
|
99
|
+
let c = 0;
|
|
100
|
+
for (const e of _fflagEsV2.values())
|
|
101
|
+
if (
|
|
102
|
+
e.profileId === profileId &&
|
|
103
|
+
(e.status === FFLAG_EVAL_LIFECYCLE_V2.QUEUED ||
|
|
104
|
+
e.status === FFLAG_EVAL_LIFECYCLE_V2.EVALUATING)
|
|
105
|
+
)
|
|
106
|
+
c++;
|
|
107
|
+
return c;
|
|
108
|
+
}
|
|
109
|
+
export function setMaxActiveFflagProfilesPerOwnerV2(n) {
|
|
110
|
+
_fflagMaxActive = _fflagPos(n, "maxActiveFflagProfilesPerOwner");
|
|
111
|
+
}
|
|
112
|
+
export function getMaxActiveFflagProfilesPerOwnerV2() {
|
|
113
|
+
return _fflagMaxActive;
|
|
114
|
+
}
|
|
115
|
+
export function setMaxPendingFflagEvalsPerProfileV2(n) {
|
|
116
|
+
_fflagMaxPending = _fflagPos(n, "maxPendingFflagEvalsPerProfile");
|
|
117
|
+
}
|
|
118
|
+
export function getMaxPendingFflagEvalsPerProfileV2() {
|
|
119
|
+
return _fflagMaxPending;
|
|
120
|
+
}
|
|
121
|
+
export function setFflagProfileIdleMsV2(n) {
|
|
122
|
+
_fflagIdleMs = _fflagPos(n, "fflagProfileIdleMs");
|
|
123
|
+
}
|
|
124
|
+
export function getFflagProfileIdleMsV2() {
|
|
125
|
+
return _fflagIdleMs;
|
|
126
|
+
}
|
|
127
|
+
export function setFflagEvalStuckMsV2(n) {
|
|
128
|
+
_fflagStuckMs = _fflagPos(n, "fflagEvalStuckMs");
|
|
129
|
+
}
|
|
130
|
+
export function getFflagEvalStuckMsV2() {
|
|
131
|
+
return _fflagStuckMs;
|
|
132
|
+
}
|
|
133
|
+
export function _resetStateFeatureFlagsV2() {
|
|
134
|
+
_fflagPsV2.clear();
|
|
135
|
+
_fflagEsV2.clear();
|
|
136
|
+
_fflagMaxActive = 15;
|
|
137
|
+
_fflagMaxPending = 30;
|
|
138
|
+
_fflagIdleMs = 30 * 24 * 60 * 60 * 1000;
|
|
139
|
+
_fflagStuckMs = 30 * 1000;
|
|
140
|
+
}
|
|
141
|
+
export function registerFflagProfileV2({ id, owner, scope, metadata } = {}) {
|
|
142
|
+
if (!id || !owner) throw new Error("id and owner required");
|
|
143
|
+
if (_fflagPsV2.has(id)) throw new Error(`fflag profile ${id} already exists`);
|
|
144
|
+
const now = Date.now();
|
|
145
|
+
const p = {
|
|
146
|
+
id,
|
|
147
|
+
owner,
|
|
148
|
+
scope: scope || "*",
|
|
149
|
+
status: FFLAG_PROFILE_MATURITY_V2.PENDING,
|
|
150
|
+
createdAt: now,
|
|
151
|
+
updatedAt: now,
|
|
152
|
+
lastTouchedAt: now,
|
|
153
|
+
activatedAt: null,
|
|
154
|
+
archivedAt: null,
|
|
155
|
+
metadata: { ...(metadata || {}) },
|
|
156
|
+
};
|
|
157
|
+
_fflagPsV2.set(id, p);
|
|
158
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
159
|
+
}
|
|
160
|
+
export function activateFflagProfileV2(id) {
|
|
161
|
+
const p = _fflagPsV2.get(id);
|
|
162
|
+
if (!p) throw new Error(`fflag profile ${id} not found`);
|
|
163
|
+
const isInitial = p.status === FFLAG_PROFILE_MATURITY_V2.PENDING;
|
|
164
|
+
_fflagCheckP(p.status, FFLAG_PROFILE_MATURITY_V2.ACTIVE);
|
|
165
|
+
if (isInitial && _fflagCountActive(p.owner) >= _fflagMaxActive)
|
|
166
|
+
throw new Error(`max active fflag profiles for owner ${p.owner} reached`);
|
|
167
|
+
const now = Date.now();
|
|
168
|
+
p.status = FFLAG_PROFILE_MATURITY_V2.ACTIVE;
|
|
169
|
+
p.updatedAt = now;
|
|
170
|
+
p.lastTouchedAt = now;
|
|
171
|
+
if (!p.activatedAt) p.activatedAt = now;
|
|
172
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
173
|
+
}
|
|
174
|
+
export function pauseFflagProfileV2(id) {
|
|
175
|
+
const p = _fflagPsV2.get(id);
|
|
176
|
+
if (!p) throw new Error(`fflag profile ${id} not found`);
|
|
177
|
+
_fflagCheckP(p.status, FFLAG_PROFILE_MATURITY_V2.PAUSED);
|
|
178
|
+
p.status = FFLAG_PROFILE_MATURITY_V2.PAUSED;
|
|
179
|
+
p.updatedAt = Date.now();
|
|
180
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
181
|
+
}
|
|
182
|
+
export function archiveFflagProfileV2(id) {
|
|
183
|
+
const p = _fflagPsV2.get(id);
|
|
184
|
+
if (!p) throw new Error(`fflag profile ${id} not found`);
|
|
185
|
+
_fflagCheckP(p.status, FFLAG_PROFILE_MATURITY_V2.ARCHIVED);
|
|
186
|
+
const now = Date.now();
|
|
187
|
+
p.status = FFLAG_PROFILE_MATURITY_V2.ARCHIVED;
|
|
188
|
+
p.updatedAt = now;
|
|
189
|
+
if (!p.archivedAt) p.archivedAt = now;
|
|
190
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
191
|
+
}
|
|
192
|
+
export function touchFflagProfileV2(id) {
|
|
193
|
+
const p = _fflagPsV2.get(id);
|
|
194
|
+
if (!p) throw new Error(`fflag profile ${id} not found`);
|
|
195
|
+
if (_fflagPTerminal.has(p.status))
|
|
196
|
+
throw new Error(`cannot touch terminal fflag profile ${id}`);
|
|
197
|
+
const now = Date.now();
|
|
198
|
+
p.lastTouchedAt = now;
|
|
199
|
+
p.updatedAt = now;
|
|
200
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
201
|
+
}
|
|
202
|
+
export function getFflagProfileV2(id) {
|
|
203
|
+
const p = _fflagPsV2.get(id);
|
|
204
|
+
if (!p) return null;
|
|
205
|
+
return { ...p, metadata: { ...p.metadata } };
|
|
206
|
+
}
|
|
207
|
+
export function listFflagProfilesV2() {
|
|
208
|
+
return [..._fflagPsV2.values()].map((p) => ({
|
|
209
|
+
...p,
|
|
210
|
+
metadata: { ...p.metadata },
|
|
211
|
+
}));
|
|
212
|
+
}
|
|
213
|
+
export function createFflagEvalV2({ id, profileId, key, metadata } = {}) {
|
|
214
|
+
if (!id || !profileId) throw new Error("id and profileId required");
|
|
215
|
+
if (_fflagEsV2.has(id)) throw new Error(`fflag eval ${id} already exists`);
|
|
216
|
+
if (!_fflagPsV2.has(profileId))
|
|
217
|
+
throw new Error(`fflag profile ${profileId} not found`);
|
|
218
|
+
if (_fflagCountPending(profileId) >= _fflagMaxPending)
|
|
219
|
+
throw new Error(`max pending fflag evals for profile ${profileId} reached`);
|
|
220
|
+
const now = Date.now();
|
|
221
|
+
const e = {
|
|
222
|
+
id,
|
|
223
|
+
profileId,
|
|
224
|
+
key: key || "",
|
|
225
|
+
status: FFLAG_EVAL_LIFECYCLE_V2.QUEUED,
|
|
226
|
+
createdAt: now,
|
|
227
|
+
updatedAt: now,
|
|
228
|
+
startedAt: null,
|
|
229
|
+
settledAt: null,
|
|
230
|
+
metadata: { ...(metadata || {}) },
|
|
231
|
+
};
|
|
232
|
+
_fflagEsV2.set(id, e);
|
|
233
|
+
return { ...e, metadata: { ...e.metadata } };
|
|
234
|
+
}
|
|
235
|
+
export function evaluatingFflagEvalV2(id) {
|
|
236
|
+
const e = _fflagEsV2.get(id);
|
|
237
|
+
if (!e) throw new Error(`fflag eval ${id} not found`);
|
|
238
|
+
_fflagCheckE(e.status, FFLAG_EVAL_LIFECYCLE_V2.EVALUATING);
|
|
239
|
+
const now = Date.now();
|
|
240
|
+
e.status = FFLAG_EVAL_LIFECYCLE_V2.EVALUATING;
|
|
241
|
+
e.updatedAt = now;
|
|
242
|
+
if (!e.startedAt) e.startedAt = now;
|
|
243
|
+
return { ...e, metadata: { ...e.metadata } };
|
|
244
|
+
}
|
|
245
|
+
export function evaluateFflagEvalV2(id) {
|
|
246
|
+
const e = _fflagEsV2.get(id);
|
|
247
|
+
if (!e) throw new Error(`fflag eval ${id} not found`);
|
|
248
|
+
_fflagCheckE(e.status, FFLAG_EVAL_LIFECYCLE_V2.EVALUATED);
|
|
249
|
+
const now = Date.now();
|
|
250
|
+
e.status = FFLAG_EVAL_LIFECYCLE_V2.EVALUATED;
|
|
251
|
+
e.updatedAt = now;
|
|
252
|
+
if (!e.settledAt) e.settledAt = now;
|
|
253
|
+
return { ...e, metadata: { ...e.metadata } };
|
|
254
|
+
}
|
|
255
|
+
export function failFflagEvalV2(id, reason) {
|
|
256
|
+
const e = _fflagEsV2.get(id);
|
|
257
|
+
if (!e) throw new Error(`fflag eval ${id} not found`);
|
|
258
|
+
_fflagCheckE(e.status, FFLAG_EVAL_LIFECYCLE_V2.FAILED);
|
|
259
|
+
const now = Date.now();
|
|
260
|
+
e.status = FFLAG_EVAL_LIFECYCLE_V2.FAILED;
|
|
261
|
+
e.updatedAt = now;
|
|
262
|
+
if (!e.settledAt) e.settledAt = now;
|
|
263
|
+
if (reason) e.metadata.failReason = String(reason);
|
|
264
|
+
return { ...e, metadata: { ...e.metadata } };
|
|
265
|
+
}
|
|
266
|
+
export function cancelFflagEvalV2(id, reason) {
|
|
267
|
+
const e = _fflagEsV2.get(id);
|
|
268
|
+
if (!e) throw new Error(`fflag eval ${id} not found`);
|
|
269
|
+
_fflagCheckE(e.status, FFLAG_EVAL_LIFECYCLE_V2.CANCELLED);
|
|
270
|
+
const now = Date.now();
|
|
271
|
+
e.status = FFLAG_EVAL_LIFECYCLE_V2.CANCELLED;
|
|
272
|
+
e.updatedAt = now;
|
|
273
|
+
if (!e.settledAt) e.settledAt = now;
|
|
274
|
+
if (reason) e.metadata.cancelReason = String(reason);
|
|
275
|
+
return { ...e, metadata: { ...e.metadata } };
|
|
276
|
+
}
|
|
277
|
+
export function getFflagEvalV2(id) {
|
|
278
|
+
const e = _fflagEsV2.get(id);
|
|
279
|
+
if (!e) return null;
|
|
280
|
+
return { ...e, metadata: { ...e.metadata } };
|
|
281
|
+
}
|
|
282
|
+
export function listFflagEvalsV2() {
|
|
283
|
+
return [..._fflagEsV2.values()].map((e) => ({
|
|
284
|
+
...e,
|
|
285
|
+
metadata: { ...e.metadata },
|
|
286
|
+
}));
|
|
287
|
+
}
|
|
288
|
+
export function autoPauseIdleFflagProfilesV2({ now } = {}) {
|
|
289
|
+
const t = now ?? Date.now();
|
|
290
|
+
const flipped = [];
|
|
291
|
+
for (const p of _fflagPsV2.values())
|
|
292
|
+
if (
|
|
293
|
+
p.status === FFLAG_PROFILE_MATURITY_V2.ACTIVE &&
|
|
294
|
+
t - p.lastTouchedAt >= _fflagIdleMs
|
|
295
|
+
) {
|
|
296
|
+
p.status = FFLAG_PROFILE_MATURITY_V2.PAUSED;
|
|
297
|
+
p.updatedAt = t;
|
|
298
|
+
flipped.push(p.id);
|
|
299
|
+
}
|
|
300
|
+
return { flipped, count: flipped.length };
|
|
301
|
+
}
|
|
302
|
+
export function autoFailStuckFflagEvalsV2({ now } = {}) {
|
|
303
|
+
const t = now ?? Date.now();
|
|
304
|
+
const flipped = [];
|
|
305
|
+
for (const e of _fflagEsV2.values())
|
|
306
|
+
if (
|
|
307
|
+
e.status === FFLAG_EVAL_LIFECYCLE_V2.EVALUATING &&
|
|
308
|
+
e.startedAt != null &&
|
|
309
|
+
t - e.startedAt >= _fflagStuckMs
|
|
310
|
+
) {
|
|
311
|
+
e.status = FFLAG_EVAL_LIFECYCLE_V2.FAILED;
|
|
312
|
+
e.updatedAt = t;
|
|
313
|
+
if (!e.settledAt) e.settledAt = t;
|
|
314
|
+
e.metadata.failReason = "auto-fail-stuck";
|
|
315
|
+
flipped.push(e.id);
|
|
316
|
+
}
|
|
317
|
+
return { flipped, count: flipped.length };
|
|
318
|
+
}
|
|
319
|
+
export function getFeatureFlagsGovStatsV2() {
|
|
320
|
+
const profilesByStatus = {};
|
|
321
|
+
for (const v of Object.values(FFLAG_PROFILE_MATURITY_V2))
|
|
322
|
+
profilesByStatus[v] = 0;
|
|
323
|
+
for (const p of _fflagPsV2.values()) profilesByStatus[p.status]++;
|
|
324
|
+
const evalsByStatus = {};
|
|
325
|
+
for (const v of Object.values(FFLAG_EVAL_LIFECYCLE_V2)) evalsByStatus[v] = 0;
|
|
326
|
+
for (const e of _fflagEsV2.values()) evalsByStatus[e.status]++;
|
|
327
|
+
return {
|
|
328
|
+
totalFflagProfilesV2: _fflagPsV2.size,
|
|
329
|
+
totalFflagEvalsV2: _fflagEsV2.size,
|
|
330
|
+
maxActiveFflagProfilesPerOwner: _fflagMaxActive,
|
|
331
|
+
maxPendingFflagEvalsPerProfile: _fflagMaxPending,
|
|
332
|
+
fflagProfileIdleMs: _fflagIdleMs,
|
|
333
|
+
fflagEvalStuckMs: _fflagStuckMs,
|
|
334
|
+
profilesByStatus,
|
|
335
|
+
evalsByStatus,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
@@ -218,3 +218,346 @@ echo "ChainlessChain: pre-commit hook running"
|
|
|
218
218
|
|
|
219
219
|
return { installed: true, hook: "pre-commit", path: hookPath };
|
|
220
220
|
}
|
|
221
|
+
|
|
222
|
+
// ===== V2 Surface: Git Integration governance overlay (CLI v0.139.0) =====
|
|
223
|
+
export const GIT_REPO_MATURITY_V2 = Object.freeze({
|
|
224
|
+
PENDING: "pending",
|
|
225
|
+
ACTIVE: "active",
|
|
226
|
+
ARCHIVED: "archived",
|
|
227
|
+
DECOMMISSIONED: "decommissioned",
|
|
228
|
+
});
|
|
229
|
+
export const GIT_COMMIT_LIFECYCLE_V2 = Object.freeze({
|
|
230
|
+
QUEUED: "queued",
|
|
231
|
+
COMMITTING: "committing",
|
|
232
|
+
COMMITTED: "committed",
|
|
233
|
+
FAILED: "failed",
|
|
234
|
+
CANCELLED: "cancelled",
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const _grTrans = new Map([
|
|
238
|
+
[
|
|
239
|
+
GIT_REPO_MATURITY_V2.PENDING,
|
|
240
|
+
new Set([GIT_REPO_MATURITY_V2.ACTIVE, GIT_REPO_MATURITY_V2.DECOMMISSIONED]),
|
|
241
|
+
],
|
|
242
|
+
[
|
|
243
|
+
GIT_REPO_MATURITY_V2.ACTIVE,
|
|
244
|
+
new Set([
|
|
245
|
+
GIT_REPO_MATURITY_V2.ARCHIVED,
|
|
246
|
+
GIT_REPO_MATURITY_V2.DECOMMISSIONED,
|
|
247
|
+
]),
|
|
248
|
+
],
|
|
249
|
+
[
|
|
250
|
+
GIT_REPO_MATURITY_V2.ARCHIVED,
|
|
251
|
+
new Set([GIT_REPO_MATURITY_V2.ACTIVE, GIT_REPO_MATURITY_V2.DECOMMISSIONED]),
|
|
252
|
+
],
|
|
253
|
+
[GIT_REPO_MATURITY_V2.DECOMMISSIONED, new Set()],
|
|
254
|
+
]);
|
|
255
|
+
const _grTerminal = new Set([GIT_REPO_MATURITY_V2.DECOMMISSIONED]);
|
|
256
|
+
const _gcTrans = new Map([
|
|
257
|
+
[
|
|
258
|
+
GIT_COMMIT_LIFECYCLE_V2.QUEUED,
|
|
259
|
+
new Set([
|
|
260
|
+
GIT_COMMIT_LIFECYCLE_V2.COMMITTING,
|
|
261
|
+
GIT_COMMIT_LIFECYCLE_V2.CANCELLED,
|
|
262
|
+
]),
|
|
263
|
+
],
|
|
264
|
+
[
|
|
265
|
+
GIT_COMMIT_LIFECYCLE_V2.COMMITTING,
|
|
266
|
+
new Set([
|
|
267
|
+
GIT_COMMIT_LIFECYCLE_V2.COMMITTED,
|
|
268
|
+
GIT_COMMIT_LIFECYCLE_V2.FAILED,
|
|
269
|
+
GIT_COMMIT_LIFECYCLE_V2.CANCELLED,
|
|
270
|
+
]),
|
|
271
|
+
],
|
|
272
|
+
[GIT_COMMIT_LIFECYCLE_V2.COMMITTED, new Set()],
|
|
273
|
+
[GIT_COMMIT_LIFECYCLE_V2.FAILED, new Set()],
|
|
274
|
+
[GIT_COMMIT_LIFECYCLE_V2.CANCELLED, new Set()],
|
|
275
|
+
]);
|
|
276
|
+
|
|
277
|
+
const _grsV2 = new Map();
|
|
278
|
+
const _gcsV2 = new Map();
|
|
279
|
+
let _grMaxActivePerOwner = 10;
|
|
280
|
+
let _grMaxPendingCommitsPerRepo = 20;
|
|
281
|
+
let _grIdleMs = 30 * 24 * 60 * 60 * 1000;
|
|
282
|
+
let _gcStuckMs = 5 * 60 * 1000;
|
|
283
|
+
|
|
284
|
+
function _grPos(n, lbl) {
|
|
285
|
+
const v = Math.floor(Number(n));
|
|
286
|
+
if (!Number.isFinite(v) || v <= 0)
|
|
287
|
+
throw new Error(`${lbl} must be positive integer`);
|
|
288
|
+
return v;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function setMaxActiveGitReposPerOwnerV2(n) {
|
|
292
|
+
_grMaxActivePerOwner = _grPos(n, "maxActiveGitReposPerOwner");
|
|
293
|
+
}
|
|
294
|
+
export function getMaxActiveGitReposPerOwnerV2() {
|
|
295
|
+
return _grMaxActivePerOwner;
|
|
296
|
+
}
|
|
297
|
+
export function setMaxPendingGitCommitsPerRepoV2(n) {
|
|
298
|
+
_grMaxPendingCommitsPerRepo = _grPos(n, "maxPendingGitCommitsPerRepo");
|
|
299
|
+
}
|
|
300
|
+
export function getMaxPendingGitCommitsPerRepoV2() {
|
|
301
|
+
return _grMaxPendingCommitsPerRepo;
|
|
302
|
+
}
|
|
303
|
+
export function setGitRepoIdleMsV2(n) {
|
|
304
|
+
_grIdleMs = _grPos(n, "gitRepoIdleMs");
|
|
305
|
+
}
|
|
306
|
+
export function getGitRepoIdleMsV2() {
|
|
307
|
+
return _grIdleMs;
|
|
308
|
+
}
|
|
309
|
+
export function setGitCommitStuckMsV2(n) {
|
|
310
|
+
_gcStuckMs = _grPos(n, "gitCommitStuckMs");
|
|
311
|
+
}
|
|
312
|
+
export function getGitCommitStuckMsV2() {
|
|
313
|
+
return _gcStuckMs;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export function _resetStateGitIntegrationV2() {
|
|
317
|
+
_grsV2.clear();
|
|
318
|
+
_gcsV2.clear();
|
|
319
|
+
_grMaxActivePerOwner = 10;
|
|
320
|
+
_grMaxPendingCommitsPerRepo = 20;
|
|
321
|
+
_grIdleMs = 30 * 24 * 60 * 60 * 1000;
|
|
322
|
+
_gcStuckMs = 5 * 60 * 1000;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export function registerGitRepoV2({ id, owner, branch, metadata } = {}) {
|
|
326
|
+
if (!id || typeof id !== "string") throw new Error("id is required");
|
|
327
|
+
if (!owner || typeof owner !== "string") throw new Error("owner is required");
|
|
328
|
+
if (_grsV2.has(id)) throw new Error(`git repo ${id} already registered`);
|
|
329
|
+
const now = Date.now();
|
|
330
|
+
const r = {
|
|
331
|
+
id,
|
|
332
|
+
owner,
|
|
333
|
+
branch: branch || "main",
|
|
334
|
+
status: GIT_REPO_MATURITY_V2.PENDING,
|
|
335
|
+
createdAt: now,
|
|
336
|
+
updatedAt: now,
|
|
337
|
+
activatedAt: null,
|
|
338
|
+
decommissionedAt: null,
|
|
339
|
+
lastTouchedAt: now,
|
|
340
|
+
metadata: { ...(metadata || {}) },
|
|
341
|
+
};
|
|
342
|
+
_grsV2.set(id, r);
|
|
343
|
+
return { ...r, metadata: { ...r.metadata } };
|
|
344
|
+
}
|
|
345
|
+
function _grCheckR(from, to) {
|
|
346
|
+
const a = _grTrans.get(from);
|
|
347
|
+
if (!a || !a.has(to))
|
|
348
|
+
throw new Error(`invalid git repo transition ${from} → ${to}`);
|
|
349
|
+
}
|
|
350
|
+
function _grCountActive(owner) {
|
|
351
|
+
let n = 0;
|
|
352
|
+
for (const r of _grsV2.values())
|
|
353
|
+
if (r.owner === owner && r.status === GIT_REPO_MATURITY_V2.ACTIVE) n++;
|
|
354
|
+
return n;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export function activateGitRepoV2(id) {
|
|
358
|
+
const r = _grsV2.get(id);
|
|
359
|
+
if (!r) throw new Error(`git repo ${id} not found`);
|
|
360
|
+
_grCheckR(r.status, GIT_REPO_MATURITY_V2.ACTIVE);
|
|
361
|
+
const recovery = r.status === GIT_REPO_MATURITY_V2.ARCHIVED;
|
|
362
|
+
if (!recovery) {
|
|
363
|
+
const c = _grCountActive(r.owner);
|
|
364
|
+
if (c >= _grMaxActivePerOwner)
|
|
365
|
+
throw new Error(
|
|
366
|
+
`max active git repos per owner (${_grMaxActivePerOwner}) reached for ${r.owner}`,
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
const now = Date.now();
|
|
370
|
+
r.status = GIT_REPO_MATURITY_V2.ACTIVE;
|
|
371
|
+
r.updatedAt = now;
|
|
372
|
+
r.lastTouchedAt = now;
|
|
373
|
+
if (!r.activatedAt) r.activatedAt = now;
|
|
374
|
+
return { ...r, metadata: { ...r.metadata } };
|
|
375
|
+
}
|
|
376
|
+
export function archiveGitRepoV2(id) {
|
|
377
|
+
const r = _grsV2.get(id);
|
|
378
|
+
if (!r) throw new Error(`git repo ${id} not found`);
|
|
379
|
+
_grCheckR(r.status, GIT_REPO_MATURITY_V2.ARCHIVED);
|
|
380
|
+
r.status = GIT_REPO_MATURITY_V2.ARCHIVED;
|
|
381
|
+
r.updatedAt = Date.now();
|
|
382
|
+
return { ...r, metadata: { ...r.metadata } };
|
|
383
|
+
}
|
|
384
|
+
export function decommissionGitRepoV2(id) {
|
|
385
|
+
const r = _grsV2.get(id);
|
|
386
|
+
if (!r) throw new Error(`git repo ${id} not found`);
|
|
387
|
+
_grCheckR(r.status, GIT_REPO_MATURITY_V2.DECOMMISSIONED);
|
|
388
|
+
const now = Date.now();
|
|
389
|
+
r.status = GIT_REPO_MATURITY_V2.DECOMMISSIONED;
|
|
390
|
+
r.updatedAt = now;
|
|
391
|
+
if (!r.decommissionedAt) r.decommissionedAt = now;
|
|
392
|
+
return { ...r, metadata: { ...r.metadata } };
|
|
393
|
+
}
|
|
394
|
+
export function touchGitRepoV2(id) {
|
|
395
|
+
const r = _grsV2.get(id);
|
|
396
|
+
if (!r) throw new Error(`git repo ${id} not found`);
|
|
397
|
+
if (_grTerminal.has(r.status))
|
|
398
|
+
throw new Error(`cannot touch terminal git repo ${id}`);
|
|
399
|
+
const now = Date.now();
|
|
400
|
+
r.lastTouchedAt = now;
|
|
401
|
+
r.updatedAt = now;
|
|
402
|
+
return { ...r, metadata: { ...r.metadata } };
|
|
403
|
+
}
|
|
404
|
+
export function getGitRepoV2(id) {
|
|
405
|
+
const r = _grsV2.get(id);
|
|
406
|
+
if (!r) return null;
|
|
407
|
+
return { ...r, metadata: { ...r.metadata } };
|
|
408
|
+
}
|
|
409
|
+
export function listGitReposV2() {
|
|
410
|
+
return [..._grsV2.values()].map((r) => ({
|
|
411
|
+
...r,
|
|
412
|
+
metadata: { ...r.metadata },
|
|
413
|
+
}));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function _gcCountPending(repoId) {
|
|
417
|
+
let n = 0;
|
|
418
|
+
for (const c of _gcsV2.values())
|
|
419
|
+
if (
|
|
420
|
+
c.repoId === repoId &&
|
|
421
|
+
(c.status === GIT_COMMIT_LIFECYCLE_V2.QUEUED ||
|
|
422
|
+
c.status === GIT_COMMIT_LIFECYCLE_V2.COMMITTING)
|
|
423
|
+
)
|
|
424
|
+
n++;
|
|
425
|
+
return n;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export function createGitCommitV2({ id, repoId, message, metadata } = {}) {
|
|
429
|
+
if (!id || typeof id !== "string") throw new Error("id is required");
|
|
430
|
+
if (!repoId || typeof repoId !== "string")
|
|
431
|
+
throw new Error("repoId is required");
|
|
432
|
+
if (_gcsV2.has(id)) throw new Error(`git commit ${id} already exists`);
|
|
433
|
+
if (!_grsV2.has(repoId)) throw new Error(`git repo ${repoId} not found`);
|
|
434
|
+
const pending = _gcCountPending(repoId);
|
|
435
|
+
if (pending >= _grMaxPendingCommitsPerRepo)
|
|
436
|
+
throw new Error(
|
|
437
|
+
`max pending git commits per repo (${_grMaxPendingCommitsPerRepo}) reached for ${repoId}`,
|
|
438
|
+
);
|
|
439
|
+
const now = Date.now();
|
|
440
|
+
const c = {
|
|
441
|
+
id,
|
|
442
|
+
repoId,
|
|
443
|
+
message: message || "",
|
|
444
|
+
status: GIT_COMMIT_LIFECYCLE_V2.QUEUED,
|
|
445
|
+
createdAt: now,
|
|
446
|
+
updatedAt: now,
|
|
447
|
+
startedAt: null,
|
|
448
|
+
settledAt: null,
|
|
449
|
+
metadata: { ...(metadata || {}) },
|
|
450
|
+
};
|
|
451
|
+
_gcsV2.set(id, c);
|
|
452
|
+
return { ...c, metadata: { ...c.metadata } };
|
|
453
|
+
}
|
|
454
|
+
function _gcCheckC(from, to) {
|
|
455
|
+
const a = _gcTrans.get(from);
|
|
456
|
+
if (!a || !a.has(to))
|
|
457
|
+
throw new Error(`invalid git commit transition ${from} → ${to}`);
|
|
458
|
+
}
|
|
459
|
+
export function startGitCommitV2(id) {
|
|
460
|
+
const c = _gcsV2.get(id);
|
|
461
|
+
if (!c) throw new Error(`git commit ${id} not found`);
|
|
462
|
+
_gcCheckC(c.status, GIT_COMMIT_LIFECYCLE_V2.COMMITTING);
|
|
463
|
+
const now = Date.now();
|
|
464
|
+
c.status = GIT_COMMIT_LIFECYCLE_V2.COMMITTING;
|
|
465
|
+
c.updatedAt = now;
|
|
466
|
+
if (!c.startedAt) c.startedAt = now;
|
|
467
|
+
return { ...c, metadata: { ...c.metadata } };
|
|
468
|
+
}
|
|
469
|
+
export function commitGitCommitV2(id) {
|
|
470
|
+
const c = _gcsV2.get(id);
|
|
471
|
+
if (!c) throw new Error(`git commit ${id} not found`);
|
|
472
|
+
_gcCheckC(c.status, GIT_COMMIT_LIFECYCLE_V2.COMMITTED);
|
|
473
|
+
const now = Date.now();
|
|
474
|
+
c.status = GIT_COMMIT_LIFECYCLE_V2.COMMITTED;
|
|
475
|
+
c.updatedAt = now;
|
|
476
|
+
if (!c.settledAt) c.settledAt = now;
|
|
477
|
+
return { ...c, metadata: { ...c.metadata } };
|
|
478
|
+
}
|
|
479
|
+
export function failGitCommitV2(id, reason) {
|
|
480
|
+
const c = _gcsV2.get(id);
|
|
481
|
+
if (!c) throw new Error(`git commit ${id} not found`);
|
|
482
|
+
_gcCheckC(c.status, GIT_COMMIT_LIFECYCLE_V2.FAILED);
|
|
483
|
+
const now = Date.now();
|
|
484
|
+
c.status = GIT_COMMIT_LIFECYCLE_V2.FAILED;
|
|
485
|
+
c.updatedAt = now;
|
|
486
|
+
if (!c.settledAt) c.settledAt = now;
|
|
487
|
+
if (reason) c.metadata.failReason = String(reason);
|
|
488
|
+
return { ...c, metadata: { ...c.metadata } };
|
|
489
|
+
}
|
|
490
|
+
export function cancelGitCommitV2(id, reason) {
|
|
491
|
+
const c = _gcsV2.get(id);
|
|
492
|
+
if (!c) throw new Error(`git commit ${id} not found`);
|
|
493
|
+
_gcCheckC(c.status, GIT_COMMIT_LIFECYCLE_V2.CANCELLED);
|
|
494
|
+
const now = Date.now();
|
|
495
|
+
c.status = GIT_COMMIT_LIFECYCLE_V2.CANCELLED;
|
|
496
|
+
c.updatedAt = now;
|
|
497
|
+
if (!c.settledAt) c.settledAt = now;
|
|
498
|
+
if (reason) c.metadata.cancelReason = String(reason);
|
|
499
|
+
return { ...c, metadata: { ...c.metadata } };
|
|
500
|
+
}
|
|
501
|
+
export function getGitCommitV2(id) {
|
|
502
|
+
const c = _gcsV2.get(id);
|
|
503
|
+
if (!c) return null;
|
|
504
|
+
return { ...c, metadata: { ...c.metadata } };
|
|
505
|
+
}
|
|
506
|
+
export function listGitCommitsV2() {
|
|
507
|
+
return [..._gcsV2.values()].map((c) => ({
|
|
508
|
+
...c,
|
|
509
|
+
metadata: { ...c.metadata },
|
|
510
|
+
}));
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
export function autoArchiveIdleGitReposV2({ now } = {}) {
|
|
514
|
+
const t = now ?? Date.now();
|
|
515
|
+
const flipped = [];
|
|
516
|
+
for (const r of _grsV2.values())
|
|
517
|
+
if (
|
|
518
|
+
r.status === GIT_REPO_MATURITY_V2.ACTIVE &&
|
|
519
|
+
t - r.lastTouchedAt >= _grIdleMs
|
|
520
|
+
) {
|
|
521
|
+
r.status = GIT_REPO_MATURITY_V2.ARCHIVED;
|
|
522
|
+
r.updatedAt = t;
|
|
523
|
+
flipped.push(r.id);
|
|
524
|
+
}
|
|
525
|
+
return { flipped, count: flipped.length };
|
|
526
|
+
}
|
|
527
|
+
export function autoFailStuckGitCommitsV2({ now } = {}) {
|
|
528
|
+
const t = now ?? Date.now();
|
|
529
|
+
const flipped = [];
|
|
530
|
+
for (const c of _gcsV2.values())
|
|
531
|
+
if (
|
|
532
|
+
c.status === GIT_COMMIT_LIFECYCLE_V2.COMMITTING &&
|
|
533
|
+
c.startedAt != null &&
|
|
534
|
+
t - c.startedAt >= _gcStuckMs
|
|
535
|
+
) {
|
|
536
|
+
c.status = GIT_COMMIT_LIFECYCLE_V2.FAILED;
|
|
537
|
+
c.updatedAt = t;
|
|
538
|
+
if (!c.settledAt) c.settledAt = t;
|
|
539
|
+
c.metadata.failReason = "auto-fail-stuck";
|
|
540
|
+
flipped.push(c.id);
|
|
541
|
+
}
|
|
542
|
+
return { flipped, count: flipped.length };
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
export function getGitIntegrationGovStatsV2() {
|
|
546
|
+
const reposByStatus = {};
|
|
547
|
+
for (const s of Object.values(GIT_REPO_MATURITY_V2)) reposByStatus[s] = 0;
|
|
548
|
+
for (const r of _grsV2.values()) reposByStatus[r.status]++;
|
|
549
|
+
const commitsByStatus = {};
|
|
550
|
+
for (const s of Object.values(GIT_COMMIT_LIFECYCLE_V2))
|
|
551
|
+
commitsByStatus[s] = 0;
|
|
552
|
+
for (const c of _gcsV2.values()) commitsByStatus[c.status]++;
|
|
553
|
+
return {
|
|
554
|
+
totalGitReposV2: _grsV2.size,
|
|
555
|
+
totalGitCommitsV2: _gcsV2.size,
|
|
556
|
+
maxActiveGitReposPerOwner: _grMaxActivePerOwner,
|
|
557
|
+
maxPendingGitCommitsPerRepo: _grMaxPendingCommitsPerRepo,
|
|
558
|
+
gitRepoIdleMs: _grIdleMs,
|
|
559
|
+
gitCommitStuckMs: _gcStuckMs,
|
|
560
|
+
reposByStatus,
|
|
561
|
+
commitsByStatus,
|
|
562
|
+
};
|
|
563
|
+
}
|