owlrunkit 0.15.0 → 0.15.1
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/README.md +2 -2
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +5 -5
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +1 -1
- package/package.json +1 -1
- package/packages/attest/distribution-manifest-v1.json +5 -0
- package/packages/attest/src/quick.mjs +5 -0
- package/registry-release-policy-v1.json +1 -1
- package/sbom.spdx.json +5 -5
- package/scripts/runkit-contract/core-contract.mjs +157 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.15.1
|
|
4
|
+
|
|
5
|
+
- Treats preserved active lease bytes in trusted `blocked` or `rejected`
|
|
6
|
+
history as non-active during upgrade safety checks.
|
|
7
|
+
- Validates closeout hashes, decisions, authorization boundaries, accepted
|
|
8
|
+
evidence, and execution pins before trusting historical closeout state.
|
|
9
|
+
- Keeps active executions, invalid closeouts, and accepted closeouts with an
|
|
10
|
+
inconsistent active lease fail-closed.
|
|
11
|
+
|
|
3
12
|
## 0.15.0
|
|
4
13
|
|
|
5
14
|
- Makes detected npm launchers resolve to absolute regular files that Formal
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ release authority.
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
npm install --save-exact owlrunkit@0.15.
|
|
14
|
+
npm install --save-exact owlrunkit@0.15.1
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Check the ordinary CLI surface, then initialize a project:
|
|
@@ -22,7 +22,7 @@ owlrunkit --version
|
|
|
22
22
|
owlrunkit init --workspace /absolute/path/to/project
|
|
23
23
|
owlrunkit doctor --workspace /absolute/path/to/project
|
|
24
24
|
owlrunkit profiles detect --workspace /absolute/path/to/project --dry-run
|
|
25
|
-
owlrunkit adopt --workspace /absolute/path/to/project --exact owlrunkit@0.15.
|
|
25
|
+
owlrunkit adopt --workspace /absolute/path/to/project --exact owlrunkit@0.15.1
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
`doctor` is read-only and distinguishes offline/DNS timeout, registry
|
|
@@ -7,8 +7,8 @@ description: Run receipt-backed, project-owned software execution with OwlCoda R
|
|
|
7
7
|
|
|
8
8
|
Use the bundled deterministic Core to coordinate work through project artifacts rather than chat memory. Require Node.js 20 or later.
|
|
9
9
|
|
|
10
|
-
Current release: standalone `owlrunkit@0.15.
|
|
11
|
-
Core `0.15.
|
|
10
|
+
Current release: standalone `owlrunkit@0.15.1`, bundling Contract `0.2` and
|
|
11
|
+
Core `0.15.1`. The root `owlcoda` package has a separate version lifecycle.
|
|
12
12
|
|
|
13
13
|
The session is an execution interface. Project artifacts are the system of record.
|
|
14
14
|
|
|
@@ -159,7 +159,7 @@ not grant Git, npm, release, deploy, or foreign-project authority.
|
|
|
159
159
|
## Core v0.15 consolidated onboarding and compatibility
|
|
160
160
|
|
|
161
161
|
Core v0.14 introduced self-service onboarding. The bundled Core is now
|
|
162
|
-
`0.15.
|
|
162
|
+
`0.15.1`; new users should not need internal module calls,
|
|
163
163
|
an external schema validator, or the full Formal lifecycle merely to understand
|
|
164
164
|
whether a project can adopt RunKit.
|
|
165
165
|
|
|
@@ -170,7 +170,7 @@ owlrunkit doctor --workspace "$PROJECT_ROOT"
|
|
|
170
170
|
owlrunkit profiles detect --workspace "$PROJECT_ROOT" --dry-run
|
|
171
171
|
owlrunkit profiles validate --workspace "$PROJECT_ROOT"
|
|
172
172
|
owlrunkit profiles impact --workspace "$PROJECT_ROOT" --changed path/to/file
|
|
173
|
-
owlrunkit adopt --workspace "$PROJECT_ROOT" --exact owlrunkit@0.15.
|
|
173
|
+
owlrunkit adopt --workspace "$PROJECT_ROOT" --exact owlrunkit@0.15.1
|
|
174
174
|
```
|
|
175
175
|
|
|
176
176
|
`doctor` and `profiles detect --dry-run` are read-only. Registry diagnostics use
|
|
@@ -179,7 +179,7 @@ failures, missing versions and local-install binding mismatches without
|
|
|
179
179
|
printing credentials. Profile detection is deterministic evidence for a human
|
|
180
180
|
decision; it does not write or overwrite project configuration.
|
|
181
181
|
|
|
182
|
-
`adopt` accepts only an exact `owlrunkit@0.15.
|
|
182
|
+
`adopt` accepts only an exact `owlrunkit@0.15.1` binding from the official npm
|
|
183
183
|
registry. It recomputes the registry and local install/lock facts, invokes the
|
|
184
184
|
strict adoption evaluator, and writes typed project evidence only after an
|
|
185
185
|
eligible result. Worktrees, `file:` dependencies, local tarballs, Git sources,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": "OwlCodaRunKitConfigV2",
|
|
3
3
|
"core": {
|
|
4
4
|
"contractVersion": "0.2",
|
|
5
|
-
"coreVersion": "0.15.
|
|
5
|
+
"coreVersion": "0.15.1",
|
|
6
6
|
"coreManifestSha256": "sha256:replace-with-installed-core-manifest",
|
|
7
7
|
"coreSourceRef": "artifact:sha256:replace-with-content-addressed-source"
|
|
8
8
|
},
|
package/package.json
CHANGED
|
@@ -36,6 +36,11 @@
|
|
|
36
36
|
"sha256": "sha256:3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986"
|
|
37
37
|
},
|
|
38
38
|
"supportedProducerCoreIdentities": [
|
|
39
|
+
{
|
|
40
|
+
"contractVersion": "0.2",
|
|
41
|
+
"coreVersion": "0.15.1",
|
|
42
|
+
"coreManifestSha256": "sha256:e8ca57522a8e473da356ceb3768bb650267894638d450d5e49463ab6f51c752b"
|
|
43
|
+
},
|
|
39
44
|
{
|
|
40
45
|
"contractVersion": "0.2",
|
|
41
46
|
"coreVersion": "0.15.0",
|
|
@@ -18,6 +18,11 @@ import { captureWorkspaceSnapshot } from "./workspace-snapshot.mjs";
|
|
|
18
18
|
export { validQuickReceiptShape } from "./quick-receipt-contract.mjs";
|
|
19
19
|
|
|
20
20
|
export const SUPPORTED_QUICK_CORE_IDENTITIES = Object.freeze([
|
|
21
|
+
{
|
|
22
|
+
contractVersion: "0.2",
|
|
23
|
+
coreVersion: "0.15.1",
|
|
24
|
+
coreManifestSha256: "sha256:e8ca57522a8e473da356ceb3768bb650267894638d450d5e49463ab6f51c752b",
|
|
25
|
+
},
|
|
21
26
|
{
|
|
22
27
|
contractVersion: "0.2",
|
|
23
28
|
coreVersion: "0.15.0",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": "OwlCodaRunKitRegistryReleasePolicyV1",
|
|
3
3
|
"status": "release_candidate_unpublished",
|
|
4
4
|
"packageName": "owlrunkit",
|
|
5
|
-
"candidateVersion": "0.15.
|
|
5
|
+
"candidateVersion": "0.15.1",
|
|
6
6
|
"registry": "https://registry.npmjs.org",
|
|
7
7
|
"registryRequiredBeforeAdoption": true,
|
|
8
8
|
"exactVersionRequired": true,
|
package/sbom.spdx.json
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"spdxVersion": "SPDX-2.3",
|
|
3
3
|
"dataLicense": "CC0-1.0",
|
|
4
4
|
"SPDXID": "SPDXRef-DOCUMENT",
|
|
5
|
-
"name": "owlrunkit-0.15.
|
|
6
|
-
"documentNamespace": "https://owlcoda.com/spdx/owlrunkit/0.15.
|
|
5
|
+
"name": "owlrunkit-0.15.1",
|
|
6
|
+
"documentNamespace": "https://owlcoda.com/spdx/owlrunkit/0.15.1",
|
|
7
7
|
"creationInfo": {
|
|
8
8
|
"created": "2026-07-29T00:00:00Z",
|
|
9
9
|
"creators": [
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
{
|
|
15
15
|
"name": "owlrunkit",
|
|
16
16
|
"SPDXID": "SPDXRef-Package-OwlRunKit",
|
|
17
|
-
"versionInfo": "0.15.
|
|
18
|
-
"downloadLocation": "https://registry.npmjs.org/owlrunkit/-/owlrunkit-0.15.
|
|
17
|
+
"versionInfo": "0.15.1",
|
|
18
|
+
"downloadLocation": "https://registry.npmjs.org/owlrunkit/-/owlrunkit-0.15.1.tgz",
|
|
19
19
|
"filesAnalyzed": false,
|
|
20
20
|
"licenseConcluded": "GPL-3.0-or-later",
|
|
21
21
|
"licenseDeclared": "GPL-3.0-or-later",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
{
|
|
25
25
|
"referenceCategory": "PACKAGE-MANAGER",
|
|
26
26
|
"referenceType": "purl",
|
|
27
|
-
"referenceLocator": "pkg:npm/owlrunkit@0.15.
|
|
27
|
+
"referenceLocator": "pkg:npm/owlrunkit@0.15.1"
|
|
28
28
|
}
|
|
29
29
|
]
|
|
30
30
|
}
|
|
@@ -14,7 +14,7 @@ import { isAbsolute, relative, resolve, win32 } from "node:path";
|
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
15
|
|
|
16
16
|
export const CONTRACT_VERSION = "0.2";
|
|
17
|
-
export const CORE_VERSION = "0.15.
|
|
17
|
+
export const CORE_VERSION = "0.15.1";
|
|
18
18
|
export const RUNTIME_ROOT = ".owlcoda/runkit";
|
|
19
19
|
|
|
20
20
|
const CORE_FILES = [
|
|
@@ -234,9 +234,15 @@ export function validateLeaseOwnedPaths(paths) {
|
|
|
234
234
|
|
|
235
235
|
export function validateExecutionPin({ expected, actual } = {}) {
|
|
236
236
|
const fields = ["contractVersion", "coreVersion", "coreManifestSha256", "coreSourceRef"];
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
237
|
+
try {
|
|
238
|
+
validateCoreIdentity(expected, { requireSourceBinding: false });
|
|
239
|
+
validateCoreIdentity(actual, { requireSourceBinding: false });
|
|
240
|
+
} catch (error) {
|
|
241
|
+
return {
|
|
242
|
+
status: "invalid_input",
|
|
243
|
+
exitCode: 3,
|
|
244
|
+
issues: [error instanceof Error ? `invalid engine pin: ${error.message}` : String(error)],
|
|
245
|
+
};
|
|
240
246
|
}
|
|
241
247
|
const issues = fields.filter((field) => expected[field] !== actual[field]).map((field) => `${field} changed during execution`);
|
|
242
248
|
return issues.length === 0
|
|
@@ -244,11 +250,27 @@ export function validateExecutionPin({ expected, actual } = {}) {
|
|
|
244
250
|
: { status: "engine_changed_during_execution", exitCode: 2, issues };
|
|
245
251
|
}
|
|
246
252
|
|
|
247
|
-
function validateCoreIdentity(core) {
|
|
253
|
+
function validateCoreIdentity(core, { requireSourceBinding = true } = {}) {
|
|
248
254
|
if (!isRecord(core)) throw new Error("Core identity must be an object.");
|
|
255
|
+
const coreKeys = ["contractVersion", "coreManifestSha256", "coreSourceRef", "coreVersion"];
|
|
256
|
+
const unsupportedCoreKeys = Object.keys(core).filter(key => !coreKeys.includes(key));
|
|
257
|
+
if (unsupportedCoreKeys.length > 0) {
|
|
258
|
+
throw new Error(`Core identity contains unsupported field: ${unsupportedCoreKeys[0]}`);
|
|
259
|
+
}
|
|
249
260
|
if (typeof core.contractVersion !== "string" || typeof core.coreVersion !== "string") throw new Error("Core identity requires contractVersion and coreVersion.");
|
|
261
|
+
if (!new Set(["0.1", CONTRACT_VERSION]).has(core.contractVersion)) {
|
|
262
|
+
throw new Error("Core identity contractVersion must be a supported RunKit contract version.");
|
|
263
|
+
}
|
|
264
|
+
if (!/^\d+\.\d+\.\d+$/u.test(core.coreVersion)) {
|
|
265
|
+
throw new Error("Core identity coreVersion must be a three-part semantic version.");
|
|
266
|
+
}
|
|
250
267
|
if (!isSha256Ref(core.coreManifestSha256)) throw new Error("Core identity requires coreManifestSha256.");
|
|
251
|
-
if (typeof core.coreSourceRef !== "string" || core.coreSourceRef.length === 0)
|
|
268
|
+
if (typeof core.coreSourceRef !== "string" || core.coreSourceRef.length === 0) {
|
|
269
|
+
throw new Error("Core identity coreSourceRef must be a non-empty string.");
|
|
270
|
+
}
|
|
271
|
+
if (requireSourceBinding && core.coreSourceRef !== `artifact:${core.coreManifestSha256}`) {
|
|
272
|
+
throw new Error("Core identity coreSourceRef must bind coreManifestSha256.");
|
|
273
|
+
}
|
|
252
274
|
}
|
|
253
275
|
|
|
254
276
|
export function validateProjectConfigV2(config) {
|
|
@@ -281,6 +303,110 @@ export function validateProjectConfigV2(config) {
|
|
|
281
303
|
}
|
|
282
304
|
}
|
|
283
305
|
|
|
306
|
+
function inspectTrustedCloseout(executionRoot, runId) {
|
|
307
|
+
const closeoutPath = resolve(executionRoot, "closeout-receipt.json");
|
|
308
|
+
try {
|
|
309
|
+
const closeoutStat = lstatSync(closeoutPath);
|
|
310
|
+
if (closeoutStat.isSymbolicLink() || !closeoutStat.isFile()) {
|
|
311
|
+
return {
|
|
312
|
+
trusted: false,
|
|
313
|
+
decision: null,
|
|
314
|
+
issues: [`Closeout receipt must be a regular file: ${runId}`],
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
const closeout = JSON.parse(readFileSync(closeoutPath, "utf8"));
|
|
318
|
+
if (closeout?.artifact?.payload?.runId !== runId) {
|
|
319
|
+
return {
|
|
320
|
+
trusted: false,
|
|
321
|
+
decision: null,
|
|
322
|
+
issues: [`Closeout receipt runId mismatch: ${runId}`],
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
const artifactGate = validateCoreArtifact(closeout.artifact);
|
|
326
|
+
if (!artifactGate.valid) {
|
|
327
|
+
return {
|
|
328
|
+
trusted: false,
|
|
329
|
+
decision: null,
|
|
330
|
+
issues: artifactGate.issues.map(issue => `Closeout receipt is invalid: ${runId}: ${issue}`),
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
if (closeout.acceptanceSha256 !== artifactGate.acceptanceSha256
|
|
334
|
+
|| closeout.artifactSha256 !== artifactGate.artifactSha256) {
|
|
335
|
+
return {
|
|
336
|
+
trusted: false,
|
|
337
|
+
decision: null,
|
|
338
|
+
issues: [`Closeout receipt hash mismatch: ${runId}`],
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
const payload = closeout.artifact.payload;
|
|
342
|
+
if (!new Set(["accepted", "rejected", "blocked"]).has(payload.decision)
|
|
343
|
+
|| payload.authorizationGranted !== false) {
|
|
344
|
+
return {
|
|
345
|
+
trusted: false,
|
|
346
|
+
decision: null,
|
|
347
|
+
issues: [`Closeout receipt decision or authorization is invalid: ${runId}`],
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
const expectedPayloadKeys = payload.decision === "accepted"
|
|
351
|
+
? ["authorizationGranted", "decision", "runId", "verification"]
|
|
352
|
+
: ["authorizationGranted", "decision", "runId"];
|
|
353
|
+
const unsupportedPayloadKeys = Object.keys(payload)
|
|
354
|
+
.filter(key => !expectedPayloadKeys.includes(key));
|
|
355
|
+
if (unsupportedPayloadKeys.length > 0) {
|
|
356
|
+
return {
|
|
357
|
+
trusted: false,
|
|
358
|
+
decision: null,
|
|
359
|
+
issues: [
|
|
360
|
+
`Closeout receipt payload contains unsupported field: ${runId}: ${unsupportedPayloadKeys[0]}`,
|
|
361
|
+
],
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
const verificationIssues = acceptedCloseoutVerificationIssues(closeout.artifact);
|
|
365
|
+
if (verificationIssues.length > 0) {
|
|
366
|
+
return {
|
|
367
|
+
trusted: false,
|
|
368
|
+
decision: null,
|
|
369
|
+
issues: verificationIssues.map(issue => `Closeout receipt is invalid: ${runId}: ${issue}`),
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
const pinPath = resolve(executionRoot, "engine-pin.json");
|
|
373
|
+
const pinStat = lstatSync(pinPath);
|
|
374
|
+
if (pinStat.isSymbolicLink() || !pinStat.isFile()) {
|
|
375
|
+
return {
|
|
376
|
+
trusted: false,
|
|
377
|
+
decision: null,
|
|
378
|
+
issues: [`Execution engine pin must be a regular file: ${runId}`],
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
const pinGate = validateExecutionPin({
|
|
382
|
+
expected: JSON.parse(readFileSync(pinPath, "utf8")),
|
|
383
|
+
actual: closeout.artifact.core,
|
|
384
|
+
});
|
|
385
|
+
if (pinGate.status !== "valid") {
|
|
386
|
+
return {
|
|
387
|
+
trusted: false,
|
|
388
|
+
decision: null,
|
|
389
|
+
issues: pinGate.issues.map(issue => `Closeout engine pin mismatch: ${runId}: ${issue}`),
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
return {
|
|
393
|
+
trusted: true,
|
|
394
|
+
decision: payload.decision,
|
|
395
|
+
issues: [],
|
|
396
|
+
};
|
|
397
|
+
} catch (error) {
|
|
398
|
+
return {
|
|
399
|
+
trusted: false,
|
|
400
|
+
decision: null,
|
|
401
|
+
issues: [
|
|
402
|
+
`Closeout receipt or engine pin is invalid: ${runId}: ${
|
|
403
|
+
error instanceof Error ? error.message : String(error)
|
|
404
|
+
}`,
|
|
405
|
+
],
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
284
410
|
export function inspectProjectUpgradeSafety({ workspaceRoot } = {}) {
|
|
285
411
|
if (typeof workspaceRoot !== "string" || workspaceRoot.length === 0) {
|
|
286
412
|
throw new Error("workspaceRoot is required.");
|
|
@@ -317,23 +443,16 @@ export function inspectProjectUpgradeSafety({ workspaceRoot } = {}) {
|
|
|
317
443
|
if (!entry.isDirectory()) continue;
|
|
318
444
|
const executionRoot = resolve(executionsRoot, entry.name);
|
|
319
445
|
const closeoutPath = resolve(executionRoot, "closeout-receipt.json");
|
|
446
|
+
let skipHistoricalLeases = false;
|
|
320
447
|
if (!existsSync(closeoutPath)) {
|
|
321
448
|
activeRunIds.push(entry.name);
|
|
322
449
|
} else {
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
}
|
|
450
|
+
const closeout = inspectTrustedCloseout(executionRoot, entry.name);
|
|
451
|
+
issues.push(...closeout.issues);
|
|
452
|
+
skipHistoricalLeases = closeout.trusted
|
|
453
|
+
&& new Set(["rejected", "blocked"]).has(closeout.decision);
|
|
336
454
|
}
|
|
455
|
+
if (skipHistoricalLeases) continue;
|
|
337
456
|
const leasesRoot = resolve(executionRoot, "leases");
|
|
338
457
|
if (!existsSync(leasesRoot)) continue;
|
|
339
458
|
const leasesStat = lstatSync(leasesRoot);
|
|
@@ -377,6 +496,14 @@ function validateProducer(producer) {
|
|
|
377
496
|
if (!isRecord(producer) || typeof producer.adapterKind !== "string" || typeof producer.adapterVersion !== "string") {
|
|
378
497
|
throw new Error("Producer requires adapterKind and adapterVersion.");
|
|
379
498
|
}
|
|
499
|
+
if (producer.adapterKind.length === 0 || producer.adapterVersion.length === 0) {
|
|
500
|
+
throw new Error("Producer adapterKind and adapterVersion must be non-empty strings.");
|
|
501
|
+
}
|
|
502
|
+
const producerKeys = ["adapterKind", "adapterVersion"];
|
|
503
|
+
const unsupportedProducerKeys = Object.keys(producer).filter(key => !producerKeys.includes(key));
|
|
504
|
+
if (unsupportedProducerKeys.length > 0) {
|
|
505
|
+
throw new Error(`Producer contains unsupported field: ${unsupportedProducerKeys[0]}`);
|
|
506
|
+
}
|
|
380
507
|
}
|
|
381
508
|
|
|
382
509
|
function validateExtensions(extensions) {
|
|
@@ -416,7 +543,18 @@ export function createCoreArtifact({ core = currentCoreIdentity(), producer, pay
|
|
|
416
543
|
export function validateCoreArtifact(artifact) {
|
|
417
544
|
try {
|
|
418
545
|
if (!isRecord(artifact) || !new Set(["RunKitCoreArtifactV1", "RunKitCoreArtifactV2"]).has(artifact.schemaVersion)) throw new Error("Unsupported core artifact schemaVersion.");
|
|
546
|
+
const artifactKeys = ["core", "extensions", "payload", "producer", "schemaVersion"];
|
|
547
|
+
const unsupportedArtifactKeys = Object.keys(artifact).filter(key => !artifactKeys.includes(key));
|
|
548
|
+
if (unsupportedArtifactKeys.length > 0) {
|
|
549
|
+
throw new Error(`Core artifact contains unsupported field: ${unsupportedArtifactKeys[0]}`);
|
|
550
|
+
}
|
|
419
551
|
validateCoreIdentity(artifact.core);
|
|
552
|
+
const expectedContractVersion = artifact.schemaVersion === "RunKitCoreArtifactV1"
|
|
553
|
+
? "0.1"
|
|
554
|
+
: CONTRACT_VERSION;
|
|
555
|
+
if (artifact.core.contractVersion !== expectedContractVersion) {
|
|
556
|
+
throw new Error(`Core artifact ${artifact.schemaVersion} requires contractVersion ${expectedContractVersion}.`);
|
|
557
|
+
}
|
|
420
558
|
validateProducer(artifact.producer);
|
|
421
559
|
if (!isRecord(artifact.payload)) throw new Error("Core artifact payload must be an object.");
|
|
422
560
|
validateExtensions(artifact.extensions);
|