@impz/testing 5.0.0 → 6.0.0
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/README.md +4 -1
- package/dist/index.js +455 -503
- package/package.json +44 -22
package/dist/index.js
CHANGED
|
@@ -21711,7 +21711,6 @@ var EnvSchema = z53.object({
|
|
|
21711
21711
|
IMP_SESSION_LOG_IMP_MAX_MIB: CountSchema3.default(64),
|
|
21712
21712
|
IMP_SESSION_LOG_IMP_MAX_LIVE: CountSchema3.default(8),
|
|
21713
21713
|
IMP_SESSION_LOG_MAX_AGE_DAYS: CountSchema3.default(7),
|
|
21714
|
-
DOCKER_HOST: z53.string().optional(),
|
|
21715
21714
|
IMP_KSM: z53.enum(["0", "1"]).default("0"),
|
|
21716
21715
|
IMP_KSM_EXEC: z53.string().default("ksm-exec"),
|
|
21717
21716
|
IMP_KSM_HEADROOM_PERCENT: z53.string().default("100"),
|
|
@@ -21876,7 +21875,6 @@ function loadConfig(env) {
|
|
|
21876
21875
|
impMaxLive: parsed.IMP_SESSION_LOG_IMP_MAX_LIVE,
|
|
21877
21876
|
maxAgeMs: parsed.IMP_SESSION_LOG_MAX_AGE_DAYS * 86400000
|
|
21878
21877
|
},
|
|
21879
|
-
dockerHost: parsed.DOCKER_HOST ?? null,
|
|
21880
21878
|
dns: parsed.IMP_DNS,
|
|
21881
21879
|
subnet,
|
|
21882
21880
|
ipv6: parseIpv6Setting(parsed.IMP_SUBNET6),
|
|
@@ -21951,7 +21949,7 @@ import { join as join46 } from "path";
|
|
|
21951
21949
|
// packages/daemon/package.json
|
|
21952
21950
|
var package_default = {
|
|
21953
21951
|
name: "@impz/daemon",
|
|
21954
|
-
version: "
|
|
21952
|
+
version: "6.0.0",
|
|
21955
21953
|
private: true,
|
|
21956
21954
|
description: "impd: the imp host daemon",
|
|
21957
21955
|
type: "module",
|
|
@@ -21960,6 +21958,7 @@ var package_default = {
|
|
|
21960
21958
|
"./testing": "./testing/index.ts",
|
|
21961
21959
|
"./testing/build-stub-clock": "./testing/platform/build-stub-clock.ts",
|
|
21962
21960
|
"./testing/data-layout": "./src/vm/storage/data-layout.ts",
|
|
21961
|
+
"./testing/pebble-images": "./testing/network/pebble-images.ts",
|
|
21963
21962
|
"./testing/start-stub-agent": "./testing/vm/start-stub-agent.ts",
|
|
21964
21963
|
"./testing/start-test-impd": "./testing/start-test-impd.ts",
|
|
21965
21964
|
"./testing/test-token": "./testing/test-token.ts",
|
|
@@ -31995,11 +31994,7 @@ function createImpServer(options, caller, calls) {
|
|
|
31995
31994
|
const printError = options.printError ?? console.error;
|
|
31996
31995
|
const server = new McpServer({ name: "imp", title: "imp", version: options.version }, {
|
|
31997
31996
|
capabilities: { tools: { listChanged: false } },
|
|
31998
|
-
instructions:
|
|
31999
|
-
'imp runs persistent Linux microVMs ("imps") that sleep when idle and wake on use.',
|
|
32000
|
-
`This server may touch ${caller.guard.summary}.`,
|
|
32001
|
-
"Create an imp, run commands and read and write files in it, checkpoint before a risky change, fork to try two fixes, and destroy what you no longer need."
|
|
32002
|
-
].join(" "),
|
|
31997
|
+
instructions: buildInstructions(caller),
|
|
32003
31998
|
supportedProtocolVersions: [...PROTOCOL_VERSIONS, ...MODERN_PROTOCOL_VERSIONS]
|
|
32004
31999
|
});
|
|
32005
32000
|
server.server.fallbackRequestHandler = (request) => Promise.reject(new ProtocolError(ProtocolErrorCode.MethodNotFound, `unknown method: ${request.method}`));
|
|
@@ -32055,6 +32050,20 @@ function createImpServer(options, caller, calls) {
|
|
|
32055
32050
|
}
|
|
32056
32051
|
return server;
|
|
32057
32052
|
}
|
|
32053
|
+
var SCOPE_GUIDES = {
|
|
32054
|
+
read: "This caller has the read scope, so it can only look: list imps, their URLs, the images and the checkpoints. Running commands, reading or writing files, and creating, checkpointing, forking or destroying an imp need a wider grant: exec or manage.",
|
|
32055
|
+
exec: "This caller has the exec scope: list imps, their URLs, the images and the checkpoints, run commands and read and write files in an imp, and put one to sleep. Creating, checkpointing, restoring, forking or destroying an imp needs a manage grant.",
|
|
32056
|
+
manage: "Create an imp, run commands and read and write files in it, checkpoint before a risky change, fork to try two fixes, and destroy what you no longer need."
|
|
32057
|
+
};
|
|
32058
|
+
function buildInstructions(caller) {
|
|
32059
|
+
const names = TOOLS.filter((tool) => hasScope(caller.scope, tool.scope)).map((tool) => tool.definition.name);
|
|
32060
|
+
return [
|
|
32061
|
+
'imp runs persistent Linux microVMs ("imps") that sleep when idle and wake on use.',
|
|
32062
|
+
`This server may touch ${caller.guard.summary}.`,
|
|
32063
|
+
SCOPE_GUIDES[caller.scope],
|
|
32064
|
+
`Its tools: ${names.join(", ")}.`
|
|
32065
|
+
].join(" ");
|
|
32066
|
+
}
|
|
32058
32067
|
function startProgress(ctx, options) {
|
|
32059
32068
|
const token = ctx.mcpReq._meta?.progressToken;
|
|
32060
32069
|
if (token === undefined) {
|
|
@@ -33440,7 +33449,7 @@ input=$target.in
|
|
|
33440
33449
|
next=$target.tmp
|
|
33441
33450
|
|
|
33442
33451
|
mkdir -p "$(dirname "$target")"
|
|
33443
|
-
cat >"$input"
|
|
33452
|
+
cat > "$input"
|
|
33444
33453
|
|
|
33445
33454
|
# the first line that is the marker, matched whole and as a fixed string
|
|
33446
33455
|
split=$(grep -n -x -F -e "$marker" "$input" | head -n 1 | cut -d : -f 1)
|
|
@@ -33467,7 +33476,7 @@ done
|
|
|
33467
33476
|
fi
|
|
33468
33477
|
echo
|
|
33469
33478
|
head -n "$((split - 1))" "$input"
|
|
33470
|
-
} >"$next"
|
|
33479
|
+
} > "$next"
|
|
33471
33480
|
|
|
33472
33481
|
rm -f "$input"
|
|
33473
33482
|
|
|
@@ -47420,7 +47429,7 @@ function createImpdServices(config, deps, parts) {
|
|
|
47420
47429
|
|
|
47421
47430
|
// packages/daemon/src/imps/images/build/create-image-builds.ts
|
|
47422
47431
|
import { existsSync as existsSync18, mkdirSync as mkdirSync20, rmSync as rmSync24, statSync as statSync12 } from "fs";
|
|
47423
|
-
import { join as
|
|
47432
|
+
import { join as join28, posix as posix3 } from "path";
|
|
47424
47433
|
|
|
47425
47434
|
// packages/local-tar/src/extract-local.ts
|
|
47426
47435
|
import tar from "tar-stream";
|
|
@@ -47885,6 +47894,9 @@ function formatFamiliarRepository(reference) {
|
|
|
47885
47894
|
}
|
|
47886
47895
|
|
|
47887
47896
|
// packages/daemon/src/imps/docker-contract/check-registry.ts
|
|
47897
|
+
function checkReferenceRegistry(reference) {
|
|
47898
|
+
return checkRegistry(parseImageReference(reference).registry);
|
|
47899
|
+
}
|
|
47888
47900
|
function checkRegistry(registry) {
|
|
47889
47901
|
const host = readRegistryHost(registry).toLowerCase();
|
|
47890
47902
|
if (host === "localhost" || host.endsWith(".localhost")) {
|
|
@@ -47895,9 +47907,6 @@ function checkRegistry(registry) {
|
|
|
47895
47907
|
}
|
|
47896
47908
|
return null;
|
|
47897
47909
|
}
|
|
47898
|
-
function checkReferenceRegistry(reference) {
|
|
47899
|
-
return checkRegistry(parseImageReference(reference).registry);
|
|
47900
|
-
}
|
|
47901
47910
|
function readRegistryHost(registry) {
|
|
47902
47911
|
if (registry.startsWith("[")) {
|
|
47903
47912
|
return registry.slice(1, registry.indexOf("]"));
|
|
@@ -48761,54 +48770,12 @@ class DockerBuildError extends Error {
|
|
|
48761
48770
|
// packages/daemon/src/imps/images/build/guest-build.ts
|
|
48762
48771
|
import * as z86 from "zod";
|
|
48763
48772
|
|
|
48764
|
-
// packages/daemon/src/
|
|
48765
|
-
|
|
48766
|
-
|
|
48767
|
-
|
|
48768
|
-
|
|
48769
|
-
|
|
48770
|
-
const dirs = [root];
|
|
48771
|
-
for (let directory = dirs.pop();directory !== undefined; directory = dirs.pop()) {
|
|
48772
|
-
const entries = await readEntries2(readDir, directory);
|
|
48773
|
-
for (const entry of entries) {
|
|
48774
|
-
if (entry.isDirectory()) {
|
|
48775
|
-
dirs.push(join28(directory, entry.name));
|
|
48776
|
-
}
|
|
48777
|
-
}
|
|
48778
|
-
const files = entries.filter((entry) => entry.isFile());
|
|
48779
|
-
const stats = await Promise.all(files.map((entry) => readFileStats(join28(directory, entry.name))));
|
|
48780
|
-
for (const stat of stats) {
|
|
48781
|
-
if (stat !== undefined) {
|
|
48782
|
-
inodes.set(`${String(stat.dev)}:${String(stat.ino)}`, Math.max(0, stat.blocks - 1) * 512);
|
|
48783
|
-
}
|
|
48784
|
-
}
|
|
48785
|
-
}
|
|
48786
|
-
return [...inodes.values()].reduce((sum, bytes) => sum + bytes, 0);
|
|
48787
|
-
}
|
|
48788
|
-
function readDirEntries(directory) {
|
|
48789
|
-
return readdir3(directory, { withFileTypes: true });
|
|
48790
|
-
}
|
|
48791
|
-
async function readEntries2(readDir, directory) {
|
|
48792
|
-
try {
|
|
48793
|
-
return await readDir(directory);
|
|
48794
|
-
} catch (error) {
|
|
48795
|
-
if (isMissing(error)) {
|
|
48796
|
-
return [];
|
|
48797
|
-
}
|
|
48798
|
-
throw error;
|
|
48799
|
-
}
|
|
48800
|
-
}
|
|
48801
|
-
function isMissing(error) {
|
|
48802
|
-
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
48803
|
-
}
|
|
48804
|
-
async function readFileStats(path) {
|
|
48805
|
-
try {
|
|
48806
|
-
return await lstat3(path);
|
|
48807
|
-
} catch (error) {
|
|
48808
|
-
if (isMissing(error)) {
|
|
48809
|
-
return;
|
|
48810
|
-
}
|
|
48811
|
-
throw error;
|
|
48773
|
+
// packages/daemon/src/imps/images/build/image-limit-error.ts
|
|
48774
|
+
class ImageLimitError extends ImpdError {
|
|
48775
|
+
name = "ImageLimitError";
|
|
48776
|
+
constructor(limit, max) {
|
|
48777
|
+
const over = limit === "bytes" ? `over ${String(Math.floor(max / 1024 ** 2))} MiB (IMP_BUILD_IMAGE_MAX_MIB)` : `over ${String(max)} files (IMP_BUILD_IMAGE_MAX_FILES)`;
|
|
48778
|
+
super("BAD_REQUEST", { message: `the built image's filesystem is ${over}` });
|
|
48812
48779
|
}
|
|
48813
48780
|
}
|
|
48814
48781
|
|
|
@@ -48844,73 +48811,9 @@ var UNPACK_TAR_ARGS = [
|
|
|
48844
48811
|
"-xpf",
|
|
48845
48812
|
"-"
|
|
48846
48813
|
];
|
|
48847
|
-
async function writeExportedTree(containerId, root) {
|
|
48848
|
-
const exporter = Bun.spawn(["docker", "export", containerId], {
|
|
48849
|
-
stdin: "ignore",
|
|
48850
|
-
stdout: "pipe",
|
|
48851
|
-
stderr: "pipe",
|
|
48852
|
-
env: process.env
|
|
48853
|
-
});
|
|
48854
|
-
const tar = await spawnTar(root, exporter);
|
|
48855
|
-
const state = { hasExportEnded: false, isTarFirst: false, isExportStopped: false };
|
|
48856
|
-
const waitForExport = async () => {
|
|
48857
|
-
await exporter.exited;
|
|
48858
|
-
state.hasExportEnded = true;
|
|
48859
|
-
};
|
|
48860
|
-
const waitForTar = async () => {
|
|
48861
|
-
const code = await tar.exited;
|
|
48862
|
-
if (code !== 0 && !state.hasExportEnded) {
|
|
48863
|
-
state.isTarFirst = true;
|
|
48864
|
-
state.isExportStopped = true;
|
|
48865
|
-
exporter.kill();
|
|
48866
|
-
}
|
|
48867
|
-
};
|
|
48868
|
-
waitForExport();
|
|
48869
|
-
waitForTar();
|
|
48870
|
-
const [exportCode, exportStderr, tarCode, tarStderr] = await Promise.all([
|
|
48871
|
-
exporter.exited,
|
|
48872
|
-
new Response(exporter.stderr).text(),
|
|
48873
|
-
tar.exited,
|
|
48874
|
-
new Response(tar.stderr).text()
|
|
48875
|
-
]);
|
|
48876
|
-
const failure = findUnpackFailure({
|
|
48877
|
-
containerId,
|
|
48878
|
-
exported: { exitCode: exportCode, stdout: "", stderr: exportStderr },
|
|
48879
|
-
unpacked: { exitCode: tarCode, stdout: "", stderr: tarStderr },
|
|
48880
|
-
isTarFirst: state.isTarFirst,
|
|
48881
|
-
isExportStopped: state.isExportStopped && exporter.signalCode !== null
|
|
48882
|
-
});
|
|
48883
|
-
if (failure !== null) {
|
|
48884
|
-
throw failure;
|
|
48885
|
-
}
|
|
48886
|
-
assertUnpacked({ exitCode: tarCode, stdout: "", stderr: tarStderr });
|
|
48887
|
-
}
|
|
48888
|
-
async function spawnTar(root, exporter) {
|
|
48889
|
-
try {
|
|
48890
|
-
return Bun.spawn([...UNPACK_TAR_ARGS, "-C", root], {
|
|
48891
|
-
stdin: exporter.stdout,
|
|
48892
|
-
stdout: "ignore",
|
|
48893
|
-
stderr: "pipe",
|
|
48894
|
-
env: { ...process.env, LC_ALL: "C" }
|
|
48895
|
-
});
|
|
48896
|
-
} catch (error) {
|
|
48897
|
-
exporter.kill();
|
|
48898
|
-
await exporter.exited;
|
|
48899
|
-
throw error;
|
|
48900
|
-
}
|
|
48901
|
-
}
|
|
48902
|
-
function findUnpackFailure(outcome) {
|
|
48903
|
-
const exportFailure = outcome.exported.exitCode === 0 || outcome.isExportStopped ? null : buildExitError(["docker", "export", outcome.containerId], outcome.exported);
|
|
48904
|
-
const tarFailure = outcome.unpacked.exitCode === 0 ? null : buildExitError(["tar"], outcome.unpacked);
|
|
48905
|
-
if (exportFailure === null || tarFailure === null) {
|
|
48906
|
-
return exportFailure ?? tarFailure;
|
|
48907
|
-
}
|
|
48908
|
-
const [first, second] = outcome.isTarFirst ? [tarFailure, exportFailure] : [exportFailure, tarFailure];
|
|
48909
|
-
return new Error(`${first.message}; ${second.message}`);
|
|
48910
|
-
}
|
|
48911
48814
|
function assertUnpacked(result) {
|
|
48912
48815
|
if (result.exitCode !== 0) {
|
|
48913
|
-
throw buildExitError(["
|
|
48816
|
+
throw buildExitError(["tar"], result);
|
|
48914
48817
|
}
|
|
48915
48818
|
const failure = findXattrFailure(result.stderr);
|
|
48916
48819
|
if (failure?.missingSetfcap === true) {
|
|
@@ -48946,6 +48849,122 @@ function readSetfcapWarning(status = readFileSync23("/proc/self/status", "utf8")
|
|
|
48946
48849
|
return `no CAP_SETFCAP, so an image add or build fails on an image with a file capability; ${SETFCAP_HELP}`;
|
|
48947
48850
|
}
|
|
48948
48851
|
|
|
48852
|
+
// packages/daemon/src/imps/images/rootfs/write-streamed-tree.ts
|
|
48853
|
+
var EXPORT_IDLE_MS = 120000;
|
|
48854
|
+
var GROW_STEP_BYTES = 256 * 1024 ** 2;
|
|
48855
|
+
var TAR_STOPPED = new Error("tar stopped before the export ended");
|
|
48856
|
+
async function writeStreamedTree(options) {
|
|
48857
|
+
const root = options.root;
|
|
48858
|
+
const limits = options.limits;
|
|
48859
|
+
const grow = options.grow ?? (() => Promise.resolve());
|
|
48860
|
+
const startTimer = options.startTimer;
|
|
48861
|
+
const maxBytes = limits.maxBytes;
|
|
48862
|
+
const tar = options.startTar([...UNPACK_TAR_ARGS, "-vv", "--quoting-style=escape", "-C", root]);
|
|
48863
|
+
const sent = { bytes: 0 };
|
|
48864
|
+
const unpacked = {};
|
|
48865
|
+
const stopExport = new AbortController;
|
|
48866
|
+
const readMax = (limit) => limit === "bytes" ? maxBytes : limits.maxFiles;
|
|
48867
|
+
const listed = { bytes: 0 };
|
|
48868
|
+
const held = { bytes: 0 };
|
|
48869
|
+
const growHold = async () => {
|
|
48870
|
+
const tree = Math.max(sent.bytes, listed.bytes);
|
|
48871
|
+
const needed = 2 * tree + planRootfsOverhead(tree);
|
|
48872
|
+
if (needed > held.bytes) {
|
|
48873
|
+
held.bytes = (Math.floor(needed / GROW_STEP_BYTES) + 1) * GROW_STEP_BYTES;
|
|
48874
|
+
const writtenBytes = await options.countTreeBytes(root);
|
|
48875
|
+
await grow(held.bytes, writtenBytes);
|
|
48876
|
+
}
|
|
48877
|
+
};
|
|
48878
|
+
const onCounted = (bytes) => {
|
|
48879
|
+
listed.bytes = bytes;
|
|
48880
|
+
};
|
|
48881
|
+
const counting = countUnpacked(tar.stdout, limits, onCounted, (limit) => {
|
|
48882
|
+
unpacked.over = limit;
|
|
48883
|
+
const max = readMax(limit);
|
|
48884
|
+
tar.kill();
|
|
48885
|
+
stopExport.abort(new ImageLimitError(limit, max));
|
|
48886
|
+
});
|
|
48887
|
+
const waitForTar = async () => {
|
|
48888
|
+
const code = await tar.exited;
|
|
48889
|
+
if (code !== 0 && unpacked.ended !== true) {
|
|
48890
|
+
stopExport.abort(TAR_STOPPED);
|
|
48891
|
+
}
|
|
48892
|
+
};
|
|
48893
|
+
waitForTar();
|
|
48894
|
+
const assertWithinLimits = () => {
|
|
48895
|
+
const over = unpacked.over;
|
|
48896
|
+
if (over !== undefined) {
|
|
48897
|
+
throw new ImageLimitError(over, readMax(over));
|
|
48898
|
+
}
|
|
48899
|
+
};
|
|
48900
|
+
const stopIdle = () => {
|
|
48901
|
+
stopExport.abort(new Error(`${options.source} sent nothing in ${String(EXPORT_IDLE_MS / 1000)} s`));
|
|
48902
|
+
};
|
|
48903
|
+
const idle = { cancel: startTimer(stopIdle, EXPORT_IDLE_MS) };
|
|
48904
|
+
const stderrText = new Response(tar.stderr).text();
|
|
48905
|
+
try {
|
|
48906
|
+
const exported = await options.runExport(async (chunk) => {
|
|
48907
|
+
idle.cancel();
|
|
48908
|
+
idle.cancel = startTimer(stopIdle, EXPORT_IDLE_MS);
|
|
48909
|
+
sent.bytes += chunk.byteLength;
|
|
48910
|
+
if (sent.bytes > maxBytes) {
|
|
48911
|
+
throw new ImageLimitError("bytes", maxBytes);
|
|
48912
|
+
}
|
|
48913
|
+
assertWithinLimits();
|
|
48914
|
+
await growHold();
|
|
48915
|
+
await tar.stdin.write(chunk);
|
|
48916
|
+
await tar.stdin.flush();
|
|
48917
|
+
}, AbortSignal.any([options.signal, stopExport.signal]));
|
|
48918
|
+
if (exported.exitCode !== 0) {
|
|
48919
|
+
throw new Error(`${options.source}: ${exported.stderr.trim()}`);
|
|
48920
|
+
}
|
|
48921
|
+
unpacked.ended = true;
|
|
48922
|
+
await tar.stdin.end();
|
|
48923
|
+
const [exitCode, stderr] = await Promise.all([tar.exited, stderrText, counting]);
|
|
48924
|
+
assertWithinLimits();
|
|
48925
|
+
assertUnpacked({ exitCode, stdout: "", stderr });
|
|
48926
|
+
await growHold();
|
|
48927
|
+
} catch (error) {
|
|
48928
|
+
tar.kill();
|
|
48929
|
+
assertWithinLimits();
|
|
48930
|
+
if (stopExport.signal.reason === TAR_STOPPED) {
|
|
48931
|
+
const [exitCode, stderr] = await Promise.all([tar.exited, stderrText]);
|
|
48932
|
+
assertUnpacked({ exitCode, stdout: "", stderr });
|
|
48933
|
+
}
|
|
48934
|
+
throw error;
|
|
48935
|
+
} finally {
|
|
48936
|
+
idle.cancel();
|
|
48937
|
+
}
|
|
48938
|
+
}
|
|
48939
|
+
async function countUnpacked(stdout, limits, onCounted, onOver) {
|
|
48940
|
+
const decoder = new TextDecoder;
|
|
48941
|
+
const counted = { lines: 0, bytes: 0, rest: "" };
|
|
48942
|
+
for await (const chunk of stdout) {
|
|
48943
|
+
const lines = `${counted.rest}${decoder.decode(chunk, { stream: true })}`.split(`
|
|
48944
|
+
`);
|
|
48945
|
+
counted.rest = lines.pop() ?? "";
|
|
48946
|
+
for (const line of lines) {
|
|
48947
|
+
counted.lines += 1;
|
|
48948
|
+
counted.bytes += readAllocatedBytes(line);
|
|
48949
|
+
}
|
|
48950
|
+
onCounted(counted.bytes);
|
|
48951
|
+
if (counted.bytes > limits.maxBytes) {
|
|
48952
|
+
onOver("bytes");
|
|
48953
|
+
return;
|
|
48954
|
+
}
|
|
48955
|
+
if (counted.lines > limits.maxFiles) {
|
|
48956
|
+
onOver("files");
|
|
48957
|
+
return;
|
|
48958
|
+
}
|
|
48959
|
+
}
|
|
48960
|
+
}
|
|
48961
|
+
var BLOCK_BYTES = 4096;
|
|
48962
|
+
function readAllocatedBytes(line) {
|
|
48963
|
+
const size = line.trim().split(/\s+/v)[2] ?? "";
|
|
48964
|
+
const bytes = /^\d+$/v.test(size) ? Number(size) : 0;
|
|
48965
|
+
return Math.max(1, Math.ceil(bytes / BLOCK_BYTES)) * BLOCK_BYTES;
|
|
48966
|
+
}
|
|
48967
|
+
|
|
48949
48968
|
// packages/daemon/src/imps/images/build/create-build-steps.ts
|
|
48950
48969
|
var STEP_PATTERN = /^\[(?:\S+ )?\d+\/\d+\] /v;
|
|
48951
48970
|
function createBuildSteps(setStep) {
|
|
@@ -49018,15 +49037,6 @@ function createPlainLines(output) {
|
|
|
49018
49037
|
};
|
|
49019
49038
|
}
|
|
49020
49039
|
|
|
49021
|
-
// packages/daemon/src/imps/images/build/image-limit-error.ts
|
|
49022
|
-
class ImageLimitError extends ImpdError {
|
|
49023
|
-
name = "ImageLimitError";
|
|
49024
|
-
constructor(limit, max) {
|
|
49025
|
-
const over = limit === "bytes" ? `over ${String(Math.floor(max / 1024 ** 2))} MiB (IMP_BUILD_IMAGE_MAX_MIB)` : `over ${String(max)} files (IMP_BUILD_IMAGE_MAX_FILES)`;
|
|
49026
|
-
super("BAD_REQUEST", { message: `the built image's filesystem is ${over}` });
|
|
49027
|
-
}
|
|
49028
|
-
}
|
|
49029
|
-
|
|
49030
49040
|
// packages/daemon/src/imps/images/build/guest-build.ts
|
|
49031
49041
|
var GUEST_BUILD_TAG = "imp-build:latest";
|
|
49032
49042
|
var LOG_MAX_CHARS = 8000;
|
|
@@ -49082,55 +49092,9 @@ async function loadGuestImage(exec, options) {
|
|
|
49082
49092
|
var DIGEST_PREFIX = "imp-build-";
|
|
49083
49093
|
var DIGEST_DOMAIN = `imp build image v1
|
|
49084
49094
|
`;
|
|
49085
|
-
var EXPORT_IDLE_MS = 120000;
|
|
49086
|
-
var GROW_STEP_BYTES = 256 * 1024 ** 2;
|
|
49087
|
-
var TAR_STOPPED = new Error("tar stopped before the export ended");
|
|
49088
|
-
function createImageHash(configText) {
|
|
49089
|
-
const config = new TextEncoder().encode(configText);
|
|
49090
|
-
const length = new Uint8Array(8);
|
|
49091
|
-
new DataView(length.buffer).setBigUint64(0, BigInt(config.byteLength));
|
|
49092
|
-
const hash = new Bun.CryptoHasher("sha256");
|
|
49093
|
-
hash.update(DIGEST_DOMAIN);
|
|
49094
|
-
hash.update(length);
|
|
49095
|
-
hash.update(config);
|
|
49096
|
-
return hash;
|
|
49097
|
-
}
|
|
49098
|
-
var BLOCK_BYTES = 4096;
|
|
49099
|
-
function readAllocatedBytes(line) {
|
|
49100
|
-
const size = line.trim().split(/\s+/v)[2] ?? "";
|
|
49101
|
-
const bytes = /^\d+$/v.test(size) ? Number(size) : 0;
|
|
49102
|
-
return Math.max(1, Math.ceil(bytes / BLOCK_BYTES)) * BLOCK_BYTES;
|
|
49103
|
-
}
|
|
49104
|
-
async function countUnpacked(stdout, limits, onCounted, onOver) {
|
|
49105
|
-
const decoder = new TextDecoder;
|
|
49106
|
-
const counted = { lines: 0, bytes: 0, rest: "" };
|
|
49107
|
-
for await (const chunk of stdout) {
|
|
49108
|
-
const lines = `${counted.rest}${decoder.decode(chunk, { stream: true })}`.split(`
|
|
49109
|
-
`);
|
|
49110
|
-
counted.rest = lines.pop() ?? "";
|
|
49111
|
-
for (const line of lines) {
|
|
49112
|
-
counted.lines += 1;
|
|
49113
|
-
counted.bytes += readAllocatedBytes(line);
|
|
49114
|
-
}
|
|
49115
|
-
onCounted(counted.bytes);
|
|
49116
|
-
if (counted.bytes > limits.maxBytes) {
|
|
49117
|
-
onOver("bytes");
|
|
49118
|
-
return;
|
|
49119
|
-
}
|
|
49120
|
-
if (counted.lines > limits.maxFiles) {
|
|
49121
|
-
onOver("files");
|
|
49122
|
-
return;
|
|
49123
|
-
}
|
|
49124
|
-
}
|
|
49125
|
-
}
|
|
49126
49095
|
async function writeGuestTree(options) {
|
|
49127
49096
|
const exec = options.exec;
|
|
49128
|
-
const root = options.root;
|
|
49129
|
-
const limits = options.limits;
|
|
49130
49097
|
const signal = options.signal;
|
|
49131
|
-
const grow = options.grow ?? (() => Promise.resolve());
|
|
49132
|
-
const startTimer = options.startTimer ?? startRealTimer;
|
|
49133
|
-
const maxBytes = limits.maxBytes;
|
|
49134
49098
|
const inspected = await exec(["docker", "image", "inspect", "--format", "{{json .Config}}", GUEST_BUILD_TAG], { signal });
|
|
49135
49099
|
if (inspected.exitCode !== 0) {
|
|
49136
49100
|
throw new Error(`docker image inspect in the builder: ${inspected.stderr.trim()}`);
|
|
@@ -49142,94 +49106,36 @@ async function writeGuestTree(options) {
|
|
|
49142
49106
|
throw new Error(`docker create in the builder: ${created.stderr.trim()}`);
|
|
49143
49107
|
}
|
|
49144
49108
|
const containerId = ContainerIdSchema.parse(created.stdout.trim());
|
|
49145
|
-
const tar = Bun.spawn([...UNPACK_TAR_ARGS, "-vv", "--quoting-style=escape", "-C", root], {
|
|
49146
|
-
stdin: "pipe",
|
|
49147
|
-
stdout: "pipe",
|
|
49148
|
-
stderr: "pipe",
|
|
49149
|
-
env: { ...process.env, LC_ALL: "C" }
|
|
49150
|
-
});
|
|
49151
49109
|
const hash = createImageHash(configText);
|
|
49152
|
-
|
|
49153
|
-
|
|
49154
|
-
|
|
49155
|
-
|
|
49156
|
-
|
|
49157
|
-
|
|
49158
|
-
|
|
49159
|
-
|
|
49160
|
-
|
|
49161
|
-
|
|
49162
|
-
|
|
49163
|
-
const writtenBytes = await countTreeBytes(root);
|
|
49164
|
-
await grow(held.bytes, writtenBytes);
|
|
49165
|
-
}
|
|
49166
|
-
};
|
|
49167
|
-
const onCounted = (bytes) => {
|
|
49168
|
-
listed.bytes = bytes;
|
|
49169
|
-
};
|
|
49170
|
-
const counting = countUnpacked(tar.stdout, limits, onCounted, (limit) => {
|
|
49171
|
-
unpacked.over = limit;
|
|
49172
|
-
const max = readMax(limit);
|
|
49173
|
-
tar.kill();
|
|
49174
|
-
stopExport.abort(new ImageLimitError(limit, max));
|
|
49175
|
-
});
|
|
49176
|
-
const waitForTar = async () => {
|
|
49177
|
-
const code = await tar.exited;
|
|
49178
|
-
if (code !== 0 && unpacked.ended !== true) {
|
|
49179
|
-
stopExport.abort(TAR_STOPPED);
|
|
49180
|
-
}
|
|
49181
|
-
};
|
|
49182
|
-
waitForTar();
|
|
49183
|
-
const assertWithinLimits = () => {
|
|
49184
|
-
const over = unpacked.over;
|
|
49185
|
-
if (over !== undefined) {
|
|
49186
|
-
throw new ImageLimitError(over, readMax(over));
|
|
49187
|
-
}
|
|
49188
|
-
};
|
|
49189
|
-
const stopIdle = () => {
|
|
49190
|
-
stopExport.abort(new Error(`docker export in the builder sent nothing in ${String(EXPORT_IDLE_MS / 1000)} s`));
|
|
49191
|
-
};
|
|
49192
|
-
const idle = { cancel: startTimer(stopIdle, EXPORT_IDLE_MS) };
|
|
49193
|
-
const stderrText = new Response(tar.stderr).text();
|
|
49194
|
-
try {
|
|
49195
|
-
const exported = await exec(["docker", "export", containerId], {
|
|
49196
|
-
signal: AbortSignal.any([signal, stopExport.signal]),
|
|
49110
|
+
await writeStreamedTree({
|
|
49111
|
+
root: options.root,
|
|
49112
|
+
limits: options.limits,
|
|
49113
|
+
signal,
|
|
49114
|
+
source: "docker export in the builder",
|
|
49115
|
+
grow: options.grow,
|
|
49116
|
+
startTimer: options.startTimer,
|
|
49117
|
+
startTar: options.startTar,
|
|
49118
|
+
countTreeBytes: options.countTreeBytes,
|
|
49119
|
+
runExport: (onStdout, exportSignal) => exec(["docker", "export", containerId], {
|
|
49120
|
+
signal: exportSignal,
|
|
49197
49121
|
onStdout: async (chunk) => {
|
|
49198
|
-
|
|
49199
|
-
idle.cancel = startTimer(stopIdle, EXPORT_IDLE_MS);
|
|
49200
|
-
sent.bytes += chunk.byteLength;
|
|
49201
|
-
if (sent.bytes > maxBytes) {
|
|
49202
|
-
throw new ImageLimitError("bytes", maxBytes);
|
|
49203
|
-
}
|
|
49204
|
-
assertWithinLimits();
|
|
49205
|
-
await growHold();
|
|
49122
|
+
await onStdout(chunk);
|
|
49206
49123
|
hash.update(chunk);
|
|
49207
|
-
await tar.stdin.write(chunk);
|
|
49208
|
-
await tar.stdin.flush();
|
|
49209
49124
|
}
|
|
49210
|
-
})
|
|
49211
|
-
|
|
49212
|
-
throw new Error(`docker export in the builder: ${exported.stderr.trim()}`);
|
|
49213
|
-
}
|
|
49214
|
-
unpacked.ended = true;
|
|
49215
|
-
await tar.stdin.end();
|
|
49216
|
-
const [exitCode, stderr] = await Promise.all([tar.exited, stderrText, counting]);
|
|
49217
|
-
assertWithinLimits();
|
|
49218
|
-
assertUnpacked({ exitCode, stdout: "", stderr });
|
|
49219
|
-
await growHold();
|
|
49220
|
-
} catch (error) {
|
|
49221
|
-
tar.kill();
|
|
49222
|
-
assertWithinLimits();
|
|
49223
|
-
if (stopExport.signal.reason === TAR_STOPPED) {
|
|
49224
|
-
const [exitCode, stderr] = await Promise.all([tar.exited, stderrText]);
|
|
49225
|
-
assertUnpacked({ exitCode, stdout: "", stderr });
|
|
49226
|
-
}
|
|
49227
|
-
throw error;
|
|
49228
|
-
} finally {
|
|
49229
|
-
idle.cancel();
|
|
49230
|
-
}
|
|
49125
|
+
})
|
|
49126
|
+
});
|
|
49231
49127
|
return { digest: `${DIGEST_PREFIX}${hash.digest("hex")}`, config };
|
|
49232
49128
|
}
|
|
49129
|
+
function createImageHash(configText) {
|
|
49130
|
+
const config = new TextEncoder().encode(configText);
|
|
49131
|
+
const length = new Uint8Array(8);
|
|
49132
|
+
new DataView(length.buffer).setBigUint64(0, BigInt(config.byteLength));
|
|
49133
|
+
const hash = new Bun.CryptoHasher("sha256");
|
|
49134
|
+
hash.update(DIGEST_DOMAIN);
|
|
49135
|
+
hash.update(length);
|
|
49136
|
+
hash.update(config);
|
|
49137
|
+
return hash;
|
|
49138
|
+
}
|
|
49233
49139
|
|
|
49234
49140
|
// packages/daemon/src/imps/images/build/write-context-tar.ts
|
|
49235
49141
|
import { open as open9 } from "fs/promises";
|
|
@@ -49397,7 +49303,7 @@ function createImageBuilds(deps, writer) {
|
|
|
49397
49303
|
});
|
|
49398
49304
|
}
|
|
49399
49305
|
const uploadsDir = buildUploadsDir(deps.config.dataDir);
|
|
49400
|
-
const tarPath =
|
|
49306
|
+
const tarPath = join28(uploadsDir, `${Bun.randomUUIDv7()}.tar`);
|
|
49401
49307
|
mkdirSync20(uploadsDir, { recursive: true, mode: 448 });
|
|
49402
49308
|
return deps.diskBudget.withRoom(tarBytes, async () => {
|
|
49403
49309
|
try {
|
|
@@ -49516,96 +49422,33 @@ function buildImageRuntimeConfig(ociConfig) {
|
|
|
49516
49422
|
|
|
49517
49423
|
// packages/daemon/src/imps/images/rootfs/create-image-writer.ts
|
|
49518
49424
|
import { existsSync as existsSync19, mkdirSync as mkdirSync22, rmSync as rmSync26, statSync as statSync13, writeFileSync as writeFileSync8 } from "fs";
|
|
49519
|
-
import { join as
|
|
49520
|
-
import * as z89 from "zod";
|
|
49521
|
-
|
|
49522
|
-
// packages/daemon/src/imps/docker-contract/refusal.ts
|
|
49523
|
-
import { z as z88 } from "zod";
|
|
49524
|
-
var PROXY_PREFIX = "imp-docker-proxy: ";
|
|
49525
|
-
var ENGINE_FAILURE_PREFIX = `${PROXY_PREFIX}the engine call failed: `;
|
|
49526
|
-
var REFUSAL_MAX_CHARS = 1000;
|
|
49527
|
-
var DAEMON_ERROR_PREFIX = "Error response from daemon: ";
|
|
49528
|
-
var UNABLE_TO_FIND_LINE = /^Unable to find image '[^'\n]+' locally$/u;
|
|
49529
|
-
var RefusalBodySchema = z88.strictObject({ message: z88.string() });
|
|
49530
|
-
function readProxyRefusal(text) {
|
|
49531
|
-
const body = readJsonMessage(text.trim());
|
|
49532
|
-
if (body !== null) {
|
|
49533
|
-
return toRefusal(body);
|
|
49534
|
-
}
|
|
49535
|
-
const errorLine = readErrorLine(text.trimEnd().split(`
|
|
49536
|
-
`));
|
|
49537
|
-
if (!errorLine.startsWith(DAEMON_ERROR_PREFIX)) {
|
|
49538
|
-
return null;
|
|
49539
|
-
}
|
|
49540
|
-
const answer = errorLine.slice(DAEMON_ERROR_PREFIX.length);
|
|
49541
|
-
return toRefusal(readJsonMessage(answer) ?? answer);
|
|
49542
|
-
}
|
|
49543
|
-
function readErrorLine(lines) {
|
|
49544
|
-
const [first = "", second = ""] = lines;
|
|
49545
|
-
if (lines.length === 1) {
|
|
49546
|
-
return first;
|
|
49547
|
-
}
|
|
49548
|
-
return lines.length === 2 && UNABLE_TO_FIND_LINE.test(first) ? second : "";
|
|
49549
|
-
}
|
|
49550
|
-
function toRefusal(message) {
|
|
49551
|
-
if (!message.startsWith(PROXY_PREFIX) || message.startsWith(ENGINE_FAILURE_PREFIX)) {
|
|
49552
|
-
return null;
|
|
49553
|
-
}
|
|
49554
|
-
const firstLine = message.split(`
|
|
49555
|
-
`, 1)[0] ?? "";
|
|
49556
|
-
return firstLine.trim().slice(0, REFUSAL_MAX_CHARS);
|
|
49557
|
-
}
|
|
49558
|
-
function readJsonMessage(text) {
|
|
49559
|
-
if (!text.startsWith("{")) {
|
|
49560
|
-
return null;
|
|
49561
|
-
}
|
|
49562
|
-
try {
|
|
49563
|
-
const body = RefusalBodySchema.safeParse(JSON.parse(text));
|
|
49564
|
-
return body.success ? body.data.message : null;
|
|
49565
|
-
} catch {
|
|
49566
|
-
return null;
|
|
49567
|
-
}
|
|
49568
|
-
}
|
|
49425
|
+
import { join as join30 } from "path";
|
|
49569
49426
|
|
|
49570
|
-
// packages/daemon/src/imps/images/rootfs/
|
|
49571
|
-
|
|
49572
|
-
|
|
49573
|
-
|
|
49574
|
-
|
|
49575
|
-
|
|
49576
|
-
|
|
49577
|
-
|
|
49578
|
-
|
|
49579
|
-
|
|
49580
|
-
}
|
|
49581
|
-
}
|
|
49582
|
-
return result;
|
|
49583
|
-
}
|
|
49584
|
-
function readRefusalError(call, output) {
|
|
49585
|
-
const refusal = readProxyRefusal(output);
|
|
49586
|
-
if (refusal === null) {
|
|
49587
|
-
return null;
|
|
49588
|
-
}
|
|
49589
|
-
getImpdLogger("image").warning("{call} refused: {refusal}", { call, refusal });
|
|
49590
|
-
return new ImpdError("BAD_REQUEST", { message: refusal });
|
|
49591
|
-
}
|
|
49592
|
-
async function runDockerChecked(argv, options = {}) {
|
|
49593
|
-
const result = await runDocker(argv, options);
|
|
49427
|
+
// packages/daemon/src/imps/images/rootfs/read-oci-config.ts
|
|
49428
|
+
import * as z88 from "zod";
|
|
49429
|
+
var OciConfigSchema3 = z88.object({
|
|
49430
|
+
digest: z88.string().regex(/^sha256:[a-f0-9]{64}$/v),
|
|
49431
|
+
config: z88.record(z88.string(), z88.unknown()).nullable()
|
|
49432
|
+
});
|
|
49433
|
+
async function readOciConfig(ref, signal, run) {
|
|
49434
|
+
const argv = ["oci-fetch", "config", ref];
|
|
49435
|
+
const result = await run(argv, { signal });
|
|
49436
|
+
signal.throwIfAborted();
|
|
49594
49437
|
if (result.exitCode !== 0) {
|
|
49595
|
-
throw buildExitError(
|
|
49438
|
+
throw buildExitError(argv, result);
|
|
49596
49439
|
}
|
|
49597
|
-
return result.stdout;
|
|
49440
|
+
return OciConfigSchema3.parse(JSON.parse(result.stdout));
|
|
49598
49441
|
}
|
|
49599
49442
|
|
|
49600
49443
|
// packages/daemon/src/imps/images/rootfs/write-image-config.ts
|
|
49601
49444
|
import { closeSync as closeSync9, constants as constants5, lstatSync as lstatSync5, mkdirSync as mkdirSync21, openSync as openSync7, rmSync as rmSync25, writeSync as writeSync3 } from "fs";
|
|
49602
|
-
import { join as
|
|
49445
|
+
import { join as join29 } from "path";
|
|
49603
49446
|
function writeImageConfig(root, contents) {
|
|
49604
|
-
const etc =
|
|
49605
|
-
const directory =
|
|
49447
|
+
const etc = join29(root, "etc");
|
|
49448
|
+
const directory = join29(etc, "imp");
|
|
49606
49449
|
createDirectory(etc, "/etc");
|
|
49607
49450
|
createDirectory(directory, "/etc/imp");
|
|
49608
|
-
const file =
|
|
49451
|
+
const file = join29(directory, "image.json");
|
|
49609
49452
|
rmSync25(file, { recursive: true, force: true });
|
|
49610
49453
|
const fd = openSync7(file, constants5.O_WRONLY | constants5.O_CREAT | constants5.O_EXCL | constants5.O_NOFOLLOW, 420);
|
|
49611
49454
|
try {
|
|
@@ -49627,14 +49470,22 @@ function createDirectory(path, name) {
|
|
|
49627
49470
|
}
|
|
49628
49471
|
}
|
|
49629
49472
|
|
|
49473
|
+
// packages/daemon/src/imps/images/rootfs/write-oci-tree.ts
|
|
49474
|
+
function writeOciTree(options) {
|
|
49475
|
+
return writeStreamedTree({
|
|
49476
|
+
root: options.root,
|
|
49477
|
+
limits: options.limits,
|
|
49478
|
+
signal: options.signal,
|
|
49479
|
+
source: `oci-fetch export ${options.ref}`,
|
|
49480
|
+
grow: options.grow,
|
|
49481
|
+
startTimer: options.startTimer,
|
|
49482
|
+
startTar: options.startTar,
|
|
49483
|
+
countTreeBytes: options.countTreeBytes,
|
|
49484
|
+
runExport: (onStdout, signal) => options.runStreamed(["oci-fetch", "export", options.ref], onStdout, signal)
|
|
49485
|
+
});
|
|
49486
|
+
}
|
|
49487
|
+
|
|
49630
49488
|
// packages/daemon/src/imps/images/rootfs/create-image-writer.ts
|
|
49631
|
-
var InspectSchema = z89.tuple([
|
|
49632
|
-
z89.object({
|
|
49633
|
-
Id: z89.string(),
|
|
49634
|
-
Config: z89.unknown(),
|
|
49635
|
-
Size: z89.number().optional()
|
|
49636
|
-
})
|
|
49637
|
-
]);
|
|
49638
49489
|
function createImageWriter(deps) {
|
|
49639
49490
|
const storageGate = deps.storageGate;
|
|
49640
49491
|
const logger = getImpdLogger("image");
|
|
@@ -49642,22 +49493,10 @@ function createImageWriter(deps) {
|
|
|
49642
49493
|
const publishers = new Map;
|
|
49643
49494
|
const freshRootfs = new Set;
|
|
49644
49495
|
const guestRootfsLock = buildKeyedMutex();
|
|
49645
|
-
const docker = { env: deps.dockerEnv };
|
|
49646
|
-
const readInspect = async (ref, signal) => {
|
|
49647
|
-
const options = { ...docker, signal };
|
|
49648
|
-
const first = await runDocker(["image", "inspect", ref], options);
|
|
49649
|
-
if (first.exitCode === 0) {
|
|
49650
|
-
return InspectSchema.parse(JSON.parse(first.stdout))[0];
|
|
49651
|
-
}
|
|
49652
|
-
await runDockerChecked(["pull", "--quiet", ref], options);
|
|
49653
|
-
signal.throwIfAborted();
|
|
49654
|
-
const stdout = await runDockerChecked(["image", "inspect", ref], options);
|
|
49655
|
-
return InspectSchema.parse(JSON.parse(stdout))[0];
|
|
49656
|
-
};
|
|
49657
49496
|
const makeWorkDir = () => {
|
|
49658
49497
|
const images = buildImagesDir(deps.config.dataDir);
|
|
49659
|
-
const work =
|
|
49660
|
-
const root =
|
|
49498
|
+
const work = join30(images, `.build-${Bun.randomUUIDv7()}`);
|
|
49499
|
+
const root = join30(work, "root");
|
|
49661
49500
|
mkdirSync22(images, { recursive: true });
|
|
49662
49501
|
mkdirSync22(work, { mode: 448 });
|
|
49663
49502
|
mkdirSync22(root, { mode: 493 });
|
|
@@ -49668,7 +49507,7 @@ function createImageWriter(deps) {
|
|
|
49668
49507
|
const usage = await readTreeUsage(root);
|
|
49669
49508
|
const plan = planRootfs(usage);
|
|
49670
49509
|
await deps.storage.createImage(digest, async (directory) => {
|
|
49671
|
-
const image =
|
|
49510
|
+
const image = join30(directory, "rootfs.ext4");
|
|
49672
49511
|
await runChecked(["truncate", "-s", String(plan.bytes), image]);
|
|
49673
49512
|
await runChecked([
|
|
49674
49513
|
"mkfs.ext4",
|
|
@@ -49681,29 +49520,10 @@ function createImageWriter(deps) {
|
|
|
49681
49520
|
root,
|
|
49682
49521
|
image
|
|
49683
49522
|
]);
|
|
49684
|
-
writeFileSync8(
|
|
49523
|
+
writeFileSync8(join30(directory, "config.json"), JSON.stringify(ociConfig ?? {}, null, 2));
|
|
49685
49524
|
});
|
|
49686
49525
|
return readDiskUsage(buildImagePaths(deps.config.dataDir, digest).rootfs);
|
|
49687
49526
|
};
|
|
49688
|
-
const createRootfs = async (ref, digest, ociConfig) => {
|
|
49689
|
-
const paths = buildImagePaths(deps.config.dataDir, digest);
|
|
49690
|
-
if (existsSync19(paths.rootfs)) {
|
|
49691
|
-
return readDiskUsage(paths.rootfs);
|
|
49692
|
-
}
|
|
49693
|
-
const created = await runDockerChecked(["create", ref, "/bin/true"], docker);
|
|
49694
|
-
const containerId = created.trim();
|
|
49695
|
-
try {
|
|
49696
|
-
const workDir = makeWorkDir();
|
|
49697
|
-
try {
|
|
49698
|
-
await writeExportedTree(containerId, workDir.root);
|
|
49699
|
-
return await writeRootfs(workDir.root, digest, ociConfig);
|
|
49700
|
-
} finally {
|
|
49701
|
-
rmSync26(workDir.work, { recursive: true, force: true });
|
|
49702
|
-
}
|
|
49703
|
-
} finally {
|
|
49704
|
-
await runCommand(["docker", "rm", "-f", containerId]);
|
|
49705
|
-
}
|
|
49706
|
-
};
|
|
49707
49527
|
const createRootfsOnce = (digest, create) => building.run(digest, create);
|
|
49708
49528
|
const writeDockerImageRow = async (imageName, ref, digest, sizeBytes) => {
|
|
49709
49529
|
const at = deps.clock.now();
|
|
@@ -49757,22 +49577,6 @@ function createImageWriter(deps) {
|
|
|
49757
49577
|
});
|
|
49758
49578
|
}
|
|
49759
49579
|
});
|
|
49760
|
-
const createImageOnHost = async (ref, imageName, options) => {
|
|
49761
|
-
const inspect = await readInspect(ref, options.signal);
|
|
49762
|
-
options.onPulled();
|
|
49763
|
-
const treeBytes = inspect.Size ?? 0;
|
|
49764
|
-
const buildBytes = 2 * treeBytes + planRootfsOverhead(treeBytes);
|
|
49765
|
-
const withRoom = deps.diskBudget.withRoom;
|
|
49766
|
-
return withRoom(buildBytes, () => storageGate.join(async () => {
|
|
49767
|
-
await holdPublisher(inspect.Id);
|
|
49768
|
-
try {
|
|
49769
|
-
const sizeBytes = await createRootfsOnce(inspect.Id, () => createRootfs(ref, inspect.Id, inspect.Config));
|
|
49770
|
-
return await writeDockerImageRow(imageName, ref, inspect.Id, sizeBytes);
|
|
49771
|
-
} finally {
|
|
49772
|
-
await releasePublisher(inspect.Id, imageName);
|
|
49773
|
-
}
|
|
49774
|
-
}));
|
|
49775
|
-
};
|
|
49776
49580
|
const writeGuestRootfsAndRow = async (guest) => {
|
|
49777
49581
|
const digest = guest.digest;
|
|
49778
49582
|
const rootfs = buildImagePaths(deps.config.dataDir, digest).rootfs;
|
|
@@ -49803,7 +49607,10 @@ function createImageWriter(deps) {
|
|
|
49803
49607
|
root,
|
|
49804
49608
|
limits: { maxBytes, maxFiles: deps.config.build.imageMaxFiles },
|
|
49805
49609
|
signal,
|
|
49806
|
-
grow
|
|
49610
|
+
grow,
|
|
49611
|
+
startTar: deps.startTar,
|
|
49612
|
+
countTreeBytes: deps.countTreeBytes,
|
|
49613
|
+
startTimer: deps.startTimer
|
|
49807
49614
|
});
|
|
49808
49615
|
return await writeGuestRootfsAndRow({
|
|
49809
49616
|
root,
|
|
@@ -49818,6 +49625,41 @@ function createImageWriter(deps) {
|
|
|
49818
49625
|
}
|
|
49819
49626
|
}));
|
|
49820
49627
|
};
|
|
49628
|
+
const createBuilderImage = async (imageName, options) => {
|
|
49629
|
+
const ref = deps.config.build.image;
|
|
49630
|
+
const signal = options.signal;
|
|
49631
|
+
const build = deps.config.build;
|
|
49632
|
+
const limits = { maxBytes: build.imageMaxBytes, maxFiles: build.imageMaxFiles };
|
|
49633
|
+
const image = await readOciConfig(ref, signal, deps.runCommand);
|
|
49634
|
+
return deps.diskBudget.withGrowingRoom((grow) => storageGate.join(async () => {
|
|
49635
|
+
const workDir = makeWorkDir();
|
|
49636
|
+
const root = workDir.root;
|
|
49637
|
+
try {
|
|
49638
|
+
await writeOciTree({
|
|
49639
|
+
ref,
|
|
49640
|
+
root,
|
|
49641
|
+
limits,
|
|
49642
|
+
signal,
|
|
49643
|
+
grow,
|
|
49644
|
+
runStreamed: deps.runStreamed,
|
|
49645
|
+
startTar: deps.startTar,
|
|
49646
|
+
countTreeBytes: deps.countTreeBytes,
|
|
49647
|
+
startTimer: deps.startTimer
|
|
49648
|
+
});
|
|
49649
|
+
options.onFetched();
|
|
49650
|
+
return await writeGuestRootfsAndRow({
|
|
49651
|
+
root,
|
|
49652
|
+
digest: image.digest,
|
|
49653
|
+
config: image.config,
|
|
49654
|
+
imageName,
|
|
49655
|
+
ref,
|
|
49656
|
+
signal: new AbortController().signal
|
|
49657
|
+
});
|
|
49658
|
+
} finally {
|
|
49659
|
+
rmSync26(workDir.work, { recursive: true, force: true });
|
|
49660
|
+
}
|
|
49661
|
+
}));
|
|
49662
|
+
};
|
|
49821
49663
|
const withPublisher = async (digest, imageName, publish) => {
|
|
49822
49664
|
await holdPublisher(digest);
|
|
49823
49665
|
try {
|
|
@@ -49826,7 +49668,7 @@ function createImageWriter(deps) {
|
|
|
49826
49668
|
await releasePublisher(digest, imageName);
|
|
49827
49669
|
}
|
|
49828
49670
|
};
|
|
49829
|
-
return {
|
|
49671
|
+
return { createBuilderImage, writeGuestImage, removeIdleRootfs, removeIdleImage, withPublisher };
|
|
49830
49672
|
}
|
|
49831
49673
|
function requireDockerImage(existing) {
|
|
49832
49674
|
if (existing?.source === "imp") {
|
|
@@ -49900,18 +49742,18 @@ function createImageAdds(deps, writer) {
|
|
|
49900
49742
|
const createBuilderImage = async (ref) => {
|
|
49901
49743
|
const pullMs = deps.builderImagePullMs ?? BUILDER_IMAGE_PULL_MS;
|
|
49902
49744
|
const timeout = deps.startPullLimit?.(pullMs) ?? AbortSignal.timeout(pullMs);
|
|
49903
|
-
const step = {
|
|
49745
|
+
const step = { fetched: false };
|
|
49904
49746
|
try {
|
|
49905
|
-
return await writer.
|
|
49747
|
+
return await writer.createBuilderImage(BUILDER_IMAGE, {
|
|
49906
49748
|
signal: timeout,
|
|
49907
|
-
|
|
49908
|
-
step.
|
|
49749
|
+
onFetched: () => {
|
|
49750
|
+
step.fetched = true;
|
|
49909
49751
|
}
|
|
49910
49752
|
});
|
|
49911
49753
|
} catch (error) {
|
|
49912
|
-
const reason = timeout.aborted && !step.
|
|
49754
|
+
const reason = timeout.aborted && !step.fetched ? `the fetch did not finish in ${formatPullLimit(pullMs)}` : getErrorMessage(error);
|
|
49913
49755
|
throw new ImpdError("SERVICE_UNAVAILABLE", {
|
|
49914
|
-
message: `impd cannot add its builder image ${ref} (IMP_BUILD_IMAGE) from
|
|
49756
|
+
message: `impd cannot add its builder image ${ref} (IMP_BUILD_IMAGE) from its registry, so no image add or build can run: ${reason}`
|
|
49915
49757
|
});
|
|
49916
49758
|
}
|
|
49917
49759
|
};
|
|
@@ -50103,7 +49945,10 @@ function createImageEviction(deps) {
|
|
|
50103
49945
|
// packages/daemon/src/imps/images/image-service.ts
|
|
50104
49946
|
function createImageService(deps) {
|
|
50105
49947
|
const clock = deps.clock ?? REAL_CLOCK;
|
|
50106
|
-
const writer = createImageWriter({
|
|
49948
|
+
const writer = createImageWriter({
|
|
49949
|
+
...deps,
|
|
49950
|
+
clock
|
|
49951
|
+
});
|
|
50107
49952
|
const builds = createImageBuilds({ ...deps, clock }, writer);
|
|
50108
49953
|
const adds = createImageAdds({ ...deps, clock }, writer);
|
|
50109
49954
|
const eviction = createImageEviction({
|
|
@@ -50190,6 +50035,91 @@ function createImageService(deps) {
|
|
|
50190
50035
|
};
|
|
50191
50036
|
}
|
|
50192
50037
|
|
|
50038
|
+
// packages/daemon/src/imps/images/rootfs/start-tar-process.ts
|
|
50039
|
+
function startTarProcess(argv) {
|
|
50040
|
+
return Bun.spawn([...argv], {
|
|
50041
|
+
stdin: "pipe",
|
|
50042
|
+
stdout: "pipe",
|
|
50043
|
+
stderr: "pipe",
|
|
50044
|
+
env: { ...process.env, LC_ALL: "C" }
|
|
50045
|
+
});
|
|
50046
|
+
}
|
|
50047
|
+
|
|
50048
|
+
// packages/daemon/src/platform/process/run-streamed.ts
|
|
50049
|
+
async function runStreamed(argv, onStdout, signal) {
|
|
50050
|
+
const child = Bun.spawn([...argv], {
|
|
50051
|
+
stdin: "ignore",
|
|
50052
|
+
stdout: "pipe",
|
|
50053
|
+
stderr: "pipe",
|
|
50054
|
+
env: process.env,
|
|
50055
|
+
signal
|
|
50056
|
+
});
|
|
50057
|
+
const stderr = new Response(child.stderr).text();
|
|
50058
|
+
try {
|
|
50059
|
+
for await (const chunk of child.stdout) {
|
|
50060
|
+
await onStdout(chunk);
|
|
50061
|
+
}
|
|
50062
|
+
} catch (error) {
|
|
50063
|
+
child.kill();
|
|
50064
|
+
await child.exited;
|
|
50065
|
+
throw error;
|
|
50066
|
+
}
|
|
50067
|
+
const exitCode = await child.exited;
|
|
50068
|
+
signal.throwIfAborted();
|
|
50069
|
+
return { exitCode, stdout: "", stderr: await stderr };
|
|
50070
|
+
}
|
|
50071
|
+
|
|
50072
|
+
// packages/daemon/src/vm/storage/count-tree-bytes.ts
|
|
50073
|
+
import { lstat as lstat3, readdir as readdir3 } from "fs/promises";
|
|
50074
|
+
import { join as join31 } from "path";
|
|
50075
|
+
async function countTreeBytes(root, deps = {}) {
|
|
50076
|
+
const readDir = deps.readDir ?? readDirEntries;
|
|
50077
|
+
const inodes = new Map;
|
|
50078
|
+
const dirs = [root];
|
|
50079
|
+
for (let directory = dirs.pop();directory !== undefined; directory = dirs.pop()) {
|
|
50080
|
+
const entries = await readEntries2(readDir, directory);
|
|
50081
|
+
for (const entry of entries) {
|
|
50082
|
+
if (entry.isDirectory()) {
|
|
50083
|
+
dirs.push(join31(directory, entry.name));
|
|
50084
|
+
}
|
|
50085
|
+
}
|
|
50086
|
+
const files = entries.filter((entry) => entry.isFile());
|
|
50087
|
+
const stats = await Promise.all(files.map((entry) => readFileStats(join31(directory, entry.name))));
|
|
50088
|
+
for (const stat of stats) {
|
|
50089
|
+
if (stat !== undefined) {
|
|
50090
|
+
inodes.set(`${String(stat.dev)}:${String(stat.ino)}`, Math.max(0, stat.blocks - 1) * 512);
|
|
50091
|
+
}
|
|
50092
|
+
}
|
|
50093
|
+
}
|
|
50094
|
+
return [...inodes.values()].reduce((sum, bytes) => sum + bytes, 0);
|
|
50095
|
+
}
|
|
50096
|
+
function readDirEntries(directory) {
|
|
50097
|
+
return readdir3(directory, { withFileTypes: true });
|
|
50098
|
+
}
|
|
50099
|
+
async function readEntries2(readDir, directory) {
|
|
50100
|
+
try {
|
|
50101
|
+
return await readDir(directory);
|
|
50102
|
+
} catch (error) {
|
|
50103
|
+
if (isMissing(error)) {
|
|
50104
|
+
return [];
|
|
50105
|
+
}
|
|
50106
|
+
throw error;
|
|
50107
|
+
}
|
|
50108
|
+
}
|
|
50109
|
+
function isMissing(error) {
|
|
50110
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
50111
|
+
}
|
|
50112
|
+
async function readFileStats(path) {
|
|
50113
|
+
try {
|
|
50114
|
+
return await lstat3(path);
|
|
50115
|
+
} catch (error) {
|
|
50116
|
+
if (isMissing(error)) {
|
|
50117
|
+
return;
|
|
50118
|
+
}
|
|
50119
|
+
throw error;
|
|
50120
|
+
}
|
|
50121
|
+
}
|
|
50122
|
+
|
|
50193
50123
|
// packages/daemon/src/vm/storage/disk-usage-cache.ts
|
|
50194
50124
|
var REFRESH_DELAY_MS = 1e4;
|
|
50195
50125
|
var CHANGES_USAGE = new Set([
|
|
@@ -50301,6 +50231,11 @@ function createImpdStorage(config, deps) {
|
|
|
50301
50231
|
storageGate,
|
|
50302
50232
|
diskBudget,
|
|
50303
50233
|
readBuilders: () => buildersHolder.builders,
|
|
50234
|
+
runCommand,
|
|
50235
|
+
runStreamed,
|
|
50236
|
+
startTar: startTarProcess,
|
|
50237
|
+
countTreeBytes,
|
|
50238
|
+
startTimer: startRealTimer,
|
|
50304
50239
|
...deps.now !== undefined && { clock: { now: deps.now } },
|
|
50305
50240
|
...deps.images
|
|
50306
50241
|
});
|
|
@@ -50317,11 +50252,11 @@ function createImpdStorage(config, deps) {
|
|
|
50317
50252
|
}
|
|
50318
50253
|
|
|
50319
50254
|
// packages/daemon/src/host-net/net/tailscale-status.ts
|
|
50320
|
-
import * as
|
|
50321
|
-
var IpListSchema =
|
|
50322
|
-
var StatusSchema =
|
|
50323
|
-
BackendState:
|
|
50324
|
-
Self:
|
|
50255
|
+
import * as z89 from "zod";
|
|
50256
|
+
var IpListSchema = z89.array(z89.string()).nullable().optional();
|
|
50257
|
+
var StatusSchema = z89.object({
|
|
50258
|
+
BackendState: z89.string(),
|
|
50259
|
+
Self: z89.object({ HostName: z89.string(), DNSName: z89.string().optional(), TailscaleIPs: IpListSchema }).optional()
|
|
50325
50260
|
});
|
|
50326
50261
|
var UNKNOWN = { state: null, hostname: null, dnsName: null, ip: null, ips: [] };
|
|
50327
50262
|
var STATUS_TTL_MS = 30000;
|
|
@@ -50599,18 +50534,18 @@ import { getOAuthProviderApi } from "@better-auth/oauth-provider";
|
|
|
50599
50534
|
import { createAuthEndpoint } from "better-auth/api";
|
|
50600
50535
|
import { setSessionCookie } from "better-auth/cookies";
|
|
50601
50536
|
import { generateRandomString } from "better-auth/crypto";
|
|
50602
|
-
import * as
|
|
50537
|
+
import * as z90 from "zod";
|
|
50603
50538
|
var CLIENT_SCOPES = [...ScopeSchema.options, "offline_access"];
|
|
50604
50539
|
var APPROVAL_EMAIL_DOMAIN = "approval.imp.invalid";
|
|
50605
|
-
var SignInBodySchema =
|
|
50606
|
-
oauth_query:
|
|
50607
|
-
tokenID:
|
|
50608
|
-
tokenName:
|
|
50609
|
-
imps:
|
|
50540
|
+
var SignInBodySchema = z90.object({
|
|
50541
|
+
oauth_query: z90.string(),
|
|
50542
|
+
tokenID: z90.string().min(1),
|
|
50543
|
+
tokenName: z90.string().min(1),
|
|
50544
|
+
imps: z90.array(z90.string()).nullable()
|
|
50610
50545
|
});
|
|
50611
|
-
var FixedClientBodySchema =
|
|
50612
|
-
name:
|
|
50613
|
-
redirectURIs:
|
|
50546
|
+
var FixedClientBodySchema = z90.object({
|
|
50547
|
+
name: z90.string().min(1),
|
|
50548
|
+
redirectURIs: z90.array(z90.string()).min(1)
|
|
50614
50549
|
});
|
|
50615
50550
|
function createApproverPlugin() {
|
|
50616
50551
|
return {
|
|
@@ -50636,7 +50571,7 @@ function createApproverPlugin() {
|
|
|
50636
50571
|
}),
|
|
50637
50572
|
verifyMCPAccessToken: createAuthEndpoint("/imp/verify-access-token", {
|
|
50638
50573
|
method: "POST",
|
|
50639
|
-
body:
|
|
50574
|
+
body: z90.object({ token: z90.string() }),
|
|
50640
50575
|
metadata: { SERVER_ONLY: true }
|
|
50641
50576
|
}, async (ctx) => {
|
|
50642
50577
|
const provider = ctx.context.getPlugin("oauth-provider");
|
|
@@ -50680,7 +50615,7 @@ function createApproverPlugin() {
|
|
|
50680
50615
|
|
|
50681
50616
|
// packages/daemon/src/identity/oauth/create-oauth-handlers.ts
|
|
50682
50617
|
import { verifyOAuthQueryParams } from "@better-auth/oauth-provider";
|
|
50683
|
-
import * as
|
|
50618
|
+
import * as z91 from "zod";
|
|
50684
50619
|
|
|
50685
50620
|
// packages/daemon/src/identity/oauth/build-bad-token-request.ts
|
|
50686
50621
|
function buildBadTokenRequest() {
|
|
@@ -51052,9 +50987,9 @@ var MINUTE_MS2 = 60000;
|
|
|
51052
50987
|
var CONSENT_MS = 10 * MINUTE_MS2;
|
|
51053
50988
|
var MAX_FORM_BYTES = 16 * 1024;
|
|
51054
50989
|
var METADATA_HEADERS = { "cache-control": "no-store" };
|
|
51055
|
-
var PKCEMissSchema =
|
|
51056
|
-
error:
|
|
51057
|
-
error_description:
|
|
50990
|
+
var PKCEMissSchema = z91.object({
|
|
50991
|
+
error: z91.literal("invalid_request"),
|
|
50992
|
+
error_description: z91.literal("code verification failed")
|
|
51058
50993
|
});
|
|
51059
50994
|
function createOAuthHandlers(deps) {
|
|
51060
50995
|
const host = new URL(deps.origin).host;
|
|
@@ -51746,8 +51681,8 @@ function loadOrCreateHostId(dataDir) {
|
|
|
51746
51681
|
|
|
51747
51682
|
// packages/daemon/src/network/tailnet-names/oauth-credential.ts
|
|
51748
51683
|
import { readFileSync as readFileSync26, statSync as statSync14 } from "fs";
|
|
51749
|
-
import * as
|
|
51750
|
-
var CredentialSchema =
|
|
51684
|
+
import * as z92 from "zod";
|
|
51685
|
+
var CredentialSchema = z92.object({ clientId: z92.string().min(1), clientSecret: z92.string().min(1) });
|
|
51751
51686
|
function readOAuthCredential(path) {
|
|
51752
51687
|
const mode = statSync14(path).mode & 511;
|
|
51753
51688
|
if ((mode & 63) !== 0) {
|
|
@@ -51768,17 +51703,17 @@ function readJson(path) {
|
|
|
51768
51703
|
}
|
|
51769
51704
|
|
|
51770
51705
|
// packages/daemon/src/host-net/net/tailscale-serve.ts
|
|
51771
|
-
import * as
|
|
51772
|
-
var HandlerSchema =
|
|
51773
|
-
var WebSchema =
|
|
51774
|
-
Handlers:
|
|
51706
|
+
import * as z93 from "zod";
|
|
51707
|
+
var HandlerSchema = z93.object({ Proxy: z93.string().optional() });
|
|
51708
|
+
var WebSchema = z93.object({
|
|
51709
|
+
Handlers: z93.record(z93.string(), HandlerSchema).nullable().optional()
|
|
51775
51710
|
});
|
|
51776
|
-
var ServiceConfigSchema =
|
|
51777
|
-
Web:
|
|
51711
|
+
var ServiceConfigSchema = z93.object({
|
|
51712
|
+
Web: z93.record(z93.string(), WebSchema).nullable().optional()
|
|
51778
51713
|
});
|
|
51779
|
-
var ServeStatusSchema =
|
|
51780
|
-
TCP:
|
|
51781
|
-
Services:
|
|
51714
|
+
var ServeStatusSchema = z93.object({
|
|
51715
|
+
TCP: z93.record(z93.string(), z93.unknown()).optional(),
|
|
51716
|
+
Services: z93.record(z93.string(), ServiceConfigSchema).nullable().optional()
|
|
51782
51717
|
});
|
|
51783
51718
|
function parseServeStatus(json) {
|
|
51784
51719
|
try {
|
|
@@ -51829,17 +51764,17 @@ function createServiceServe(run = runChecked) {
|
|
|
51829
51764
|
}
|
|
51830
51765
|
|
|
51831
51766
|
// packages/daemon/src/network/tailnet-names/services-api.ts
|
|
51832
|
-
import * as
|
|
51767
|
+
import * as z94 from "zod";
|
|
51833
51768
|
var TAILSCALE_API = "https://api.tailscale.com/api/v2";
|
|
51834
51769
|
var TOKEN_MARGIN_MS = 60000;
|
|
51835
|
-
var ServiceSchema2 =
|
|
51836
|
-
name:
|
|
51837
|
-
comment:
|
|
51838
|
-
ports:
|
|
51839
|
-
tags:
|
|
51770
|
+
var ServiceSchema2 = z94.object({
|
|
51771
|
+
name: z94.string(),
|
|
51772
|
+
comment: z94.string().optional(),
|
|
51773
|
+
ports: z94.array(z94.string()).readonly().optional(),
|
|
51774
|
+
tags: z94.array(z94.string()).readonly().nullable().optional()
|
|
51840
51775
|
}).readonly();
|
|
51841
|
-
var ServiceListSchema2 =
|
|
51842
|
-
var TokenSchema3 =
|
|
51776
|
+
var ServiceListSchema2 = z94.object({ vipServices: z94.array(ServiceSchema2).nullable().optional() });
|
|
51777
|
+
var TokenSchema3 = z94.object({ access_token: z94.string(), expires_in: z94.number().positive() });
|
|
51843
51778
|
|
|
51844
51779
|
class TailscaleApiError extends Error {
|
|
51845
51780
|
status;
|
|
@@ -51928,7 +51863,7 @@ function createServicesApi(options) {
|
|
|
51928
51863
|
}
|
|
51929
51864
|
};
|
|
51930
51865
|
}
|
|
51931
|
-
var ErrorSchema =
|
|
51866
|
+
var ErrorSchema = z94.object({ message: z94.string() });
|
|
51932
51867
|
function readErrorText(text) {
|
|
51933
51868
|
try {
|
|
51934
51869
|
return ErrorSchema.parse(JSON.parse(text)).message;
|
|
@@ -52186,7 +52121,6 @@ async function listPublicImpNames(db) {
|
|
|
52186
52121
|
|
|
52187
52122
|
// packages/daemon/src/network/https/acme/acme-issuer.ts
|
|
52188
52123
|
import { Agent } from "https";
|
|
52189
|
-
import * as acme from "acme-client";
|
|
52190
52124
|
|
|
52191
52125
|
// packages/daemon/src/network/https/acme/renewal-policy.ts
|
|
52192
52126
|
var FIRST_BACKOFF_MS2 = 15 * 60000;
|
|
@@ -52228,10 +52162,12 @@ function findRenewalReason(info, domain, now) {
|
|
|
52228
52162
|
|
|
52229
52163
|
// packages/daemon/src/network/https/acme/acme-issuer.ts
|
|
52230
52164
|
function createAcmeIssuer(options) {
|
|
52231
|
-
|
|
52232
|
-
|
|
52233
|
-
|
|
52234
|
-
|
|
52165
|
+
const loading = { promise: null };
|
|
52166
|
+
const loadAcme = () => {
|
|
52167
|
+
loading.promise ??= readAcmeClient(options.caPem);
|
|
52168
|
+
return loading.promise;
|
|
52169
|
+
};
|
|
52170
|
+
const openClient = async (acme) => {
|
|
52235
52171
|
const stored = options.store.readAccount(options.directoryUrl);
|
|
52236
52172
|
if (stored !== null) {
|
|
52237
52173
|
return new acme.Client({
|
|
@@ -52261,7 +52197,8 @@ function createAcmeIssuer(options) {
|
|
|
52261
52197
|
};
|
|
52262
52198
|
const runOrder = async (domain) => {
|
|
52263
52199
|
await checkDirectory2(options.directoryUrl, options.caPem);
|
|
52264
|
-
const
|
|
52200
|
+
const acme = await loadAcme();
|
|
52201
|
+
const client = await openClient(acme);
|
|
52265
52202
|
const names = listCertificateNames(domain);
|
|
52266
52203
|
const order = await client.createOrder({
|
|
52267
52204
|
identifiers: names.map((name) => ({ type: "dns", value: name }))
|
|
@@ -52310,24 +52247,12 @@ function createAcmeIssuer(options) {
|
|
|
52310
52247
|
}
|
|
52311
52248
|
};
|
|
52312
52249
|
}
|
|
52313
|
-
function
|
|
52314
|
-
const
|
|
52315
|
-
|
|
52316
|
-
|
|
52317
|
-
return { notBefore: info.notBefore, notAfter: info.notAfter, names: [...names] };
|
|
52318
|
-
}
|
|
52319
|
-
async function removeRecords(options, records) {
|
|
52320
|
-
for (const record of records) {
|
|
52321
|
-
try {
|
|
52322
|
-
await options.dns.removeTxt(record);
|
|
52323
|
-
} catch (error) {
|
|
52324
|
-
const reason = getErrorMessage(error);
|
|
52325
|
-
getImpdLogger("https").warning("could not remove the TXT record {fqdn}: {reason}", {
|
|
52326
|
-
fqdn: record.fqdn,
|
|
52327
|
-
reason
|
|
52328
|
-
});
|
|
52329
|
-
}
|
|
52250
|
+
async function readAcmeClient(caPem) {
|
|
52251
|
+
const acme = await import("acme-client");
|
|
52252
|
+
if (caPem !== null) {
|
|
52253
|
+
acme.axios.defaults.httpsAgent = new Agent({ ca: caPem });
|
|
52330
52254
|
}
|
|
52255
|
+
return acme;
|
|
52331
52256
|
}
|
|
52332
52257
|
async function checkDirectory2(directoryUrl, caPem) {
|
|
52333
52258
|
try {
|
|
@@ -52344,6 +52269,19 @@ async function checkDirectory2(directoryUrl, caPem) {
|
|
|
52344
52269
|
});
|
|
52345
52270
|
}
|
|
52346
52271
|
}
|
|
52272
|
+
async function removeRecords(options, records) {
|
|
52273
|
+
for (const record of records) {
|
|
52274
|
+
try {
|
|
52275
|
+
await options.dns.removeTxt(record);
|
|
52276
|
+
} catch (error) {
|
|
52277
|
+
const reason = getErrorMessage(error);
|
|
52278
|
+
getImpdLogger("https").warning("could not remove the TXT record {fqdn}: {reason}", {
|
|
52279
|
+
fqdn: record.fqdn,
|
|
52280
|
+
reason
|
|
52281
|
+
});
|
|
52282
|
+
}
|
|
52283
|
+
}
|
|
52284
|
+
}
|
|
52347
52285
|
function formatAcmeError(error) {
|
|
52348
52286
|
const message = getErrorMessage(error);
|
|
52349
52287
|
if (error instanceof TypeError && message.includes("response.config")) {
|
|
@@ -52355,14 +52293,14 @@ function formatAcmeError(error) {
|
|
|
52355
52293
|
// packages/daemon/src/network/https/acme/cert-store.ts
|
|
52356
52294
|
import { existsSync as existsSync22, mkdirSync as mkdirSync24, readFileSync as readFileSync27, rmSync as rmSync28 } from "fs";
|
|
52357
52295
|
import { join as join35 } from "path";
|
|
52358
|
-
import * as
|
|
52359
|
-
var AttemptStateSchema =
|
|
52360
|
-
failures:
|
|
52361
|
-
lastAttemptAt:
|
|
52362
|
-
lastError:
|
|
52296
|
+
import * as z95 from "zod";
|
|
52297
|
+
var AttemptStateSchema = z95.object({
|
|
52298
|
+
failures: z95.int().nonnegative(),
|
|
52299
|
+
lastAttemptAt: z95.number().nullable(),
|
|
52300
|
+
lastError: z95.string().nullable()
|
|
52363
52301
|
});
|
|
52364
|
-
var AccountSchema =
|
|
52365
|
-
var LegacyAccountSchema =
|
|
52302
|
+
var AccountSchema = z95.object({ directoryUrl: z95.string(), url: z95.string(), keyPem: z95.string() });
|
|
52303
|
+
var LegacyAccountSchema = z95.object({ directoryUrl: z95.string(), url: z95.string() });
|
|
52366
52304
|
var NO_ATTEMPTS = { failures: 0, lastAttemptAt: null, lastError: null };
|
|
52367
52305
|
function createCertStore(dataDir) {
|
|
52368
52306
|
const directory = join35(dataDir, "tls");
|
|
@@ -52500,7 +52438,7 @@ function createChalltestsrvProvider(apiUrl) {
|
|
|
52500
52438
|
}
|
|
52501
52439
|
|
|
52502
52440
|
// packages/daemon/src/network/https/dns/cloudflare-provider.ts
|
|
52503
|
-
import * as
|
|
52441
|
+
import * as z96 from "zod";
|
|
52504
52442
|
|
|
52505
52443
|
// packages/daemon/src/network/https/dns/wait-for-txt.ts
|
|
52506
52444
|
import { Resolver as Resolver2 } from "dns/promises";
|
|
@@ -52562,30 +52500,30 @@ var CLOUDFLARE_API = "https://api.cloudflare.com/client/v4";
|
|
|
52562
52500
|
var TXT_TTL_S = 60;
|
|
52563
52501
|
var A_TTL_S = 300;
|
|
52564
52502
|
var PAGE_SIZE = 500;
|
|
52565
|
-
var ApiErrorSchema =
|
|
52566
|
-
var EnvelopeSchema =
|
|
52567
|
-
success:
|
|
52568
|
-
errors:
|
|
52569
|
-
result:
|
|
52570
|
-
result_info:
|
|
52503
|
+
var ApiErrorSchema = z96.object({ code: z96.number().optional(), message: z96.string() });
|
|
52504
|
+
var EnvelopeSchema = z96.object({
|
|
52505
|
+
success: z96.boolean(),
|
|
52506
|
+
errors: z96.array(ApiErrorSchema).default([]),
|
|
52507
|
+
result: z96.unknown().optional(),
|
|
52508
|
+
result_info: z96.object({ page: z96.number(), total_pages: z96.number() }).optional()
|
|
52571
52509
|
});
|
|
52572
|
-
var ZoneSchema =
|
|
52573
|
-
id:
|
|
52574
|
-
name:
|
|
52575
|
-
name_servers:
|
|
52510
|
+
var ZoneSchema = z96.object({
|
|
52511
|
+
id: z96.string(),
|
|
52512
|
+
name: z96.string(),
|
|
52513
|
+
name_servers: z96.array(z96.string())
|
|
52576
52514
|
});
|
|
52577
|
-
var RecordSchema =
|
|
52578
|
-
id:
|
|
52579
|
-
name:
|
|
52580
|
-
type:
|
|
52581
|
-
content:
|
|
52582
|
-
proxied:
|
|
52583
|
-
comment:
|
|
52515
|
+
var RecordSchema = z96.object({
|
|
52516
|
+
id: z96.string(),
|
|
52517
|
+
name: z96.string().optional(),
|
|
52518
|
+
type: z96.string().optional(),
|
|
52519
|
+
content: z96.string(),
|
|
52520
|
+
proxied: z96.boolean().optional(),
|
|
52521
|
+
comment: z96.string().nullable().optional()
|
|
52584
52522
|
});
|
|
52585
52523
|
var MANAGED_COMMENT = "managed by impd";
|
|
52586
52524
|
var ADDRESS_TYPES = new Set(["A", "AAAA", "CNAME"]);
|
|
52587
|
-
var ZoneListSchema =
|
|
52588
|
-
var RecordListSchema =
|
|
52525
|
+
var ZoneListSchema = z96.array(ZoneSchema);
|
|
52526
|
+
var RecordListSchema = z96.array(RecordSchema);
|
|
52589
52527
|
function createCloudflareProvider(options) {
|
|
52590
52528
|
const base = options.apiUrl ?? CLOUDFLARE_API;
|
|
52591
52529
|
const send = options.fetch ?? fetch;
|
|
@@ -52751,6 +52689,20 @@ function createDnsProvider(config, token) {
|
|
|
52751
52689
|
});
|
|
52752
52690
|
}
|
|
52753
52691
|
|
|
52692
|
+
// packages/daemon/src/network/https/acme/read-certificate-info.ts
|
|
52693
|
+
import"reflect-metadata";
|
|
52694
|
+
import * as x5092 from "@peculiar/x509";
|
|
52695
|
+
function readCertificateInfo(chainPem) {
|
|
52696
|
+
const certificate = new x5092.X509Certificate(x5092.PemConverter.decodeFirst(chainPem));
|
|
52697
|
+
const altNames = certificate.getExtension(x5092.SubjectAlternativeNameExtension);
|
|
52698
|
+
const names = new Set([
|
|
52699
|
+
...certificate.subjectName.getField("CN"),
|
|
52700
|
+
...altNames?.names.items.map((name) => name.value) ?? []
|
|
52701
|
+
]);
|
|
52702
|
+
names.delete("");
|
|
52703
|
+
return { notBefore: certificate.notBefore, notAfter: certificate.notAfter, names: [...names] };
|
|
52704
|
+
}
|
|
52705
|
+
|
|
52754
52706
|
// packages/daemon/src/network/https/acme/cert-manager.ts
|
|
52755
52707
|
function createCertManager(options) {
|
|
52756
52708
|
const domain = options.domain;
|
|
@@ -55104,8 +55056,8 @@ import {
|
|
|
55104
55056
|
statSync as statSync15
|
|
55105
55057
|
} from "fs";
|
|
55106
55058
|
import { basename as basename5, dirname as dirname11, join as join37 } from "path";
|
|
55107
|
-
import * as
|
|
55108
|
-
var CopiesSchema =
|
|
55059
|
+
import * as z97 from "zod";
|
|
55060
|
+
var CopiesSchema = z97.record(z97.string(), z97.object({ ino: z97.string(), ctimeNs: z97.string(), size: z97.string() }));
|
|
55109
55061
|
function createXfsBackups(ctx) {
|
|
55110
55062
|
const backup = buildBackupPaths(ctx.dataDir);
|
|
55111
55063
|
const freshCopies = new Set;
|