owlrunkit 0.18.2 → 0.18.3
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 +21 -0
- package/README.md +41 -7
- package/docs/architecture/OWLCODA_RUNKIT_018_PROJECT_DRIVER_PRODUCT_DEFINITION.md +16 -5
- package/docs/architecture/OWLCODA_RUNKIT_USAGE_MODE_SELECTION_PRODUCT_DECISION_20260811.md +53 -3
- package/docs/architecture/OWLCODA_RUN_KIT_CONTRACT_V0_2.md +7 -0
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +28 -9
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
- package/integrations/codex/skills/owlcoda-runkit/references/contract-v0.1.md +160 -0
- package/integrations/codex/skills/owlcoda-runkit/references/contract-v0.2.md +545 -0
- package/package.json +1 -1
- package/packages/attest/README.md +4 -3
- package/packages/attest/consumer-surface-v1.json +1 -0
- package/packages/attest/distribution-manifest-v1.json +7 -1
- package/packages/attest/schemas/quick-verification-receipt-v2.schema.json +232 -0
- package/packages/attest/src/index.mjs +4 -1
- package/packages/attest/src/quick-receipt-contract.mjs +13 -3
- package/packages/attest/src/quick.mjs +16 -2
- package/registry-release-policy-v1.json +5 -5
- package/sbom.spdx.json +5 -5
- package/scripts/runkit-contract/core-contract.mjs +2 -1
- package/scripts/runkit-contract/quick-metrics.mjs +3 -3
- package/scripts/runkit-contract/quick-receipt.mjs +79 -12
- package/scripts/runkit-contract/quick-verify.mjs +88 -11
- package/scripts/runkit-contract/runkit-cli.mjs +91 -9
- package/scripts/runkit-contract/schemas/team-project-event-v2.schema.json +128 -0
- package/scripts/runkit-contract/team-project.mjs +310 -45
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://owlcoda.dev/schemas/runkit/trust/quick-verification-receipt-v2.schema.json",
|
|
4
|
+
"title": "OwlCoda Quick Verification Receipt V2",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"schemaVersion",
|
|
9
|
+
"receiptId",
|
|
10
|
+
"assurance",
|
|
11
|
+
"authorizationGranted",
|
|
12
|
+
"coreIdentity",
|
|
13
|
+
"workspaceBefore",
|
|
14
|
+
"exactCommand",
|
|
15
|
+
"inputArtifacts",
|
|
16
|
+
"verificationContext",
|
|
17
|
+
"startedAt",
|
|
18
|
+
"finishedAt",
|
|
19
|
+
"exitResult",
|
|
20
|
+
"outputArtifacts",
|
|
21
|
+
"workspaceAfter",
|
|
22
|
+
"mutationDecision",
|
|
23
|
+
"issueCodes"
|
|
24
|
+
],
|
|
25
|
+
"properties": {
|
|
26
|
+
"schemaVersion": {
|
|
27
|
+
"const": "OwlCodaQuickVerificationReceiptV2"
|
|
28
|
+
},
|
|
29
|
+
"receiptId": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"minLength": 1
|
|
32
|
+
},
|
|
33
|
+
"assurance": {
|
|
34
|
+
"const": "captured_verification"
|
|
35
|
+
},
|
|
36
|
+
"authorizationGranted": {
|
|
37
|
+
"const": false
|
|
38
|
+
},
|
|
39
|
+
"coreIdentity": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"additionalProperties": false,
|
|
42
|
+
"required": [
|
|
43
|
+
"contractVersion",
|
|
44
|
+
"coreVersion",
|
|
45
|
+
"coreManifestSha256"
|
|
46
|
+
],
|
|
47
|
+
"properties": {
|
|
48
|
+
"contractVersion": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"minLength": 1
|
|
51
|
+
},
|
|
52
|
+
"coreVersion": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"minLength": 1
|
|
55
|
+
},
|
|
56
|
+
"coreManifestSha256": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"pattern": "^sha256:[0-9a-f]{64}$"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"workspaceBefore": {
|
|
63
|
+
"$ref": "workspace-snapshot-v1.schema.json"
|
|
64
|
+
},
|
|
65
|
+
"exactCommand": {
|
|
66
|
+
"type": "object",
|
|
67
|
+
"additionalProperties": false,
|
|
68
|
+
"required": [
|
|
69
|
+
"executable",
|
|
70
|
+
"argv",
|
|
71
|
+
"cwd"
|
|
72
|
+
],
|
|
73
|
+
"properties": {
|
|
74
|
+
"executable": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"minLength": 1
|
|
77
|
+
},
|
|
78
|
+
"argv": {
|
|
79
|
+
"type": "array",
|
|
80
|
+
"items": {
|
|
81
|
+
"type": "string"
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"cwd": {
|
|
85
|
+
"type": "string",
|
|
86
|
+
"minLength": 1
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"inputArtifacts": {
|
|
91
|
+
"type": "object",
|
|
92
|
+
"additionalProperties": false,
|
|
93
|
+
"required": [
|
|
94
|
+
"stdin"
|
|
95
|
+
],
|
|
96
|
+
"properties": {
|
|
97
|
+
"stdin": {
|
|
98
|
+
"$ref": "#/$defs/materialArtifact"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"verificationContext": {
|
|
103
|
+
"type": "object",
|
|
104
|
+
"additionalProperties": false,
|
|
105
|
+
"required": [
|
|
106
|
+
"platform",
|
|
107
|
+
"architecture",
|
|
108
|
+
"runtime"
|
|
109
|
+
],
|
|
110
|
+
"properties": {
|
|
111
|
+
"platform": {
|
|
112
|
+
"type": "string",
|
|
113
|
+
"minLength": 1
|
|
114
|
+
},
|
|
115
|
+
"architecture": {
|
|
116
|
+
"type": "string",
|
|
117
|
+
"minLength": 1
|
|
118
|
+
},
|
|
119
|
+
"runtime": {
|
|
120
|
+
"type": "string",
|
|
121
|
+
"minLength": 1
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"startedAt": {
|
|
126
|
+
"type": "string",
|
|
127
|
+
"format": "date-time"
|
|
128
|
+
},
|
|
129
|
+
"finishedAt": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"format": "date-time"
|
|
132
|
+
},
|
|
133
|
+
"exitResult": {
|
|
134
|
+
"type": "object",
|
|
135
|
+
"additionalProperties": false,
|
|
136
|
+
"required": [
|
|
137
|
+
"exitCode",
|
|
138
|
+
"signal"
|
|
139
|
+
],
|
|
140
|
+
"properties": {
|
|
141
|
+
"exitCode": {
|
|
142
|
+
"type": [
|
|
143
|
+
"integer",
|
|
144
|
+
"null"
|
|
145
|
+
]
|
|
146
|
+
},
|
|
147
|
+
"signal": {
|
|
148
|
+
"type": [
|
|
149
|
+
"string",
|
|
150
|
+
"null"
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
"outputArtifacts": {
|
|
156
|
+
"type": "object",
|
|
157
|
+
"additionalProperties": false,
|
|
158
|
+
"required": [
|
|
159
|
+
"stdout",
|
|
160
|
+
"stderr"
|
|
161
|
+
],
|
|
162
|
+
"properties": {
|
|
163
|
+
"stdout": {
|
|
164
|
+
"$ref": "#/$defs/materialArtifact"
|
|
165
|
+
},
|
|
166
|
+
"stderr": {
|
|
167
|
+
"$ref": "#/$defs/materialArtifact"
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
"workspaceAfter": {
|
|
172
|
+
"$ref": "workspace-snapshot-v1.schema.json"
|
|
173
|
+
},
|
|
174
|
+
"mutationDecision": {
|
|
175
|
+
"enum": [
|
|
176
|
+
"source_unchanged",
|
|
177
|
+
"invalidated_by_command_source_mutation"
|
|
178
|
+
]
|
|
179
|
+
},
|
|
180
|
+
"issueCodes": {
|
|
181
|
+
"type": "array",
|
|
182
|
+
"uniqueItems": true,
|
|
183
|
+
"items": {
|
|
184
|
+
"type": "string",
|
|
185
|
+
"minLength": 1
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
"signatureRef": {
|
|
189
|
+
"type": "object",
|
|
190
|
+
"additionalProperties": false,
|
|
191
|
+
"required": [
|
|
192
|
+
"path",
|
|
193
|
+
"sha256"
|
|
194
|
+
],
|
|
195
|
+
"properties": {
|
|
196
|
+
"path": {
|
|
197
|
+
"type": "string",
|
|
198
|
+
"minLength": 1
|
|
199
|
+
},
|
|
200
|
+
"sha256": {
|
|
201
|
+
"type": "string",
|
|
202
|
+
"pattern": "^sha256:[0-9a-f]{64}$"
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
"$defs": {
|
|
208
|
+
"materialArtifact": {
|
|
209
|
+
"type": "object",
|
|
210
|
+
"additionalProperties": false,
|
|
211
|
+
"required": [
|
|
212
|
+
"path",
|
|
213
|
+
"sha256",
|
|
214
|
+
"sizeBytes"
|
|
215
|
+
],
|
|
216
|
+
"properties": {
|
|
217
|
+
"path": {
|
|
218
|
+
"type": "string",
|
|
219
|
+
"minLength": 1
|
|
220
|
+
},
|
|
221
|
+
"sha256": {
|
|
222
|
+
"type": "string",
|
|
223
|
+
"pattern": "^sha256:[0-9a-f]{64}$"
|
|
224
|
+
},
|
|
225
|
+
"sizeBytes": {
|
|
226
|
+
"type": "integer",
|
|
227
|
+
"minimum": 0
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -41,7 +41,10 @@ function readSubject(subjectPath) {
|
|
|
41
41
|
|
|
42
42
|
export function attestFile({ subjectPath, workspaceRoot }) {
|
|
43
43
|
const subject = readSubject(subjectPath);
|
|
44
|
-
if (
|
|
44
|
+
if (new Set([
|
|
45
|
+
"OwlCodaQuickVerificationReceiptV1",
|
|
46
|
+
"OwlCodaQuickVerificationReceiptV2",
|
|
47
|
+
]).has(subject.value.schemaVersion)) {
|
|
45
48
|
const details = attestQuickReceiptDetailsFromBytes({
|
|
46
49
|
receiptPath: subject.absolutePath,
|
|
47
50
|
receiptBytes: subject.bytes,
|
|
@@ -110,10 +110,14 @@ function validOutputArtifact(value) {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
export function validQuickReceiptShape(receipt) {
|
|
113
|
-
|
|
113
|
+
const receiptV1 = receipt?.schemaVersion === "OwlCodaQuickVerificationReceiptV1";
|
|
114
|
+
const receiptV2 = receipt?.schemaVersion === "OwlCodaQuickVerificationReceiptV2";
|
|
115
|
+
const requiredKeys = receiptV2
|
|
116
|
+
? [...RECEIPT_REQUIRED_KEYS, "inputArtifacts"]
|
|
117
|
+
: RECEIPT_REQUIRED_KEYS;
|
|
118
|
+
if (!(receiptV1 || receiptV2) || !hasExactKeys(receipt, requiredKeys, ["signatureRef"])) return false;
|
|
114
119
|
if (
|
|
115
|
-
receipt.
|
|
116
|
-
|| typeof receipt.receiptId !== "string"
|
|
120
|
+
typeof receipt.receiptId !== "string"
|
|
117
121
|
|| receipt.receiptId.length === 0
|
|
118
122
|
|| receipt.assurance !== "captured_verification"
|
|
119
123
|
|| receipt.authorizationGranted !== false
|
|
@@ -145,6 +149,12 @@ export function validQuickReceiptShape(receipt) {
|
|
|
145
149
|
) {
|
|
146
150
|
return false;
|
|
147
151
|
}
|
|
152
|
+
if (receiptV2 && (
|
|
153
|
+
!hasExactKeys(receipt.inputArtifacts, ["stdin"])
|
|
154
|
+
|| !validOutputArtifact(receipt.inputArtifacts.stdin)
|
|
155
|
+
)) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
148
158
|
if (receipt.signatureRef !== undefined) {
|
|
149
159
|
if (!hasExactKeys(receipt.signatureRef, ["path", "sha256"])
|
|
150
160
|
|| typeof receipt.signatureRef.path !== "string"
|
|
@@ -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.18.3",
|
|
24
|
+
coreManifestSha256: "sha256:cc7a923649592084150828f24f0d90f280af92514bafdbc4999f4015b3175d5c",
|
|
25
|
+
},
|
|
21
26
|
{
|
|
22
27
|
contractVersion: "0.2",
|
|
23
28
|
coreVersion: "0.18.2",
|
|
@@ -194,7 +199,11 @@ export function attestQuickReceiptDetailsFromBytes({
|
|
|
194
199
|
throw error;
|
|
195
200
|
}
|
|
196
201
|
|
|
197
|
-
const issues = [
|
|
202
|
+
const issues = [
|
|
203
|
+
...receipt.issueCodes.filter((issue) => issue !== "quick_ignored_artifact_unbound"),
|
|
204
|
+
"signature_absent",
|
|
205
|
+
"anchor_absent",
|
|
206
|
+
];
|
|
198
207
|
const materials = [];
|
|
199
208
|
let materialMissing = false;
|
|
200
209
|
let materialMismatch = false;
|
|
@@ -233,7 +242,12 @@ export function attestQuickReceiptDetailsFromBytes({
|
|
|
233
242
|
const commandWorkspaceMatches = selectedWorkspaceRoot !== undefined
|
|
234
243
|
&& receiptWorkspaceRoot === selectedWorkspaceRoot;
|
|
235
244
|
if (commandWorkspaceMatches) {
|
|
236
|
-
|
|
245
|
+
const boundArtifacts = [
|
|
246
|
+
...(receipt.inputArtifacts === undefined ? [] : [receipt.inputArtifacts.stdin]),
|
|
247
|
+
receipt.outputArtifacts.stdout,
|
|
248
|
+
receipt.outputArtifacts.stderr,
|
|
249
|
+
];
|
|
250
|
+
for (const artifact of boundArtifacts) {
|
|
237
251
|
try {
|
|
238
252
|
const materialPath = resolveBoundMaterial(receiptWorkspaceRoot, artifact.path);
|
|
239
253
|
const { bytes } = readFileBytesBounded(materialPath);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": "OwlCodaRunKitRegistryReleasePolicyV1",
|
|
3
3
|
"status": "release_candidate_unpublished",
|
|
4
4
|
"packageName": "owlrunkit",
|
|
5
|
-
"candidateVersion": "0.18.
|
|
5
|
+
"candidateVersion": "0.18.3",
|
|
6
6
|
"registry": "https://registry.npmjs.org",
|
|
7
7
|
"registryRequiredBeforeAdoption": true,
|
|
8
8
|
"exactVersionRequired": true,
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
],
|
|
24
24
|
"currentPublishedRelease": {
|
|
25
25
|
"packageName": "owlrunkit",
|
|
26
|
-
"version": "0.18.
|
|
27
|
-
"shasum": "
|
|
28
|
-
"integrity": "sha512-
|
|
29
|
-
"tarballUrl": "https://registry.npmjs.org/owlrunkit/-/owlrunkit-0.18.
|
|
26
|
+
"version": "0.18.2",
|
|
27
|
+
"shasum": "4a85e31dad306260e4a6b1fcbccb3807e8647a0a",
|
|
28
|
+
"integrity": "sha512-Clpr8UQdNitzMMmQHUtjs9mesmukUqZrQ5nrda+Ryo8SuvECH8eBiis378NnzV1lI79kmljv49ToRRe867zlBg==",
|
|
29
|
+
"tarballUrl": "https://registry.npmjs.org/owlrunkit/-/owlrunkit-0.18.2.tgz"
|
|
30
30
|
},
|
|
31
31
|
"registryRelease": null,
|
|
32
32
|
"registryEvidenceMustBeExternal": 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.18.
|
|
6
|
-
"documentNamespace": "https://owlcoda.com/spdx/owlrunkit/0.18.
|
|
5
|
+
"name": "owlrunkit-0.18.3",
|
|
6
|
+
"documentNamespace": "https://owlcoda.com/spdx/owlrunkit/0.18.3",
|
|
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.18.
|
|
18
|
-
"downloadLocation": "https://registry.npmjs.org/owlrunkit/-/owlrunkit-0.18.
|
|
17
|
+
"versionInfo": "0.18.3",
|
|
18
|
+
"downloadLocation": "https://registry.npmjs.org/owlrunkit/-/owlrunkit-0.18.3.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.18.
|
|
27
|
+
"referenceLocator": "pkg:npm/owlrunkit@0.18.3"
|
|
28
28
|
}
|
|
29
29
|
]
|
|
30
30
|
}
|
|
@@ -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.3";
|
|
18
18
|
export const RUNTIME_ROOT = ".owlcoda/runkit";
|
|
19
19
|
|
|
20
20
|
const CORE_FILES = [
|
|
@@ -126,6 +126,7 @@ const CORE_FILES = [
|
|
|
126
126
|
"schemas/verification-context-v1.schema.json",
|
|
127
127
|
"schemas/team-project-definition-v1.schema.json",
|
|
128
128
|
"schemas/team-project-event-v1.schema.json",
|
|
129
|
+
"schemas/team-project-event-v2.schema.json",
|
|
129
130
|
"schemas/team-project-successor-receipt-v1.schema.json",
|
|
130
131
|
"schemas/team-project-status-v1.schema.json",
|
|
131
132
|
"schemas/usage-mode-recommendation-v1.schema.json",
|
|
@@ -59,7 +59,7 @@ function trustedReceiptRoot(workspaceRoot) {
|
|
|
59
59
|
return { status: "valid", root };
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
export function readLocalQuickMetrics(workspaceRoot) {
|
|
62
|
+
export function readLocalQuickMetrics(workspaceRoot, { verbose = false } = {}) {
|
|
63
63
|
const selected = trustedReceiptRoot(workspaceRoot);
|
|
64
64
|
if (selected.status === "invalid") {
|
|
65
65
|
return {
|
|
@@ -68,7 +68,7 @@ export function readLocalQuickMetrics(workspaceRoot) {
|
|
|
68
68
|
schemaVersion: "OwlCodaRunKitLocalMetricsV1",
|
|
69
69
|
telemetry: false,
|
|
70
70
|
networkRequests: 0,
|
|
71
|
-
inputs: [],
|
|
71
|
+
...(verbose ? { inputs: [] } : {}),
|
|
72
72
|
quick: {
|
|
73
73
|
total: 0,
|
|
74
74
|
passed: 0,
|
|
@@ -117,7 +117,7 @@ export function readLocalQuickMetrics(workspaceRoot) {
|
|
|
117
117
|
schemaVersion: "OwlCodaRunKitLocalMetricsV1",
|
|
118
118
|
telemetry: false,
|
|
119
119
|
networkRequests: 0,
|
|
120
|
-
inputs: receiptPaths,
|
|
120
|
+
...(verbose ? { inputs: receiptPaths } : {}),
|
|
121
121
|
quick: totals,
|
|
122
122
|
authorizationGranted: false,
|
|
123
123
|
};
|
|
@@ -97,20 +97,36 @@ function cleanOutputLine(line) {
|
|
|
97
97
|
.trim();
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
function
|
|
101
|
-
|
|
102
|
-
let text = bytes.subarray(start).toString("utf8");
|
|
103
|
-
if (start > 0) {
|
|
104
|
-
const firstLineBreak = text.indexOf("\n");
|
|
105
|
-
if (firstLineBreak >= 0) text = text.slice(firstLineBreak + 1);
|
|
106
|
-
}
|
|
107
|
-
const lines = text
|
|
100
|
+
function cleanOutputLines(text) {
|
|
101
|
+
return text
|
|
108
102
|
.split(/\r?\n/u)
|
|
109
103
|
.map(cleanOutputLine)
|
|
110
104
|
.filter(Boolean);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function summarizeOutputBytes(bytes) {
|
|
108
|
+
if (bytes.byteLength <= OUTPUT_SUMMARY_BYTES) {
|
|
109
|
+
const lines = cleanOutputLines(bytes.toString("utf8"));
|
|
110
|
+
return {
|
|
111
|
+
headLines: lines.slice(0, OUTPUT_SUMMARY_LINES),
|
|
112
|
+
tailLines: lines.slice(-OUTPUT_SUMMARY_LINES),
|
|
113
|
+
truncated: lines.length > OUTPUT_SUMMARY_LINES * 2,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const windowBytes = Math.floor(OUTPUT_SUMMARY_BYTES / 2);
|
|
118
|
+
let headText = bytes.subarray(0, windowBytes).toString("utf8");
|
|
119
|
+
const lastHeadBreak = headText.lastIndexOf("\n");
|
|
120
|
+
if (lastHeadBreak >= 0) headText = headText.slice(0, lastHeadBreak + 1);
|
|
121
|
+
|
|
122
|
+
let tailText = bytes.subarray(bytes.byteLength - windowBytes).toString("utf8");
|
|
123
|
+
const firstTailBreak = tailText.indexOf("\n");
|
|
124
|
+
if (firstTailBreak >= 0) tailText = tailText.slice(firstTailBreak + 1);
|
|
125
|
+
|
|
111
126
|
return {
|
|
112
|
-
|
|
113
|
-
|
|
127
|
+
headLines: cleanOutputLines(headText).slice(0, OUTPUT_SUMMARY_LINES),
|
|
128
|
+
tailLines: cleanOutputLines(tailText).slice(-OUTPUT_SUMMARY_LINES),
|
|
129
|
+
truncated: true,
|
|
114
130
|
};
|
|
115
131
|
}
|
|
116
132
|
|
|
@@ -145,10 +161,59 @@ function validateQuickReceiptStore(workspaceRoot, store) {
|
|
|
145
161
|
assertStorePath(root, store.receiptRoot, store.receiptRootIdentity, "directory");
|
|
146
162
|
assertStorePath(root, store.stdoutPath, store.stdoutIdentity, "file");
|
|
147
163
|
assertStorePath(root, store.stderrPath, store.stderrIdentity, "file");
|
|
164
|
+
if (store.stdinPath !== undefined) {
|
|
165
|
+
assertStorePath(root, store.stdinPath, store.stdinIdentity, "file");
|
|
166
|
+
const bytes = readFileSync(store.stdinPath);
|
|
167
|
+
if (
|
|
168
|
+
bytes.byteLength !== store.stdinArtifact.sizeBytes
|
|
169
|
+
|| sha256Bytes(bytes) !== store.stdinArtifact.sha256
|
|
170
|
+
) {
|
|
171
|
+
throw new Error(`Quick receipt store stdin material changed: ${store.stdinPath}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function persistQuickInputArtifact({ workspaceRoot, store, bytes }) {
|
|
177
|
+
if (!Buffer.isBuffer(bytes)) throw new Error("Quick stdin material must be bytes.");
|
|
178
|
+
validateQuickReceiptStore(workspaceRoot, store);
|
|
179
|
+
const stdinPath = path.join(store.receiptRoot, "stdin.bin");
|
|
180
|
+
writeFileSync(stdinPath, bytes, { flag: "wx", mode: 0o600 });
|
|
181
|
+
const stat = lstatSync(stdinPath);
|
|
182
|
+
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
183
|
+
throw new Error(`Quick receipt store stdin material is not trusted: ${stdinPath}`);
|
|
184
|
+
}
|
|
185
|
+
const root = realpathSync(workspaceRoot);
|
|
186
|
+
const resolved = realpathSync(stdinPath);
|
|
187
|
+
if (!withinRoot(root, resolved)) {
|
|
188
|
+
throw new Error(`Quick receipt store stdin material escapes the workspace: ${stdinPath}`);
|
|
189
|
+
}
|
|
190
|
+
store.stdinPath = stdinPath;
|
|
191
|
+
store.stdinIdentity = { dev: stat.dev, ino: stat.ino };
|
|
192
|
+
store.stdinArtifact = {
|
|
193
|
+
path: path.relative(root, resolved).split(path.sep).join("/"),
|
|
194
|
+
sha256: sha256Bytes(bytes),
|
|
195
|
+
sizeBytes: bytes.byteLength,
|
|
196
|
+
};
|
|
197
|
+
return {
|
|
198
|
+
artifact: store.stdinArtifact,
|
|
199
|
+
inputPath: stdinPath,
|
|
200
|
+
};
|
|
148
201
|
}
|
|
149
202
|
|
|
150
203
|
export function persistQuickReceipt({ workspaceRoot, store, receipt }) {
|
|
151
204
|
validateQuickReceiptStore(workspaceRoot, store);
|
|
205
|
+
const receiptStdin = receipt.inputArtifacts?.stdin;
|
|
206
|
+
if (
|
|
207
|
+
(store.stdinArtifact === undefined) !== (receiptStdin === undefined)
|
|
208
|
+
|| store.stdinArtifact !== undefined
|
|
209
|
+
&& (
|
|
210
|
+
receiptStdin.path !== store.stdinArtifact.path
|
|
211
|
+
|| receiptStdin.sha256 !== store.stdinArtifact.sha256
|
|
212
|
+
|| receiptStdin.sizeBytes !== store.stdinArtifact.sizeBytes
|
|
213
|
+
)
|
|
214
|
+
) {
|
|
215
|
+
throw new Error("Quick receipt stdin binding does not match the persisted material.");
|
|
216
|
+
}
|
|
152
217
|
const stdout = outputArtifact(workspaceRoot, store.stdoutPath);
|
|
153
218
|
const stderr = outputArtifact(workspaceRoot, store.stderrPath);
|
|
154
219
|
const complete = {
|
|
@@ -164,8 +229,10 @@ export function persistQuickReceipt({ workspaceRoot, store, receipt }) {
|
|
|
164
229
|
receiptPath: store.receiptPath,
|
|
165
230
|
receiptSha256: sha256Bytes(readFileSync(store.receiptPath)),
|
|
166
231
|
outputSummary: {
|
|
167
|
-
|
|
168
|
-
|
|
232
|
+
stdoutHead: stdout.summary.headLines,
|
|
233
|
+
stdoutTail: stdout.summary.tailLines,
|
|
234
|
+
stderrHead: stderr.summary.headLines,
|
|
235
|
+
stderrTail: stderr.summary.tailLines,
|
|
169
236
|
stdoutTruncated: stdout.summary.truncated,
|
|
170
237
|
stderrTruncated: stderr.summary.truncated,
|
|
171
238
|
},
|