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
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import {
|
|
3
3
|
existsSync,
|
|
4
|
+
lstatSync,
|
|
4
5
|
mkdirSync,
|
|
5
6
|
readFileSync,
|
|
7
|
+
readdirSync,
|
|
6
8
|
realpathSync,
|
|
7
9
|
renameSync,
|
|
8
10
|
rmSync,
|
|
@@ -12,7 +14,7 @@ import { isAbsolute, relative, resolve, win32 } from "node:path";
|
|
|
12
14
|
import { fileURLToPath } from "node:url";
|
|
13
15
|
|
|
14
16
|
export const CONTRACT_VERSION = "0.2";
|
|
15
|
-
export const CORE_VERSION = "0.
|
|
17
|
+
export const CORE_VERSION = "0.15.0";
|
|
16
18
|
export const RUNTIME_ROOT = ".owlcoda/runkit";
|
|
17
19
|
|
|
18
20
|
const CORE_FILES = [
|
|
@@ -26,7 +28,9 @@ const CORE_FILES = [
|
|
|
26
28
|
"lease-lifecycle.mjs",
|
|
27
29
|
"lifecycle-orchestration.mjs",
|
|
28
30
|
"offline-store.mjs",
|
|
31
|
+
"onboarding-doctor.mjs",
|
|
29
32
|
"profile-impact.mjs",
|
|
33
|
+
"profile-onboarding.mjs",
|
|
30
34
|
"provenance-common.mjs",
|
|
31
35
|
"quick-attest.mjs",
|
|
32
36
|
"quick-canonical.mjs",
|
|
@@ -36,6 +40,8 @@ const CORE_FILES = [
|
|
|
36
40
|
"quick-workspace-snapshot.mjs",
|
|
37
41
|
"ready-for-commit.mjs",
|
|
38
42
|
"receipt-lineage.mjs",
|
|
43
|
+
"registry-adoption-gate.mjs",
|
|
44
|
+
"registry-adoption.mjs",
|
|
39
45
|
"repair-execution.mjs",
|
|
40
46
|
"resume-execution.mjs",
|
|
41
47
|
"resource-preflight.mjs",
|
|
@@ -47,6 +53,7 @@ const CORE_FILES = [
|
|
|
47
53
|
"verification-plan.mjs",
|
|
48
54
|
"visual-smoke.mjs",
|
|
49
55
|
"schemas/closeout-receipt-v2.schema.json",
|
|
56
|
+
"schemas/adoption-evidence-v1.schema.json",
|
|
50
57
|
"schemas/coverage-adopt-request-v1.schema.json",
|
|
51
58
|
"schemas/core-artifact.schema.json",
|
|
52
59
|
"schemas/core-artifact-v2.schema.json",
|
|
@@ -67,6 +74,8 @@ const CORE_FILES = [
|
|
|
67
74
|
"schemas/visual-smoke-request-v1.schema.json",
|
|
68
75
|
"schemas/visual-smoke-result-v1.schema.json",
|
|
69
76
|
"schemas/verification-context-v1.schema.json",
|
|
77
|
+
"schemas/verification-finalize-attempt-v1.schema.json",
|
|
78
|
+
"schemas/verification-preflight-v1.schema.json",
|
|
70
79
|
"schemas/verification-plan-v1.schema.json",
|
|
71
80
|
"schemas/verification-receipt-v2.schema.json",
|
|
72
81
|
"schemas/verify-plan-request-v1.schema.json",
|
|
@@ -131,6 +140,22 @@ function migrationReceiptName(migration, fromCore, toCore) {
|
|
|
131
140
|
return `config-v02-core-refresh-${from}-to-${to}.json`;
|
|
132
141
|
}
|
|
133
142
|
|
|
143
|
+
function writeMigrationReceiptAppendOnly(receiptsRoot, baseName, receipt) {
|
|
144
|
+
const bytes = `${JSON.stringify(receipt, null, 2)}\n`;
|
|
145
|
+
const stem = baseName.slice(0, -".json".length);
|
|
146
|
+
for (let attempt = 1; ; attempt += 1) {
|
|
147
|
+
const receiptName = attempt === 1
|
|
148
|
+
? baseName
|
|
149
|
+
: `${stem}-attempt-${String(attempt).padStart(3, "0")}.json`;
|
|
150
|
+
try {
|
|
151
|
+
writeFileSync(resolve(receiptsRoot, receiptName), bytes, { flag: "wx" });
|
|
152
|
+
return receiptName;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
if (error?.code !== "EEXIST") throw error;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
134
159
|
function migrateProjectConfig({ runtimeRoot, configPath, config, migration }) {
|
|
135
160
|
validateCoreIdentity(config.core);
|
|
136
161
|
const next = projectConfig();
|
|
@@ -145,8 +170,11 @@ function migrateProjectConfig({ runtimeRoot, configPath, config, migration }) {
|
|
|
145
170
|
};
|
|
146
171
|
const receiptsRoot = resolve(runtimeRoot, "config-migration-receipts");
|
|
147
172
|
mkdirSync(receiptsRoot, { recursive: true });
|
|
148
|
-
const receiptName =
|
|
149
|
-
|
|
173
|
+
const receiptName = writeMigrationReceiptAppendOnly(
|
|
174
|
+
receiptsRoot,
|
|
175
|
+
migrationReceiptName(migration, config.core, next.core),
|
|
176
|
+
receipt,
|
|
177
|
+
);
|
|
150
178
|
atomicWriteJson(configPath, next);
|
|
151
179
|
return `${RUNTIME_ROOT}/config-migration-receipts/${receiptName}`;
|
|
152
180
|
}
|
|
@@ -253,6 +281,98 @@ export function validateProjectConfigV2(config) {
|
|
|
253
281
|
}
|
|
254
282
|
}
|
|
255
283
|
|
|
284
|
+
export function inspectProjectUpgradeSafety({ workspaceRoot } = {}) {
|
|
285
|
+
if (typeof workspaceRoot !== "string" || workspaceRoot.length === 0) {
|
|
286
|
+
throw new Error("workspaceRoot is required.");
|
|
287
|
+
}
|
|
288
|
+
const root = realpathSync(workspaceRoot);
|
|
289
|
+
const executionsRoot = resolve(root, RUNTIME_ROOT, "executions");
|
|
290
|
+
if (!existsSync(executionsRoot)) {
|
|
291
|
+
return {
|
|
292
|
+
status: "safe",
|
|
293
|
+
activeRunIds: [],
|
|
294
|
+
activeLeaseIds: [],
|
|
295
|
+
issues: [],
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
const executionsStat = lstatSync(executionsRoot);
|
|
299
|
+
if (executionsStat.isSymbolicLink() || !executionsStat.isDirectory()) {
|
|
300
|
+
return {
|
|
301
|
+
status: "unsafe",
|
|
302
|
+
activeRunIds: [],
|
|
303
|
+
activeLeaseIds: [],
|
|
304
|
+
issues: ["RunKit executions root must be a regular directory."],
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
const activeRunIds = [];
|
|
308
|
+
const activeLeaseIds = [];
|
|
309
|
+
const issues = [];
|
|
310
|
+
const entries = readdirSync(executionsRoot, { withFileTypes: true })
|
|
311
|
+
.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
312
|
+
for (const entry of entries) {
|
|
313
|
+
if (entry.isSymbolicLink()) {
|
|
314
|
+
issues.push(`Execution directory must not be a symlink: ${entry.name}`);
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (!entry.isDirectory()) continue;
|
|
318
|
+
const executionRoot = resolve(executionsRoot, entry.name);
|
|
319
|
+
const closeoutPath = resolve(executionRoot, "closeout-receipt.json");
|
|
320
|
+
if (!existsSync(closeoutPath)) {
|
|
321
|
+
activeRunIds.push(entry.name);
|
|
322
|
+
} else {
|
|
323
|
+
const closeoutStat = lstatSync(closeoutPath);
|
|
324
|
+
if (closeoutStat.isSymbolicLink() || !closeoutStat.isFile()) {
|
|
325
|
+
issues.push(`Closeout receipt must be a regular file: ${entry.name}`);
|
|
326
|
+
} else {
|
|
327
|
+
try {
|
|
328
|
+
const closeout = JSON.parse(readFileSync(closeoutPath, "utf8"));
|
|
329
|
+
if (closeout?.artifact?.payload?.runId !== entry.name) {
|
|
330
|
+
issues.push(`Closeout receipt runId mismatch: ${entry.name}`);
|
|
331
|
+
}
|
|
332
|
+
} catch {
|
|
333
|
+
issues.push(`Closeout receipt is invalid JSON: ${entry.name}`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const leasesRoot = resolve(executionRoot, "leases");
|
|
338
|
+
if (!existsSync(leasesRoot)) continue;
|
|
339
|
+
const leasesStat = lstatSync(leasesRoot);
|
|
340
|
+
if (leasesStat.isSymbolicLink() || !leasesStat.isDirectory()) {
|
|
341
|
+
issues.push(`Lease directory must be a regular directory: ${entry.name}`);
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
const leaseEntries = readdirSync(leasesRoot, { withFileTypes: true })
|
|
345
|
+
.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
346
|
+
for (const leaseEntry of leaseEntries) {
|
|
347
|
+
if (!leaseEntry.name.endsWith(".json")) continue;
|
|
348
|
+
if (leaseEntry.isSymbolicLink() || !leaseEntry.isFile()) {
|
|
349
|
+
issues.push(`Lease artifact must be a regular file: ${entry.name}/${leaseEntry.name}`);
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
try {
|
|
353
|
+
const lease = JSON.parse(readFileSync(resolve(leasesRoot, leaseEntry.name), "utf8"));
|
|
354
|
+
if (lease.state === "active") {
|
|
355
|
+
activeLeaseIds.push(`${entry.name}:${lease.workItemId ?? leaseEntry.name}`);
|
|
356
|
+
} else if (lease.state !== "released") {
|
|
357
|
+
issues.push(`Lease state is invalid: ${entry.name}/${leaseEntry.name}`);
|
|
358
|
+
}
|
|
359
|
+
} catch {
|
|
360
|
+
issues.push(`Lease artifact is invalid JSON: ${entry.name}/${leaseEntry.name}`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
status: activeRunIds.length === 0
|
|
366
|
+
&& activeLeaseIds.length === 0
|
|
367
|
+
&& issues.length === 0
|
|
368
|
+
? "safe"
|
|
369
|
+
: "unsafe",
|
|
370
|
+
activeRunIds,
|
|
371
|
+
activeLeaseIds,
|
|
372
|
+
issues,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
256
376
|
function validateProducer(producer) {
|
|
257
377
|
if (!isRecord(producer) || typeof producer.adapterKind !== "string" || typeof producer.adapterVersion !== "string") {
|
|
258
378
|
throw new Error("Producer requires adapterKind and adapterVersion.");
|
|
@@ -335,10 +455,10 @@ export async function initializeProjectRunKit({ workspaceRoot } = {}) {
|
|
|
335
455
|
const runtimeRoot = resolve(root, RUNTIME_ROOT);
|
|
336
456
|
const remainder = relative(root, runtimeRoot);
|
|
337
457
|
if (remainder.startsWith("..") || isAbsolute(remainder)) throw new Error("Runtime root escapes the workspace.");
|
|
338
|
-
mkdirSync(resolve(runtimeRoot, "executions"), { recursive: true });
|
|
339
458
|
const configPath = resolve(runtimeRoot, "config.json");
|
|
340
459
|
const profilesPath = resolve(runtimeRoot, "profiles.json");
|
|
341
460
|
if (!existsSync(configPath)) {
|
|
461
|
+
mkdirSync(resolve(runtimeRoot, "executions"), { recursive: true });
|
|
342
462
|
atomicWriteJson(configPath, projectConfig());
|
|
343
463
|
} else {
|
|
344
464
|
const config = JSON.parse(readFileSync(configPath, "utf8"));
|
|
@@ -350,6 +470,16 @@ export async function initializeProjectRunKit({ workspaceRoot } = {}) {
|
|
|
350
470
|
if (config.schemaVersion === "OwlCodaRunKitConfigV2" && currentGate.status === "valid") {
|
|
351
471
|
// The project already points at this exact Core identity.
|
|
352
472
|
} else if (config.schemaVersion === "OwlCodaRunKitConfigV1" && config.core?.contractVersion === "0.1") {
|
|
473
|
+
const safety = inspectProjectUpgradeSafety({ workspaceRoot: root });
|
|
474
|
+
if (safety.status !== "safe") {
|
|
475
|
+
return {
|
|
476
|
+
...safety,
|
|
477
|
+
status: "upgrade_blocked_active_execution",
|
|
478
|
+
exitCode: 2,
|
|
479
|
+
runtimeRoot: RUNTIME_ROOT,
|
|
480
|
+
core: current,
|
|
481
|
+
};
|
|
482
|
+
}
|
|
353
483
|
const migrationReceipt = migrateProjectConfig({
|
|
354
484
|
runtimeRoot,
|
|
355
485
|
configPath,
|
|
@@ -361,6 +491,16 @@ export async function initializeProjectRunKit({ workspaceRoot } = {}) {
|
|
|
361
491
|
}
|
|
362
492
|
return { status: "upgraded", exitCode: 0, runtimeRoot: RUNTIME_ROOT, core: current, migrationReceipt };
|
|
363
493
|
} else if (config.schemaVersion === "OwlCodaRunKitConfigV2" && config.core?.contractVersion === CONTRACT_VERSION) {
|
|
494
|
+
const safety = inspectProjectUpgradeSafety({ workspaceRoot: root });
|
|
495
|
+
if (safety.status !== "safe") {
|
|
496
|
+
return {
|
|
497
|
+
...safety,
|
|
498
|
+
status: "upgrade_blocked_active_execution",
|
|
499
|
+
exitCode: 2,
|
|
500
|
+
runtimeRoot: RUNTIME_ROOT,
|
|
501
|
+
core: current,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
364
504
|
const migrationReceipt = migrateProjectConfig({
|
|
365
505
|
runtimeRoot,
|
|
366
506
|
configPath,
|
|
@@ -375,6 +515,7 @@ export async function initializeProjectRunKit({ workspaceRoot } = {}) {
|
|
|
375
515
|
throw new Error(`Unsupported project config migration: ${config.schemaVersion ?? "missing schemaVersion"}.`);
|
|
376
516
|
}
|
|
377
517
|
}
|
|
518
|
+
mkdirSync(resolve(runtimeRoot, "executions"), { recursive: true });
|
|
378
519
|
if (!existsSync(profilesPath)) {
|
|
379
520
|
writeFileSync(profilesPath, `${JSON.stringify({ schemaVersion: "OwlCodaRunKitProfilesV1", profiles: [] }, null, 2)}\n`);
|
|
380
521
|
}
|