owlrunkit 0.18.0 → 0.18.2
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/CHANGELOG.md +41 -0
- package/README.md +61 -17
- package/docs/architecture/OWLCODA_RUNKIT_USAGE_MODE_SELECTION_PRODUCT_DECISION_20260811.md +166 -0
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +41 -16
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
- package/package.json +1 -1
- package/packages/attest/distribution-manifest-v1.json +11 -1
- package/packages/attest/src/quick.mjs +11 -1
- package/registry-release-policy-v1.json +5 -5
- package/sbom.spdx.json +5 -5
- package/scripts/runkit-contract/assurance-router.mjs +31 -21
- package/scripts/runkit-contract/core-contract.mjs +5 -1
- package/scripts/runkit-contract/inspect-presentation.mjs +38 -5
- package/scripts/runkit-contract/install-codex-skill.mjs +60 -1
- package/scripts/runkit-contract/onboarding-doctor.mjs +65 -19
- package/scripts/runkit-contract/profile-onboarding.mjs +601 -22
- package/scripts/runkit-contract/project-cli-resolver.mjs +6 -0
- package/scripts/runkit-contract/quick-attest.mjs +1 -0
- package/scripts/runkit-contract/quick-metrics.mjs +43 -5
- package/scripts/runkit-contract/quick-receipt.mjs +43 -5
- package/scripts/runkit-contract/quick-verify.mjs +20 -4
- package/scripts/runkit-contract/runkit-cli.mjs +278 -24
- package/scripts/runkit-contract/schemas/profile-reconcile-plan-v1.schema.json +107 -0
- package/scripts/runkit-contract/schemas/profile-reconcile-receipt-v1.schema.json +60 -0
- package/scripts/runkit-contract/schemas/usage-mode-recommendation-v1.schema.json +136 -0
- package/scripts/runkit-contract/templates/assurance-request.json +6 -0
- package/scripts/runkit-contract/usage-mode.mjs +227 -0
|
@@ -6,9 +6,15 @@ const BOOLEAN_FACTS = [
|
|
|
6
6
|
"longRunning",
|
|
7
7
|
"interruptionRecovery",
|
|
8
8
|
"dirtyParallelWorkspace",
|
|
9
|
+
"irreversibleChange",
|
|
10
|
+
"rollbackUncertain",
|
|
11
|
+
"permissionSensitive",
|
|
12
|
+
"securitySensitive",
|
|
13
|
+
"fundsAtRisk",
|
|
14
|
+
"schemaOrDataMigration",
|
|
9
15
|
];
|
|
10
16
|
|
|
11
|
-
const
|
|
17
|
+
export const RUNKIT_RISK_CATEGORIES = Object.freeze([
|
|
12
18
|
"backtest",
|
|
13
19
|
"deployment",
|
|
14
20
|
"destructive",
|
|
@@ -20,13 +26,13 @@ const RISK_CATEGORIES = new Set([
|
|
|
20
26
|
"release",
|
|
21
27
|
]);
|
|
22
28
|
|
|
23
|
-
const
|
|
24
|
-
|
|
29
|
+
const RISK_CATEGORIES = new Set(RUNKIT_RISK_CATEGORIES);
|
|
30
|
+
|
|
31
|
+
const MATERIAL_RISK_CATEGORIES = new Set([
|
|
25
32
|
"destructive",
|
|
26
33
|
"foreign_write",
|
|
27
34
|
"funds",
|
|
28
|
-
"
|
|
29
|
-
"release",
|
|
35
|
+
"migration",
|
|
30
36
|
]);
|
|
31
37
|
|
|
32
38
|
function incompleteReasons(input) {
|
|
@@ -76,26 +82,21 @@ export function routeRunKitAssuranceV1(input) {
|
|
|
76
82
|
const categories = [...new Set(input.riskCategories)].sort();
|
|
77
83
|
const formalReasons = [
|
|
78
84
|
...(input.needsFormalAcceptance ? ["formal_acceptance_required"] : []),
|
|
79
|
-
...(input.
|
|
80
|
-
...(input.
|
|
81
|
-
...(input.
|
|
82
|
-
...(input.
|
|
83
|
-
...(input.
|
|
84
|
-
...(input.
|
|
85
|
-
...(input.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
? ["multiple_commands"]
|
|
90
|
-
: []),
|
|
91
|
-
...categories.map(category => `risk_${category}`),
|
|
85
|
+
...(input.writerCount > 1 ? ["multiple_writers"] : []),
|
|
86
|
+
...(input.permissionSensitive === true ? ["permission_sensitive"] : []),
|
|
87
|
+
...(input.securitySensitive === true ? ["security_sensitive"] : []),
|
|
88
|
+
...(input.fundsAtRisk === true ? ["funds_at_risk"] : []),
|
|
89
|
+
...(input.irreversibleChange === true ? ["irreversible_change"] : []),
|
|
90
|
+
...(input.rollbackUncertain === true ? ["rollback_uncertain"] : []),
|
|
91
|
+
...(input.schemaOrDataMigration === true ? ["schema_or_data_migration"] : []),
|
|
92
|
+
...categories
|
|
93
|
+
.filter(category => MATERIAL_RISK_CATEGORIES.has(category))
|
|
94
|
+
.map(category => `risk_${category}`),
|
|
92
95
|
];
|
|
93
96
|
if (formalReasons.length > 0) {
|
|
94
|
-
const full = input.externalSideEffects
|
|
95
|
-
|| categories.some(category => FULL_RISK_CATEGORIES.has(category));
|
|
96
97
|
return result({
|
|
97
98
|
lane: "formal",
|
|
98
|
-
riskMode:
|
|
99
|
+
riskMode: "full",
|
|
99
100
|
reasonCodes: [...new Set(formalReasons)].sort(),
|
|
100
101
|
nextCommand: "owlrunkit formal start",
|
|
101
102
|
});
|
|
@@ -110,6 +111,15 @@ export function routeRunKitAssuranceV1(input) {
|
|
|
110
111
|
});
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
if (input.exactCommandCount !== 1) {
|
|
115
|
+
return result({
|
|
116
|
+
lane: "none",
|
|
117
|
+
riskMode: "lightweight",
|
|
118
|
+
reasonCodes: ["quick_exact_command_required"],
|
|
119
|
+
nextCommand: null,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
113
123
|
return result({
|
|
114
124
|
lane: "quick",
|
|
115
125
|
riskMode: "lightweight",
|
|
@@ -14,7 +14,7 @@ import { fileURLToPath } from "node:url";
|
|
|
14
14
|
import { parseProjectControlState } from "./project-control-parser.mjs";
|
|
15
15
|
|
|
16
16
|
export const CONTRACT_VERSION = "0.2";
|
|
17
|
-
export const CORE_VERSION = "0.18.
|
|
17
|
+
export const CORE_VERSION = "0.18.2";
|
|
18
18
|
export const RUNTIME_ROOT = ".owlcoda/runkit";
|
|
19
19
|
|
|
20
20
|
const CORE_FILES = [
|
|
@@ -68,6 +68,7 @@ const CORE_FILES = [
|
|
|
68
68
|
"sync-release-identity.mjs",
|
|
69
69
|
"team-project-successor.mjs",
|
|
70
70
|
"team-project.mjs",
|
|
71
|
+
"usage-mode.mjs",
|
|
71
72
|
"verification-envelope.mjs",
|
|
72
73
|
"verification-envelope-check.mjs",
|
|
73
74
|
"verification-receipt-gate.mjs",
|
|
@@ -101,6 +102,8 @@ const CORE_FILES = [
|
|
|
101
102
|
"schemas/owner-deployment-decision-v1.schema.json",
|
|
102
103
|
"schemas/profile-apply-receipt-v1.schema.json",
|
|
103
104
|
"schemas/profile-detection-v2.schema.json",
|
|
105
|
+
"schemas/profile-reconcile-plan-v1.schema.json",
|
|
106
|
+
"schemas/profile-reconcile-receipt-v1.schema.json",
|
|
104
107
|
"schemas/project-config.schema.json",
|
|
105
108
|
"schemas/project-config-v2.schema.json",
|
|
106
109
|
"schemas/profiles.schema.json",
|
|
@@ -125,6 +128,7 @@ const CORE_FILES = [
|
|
|
125
128
|
"schemas/team-project-event-v1.schema.json",
|
|
126
129
|
"schemas/team-project-successor-receipt-v1.schema.json",
|
|
127
130
|
"schemas/team-project-status-v1.schema.json",
|
|
131
|
+
"schemas/usage-mode-recommendation-v1.schema.json",
|
|
128
132
|
"schemas/verification-envelope-v1.schema.json",
|
|
129
133
|
"schemas/verification-finalize-attempt-v1.schema.json",
|
|
130
134
|
"schemas/verification-preflight-v1.schema.json",
|
|
@@ -40,7 +40,7 @@ function closeoutDecisionCounts(inspected) {
|
|
|
40
40
|
|
|
41
41
|
export function buildInspectSummary(
|
|
42
42
|
inspected,
|
|
43
|
-
{ maintenanceNextAction = null } = {},
|
|
43
|
+
{ maintenanceNextAction = null, projectDriver = null } = {},
|
|
44
44
|
) {
|
|
45
45
|
const humanStatus = deriveHumanStatusFromInspectV1(inspected);
|
|
46
46
|
const focus = selectedFocus(inspected);
|
|
@@ -80,6 +80,21 @@ export function buildInspectSummary(
|
|
|
80
80
|
...(inspected.controlIssues ?? []),
|
|
81
81
|
...(selectedResource?.blockers ?? []),
|
|
82
82
|
].filter((reason, index, all) => all.indexOf(reason) === index);
|
|
83
|
+
const projectGapCode = projectDriver === null
|
|
84
|
+
? null
|
|
85
|
+
: projectDriver.dominantGap.id === null
|
|
86
|
+
? projectDriver.dominantGap.kind
|
|
87
|
+
: `${projectDriver.dominantGap.kind}:${projectDriver.dominantGap.id}`;
|
|
88
|
+
const projectDriverSummary = projectDriver === null
|
|
89
|
+
? null
|
|
90
|
+
: {
|
|
91
|
+
projectId: projectDriver.projectId,
|
|
92
|
+
overall: projectDriver.overall,
|
|
93
|
+
headline: projectDriver.headline,
|
|
94
|
+
dominantGap: structuredClone(projectDriver.dominantGap),
|
|
95
|
+
nextAction: projectDriver.nextAction,
|
|
96
|
+
nextActorId: projectDriver.nextActorId,
|
|
97
|
+
};
|
|
83
98
|
return {
|
|
84
99
|
schemaVersion: "OwlCodaRunKitInspectSummaryV1",
|
|
85
100
|
currentExecution: {
|
|
@@ -139,13 +154,22 @@ export function buildInspectSummary(
|
|
|
139
154
|
resources: [...(selectedResource?.resources ?? [])],
|
|
140
155
|
},
|
|
141
156
|
dominantGap: {
|
|
142
|
-
code: humanStatus.nextAllowedAction,
|
|
143
|
-
reasons:
|
|
157
|
+
code: projectGapCode ?? humanStatus.nextAllowedAction,
|
|
158
|
+
reasons: projectDriver === null
|
|
159
|
+
? [...reasons]
|
|
160
|
+
: [projectDriver.dominantGap.reason].filter(Boolean),
|
|
144
161
|
},
|
|
162
|
+
...(projectDriverSummary === null ? {} : { projectDriver: projectDriverSummary }),
|
|
145
163
|
lifecycleNextAction: inspected.recovery.nextAllowedAction,
|
|
146
164
|
maintenanceNextAction,
|
|
147
165
|
optionalReviewAction: independentHistory ? "inspect_closed_history" : null,
|
|
148
166
|
nextAllowedAction: humanStatus.nextAllowedAction,
|
|
167
|
+
...(projectDriver === null
|
|
168
|
+
? {}
|
|
169
|
+
: {
|
|
170
|
+
recommendedNextAction:
|
|
171
|
+
projectDriver.nextAction ?? humanStatus.nextAllowedAction,
|
|
172
|
+
}),
|
|
149
173
|
authorizationGranted: false,
|
|
150
174
|
gitAuthorization: false,
|
|
151
175
|
releaseAuthorization: false,
|
|
@@ -192,8 +216,17 @@ function summaryLines(summary, humanStatus) {
|
|
|
192
216
|
? `$${summary.resourcePreflight.estimate.cost.valueUsd}`
|
|
193
217
|
: "unknown"}`,
|
|
194
218
|
`Receipt reuse: ${summary.resourcePreflight.receiptReuse.appliedCount}/${summary.resourcePreflight.receiptReuse.reusableCount}`,
|
|
195
|
-
|
|
196
|
-
|
|
219
|
+
...(summary.projectDriver
|
|
220
|
+
? [
|
|
221
|
+
`Project dominant gap: ${summary.dominantGap.code}`,
|
|
222
|
+
`Recommended next action: ${value(summary.recommendedNextAction)}`,
|
|
223
|
+
`Execution state: ${summary.currentExecution.state}`,
|
|
224
|
+
`Execution next allowed action: ${summary.nextAllowedAction}`,
|
|
225
|
+
]
|
|
226
|
+
: [
|
|
227
|
+
`Dominant gap: ${summary.dominantGap.code}`,
|
|
228
|
+
`Next allowed action: ${summary.nextAllowedAction}`,
|
|
229
|
+
]),
|
|
197
230
|
...(summary.maintenanceNextAction
|
|
198
231
|
? [`Maintenance: ${summary.maintenanceNextAction}`]
|
|
199
232
|
: []),
|
|
@@ -16,6 +16,7 @@ import path from "node:path";
|
|
|
16
16
|
import { pathToFileURL } from "node:url";
|
|
17
17
|
|
|
18
18
|
import {
|
|
19
|
+
currentCoreIdentity,
|
|
19
20
|
initializeProjectRunKit,
|
|
20
21
|
isDirectExecution,
|
|
21
22
|
validateProjectConfigV2,
|
|
@@ -193,6 +194,64 @@ export async function inspectInstalledSkill({ targetRoot } = {}) {
|
|
|
193
194
|
};
|
|
194
195
|
}
|
|
195
196
|
|
|
197
|
+
export async function inspectInstalledSkillCompatibility({ targetRoot } = {}) {
|
|
198
|
+
const integrity = await inspectInstalledSkill({ targetRoot });
|
|
199
|
+
if (!integrity.valid) {
|
|
200
|
+
return {
|
|
201
|
+
...integrity,
|
|
202
|
+
integrityValid: false,
|
|
203
|
+
authorizationGranted: false,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const expectedCore = currentCoreIdentity();
|
|
208
|
+
let installedSkillVersion = null;
|
|
209
|
+
let installedConfigCore = null;
|
|
210
|
+
const issues = [];
|
|
211
|
+
try {
|
|
212
|
+
const skillText = await readFile(path.join(targetRoot, "SKILL.md"), "utf8");
|
|
213
|
+
installedSkillVersion = skillText.match(/standalone `owlrunkit@(\d+\.\d+\.\d+)`/u)?.[1] ?? null;
|
|
214
|
+
installedConfigCore = JSON.parse(await readFile(
|
|
215
|
+
path.join(targetRoot, "assets/templates/config.json"),
|
|
216
|
+
"utf8",
|
|
217
|
+
)).core ?? null;
|
|
218
|
+
} catch {
|
|
219
|
+
issues.push("installed_skill_version_declaration_invalid");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const installedConfigCoreVersion = installedConfigCore?.coreVersion ?? null;
|
|
223
|
+
if (installedSkillVersion === null || installedConfigCoreVersion === null) {
|
|
224
|
+
issues.push("installed_skill_core_version_declaration_missing");
|
|
225
|
+
} else if (
|
|
226
|
+
installedSkillVersion !== expectedCore.coreVersion
|
|
227
|
+
|| installedConfigCoreVersion !== expectedCore.coreVersion
|
|
228
|
+
) {
|
|
229
|
+
issues.push(
|
|
230
|
+
`installed_skill_core_version_mismatch:expected_${expectedCore.coreVersion}`
|
|
231
|
+
+ `:skill_${installedSkillVersion}:config_${installedConfigCoreVersion}`,
|
|
232
|
+
);
|
|
233
|
+
} else if (!sameCoreIdentity(installedConfigCore, expectedCore)) {
|
|
234
|
+
issues.push(
|
|
235
|
+
`installed_skill_core_identity_mismatch:expected_${expectedCore.coreManifestSha256}`
|
|
236
|
+
+ `:config_${installedConfigCore?.coreManifestSha256 ?? "missing"}`,
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
...integrity,
|
|
242
|
+
status: issues.length === 0 ? "valid" : "version_mismatch",
|
|
243
|
+
valid: issues.length === 0,
|
|
244
|
+
integrityValid: true,
|
|
245
|
+
expectedCoreVersion: expectedCore.coreVersion,
|
|
246
|
+
expectedCore,
|
|
247
|
+
installedSkillVersion,
|
|
248
|
+
installedConfigCoreVersion,
|
|
249
|
+
installedConfigCore,
|
|
250
|
+
issues,
|
|
251
|
+
authorizationGranted: false,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
196
255
|
export async function inspectFleetForSkillChange({
|
|
197
256
|
workspaceRoots = null,
|
|
198
257
|
fleetManifestPath = null,
|
|
@@ -731,7 +790,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
731
790
|
return { exitCode: 0, ...output };
|
|
732
791
|
}
|
|
733
792
|
if (command === "inspect") {
|
|
734
|
-
const output = await
|
|
793
|
+
const output = await inspectInstalledSkillCompatibility({ targetRoot: options.target });
|
|
735
794
|
return { exitCode: output.valid ? 0 : 2, ...output };
|
|
736
795
|
}
|
|
737
796
|
if (command === "restore") {
|
|
@@ -190,11 +190,16 @@ function parseJsonStrict(text) {
|
|
|
190
190
|
export function resolveBoundedWorkspaceRoot(workspaceRoot) {
|
|
191
191
|
const requested = path.resolve(workspaceRoot);
|
|
192
192
|
const stat = lstatSync(requested);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
193
|
+
const resolved = realpathSync(requested);
|
|
194
|
+
if ((stat.isSymbolicLink() || resolved !== requested)
|
|
195
|
+
&& lstatSync(resolved).isDirectory()) {
|
|
196
|
+
const quoted = `'${resolved.replaceAll("'", `'"'"'`)}'`;
|
|
197
|
+
throw new Error(
|
|
198
|
+
"workspace root must be a real directory without symlink ancestors. "
|
|
199
|
+
+ `Retry with --workspace ${quoted}.`,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
if (!stat.isDirectory()) {
|
|
198
203
|
throw new Error("workspace root must be a real directory without symlink ancestors");
|
|
199
204
|
}
|
|
200
205
|
return requested;
|
|
@@ -317,28 +322,51 @@ export function readLocalInstallBinding({ workspaceRoot } = {}) {
|
|
|
317
322
|
};
|
|
318
323
|
}
|
|
319
324
|
|
|
325
|
+
function registryTransportFailure(error) {
|
|
326
|
+
const code = error?.cause?.code ?? error?.code ?? null;
|
|
327
|
+
if (error?.name === "TimeoutError"
|
|
328
|
+
|| error?.name === "AbortError"
|
|
329
|
+
|| code === "ETIMEDOUT") {
|
|
330
|
+
return { issueCode: "registry_timeout", retryable: true };
|
|
331
|
+
}
|
|
332
|
+
if (code === "ENOTFOUND" || code === "EAI_AGAIN") {
|
|
333
|
+
return { issueCode: "registry_dns_failed", retryable: true };
|
|
334
|
+
}
|
|
335
|
+
if (code === "ERR_INVALID_URL") {
|
|
336
|
+
return { issueCode: "registry_url_invalid", retryable: false };
|
|
337
|
+
}
|
|
338
|
+
return { issueCode: "registry_connection_failed", retryable: true };
|
|
339
|
+
}
|
|
340
|
+
|
|
320
341
|
export async function readRegistryExact({
|
|
321
342
|
registryUrl,
|
|
322
343
|
packageName = PACKAGE_NAME,
|
|
323
344
|
version = EXPECTED_VERSION,
|
|
324
345
|
timeoutMs = 1_500,
|
|
346
|
+
fetchImpl = fetch,
|
|
325
347
|
} = {}) {
|
|
326
348
|
const base = registryUrl.replace(/\/+$/, "");
|
|
327
349
|
const url = `${base}/${encodeURIComponent(packageName)}/${encodeURIComponent(version)}`;
|
|
328
350
|
let response;
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
351
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
352
|
+
try {
|
|
353
|
+
response = await fetchImpl(url, {
|
|
354
|
+
headers: {
|
|
355
|
+
accept: "application/json",
|
|
356
|
+
},
|
|
357
|
+
redirect: "manual",
|
|
358
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
359
|
+
});
|
|
360
|
+
break;
|
|
361
|
+
} catch (error) {
|
|
362
|
+
const failure = registryTransportFailure(error);
|
|
363
|
+
if (!failure.retryable || attempt === 1) {
|
|
364
|
+
return {
|
|
365
|
+
status: "unavailable",
|
|
366
|
+
issueCodes: [failure.issueCode],
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
}
|
|
342
370
|
}
|
|
343
371
|
if (response.status === 401 || response.status === 403) {
|
|
344
372
|
return {
|
|
@@ -583,6 +611,24 @@ function profilesCheck(root) {
|
|
|
583
611
|
}
|
|
584
612
|
}
|
|
585
613
|
|
|
614
|
+
async function doctorProfilesCheck(root) {
|
|
615
|
+
const launcherCheck = profilesCheck(root);
|
|
616
|
+
if (launcherCheck.status === "missing") return launcherCheck;
|
|
617
|
+
const { validateProfiles } = await import("./profile-onboarding.mjs");
|
|
618
|
+
const validation = validateProfiles({ workspaceRoot: root });
|
|
619
|
+
const issues = [...new Set([
|
|
620
|
+
...(launcherCheck.launcherIssues ?? []),
|
|
621
|
+
...validation.issues,
|
|
622
|
+
])].sort();
|
|
623
|
+
return {
|
|
624
|
+
...launcherCheck,
|
|
625
|
+
status: issues.length === 0 ? "ok" : "invalid",
|
|
626
|
+
validationStatus: validation.status,
|
|
627
|
+
validationIssues: validation.issues,
|
|
628
|
+
...(issues.length > 0 ? { issues } : {}),
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
|
|
586
632
|
export function readProfileLauncherReadiness({ workspaceRoot } = {}) {
|
|
587
633
|
const profiles = profilesCheck(resolveBoundedWorkspaceRoot(workspaceRoot));
|
|
588
634
|
const launcherWarnings = profiles.launcherWarnings ?? [];
|
|
@@ -646,7 +692,7 @@ export async function runDoctor({
|
|
|
646
692
|
});
|
|
647
693
|
const nodeOk = Number(process.versions.node.split(".")[0]) >= 20;
|
|
648
694
|
const config = configCheck(root);
|
|
649
|
-
const profiles =
|
|
695
|
+
const profiles = await doctorProfilesCheck(root);
|
|
650
696
|
const adoption = adoptionCheck(root, installed);
|
|
651
697
|
const runtimeIsolation = runtimeIsolationCheck(root);
|
|
652
698
|
const checks = {
|