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.
- package/benchmark-packages/automationbench/RESOURCES.md +27 -0
- package/benchmark-packages/automationbench/resources.mjs +86 -0
- package/benchmark-packages/shared-tree/README.md +14 -0
- package/benchmark-packages/shared-tree/import.mjs +39 -0
- package/dist/scripts/canary-automation-resources.js +86 -0
- package/dist/scripts/canary-automation-resources.js.map +1 -0
- package/dist/scripts/canary-resource-empty-daemon.js +195 -0
- package/dist/scripts/canary-resource-empty-daemon.js.map +1 -0
- package/dist/scripts/canary-resources.js +89 -0
- package/dist/scripts/canary-resources.js.map +1 -0
- package/dist/scripts/check-architecture.js +6 -5
- package/dist/scripts/check-architecture.js.map +1 -1
- package/dist/scripts/resource-space-sampler.js +83 -0
- package/dist/scripts/resource-space-sampler.js.map +1 -0
- package/dist/src/backends/harbor/backend.js +5 -1
- package/dist/src/backends/harbor/backend.js.map +1 -1
- package/dist/src/backends/harbor/resource-admission.js +23 -0
- package/dist/src/backends/harbor/resource-admission.js.map +1 -0
- package/dist/src/cli/commands/resources.js +23 -0
- package/dist/src/cli/commands/resources.js.map +1 -0
- package/dist/src/cli/main.js +2 -0
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/control-plane/remote-offer-contract.js +11 -2
- package/dist/src/control-plane/remote-offer-contract.js.map +1 -1
- package/dist/src/control-plane/remote-result-transport.js +10 -0
- package/dist/src/control-plane/remote-result-transport.js.map +1 -1
- package/dist/src/control-plane/remote-work-coordinator.js +4 -1
- package/dist/src/control-plane/remote-work-coordinator.js.map +1 -1
- package/dist/src/control-plane/remote-work-inputs.js +16 -4
- package/dist/src/control-plane/remote-work-inputs.js.map +1 -1
- package/dist/src/control-plane/remote-worker-client.js +22 -0
- package/dist/src/control-plane/remote-worker-client.js.map +1 -1
- package/dist/src/control-plane/remote-worker-codecs.js +1 -1
- package/dist/src/control-plane/remote-worker-codecs.js.map +1 -1
- package/dist/src/control-plane/remote-worker-protocol.js +12 -2
- package/dist/src/control-plane/remote-worker-protocol.js.map +1 -1
- package/dist/src/control-plane/remote-worker-runner.js +1 -1
- package/dist/src/control-plane/remote-worker-runner.js.map +1 -1
- package/dist/src/control-plane/remote-workers.js +2 -1
- package/dist/src/control-plane/remote-workers.js.map +1 -1
- package/dist/src/evals/benchmark-adapter-manifest.js +4 -0
- package/dist/src/evals/benchmark-adapter-manifest.js.map +1 -1
- package/dist/src/evals/benchmark-candidate.js +11 -3
- package/dist/src/evals/benchmark-candidate.js.map +1 -1
- package/dist/src/evals/eval-result-builder.js +22 -1
- package/dist/src/evals/eval-result-builder.js.map +1 -1
- package/dist/src/evals/index.js +1 -0
- package/dist/src/evals/index.js.map +1 -1
- package/dist/src/evals/planned-execution.js +5 -14
- package/dist/src/evals/planned-execution.js.map +1 -1
- package/dist/src/evals/request.js +9 -0
- package/dist/src/evals/request.js.map +1 -1
- package/dist/src/evals/resource-eval-lifecycle.js +31 -0
- package/dist/src/evals/resource-eval-lifecycle.js.map +1 -0
- package/dist/src/evals/resource-legacy-export.js +57 -0
- package/dist/src/evals/resource-legacy-export.js.map +1 -0
- package/dist/src/evals/service.js +5 -20
- package/dist/src/evals/service.js.map +1 -1
- package/dist/src/evals/task-resources.js +12 -4
- package/dist/src/evals/task-resources.js.map +1 -1
- package/dist/src/evals/trial-bundle-files.js +81 -0
- package/dist/src/evals/trial-bundle-files.js.map +1 -0
- package/dist/src/evals/trial-import.js +7 -78
- package/dist/src/evals/trial-import.js.map +1 -1
- package/dist/src/foundation/durable-json.js +33 -0
- package/dist/src/foundation/durable-json.js.map +1 -0
- package/dist/src/foundation/index.js +2 -0
- package/dist/src/foundation/index.js.map +1 -1
- package/dist/src/foundation/resource-image-references.js +52 -0
- package/dist/src/foundation/resource-image-references.js.map +1 -0
- package/dist/src/images/gc.js +2 -2
- package/dist/src/images/gc.js.map +1 -1
- package/dist/src/images/registry.js +27 -2
- package/dist/src/images/registry.js.map +1 -1
- package/dist/src/resources/api.js +207 -0
- package/dist/src/resources/api.js.map +1 -0
- package/dist/src/resources/bundle.js +117 -0
- package/dist/src/resources/bundle.js.map +1 -0
- package/dist/src/resources/evidence.js +93 -0
- package/dist/src/resources/evidence.js.map +1 -0
- package/dist/src/resources/gc.js +123 -0
- package/dist/src/resources/gc.js.map +1 -0
- package/dist/src/resources/index.js +14 -0
- package/dist/src/resources/index.js.map +1 -0
- package/dist/src/resources/materialize.js +143 -0
- package/dist/src/resources/materialize.js.map +1 -0
- package/dist/src/resources/object-store.js +191 -0
- package/dist/src/resources/object-store.js.map +1 -0
- package/dist/src/resources/oci-layer-validation.js +202 -0
- package/dist/src/resources/oci-layer-validation.js.map +1 -0
- package/dist/src/resources/oci-provider.js +101 -0
- package/dist/src/resources/oci-provider.js.map +1 -0
- package/dist/src/resources/oci-registry-bundle.js +253 -0
- package/dist/src/resources/oci-registry-bundle.js.map +1 -0
- package/dist/src/resources/protocol.js +222 -0
- package/dist/src/resources/protocol.js.map +1 -0
- package/dist/src/resources/result-retention.js +93 -0
- package/dist/src/resources/result-retention.js.map +1 -0
- package/dist/src/resources/role-context.js +54 -0
- package/dist/src/resources/role-context.js.map +1 -0
- package/dist/src/resources/stats.js +36 -0
- package/dist/src/resources/stats.js.map +1 -0
- package/dist/src/resources/store.js +310 -0
- package/dist/src/resources/store.js.map +1 -0
- package/dist/src/resources/tasks.js +153 -0
- package/dist/src/resources/tasks.js.map +1 -0
- package/dist/src/resources/transfer.js +162 -0
- package/dist/src/resources/transfer.js.map +1 -0
- package/dist/src/resources/views.js +130 -0
- package/dist/src/resources/views.js.map +1 -0
- package/dist/src/workers/remote-harbor-work-spec.js +9 -3
- package/dist/src/workers/remote-harbor-work-spec.js.map +1 -1
- package/dist/src/workers/remote-harbor-worker.js +24 -9
- package/dist/src/workers/remote-harbor-worker.js.map +1 -1
- package/docs/resource-storage.zh-CN.md +95 -0
- package/docs/schemas/execution-provider-status.schema.json +20 -0
- package/docs/schemas/hitch-resource-dataset-v2.schema.json +590 -0
- package/docs/schemas/hitch-resource-lock-v1.schema.json +341 -0
- package/docs/schemas/hitch-resource-plan-v1.schema.json +423 -0
- package/docs/schemas/hitch-resource-selection-v1.schema.json +608 -0
- package/docs/schemas/hitch-tree-v1.schema.json +75 -0
- package/docs/schemas/remote-harbor-work-spec.schema.json +47 -3
- package/docs/schemas/remote-work-offer.schema.json +2 -1
- package/docs/schemas/remote-worker-registration.schema.json +20 -0
- package/package.json +9 -3
- package/test-contracts/hitch-resources-v1.json +197 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { ResourceStore } from "./store.js";
|
|
2
|
+
export { resourceRequest, resourceFileRequest, configuredResourceStore, preflightResourceInput } from "./api.js";
|
|
3
|
+
export { readResourceInput, parseResourceDataset, parseResourceSelection, parseResourceTask, selectResources, sealResourceTask, preflightResourceTask, taskClosureLock, resourceDescriptorDirectory } from "./tasks.js";
|
|
4
|
+
export { materializeResourceTask, finishResourceWorkspace } from "./materialize.js";
|
|
5
|
+
export { parseResource, parseResourceLock, parseStrictJson, parseTree, canonical, identity, hash, RESOURCE_CAPABILITIES } from "./protocol.js";
|
|
6
|
+
export { DockerResourceImageProvider } from "./oci-provider.js";
|
|
7
|
+
export { auditResources } from "./gc.js";
|
|
8
|
+
export { DEFAULT_RESOURCE_LIMITS } from "./object-store.js";
|
|
9
|
+
export { createResourceDelivery, parseResourceDelivery, receiveResourceDelivery } from "./transfer.js";
|
|
10
|
+
export { exportResourceBundle, importResourceBundle } from "./bundle.js";
|
|
11
|
+
export { readResourceEvidence, sealResourceEvidence, confirmResourceResultSealed, reclaimResourceExecution } from "./evidence.js";
|
|
12
|
+
export { resourceStorageStats } from "./stats.js";
|
|
13
|
+
export { retainResourceResult } from "./result-retention.js";
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/resources/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAEjH,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,eAAe,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAExN,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAGpF,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAE/I,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAEvG,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AAClI,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { roleDirectory, validateRoleContexts } from "./role-context.js";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { constants } from "node:fs";
|
|
4
|
+
import { appendFile, chmod, copyFile, lstat, mkdir, readFile, rename, rm, statfs, writeFile } from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { durableWriteJSON as atomicWriteJSON } from "../foundation/index.js";
|
|
7
|
+
import { canonical, identity, parseStrictJson } from "./protocol.js";
|
|
8
|
+
import { validateLease } from "./store.js";
|
|
9
|
+
import { preflightResourceTask, parseResourceTask, taskClosureLock } from "./tasks.js";
|
|
10
|
+
export async function materializeResourceTask(store, plan, input) {
|
|
11
|
+
const checked = await preflightResourceTask(store, plan.task, { platform: plan.platform, owner: input.owner, generation: input.generation, ...(input.signal ? { signal: input.signal } : {}) });
|
|
12
|
+
if (checked.plan.digest !== plan.digest)
|
|
13
|
+
throw new Error("resource execution plan changed");
|
|
14
|
+
const { lease } = await store.acquire(input.owner, taskClosureLock(plan.task), "execution", input.signal), id = randomUUID();
|
|
15
|
+
const directory = path.join(store.directory, "workspaces", id), temp = `${directory}.tmp`, taskDirectory = path.join(directory, plan.task.task_id);
|
|
16
|
+
const evidence = { protocol: "hitch-resource-workspace@1", id, state: "active", leaseId: lease.id, plan, taskDirectory, materialized_task_digest: "", clonedBytes: 0, copiedBytes: 0, fallbackReasons: {}, images: checked.closure.images };
|
|
17
|
+
try {
|
|
18
|
+
return await store.locked(async () => {
|
|
19
|
+
const mode = input.policy?.mode ?? "auto", maxWorkspaces = input.policy?.maxWorkspaces ?? 4, maxCopy = input.policy?.maxCopyBytes ?? store.objects.budget.maxTotalBytes;
|
|
20
|
+
if (!["auto", "copy", "require-clone"].includes(mode) || !Number.isSafeInteger(maxWorkspaces) || maxWorkspaces < 1 || !Number.isSafeInteger(maxCopy) || maxCopy < 0)
|
|
21
|
+
throw new TypeError("invalid workspace policy");
|
|
22
|
+
const active = (await store.records("leases")).filter(l => { validateLease(l); return l.purpose === "execution" && l.state !== "released"; });
|
|
23
|
+
if (active.length > maxWorkspaces)
|
|
24
|
+
throw new Error("resource workspace concurrency budget exceeded");
|
|
25
|
+
await mkdir(path.dirname(directory), { recursive: true });
|
|
26
|
+
await mkdir(temp, { mode: 0o700 });
|
|
27
|
+
const destination = path.join(temp, plan.task.task_id);
|
|
28
|
+
await mkdir(destination);
|
|
29
|
+
const file = async (digest, target, size, executable) => {
|
|
30
|
+
input.signal?.throwIfAborted();
|
|
31
|
+
await store.objects.verify(digest, size);
|
|
32
|
+
let clone = false;
|
|
33
|
+
if (mode !== "copy")
|
|
34
|
+
try {
|
|
35
|
+
await copyFile(store.objects.objectPath(digest), target, constants.COPYFILE_FICLONE_FORCE | constants.COPYFILE_EXCL);
|
|
36
|
+
clone = true;
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
const code = error.code ?? "unknown";
|
|
40
|
+
if (mode === "require-clone" || !["ENOTSUP", "EOPNOTSUPP", "EXDEV", "ENOSYS"].includes(code))
|
|
41
|
+
throw error;
|
|
42
|
+
evidence.fallbackReasons[code] = (evidence.fallbackReasons[code] ?? 0) + 1;
|
|
43
|
+
}
|
|
44
|
+
if (clone)
|
|
45
|
+
evidence.clonedBytes += size;
|
|
46
|
+
else {
|
|
47
|
+
if (evidence.copiedBytes + size > maxCopy)
|
|
48
|
+
throw new Error("resource workspace copy budget exceeded");
|
|
49
|
+
const space = await statfs(temp, { bigint: true });
|
|
50
|
+
if (space.bavail * space.bsize < BigInt(size + store.objects.budget.minFreeBytes))
|
|
51
|
+
throw new Error("resource workspace free-space reserve exceeded");
|
|
52
|
+
await copyFile(store.objects.objectPath(digest), target, constants.COPYFILE_EXCL);
|
|
53
|
+
evidence.copiedBytes += size;
|
|
54
|
+
}
|
|
55
|
+
input.signal?.throwIfAborted();
|
|
56
|
+
await store.objects.verify(digest, size, target);
|
|
57
|
+
await chmod(target, executable ? 0o755 : 0o644);
|
|
58
|
+
};
|
|
59
|
+
const tree = async (resource, target) => {
|
|
60
|
+
const manifest = await store.objects.readTree(resource.manifestDigest);
|
|
61
|
+
for (const entry of manifest.entries) {
|
|
62
|
+
const output = path.join(target, entry.path);
|
|
63
|
+
if (entry.kind === "directory")
|
|
64
|
+
await mkdir(output, { mode: 0o755 });
|
|
65
|
+
else
|
|
66
|
+
await file(entry.digest, output, entry.size, entry.executable);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
await tree(plan.task.sourceTree, destination);
|
|
70
|
+
await validateRoleContexts(destination, plan.task.lock.bindings, plan.platform, checked.closure.images);
|
|
71
|
+
for (const binding of plan.task.lock.bindings) {
|
|
72
|
+
if (binding.use === "environment-image")
|
|
73
|
+
continue;
|
|
74
|
+
const target = path.join(destination, roleDirectory(binding.consumer), binding.target), resource = plan.task.lock.resources[binding.resource];
|
|
75
|
+
await mkdir(path.dirname(target), { recursive: true });
|
|
76
|
+
// A source descriptor may not overwrite or shadow a resource binding.
|
|
77
|
+
if (await lstat(target).catch((e) => { if (e.code !== "ENOENT")
|
|
78
|
+
throw e; return undefined; }))
|
|
79
|
+
throw new Error("resource target collides with task descriptor");
|
|
80
|
+
if (resource.kind === "tree") {
|
|
81
|
+
await mkdir(target);
|
|
82
|
+
await tree(resource, target);
|
|
83
|
+
}
|
|
84
|
+
else if (resource.kind === "blob" && binding.use === "input-file")
|
|
85
|
+
await file(resource.digest, target, resource.size, binding.executable);
|
|
86
|
+
else
|
|
87
|
+
throw new Error("resource binding changed after preflight");
|
|
88
|
+
}
|
|
89
|
+
const roles = new Map(plan.task.lock.bindings.map(b => [canonical(b.consumer), b.consumer]));
|
|
90
|
+
for (const [role, consumer] of roles) {
|
|
91
|
+
const file = path.join(destination, roleDirectory(consumer), "Dockerfile"), text = await readFile(file, "utf8");
|
|
92
|
+
const transported = text.replace(/^(FROM\s+)(\S+)(.*)$/gim, (_all, prefix, ref, suffix) => {
|
|
93
|
+
const image = checked.closure.images.find(i => ref.endsWith(`@${i.manifestDigest}`));
|
|
94
|
+
if (!image)
|
|
95
|
+
throw new Error("unresolved OCI build base");
|
|
96
|
+
return `${prefix}${image.reference}${suffix}`;
|
|
97
|
+
});
|
|
98
|
+
await writeFile(file, transported);
|
|
99
|
+
await appendFile(file, `\nLABEL dev.hitch.resource.plan=${JSON.stringify(plan.digest)} dev.hitch.resource.consumer=${JSON.stringify(role)}\n`);
|
|
100
|
+
}
|
|
101
|
+
// Import the actual output as an identity proof while holding the same lock.
|
|
102
|
+
// Its independent evidence lease keeps it reproducible until result sealing.
|
|
103
|
+
const staging = path.join(temp, "proof");
|
|
104
|
+
await mkdir(staging);
|
|
105
|
+
const actual = await store.objects.importTree(destination, staging, d => store.protect(lease, d), input.signal);
|
|
106
|
+
evidence.materializedTree = actual.resource;
|
|
107
|
+
evidence.materialized_task_digest = identity("hitch-materialized-task@1", actual.resource);
|
|
108
|
+
await rm(staging, { recursive: true });
|
|
109
|
+
input.signal?.throwIfAborted();
|
|
110
|
+
await rename(temp, directory);
|
|
111
|
+
await atomicWriteJSON(path.join(store.directory, "workspace-records", `${id}.json`), evidence);
|
|
112
|
+
return evidence;
|
|
113
|
+
}, input.signal);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
await rm(temp, { recursive: true, force: true });
|
|
117
|
+
// If published, retain the workspace and execution lease for explicit recovery.
|
|
118
|
+
if (!await lstat(directory).catch(() => undefined))
|
|
119
|
+
await store.finishLease(lease.id, "ended");
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
export async function finishResourceWorkspace(store, id, confirmation) {
|
|
124
|
+
if (!/^[a-f0-9-]{36}$/.test(id) || confirmation.executionEnded !== true || confirmation.resultSealed !== true)
|
|
125
|
+
throw new TypeError("workspace cleanup requires execution end and result sealing confirmation");
|
|
126
|
+
await store.locked(async () => {
|
|
127
|
+
const file = path.join(store.directory, "workspace-records", `${id}.json`), record = parseStrictJson(await readFile(file, "utf8"));
|
|
128
|
+
parseResourceTask(record.plan.task);
|
|
129
|
+
if (record.protocol !== "hitch-resource-workspace@1" || record.id !== id || record.taskDirectory !== path.join(store.directory, "workspaces", id, record.plan.task.task_id))
|
|
130
|
+
throw new TypeError("workspace ownership record mismatch");
|
|
131
|
+
record.state = "sealed";
|
|
132
|
+
await atomicWriteJSON(file, record);
|
|
133
|
+
await rm(path.join(store.directory, "workspaces", id), { recursive: true, force: true });
|
|
134
|
+
const lease = await store.readRecord("leases", record.leaseId);
|
|
135
|
+
if (!lease)
|
|
136
|
+
throw new Error("workspace lease missing");
|
|
137
|
+
validateLease(lease);
|
|
138
|
+
await store.finishLeaseLocked(lease);
|
|
139
|
+
record.state = "removed";
|
|
140
|
+
await atomicWriteJSON(file, record);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=materialize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"materialize.js","sourceRoot":"","sources":["../../../src/resources/materialize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { lstat, mkdir, open, readFile, readdir, rename, rm, statfs, writeFile } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { syncDirectory } from "../foundation/index.js";
|
|
6
|
+
import { canonical, compare, hash, natural, parseStrictJson, parseTree, portablePath, sha } from "./protocol.js";
|
|
7
|
+
export const DEFAULT_RESOURCE_LIMITS = { maxObjectBytes: 1024 ** 3, maxTotalBytes: 8 * 1024 ** 3, maxFiles: 100_000, minFreeBytes: 64 * 1024 ** 2, maxViewBytes: 512 * 1024 ** 2 };
|
|
8
|
+
export function limits(input = {}) {
|
|
9
|
+
if (Object.keys(input).some(k => !Object.keys(DEFAULT_RESOURCE_LIMITS).includes(k)))
|
|
10
|
+
throw new TypeError("unknown resource budget");
|
|
11
|
+
const value = { ...DEFAULT_RESOURCE_LIMITS, ...input };
|
|
12
|
+
Object.values(value).forEach(natural);
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
const signature = (s) => [s.dev, s.ino, s.mode, s.size, s.mtimeMs, s.ctimeMs].join(":");
|
|
16
|
+
export class ObjectStore {
|
|
17
|
+
root;
|
|
18
|
+
budget;
|
|
19
|
+
directory;
|
|
20
|
+
constructor(root, budget = DEFAULT_RESOURCE_LIMITS) {
|
|
21
|
+
this.root = root;
|
|
22
|
+
this.budget = budget;
|
|
23
|
+
this.directory = path.join(root, "store", "benchmark-resources");
|
|
24
|
+
}
|
|
25
|
+
objectPath(digest) { return path.join(this.directory, "objects", "sha256", sha(digest).slice(7)); }
|
|
26
|
+
async verify(digest, expectedSize, file = this.objectPath(digest)) {
|
|
27
|
+
const info = await lstat(file);
|
|
28
|
+
if (!info.isFile() || info.isSymbolicLink() || info.size > this.budget.maxObjectBytes || expectedSize !== undefined && info.size !== expectedSize)
|
|
29
|
+
throw new Error("resource object type or size mismatch");
|
|
30
|
+
const handle = await open(file, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
31
|
+
try {
|
|
32
|
+
if (signature(await handle.stat()) !== signature(info))
|
|
33
|
+
throw new Error("resource object changed while opening");
|
|
34
|
+
const h = createHash("sha256");
|
|
35
|
+
let size = 0;
|
|
36
|
+
for await (const chunk of handle.createReadStream({ autoClose: false })) {
|
|
37
|
+
size += chunk.length;
|
|
38
|
+
if (size > this.budget.maxObjectBytes)
|
|
39
|
+
throw new Error("resource object byte budget exceeded");
|
|
40
|
+
h.update(chunk);
|
|
41
|
+
}
|
|
42
|
+
if (`sha256:${h.digest("hex")}` !== digest || size !== info.size || signature(await handle.stat()) !== signature(info) || signature(await lstat(file)) !== signature(info))
|
|
43
|
+
throw new Error("resource object digest mismatch or concurrent modification");
|
|
44
|
+
return size;
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
await handle.close();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async readTree(digest) {
|
|
51
|
+
const size = await this.verify(digest);
|
|
52
|
+
if (size > 16 * 1024 ** 2)
|
|
53
|
+
throw new Error("tree manifest byte budget exceeded");
|
|
54
|
+
const text = await readFile(this.objectPath(digest), "utf8"), tree = parseTree(parseStrictJson(text));
|
|
55
|
+
if (hash(text) !== digest || text !== canonical(tree))
|
|
56
|
+
throw new Error("non-canonical tree manifest");
|
|
57
|
+
if (tree.entries.length > this.budget.maxFiles)
|
|
58
|
+
throw new Error("tree file budget exceeded");
|
|
59
|
+
return tree;
|
|
60
|
+
}
|
|
61
|
+
async publish(staged, digest, size, protect) {
|
|
62
|
+
await protect(digest); // Durable producer lease precedes first visibility.
|
|
63
|
+
const destination = this.objectPath(digest);
|
|
64
|
+
await mkdir(path.dirname(destination), { recursive: true });
|
|
65
|
+
const existing = await lstat(destination).catch((e) => { if (e.code !== "ENOENT")
|
|
66
|
+
throw e; return undefined; });
|
|
67
|
+
if (existing) {
|
|
68
|
+
await this.verify(digest, size);
|
|
69
|
+
await rm(staged);
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
const quarantined = path.join(this.directory, "quarantine", digest.slice(7));
|
|
73
|
+
if (await lstat(quarantined).catch((e) => { if (e.code !== "ENOENT")
|
|
74
|
+
throw e; return undefined; })) {
|
|
75
|
+
await this.verify(digest, size, quarantined);
|
|
76
|
+
await rename(quarantined, destination);
|
|
77
|
+
await syncDirectory(path.dirname(destination));
|
|
78
|
+
await rm(path.join(this.directory, "quarantine-index", `${digest.slice(7)}.json`), { force: true });
|
|
79
|
+
await rm(staged);
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
// All publishers and GC hold the store lock. An existing empty/corrupt object
|
|
83
|
+
// is never overwritten and shared files are never hardlinked into workspaces.
|
|
84
|
+
const handle = await open(staged, "r");
|
|
85
|
+
try {
|
|
86
|
+
await handle.sync();
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
await handle.close();
|
|
90
|
+
}
|
|
91
|
+
await rename(staged, destination);
|
|
92
|
+
await syncDirectory(path.dirname(destination));
|
|
93
|
+
await this.verify(digest, size);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
async importFile(source, staging, protect, signal) {
|
|
97
|
+
signal?.throwIfAborted();
|
|
98
|
+
const info = await lstat(source);
|
|
99
|
+
if (!info.isFile() || info.isSymbolicLink())
|
|
100
|
+
throw new TypeError("resource import requires a regular file");
|
|
101
|
+
if (info.size > this.budget.maxObjectBytes || info.size > this.budget.maxTotalBytes)
|
|
102
|
+
throw new Error("resource object byte budget exceeded");
|
|
103
|
+
const space = await statfs(staging, { bigint: true });
|
|
104
|
+
if (space.bsize * space.bavail < BigInt(info.size + this.budget.minFreeBytes))
|
|
105
|
+
throw new Error("resource import free-space reserve exceeded");
|
|
106
|
+
const input = await open(source, constants.O_RDONLY | constants.O_NOFOLLOW), file = path.join(staging, randomUUID());
|
|
107
|
+
try {
|
|
108
|
+
if (signature(await input.stat()) !== signature(info))
|
|
109
|
+
throw new Error("resource source changed");
|
|
110
|
+
const output = await open(file, "wx", 0o600), digest = createHash("sha256");
|
|
111
|
+
let size = 0;
|
|
112
|
+
try {
|
|
113
|
+
for await (const chunk of input.createReadStream({ autoClose: false })) {
|
|
114
|
+
signal?.throwIfAborted();
|
|
115
|
+
size += chunk.length;
|
|
116
|
+
if (size > this.budget.maxObjectBytes || size > info.size)
|
|
117
|
+
throw new Error("resource source grew past byte budget");
|
|
118
|
+
digest.update(chunk);
|
|
119
|
+
await output.writeFile(chunk);
|
|
120
|
+
}
|
|
121
|
+
await output.sync();
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
await output.close();
|
|
125
|
+
}
|
|
126
|
+
if (size !== info.size || signature(await input.stat()) !== signature(info) || signature(await lstat(source)) !== signature(info))
|
|
127
|
+
throw new Error("resource source changed during import");
|
|
128
|
+
const resource = { kind: "blob", digest: `sha256:${digest.digest("hex")}`, size };
|
|
129
|
+
signal?.throwIfAborted();
|
|
130
|
+
return { resource, storedBytes: await this.publish(file, resource.digest, size, protect) ? size : 0 };
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
await input.close();
|
|
134
|
+
await rm(file, { force: true });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async importTree(source, staging, protect, signal) {
|
|
138
|
+
const snapshot = [];
|
|
139
|
+
let logicalBytes = 0, storedBytes = 0;
|
|
140
|
+
const visit = async (file, name) => {
|
|
141
|
+
signal?.throwIfAborted();
|
|
142
|
+
const info = await lstat(file);
|
|
143
|
+
if (!info.isFile() && !info.isDirectory())
|
|
144
|
+
throw new TypeError("resource import refuses links and special files");
|
|
145
|
+
if (name)
|
|
146
|
+
portablePath(name);
|
|
147
|
+
snapshot.push({ name, file, info });
|
|
148
|
+
if (snapshot.length > this.budget.maxFiles + 1)
|
|
149
|
+
throw new Error("resource file budget exceeded");
|
|
150
|
+
if (info.isFile()) {
|
|
151
|
+
logicalBytes += info.size;
|
|
152
|
+
if (info.size > this.budget.maxObjectBytes || logicalBytes > this.budget.maxTotalBytes)
|
|
153
|
+
throw new Error("resource total byte budget exceeded");
|
|
154
|
+
}
|
|
155
|
+
else
|
|
156
|
+
for (const child of (await readdir(file)).sort(compare))
|
|
157
|
+
await visit(path.join(file, child), name ? `${name}/${child}` : child);
|
|
158
|
+
};
|
|
159
|
+
await visit(source, "");
|
|
160
|
+
if (!snapshot[0]?.info.isDirectory())
|
|
161
|
+
throw new TypeError("tree source must be a directory");
|
|
162
|
+
// Validate shape, Unicode and case collisions before publishing any content.
|
|
163
|
+
const skeleton = { protocol: "hitch-tree@1", entries: snapshot.filter(e => e.name).map(e => e.info.isDirectory()
|
|
164
|
+
? { path: e.name, kind: "directory" } : { path: e.name, kind: "file", digest: `sha256:${"0".repeat(64)}`, size: e.info.size, executable: Boolean(e.info.mode & 0o111) }).sort((a, b) => compare(a.path, b.path)) };
|
|
165
|
+
parseTree(skeleton);
|
|
166
|
+
const entries = [];
|
|
167
|
+
for (const e of snapshot) {
|
|
168
|
+
if (signature(await lstat(e.file)) !== signature(e.info))
|
|
169
|
+
throw new Error("resource source path changed");
|
|
170
|
+
if (!e.name)
|
|
171
|
+
continue;
|
|
172
|
+
if (e.info.isDirectory())
|
|
173
|
+
entries.push({ path: e.name, kind: "directory" });
|
|
174
|
+
else {
|
|
175
|
+
const imported = await this.importFile(e.file, staging, protect, signal);
|
|
176
|
+
storedBytes += imported.storedBytes;
|
|
177
|
+
entries.push({ path: e.name, kind: "file", digest: imported.resource.digest, size: imported.resource.size, executable: Boolean(e.info.mode & 0o111) });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
for (const e of snapshot)
|
|
181
|
+
if (signature(await lstat(e.file)) !== signature(e.info))
|
|
182
|
+
throw new Error("resource source changed during tree import");
|
|
183
|
+
const tree = parseTree({ protocol: "hitch-tree@1", entries: entries.sort((a, b) => compare(a.path, b.path)) }), text = canonical(tree), manifestDigest = hash(text), file = path.join(staging, randomUUID());
|
|
184
|
+
if (Buffer.byteLength(text) > Math.min(this.budget.maxObjectBytes, 16 * 1024 ** 2))
|
|
185
|
+
throw new Error("tree manifest byte budget exceeded");
|
|
186
|
+
await writeFile(file, text, { flag: "wx", mode: 0o600 });
|
|
187
|
+
storedBytes += await this.publish(file, manifestDigest, Buffer.byteLength(text), protect) ? Buffer.byteLength(text) : 0;
|
|
188
|
+
return { resource: { kind: "tree", format: "hitch-tree@1", manifestDigest }, logicalBytes, storedBytes };
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=object-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"object-store.js","sourceRoot":"","sources":["../../../src/resources/object-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { createReadStream } from "node:fs";
|
|
3
|
+
import { Writable } from "node:stream";
|
|
4
|
+
import { pipeline } from "node:stream/promises";
|
|
5
|
+
import { createGunzip } from "node:zlib";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
export const DEFAULT_IMAGE_IMPORT_BUDGET = { maxExpandedBytes: 8 * 1024 ** 3, maxFiles: 100_000 };
|
|
8
|
+
const utf8 = new TextDecoder("utf-8", { fatal: true });
|
|
9
|
+
const text = (bytes) => utf8.decode(bytes.subarray(0, bytes.indexOf(0) < 0 ? bytes.length : bytes.indexOf(0)));
|
|
10
|
+
function number(bytes) {
|
|
11
|
+
const value = text(bytes).trim();
|
|
12
|
+
if (!/^[0-7]*$/.test(value))
|
|
13
|
+
throw new Error("unsupported OCI tar numeric encoding");
|
|
14
|
+
const parsed = parseInt(value || "0", 8);
|
|
15
|
+
if (!Number.isSafeInteger(parsed))
|
|
16
|
+
throw new Error("OCI tar size overflow");
|
|
17
|
+
return parsed;
|
|
18
|
+
}
|
|
19
|
+
function safePath(value) {
|
|
20
|
+
if (!value || Buffer.byteLength(value) > 4096 || value.startsWith("/") || /[\\\0]/.test(value) || value.split("/").some(part => part === ".." || Buffer.byteLength(part) > 255))
|
|
21
|
+
throw new Error("unsafe OCI layer path");
|
|
22
|
+
return path.posix.normalize(value).replace(/\/$/, "");
|
|
23
|
+
}
|
|
24
|
+
/** Inspects an uncompressed tar stream without writing any entry to disk.
|
|
25
|
+
* Supports regular entries, directories, links, PAX metadata and GNU long names.
|
|
26
|
+
* Sparse files and unknown entry/metadata formats fail closed before Docker runs. */
|
|
27
|
+
class TarBudget {
|
|
28
|
+
budget;
|
|
29
|
+
files = 0;
|
|
30
|
+
header = Buffer.alloc(512);
|
|
31
|
+
fill = 0;
|
|
32
|
+
remaining = 0;
|
|
33
|
+
content = 0;
|
|
34
|
+
extension = "";
|
|
35
|
+
metadata = [];
|
|
36
|
+
zeros = 0;
|
|
37
|
+
pending = {};
|
|
38
|
+
paths = new Map();
|
|
39
|
+
parents = new Set();
|
|
40
|
+
constructor(budget) {
|
|
41
|
+
this.budget = budget;
|
|
42
|
+
}
|
|
43
|
+
accept(bytes) {
|
|
44
|
+
let offset = 0;
|
|
45
|
+
while (offset < bytes.length) {
|
|
46
|
+
if (this.remaining) {
|
|
47
|
+
const size = Math.min(this.remaining, bytes.length - offset);
|
|
48
|
+
if (this.extension && this.content)
|
|
49
|
+
this.metadata.push(Buffer.from(bytes.subarray(offset, offset + Math.min(size, this.content))));
|
|
50
|
+
this.content = Math.max(0, this.content - size);
|
|
51
|
+
this.remaining -= size;
|
|
52
|
+
offset += size;
|
|
53
|
+
if (!this.remaining && this.extension)
|
|
54
|
+
this.finishExtension();
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const headerBytes = Math.min(512 - this.fill, bytes.length - offset);
|
|
58
|
+
bytes.copy(this.header, this.fill, offset, offset + headerBytes);
|
|
59
|
+
this.fill += headerBytes;
|
|
60
|
+
offset += headerBytes;
|
|
61
|
+
if (this.fill !== 512)
|
|
62
|
+
continue;
|
|
63
|
+
this.fill = 0;
|
|
64
|
+
if (this.header.every(n => n === 0)) {
|
|
65
|
+
this.zeros++;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (this.zeros)
|
|
69
|
+
throw new Error("OCI tar data after end marker");
|
|
70
|
+
const sum = this.header.reduce((n, byte, i) => n + (i >= 148 && i < 156 ? 32 : byte), 0);
|
|
71
|
+
if (sum !== number(this.header.subarray(148, 156)))
|
|
72
|
+
throw new Error("OCI tar checksum mismatch");
|
|
73
|
+
if (++this.files > this.budget.maxFiles)
|
|
74
|
+
throw new Error("OCI layer entry budget exceeded");
|
|
75
|
+
const type = String.fromCharCode(this.header[156] || 48), rawSize = number(this.header.subarray(124, 136));
|
|
76
|
+
const magic = this.header.subarray(257, 263).toString("latin1");
|
|
77
|
+
if (!["ustar\0", "ustar ", "\0\0\0\0\0\0"].includes(magic))
|
|
78
|
+
throw new Error("unsupported OCI tar header format");
|
|
79
|
+
if (["x", "g", "L", "K"].includes(type)) {
|
|
80
|
+
if (rawSize < 1 || rawSize > 64 * 1024)
|
|
81
|
+
throw new Error("OCI tar metadata budget exceeded");
|
|
82
|
+
this.extension = type;
|
|
83
|
+
this.content = rawSize;
|
|
84
|
+
this.remaining = Math.ceil(rawSize / 512) * 512;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (!["0", "1", "2", "5"].includes(type))
|
|
88
|
+
throw new Error(`unsupported OCI tar entry type: ${type}`);
|
|
89
|
+
const metadata = this.pending;
|
|
90
|
+
this.pending = {};
|
|
91
|
+
const prefix = magic === "ustar\0" ? text(this.header.subarray(345, 500)) : "", name = safePath(metadata.path ?? `${prefix ? `${prefix}/` : ""}${text(this.header.subarray(0, 100))}`);
|
|
92
|
+
if (name === "." && type !== "5")
|
|
93
|
+
throw new Error("invalid OCI root entry");
|
|
94
|
+
if (this.paths.has(name))
|
|
95
|
+
throw new Error("duplicate OCI layer path");
|
|
96
|
+
if (type !== "5" && this.parents.has(name))
|
|
97
|
+
throw new Error("OCI layer path replaces a parent directory");
|
|
98
|
+
const parts = name.split("/");
|
|
99
|
+
for (let i = 1; i < parts.length; i++) {
|
|
100
|
+
const parentPath = parts.slice(0, i).join("/");
|
|
101
|
+
this.parents.add(parentPath);
|
|
102
|
+
const parent = this.paths.get(parentPath);
|
|
103
|
+
if (parent && parent !== "5")
|
|
104
|
+
throw new Error("OCI layer path traverses a non-directory entry");
|
|
105
|
+
}
|
|
106
|
+
this.paths.set(name, type);
|
|
107
|
+
const size = metadata.size === undefined ? rawSize : Number(metadata.size);
|
|
108
|
+
if (!Number.isSafeInteger(size) || size < 0 || size > this.budget.maxExpandedBytes)
|
|
109
|
+
throw new Error("OCI layer expanded byte budget exceeded");
|
|
110
|
+
if (type !== "0" && size !== 0)
|
|
111
|
+
throw new Error("OCI non-file entry contains data");
|
|
112
|
+
if (type === "1" || type === "2") {
|
|
113
|
+
const target = metadata.linkpath ?? text(this.header.subarray(157, 257));
|
|
114
|
+
if (!target || Buffer.byteLength(target) > 4096 || /[\\\0]/.test(target))
|
|
115
|
+
throw new Error("unsafe OCI layer link");
|
|
116
|
+
if (type === "1")
|
|
117
|
+
safePath(target);
|
|
118
|
+
else {
|
|
119
|
+
// Absolute symlinks are container-root relative. Relative links must
|
|
120
|
+
// stay within that root; no host path is ever opened by this scanner.
|
|
121
|
+
const resolved = path.posix.normalize(target.startsWith("/") ? target.slice(1) : path.posix.join(path.posix.dirname(name), target));
|
|
122
|
+
if (resolved === ".." || resolved.startsWith("../"))
|
|
123
|
+
throw new Error("unsafe OCI layer link escape");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
this.remaining = Math.ceil(size / 512) * 512;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
finishExtension() {
|
|
130
|
+
const bytes = Buffer.concat(this.metadata);
|
|
131
|
+
this.metadata = [];
|
|
132
|
+
const type = this.extension;
|
|
133
|
+
this.extension = "";
|
|
134
|
+
if (type === "L" || type === "K") {
|
|
135
|
+
this.pending[type === "L" ? "path" : "linkpath"] = text(bytes);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const values = {};
|
|
139
|
+
for (let offset = 0; offset < bytes.length;) {
|
|
140
|
+
const space = bytes.indexOf(32, offset), lengthText = bytes.subarray(offset, space).toString();
|
|
141
|
+
const length = Number(lengthText);
|
|
142
|
+
if (space < offset || !/^[1-9][0-9]*$/.test(lengthText) || !Number.isSafeInteger(length) || length <= space - offset + 1 || offset + length > bytes.length || bytes[offset + length - 1] !== 10)
|
|
143
|
+
throw new Error("invalid OCI PAX record");
|
|
144
|
+
const value = utf8.decode(bytes.subarray(space + 1, offset + length - 1)), equals = value.indexOf("="), key = value.slice(0, equals);
|
|
145
|
+
if (equals < 1 || Object.hasOwn(values, key))
|
|
146
|
+
throw new Error("invalid OCI PAX key");
|
|
147
|
+
if (!["path", "linkpath", "size", "mtime", "atime", "ctime", "uid", "gid", "uname", "gname", "charset", "comment", "LIBARCHIVE.creationtime"].includes(key) && !key.startsWith("SCHILY.xattr."))
|
|
148
|
+
throw new Error(`unsupported OCI PAX metadata: ${key}`);
|
|
149
|
+
values[key] = value.slice(equals + 1);
|
|
150
|
+
offset += length;
|
|
151
|
+
}
|
|
152
|
+
if (values.size !== undefined && !/^(0|[1-9][0-9]*)$/.test(values.size))
|
|
153
|
+
throw new Error("invalid OCI PAX size");
|
|
154
|
+
if (type === "g") {
|
|
155
|
+
if (["path", "linkpath", "size"].some(key => key in values))
|
|
156
|
+
throw new Error("unsupported OCI global path/size override");
|
|
157
|
+
}
|
|
158
|
+
else
|
|
159
|
+
for (const key of ["path", "linkpath", "size"])
|
|
160
|
+
if (values[key] !== undefined)
|
|
161
|
+
this.pending[key] = values[key];
|
|
162
|
+
}
|
|
163
|
+
finish() {
|
|
164
|
+
if (this.fill || this.remaining || this.zeros < 2 || Object.keys(this.pending).length)
|
|
165
|
+
throw new Error("truncated OCI layer tar");
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
export async function validateOciLayer(file, start, size, mediaType, budget, signal) {
|
|
169
|
+
const gzip = ["application/vnd.oci.image.layer.v1.tar+gzip", "application/vnd.docker.image.rootfs.diff.tar.gzip"].includes(mediaType);
|
|
170
|
+
if (!gzip && mediaType !== "application/vnd.oci.image.layer.v1.tar" && mediaType !== "application/vnd.docker.image.rootfs.diff.tar")
|
|
171
|
+
throw new Error(`unsupported OCI layer media type: ${mediaType}`);
|
|
172
|
+
if (!size)
|
|
173
|
+
throw new Error("empty OCI layer blob");
|
|
174
|
+
const parser = new TarBudget(budget), hash = createHash("sha256");
|
|
175
|
+
let expandedBytes = 0;
|
|
176
|
+
// A Writable preserves the parser failure across pipeline teardown on Node 22;
|
|
177
|
+
// throwing from an async-iterator sink can replace it with an AbortError.
|
|
178
|
+
const consume = new Writable({
|
|
179
|
+
write(chunk, _encoding, callback) {
|
|
180
|
+
try {
|
|
181
|
+
signal?.throwIfAborted();
|
|
182
|
+
expandedBytes += chunk.length;
|
|
183
|
+
if (expandedBytes > budget.maxExpandedBytes)
|
|
184
|
+
throw new Error("OCI layer expanded byte budget exceeded");
|
|
185
|
+
hash.update(chunk);
|
|
186
|
+
parser.accept(chunk);
|
|
187
|
+
callback();
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
callback(error instanceof Error ? error : new Error(String(error)));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
const input = createReadStream(file, { start, end: start + size - 1 });
|
|
195
|
+
if (gzip)
|
|
196
|
+
await pipeline(input, createGunzip(), consume, { signal });
|
|
197
|
+
else
|
|
198
|
+
await pipeline(input, consume, { signal });
|
|
199
|
+
parser.finish();
|
|
200
|
+
return { expandedBytes, files: parser.files, diffId: `sha256:${hash.digest("hex")}` };
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=oci-layer-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oci-layer-validation.js","sourceRoot":"","sources":["../../../src/resources/oci-layer-validation.ts"],"names":[],"mappings":"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"}
|