agent-hitch 0.2.13 → 0.2.14

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.
Files changed (126) hide show
  1. package/benchmark-packages/automationbench/RESOURCES.md +27 -0
  2. package/benchmark-packages/automationbench/resources.mjs +86 -0
  3. package/benchmark-packages/shared-tree/README.md +14 -0
  4. package/benchmark-packages/shared-tree/import.mjs +39 -0
  5. package/dist/scripts/canary-automation-resources.js +86 -0
  6. package/dist/scripts/canary-automation-resources.js.map +1 -0
  7. package/dist/scripts/canary-resource-empty-daemon.js +195 -0
  8. package/dist/scripts/canary-resource-empty-daemon.js.map +1 -0
  9. package/dist/scripts/canary-resources.js +89 -0
  10. package/dist/scripts/canary-resources.js.map +1 -0
  11. package/dist/scripts/check-architecture.js +6 -5
  12. package/dist/scripts/check-architecture.js.map +1 -1
  13. package/dist/scripts/resource-space-sampler.js +83 -0
  14. package/dist/scripts/resource-space-sampler.js.map +1 -0
  15. package/dist/src/backends/harbor/backend.js +5 -1
  16. package/dist/src/backends/harbor/backend.js.map +1 -1
  17. package/dist/src/backends/harbor/resource-admission.js +23 -0
  18. package/dist/src/backends/harbor/resource-admission.js.map +1 -0
  19. package/dist/src/cli/commands/resources.js +23 -0
  20. package/dist/src/cli/commands/resources.js.map +1 -0
  21. package/dist/src/cli/main.js +2 -0
  22. package/dist/src/cli/main.js.map +1 -1
  23. package/dist/src/control-plane/remote-offer-contract.js +11 -2
  24. package/dist/src/control-plane/remote-offer-contract.js.map +1 -1
  25. package/dist/src/control-plane/remote-result-transport.js +10 -0
  26. package/dist/src/control-plane/remote-result-transport.js.map +1 -1
  27. package/dist/src/control-plane/remote-work-coordinator.js +4 -1
  28. package/dist/src/control-plane/remote-work-coordinator.js.map +1 -1
  29. package/dist/src/control-plane/remote-work-inputs.js +16 -4
  30. package/dist/src/control-plane/remote-work-inputs.js.map +1 -1
  31. package/dist/src/control-plane/remote-worker-client.js +22 -0
  32. package/dist/src/control-plane/remote-worker-client.js.map +1 -1
  33. package/dist/src/control-plane/remote-worker-codecs.js +1 -1
  34. package/dist/src/control-plane/remote-worker-codecs.js.map +1 -1
  35. package/dist/src/control-plane/remote-worker-protocol.js +12 -2
  36. package/dist/src/control-plane/remote-worker-protocol.js.map +1 -1
  37. package/dist/src/control-plane/remote-worker-runner.js +1 -1
  38. package/dist/src/control-plane/remote-worker-runner.js.map +1 -1
  39. package/dist/src/control-plane/remote-workers.js +2 -1
  40. package/dist/src/control-plane/remote-workers.js.map +1 -1
  41. package/dist/src/evals/benchmark-adapter-manifest.js +4 -0
  42. package/dist/src/evals/benchmark-adapter-manifest.js.map +1 -1
  43. package/dist/src/evals/benchmark-candidate.js +11 -3
  44. package/dist/src/evals/benchmark-candidate.js.map +1 -1
  45. package/dist/src/evals/eval-result-builder.js +22 -1
  46. package/dist/src/evals/eval-result-builder.js.map +1 -1
  47. package/dist/src/evals/index.js +1 -0
  48. package/dist/src/evals/index.js.map +1 -1
  49. package/dist/src/evals/planned-execution.js +5 -14
  50. package/dist/src/evals/planned-execution.js.map +1 -1
  51. package/dist/src/evals/request.js +9 -0
  52. package/dist/src/evals/request.js.map +1 -1
  53. package/dist/src/evals/resource-eval-lifecycle.js +31 -0
  54. package/dist/src/evals/resource-eval-lifecycle.js.map +1 -0
  55. package/dist/src/evals/resource-legacy-export.js +57 -0
  56. package/dist/src/evals/resource-legacy-export.js.map +1 -0
  57. package/dist/src/evals/service.js +5 -20
  58. package/dist/src/evals/service.js.map +1 -1
  59. package/dist/src/evals/task-resources.js +12 -4
  60. package/dist/src/evals/task-resources.js.map +1 -1
  61. package/dist/src/evals/trial-bundle-files.js +81 -0
  62. package/dist/src/evals/trial-bundle-files.js.map +1 -0
  63. package/dist/src/evals/trial-import.js +7 -78
  64. package/dist/src/evals/trial-import.js.map +1 -1
  65. package/dist/src/foundation/durable-json.js +33 -0
  66. package/dist/src/foundation/durable-json.js.map +1 -0
  67. package/dist/src/foundation/index.js +2 -0
  68. package/dist/src/foundation/index.js.map +1 -1
  69. package/dist/src/foundation/resource-image-references.js +52 -0
  70. package/dist/src/foundation/resource-image-references.js.map +1 -0
  71. package/dist/src/images/gc.js +2 -2
  72. package/dist/src/images/gc.js.map +1 -1
  73. package/dist/src/images/registry.js +27 -2
  74. package/dist/src/images/registry.js.map +1 -1
  75. package/dist/src/resources/api.js +207 -0
  76. package/dist/src/resources/api.js.map +1 -0
  77. package/dist/src/resources/bundle.js +117 -0
  78. package/dist/src/resources/bundle.js.map +1 -0
  79. package/dist/src/resources/evidence.js +93 -0
  80. package/dist/src/resources/evidence.js.map +1 -0
  81. package/dist/src/resources/gc.js +123 -0
  82. package/dist/src/resources/gc.js.map +1 -0
  83. package/dist/src/resources/index.js +14 -0
  84. package/dist/src/resources/index.js.map +1 -0
  85. package/dist/src/resources/materialize.js +143 -0
  86. package/dist/src/resources/materialize.js.map +1 -0
  87. package/dist/src/resources/object-store.js +191 -0
  88. package/dist/src/resources/object-store.js.map +1 -0
  89. package/dist/src/resources/oci-layer-validation.js +202 -0
  90. package/dist/src/resources/oci-layer-validation.js.map +1 -0
  91. package/dist/src/resources/oci-provider.js +101 -0
  92. package/dist/src/resources/oci-provider.js.map +1 -0
  93. package/dist/src/resources/oci-registry-bundle.js +253 -0
  94. package/dist/src/resources/oci-registry-bundle.js.map +1 -0
  95. package/dist/src/resources/protocol.js +222 -0
  96. package/dist/src/resources/protocol.js.map +1 -0
  97. package/dist/src/resources/result-retention.js +93 -0
  98. package/dist/src/resources/result-retention.js.map +1 -0
  99. package/dist/src/resources/role-context.js +54 -0
  100. package/dist/src/resources/role-context.js.map +1 -0
  101. package/dist/src/resources/stats.js +36 -0
  102. package/dist/src/resources/stats.js.map +1 -0
  103. package/dist/src/resources/store.js +310 -0
  104. package/dist/src/resources/store.js.map +1 -0
  105. package/dist/src/resources/tasks.js +153 -0
  106. package/dist/src/resources/tasks.js.map +1 -0
  107. package/dist/src/resources/transfer.js +162 -0
  108. package/dist/src/resources/transfer.js.map +1 -0
  109. package/dist/src/resources/views.js +130 -0
  110. package/dist/src/resources/views.js.map +1 -0
  111. package/dist/src/workers/remote-harbor-work-spec.js +9 -3
  112. package/dist/src/workers/remote-harbor-work-spec.js.map +1 -1
  113. package/dist/src/workers/remote-harbor-worker.js +24 -9
  114. package/dist/src/workers/remote-harbor-worker.js.map +1 -1
  115. package/docs/resource-storage.zh-CN.md +95 -0
  116. package/docs/schemas/execution-provider-status.schema.json +20 -0
  117. package/docs/schemas/hitch-resource-dataset-v2.schema.json +590 -0
  118. package/docs/schemas/hitch-resource-lock-v1.schema.json +341 -0
  119. package/docs/schemas/hitch-resource-plan-v1.schema.json +423 -0
  120. package/docs/schemas/hitch-resource-selection-v1.schema.json +608 -0
  121. package/docs/schemas/hitch-tree-v1.schema.json +75 -0
  122. package/docs/schemas/remote-harbor-work-spec.schema.json +47 -3
  123. package/docs/schemas/remote-work-offer.schema.json +2 -1
  124. package/docs/schemas/remote-worker-registration.schema.json +20 -0
  125. package/package.json +9 -3
  126. package/test-contracts/hitch-resources-v1.json +197 -0
@@ -0,0 +1,101 @@
1
+ import { DockerRegistryResolver, resolveRegistryEnvironmentImage } from "../images/index.js";
2
+ import { durableWriteJSON } from "../foundation/index.js";
3
+ import { lstat, mkdir, mkdtemp, readFile, rm } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { exportRegistryBundle, fetchRegistryManifest, verifyRegistryManifest, importRegistryBundle, validateRegistryBundle, isRegistryBundle } from "./oci-registry-bundle.js";
6
+ import { DEFAULT_IMAGE_IMPORT_BUDGET } from "./oci-layer-validation.js";
7
+ import { DEFAULT_RESOURCE_LIMITS } from "./object-store.js";
8
+ import { sha } from "./protocol.js";
9
+ /** Transport is deliberately separate from the lock and all task identities. */
10
+ export class DockerResourceImageProvider {
11
+ root;
12
+ transport;
13
+ resolver;
14
+ offline;
15
+ imagePolicy;
16
+ budget;
17
+ protocol = "hitch-oci-provider@1";
18
+ constructor(root, transport, resolver = new DockerRegistryResolver({ policy: "cache-first" }), offline = {}, imagePolicy = "cache-first", budget = DEFAULT_RESOURCE_LIMITS) {
19
+ this.root = root;
20
+ this.transport = transport;
21
+ this.resolver = resolver;
22
+ this.offline = offline;
23
+ this.imagePolicy = imagePolicy;
24
+ this.budget = budget;
25
+ if (resolver instanceof DockerRegistryResolver)
26
+ this.resolver = resolver.withBeforePull(async (reference, platform, signal) => {
27
+ const resource = { kind: "oci-image", manifestDigest: sha(reference.split("@")[1]), platform };
28
+ const staging = path.join(root, "store", "benchmark-resources", "staging");
29
+ await mkdir(staging, { recursive: true });
30
+ const temporary = await mkdtemp(path.join(staging, "oci-validation-")), file = path.join(temporary, "bundle");
31
+ try {
32
+ await exportRegistryBundle(resource, reference, file, { maxBytes: Math.min(budget.maxObjectBytes, budget.maxTotalBytes), minFreeBytes: budget.minFreeBytes }, signal);
33
+ const validated = await validateRegistryBundle(resource, file, signal, { maxExpandedBytes: budget.maxTotalBytes, maxFiles: budget.maxFiles });
34
+ await durableWriteJSON(this.proofFile(resource), { manifest: validated.manifest });
35
+ }
36
+ finally {
37
+ await rm(temporary, { recursive: true, force: true });
38
+ }
39
+ });
40
+ }
41
+ proofFile(resource) { return path.join(this.root, "store", "benchmark-resources", "oci-manifests", `${resource.manifestDigest.slice(7)}.json`); }
42
+ async exportArchive(resource, destination, signal, budget) {
43
+ if (this.offline.exportFormat === "docker-archive")
44
+ throw new Error("docker-archive cannot preserve verified manifest and expanded-layer budgets; use registry-bundle");
45
+ const image = await this.resolve(resource, signal);
46
+ return exportRegistryBundle(resource, image.reference, destination, budget ?? { maxBytes: 1024 ** 3, minFreeBytes: 64 * 1024 ** 2 }, signal);
47
+ }
48
+ async validateArchive(resource, source, signal, budget = DEFAULT_IMAGE_IMPORT_BUDGET) {
49
+ if (!await isRegistryBundle(source))
50
+ throw new Error("unvalidated Docker archives are unsupported; import a registry-bundle");
51
+ return (await validateRegistryBundle(resource, source, signal, budget)).usage;
52
+ }
53
+ async importArchive(resource, source, signal, budget = DEFAULT_IMAGE_IMPORT_BUDGET) {
54
+ signal = AbortSignal.any([AbortSignal.timeout(120_000), ...(signal ? [signal] : [])]);
55
+ const reference = this.transport[resource.manifestDigest];
56
+ if (!reference)
57
+ throw new Error("offline OCI transport missing");
58
+ if (await isRegistryBundle(source)) {
59
+ if (!this.offline.registry)
60
+ throw new Error("registry bundle import requires an explicitly configured local offline registry");
61
+ const raw = await importRegistryBundle(resource, source, reference, this.offline.registry, signal, budget);
62
+ await durableWriteJSON(this.proofFile(resource), { manifest: raw });
63
+ // Only the explicitly configured loopback registry is contacted here.
64
+ // Normal execution remains cache-only and uses the exact original digest.
65
+ await new DockerRegistryResolver({ policy: "registry" }).resolve(reference, resource.platform, signal);
66
+ }
67
+ else
68
+ throw new Error("unvalidated Docker archives are unsupported; import a registry-bundle");
69
+ await new DockerRegistryResolver({ policy: "cache-only" }).resolve(reference, resource.platform, signal);
70
+ }
71
+ async resolve(resource, signal) {
72
+ signal = AbortSignal.any([AbortSignal.timeout(120_000), ...(signal ? [signal] : [])]);
73
+ if (resource.indexDigest)
74
+ throw new Error("this OCI provider does not support index membership verification; supply a platform manifest without an index lock");
75
+ const reference = this.transport[resource.manifestDigest];
76
+ if (!reference || !reference.endsWith(`@${resource.manifestDigest}`) || /[\s\0]/.test(reference))
77
+ throw new Error("immutable OCI resource transport is missing or mismatched");
78
+ const { manifest } = await resolveRegistryEnvironmentImage({ root: this.root, benchmarkId: "benchmark-resource", benchmarkRevision: resource.manifestDigest,
79
+ reference, platform: resource.platform, resolver: this.resolver, ...(signal ? { signal } : {}) });
80
+ if (!manifest.output.config_digest)
81
+ throw new Error("OCI resource provider must verify the config digest");
82
+ const file = this.proofFile(resource), info = await lstat(file).catch(e => { if (e.code !== "ENOENT")
83
+ throw e; return undefined; });
84
+ let raw;
85
+ if (info) {
86
+ if (!info.isFile() || info.size > 4 * 1024 ** 2)
87
+ throw new Error("invalid cached OCI manifest proof");
88
+ raw = JSON.parse(await readFile(file, "utf8")).manifest;
89
+ }
90
+ else {
91
+ if (this.imagePolicy === "cache-only")
92
+ throw new Error("cache-only OCI manifest proof missing; import a registry bundle or verify the image online first");
93
+ raw = await fetchRegistryManifest(resource, reference, signal);
94
+ }
95
+ verifyRegistryManifest(raw, resource, manifest.output.config_digest);
96
+ if (!info)
97
+ await durableWriteJSON(file, { manifest: raw });
98
+ return { imageId: manifest.image_id, manifestDigest: manifest.output.manifest_digest, configDigest: manifest.output.config_digest, platform: manifest.platform, reference: manifest.output.reference };
99
+ }
100
+ }
101
+ //# sourceMappingURL=oci-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oci-provider.js","sourceRoot":"","sources":["../../../src/resources/oci-provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,253 @@
1
+ import { createHash } from "node:crypto";
2
+ import { createReadStream } from "node:fs";
3
+ import { open, rm, statfs } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { canonical, hash, natural, object, parseStrictJson, sha } from "./protocol.js";
6
+ import { DEFAULT_IMAGE_IMPORT_BUDGET, validateOciLayer } from "./oci-layer-validation.js";
7
+ // Exact registry bytes, not a tar extraction or a reconstructed manifest.
8
+ // API: https://distribution.github.io/distribution/spec/api/
9
+ const MAGIC = Buffer.from("HITCH-OCI-REGISTRY-1\n"), MAX_HEADER = 4 * 1024 ** 2;
10
+ const ACCEPT = "application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.v2+json";
11
+ const FETCH_ACCEPT = `${ACCEPT}, application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json`;
12
+ export function registryLocation(reference) {
13
+ const [name, digest] = reference.split("@");
14
+ sha(digest);
15
+ const components = name.split("/"), explicit = /[.:]/.test(components[0]) || components[0] === "localhost";
16
+ const host = explicit ? components.shift() : "registry-1.docker.io";
17
+ if (components.length === 1 && !explicit)
18
+ components.unshift("library");
19
+ const repository = components.join("/");
20
+ if (!/^[a-z0-9]+(?:[._-][a-z0-9]+)*(?:\/[a-z0-9]+(?:[._-][a-z0-9]+)*)*$/.test(repository))
21
+ throw new Error("invalid registry repository");
22
+ const local = /^(localhost|127\.0\.0\.1)(:\d+)?$/.test(host);
23
+ return { origin: `${local ? "http" : "https"}://${host === "docker.io" ? "registry-1.docker.io" : host}`, repository, digest: digest };
24
+ }
25
+ export function localRegistry(value) {
26
+ const url = new URL(value);
27
+ if (!["localhost", "127.0.0.1", "[::1]"].includes(url.hostname) || !["http:", "https:"].includes(url.protocol) || url.username || url.password || url.pathname !== "/" || url.search || url.hash)
28
+ throw new Error("offline registry must be an explicitly configured loopback origin");
29
+ return url;
30
+ }
31
+ async function boundedBody(response, maximum) {
32
+ if (!response.ok || !response.body)
33
+ throw new Error(`OCI registry request failed: HTTP ${response.status}`);
34
+ const chunks = [];
35
+ let size = 0;
36
+ for await (const chunk of response.body) {
37
+ size += chunk.length;
38
+ if (size > maximum)
39
+ throw new Error("OCI response byte budget exceeded");
40
+ chunks.push(chunk);
41
+ }
42
+ return Buffer.concat(chunks);
43
+ }
44
+ function descriptors(manifest, image) {
45
+ if (hash(manifest) !== image.manifestDigest)
46
+ throw new Error("offline OCI manifest digest mismatch");
47
+ const m = parseStrictJson(manifest);
48
+ if (m.schemaVersion !== 2 || !Array.isArray(m.layers) || !ACCEPT.split(", ").includes(String(m.mediaType)))
49
+ throw new Error("offline registry format requires a platform image manifest, not an index");
50
+ const parse = (v) => { const d = v; if (!d || typeof d !== "object" || d.urls !== undefined)
51
+ throw new Error("foreign OCI blobs are unsupported"); return { digest: sha(d.digest), size: natural(d.size) }; };
52
+ const layers = m.layers.map(v => ({ ...parse(v), mediaType: String(v.mediaType) }));
53
+ const config = parse(m.config), entries = [config, ...layers], unique = new Map();
54
+ for (const e of entries) {
55
+ if (unique.has(e.digest) && unique.get(e.digest).size !== e.size)
56
+ throw new Error("conflicting OCI descriptor sizes");
57
+ unique.set(e.digest, e);
58
+ }
59
+ return { blobs: [...unique.values()].map(({ digest, size }) => ({ digest, size })).sort((a, b) => a.digest.localeCompare(b.digest)), layers, mediaType: String(m.mediaType), config };
60
+ }
61
+ async function registryReader(reference, signal) {
62
+ const location = registryLocation(reference);
63
+ let token;
64
+ return async (suffix, accept) => {
65
+ const url = `${location.origin}/v2/${location.repository}/${suffix}`;
66
+ const request = () => fetch(url, { signal, headers: { ...(accept ? { accept } : {}), ...(token ? { authorization: `Bearer ${token}` } : {}) } });
67
+ let response = await request();
68
+ if (response.status === 401 && !token) {
69
+ const challenge = response.headers.get("www-authenticate") ?? "";
70
+ await response.body?.cancel();
71
+ const realm = challenge.match(/realm="([^"]+)"/)?.[1], service = challenge.match(/service="([^"]+)"/)?.[1];
72
+ if (!challenge.startsWith("Bearer ") || !realm)
73
+ throw new Error("registry bundle export supports anonymous bearer authentication only");
74
+ const auth = new URL(realm);
75
+ if (auth.protocol !== "https:" || auth.username || auth.password)
76
+ throw new Error("unsafe registry authentication endpoint");
77
+ if (service)
78
+ auth.searchParams.set("service", service);
79
+ auth.searchParams.set("scope", `repository:${location.repository}:pull`);
80
+ const value = parseStrictJson((await boundedBody(await fetch(auth, { signal }), 64 * 1024)).toString());
81
+ token = typeof value.token === "string" ? value.token : typeof value.access_token === "string" ? value.access_token : undefined;
82
+ if (!token)
83
+ throw new Error("registry authentication unavailable");
84
+ response = await request();
85
+ }
86
+ if (!response.ok) {
87
+ await response.body?.cancel();
88
+ throw new Error(`OCI registry request failed: HTTP ${response.status}`);
89
+ }
90
+ return response;
91
+ };
92
+ }
93
+ export async function fetchRegistryManifest(image, reference, signal) {
94
+ const bounded = AbortSignal.any([AbortSignal.timeout(30_000), ...(signal ? [signal] : [])]);
95
+ const get = await registryReader(reference, bounded), manifest = (await boundedBody(await get(`manifests/${image.manifestDigest}`, FETCH_ACCEPT), MAX_HEADER)).toString();
96
+ descriptors(manifest, image);
97
+ return manifest;
98
+ }
99
+ export function verifyRegistryManifest(manifest, image, configDigest) {
100
+ if (descriptors(manifest, image).config.digest !== configDigest)
101
+ throw new Error("OCI manifest config differs from actual Docker image");
102
+ }
103
+ export async function exportRegistryBundle(image, reference, file, budget, signal) {
104
+ const bounded = AbortSignal.any([AbortSignal.timeout(120_000), ...(signal ? [signal] : [])]);
105
+ const get = await registryReader(reference, bounded), manifest = (await boundedBody(await get(`manifests/${image.manifestDigest}`, FETCH_ACCEPT), MAX_HEADER)).toString();
106
+ const { blobs } = descriptors(manifest, image), header = Buffer.from(JSON.stringify({ protocol: "hitch-oci-registry-bundle@1", manifest, blobs })), length = Buffer.alloc(4);
107
+ length.writeUInt32BE(header.length);
108
+ const total = MAGIC.length + 4 + header.length + blobs.reduce((n, b) => n + b.size, 0);
109
+ if (header.length > MAX_HEADER || total > budget.maxBytes)
110
+ throw new Error("offline OCI archive budget exceeded");
111
+ const space = await statfs(path.dirname(file), { bigint: true });
112
+ if (space.bavail * space.bsize < BigInt(total + budget.minFreeBytes))
113
+ throw new Error("offline OCI free-space reserve exceeded");
114
+ const output = await open(file, "wx", 0o600);
115
+ try {
116
+ await output.writeFile(Buffer.concat([MAGIC, length, header]));
117
+ for (const blob of blobs) {
118
+ let size = 0;
119
+ const digest = createHash("sha256"), response = await get(`blobs/${blob.digest}`);
120
+ for await (const chunk of response.body) {
121
+ bounded.throwIfAborted();
122
+ size += chunk.length;
123
+ if (size > blob.size)
124
+ throw new Error("OCI blob exceeds its manifest size");
125
+ digest.update(chunk);
126
+ await output.writeFile(chunk);
127
+ }
128
+ if (size !== blob.size || `sha256:${digest.digest("hex")}` !== blob.digest)
129
+ throw new Error("OCI blob integrity mismatch");
130
+ }
131
+ await output.sync();
132
+ }
133
+ catch (error) {
134
+ await rm(file, { force: true });
135
+ throw error;
136
+ }
137
+ finally {
138
+ await output.close();
139
+ }
140
+ }
141
+ export async function isRegistryBundle(file) {
142
+ const handle = await open(file, "r");
143
+ try {
144
+ const buffer = Buffer.alloc(MAGIC.length);
145
+ await handle.read(buffer, 0, buffer.length, 0);
146
+ return buffer.equals(MAGIC);
147
+ }
148
+ finally {
149
+ await handle.close();
150
+ }
151
+ }
152
+ /** Validates digests, expanded bytes, entry counts and tar paths without extraction. */
153
+ export async function validateRegistryBundle(image, file, signal, budget = DEFAULT_IMAGE_IMPORT_BUDGET) {
154
+ natural(budget.maxExpandedBytes);
155
+ natural(budget.maxFiles);
156
+ const bounded = AbortSignal.any([AbortSignal.timeout(120_000), ...(signal ? [signal] : [])]), handle = await open(file, "r");
157
+ let manifest, blobs, layers, mediaType, config, offset;
158
+ try {
159
+ const prefix = Buffer.alloc(MAGIC.length + 4);
160
+ await handle.read(prefix, 0, prefix.length, 0);
161
+ const length = prefix.readUInt32BE(MAGIC.length);
162
+ if (!prefix.subarray(0, MAGIC.length).equals(MAGIC) || length > MAX_HEADER)
163
+ throw new Error("invalid OCI bundle header");
164
+ const header = Buffer.alloc(length);
165
+ if ((await handle.read(header, 0, length, prefix.length)).bytesRead !== length)
166
+ throw new Error("truncated OCI header");
167
+ const raw = object(parseStrictJson(header.toString()), ["protocol", "manifest", "blobs"]);
168
+ if (raw.protocol !== "hitch-oci-registry-bundle@1" || typeof raw.manifest !== "string")
169
+ throw new Error("invalid OCI bundle protocol");
170
+ manifest = raw.manifest;
171
+ ({ blobs, layers, mediaType, config } = descriptors(manifest, image));
172
+ if (canonical(raw.blobs) !== canonical(blobs))
173
+ throw new Error("OCI bundle inventory mismatch");
174
+ offset = prefix.length + length;
175
+ if ((await handle.stat()).size !== offset + blobs.reduce((n, b) => n + b.size, 0))
176
+ throw new Error("OCI bundle size mismatch");
177
+ }
178
+ finally {
179
+ await handle.close();
180
+ }
181
+ let position = offset;
182
+ const positions = new Map();
183
+ let diffIds = [];
184
+ for (const blob of blobs) {
185
+ positions.set(blob.digest, position);
186
+ const digest = createHash("sha256"), chunks = [];
187
+ if (blob.size)
188
+ for await (const chunk of createReadStream(file, { start: position, end: position + blob.size - 1 })) {
189
+ bounded.throwIfAborted();
190
+ digest.update(chunk);
191
+ if (blob.digest === config.digest) {
192
+ if (blob.size > MAX_HEADER)
193
+ throw new Error("OCI config size budget exceeded");
194
+ chunks.push(chunk);
195
+ }
196
+ }
197
+ if (`sha256:${digest.digest("hex")}` !== blob.digest)
198
+ throw new Error("offline OCI blob digest mismatch");
199
+ if (blob.digest === config.digest) {
200
+ const c = parseStrictJson(Buffer.concat(chunks).toString());
201
+ if (`${c.os}/${c.architecture}${c.variant ? `/${c.variant}` : ""}` !== image.platform)
202
+ throw new Error("offline OCI config platform mismatch");
203
+ const rootfs = c.rootfs;
204
+ if (!rootfs || rootfs.type !== "layers" || !Array.isArray(rootfs.diff_ids) || rootfs.diff_ids.length !== layers.length)
205
+ throw new Error("offline OCI config layer inventory mismatch");
206
+ diffIds = rootfs.diff_ids.map(sha);
207
+ }
208
+ position += blob.size;
209
+ }
210
+ const usage = { expandedBytes: 0, files: 0 };
211
+ for (const [i, layer] of layers.entries()) {
212
+ const observed = await validateOciLayer(file, positions.get(layer.digest), layer.size, layer.mediaType, { maxExpandedBytes: budget.maxExpandedBytes - usage.expandedBytes, maxFiles: budget.maxFiles - usage.files }, bounded);
213
+ if (observed.diffId !== diffIds[i])
214
+ throw new Error("offline OCI uncompressed layer digest mismatch");
215
+ usage.expandedBytes += observed.expandedBytes;
216
+ usage.files += observed.files;
217
+ }
218
+ return { manifest: manifest, blobs: blobs, mediaType: mediaType, offset: offset, usage };
219
+ }
220
+ /** Validates every layer before any local registry or Docker mutation. */
221
+ export async function importRegistryBundle(image, file, reference, registry, signal, budget = DEFAULT_IMAGE_IMPORT_BUDGET) {
222
+ const origin = localRegistry(registry), location = registryLocation(reference);
223
+ if (new URL(location.origin).host !== origin.host)
224
+ throw new Error("offline image transport must name the configured local registry");
225
+ const bounded = AbortSignal.any([AbortSignal.timeout(120_000), ...(signal ? [signal] : [])]);
226
+ const { manifest, blobs, mediaType, offset } = await validateRegistryBundle(image, file, bounded, budget);
227
+ let position = offset;
228
+ const api = `${origin.origin}/v2/${location.repository}`;
229
+ for (const blob of blobs) {
230
+ const head = await fetch(`${api}/blobs/${blob.digest}`, { method: "HEAD", signal: bounded, redirect: "error" });
231
+ if (head.status !== 200) {
232
+ if (head.status !== 404)
233
+ throw new Error(`offline registry probe failed: ${head.status}`);
234
+ const start = await fetch(`${api}/blobs/uploads/`, { method: "POST", signal: bounded, redirect: "error" });
235
+ await start.body?.cancel();
236
+ const upload = new URL(start.headers.get("location") ?? "", api);
237
+ if (start.status !== 202 || upload.origin !== origin.origin || !upload.pathname.startsWith(`/v2/${location.repository}/blobs/uploads/`))
238
+ throw new Error("unsafe local registry upload location");
239
+ upload.searchParams.set("digest", blob.digest);
240
+ const response = await fetch(upload, { method: "PUT", signal: bounded, redirect: "error", headers: { "content-type": "application/octet-stream", "content-length": String(blob.size) }, body: blob.size ? createReadStream(file, { start: position, end: position + blob.size - 1 }) : Buffer.alloc(0), duplex: "half" });
241
+ await response.body?.cancel();
242
+ if (response.status !== 201)
243
+ throw new Error(`offline registry blob upload failed: ${response.status}`);
244
+ }
245
+ position += blob.size;
246
+ }
247
+ const result = await fetch(`${api}/manifests/${image.manifestDigest}`, { method: "PUT", signal: bounded, redirect: "error", headers: { "content-type": mediaType }, body: manifest });
248
+ await result.body?.cancel();
249
+ if (result.status !== 201)
250
+ throw new Error(`offline registry manifest publication failed: ${result.status}`);
251
+ return manifest;
252
+ }
253
+ //# sourceMappingURL=oci-registry-bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oci-registry-bundle.js","sourceRoot":"","sources":["../../../src/resources/oci-registry-bundle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,222 @@
1
+ import { createHash } from "node:crypto";
2
+ export const RESOURCE_CAPABILITIES = ["blob@1", "tree@1", "oci-image@1", "input-file@1", "input-tree@1", "environment-image@1", "private-copy@1", "read-only@1"];
3
+ export function canonical(value) {
4
+ if (Array.isArray(value))
5
+ return `[${value.map(canonical).join(",")}]`;
6
+ if (value && typeof value === "object")
7
+ return `{${Object.keys(value).sort(compare).map(key => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}`;
8
+ const result = JSON.stringify(value);
9
+ if (result === undefined || typeof value === "number" && !Number.isFinite(value))
10
+ throw new TypeError("non-canonical JSON value");
11
+ return result;
12
+ }
13
+ export function hash(bytes) { return `sha256:${createHash("sha256").update(bytes).digest("hex")}`; }
14
+ export function identity(domain, value) { return hash(canonical({ protocol: domain, value })); }
15
+ export function compare(a, b) { return Buffer.compare(Buffer.from(a), Buffer.from(b)); }
16
+ export function object(value, keys) {
17
+ if (!value || typeof value !== "object" || Array.isArray(value))
18
+ throw new TypeError("resource record must be an object");
19
+ const result = value;
20
+ if (Object.keys(result).some(key => !keys.includes(key)))
21
+ throw new TypeError("unknown resource field");
22
+ return result;
23
+ }
24
+ export function sha(value) {
25
+ if (typeof value !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value))
26
+ throw new TypeError("invalid resource SHA-256");
27
+ return value;
28
+ }
29
+ export function natural(value) {
30
+ if (!Number.isSafeInteger(value) || value < 0)
31
+ throw new TypeError("resource size must be a non-negative safe integer");
32
+ return value;
33
+ }
34
+ export function resourceId(value) {
35
+ if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/.test(value) || value === "." || value === "..")
36
+ throw new TypeError("invalid resource ID");
37
+ return value;
38
+ }
39
+ export function portablePath(value) {
40
+ if (typeof value !== "string" || !value || value.length > 4096 || value.normalize("NFC") !== value
41
+ || /[\\\x00-\x1f\x7f:]/.test(value) || value.split("/").some(part => !part || part === "." || part === ".." || /[. ]$/.test(part)
42
+ || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(part)))
43
+ throw new TypeError("non-portable resource path");
44
+ return value;
45
+ }
46
+ export function parseResource(value) {
47
+ const raw = object(value, ["kind", "digest", "size", "format", "manifestDigest", "platform", "indexDigest"]);
48
+ if (raw.kind === "blob") {
49
+ object(raw, ["kind", "digest", "size"]);
50
+ return { kind: "blob", digest: sha(raw.digest), size: natural(raw.size) };
51
+ }
52
+ if (raw.kind === "tree") {
53
+ object(raw, ["kind", "format", "manifestDigest"]);
54
+ if (raw.format !== "hitch-tree@1")
55
+ throw new TypeError("unsupported resource tree format");
56
+ return { kind: "tree", format: raw.format, manifestDigest: sha(raw.manifestDigest) };
57
+ }
58
+ if (raw.kind === "oci-image") {
59
+ object(raw, ["kind", "manifestDigest", "platform", "indexDigest"]);
60
+ if (typeof raw.platform !== "string" || !/^linux\/(amd64|arm64)(\/v[0-9]+)?$/.test(raw.platform))
61
+ throw new TypeError("unsupported OCI platform");
62
+ return { kind: "oci-image", manifestDigest: sha(raw.manifestDigest), platform: raw.platform,
63
+ ...(raw.indexDigest === undefined ? {} : { indexDigest: sha(raw.indexDigest) }) };
64
+ }
65
+ throw new TypeError("unsupported resource kind");
66
+ }
67
+ export function parseResourceLock(value) {
68
+ const raw = object(value, ["protocol", "resources", "bindings", "requiredCapabilities"]);
69
+ if (raw.protocol !== "hitch-resource-lock@1")
70
+ throw new TypeError("unsupported resource lock protocol");
71
+ if (!raw.resources || typeof raw.resources !== "object" || Array.isArray(raw.resources))
72
+ throw new TypeError("resources must be a map");
73
+ const resources = Object.fromEntries(Object.entries(raw.resources).sort(([a], [b]) => compare(a, b)).map(([id, resource]) => [resourceId(id), parseResource(resource)]));
74
+ if (!Array.isArray(raw.requiredCapabilities) || raw.requiredCapabilities.some(c => typeof c !== "string" || !/^[a-z][a-z0-9-]*@[1-9][0-9]*$/.test(c))
75
+ || new Set(raw.requiredCapabilities).size !== raw.requiredCapabilities.length)
76
+ throw new TypeError("invalid required resource capabilities");
77
+ if (!Array.isArray(raw.bindings))
78
+ throw new TypeError("resource bindings must be an array");
79
+ const bindings = raw.bindings.map(value => {
80
+ const binding = object(value, ["resource", "consumer", "use", "target", "executable", "access", "slot"]);
81
+ const resource = resourceId(binding.resource), dependency = resources[resource];
82
+ if (!dependency)
83
+ throw new TypeError("binding refers to missing resource");
84
+ const role = object(binding.consumer, ["role", "serviceId"]);
85
+ let consumer;
86
+ if (role.role === "service")
87
+ consumer = { role: "service", serviceId: portablePath(resourceId(role.serviceId)) };
88
+ else if ((role.role === "candidate" || role.role === "verifier") && role.serviceId === undefined)
89
+ consumer = { role: role.role };
90
+ else
91
+ throw new TypeError("invalid resource consumer");
92
+ if (consumer.role === "service" && consumer.serviceId.toLowerCase() === "main")
93
+ throw new TypeError("main is reserved for the candidate role");
94
+ if (binding.use === "environment-image") {
95
+ object(binding, ["resource", "consumer", "use", "slot"]);
96
+ if (dependency.kind !== "oci-image" || binding.slot !== "runtime" && binding.slot !== "build-base")
97
+ throw new TypeError("invalid environment image binding");
98
+ return { resource, consumer, use: binding.use, slot: binding.slot };
99
+ }
100
+ object(binding, ["resource", "consumer", "use", "target", "executable", "access"]);
101
+ const target = portablePath(binding.target), access = binding.access;
102
+ if (access !== "read-only" && access !== "private-copy")
103
+ throw new TypeError("invalid resource access");
104
+ if (binding.use === "input-file" && dependency.kind === "blob" && typeof binding.executable === "boolean")
105
+ return { resource, consumer, use: binding.use, target, access, executable: binding.executable };
106
+ if (binding.use === "input-tree" && dependency.kind === "tree" && binding.executable === undefined)
107
+ return { resource, consumer, use: binding.use, target, access };
108
+ throw new TypeError("resource kind and binding use disagree");
109
+ });
110
+ for (const [index, binding] of bindings.entries())
111
+ for (const other of bindings.slice(0, index)) {
112
+ if (canonical(binding.consumer).toLowerCase() !== canonical(other.consumer).toLowerCase())
113
+ continue;
114
+ if (binding.use === "environment-image" && other.use === "environment-image" && binding.slot === other.slot)
115
+ throw new TypeError("duplicate environment slot");
116
+ if (binding.use !== "environment-image" && other.use !== "environment-image") {
117
+ const a = binding.target.toLocaleLowerCase("en-US"), b = other.target.toLocaleLowerCase("en-US");
118
+ if (a === b || a.startsWith(`${b}/`) || b.startsWith(`${a}/`))
119
+ throw new TypeError("conflicting resource paths");
120
+ }
121
+ }
122
+ return { protocol: raw.protocol, resources, bindings, requiredCapabilities: [...raw.requiredCapabilities] };
123
+ }
124
+ export function parseTree(value) {
125
+ const raw = object(value, ["protocol", "entries"]);
126
+ if (raw.protocol !== "hitch-tree@1" || !Array.isArray(raw.entries))
127
+ throw new TypeError("unsupported tree manifest");
128
+ const entries = raw.entries.map(value => {
129
+ const entry = object(value, ["path", "kind", "digest", "size", "executable"]), name = portablePath(entry.path);
130
+ if (entry.kind === "directory") {
131
+ object(entry, ["path", "kind"]);
132
+ return { path: name, kind: "directory" };
133
+ }
134
+ if (entry.kind !== "file" || typeof entry.executable !== "boolean")
135
+ throw new TypeError("invalid tree entry");
136
+ return { path: name, kind: "file", digest: sha(entry.digest), size: natural(entry.size), executable: entry.executable };
137
+ });
138
+ const paths = new Map();
139
+ for (const [i, entry] of entries.entries()) {
140
+ if (i && compare(entries[i - 1].path, entry.path) >= 0)
141
+ throw new TypeError("tree paths must be unique and byte-sorted");
142
+ const key = entry.path.toLocaleLowerCase("en-US");
143
+ if (paths.has(key))
144
+ throw new TypeError("tree paths collide on a case-insensitive filesystem");
145
+ paths.set(key, entry);
146
+ const parts = entry.path.split("/");
147
+ parts.pop();
148
+ if (parts.length && paths.get(parts.join("/").toLocaleLowerCase("en-US"))?.kind !== "directory")
149
+ throw new TypeError("tree parent directory is missing");
150
+ }
151
+ return { protocol: raw.protocol, entries };
152
+ }
153
+ /** JSON.parse accepts duplicate keys. Lock/manifest text must reject them before
154
+ * parsing so a repeated resource ID cannot silently replace its first value. */
155
+ export function parseStrictJson(text) {
156
+ let at = 0;
157
+ const white = () => { while (/\s/u.test(text[at] ?? "") && at < text.length)
158
+ at++; };
159
+ const string = () => {
160
+ white();
161
+ const start = at;
162
+ if (text[at++] !== '"')
163
+ throw new TypeError("invalid JSON key");
164
+ while (at < text.length) {
165
+ const c = text[at++];
166
+ if (c === "\\")
167
+ at++;
168
+ else if (c === '"')
169
+ return JSON.parse(text.slice(start, at));
170
+ }
171
+ throw new TypeError("unterminated JSON string");
172
+ };
173
+ const value = (depth) => {
174
+ if (depth > 128)
175
+ throw new TypeError("resource JSON nesting limit exceeded");
176
+ white();
177
+ const c = text[at];
178
+ if (c === '"') {
179
+ string();
180
+ return;
181
+ }
182
+ if (c === "{" || c === "[") {
183
+ at++;
184
+ white();
185
+ const close = c === "{" ? "}" : "]", keys = new Set();
186
+ if (text[at] === close) {
187
+ at++;
188
+ return;
189
+ }
190
+ for (;;) {
191
+ if (c === "{") {
192
+ const key = string();
193
+ if (keys.has(key))
194
+ throw new TypeError("duplicate JSON key");
195
+ keys.add(key);
196
+ white();
197
+ if (text[at++] !== ":")
198
+ throw new TypeError("invalid JSON");
199
+ }
200
+ value(depth + 1);
201
+ white();
202
+ const end = text[at++];
203
+ if (end === close)
204
+ break;
205
+ if (end !== ",")
206
+ throw new TypeError("invalid JSON");
207
+ }
208
+ return;
209
+ }
210
+ const start = at;
211
+ while (at < text.length && !/[\s,}\]]/.test(text[at]))
212
+ at++;
213
+ if (at === start)
214
+ throw new TypeError("invalid JSON");
215
+ };
216
+ value(0);
217
+ white();
218
+ if (at !== text.length)
219
+ throw new TypeError("trailing JSON content");
220
+ return JSON.parse(text);
221
+ }
222
+ //# sourceMappingURL=protocol.js.map