@opengeni/artifact-tool 0.2.2
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/LICENSE +190 -0
- package/README.md +104 -0
- package/dist/bounded-zip.d.ts +28 -0
- package/dist/chunk-4BSENMAE.js +39 -0
- package/dist/chunk-4BSENMAE.js.map +1 -0
- package/dist/chunk-4FRFNNBM.js +276 -0
- package/dist/chunk-4FRFNNBM.js.map +1 -0
- package/dist/chunk-5XIA6KHZ.js +49 -0
- package/dist/chunk-5XIA6KHZ.js.map +1 -0
- package/dist/chunk-66CT2ZRP.js +2229 -0
- package/dist/chunk-66CT2ZRP.js.map +1 -0
- package/dist/chunk-72SYDU4O.js +319 -0
- package/dist/chunk-72SYDU4O.js.map +1 -0
- package/dist/chunk-B2ZJRZDA.js +279 -0
- package/dist/chunk-B2ZJRZDA.js.map +1 -0
- package/dist/chunk-DCTLTNPW.js +2458 -0
- package/dist/chunk-DCTLTNPW.js.map +1 -0
- package/dist/chunk-FGZM4FQ6.js +473 -0
- package/dist/chunk-FGZM4FQ6.js.map +1 -0
- package/dist/chunk-HTJBLBDE.js +591 -0
- package/dist/chunk-HTJBLBDE.js.map +1 -0
- package/dist/chunk-IYFXZQ34.js +5306 -0
- package/dist/chunk-IYFXZQ34.js.map +1 -0
- package/dist/chunk-J6N7IR44.js +172 -0
- package/dist/chunk-J6N7IR44.js.map +1 -0
- package/dist/chunk-N2IHNWBS.js +1002 -0
- package/dist/chunk-N2IHNWBS.js.map +1 -0
- package/dist/chunk-NAG44K67.js +549 -0
- package/dist/chunk-NAG44K67.js.map +1 -0
- package/dist/chunk-O24KTEGP.js +33 -0
- package/dist/chunk-O24KTEGP.js.map +1 -0
- package/dist/chunk-PI2AZVGK.js +2074 -0
- package/dist/chunk-PI2AZVGK.js.map +1 -0
- package/dist/chunk-Q2FFHEY7.js +40 -0
- package/dist/chunk-Q2FFHEY7.js.map +1 -0
- package/dist/chunk-RBGQADXA.js +57 -0
- package/dist/chunk-RBGQADXA.js.map +1 -0
- package/dist/chunk-U5G2BKM2.js +613 -0
- package/dist/chunk-U5G2BKM2.js.map +1 -0
- package/dist/chunk-VOJHMSM3.js +39 -0
- package/dist/chunk-VOJHMSM3.js.map +1 -0
- package/dist/chunk-VUNV25KB.js +16 -0
- package/dist/chunk-VUNV25KB.js.map +1 -0
- package/dist/chunk-XIFUNVBO.js +101 -0
- package/dist/chunk-XIFUNVBO.js.map +1 -0
- package/dist/chunk-ZCJYRJGB.js +489 -0
- package/dist/chunk-ZCJYRJGB.js.map +1 -0
- package/dist/chunk-ZZXPEE4S.js +2260 -0
- package/dist/chunk-ZZXPEE4S.js.map +1 -0
- package/dist/document-docx-api.d.ts +49 -0
- package/dist/document-docx-codec.d.ts +9 -0
- package/dist/document-docx-codec.js +4765 -0
- package/dist/document-docx-codec.js.map +1 -0
- package/dist/document-docx-import.d.ts +261 -0
- package/dist/document-docx-packer.d.ts +18 -0
- package/dist/document-docx-state.d.ts +12 -0
- package/dist/document-render.d.ts +3 -0
- package/dist/document-render.js +492 -0
- package/dist/document-render.js.map +1 -0
- package/dist/document.d.ts +432 -0
- package/dist/document.js +37 -0
- package/dist/document.js.map +1 -0
- package/dist/errors.d.ts +23 -0
- package/dist/file-blob.d.ts +11 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +186 -0
- package/dist/index.js.map +1 -0
- package/dist/inspection.d.ts +12 -0
- package/dist/kernel.d.ts +101 -0
- package/dist/lib-GQLPACFK.js +9746 -0
- package/dist/lib-GQLPACFK.js.map +1 -0
- package/dist/materializer-cli-entry.d.ts +2 -0
- package/dist/materializer-cli-entry.js +29 -0
- package/dist/materializer-cli-entry.js.map +1 -0
- package/dist/materializer-cli.d.ts +24 -0
- package/dist/materializer-cli.js +24 -0
- package/dist/materializer-cli.js.map +1 -0
- package/dist/native-raster.d.ts +10 -0
- package/dist/native.d.ts +129 -0
- package/dist/native.js +31 -0
- package/dist/native.js.map +1 -0
- package/dist/office-import.d.ts +48 -0
- package/dist/office-import.js +154 -0
- package/dist/office-import.js.map +1 -0
- package/dist/presentation-pptx-api.d.ts +60 -0
- package/dist/presentation-pptx-import.d.ts +5 -0
- package/dist/presentation-pptx-state-digest-765CZZED.js +10 -0
- package/dist/presentation-pptx-state-digest-765CZZED.js.map +1 -0
- package/dist/presentation-pptx-state-digest.d.ts +3 -0
- package/dist/presentation-pptx-state.d.ts +4 -0
- package/dist/presentation-pptx-xml.d.ts +29 -0
- package/dist/presentation-pptx.d.ts +5 -0
- package/dist/presentation-pptx.js +2365 -0
- package/dist/presentation-pptx.js.map +1 -0
- package/dist/presentation-render.d.ts +29 -0
- package/dist/presentation-render.js +375 -0
- package/dist/presentation-render.js.map +1 -0
- package/dist/presentation.d.ts +565 -0
- package/dist/presentation.js +71 -0
- package/dist/presentation.js.map +1 -0
- package/dist/production-composite.d.ts +68 -0
- package/dist/production-document.d.ts +2 -0
- package/dist/production-document.js +176 -0
- package/dist/production-document.js.map +1 -0
- package/dist/production-native-codecs.d.ts +94 -0
- package/dist/production-presentation.d.ts +2 -0
- package/dist/production-presentation.js +176 -0
- package/dist/production-presentation.js.map +1 -0
- package/dist/production-sha256.d.ts +2 -0
- package/dist/production-spreadsheet-native.d.ts +12 -0
- package/dist/production-spreadsheet.d.ts +2 -0
- package/dist/production-spreadsheet.js +176 -0
- package/dist/production-spreadsheet.js.map +1 -0
- package/dist/production.d.ts +96 -0
- package/dist/raster-image.d.ts +34 -0
- package/dist/reference.d.ts +17 -0
- package/dist/reference.js +574 -0
- package/dist/reference.js.map +1 -0
- package/dist/runtime-cli-entry.d.ts +2 -0
- package/dist/runtime-cli-entry.js +21 -0
- package/dist/runtime-cli-entry.js.map +1 -0
- package/dist/runtime-cli.d.ts +76 -0
- package/dist/runtime-cli.js +26 -0
- package/dist/runtime-cli.js.map +1 -0
- package/dist/runtime-development.d.ts +73 -0
- package/dist/runtime-development.js +30 -0
- package/dist/runtime-development.js.map +1 -0
- package/dist/runtime-receipt.d.ts +19 -0
- package/dist/runtime.d.ts +355 -0
- package/dist/runtime.js +42 -0
- package/dist/runtime.js.map +1 -0
- package/dist/snapshot.d.ts +26 -0
- package/dist/snapshot.js +61 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/spreadsheet-address.d.ts +18 -0
- package/dist/spreadsheet-file.d.ts +17 -0
- package/dist/spreadsheet-formula.d.ts +67 -0
- package/dist/spreadsheet-image.d.ts +17 -0
- package/dist/spreadsheet-render.d.ts +5 -0
- package/dist/spreadsheet-render.js +752 -0
- package/dist/spreadsheet-render.js.map +1 -0
- package/dist/spreadsheet-types.d.ts +155 -0
- package/dist/spreadsheet-xlsx-api.d.ts +88 -0
- package/dist/spreadsheet-xlsx-codec.d.ts +16 -0
- package/dist/spreadsheet-xlsx-codec.js +16 -0
- package/dist/spreadsheet-xlsx-codec.js.map +1 -0
- package/dist/spreadsheet-xlsx-state.d.ts +16 -0
- package/dist/spreadsheet.d.ts +724 -0
- package/dist/spreadsheet.js +67 -0
- package/dist/spreadsheet.js.map +1 -0
- package/package.json +129 -0
- package/src/bounded-zip.ts +450 -0
- package/src/document-docx-api.ts +64 -0
- package/src/document-docx-codec.ts +2253 -0
- package/src/document-docx-import.ts +3814 -0
- package/src/document-docx-packer.ts +72 -0
- package/src/document-docx-state.ts +59 -0
- package/src/document-render.ts +615 -0
- package/src/document.ts +2800 -0
- package/src/errors.ts +45 -0
- package/src/file-blob.ts +43 -0
- package/src/index.ts +4 -0
- package/src/inspection.ts +50 -0
- package/src/kernel.ts +525 -0
- package/src/materializer-cli-entry.ts +11 -0
- package/src/materializer-cli.ts +743 -0
- package/src/native-raster.ts +141 -0
- package/src/native.ts +390 -0
- package/src/office-import.ts +243 -0
- package/src/presentation-pptx-api.ts +107 -0
- package/src/presentation-pptx-import.ts +2065 -0
- package/src/presentation-pptx-state-digest.ts +46 -0
- package/src/presentation-pptx-state.ts +18 -0
- package/src/presentation-pptx-xml.ts +473 -0
- package/src/presentation-pptx.ts +447 -0
- package/src/presentation-render.ts +532 -0
- package/src/presentation.ts +2599 -0
- package/src/production-composite.ts +805 -0
- package/src/production-document.ts +2 -0
- package/src/production-native-codecs.ts +1141 -0
- package/src/production-presentation.ts +2 -0
- package/src/production-sha256.ts +150 -0
- package/src/production-spreadsheet-native.ts +686 -0
- package/src/production-spreadsheet.ts +2 -0
- package/src/production.ts +1022 -0
- package/src/raster-image.ts +586 -0
- package/src/reference.ts +17 -0
- package/src/runtime-cli-entry.ts +13 -0
- package/src/runtime-cli.ts +670 -0
- package/src/runtime-development.ts +689 -0
- package/src/runtime-receipt.ts +169 -0
- package/src/runtime.ts +1463 -0
- package/src/snapshot.ts +88 -0
- package/src/spreadsheet-address.ts +90 -0
- package/src/spreadsheet-file.ts +71 -0
- package/src/spreadsheet-formula.ts +1212 -0
- package/src/spreadsheet-image.ts +242 -0
- package/src/spreadsheet-render.ts +985 -0
- package/src/spreadsheet-types.ts +171 -0
- package/src/spreadsheet-xlsx-api.ts +123 -0
- package/src/spreadsheet-xlsx-codec.ts +3063 -0
- package/src/spreadsheet-xlsx-state.ts +191 -0
- package/src/spreadsheet.ts +5195 -0
|
@@ -0,0 +1,670 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { open, realpath, stat } from "node:fs/promises";
|
|
5
|
+
import { isAbsolute, relative, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
|
+
import {
|
|
8
|
+
ARTIFACT_RUNTIME_ENVIRONMENT,
|
|
9
|
+
ArtifactRuntimeError,
|
|
10
|
+
loadArtifactKernelRuntime,
|
|
11
|
+
locateArtifactRuntimeDependencies,
|
|
12
|
+
resolveArtifactRuntimeTarget,
|
|
13
|
+
validateArtifactRuntimeInstallationManifest,
|
|
14
|
+
validateCompleteArtifactRuntimeReleaseManifest,
|
|
15
|
+
type ArtifactKernelRuntime,
|
|
16
|
+
type ArtifactRuntimeInstallationManifest,
|
|
17
|
+
type ArtifactRuntimeTarget,
|
|
18
|
+
} from "./runtime";
|
|
19
|
+
|
|
20
|
+
const MAX_INSTALLATION_MANIFEST_BYTES = 256 * 1024;
|
|
21
|
+
const MAX_RELEASE_MANIFEST_BYTES = 1024 * 1024;
|
|
22
|
+
const MAX_ARTIFACT_TOOL_ARCHIVE_BYTES = 256 * 1024 * 1024;
|
|
23
|
+
const MAX_JAVASCRIPT_ENTRYPOINT_BYTES = 32 * 1024 * 1024;
|
|
24
|
+
const MAX_SKILL_FACADE_SUPPORT_FILE_BYTES = 128 * 1024 * 1024;
|
|
25
|
+
const MAX_KERNEL_ASSET_BYTES = 512 * 1024 * 1024;
|
|
26
|
+
|
|
27
|
+
type RuntimeEnvironment = Readonly<Record<string, string | undefined>>;
|
|
28
|
+
|
|
29
|
+
export type VerifiedArtifactRuntimeLocation = Readonly<{
|
|
30
|
+
schemaVersion: 1;
|
|
31
|
+
target: ArtifactRuntimeTarget;
|
|
32
|
+
manifestPath: string;
|
|
33
|
+
releaseManifestPath: string;
|
|
34
|
+
artifactTool: Readonly<{
|
|
35
|
+
packageName: "@opengeni/artifact-tool";
|
|
36
|
+
packageVersion: string;
|
|
37
|
+
integrity: `sha512-${string}`;
|
|
38
|
+
}>;
|
|
39
|
+
artifactToolArchive?: string;
|
|
40
|
+
skillFacadeEntrypoint: string;
|
|
41
|
+
skillFacadeSupportFiles?: readonly string[];
|
|
42
|
+
kernel: Readonly<{
|
|
43
|
+
packageName: string;
|
|
44
|
+
packageVersion: string;
|
|
45
|
+
entrypoint: string;
|
|
46
|
+
asset: string;
|
|
47
|
+
supportFiles: readonly string[];
|
|
48
|
+
buildIdentity: string;
|
|
49
|
+
}>;
|
|
50
|
+
}>;
|
|
51
|
+
|
|
52
|
+
export type LocateArtifactRuntimeOptions = Readonly<{
|
|
53
|
+
environment?: RuntimeEnvironment;
|
|
54
|
+
/** Test/build override. The executable always derives the current host target. */
|
|
55
|
+
expectedTarget?: ArtifactRuntimeTarget;
|
|
56
|
+
}>;
|
|
57
|
+
|
|
58
|
+
export type DoctorArtifactRuntimeOptions = LocateArtifactRuntimeOptions &
|
|
59
|
+
Readonly<{
|
|
60
|
+
importer?: (specifier: string) => unknown | Promise<unknown>;
|
|
61
|
+
probe?: (module: Record<string, unknown>) => void | Promise<void>;
|
|
62
|
+
}>;
|
|
63
|
+
|
|
64
|
+
export type VerifiedArtifactKernelRuntime = Readonly<{
|
|
65
|
+
location: VerifiedArtifactRuntimeLocation;
|
|
66
|
+
runtime: ArtifactKernelRuntime;
|
|
67
|
+
}>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Verifies the complete local release/install chain without evaluating any
|
|
71
|
+
* package code. All reads are local, bounded, streamed where large, and pinned
|
|
72
|
+
* to exact byte counts plus SHA-256 digests.
|
|
73
|
+
*/
|
|
74
|
+
export async function locateVerifiedArtifactRuntime(
|
|
75
|
+
options: LocateArtifactRuntimeOptions = {},
|
|
76
|
+
): Promise<VerifiedArtifactRuntimeLocation> {
|
|
77
|
+
const environment = options.environment ?? process.env;
|
|
78
|
+
const manifestInput = requiredEnvironmentPath(environment, ARTIFACT_RUNTIME_ENVIRONMENT.manifest);
|
|
79
|
+
const facadeInput = requiredEnvironmentPath(
|
|
80
|
+
environment,
|
|
81
|
+
ARTIFACT_RUNTIME_ENVIRONMENT.toolEntrypoint,
|
|
82
|
+
);
|
|
83
|
+
const expectedTarget = options.expectedTarget ?? resolveCurrentArtifactRuntimeTarget();
|
|
84
|
+
|
|
85
|
+
const manifestPath = await canonicalFile(manifestInput, "installation manifest");
|
|
86
|
+
const installationFile = await readInstallationManifest(manifestPath);
|
|
87
|
+
const installation = installationFile.manifest;
|
|
88
|
+
const dependencies = locateArtifactRuntimeDependencies(
|
|
89
|
+
installation,
|
|
90
|
+
pathToFileURL(manifestPath),
|
|
91
|
+
expectedTarget,
|
|
92
|
+
);
|
|
93
|
+
assertCanonicalInstallationManifest(installationFile.bytes, dependencies.manifest);
|
|
94
|
+
const installationRoot = await realpath(resolve(manifestPath, ".."));
|
|
95
|
+
|
|
96
|
+
const releaseManifestPath = await confinedCanonicalFile(
|
|
97
|
+
dependencies.releaseManifestUrl,
|
|
98
|
+
installationRoot,
|
|
99
|
+
"release manifest",
|
|
100
|
+
);
|
|
101
|
+
const artifactToolArchive = dependencies.artifactToolArchiveUrl
|
|
102
|
+
? await confinedCanonicalFile(
|
|
103
|
+
dependencies.artifactToolArchiveUrl,
|
|
104
|
+
installationRoot,
|
|
105
|
+
"artifact-tool archive",
|
|
106
|
+
)
|
|
107
|
+
: undefined;
|
|
108
|
+
const skillFacadeEntrypoint = await confinedCanonicalFile(
|
|
109
|
+
dependencies.skillFacadeEntrypoint,
|
|
110
|
+
installationRoot,
|
|
111
|
+
"skill facade entrypoint",
|
|
112
|
+
);
|
|
113
|
+
const skillFacadeSupportFiles = await Promise.all(
|
|
114
|
+
dependencies.skillFacadeSupportFiles.map((url, index) =>
|
|
115
|
+
confinedCanonicalFile(url, installationRoot, `skill facade support file ${index}`),
|
|
116
|
+
),
|
|
117
|
+
);
|
|
118
|
+
const kernelEntrypoint = await confinedCanonicalFile(
|
|
119
|
+
dependencies.kernelEntrypoint,
|
|
120
|
+
installationRoot,
|
|
121
|
+
"kernel entrypoint",
|
|
122
|
+
);
|
|
123
|
+
const kernelAsset = await confinedCanonicalFile(
|
|
124
|
+
dependencies.kernelAsset,
|
|
125
|
+
installationRoot,
|
|
126
|
+
"kernel asset",
|
|
127
|
+
);
|
|
128
|
+
const kernelSupportFiles = await Promise.all(
|
|
129
|
+
dependencies.kernelSupportFiles.map((url, index) =>
|
|
130
|
+
confinedCanonicalFile(url, installationRoot, `kernel support file ${index}`),
|
|
131
|
+
),
|
|
132
|
+
);
|
|
133
|
+
const configuredFacade = await canonicalFile(facadeInput, "configured skill facade entrypoint");
|
|
134
|
+
if (configuredFacade !== skillFacadeEntrypoint) {
|
|
135
|
+
integrity(
|
|
136
|
+
`${ARTIFACT_RUNTIME_ENVIRONMENT.toolEntrypoint} does not name the manifest-pinned skill facade entrypoint`,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const releaseBytes = await readAndVerifyFile(
|
|
141
|
+
releaseManifestPath,
|
|
142
|
+
installation.releaseManifest,
|
|
143
|
+
MAX_RELEASE_MANIFEST_BYTES,
|
|
144
|
+
"release manifest",
|
|
145
|
+
);
|
|
146
|
+
const release = parseReleaseManifest(releaseBytes);
|
|
147
|
+
assertCanonicalReleaseManifest(releaseBytes, release);
|
|
148
|
+
if (!sameJson(release.artifactTool, installation.artifactTool)) {
|
|
149
|
+
integrity("Installed artifact-tool identity does not match the complete release manifest");
|
|
150
|
+
}
|
|
151
|
+
if (artifactToolArchive && installation.artifactToolArchive) {
|
|
152
|
+
const archiveBytes = await readAndVerifyFile(
|
|
153
|
+
artifactToolArchive,
|
|
154
|
+
installation.artifactToolArchive,
|
|
155
|
+
MAX_ARTIFACT_TOOL_ARCHIVE_BYTES,
|
|
156
|
+
"artifact-tool archive",
|
|
157
|
+
);
|
|
158
|
+
const archiveIntegrity = `sha512-${createHash("sha512").update(archiveBytes).digest("base64")}`;
|
|
159
|
+
if (archiveIntegrity !== installation.artifactTool.integrity) {
|
|
160
|
+
integrity("Packed artifact-tool archive differs from its installation identity");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const releasedKernel = release.targets.find((entry) => entry.target === installation.target);
|
|
164
|
+
if (!releasedKernel || !sameJson(releasedKernel, installation.kernel)) {
|
|
165
|
+
integrity("Installed kernel package does not match its exact release target manifest");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Do not evaluate the facade or kernel package until every executable/data
|
|
169
|
+
// file in the chain has independently passed its byte proof.
|
|
170
|
+
await verifyFile(
|
|
171
|
+
skillFacadeEntrypoint,
|
|
172
|
+
installation.skillFacadeEntrypoint,
|
|
173
|
+
MAX_JAVASCRIPT_ENTRYPOINT_BYTES,
|
|
174
|
+
"skill facade entrypoint",
|
|
175
|
+
);
|
|
176
|
+
for (const [index, path] of skillFacadeSupportFiles.entries()) {
|
|
177
|
+
await verifyFile(
|
|
178
|
+
path,
|
|
179
|
+
installation.skillFacadeSupportFiles![index]!,
|
|
180
|
+
MAX_SKILL_FACADE_SUPPORT_FILE_BYTES,
|
|
181
|
+
`skill facade support file ${index}`,
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
await verifyFile(
|
|
185
|
+
kernelEntrypoint,
|
|
186
|
+
installation.kernel.entrypoint,
|
|
187
|
+
MAX_JAVASCRIPT_ENTRYPOINT_BYTES,
|
|
188
|
+
"kernel entrypoint",
|
|
189
|
+
);
|
|
190
|
+
await verifyFile(kernelAsset, installation.kernel.asset, MAX_KERNEL_ASSET_BYTES, "kernel asset");
|
|
191
|
+
for (const [index, path] of kernelSupportFiles.entries()) {
|
|
192
|
+
await verifyFile(
|
|
193
|
+
path,
|
|
194
|
+
installation.kernel.supportFiles[index]!,
|
|
195
|
+
MAX_JAVASCRIPT_ENTRYPOINT_BYTES,
|
|
196
|
+
`kernel support file ${index}`,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return Object.freeze({
|
|
201
|
+
schemaVersion: 1,
|
|
202
|
+
target: installation.target,
|
|
203
|
+
manifestPath,
|
|
204
|
+
releaseManifestPath,
|
|
205
|
+
artifactTool: Object.freeze({ ...installation.artifactTool }),
|
|
206
|
+
...(artifactToolArchive ? { artifactToolArchive } : {}),
|
|
207
|
+
skillFacadeEntrypoint,
|
|
208
|
+
...(skillFacadeSupportFiles.length > 0
|
|
209
|
+
? { skillFacadeSupportFiles: Object.freeze([...skillFacadeSupportFiles]) }
|
|
210
|
+
: {}),
|
|
211
|
+
kernel: Object.freeze({
|
|
212
|
+
packageName: installation.kernel.packageName,
|
|
213
|
+
packageVersion: installation.kernel.packageVersion,
|
|
214
|
+
entrypoint: kernelEntrypoint,
|
|
215
|
+
asset: kernelAsset,
|
|
216
|
+
supportFiles: Object.freeze([...kernelSupportFiles]),
|
|
217
|
+
buildIdentity: installation.kernel.buildIdentity,
|
|
218
|
+
}),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Verify first, then evaluate the one pinned bootstrap and probe its facade. */
|
|
223
|
+
export async function doctorVerifiedArtifactRuntime(
|
|
224
|
+
options: DoctorArtifactRuntimeOptions = {},
|
|
225
|
+
): Promise<VerifiedArtifactRuntimeLocation> {
|
|
226
|
+
const location = await locateVerifiedArtifactRuntime(options);
|
|
227
|
+
const importer = options.importer ?? defaultImporter;
|
|
228
|
+
let imported: unknown;
|
|
229
|
+
try {
|
|
230
|
+
imported = await importer(pathToFileURL(location.skillFacadeEntrypoint).href);
|
|
231
|
+
} catch (cause) {
|
|
232
|
+
throw new ArtifactRuntimeError(
|
|
233
|
+
"ARTIFACT_RUNTIME_UNAVAILABLE",
|
|
234
|
+
"Verified skill facade bootstrap could not be evaluated",
|
|
235
|
+
{ cause },
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
if (typeof imported !== "object" || imported === null || Array.isArray(imported)) {
|
|
239
|
+
incompatible("skill facade bootstrap did not export a module namespace");
|
|
240
|
+
}
|
|
241
|
+
const module = imported as Record<string, unknown>;
|
|
242
|
+
if (options.probe) {
|
|
243
|
+
await options.probe(module);
|
|
244
|
+
} else {
|
|
245
|
+
probeVerifiedArtifactSkillFacade(module, location);
|
|
246
|
+
}
|
|
247
|
+
return location;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Loads only the manifest-pinned native kernel package after verifying every
|
|
252
|
+
* selected runtime byte. A second complete verification after evaluation
|
|
253
|
+
* closes ordinary replacement races; production additionally mounts this
|
|
254
|
+
* installation read-only inside the materializer sandbox.
|
|
255
|
+
*/
|
|
256
|
+
export async function loadVerifiedArtifactKernelRuntime(
|
|
257
|
+
options: LocateArtifactRuntimeOptions = {},
|
|
258
|
+
): Promise<VerifiedArtifactKernelRuntime> {
|
|
259
|
+
const environment = options.environment ?? process.env;
|
|
260
|
+
const expectedTarget = options.expectedTarget ?? resolveCurrentArtifactRuntimeTarget();
|
|
261
|
+
const before = await locateVerifiedArtifactRuntime({ environment, expectedTarget });
|
|
262
|
+
const installation = (await readInstallationManifest(before.manifestPath)).manifest;
|
|
263
|
+
const dependencies = locateArtifactRuntimeDependencies(
|
|
264
|
+
installation,
|
|
265
|
+
pathToFileURL(before.manifestPath),
|
|
266
|
+
expectedTarget,
|
|
267
|
+
);
|
|
268
|
+
const runtime = await loadArtifactKernelRuntime(dependencies);
|
|
269
|
+
const after = await locateVerifiedArtifactRuntime({ environment, expectedTarget });
|
|
270
|
+
if (
|
|
271
|
+
!sameJson(before, after) ||
|
|
272
|
+
runtime.kind !== "native" ||
|
|
273
|
+
runtime.target !== before.target ||
|
|
274
|
+
runtime.buildIdentity !== before.kernel.buildIdentity
|
|
275
|
+
) {
|
|
276
|
+
integrity("Loaded kernel runtime does not match the verified installation");
|
|
277
|
+
}
|
|
278
|
+
return Object.freeze({ location: after, runtime });
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** Deterministic bytes used by release tooling and verified by the locator. */
|
|
282
|
+
export function canonicalArtifactRuntimeReleaseManifestBytes(value: unknown): Uint8Array {
|
|
283
|
+
const manifest = validateCompleteArtifactRuntimeReleaseManifest(value);
|
|
284
|
+
return new TextEncoder().encode(`${JSON.stringify(manifest, null, 2)}\n`);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** Deterministic installation bytes; the strict locator rejects other encodings. */
|
|
288
|
+
export function canonicalArtifactRuntimeInstallationManifestBytes(value: unknown): Uint8Array {
|
|
289
|
+
const manifest = validateArtifactRuntimeInstallationManifest(value);
|
|
290
|
+
return new TextEncoder().encode(`${JSON.stringify(manifest, null, 2)}\n`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export function resolveCurrentArtifactRuntimeTarget(): ArtifactRuntimeTarget {
|
|
294
|
+
const platform = process.platform;
|
|
295
|
+
const arch = process.arch;
|
|
296
|
+
if (platform !== "linux") {
|
|
297
|
+
return resolveArtifactRuntimeTarget({ platform, arch }).target;
|
|
298
|
+
}
|
|
299
|
+
return resolveArtifactRuntimeTarget({ platform, arch, libc: detectLinuxLibc() }).target;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export async function runArtifactRuntimeCli(
|
|
303
|
+
args: readonly string[],
|
|
304
|
+
environment: RuntimeEnvironment = process.env,
|
|
305
|
+
): Promise<string> {
|
|
306
|
+
const [command, format, ...rest] = args;
|
|
307
|
+
if ((command !== "locate" && command !== "doctor") || format !== "--json" || rest.length > 0) {
|
|
308
|
+
throw new ArtifactRuntimeError("ARTIFACT_RUNTIME_MANIFEST_INVALID", artifactRuntimeCliUsage());
|
|
309
|
+
}
|
|
310
|
+
const location =
|
|
311
|
+
command === "doctor"
|
|
312
|
+
? await doctorVerifiedArtifactRuntime({ environment })
|
|
313
|
+
: await locateVerifiedArtifactRuntime({ environment });
|
|
314
|
+
return `${JSON.stringify(location)}\n`;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function artifactRuntimeCliUsage(): string {
|
|
318
|
+
return "Usage: opengeni-artifact-runtime <locate|doctor> --json";
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async function readInstallationManifest(
|
|
322
|
+
path: string,
|
|
323
|
+
): Promise<Readonly<{ manifest: ArtifactRuntimeInstallationManifest; bytes: Uint8Array }>> {
|
|
324
|
+
const bytes = await readBoundedFile(
|
|
325
|
+
path,
|
|
326
|
+
MAX_INSTALLATION_MANIFEST_BYTES,
|
|
327
|
+
"installation manifest",
|
|
328
|
+
);
|
|
329
|
+
let parsed: unknown;
|
|
330
|
+
try {
|
|
331
|
+
parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
332
|
+
} catch (cause) {
|
|
333
|
+
throw new ArtifactRuntimeError(
|
|
334
|
+
"ARTIFACT_RUNTIME_MANIFEST_INVALID",
|
|
335
|
+
"Installation manifest is not strict UTF-8 JSON",
|
|
336
|
+
{ cause },
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
// locateArtifactRuntimeDependencies performs the exact schema validation.
|
|
340
|
+
return Object.freeze({
|
|
341
|
+
manifest: parsed as ArtifactRuntimeInstallationManifest,
|
|
342
|
+
bytes,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function parseReleaseManifest(
|
|
347
|
+
bytes: Uint8Array,
|
|
348
|
+
): ReturnType<typeof validateCompleteArtifactRuntimeReleaseManifest> {
|
|
349
|
+
let parsed: unknown;
|
|
350
|
+
try {
|
|
351
|
+
parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
352
|
+
} catch (cause) {
|
|
353
|
+
throw new ArtifactRuntimeError(
|
|
354
|
+
"ARTIFACT_RUNTIME_MANIFEST_INVALID",
|
|
355
|
+
"Release manifest is not strict UTF-8 JSON",
|
|
356
|
+
{ cause },
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
return validateCompleteArtifactRuntimeReleaseManifest(parsed);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function assertCanonicalReleaseManifest(
|
|
363
|
+
actual: Uint8Array,
|
|
364
|
+
manifest: ReturnType<typeof validateCompleteArtifactRuntimeReleaseManifest>,
|
|
365
|
+
): void {
|
|
366
|
+
const canonical = canonicalArtifactRuntimeReleaseManifestBytes(manifest);
|
|
367
|
+
if (!sameBytes(actual, canonical)) {
|
|
368
|
+
integrity("Release manifest bytes are not in the canonical deterministic form");
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function assertCanonicalInstallationManifest(
|
|
373
|
+
actual: Uint8Array,
|
|
374
|
+
manifest: ArtifactRuntimeInstallationManifest,
|
|
375
|
+
): void {
|
|
376
|
+
const canonical = canonicalArtifactRuntimeInstallationManifestBytes(manifest);
|
|
377
|
+
if (!sameBytes(actual, canonical)) {
|
|
378
|
+
integrity("Installation manifest bytes are not in the canonical deterministic form");
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async function confinedCanonicalFile(url: URL, root: string, name: string): Promise<string> {
|
|
383
|
+
const path = await canonicalFile(fileURLToPath(url), name);
|
|
384
|
+
const fromRoot = relative(root, path);
|
|
385
|
+
if (
|
|
386
|
+
fromRoot === ".." ||
|
|
387
|
+
fromRoot.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) ||
|
|
388
|
+
isAbsolute(fromRoot)
|
|
389
|
+
) {
|
|
390
|
+
throw new ArtifactRuntimeError(
|
|
391
|
+
"ARTIFACT_RUNTIME_MANIFEST_INVALID",
|
|
392
|
+
`${name} resolves outside the installation root`,
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
return path;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async function canonicalFile(input: string, name: string): Promise<string> {
|
|
399
|
+
if (!isAbsolute(input)) {
|
|
400
|
+
throw new ArtifactRuntimeError(
|
|
401
|
+
"ARTIFACT_RUNTIME_MANIFEST_INVALID",
|
|
402
|
+
`${name} path must be absolute`,
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
try {
|
|
406
|
+
const path = await realpath(input);
|
|
407
|
+
const metadata = await stat(path);
|
|
408
|
+
if (!metadata.isFile()) unavailable(`${name} is not a regular file`);
|
|
409
|
+
return path;
|
|
410
|
+
} catch (cause) {
|
|
411
|
+
if (cause instanceof ArtifactRuntimeError) throw cause;
|
|
412
|
+
throw new ArtifactRuntimeError("ARTIFACT_RUNTIME_UNAVAILABLE", `${name} is not available`, {
|
|
413
|
+
cause,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
async function readBoundedFile(
|
|
419
|
+
path: string,
|
|
420
|
+
maximumBytes: number,
|
|
421
|
+
name: string,
|
|
422
|
+
): Promise<Uint8Array> {
|
|
423
|
+
let handle;
|
|
424
|
+
try {
|
|
425
|
+
handle = await open(path, "r");
|
|
426
|
+
const before = await handle.stat();
|
|
427
|
+
if (!before.isFile()) unavailable(`${name} is not a regular file`);
|
|
428
|
+
if (before.size <= 0 || before.size > maximumBytes) {
|
|
429
|
+
integrity(`${name} size is outside its allowed bound`);
|
|
430
|
+
}
|
|
431
|
+
const bytes = new Uint8Array(await handle.readFile());
|
|
432
|
+
const after = await handle.stat();
|
|
433
|
+
if (after.size !== before.size || bytes.byteLength !== before.size) {
|
|
434
|
+
integrity(`${name} changed while it was being read`);
|
|
435
|
+
}
|
|
436
|
+
return bytes;
|
|
437
|
+
} catch (cause) {
|
|
438
|
+
if (cause instanceof ArtifactRuntimeError) throw cause;
|
|
439
|
+
throw new ArtifactRuntimeError("ARTIFACT_RUNTIME_UNAVAILABLE", `Could not read ${name}`, {
|
|
440
|
+
cause,
|
|
441
|
+
});
|
|
442
|
+
} finally {
|
|
443
|
+
await handle?.close();
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
async function readAndVerifyFile(
|
|
448
|
+
path: string,
|
|
449
|
+
expected: Readonly<{ bytes: number; sha256: `sha256:${string}` }>,
|
|
450
|
+
maximumBytes: number,
|
|
451
|
+
name: string,
|
|
452
|
+
): Promise<Uint8Array> {
|
|
453
|
+
if (expected.bytes > maximumBytes) integrity(`${name} exceeds its allowed bound`);
|
|
454
|
+
const bytes = await readBoundedFile(path, maximumBytes, name);
|
|
455
|
+
if (bytes.byteLength !== expected.bytes)
|
|
456
|
+
integrity(`${name} byte count does not match its manifest`);
|
|
457
|
+
const digest = `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
|
|
458
|
+
if (digest !== expected.sha256) integrity(`${name} digest does not match its manifest`);
|
|
459
|
+
return bytes;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
async function verifyFile(
|
|
463
|
+
path: string,
|
|
464
|
+
expected: Readonly<{ bytes: number; sha256: `sha256:${string}` }>,
|
|
465
|
+
maximumBytes: number,
|
|
466
|
+
name: string,
|
|
467
|
+
): Promise<void> {
|
|
468
|
+
if (expected.bytes > maximumBytes) integrity(`${name} exceeds its allowed bound`);
|
|
469
|
+
let handle;
|
|
470
|
+
try {
|
|
471
|
+
handle = await open(path, "r");
|
|
472
|
+
const before = await handle.stat();
|
|
473
|
+
if (!before.isFile()) unavailable(`${name} is not a regular file`);
|
|
474
|
+
if (before.size !== expected.bytes) integrity(`${name} byte count does not match its manifest`);
|
|
475
|
+
const hash = createHash("sha256");
|
|
476
|
+
let bytes = 0;
|
|
477
|
+
for await (const chunk of handle.createReadStream({ autoClose: false })) {
|
|
478
|
+
const buffer = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
479
|
+
bytes += buffer.byteLength;
|
|
480
|
+
if (bytes > maximumBytes || bytes > expected.bytes) integrity(`${name} exceeds its manifest`);
|
|
481
|
+
hash.update(buffer);
|
|
482
|
+
}
|
|
483
|
+
const after = await handle.stat();
|
|
484
|
+
if (after.size !== before.size || bytes !== before.size)
|
|
485
|
+
integrity(`${name} changed while hashing`);
|
|
486
|
+
const digest = `sha256:${hash.digest("hex")}`;
|
|
487
|
+
if (digest !== expected.sha256) integrity(`${name} digest does not match its manifest`);
|
|
488
|
+
} catch (cause) {
|
|
489
|
+
if (cause instanceof ArtifactRuntimeError) throw cause;
|
|
490
|
+
throw new ArtifactRuntimeError("ARTIFACT_RUNTIME_UNAVAILABLE", `Could not verify ${name}`, {
|
|
491
|
+
cause,
|
|
492
|
+
});
|
|
493
|
+
} finally {
|
|
494
|
+
await handle?.close();
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function requiredEnvironmentPath(environment: RuntimeEnvironment, name: string): string {
|
|
499
|
+
const value = environment[name];
|
|
500
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
501
|
+
unavailable(`${name} is required; runtime discovery never downloads or guesses an asset`);
|
|
502
|
+
}
|
|
503
|
+
return value;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
export type LinuxLibcRuntimeEvidence = Readonly<{
|
|
507
|
+
arch: string;
|
|
508
|
+
versions: Readonly<Record<string, string | undefined>>;
|
|
509
|
+
report?: Readonly<{
|
|
510
|
+
header?: Readonly<{
|
|
511
|
+
glibcVersionRuntime?: unknown;
|
|
512
|
+
release?: Readonly<{ sourceUrl?: unknown }>;
|
|
513
|
+
}>;
|
|
514
|
+
sharedObjects?: unknown;
|
|
515
|
+
}>;
|
|
516
|
+
}>;
|
|
517
|
+
|
|
518
|
+
/** Resolves only mutually consistent evidence about the running Linux binary. */
|
|
519
|
+
export function resolveLinuxLibcFromRuntimeEvidence(
|
|
520
|
+
evidence: LinuxLibcRuntimeEvidence,
|
|
521
|
+
): "gnu" | "musl" {
|
|
522
|
+
const candidates = new Set<"gnu" | "musl">();
|
|
523
|
+
if (nonempty(evidence.versions.musl)) candidates.add("musl");
|
|
524
|
+
if (nonempty(evidence.report?.header?.glibcVersionRuntime)) candidates.add("gnu");
|
|
525
|
+
if (
|
|
526
|
+
Array.isArray(evidence.report?.sharedObjects) &&
|
|
527
|
+
evidence.report.sharedObjects.some(
|
|
528
|
+
(entry) =>
|
|
529
|
+
typeof entry === "string" &&
|
|
530
|
+
/(?:^|[\\/])ld-musl-(?:aarch64|x86_64)\.so(?:\.\d+)?$/u.test(entry),
|
|
531
|
+
)
|
|
532
|
+
) {
|
|
533
|
+
candidates.add("musl");
|
|
534
|
+
}
|
|
535
|
+
const bunArchiveLibc = libcFromOfficialBunRelease(
|
|
536
|
+
evidence.report?.header?.release?.sourceUrl,
|
|
537
|
+
evidence.versions.bun,
|
|
538
|
+
evidence.arch,
|
|
539
|
+
);
|
|
540
|
+
if (bunArchiveLibc) candidates.add(bunArchiveLibc);
|
|
541
|
+
if (candidates.size === 1) return [...candidates][0]!;
|
|
542
|
+
throw new ArtifactRuntimeError(
|
|
543
|
+
"ARTIFACT_RUNTIME_UNSUPPORTED_TARGET",
|
|
544
|
+
"Could not prove whether this Linux host uses glibc or musl",
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function detectLinuxLibc(): "gnu" | "musl" {
|
|
549
|
+
const report = process.report?.getReport() as LinuxLibcRuntimeEvidence["report"];
|
|
550
|
+
return resolveLinuxLibcFromRuntimeEvidence({
|
|
551
|
+
arch: process.arch,
|
|
552
|
+
versions: process.versions as Record<string, string | undefined>,
|
|
553
|
+
...(report ? { report } : {}),
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function libcFromOfficialBunRelease(
|
|
558
|
+
sourceUrl: unknown,
|
|
559
|
+
bunVersion: string | undefined,
|
|
560
|
+
arch: string,
|
|
561
|
+
): "gnu" | "musl" | undefined {
|
|
562
|
+
if (!nonempty(sourceUrl) || !nonempty(bunVersion)) return undefined;
|
|
563
|
+
let url: URL;
|
|
564
|
+
try {
|
|
565
|
+
url = new URL(sourceUrl);
|
|
566
|
+
} catch {
|
|
567
|
+
return undefined;
|
|
568
|
+
}
|
|
569
|
+
if (
|
|
570
|
+
url.protocol !== "https:" ||
|
|
571
|
+
url.hostname !== "github.com" ||
|
|
572
|
+
url.username ||
|
|
573
|
+
url.password ||
|
|
574
|
+
url.search ||
|
|
575
|
+
url.hash
|
|
576
|
+
) {
|
|
577
|
+
return undefined;
|
|
578
|
+
}
|
|
579
|
+
const match = /^\/oven-sh\/bun\/releases\/download\/bun-v([^/]+)\/bun-linux-([^/]+)\.zip$/u.exec(
|
|
580
|
+
url.pathname,
|
|
581
|
+
);
|
|
582
|
+
if (!match || match[1] !== bunVersion) return undefined;
|
|
583
|
+
const archiveTarget = match[2]!;
|
|
584
|
+
const validTarget =
|
|
585
|
+
arch === "arm64"
|
|
586
|
+
? /^aarch64(?:-musl)?(?:-profile)?$/u.test(archiveTarget)
|
|
587
|
+
: arch === "x64"
|
|
588
|
+
? /^x64(?:-musl)?(?:-baseline)?(?:-profile)?$/u.test(archiveTarget)
|
|
589
|
+
: false;
|
|
590
|
+
if (!validTarget) return undefined;
|
|
591
|
+
return archiveTarget.split("-").includes("musl") ? "musl" : "gnu";
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function nonempty(value: unknown): value is string {
|
|
595
|
+
return typeof value === "string" && value.length > 0;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
export function probeVerifiedArtifactSkillFacade(
|
|
599
|
+
module: Record<string, unknown>,
|
|
600
|
+
location: Pick<VerifiedArtifactRuntimeLocation, "target" | "kernel">,
|
|
601
|
+
): void {
|
|
602
|
+
const getRuntime = module.getConfiguredArtifactRuntime;
|
|
603
|
+
if (typeof getRuntime !== "function") {
|
|
604
|
+
incompatible("skill facade bootstrap does not expose getConfiguredArtifactRuntime()");
|
|
605
|
+
}
|
|
606
|
+
const runtime = getRuntime();
|
|
607
|
+
if (typeof runtime !== "object" || runtime === null) {
|
|
608
|
+
incompatible("skill facade bootstrap did not configure its exact native runtime");
|
|
609
|
+
}
|
|
610
|
+
const record = runtime as Record<string, unknown>;
|
|
611
|
+
if (
|
|
612
|
+
record.kind !== "native" ||
|
|
613
|
+
record.target !== location.target ||
|
|
614
|
+
record.buildIdentity !== location.kernel.buildIdentity ||
|
|
615
|
+
typeof record.capabilities !== "object" ||
|
|
616
|
+
record.capabilities === null
|
|
617
|
+
) {
|
|
618
|
+
incompatible("skill facade bootstrap configured an incompatible runtime");
|
|
619
|
+
}
|
|
620
|
+
const workbookFactory = module.Workbook as { create?: () => unknown } | undefined;
|
|
621
|
+
const diagnostics = module.getArtifactCompositeDiagnostics;
|
|
622
|
+
const snapshot = module.createArtifactSnapshot;
|
|
623
|
+
const dispose = module.disposeArtifact;
|
|
624
|
+
if (
|
|
625
|
+
typeof workbookFactory?.create !== "function" ||
|
|
626
|
+
typeof diagnostics !== "function" ||
|
|
627
|
+
typeof snapshot !== "function" ||
|
|
628
|
+
typeof dispose !== "function"
|
|
629
|
+
) {
|
|
630
|
+
incompatible("skill facade bootstrap is missing its native workbook health surface");
|
|
631
|
+
}
|
|
632
|
+
const workbook = workbookFactory.create();
|
|
633
|
+
try {
|
|
634
|
+
const proof = diagnostics(workbook) as Record<string, unknown>;
|
|
635
|
+
if (
|
|
636
|
+
proof.runtimeTarget !== location.target ||
|
|
637
|
+
proof.runtimeBuildIdentity !== location.kernel.buildIdentity ||
|
|
638
|
+
typeof proof.nativeStateHash !== "string"
|
|
639
|
+
) {
|
|
640
|
+
incompatible("skill facade workbook health proof does not match the pinned runtime");
|
|
641
|
+
}
|
|
642
|
+
} finally {
|
|
643
|
+
dispose(workbook);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function defaultImporter(specifier: string): Promise<unknown> {
|
|
648
|
+
return import(/* @vite-ignore */ specifier);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function sameJson(left: unknown, right: unknown): boolean {
|
|
652
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function sameBytes(left: Uint8Array, right: Uint8Array): boolean {
|
|
656
|
+
if (left.byteLength !== right.byteLength) return false;
|
|
657
|
+
return left.every((value, index) => value === right[index]);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function integrity(message: string): never {
|
|
661
|
+
throw new ArtifactRuntimeError("ARTIFACT_RUNTIME_INTEGRITY", message);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function unavailable(message: string): never {
|
|
665
|
+
throw new ArtifactRuntimeError("ARTIFACT_RUNTIME_UNAVAILABLE", message);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
function incompatible(message: string): never {
|
|
669
|
+
throw new ArtifactRuntimeError("ARTIFACT_RUNTIME_INCOMPATIBLE", message);
|
|
670
|
+
}
|