owlrunkit 0.13.0 → 0.15.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/CHANGELOG.md +41 -0
- package/README.md +66 -4
- package/docs/architecture/OWLCODA_RUN_KIT_CONTRACT_V0_2.md +79 -0
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +163 -2
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +1 -1
- package/package.json +1 -1
- package/packages/attest/distribution-manifest-v1.json +10 -0
- package/packages/attest/src/quick.mjs +10 -0
- package/registry-release-policy-v1.json +1 -1
- package/sbom.spdx.json +5 -5
- package/scripts/runkit-contract/core-contract.mjs +145 -4
- package/scripts/runkit-contract/install-codex-skill.mjs +505 -11
- package/scripts/runkit-contract/lifecycle-orchestration.mjs +119 -4
- package/scripts/runkit-contract/onboarding-doctor.mjs +805 -0
- package/scripts/runkit-contract/profile-onboarding.mjs +510 -0
- package/scripts/runkit-contract/registry-adoption-gate.mjs +121 -7
- package/scripts/runkit-contract/registry-adoption.mjs +198 -0
- package/scripts/runkit-contract/resume-execution.mjs +8 -2
- package/scripts/runkit-contract/runkit-cli.mjs +216 -6
- package/scripts/runkit-contract/schemas/adoption-evidence-v1.schema.json +63 -0
- package/scripts/runkit-contract/schemas/verification-finalize-attempt-v1.schema.json +52 -0
- package/scripts/runkit-contract/schemas/verification-preflight-v1.schema.json +52 -0
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
lstatSync,
|
|
4
|
+
readdirSync,
|
|
5
|
+
realpathSync,
|
|
6
|
+
} from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
readWorkspaceJsonBounded,
|
|
11
|
+
resolveBoundedWorkspaceRoot,
|
|
12
|
+
} from "./onboarding-doctor.mjs";
|
|
13
|
+
import { resolveProfileImpactDetailed } from "./profile-impact.mjs";
|
|
14
|
+
import {
|
|
15
|
+
safeIdentifier,
|
|
16
|
+
safeRelativePath,
|
|
17
|
+
} from "./provenance-common.mjs";
|
|
18
|
+
|
|
19
|
+
function resolveRegularExecutable(name) {
|
|
20
|
+
const pathEntries = (process.env.PATH ?? "").split(path.delimiter);
|
|
21
|
+
const suffixes = process.platform === "win32"
|
|
22
|
+
? (process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";")
|
|
23
|
+
: [""];
|
|
24
|
+
for (const directory of pathEntries) {
|
|
25
|
+
for (const suffix of suffixes) {
|
|
26
|
+
const candidate = path.resolve(directory || ".", `${name}${suffix}`);
|
|
27
|
+
try {
|
|
28
|
+
const resolved = realpathSync(candidate);
|
|
29
|
+
const stat = lstatSync(resolved);
|
|
30
|
+
if (stat.isFile() && !stat.isSymbolicLink()) return resolved;
|
|
31
|
+
} catch {
|
|
32
|
+
// Continue through PATH without treating a missing or unsafe entry as authority.
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function command(id, script, executable) {
|
|
40
|
+
return {
|
|
41
|
+
id,
|
|
42
|
+
cwd: ".",
|
|
43
|
+
executable,
|
|
44
|
+
argv: ["run", script],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function workspaceCommand(id, workspacePath, script, executable) {
|
|
49
|
+
return {
|
|
50
|
+
id,
|
|
51
|
+
cwd: ".",
|
|
52
|
+
executable,
|
|
53
|
+
argv: ["run", "--workspace", workspacePath, script],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function detectedScripts(manifest) {
|
|
58
|
+
return Object.keys(manifest.scripts ?? {}).sort();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function workspacePatterns(manifest) {
|
|
62
|
+
if (Array.isArray(manifest.workspaces)) return manifest.workspaces;
|
|
63
|
+
if (
|
|
64
|
+
manifest.workspaces
|
|
65
|
+
&& typeof manifest.workspaces === "object"
|
|
66
|
+
&& Array.isArray(manifest.workspaces.packages)
|
|
67
|
+
) {
|
|
68
|
+
return manifest.workspaces.packages;
|
|
69
|
+
}
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function safeWorkspacePattern(value) {
|
|
74
|
+
if (
|
|
75
|
+
typeof value !== "string"
|
|
76
|
+
|| value.length === 0
|
|
77
|
+
|| path.isAbsolute(value)
|
|
78
|
+
|| value.includes("\\")
|
|
79
|
+
|| value.includes("\0")
|
|
80
|
+
) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const segments = value.split("/");
|
|
84
|
+
if (segments.some((segment) => segment.length === 0 || segment === "." || segment === "..")) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const wildcardCount = [...value].filter((character) => character === "*").length;
|
|
88
|
+
if (wildcardCount === 0) return { kind: "literal", path: value };
|
|
89
|
+
if (wildcardCount === 1 && value.endsWith("/*")) {
|
|
90
|
+
return { kind: "children", path: value.slice(0, -2) };
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function discoverWorkspaces(root, manifest) {
|
|
96
|
+
const warnings = [];
|
|
97
|
+
const paths = new Set();
|
|
98
|
+
for (const rawPattern of workspacePatterns(manifest)) {
|
|
99
|
+
const pattern = safeWorkspacePattern(rawPattern);
|
|
100
|
+
if (pattern === null) {
|
|
101
|
+
warnings.push(`unsupported_workspace_pattern:${String(rawPattern)}`);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (pattern.kind === "literal") {
|
|
105
|
+
paths.add(pattern.path);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const parent = path.join(root, pattern.path);
|
|
109
|
+
try {
|
|
110
|
+
const parentStat = lstatSync(parent);
|
|
111
|
+
if (
|
|
112
|
+
parentStat.isSymbolicLink()
|
|
113
|
+
|| !parentStat.isDirectory()
|
|
114
|
+
|| realpathSync(parent) !== path.resolve(parent)
|
|
115
|
+
) {
|
|
116
|
+
warnings.push(`unsafe_workspace_parent:${pattern.path}`);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
for (const entry of readdirSync(parent, { withFileTypes: true })
|
|
120
|
+
.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
121
|
+
if (entry.isDirectory() && !entry.isSymbolicLink()) {
|
|
122
|
+
paths.add(`${pattern.path}/${entry.name}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch {
|
|
126
|
+
// A declared workspace pattern may legitimately have no current matches.
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const workspaces = [];
|
|
131
|
+
for (const workspacePath of [...paths].sort()) {
|
|
132
|
+
try {
|
|
133
|
+
const workspaceManifest = readWorkspaceJsonBounded(
|
|
134
|
+
root,
|
|
135
|
+
path.join(workspacePath, "package.json"),
|
|
136
|
+
);
|
|
137
|
+
workspaces.push({
|
|
138
|
+
name: typeof workspaceManifest.name === "string"
|
|
139
|
+
? workspaceManifest.name
|
|
140
|
+
: workspacePath,
|
|
141
|
+
path: workspacePath,
|
|
142
|
+
scripts: detectedScripts(workspaceManifest),
|
|
143
|
+
});
|
|
144
|
+
} catch {
|
|
145
|
+
warnings.push(`workspace_manifest_unreadable:${workspacePath}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
workspaces,
|
|
150
|
+
warnings: [...new Set(warnings)].sort(),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function profileIdForWorkspace(workspacePath) {
|
|
155
|
+
return `workspace-${workspacePath.replace(/[^A-Za-z0-9._-]+/g, "-")}`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function workspaceProfiles(workspaces, npmExecutable) {
|
|
159
|
+
return workspaces
|
|
160
|
+
.map((workspace) => {
|
|
161
|
+
const qualityScripts = workspace.scripts.filter((script) =>
|
|
162
|
+
["build", "e2e", "lint", "test", "typecheck"].includes(script));
|
|
163
|
+
if (qualityScripts.length === 0) return null;
|
|
164
|
+
const profileId = profileIdForWorkspace(workspace.path);
|
|
165
|
+
return {
|
|
166
|
+
id: profileId,
|
|
167
|
+
paths: [`${workspace.path}/**`],
|
|
168
|
+
role: "primary",
|
|
169
|
+
primary: false,
|
|
170
|
+
requiresProfileIds: [],
|
|
171
|
+
commands: npmExecutable
|
|
172
|
+
? qualityScripts.map((script) => workspaceCommand(
|
|
173
|
+
`npm-${profileId}-${script}`,
|
|
174
|
+
workspace.path,
|
|
175
|
+
script,
|
|
176
|
+
npmExecutable,
|
|
177
|
+
))
|
|
178
|
+
: [],
|
|
179
|
+
};
|
|
180
|
+
})
|
|
181
|
+
.filter(Boolean);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function profileCandidates(root, manifest, npmExecutable, workspaces) {
|
|
185
|
+
const scripts = new Set(detectedScripts(manifest));
|
|
186
|
+
const candidates = [];
|
|
187
|
+
if (
|
|
188
|
+
existsSync(path.join(root, "prisma"))
|
|
189
|
+
|| [...scripts].some((script) => /^(db:|prisma)/.test(script))
|
|
190
|
+
) {
|
|
191
|
+
const commands = [...scripts]
|
|
192
|
+
.filter((script) => /^(db:|prisma)/.test(script))
|
|
193
|
+
.map((script) => npmExecutable
|
|
194
|
+
? command(`npm-${script.replaceAll(":", "-")}`, script, npmExecutable)
|
|
195
|
+
: null)
|
|
196
|
+
.filter(Boolean);
|
|
197
|
+
candidates.push({
|
|
198
|
+
id: "database",
|
|
199
|
+
paths: ["prisma/**"],
|
|
200
|
+
role: "primary",
|
|
201
|
+
primary: false,
|
|
202
|
+
requiresProfileIds: [],
|
|
203
|
+
commands,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
if (
|
|
207
|
+
existsSync(path.join(root, "package.json"))
|
|
208
|
+
&& [...scripts].some((script) => ["build", "lint", "test", "typecheck"].includes(script))
|
|
209
|
+
) {
|
|
210
|
+
const commands = ["build", "lint", "test", "typecheck"]
|
|
211
|
+
.filter((script) => scripts.has(script))
|
|
212
|
+
.map((script) => npmExecutable ? command(`npm-${script}`, script, npmExecutable) : null)
|
|
213
|
+
.filter(Boolean);
|
|
214
|
+
candidates.push({
|
|
215
|
+
id: "node-quality",
|
|
216
|
+
paths: ["src/**", "tests/**"],
|
|
217
|
+
role: "primary",
|
|
218
|
+
primary: false,
|
|
219
|
+
requiresProfileIds: [],
|
|
220
|
+
commands,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
if (
|
|
224
|
+
existsSync(path.join(root, "playwright.config.ts"))
|
|
225
|
+
|| existsSync(path.join(root, "playwright.config.js"))
|
|
226
|
+
|| scripts.has("e2e")
|
|
227
|
+
) {
|
|
228
|
+
const commands = scripts.has("e2e") && npmExecutable
|
|
229
|
+
? [command("npm-e2e", "e2e", npmExecutable)]
|
|
230
|
+
: [];
|
|
231
|
+
candidates.push({
|
|
232
|
+
id: "web-e2e",
|
|
233
|
+
paths: ["playwright.config.ts", "tests/e2e/**"],
|
|
234
|
+
role: "primary",
|
|
235
|
+
primary: false,
|
|
236
|
+
requiresProfileIds: [],
|
|
237
|
+
commands,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
candidates.push(...workspaceProfiles(workspaces, npmExecutable));
|
|
241
|
+
return candidates.sort((left, right) => left.id.localeCompare(right.id));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function detectProfiles({ workspaceRoot } = {}) {
|
|
245
|
+
const root = resolveBoundedWorkspaceRoot(workspaceRoot);
|
|
246
|
+
const manifest = readWorkspaceJsonBounded(root, "package.json");
|
|
247
|
+
const npmExecutable = resolveRegularExecutable("npm");
|
|
248
|
+
const discovered = discoverWorkspaces(root, manifest);
|
|
249
|
+
const candidates = profileCandidates(root, manifest, npmExecutable, discovered.workspaces);
|
|
250
|
+
const primaryCandidates = candidates.filter((profile) => profile.role === "primary");
|
|
251
|
+
const warnings = primaryCandidates.length > 1
|
|
252
|
+
? [`ambiguous_primary_profile:${primaryCandidates.map((profile) => profile.id).join(",")}`]
|
|
253
|
+
: [];
|
|
254
|
+
warnings.push(...discovered.warnings);
|
|
255
|
+
if (
|
|
256
|
+
npmExecutable === null
|
|
257
|
+
&& (
|
|
258
|
+
detectedScripts(manifest).some((script) =>
|
|
259
|
+
/^(build|db:|e2e|lint|prisma|test|typecheck)/.test(script))
|
|
260
|
+
|| discovered.workspaces.some((workspace) => workspace.scripts.some((script) =>
|
|
261
|
+
/^(build|e2e|lint|test|typecheck)$/.test(script)))
|
|
262
|
+
)
|
|
263
|
+
) {
|
|
264
|
+
warnings.push("npm_regular_executable_unavailable");
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
schemaVersion: "OwlCodaRunKitProfileDetectionV1",
|
|
268
|
+
status: "profiles_detected",
|
|
269
|
+
dryRun: true,
|
|
270
|
+
writesPerformed: 0,
|
|
271
|
+
candidates,
|
|
272
|
+
primaryProfileId: primaryCandidates.length === 1 ? primaryCandidates[0].id : null,
|
|
273
|
+
warnings,
|
|
274
|
+
detectedScripts: detectedScripts(manifest),
|
|
275
|
+
detectedWorkspaces: discovered.workspaces,
|
|
276
|
+
authorizationGranted: false,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function exactKeys(value, allowed) {
|
|
281
|
+
return value
|
|
282
|
+
&& typeof value === "object"
|
|
283
|
+
&& !Array.isArray(value)
|
|
284
|
+
&& Object.keys(value).every((key) => allowed.includes(key));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function representativePath(rule) {
|
|
288
|
+
return rule.endsWith("/**")
|
|
289
|
+
? `${rule.slice(0, -3)}/__owlrunkit_profile_surface__`
|
|
290
|
+
: rule;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function validateProfileDocument(config) {
|
|
294
|
+
const issues = [];
|
|
295
|
+
if (
|
|
296
|
+
!exactKeys(config, ["schemaVersion", "profiles"])
|
|
297
|
+
|| config.schemaVersion !== "OwlCodaRunKitProfilesV1"
|
|
298
|
+
|| !Array.isArray(config.profiles)
|
|
299
|
+
|| config.profiles.length === 0
|
|
300
|
+
) {
|
|
301
|
+
return ["profiles_document_invalid"];
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const ids = new Set();
|
|
305
|
+
const commandIds = new Set();
|
|
306
|
+
for (const profile of config.profiles) {
|
|
307
|
+
if (!exactKeys(profile, [
|
|
308
|
+
"commands",
|
|
309
|
+
"id",
|
|
310
|
+
"paths",
|
|
311
|
+
"primary",
|
|
312
|
+
"requiresProfileIds",
|
|
313
|
+
"role",
|
|
314
|
+
])) {
|
|
315
|
+
issues.push("profile_shape_invalid");
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (typeof profile.id !== "string" || profile.id.length === 0) {
|
|
319
|
+
issues.push("profile_id_invalid");
|
|
320
|
+
} else if (ids.has(profile.id)) {
|
|
321
|
+
issues.push(`duplicate_profile_id:${profile.id}`);
|
|
322
|
+
} else {
|
|
323
|
+
ids.add(profile.id);
|
|
324
|
+
}
|
|
325
|
+
if (!Array.isArray(profile.paths) || profile.paths.length === 0) {
|
|
326
|
+
issues.push(`profile_paths_missing:${profile.id ?? "unknown"}`);
|
|
327
|
+
}
|
|
328
|
+
if (profile.role !== undefined && !["primary", "supporting"].includes(profile.role)) {
|
|
329
|
+
issues.push(`profile_role_invalid:${profile.id ?? "unknown"}`);
|
|
330
|
+
}
|
|
331
|
+
if (profile.primary !== undefined && typeof profile.primary !== "boolean") {
|
|
332
|
+
issues.push(`profile_primary_invalid:${profile.id ?? "unknown"}`);
|
|
333
|
+
}
|
|
334
|
+
if (
|
|
335
|
+
profile.requiresProfileIds !== undefined
|
|
336
|
+
&& (
|
|
337
|
+
!Array.isArray(profile.requiresProfileIds)
|
|
338
|
+
|| profile.requiresProfileIds.some((id) => typeof id !== "string" || id.length === 0)
|
|
339
|
+
|| new Set(profile.requiresProfileIds).size !== profile.requiresProfileIds.length
|
|
340
|
+
)
|
|
341
|
+
) {
|
|
342
|
+
issues.push(`profile_requirements_invalid:${profile.id ?? "unknown"}`);
|
|
343
|
+
}
|
|
344
|
+
if (profile.commands !== undefined) {
|
|
345
|
+
if (!Array.isArray(profile.commands)) {
|
|
346
|
+
issues.push(`profile_commands_invalid:${profile.id ?? "unknown"}`);
|
|
347
|
+
} else {
|
|
348
|
+
for (const entry of profile.commands) {
|
|
349
|
+
if (
|
|
350
|
+
!exactKeys(entry, ["argv", "cwd", "executable", "id"])
|
|
351
|
+
|| typeof entry.id !== "string"
|
|
352
|
+
|| entry.id.length === 0
|
|
353
|
+
|| typeof entry.cwd !== "string"
|
|
354
|
+
|| entry.cwd.length === 0
|
|
355
|
+
|| typeof entry.executable !== "string"
|
|
356
|
+
|| entry.executable.length === 0
|
|
357
|
+
|| !Array.isArray(entry.argv)
|
|
358
|
+
|| entry.argv.some((argument) => typeof argument !== "string" || argument.length === 0)
|
|
359
|
+
) {
|
|
360
|
+
issues.push(`profile_command_invalid:${profile.id ?? "unknown"}`);
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
try {
|
|
364
|
+
safeIdentifier(entry.id, "profile command id");
|
|
365
|
+
} catch {
|
|
366
|
+
issues.push(`profile_command_id_invalid:${profile.id}:${entry.id}`);
|
|
367
|
+
}
|
|
368
|
+
try {
|
|
369
|
+
safeRelativePath(entry.cwd, "profile command cwd", { allowDot: true });
|
|
370
|
+
} catch {
|
|
371
|
+
issues.push(`profile_command_cwd_invalid:${profile.id}:${entry.id}`);
|
|
372
|
+
}
|
|
373
|
+
if (commandIds.has(entry.id)) {
|
|
374
|
+
issues.push(`duplicate_profile_command_id:${entry.id}`);
|
|
375
|
+
} else {
|
|
376
|
+
commandIds.add(entry.id);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (issues.length > 0) return [...new Set(issues)].sort();
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
const changedPaths = config.profiles.flatMap((profile) =>
|
|
386
|
+
profile.paths.map(representativePath));
|
|
387
|
+
resolveProfileImpactDetailed({ changedPaths, profiles: config.profiles });
|
|
388
|
+
} catch (error) {
|
|
389
|
+
issues.push(error instanceof Error ? error.message : String(error));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
for (const profile of config.profiles) {
|
|
393
|
+
for (const requiredId of profile.requiresProfileIds ?? []) {
|
|
394
|
+
if (!ids.has(requiredId)) {
|
|
395
|
+
issues.push(`required_profile_missing:${profile.id}:${requiredId}`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
const actionable = config.profiles.filter((profile) =>
|
|
400
|
+
(profile.role ?? "primary") === "primary"
|
|
401
|
+
&& Array.isArray(profile.commands)
|
|
402
|
+
&& profile.commands.length > 0);
|
|
403
|
+
if (actionable.length === 0) issues.push("actionable_primary_profile_missing");
|
|
404
|
+
const explicitPrimaryIds = actionable
|
|
405
|
+
.filter((profile) => profile.primary === true)
|
|
406
|
+
.map((profile) => profile.id)
|
|
407
|
+
.sort();
|
|
408
|
+
if (explicitPrimaryIds.length > 1) {
|
|
409
|
+
issues.push(`ambiguous_primary_profile:${explicitPrimaryIds.join(",")}`);
|
|
410
|
+
} else if (explicitPrimaryIds.length === 0 && actionable.length > 1) {
|
|
411
|
+
issues.push(`ambiguous_primary_profile:${actionable.map((profile) => profile.id).sort().join(",")}`);
|
|
412
|
+
}
|
|
413
|
+
return [...new Set(issues)].sort();
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export function validateProfiles({ workspaceRoot } = {}) {
|
|
417
|
+
const root = resolveBoundedWorkspaceRoot(workspaceRoot);
|
|
418
|
+
const issues = [];
|
|
419
|
+
let profileIds = [];
|
|
420
|
+
try {
|
|
421
|
+
const config = readWorkspaceJsonBounded(
|
|
422
|
+
root,
|
|
423
|
+
path.join(".owlcoda", "runkit", "profiles.json"),
|
|
424
|
+
);
|
|
425
|
+
profileIds = Array.isArray(config.profiles)
|
|
426
|
+
? config.profiles
|
|
427
|
+
.map((profile) => profile?.id)
|
|
428
|
+
.filter((id) => typeof id === "string")
|
|
429
|
+
.sort()
|
|
430
|
+
: [];
|
|
431
|
+
issues.push(...validateProfileDocument(config));
|
|
432
|
+
if (issues.length === 0) {
|
|
433
|
+
const detected = detectProfiles({ workspaceRoot: root });
|
|
434
|
+
const detectedPaths = detected.candidates.flatMap((profile) =>
|
|
435
|
+
profile.paths.map(representativePath));
|
|
436
|
+
if (detectedPaths.length > 0) {
|
|
437
|
+
const impact = resolveProfileImpactDetailed({
|
|
438
|
+
changedPaths: detectedPaths,
|
|
439
|
+
profiles: config.profiles,
|
|
440
|
+
});
|
|
441
|
+
issues.push(...impact.uncoveredPaths.map((value) =>
|
|
442
|
+
`uncovered_detected_surface:${value}`));
|
|
443
|
+
issues.push(...impact.warnings
|
|
444
|
+
.filter((warning) => warning.startsWith("supporting_only_match:"))
|
|
445
|
+
.map((warning) => `detected_surface_${warning}`));
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
} catch (error) {
|
|
449
|
+
issues.push(error instanceof Error ? error.message : String(error));
|
|
450
|
+
}
|
|
451
|
+
const valid = issues.length === 0;
|
|
452
|
+
return {
|
|
453
|
+
schemaVersion: "OwlCodaRunKitProfilesValidationV1",
|
|
454
|
+
status: valid ? "valid" : "invalid",
|
|
455
|
+
valid,
|
|
456
|
+
exitCode: valid ? 0 : 2,
|
|
457
|
+
profileIds,
|
|
458
|
+
issues: [...new Set(issues)].sort(),
|
|
459
|
+
authorizationGranted: false,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
export function resolveProfilesImpact({ workspaceRoot, changedPaths } = {}) {
|
|
464
|
+
const root = resolveBoundedWorkspaceRoot(workspaceRoot);
|
|
465
|
+
const config = readWorkspaceJsonBounded(
|
|
466
|
+
root,
|
|
467
|
+
path.join(".owlcoda", "runkit", "profiles.json"),
|
|
468
|
+
);
|
|
469
|
+
const documentIssues = validateProfileDocument(config);
|
|
470
|
+
if (documentIssues.length > 0) {
|
|
471
|
+
return {
|
|
472
|
+
schemaVersion: "OwlCodaRunKitProfileImpactV1",
|
|
473
|
+
status: "profile_impact_blocked",
|
|
474
|
+
valid: false,
|
|
475
|
+
exitCode: 2,
|
|
476
|
+
changedPaths: [...new Set(changedPaths)].sort(),
|
|
477
|
+
decision: "invalid_profiles",
|
|
478
|
+
primaryProfileId: null,
|
|
479
|
+
directProfileIds: [],
|
|
480
|
+
transitiveProfileIds: [],
|
|
481
|
+
supportingProfileIds: [],
|
|
482
|
+
selectedProfileIds: [],
|
|
483
|
+
uncoveredPaths: [],
|
|
484
|
+
warnings: [],
|
|
485
|
+
issues: documentIssues,
|
|
486
|
+
authorizationGranted: false,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
const impact = resolveProfileImpactDetailed({
|
|
490
|
+
changedPaths,
|
|
491
|
+
profiles: config.profiles,
|
|
492
|
+
});
|
|
493
|
+
const issues = [
|
|
494
|
+
...impact.uncoveredPaths.map((value) => `uncovered_changed_path:${value}`),
|
|
495
|
+
...impact.warnings.filter((warning) =>
|
|
496
|
+
warning.startsWith("supporting_only_match:")
|
|
497
|
+
|| warning.startsWith("ambiguous_primary_profile:")),
|
|
498
|
+
];
|
|
499
|
+
const valid = impact.decision === "targeted_profiles" && issues.length === 0;
|
|
500
|
+
return {
|
|
501
|
+
schemaVersion: "OwlCodaRunKitProfileImpactV1",
|
|
502
|
+
status: valid ? "profile_impact_resolved" : "profile_impact_blocked",
|
|
503
|
+
valid,
|
|
504
|
+
exitCode: valid ? 0 : 2,
|
|
505
|
+
changedPaths: [...new Set(changedPaths)].sort(),
|
|
506
|
+
...impact,
|
|
507
|
+
issues: [...new Set(issues)].sort(),
|
|
508
|
+
authorizationGranted: false,
|
|
509
|
+
};
|
|
510
|
+
}
|
|
@@ -21,6 +21,45 @@ const RELEASE_EVIDENCE_FIELDS = Object.freeze([
|
|
|
21
21
|
"version",
|
|
22
22
|
]);
|
|
23
23
|
|
|
24
|
+
const STORED_EVIDENCE_FIELDS = Object.freeze([
|
|
25
|
+
"authorizationGranted",
|
|
26
|
+
"authority",
|
|
27
|
+
"decision",
|
|
28
|
+
"exactSpec",
|
|
29
|
+
"installedBinding",
|
|
30
|
+
"packageName",
|
|
31
|
+
"registryRelease",
|
|
32
|
+
"schemaVersion",
|
|
33
|
+
"status",
|
|
34
|
+
"version",
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
const STORED_RELEASE_FIELDS = Object.freeze([
|
|
38
|
+
"integrity",
|
|
39
|
+
"packageName",
|
|
40
|
+
"registry",
|
|
41
|
+
"shasum",
|
|
42
|
+
"tarballUrl",
|
|
43
|
+
"version",
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
const STORED_INSTALL_FIELDS = Object.freeze([
|
|
47
|
+
"integrity",
|
|
48
|
+
"packageName",
|
|
49
|
+
"requestedSpec",
|
|
50
|
+
"resolved",
|
|
51
|
+
"version",
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
function isRecord(value) {
|
|
55
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function hasExactFields(value, fields) {
|
|
59
|
+
return isRecord(value)
|
|
60
|
+
&& Object.keys(value).sort().join("\n") === [...fields].sort().join("\n");
|
|
61
|
+
}
|
|
62
|
+
|
|
24
63
|
function decision(policy, issueCodes) {
|
|
25
64
|
return {
|
|
26
65
|
schemaVersion: "OwlCodaRunKitRegistryAdoptionDecisionV1",
|
|
@@ -32,12 +71,12 @@ function decision(policy, issueCodes) {
|
|
|
32
71
|
};
|
|
33
72
|
}
|
|
34
73
|
|
|
35
|
-
function
|
|
36
|
-
const tarballName =
|
|
37
|
-
return `${OFFICIAL_NPM_REGISTRY}/${
|
|
74
|
+
export function expectedRegistryTarballUrl(packageName, version) {
|
|
75
|
+
const tarballName = packageName.split("/").at(-1);
|
|
76
|
+
return `${OFFICIAL_NPM_REGISTRY}/${packageName}/-/${tarballName}-${version}.tgz`;
|
|
38
77
|
}
|
|
39
78
|
|
|
40
|
-
function
|
|
79
|
+
export function isCanonicalSha512Integrity(value) {
|
|
41
80
|
if (typeof value !== "string") return false;
|
|
42
81
|
const match = /^sha512-([A-Za-z0-9+/]+={0,2})$/.exec(value);
|
|
43
82
|
if (!match) return false;
|
|
@@ -45,13 +84,88 @@ function validSha512Integrity(value) {
|
|
|
45
84
|
return bytes.length === 64 && bytes.toString("base64") === match[1];
|
|
46
85
|
}
|
|
47
86
|
|
|
87
|
+
export function validateStoredRegistryAdoptionEvidence({
|
|
88
|
+
evidence,
|
|
89
|
+
packageName,
|
|
90
|
+
version,
|
|
91
|
+
localInstall,
|
|
92
|
+
}) {
|
|
93
|
+
const issues = [];
|
|
94
|
+
const exactSpec = `${packageName}@${version}`;
|
|
95
|
+
const expectedTarball = expectedRegistryTarballUrl(packageName, version);
|
|
96
|
+
if (
|
|
97
|
+
!hasExactFields(evidence, STORED_EVIDENCE_FIELDS)
|
|
98
|
+
|| evidence.schemaVersion !== "OwlCodaRunKitRegistryAdoptionEvidenceV1"
|
|
99
|
+
|| evidence.status !== "adopted"
|
|
100
|
+
|| evidence.decision !== "ELIGIBLE"
|
|
101
|
+
|| evidence.packageName !== packageName
|
|
102
|
+
|| evidence.version !== version
|
|
103
|
+
|| evidence.exactSpec !== exactSpec
|
|
104
|
+
|| evidence.authorizationGranted !== false
|
|
105
|
+
) {
|
|
106
|
+
issues.push("adoption_evidence_invalid");
|
|
107
|
+
}
|
|
108
|
+
const release = evidence?.registryRelease;
|
|
109
|
+
if (
|
|
110
|
+
!hasExactFields(release, STORED_RELEASE_FIELDS)
|
|
111
|
+
|| release.registry !== OFFICIAL_NPM_REGISTRY
|
|
112
|
+
|| release.packageName !== packageName
|
|
113
|
+
|| release.version !== version
|
|
114
|
+
|| !/^[0-9a-f]{40}$/i.test(release.shasum)
|
|
115
|
+
|| !isCanonicalSha512Integrity(release.integrity)
|
|
116
|
+
|| release.tarballUrl !== expectedTarball
|
|
117
|
+
) {
|
|
118
|
+
issues.push("adoption_registry_binding_invalid");
|
|
119
|
+
}
|
|
120
|
+
const installed = evidence?.installedBinding;
|
|
121
|
+
if (
|
|
122
|
+
!hasExactFields(installed, STORED_INSTALL_FIELDS)
|
|
123
|
+
|| installed.requestedSpec !== exactSpec
|
|
124
|
+
|| installed.packageName !== packageName
|
|
125
|
+
|| installed.version !== version
|
|
126
|
+
|| installed.integrity !== release?.integrity
|
|
127
|
+
|| installed.resolved !== expectedTarball
|
|
128
|
+
) {
|
|
129
|
+
issues.push("adoption_installed_binding_invalid");
|
|
130
|
+
}
|
|
131
|
+
if (
|
|
132
|
+
!hasExactFields(evidence?.authority, ["foreignProjectWrite", "git", "release"])
|
|
133
|
+
|| evidence.authority.git !== false
|
|
134
|
+
|| evidence.authority.release !== false
|
|
135
|
+
|| evidence.authority.foreignProjectWrite !== false
|
|
136
|
+
) {
|
|
137
|
+
issues.push("adoption_authority_invalid");
|
|
138
|
+
}
|
|
139
|
+
if (
|
|
140
|
+
localInstall !== undefined
|
|
141
|
+
&& (
|
|
142
|
+
localInstall.status !== "bound"
|
|
143
|
+
|| localInstall.requestedSpec !== exactSpec
|
|
144
|
+
|| localInstall.packageName !== packageName
|
|
145
|
+
|| localInstall.version !== version
|
|
146
|
+
|| localInstall.integrity !== release?.integrity
|
|
147
|
+
|| localInstall.resolved !== expectedTarball
|
|
148
|
+
)
|
|
149
|
+
) {
|
|
150
|
+
issues.push("adoption_local_install_drift");
|
|
151
|
+
}
|
|
152
|
+
const issueCodes = [...new Set(issues)].sort();
|
|
153
|
+
return {
|
|
154
|
+
valid: issueCodes.length === 0,
|
|
155
|
+
issueCodes,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
48
159
|
function validReleaseEvidence(policy, release) {
|
|
49
160
|
if (
|
|
50
161
|
policy.registry !== OFFICIAL_NPM_REGISTRY
|
|
51
162
|
|| Object.keys(release).sort().join("\n") !== RELEASE_EVIDENCE_FIELDS.join("\n")
|
|
52
|
-
|| !/^[0-9a-f]{40}
|
|
53
|
-
|| !
|
|
54
|
-
|| release.tarballUrl !==
|
|
163
|
+
|| !/^[0-9a-f]{40}$/i.test(release.shasum)
|
|
164
|
+
|| !isCanonicalSha512Integrity(release.integrity)
|
|
165
|
+
|| release.tarballUrl !== expectedRegistryTarballUrl(
|
|
166
|
+
policy.packageName,
|
|
167
|
+
policy.candidateVersion,
|
|
168
|
+
)
|
|
55
169
|
) {
|
|
56
170
|
return false;
|
|
57
171
|
}
|