owlrunkit 0.13.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 +9 -0
- package/LICENSE +674 -0
- package/NOTICE.md +137 -0
- package/README.md +44 -0
- package/SOURCE.md +10 -0
- package/docs/architecture/OWLCODA_RUN_KIT_CONTRACT_V0_1.md +160 -0
- package/docs/architecture/OWLCODA_RUN_KIT_CONTRACT_V0_2.md +459 -0
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +408 -0
- package/integrations/codex/skills/owlcoda-runkit/agents/openai.yaml +4 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/agents-snippet.md +7 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +10 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/coverage-adopt-request.json +26 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/execution-delivery-packet.json +13 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/finalize-request.json +18 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/goal-contract.json +11 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/profiles.json +9 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/ready-for-commit-request.json +11 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/resource-preflight-request.json +56 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/resume-request.json +10 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/snapshot-request.json +23 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/verification-context.json +15 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/verification-receipt.json +11 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/verify-plan-request.json +31 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/visual-smoke-request.json +32 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/worker-lease.json +7 -0
- package/package.json +24 -0
- package/packages/attest/LICENSE +674 -0
- package/packages/attest/README.md +63 -0
- package/packages/attest/cli/owlcoda-attest.mjs +155 -0
- package/packages/attest/consumer-surface-v1.json +255 -0
- package/packages/attest/distribution-manifest-v1.json +104 -0
- package/packages/attest/fixtures/bad/accepted-authority-leak.json +7 -0
- package/packages/attest/fixtures/bad/accepted-failed-receipt.json +7 -0
- package/packages/attest/fixtures/bad/active-context-binding-mismatch.json +7 -0
- package/packages/attest/fixtures/bad/active-source-binding-mismatch.json +7 -0
- package/packages/attest/fixtures/bad/context-fingerprint-mismatch.json +7 -0
- package/packages/attest/fixtures/bad/duplicate-object-key.json +8 -0
- package/packages/attest/fixtures/bad/multiple-active-leaves.json +10 -0
- package/packages/attest/fixtures/bad/orphan-replacement.json +7 -0
- package/packages/attest/fixtures/bad/root-shape-extra-property.json +8 -0
- package/packages/attest/fixtures/bad/source-fingerprint-mismatch.json +7 -0
- package/packages/attest/fixtures/good/accepted-replacement-chain.json +59 -0
- package/packages/attest/fixtures/good/blocked-single-leaf.json +9 -0
- package/packages/attest/fixtures/index.json +90 -0
- package/packages/attest/sbom.spdx.json +33 -0
- package/packages/attest/schemas/attestation-ref-v1.schema.json +44 -0
- package/packages/attest/schemas/attestation-result-v1.schema.json +163 -0
- package/packages/attest/schemas/offline-attestation-bundle-v1.schema.json +30 -0
- package/packages/attest/schemas/public-verification-bundle-v1.schema.json +74 -0
- package/packages/attest/schemas/quick-verification-receipt-v1.schema.json +219 -0
- package/packages/attest/schemas/workspace-snapshot-v1.schema.json +135 -0
- package/packages/attest/src/canonical.mjs +12 -0
- package/packages/attest/src/formal.mjs +762 -0
- package/packages/attest/src/index.mjs +113 -0
- package/packages/attest/src/offline-bundle.mjs +108 -0
- package/packages/attest/src/quick-receipt-contract.mjs +156 -0
- package/packages/attest/src/quick.mjs +274 -0
- package/packages/attest/src/reference-contract.mjs +64 -0
- package/packages/attest/src/reference.mjs +131 -0
- package/packages/attest/src/workspace-snapshot.mjs +171 -0
- package/registry-release-policy-v1.json +34 -0
- package/sbom.spdx.json +39 -0
- package/scripts/runkit-contract/acceptance-evidence.mjs +103 -0
- package/scripts/runkit-contract/core-contract.mjs +392 -0
- package/scripts/runkit-contract/coverage-adoption.mjs +414 -0
- package/scripts/runkit-contract/delivery-create.mjs +252 -0
- package/scripts/runkit-contract/delivery-selection.mjs +31 -0
- package/scripts/runkit-contract/finalize.mjs +790 -0
- package/scripts/runkit-contract/inspect-presentation.mjs +183 -0
- package/scripts/runkit-contract/install-codex-skill.mjs +239 -0
- package/scripts/runkit-contract/lease-lifecycle.mjs +324 -0
- package/scripts/runkit-contract/lifecycle-orchestration.mjs +415 -0
- package/scripts/runkit-contract/offline-store.mjs +248 -0
- package/scripts/runkit-contract/profile-impact.mjs +223 -0
- package/scripts/runkit-contract/provenance-common.mjs +153 -0
- package/scripts/runkit-contract/quick-attest.mjs +4 -0
- package/scripts/runkit-contract/quick-canonical.mjs +41 -0
- package/scripts/runkit-contract/quick-metrics.mjs +86 -0
- package/scripts/runkit-contract/quick-receipt.mjs +139 -0
- package/scripts/runkit-contract/quick-verify.mjs +201 -0
- package/scripts/runkit-contract/quick-workspace-snapshot.mjs +3 -0
- package/scripts/runkit-contract/ready-for-commit.mjs +188 -0
- package/scripts/runkit-contract/receipt-lineage.mjs +332 -0
- package/scripts/runkit-contract/registry-adoption-gate.mjs +117 -0
- package/scripts/runkit-contract/repair-execution.mjs +997 -0
- package/scripts/runkit-contract/resource-preflight.mjs +829 -0
- package/scripts/runkit-contract/resume-execution.mjs +569 -0
- package/scripts/runkit-contract/runkit-cli.mjs +1214 -0
- package/scripts/runkit-contract/schemas/closeout-receipt-v2.schema.json +42 -0
- package/scripts/runkit-contract/schemas/core-artifact-v2.schema.json +23 -0
- package/scripts/runkit-contract/schemas/core-artifact.schema.json +23 -0
- package/scripts/runkit-contract/schemas/coverage-adopt-request-v1.schema.json +66 -0
- package/scripts/runkit-contract/schemas/engine-pin-v2.schema.json +14 -0
- package/scripts/runkit-contract/schemas/engine-pin.schema.json +14 -0
- package/scripts/runkit-contract/schemas/evidence-coverage-index-v1.schema.json +131 -0
- package/scripts/runkit-contract/schemas/finalize-request-v1.schema.json +131 -0
- package/scripts/runkit-contract/schemas/profiles.schema.json +47 -0
- package/scripts/runkit-contract/schemas/project-config-v2.schema.json +13 -0
- package/scripts/runkit-contract/schemas/project-config.schema.json +13 -0
- package/scripts/runkit-contract/schemas/ready-for-commit-v1.schema.json +26 -0
- package/scripts/runkit-contract/schemas/replayable-evidence-v1.schema.json +82 -0
- package/scripts/runkit-contract/schemas/resource-preflight-request-v1.schema.json +178 -0
- package/scripts/runkit-contract/schemas/resource-preflight-v1.schema.json +153 -0
- package/scripts/runkit-contract/schemas/resume-attempt-v1.schema.json +80 -0
- package/scripts/runkit-contract/schemas/resume-request-v1.schema.json +77 -0
- package/scripts/runkit-contract/schemas/snapshot-v1.schema.json +71 -0
- package/scripts/runkit-contract/schemas/verification-context-v1.schema.json +92 -0
- package/scripts/runkit-contract/schemas/verification-plan-v1.schema.json +150 -0
- package/scripts/runkit-contract/schemas/verification-receipt-v2.schema.json +118 -0
- package/scripts/runkit-contract/schemas/verify-plan-request-v1.schema.json +56 -0
- package/scripts/runkit-contract/schemas/visual-smoke-request-v1.schema.json +64 -0
- package/scripts/runkit-contract/schemas/visual-smoke-result-v1.schema.json +68 -0
- package/scripts/runkit-contract/snapshot.mjs +340 -0
- package/scripts/runkit-contract/source-fingerprint.mjs +404 -0
- package/scripts/runkit-contract/templates/coverage-adopt-request.json +26 -0
- package/scripts/runkit-contract/templates/finalize-request.json +18 -0
- package/scripts/runkit-contract/templates/goal-contract.json +11 -0
- package/scripts/runkit-contract/templates/ready-for-commit-request.json +11 -0
- package/scripts/runkit-contract/templates/resource-preflight-request.json +56 -0
- package/scripts/runkit-contract/templates/resume-request.json +10 -0
- package/scripts/runkit-contract/templates/snapshot-request.json +23 -0
- package/scripts/runkit-contract/templates/verify-plan-request.json +31 -0
- package/scripts/runkit-contract/templates/visual-smoke-request.json +32 -0
- package/scripts/runkit-contract/templates/worker-lease.json +7 -0
- package/scripts/runkit-contract/verification-context.mjs +141 -0
- package/scripts/runkit-contract/verification-plan.mjs +608 -0
- package/scripts/runkit-contract/verification-receipt-gate.mjs +566 -0
- package/scripts/runkit-contract/visual-smoke.mjs +410 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
realpathSync,
|
|
7
|
+
renameSync,
|
|
8
|
+
rmSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from "node:fs";
|
|
11
|
+
import { isAbsolute, relative, resolve, win32 } from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
|
|
14
|
+
export const CONTRACT_VERSION = "0.2";
|
|
15
|
+
export const CORE_VERSION = "0.13.0";
|
|
16
|
+
export const RUNTIME_ROOT = ".owlcoda/runkit";
|
|
17
|
+
|
|
18
|
+
const CORE_FILES = [
|
|
19
|
+
"acceptance-evidence.mjs",
|
|
20
|
+
"coverage-adoption.mjs",
|
|
21
|
+
"core-contract.mjs",
|
|
22
|
+
"delivery-create.mjs",
|
|
23
|
+
"delivery-selection.mjs",
|
|
24
|
+
"finalize.mjs",
|
|
25
|
+
"inspect-presentation.mjs",
|
|
26
|
+
"lease-lifecycle.mjs",
|
|
27
|
+
"lifecycle-orchestration.mjs",
|
|
28
|
+
"offline-store.mjs",
|
|
29
|
+
"profile-impact.mjs",
|
|
30
|
+
"provenance-common.mjs",
|
|
31
|
+
"quick-attest.mjs",
|
|
32
|
+
"quick-canonical.mjs",
|
|
33
|
+
"quick-metrics.mjs",
|
|
34
|
+
"quick-receipt.mjs",
|
|
35
|
+
"quick-verify.mjs",
|
|
36
|
+
"quick-workspace-snapshot.mjs",
|
|
37
|
+
"ready-for-commit.mjs",
|
|
38
|
+
"receipt-lineage.mjs",
|
|
39
|
+
"repair-execution.mjs",
|
|
40
|
+
"resume-execution.mjs",
|
|
41
|
+
"resource-preflight.mjs",
|
|
42
|
+
"runkit-cli.mjs",
|
|
43
|
+
"snapshot.mjs",
|
|
44
|
+
"source-fingerprint.mjs",
|
|
45
|
+
"verification-receipt-gate.mjs",
|
|
46
|
+
"verification-context.mjs",
|
|
47
|
+
"verification-plan.mjs",
|
|
48
|
+
"visual-smoke.mjs",
|
|
49
|
+
"schemas/closeout-receipt-v2.schema.json",
|
|
50
|
+
"schemas/coverage-adopt-request-v1.schema.json",
|
|
51
|
+
"schemas/core-artifact.schema.json",
|
|
52
|
+
"schemas/core-artifact-v2.schema.json",
|
|
53
|
+
"schemas/engine-pin.schema.json",
|
|
54
|
+
"schemas/engine-pin-v2.schema.json",
|
|
55
|
+
"schemas/evidence-coverage-index-v1.schema.json",
|
|
56
|
+
"schemas/project-config.schema.json",
|
|
57
|
+
"schemas/project-config-v2.schema.json",
|
|
58
|
+
"schemas/profiles.schema.json",
|
|
59
|
+
"schemas/resume-attempt-v1.schema.json",
|
|
60
|
+
"schemas/resume-request-v1.schema.json",
|
|
61
|
+
"schemas/resource-preflight-request-v1.schema.json",
|
|
62
|
+
"schemas/resource-preflight-v1.schema.json",
|
|
63
|
+
"schemas/replayable-evidence-v1.schema.json",
|
|
64
|
+
"schemas/finalize-request-v1.schema.json",
|
|
65
|
+
"schemas/ready-for-commit-v1.schema.json",
|
|
66
|
+
"schemas/snapshot-v1.schema.json",
|
|
67
|
+
"schemas/visual-smoke-request-v1.schema.json",
|
|
68
|
+
"schemas/visual-smoke-result-v1.schema.json",
|
|
69
|
+
"schemas/verification-context-v1.schema.json",
|
|
70
|
+
"schemas/verification-plan-v1.schema.json",
|
|
71
|
+
"schemas/verification-receipt-v2.schema.json",
|
|
72
|
+
"schemas/verify-plan-request-v1.schema.json",
|
|
73
|
+
"templates/goal-contract.json",
|
|
74
|
+
"templates/coverage-adopt-request.json",
|
|
75
|
+
"templates/finalize-request.json",
|
|
76
|
+
"templates/ready-for-commit-request.json",
|
|
77
|
+
"templates/resume-request.json",
|
|
78
|
+
"templates/resource-preflight-request.json",
|
|
79
|
+
"templates/snapshot-request.json",
|
|
80
|
+
"templates/visual-smoke-request.json",
|
|
81
|
+
"templates/verify-plan-request.json",
|
|
82
|
+
"templates/worker-lease.json",
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
const CORE_DEPENDENCY_FILES = [
|
|
86
|
+
"packages/attest/src/formal.mjs",
|
|
87
|
+
"packages/attest/src/offline-bundle.mjs",
|
|
88
|
+
"packages/attest/src/quick-receipt-contract.mjs",
|
|
89
|
+
"packages/attest/src/reference-contract.mjs",
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
function sha256(value) {
|
|
93
|
+
return createHash("sha256").update(value).digest("hex");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function canonicalJson(value) {
|
|
97
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
98
|
+
if (value && typeof value === "object") {
|
|
99
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(",")}}`;
|
|
100
|
+
}
|
|
101
|
+
return JSON.stringify(value);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function isRecord(value) {
|
|
105
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function isSha256Ref(value) {
|
|
109
|
+
return typeof value === "string" && /^sha256:[a-f0-9]{64}$/.test(value);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function atomicWriteJson(filePath, value) {
|
|
113
|
+
const temporaryPath = `${filePath}.tmp-${process.pid}`;
|
|
114
|
+
rmSync(temporaryPath, { force: true });
|
|
115
|
+
writeFileSync(temporaryPath, `${JSON.stringify(value, null, 2)}\n`, { flag: "wx" });
|
|
116
|
+
renameSync(temporaryPath, filePath);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function projectConfig(core = currentCoreIdentity()) {
|
|
120
|
+
return {
|
|
121
|
+
schemaVersion: "OwlCodaRunKitConfigV2",
|
|
122
|
+
core,
|
|
123
|
+
authorizationPolicy: "external_explicit_authority_required",
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function migrationReceiptName(migration, fromCore, toCore) {
|
|
128
|
+
if (migration === "config-v01-to-v02") return "config-v01-to-v02.json";
|
|
129
|
+
const from = fromCore.coreManifestSha256.slice("sha256:".length);
|
|
130
|
+
const to = toCore.coreManifestSha256.slice("sha256:".length);
|
|
131
|
+
return `config-v02-core-refresh-${from}-to-${to}.json`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function migrateProjectConfig({ runtimeRoot, configPath, config, migration }) {
|
|
135
|
+
validateCoreIdentity(config.core);
|
|
136
|
+
const next = projectConfig();
|
|
137
|
+
const receipt = {
|
|
138
|
+
schemaVersion: "OwlCodaRunKitConfigMigrationReceiptV1",
|
|
139
|
+
migration,
|
|
140
|
+
fromSchemaVersion: config.schemaVersion,
|
|
141
|
+
toSchemaVersion: next.schemaVersion,
|
|
142
|
+
fromCore: config.core,
|
|
143
|
+
toCore: next.core,
|
|
144
|
+
authorizationGranted: false,
|
|
145
|
+
};
|
|
146
|
+
const receiptsRoot = resolve(runtimeRoot, "config-migration-receipts");
|
|
147
|
+
mkdirSync(receiptsRoot, { recursive: true });
|
|
148
|
+
const receiptName = migrationReceiptName(migration, config.core, next.core);
|
|
149
|
+
atomicWriteJson(resolve(receiptsRoot, receiptName), receipt);
|
|
150
|
+
atomicWriteJson(configPath, next);
|
|
151
|
+
return `${RUNTIME_ROOT}/config-migration-receipts/${receiptName}`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function coreDirectory() {
|
|
155
|
+
return resolve(fileURLToPath(new URL(".", import.meta.url)));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function coreManifestStream() {
|
|
159
|
+
const coreRoot = coreDirectory();
|
|
160
|
+
const projectRoot = resolve(coreRoot, "../..");
|
|
161
|
+
return [
|
|
162
|
+
...CORE_FILES.map((name) => `${name}\tsha256:${sha256(readFileSync(resolve(coreRoot, name)))}\n`),
|
|
163
|
+
...CORE_DEPENDENCY_FILES.map((name) => `${name}\tsha256:${sha256(readFileSync(resolve(projectRoot, name)))}\n`),
|
|
164
|
+
].join("");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function currentCoreIdentity() {
|
|
168
|
+
const manifestSha256 = sha256(coreManifestStream());
|
|
169
|
+
return {
|
|
170
|
+
contractVersion: CONTRACT_VERSION,
|
|
171
|
+
coreVersion: CORE_VERSION,
|
|
172
|
+
coreManifestSha256: `sha256:${manifestSha256}`,
|
|
173
|
+
coreSourceRef: `artifact:sha256:${manifestSha256}`,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function isDirectExecution(moduleUrl, argvPath = process.argv[1]) {
|
|
178
|
+
if (!argvPath) return false;
|
|
179
|
+
try {
|
|
180
|
+
return realpathSync(argvPath) === realpathSync(fileURLToPath(moduleUrl));
|
|
181
|
+
} catch {
|
|
182
|
+
return resolve(argvPath) === fileURLToPath(moduleUrl);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function isReservedRuntimePath(value) {
|
|
187
|
+
return value === RUNTIME_ROOT || value.startsWith(`${RUNTIME_ROOT}/`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function validateRepoRelativePath(value, label) {
|
|
191
|
+
if (typeof value !== "string" || value.length === 0 || isAbsolute(value) || win32.isAbsolute(value) || value.includes("\\")) {
|
|
192
|
+
throw new Error(`${label} must be a safe repository-relative path.`);
|
|
193
|
+
}
|
|
194
|
+
const segments = value.split("/");
|
|
195
|
+
if (segments.some((segment) => segment.length === 0 || segment === "." || segment === "..")) {
|
|
196
|
+
throw new Error(`${label} must not contain empty, dot, or parent segments.`);
|
|
197
|
+
}
|
|
198
|
+
if (isReservedRuntimePath(value)) throw new Error(`${label} uses the reserved runtime path ${RUNTIME_ROOT}.`);
|
|
199
|
+
return value;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function validateLeaseOwnedPaths(paths) {
|
|
203
|
+
if (!Array.isArray(paths) || paths.length === 0) throw new Error("Lease owned paths must be a non-empty array.");
|
|
204
|
+
return [...new Set(paths.map((value) => validateRepoRelativePath(value, "Lease owned path")))].sort();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function validateExecutionPin({ expected, actual } = {}) {
|
|
208
|
+
const fields = ["contractVersion", "coreVersion", "coreManifestSha256", "coreSourceRef"];
|
|
209
|
+
const malformed = fields.filter((field) => typeof expected?.[field] !== "string" || typeof actual?.[field] !== "string");
|
|
210
|
+
if (malformed.length > 0) {
|
|
211
|
+
return { status: "invalid_input", exitCode: 3, issues: malformed.map((field) => `missing engine pin field: ${field}`) };
|
|
212
|
+
}
|
|
213
|
+
const issues = fields.filter((field) => expected[field] !== actual[field]).map((field) => `${field} changed during execution`);
|
|
214
|
+
return issues.length === 0
|
|
215
|
+
? { status: "valid", exitCode: 0, issues: [] }
|
|
216
|
+
: { status: "engine_changed_during_execution", exitCode: 2, issues };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function validateCoreIdentity(core) {
|
|
220
|
+
if (!isRecord(core)) throw new Error("Core identity must be an object.");
|
|
221
|
+
if (typeof core.contractVersion !== "string" || typeof core.coreVersion !== "string") throw new Error("Core identity requires contractVersion and coreVersion.");
|
|
222
|
+
if (!isSha256Ref(core.coreManifestSha256)) throw new Error("Core identity requires coreManifestSha256.");
|
|
223
|
+
if (typeof core.coreSourceRef !== "string" || core.coreSourceRef.length === 0) throw new Error("Core identity requires coreSourceRef.");
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function validateProjectConfigV2(config) {
|
|
227
|
+
try {
|
|
228
|
+
if (!isRecord(config)) throw new Error("Project config must be an object.");
|
|
229
|
+
const configKeys = ["authorizationPolicy", "core", "schemaVersion"];
|
|
230
|
+
const unsupportedConfigKeys = Object.keys(config).filter(key => !configKeys.includes(key));
|
|
231
|
+
if (unsupportedConfigKeys.length > 0) {
|
|
232
|
+
throw new Error(`Project config contains unsupported field: ${unsupportedConfigKeys[0]}`);
|
|
233
|
+
}
|
|
234
|
+
if (config.schemaVersion !== "OwlCodaRunKitConfigV2") {
|
|
235
|
+
throw new Error("Project config schemaVersion must be OwlCodaRunKitConfigV2.");
|
|
236
|
+
}
|
|
237
|
+
if (config.authorizationPolicy !== "external_explicit_authority_required") {
|
|
238
|
+
throw new Error("Unsupported project config authorizationPolicy.");
|
|
239
|
+
}
|
|
240
|
+
if (!isRecord(config.core)) throw new Error("Project config core must be an object.");
|
|
241
|
+
const coreKeys = ["contractVersion", "coreManifestSha256", "coreSourceRef", "coreVersion"];
|
|
242
|
+
const unsupportedCoreKeys = Object.keys(config.core).filter(key => !coreKeys.includes(key));
|
|
243
|
+
if (unsupportedCoreKeys.length > 0) {
|
|
244
|
+
throw new Error(`Project config core contains unsupported field: ${unsupportedCoreKeys[0]}`);
|
|
245
|
+
}
|
|
246
|
+
validateCoreIdentity(config.core);
|
|
247
|
+
if (config.core.contractVersion !== CONTRACT_VERSION) {
|
|
248
|
+
throw new Error(`Project config core contractVersion must be ${CONTRACT_VERSION}.`);
|
|
249
|
+
}
|
|
250
|
+
return { valid: true, issues: [] };
|
|
251
|
+
} catch (error) {
|
|
252
|
+
return { valid: false, issues: [error instanceof Error ? error.message : String(error)] };
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function validateProducer(producer) {
|
|
257
|
+
if (!isRecord(producer) || typeof producer.adapterKind !== "string" || typeof producer.adapterVersion !== "string") {
|
|
258
|
+
throw new Error("Producer requires adapterKind and adapterVersion.");
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function validateExtensions(extensions) {
|
|
263
|
+
if (!isRecord(extensions)) throw new Error("Extensions must be an object.");
|
|
264
|
+
for (const namespace of Object.keys(extensions)) {
|
|
265
|
+
if (!namespace.includes(".")) throw new Error(`Extension namespace must be qualified: ${namespace}`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function artifactHashes(artifact) {
|
|
270
|
+
const acceptance = {
|
|
271
|
+
schemaVersion: artifact.schemaVersion,
|
|
272
|
+
core: artifact.core,
|
|
273
|
+
payload: artifact.payload,
|
|
274
|
+
};
|
|
275
|
+
return {
|
|
276
|
+
acceptanceSha256: `sha256:${sha256(canonicalJson(acceptance))}`,
|
|
277
|
+
artifactSha256: `sha256:${sha256(canonicalJson(artifact))}`,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export function createCoreArtifact({ core = currentCoreIdentity(), producer, payload, extensions = {} } = {}) {
|
|
282
|
+
validateCoreIdentity(core);
|
|
283
|
+
validateProducer(producer);
|
|
284
|
+
if (!isRecord(payload)) throw new Error("Core artifact payload must be an object.");
|
|
285
|
+
validateExtensions(extensions);
|
|
286
|
+
const artifact = {
|
|
287
|
+
schemaVersion: "RunKitCoreArtifactV2",
|
|
288
|
+
core: structuredClone(core),
|
|
289
|
+
producer: structuredClone(producer),
|
|
290
|
+
payload: structuredClone(payload),
|
|
291
|
+
extensions: structuredClone(extensions),
|
|
292
|
+
};
|
|
293
|
+
return { artifact, ...artifactHashes(artifact) };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function validateCoreArtifact(artifact) {
|
|
297
|
+
try {
|
|
298
|
+
if (!isRecord(artifact) || !new Set(["RunKitCoreArtifactV1", "RunKitCoreArtifactV2"]).has(artifact.schemaVersion)) throw new Error("Unsupported core artifact schemaVersion.");
|
|
299
|
+
validateCoreIdentity(artifact.core);
|
|
300
|
+
validateProducer(artifact.producer);
|
|
301
|
+
if (!isRecord(artifact.payload)) throw new Error("Core artifact payload must be an object.");
|
|
302
|
+
validateExtensions(artifact.extensions);
|
|
303
|
+
return { valid: true, issues: [], ...artifactHashes(artifact) };
|
|
304
|
+
} catch (error) {
|
|
305
|
+
return { valid: false, issues: [error instanceof Error ? error.message : String(error)] };
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function acceptedCloseoutVerificationIssues(artifact) {
|
|
310
|
+
if (artifact?.core?.contractVersion !== "0.2" || artifact?.payload?.decision !== "accepted") return [];
|
|
311
|
+
const verification = artifact.payload.verification;
|
|
312
|
+
const validFields = [
|
|
313
|
+
verification?.contractVersion === "0.2",
|
|
314
|
+
verification?.gateDecision === "accepted_passed",
|
|
315
|
+
typeof verification?.gateInputSha256 === "string" && /^[a-f0-9]{64}$/.test(verification.gateInputSha256),
|
|
316
|
+
typeof verification?.activeReceiptSha256 === "string" && /^[a-f0-9]{64}$/.test(verification.activeReceiptSha256),
|
|
317
|
+
typeof verification?.sourceFingerprint === "string" && /^[a-f0-9]{64}$/.test(verification.sourceFingerprint),
|
|
318
|
+
typeof verification?.verificationContextFingerprint === "string" && /^[a-f0-9]{64}$/.test(verification.verificationContextFingerprint),
|
|
319
|
+
verification?.leaseState === "released",
|
|
320
|
+
Array.isArray(verification?.selectedProfileIds)
|
|
321
|
+
&& verification.selectedProfileIds.length > 0
|
|
322
|
+
&& verification.selectedProfileIds.every(value => typeof value === "string" && value.length > 0),
|
|
323
|
+
Array.isArray(verification?.releasedLeaseIds)
|
|
324
|
+
&& verification.releasedLeaseIds.length > 0
|
|
325
|
+
&& verification.releasedLeaseIds.every(value => typeof value === "string" && value.length > 0)
|
|
326
|
+
&& new Set(verification.releasedLeaseIds).size === verification.releasedLeaseIds.length,
|
|
327
|
+
];
|
|
328
|
+
return validFields.every(Boolean) ? [] : ["Contract v0.2 accepted closeout requires complete verification evidence"];
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export async function initializeProjectRunKit({ workspaceRoot } = {}) {
|
|
332
|
+
if (typeof workspaceRoot !== "string" || workspaceRoot.length === 0) throw new Error("workspaceRoot is required.");
|
|
333
|
+
const root = realpathSync(workspaceRoot);
|
|
334
|
+
if (existsSync(resolve(root, ".owlrunkit"))) throw new Error("Legacy .owlrunkit exists; explicit migration is required before initialization.");
|
|
335
|
+
const runtimeRoot = resolve(root, RUNTIME_ROOT);
|
|
336
|
+
const remainder = relative(root, runtimeRoot);
|
|
337
|
+
if (remainder.startsWith("..") || isAbsolute(remainder)) throw new Error("Runtime root escapes the workspace.");
|
|
338
|
+
mkdirSync(resolve(runtimeRoot, "executions"), { recursive: true });
|
|
339
|
+
const configPath = resolve(runtimeRoot, "config.json");
|
|
340
|
+
const profilesPath = resolve(runtimeRoot, "profiles.json");
|
|
341
|
+
if (!existsSync(configPath)) {
|
|
342
|
+
atomicWriteJson(configPath, projectConfig());
|
|
343
|
+
} else {
|
|
344
|
+
const config = JSON.parse(readFileSync(configPath, "utf8"));
|
|
345
|
+
if (config.authorizationPolicy !== "external_explicit_authority_required") {
|
|
346
|
+
throw new Error("Unsupported project config authorizationPolicy.");
|
|
347
|
+
}
|
|
348
|
+
const current = currentCoreIdentity();
|
|
349
|
+
const currentGate = validateExecutionPin({ expected: config.core, actual: current });
|
|
350
|
+
if (config.schemaVersion === "OwlCodaRunKitConfigV2" && currentGate.status === "valid") {
|
|
351
|
+
// The project already points at this exact Core identity.
|
|
352
|
+
} else if (config.schemaVersion === "OwlCodaRunKitConfigV1" && config.core?.contractVersion === "0.1") {
|
|
353
|
+
const migrationReceipt = migrateProjectConfig({
|
|
354
|
+
runtimeRoot,
|
|
355
|
+
configPath,
|
|
356
|
+
config,
|
|
357
|
+
migration: "config-v01-to-v02",
|
|
358
|
+
});
|
|
359
|
+
if (!existsSync(profilesPath)) {
|
|
360
|
+
writeFileSync(profilesPath, `${JSON.stringify({ schemaVersion: "OwlCodaRunKitProfilesV1", profiles: [] }, null, 2)}\n`);
|
|
361
|
+
}
|
|
362
|
+
return { status: "upgraded", exitCode: 0, runtimeRoot: RUNTIME_ROOT, core: current, migrationReceipt };
|
|
363
|
+
} else if (config.schemaVersion === "OwlCodaRunKitConfigV2" && config.core?.contractVersion === CONTRACT_VERSION) {
|
|
364
|
+
const migrationReceipt = migrateProjectConfig({
|
|
365
|
+
runtimeRoot,
|
|
366
|
+
configPath,
|
|
367
|
+
config,
|
|
368
|
+
migration: "config-v02-core-refresh",
|
|
369
|
+
});
|
|
370
|
+
if (!existsSync(profilesPath)) {
|
|
371
|
+
writeFileSync(profilesPath, `${JSON.stringify({ schemaVersion: "OwlCodaRunKitProfilesV1", profiles: [] }, null, 2)}\n`);
|
|
372
|
+
}
|
|
373
|
+
return { status: "upgraded", exitCode: 0, runtimeRoot: RUNTIME_ROOT, core: current, migrationReceipt };
|
|
374
|
+
} else {
|
|
375
|
+
throw new Error(`Unsupported project config migration: ${config.schemaVersion ?? "missing schemaVersion"}.`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (!existsSync(profilesPath)) {
|
|
379
|
+
writeFileSync(profilesPath, `${JSON.stringify({ schemaVersion: "OwlCodaRunKitProfilesV1", profiles: [] }, null, 2)}\n`);
|
|
380
|
+
}
|
|
381
|
+
return { status: "initialized", exitCode: 0, runtimeRoot: RUNTIME_ROOT, core: currentCoreIdentity() };
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export function coreManifest() {
|
|
385
|
+
const identity = currentCoreIdentity();
|
|
386
|
+
return {
|
|
387
|
+
schemaVersion: "OwlCodaRunKitCoreManifestV1",
|
|
388
|
+
...identity,
|
|
389
|
+
files: [...CORE_FILES],
|
|
390
|
+
dependencyFiles: [...CORE_DEPENDENCY_FILES],
|
|
391
|
+
};
|
|
392
|
+
}
|