@forgezero/agent 0.1.41 → 0.1.42
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 +299 -86
- package/dist/agent-heartbeat.js +6 -3
- package/dist/agent-update-helper.js +5 -2
- package/dist/agent-update.js +5 -2
- package/dist/bootstrap.d.ts +17 -8
- package/dist/bootstrap.js +1504 -512
- package/dist/cli/agent-install.d.ts +6 -5
- package/dist/cli/cloudflare-bootstrap.d.ts +12 -1
- package/dist/cli/maintenance.d.ts +23 -0
- package/dist/cli/run.d.ts +3 -1
- package/dist/cli/session-store.d.ts +5 -0
- package/dist/cloudflare-bootstrap.d.ts +73 -35
- package/dist/cloudflare-bootstrap.js +587 -90
- package/dist/cloudflare-edge.d.ts +64 -12
- package/dist/cloudflare-edge.js +103 -8
- package/dist/community-rehearsal-host.d.ts +51 -0
- package/dist/community-rehearsal-host.js +272 -0
- package/dist/credential-schema.d.ts +54 -0
- package/dist/credential-schema.js +47 -0
- package/dist/definition.d.ts +31 -5
- package/dist/definition.js +271 -44
- package/dist/deploy-file.js +294 -68
- package/dist/deployment-runner.js +18 -5
- package/dist/deployment.d.ts +13 -1
- package/dist/fz-agent.js +3932 -582
- package/dist/fz-git-ssh.js +122 -0
- package/dist/fz.js +3634 -1266
- package/dist/git-ssh.d.ts +5 -0
- package/dist/guest-enrolment.d.ts +2 -0
- package/dist/guest-enrolment.js +1 -0
- package/dist/host-maintenance.d.ts +39 -0
- package/dist/host-maintenance.js +135 -0
- package/dist/index.d.ts +4 -2
- package/dist/mesh-connector.d.ts +16 -0
- package/dist/mesh-connector.js +46 -0
- package/dist/metal-bootstrap.js +145 -7
- package/dist/metal-helper-socket.js +61 -31
- package/dist/metal-provision.d.ts +2 -2
- package/dist/metal-provision.js +62 -32
- package/dist/operator-bootstrap.d.ts +90 -0
- package/dist/operator-bootstrap.js +5704 -0
- package/dist/otel-collector.d.ts +18 -0
- package/dist/pipeline.d.ts +3 -2
- package/dist/pipeline.js +1 -1
- package/dist/platform-bootstrap-runtime.d.ts +39 -21
- package/dist/platform-bootstrap-runtime.js +182 -59
- package/dist/platform-fleet-verification.d.ts +19 -0
- package/dist/platform-fleet-verification.js +3873 -0
- package/dist/platform-genesis-config.d.ts +7 -0
- package/dist/platform-genesis.d.ts +17 -0
- package/dist/provision.d.ts +76 -3
- package/dist/provision.js +1061 -229
- package/dist/recovery-host.d.ts +7 -0
- package/dist/recovery-host.js +124 -0
- package/dist/service-supervisor.d.ts +42 -0
- package/dist/software-helper.d.ts +4 -0
- package/dist/software-helper.js +865 -63
- package/dist/software.d.ts +14 -3
- package/dist/software.js +163 -37
- package/dist/ssh-bootstrap.d.ts +97 -0
- package/dist/supervised-app.d.ts +2 -0
- package/dist/version.d.ts +1 -1
- package/package.json +175 -164
- package/schema/{deploy-v2.json → deploy-v3.json} +53 -6
package/dist/software.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type CatalogStatus = 'testing' | 'active' | 'retired';
|
|
2
2
|
export type DeploymentChannel = 'development' | 'production';
|
|
3
|
-
export type SoftwareId = 'bun' | 'nginx' | 'arangodb' | 'cloudflared' | 'ufw' | 'openssh-client';
|
|
3
|
+
export type SoftwareId = 'bun' | 'nginx' | 'arangodb' | 'cloudflared' | 'cloudflare-warp' | 'ufw' | 'openssh-client';
|
|
4
4
|
/** Repository input is a catalogue coordinate, never a root command. */
|
|
5
5
|
export interface SoftwareRequirement {
|
|
6
6
|
id: SoftwareId;
|
|
@@ -31,12 +31,23 @@ export interface SoftwareCommandResult {
|
|
|
31
31
|
exitCode: number;
|
|
32
32
|
output: string;
|
|
33
33
|
}
|
|
34
|
-
export type
|
|
34
|
+
export type SoftwareOperation = {
|
|
35
|
+
kind: 'check';
|
|
36
|
+
software: SoftwareId;
|
|
37
|
+
version: string;
|
|
38
|
+
} | {
|
|
39
|
+
kind: 'install';
|
|
40
|
+
software: SoftwareId;
|
|
41
|
+
version: string;
|
|
42
|
+
};
|
|
43
|
+
export type SoftwareExec = (operation: SoftwareOperation) => Promise<SoftwareCommandResult>;
|
|
35
44
|
export declare const PINNED_BUN_VERSION = "1.3.14";
|
|
36
|
-
export declare const
|
|
45
|
+
export declare const BUN_RELEASE_SHA256 = "951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cbccdf7e848f";
|
|
37
46
|
/** Public, command-free catalog. Root strategies remain private below. */
|
|
38
47
|
export declare const OS_CATALOG: readonly OsCatalogEntry[];
|
|
39
48
|
export declare const SOFTWARE_CATALOG: readonly SoftwareCatalogEntry[];
|
|
49
|
+
/** Closed, fixed-argv privileged strategy executor. No repository value becomes a command. */
|
|
50
|
+
export declare function executeSoftwareOperation(operation: SoftwareOperation): Promise<SoftwareCommandResult>;
|
|
40
51
|
export declare function observeSoftwareHost(osRelease?: string, architecture?: NodeJS.Architecture): SoftwareObservation;
|
|
41
52
|
export declare function validateSoftwareRequirements(value: unknown, _options?: {
|
|
42
53
|
channel?: DeploymentChannel;
|
package/dist/software.js
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
// src/software.ts
|
|
2
|
-
import {
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import {
|
|
4
|
+
accessSync,
|
|
5
|
+
chmodSync,
|
|
6
|
+
copyFileSync,
|
|
7
|
+
mkdtempSync,
|
|
8
|
+
mkdirSync,
|
|
9
|
+
readFileSync,
|
|
10
|
+
renameSync,
|
|
11
|
+
rmSync,
|
|
12
|
+
symlinkSync,
|
|
13
|
+
unlinkSync,
|
|
14
|
+
writeFileSync
|
|
15
|
+
} from "node:fs";
|
|
16
|
+
import { tmpdir } from "node:os";
|
|
17
|
+
import { join } from "node:path";
|
|
3
18
|
var PINNED_BUN_VERSION = "1.3.14";
|
|
4
|
-
var
|
|
19
|
+
var BUN_RELEASE_SHA256 = "951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cbccdf7e848f";
|
|
5
20
|
var ARANGO_SHA256 = "b5a9197b4343f2ed554e1ebc1ef8e6529c7c39cde0035cdc311a4747a3355066";
|
|
6
21
|
var CLOUDFLARED_SHA256 = "9d71c677db00134c1bd4144b7783486b654ad281b1ea62b4972098d19f770f17";
|
|
7
22
|
var OS_CATALOG = [
|
|
@@ -12,41 +27,151 @@ var SOFTWARE_CATALOG = [
|
|
|
12
27
|
{ id: "nginx", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
13
28
|
{ id: "arangodb", version: "3.11.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
14
29
|
{ id: "cloudflared", version: "2026.7.3", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
30
|
+
{ id: "cloudflare-warp", version: "2026.6.822.0-min", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
15
31
|
{ id: "ufw", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
16
32
|
{ id: "openssh-client", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" }
|
|
17
33
|
];
|
|
18
34
|
var UBUNTU_2604_X64 = [
|
|
19
|
-
{
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
|
|
26
|
-
check: "command -v nginx >/dev/null && systemctl is-active --quiet nginx",
|
|
27
|
-
install: "DEBIAN_FRONTEND=noninteractive apt-get update -qq && apt-get install -y nginx && systemctl enable --now nginx"
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
requirement: { id: "arangodb", version: "3.11.14" },
|
|
31
|
-
check: `arangod --version 2>/dev/null | head -1 | grep -q '3.11.14' && ` + `! systemctl is-active --quiet arangodb3.service && ` + `! systemctl is-enabled --quiet arangodb3.service`,
|
|
32
|
-
install: `tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT; ` + `curl -fsSL 'https://download.arangodb.com/arangodb311/DEBIAN/amd64/arangodb3_3.11.14-1_amd64.deb' -o "$tmp/arangodb.deb"; ` + `echo "${ARANGO_SHA256} $tmp/arangodb.deb" | sha256sum -c -; ` + `DEBIAN_FRONTEND=noninteractive dpkg -i "$tmp/arangodb.deb" >/dev/null 2>&1 || ` + `DEBIAN_FRONTEND=noninteractive apt-get -y -f install; ` + `systemctl disable --now arangodb3.service`
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
requirement: { id: "cloudflared", version: "2026.7.3" },
|
|
36
|
-
check: `cloudflared --version 2>/dev/null | grep -q '2026.7.3'`,
|
|
37
|
-
install: `tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT; ` + `curl -fsSL 'https://github.com/cloudflare/cloudflared/releases/download/2026.7.3/cloudflared-linux-amd64' -o "$tmp/cloudflared"; ` + `echo "${CLOUDFLARED_SHA256} $tmp/cloudflared" | sha256sum -c -; ` + `install -m 0755 "$tmp/cloudflared" /usr/local/bin/cloudflared`
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
requirement: { id: "ufw", version: "ubuntu-26.04" },
|
|
41
|
-
check: "command -v ufw >/dev/null",
|
|
42
|
-
install: "DEBIAN_FRONTEND=noninteractive apt-get update -qq && apt-get install -y ufw"
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
requirement: { id: "openssh-client", version: "ubuntu-26.04" },
|
|
46
|
-
check: "command -v ssh >/dev/null && command -v scp >/dev/null && command -v ssh-keyscan >/dev/null && command -v ssh-keygen >/dev/null",
|
|
47
|
-
install: "DEBIAN_FRONTEND=noninteractive apt-get update -qq && apt-get install -y openssh-client"
|
|
48
|
-
}
|
|
35
|
+
{ requirement: { id: "bun", version: "1.3.14" } },
|
|
36
|
+
{ requirement: { id: "nginx", version: "ubuntu-26.04" } },
|
|
37
|
+
{ requirement: { id: "arangodb", version: "3.11.14" } },
|
|
38
|
+
{ requirement: { id: "cloudflared", version: "2026.7.3" } },
|
|
39
|
+
{ requirement: { id: "cloudflare-warp", version: "2026.6.822.0-min" } },
|
|
40
|
+
{ requirement: { id: "ufw", version: "ubuntu-26.04" } },
|
|
41
|
+
{ requirement: { id: "openssh-client", version: "ubuntu-26.04" } }
|
|
49
42
|
];
|
|
43
|
+
var path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
44
|
+
var run = async (argv, env = {}) => {
|
|
45
|
+
const child = Bun.spawn([...argv], { stdout: "pipe", stderr: "pipe", env: { PATH: path, LANG: "C", LC_ALL: "C", ...env } });
|
|
46
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
47
|
+
new Response(child.stdout).text(),
|
|
48
|
+
new Response(child.stderr).text(),
|
|
49
|
+
child.exited
|
|
50
|
+
]);
|
|
51
|
+
return { exitCode, output: `${stdout}${stderr}` };
|
|
52
|
+
};
|
|
53
|
+
var download = async (url, destination, sha256) => {
|
|
54
|
+
const response = await fetch(url, { redirect: "follow", signal: AbortSignal.timeout(120000) });
|
|
55
|
+
if (!response.ok)
|
|
56
|
+
throw new Error(`download failed with HTTP ${response.status}`);
|
|
57
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
58
|
+
if (createHash("sha256").update(bytes).digest("hex") !== sha256)
|
|
59
|
+
throw new Error("download checksum mismatch");
|
|
60
|
+
writeFileSync(destination, bytes, { mode: 384, flag: "wx" });
|
|
61
|
+
};
|
|
62
|
+
var successful = (result, pattern) => result.exitCode === 0 && (!pattern || pattern.test(result.output));
|
|
63
|
+
var aptInstall = async (name) => {
|
|
64
|
+
const environment = { DEBIAN_FRONTEND: "noninteractive" };
|
|
65
|
+
const update = await run(["/usr/bin/apt-get", "update", "-qq"], environment);
|
|
66
|
+
return update.exitCode === 0 ? run(["/usr/bin/apt-get", "install", "-y", name], environment) : update;
|
|
67
|
+
};
|
|
68
|
+
async function executeSoftwareOperation(operation) {
|
|
69
|
+
const { software, version } = operation;
|
|
70
|
+
if (!UBUNTU_2604_X64.some(({ requirement }) => requirement.id === software && requirement.version === version)) {
|
|
71
|
+
return { exitCode: 2, output: "unsupported software operation" };
|
|
72
|
+
}
|
|
73
|
+
if (operation.kind === "check") {
|
|
74
|
+
if (software === "bun")
|
|
75
|
+
return run(["/usr/local/bin/bun", "--version"]).then((r) => ({ ...r, exitCode: successful(r, /^1\.3\.14\s*$/m) ? 0 : 1 }));
|
|
76
|
+
if (software === "nginx") {
|
|
77
|
+
const binary = await run(["/usr/sbin/nginx", "-v"]);
|
|
78
|
+
return binary.exitCode === 0 ? run(["/usr/bin/systemctl", "is-active", "--quiet", "nginx.service"]) : binary;
|
|
79
|
+
}
|
|
80
|
+
if (software === "arangodb") {
|
|
81
|
+
const binary = await run(["/usr/bin/arangod", "--version"]);
|
|
82
|
+
if (!successful(binary, /3\.11\.14/))
|
|
83
|
+
return { ...binary, exitCode: 1 };
|
|
84
|
+
const [active, enabled] = await Promise.all([
|
|
85
|
+
run(["/usr/bin/systemctl", "is-active", "--quiet", "arangodb3.service"]),
|
|
86
|
+
run(["/usr/bin/systemctl", "is-enabled", "--quiet", "arangodb3.service"])
|
|
87
|
+
]);
|
|
88
|
+
return active.exitCode !== 0 && enabled.exitCode !== 0 ? { exitCode: 0, output: binary.output } : { exitCode: 1, output: "vendor standalone unit remains active or enabled" };
|
|
89
|
+
}
|
|
90
|
+
if (software === "cloudflared")
|
|
91
|
+
return run(["/usr/local/bin/cloudflared", "--version"]).then((r) => ({ ...r, exitCode: successful(r, /2026\.7\.3/) ? 0 : 1 }));
|
|
92
|
+
if (software === "cloudflare-warp")
|
|
93
|
+
return run(["/usr/bin/warp-cli", "--version"]).then((result) => {
|
|
94
|
+
const match = result.output.match(/(\d{4})\.(\d+)\.(\d+)\.(\d+)/);
|
|
95
|
+
const observed = match?.slice(1).map(Number);
|
|
96
|
+
const minimum = [2026, 6, 822, 0];
|
|
97
|
+
const supported = observed && observed.some((part, index) => part > minimum[index] && observed.slice(0, index).every((prior, priorIndex) => prior === minimum[priorIndex])) || observed?.every((part, index) => part === minimum[index]);
|
|
98
|
+
return { ...result, exitCode: result.exitCode === 0 && supported ? 0 : 1 };
|
|
99
|
+
});
|
|
100
|
+
const binaries = software === "ufw" ? ["/usr/sbin/ufw"] : ["/usr/bin/ssh", "/usr/bin/scp", "/usr/bin/ssh-keyscan", "/usr/bin/ssh-keygen"];
|
|
101
|
+
try {
|
|
102
|
+
binaries.forEach((binary) => accessSync(binary));
|
|
103
|
+
return { exitCode: 0, output: "" };
|
|
104
|
+
} catch (cause) {
|
|
105
|
+
return { exitCode: 1, output: cause instanceof Error ? cause.message : String(cause) };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (software === "nginx" || software === "ufw" || software === "openssh-client") {
|
|
109
|
+
const installed = await aptInstall(software === "openssh-client" ? "openssh-client" : software);
|
|
110
|
+
if (installed.exitCode !== 0 || software !== "nginx")
|
|
111
|
+
return installed;
|
|
112
|
+
return run(["/usr/bin/systemctl", "enable", "--now", "nginx.service"]);
|
|
113
|
+
}
|
|
114
|
+
const directory = mkdtempSync(join(tmpdir(), "forgezero-software-"));
|
|
115
|
+
try {
|
|
116
|
+
if (software === "cloudflare-warp") {
|
|
117
|
+
const key = join(directory, "cloudflare-warp-key.gpg");
|
|
118
|
+
await download("https://pkg.cloudflareclient.com/pubkey.gpg", key, "0f37fc298c98e88ee3c0ee68c95b69f1dba9eb477abe3167e13982105911264d");
|
|
119
|
+
mkdirSync("/usr/share/keyrings", { recursive: true, mode: 493 });
|
|
120
|
+
const dearmored = await run([
|
|
121
|
+
"/usr/bin/gpg",
|
|
122
|
+
"--batch",
|
|
123
|
+
"--yes",
|
|
124
|
+
"--dearmor",
|
|
125
|
+
"-o",
|
|
126
|
+
"/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg",
|
|
127
|
+
key
|
|
128
|
+
]);
|
|
129
|
+
if (dearmored.exitCode !== 0)
|
|
130
|
+
return dearmored;
|
|
131
|
+
mkdirSync("/etc/apt/sources.list.d", { recursive: true, mode: 493 });
|
|
132
|
+
writeFileSync("/etc/apt/sources.list.d/cloudflare-client.list", `deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ resolute main
|
|
133
|
+
`, { mode: 420 });
|
|
134
|
+
return aptInstall("cloudflare-warp");
|
|
135
|
+
}
|
|
136
|
+
if (software === "bun") {
|
|
137
|
+
const archive = join(directory, "bun.zip");
|
|
138
|
+
await download("https://github.com/oven-sh/bun/releases/download/bun-v1.3.14/bun-linux-x64.zip", archive, BUN_RELEASE_SHA256);
|
|
139
|
+
const unpacked = join(directory, "unpacked");
|
|
140
|
+
mkdirSync(unpacked, { mode: 448 });
|
|
141
|
+
const unzipped = await run(["/usr/bin/unzip", "-q", archive, "-d", unpacked]);
|
|
142
|
+
if (unzipped.exitCode !== 0)
|
|
143
|
+
return unzipped;
|
|
144
|
+
mkdirSync("/usr/local/lib/forgezero/runtime", { recursive: true, mode: 493 });
|
|
145
|
+
copyFileSync(join(unpacked, "bun-linux-x64", "bun"), "/usr/local/lib/forgezero/runtime/bun.next");
|
|
146
|
+
chmodSync("/usr/local/lib/forgezero/runtime/bun.next", 493);
|
|
147
|
+
renameSync("/usr/local/lib/forgezero/runtime/bun.next", "/usr/local/lib/forgezero/runtime/bun");
|
|
148
|
+
try {
|
|
149
|
+
unlinkSync("/usr/local/bin/bun");
|
|
150
|
+
} catch {}
|
|
151
|
+
symlinkSync("/usr/local/lib/forgezero/runtime/bun", "/usr/local/bin/bun");
|
|
152
|
+
return { exitCode: 0, output: "" };
|
|
153
|
+
}
|
|
154
|
+
if (software === "cloudflared") {
|
|
155
|
+
const binary = join(directory, "cloudflared");
|
|
156
|
+
await download("https://github.com/cloudflare/cloudflared/releases/download/2026.7.3/cloudflared-linux-amd64", binary, CLOUDFLARED_SHA256);
|
|
157
|
+
chmodSync(binary, 493);
|
|
158
|
+
copyFileSync(binary, "/usr/local/bin/cloudflared");
|
|
159
|
+
chmodSync("/usr/local/bin/cloudflared", 493);
|
|
160
|
+
return { exitCode: 0, output: "" };
|
|
161
|
+
}
|
|
162
|
+
const deb = join(directory, "arangodb.deb");
|
|
163
|
+
await download("https://download.arangodb.com/arangodb311/DEBIAN/amd64/arangodb3_3.11.14-1_amd64.deb", deb, ARANGO_SHA256);
|
|
164
|
+
let installed = await run(["/usr/bin/dpkg", "-i", deb], { DEBIAN_FRONTEND: "noninteractive" });
|
|
165
|
+
if (installed.exitCode !== 0)
|
|
166
|
+
installed = await run(["/usr/bin/apt-get", "-y", "-f", "install"], { DEBIAN_FRONTEND: "noninteractive" });
|
|
167
|
+
if (installed.exitCode !== 0)
|
|
168
|
+
return installed;
|
|
169
|
+
await run(["/usr/bin/systemctl", "disable", "--now", "arangodb3.service"]);
|
|
170
|
+
return { exitCode: 0, output: "" };
|
|
171
|
+
} finally {
|
|
172
|
+
rmSync(directory, { recursive: true, force: true });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
50
175
|
function observeSoftwareHost(osRelease = readFileSync("/etc/os-release", "utf8"), architecture = process.arch) {
|
|
51
176
|
const values = Object.fromEntries(osRelease.split(`
|
|
52
177
|
`).flatMap((line) => {
|
|
@@ -69,7 +194,7 @@ function validateSoftwareRequirements(value, _options = {}) {
|
|
|
69
194
|
if (Object.keys(row).some((key) => key !== "id" && key !== "version")) {
|
|
70
195
|
throw new Error("software requirement contains an unknown field");
|
|
71
196
|
}
|
|
72
|
-
if (!["bun", "nginx", "arangodb", "cloudflared", "ufw", "openssh-client"].includes(String(row.id)) || typeof row.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9.-]{0,31}$/.test(row.version)) {
|
|
197
|
+
if (!["bun", "nginx", "arangodb", "cloudflared", "cloudflare-warp", "ufw", "openssh-client"].includes(String(row.id)) || typeof row.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9.-]{0,31}$/.test(row.version)) {
|
|
73
198
|
throw new Error("software requirement coordinate is invalid");
|
|
74
199
|
}
|
|
75
200
|
const requirement = { id: row.id, version: row.version };
|
|
@@ -95,15 +220,15 @@ async function ensureSoftwareRequirements(requirementsInput, options) {
|
|
|
95
220
|
const strategy = UBUNTU_2604_X64.find(({ requirement: candidate }) => candidate.id === requirement.id && candidate.version === requirement.version);
|
|
96
221
|
if (!strategy)
|
|
97
222
|
throw new Error(`unsupported software requirement: ${requirement.id}@${requirement.version}`);
|
|
98
|
-
const before = await options.exec(
|
|
223
|
+
const before = await options.exec({ kind: "check", software: requirement.id, version: requirement.version });
|
|
99
224
|
if (before.exitCode === 0) {
|
|
100
225
|
results.push({ ...requirement, changed: false });
|
|
101
226
|
continue;
|
|
102
227
|
}
|
|
103
|
-
const installed = await options.exec(
|
|
228
|
+
const installed = await options.exec({ kind: "install", software: requirement.id, version: requirement.version });
|
|
104
229
|
if (installed.exitCode !== 0)
|
|
105
230
|
throw new Error(`could not install ${requirement.id}@${requirement.version}: ${installed.output.trim()}`);
|
|
106
|
-
const after = await options.exec(
|
|
231
|
+
const after = await options.exec({ kind: "check", software: requirement.id, version: requirement.version });
|
|
107
232
|
if (after.exitCode !== 0)
|
|
108
233
|
throw new Error(`${requirement.id}@${requirement.version} did not pass its post-install check`);
|
|
109
234
|
results.push({ ...requirement, changed: true });
|
|
@@ -113,9 +238,10 @@ async function ensureSoftwareRequirements(requirementsInput, options) {
|
|
|
113
238
|
export {
|
|
114
239
|
validateSoftwareRequirements,
|
|
115
240
|
observeSoftwareHost,
|
|
241
|
+
executeSoftwareOperation,
|
|
116
242
|
ensureSoftwareRequirements,
|
|
117
243
|
SOFTWARE_CATALOG,
|
|
118
244
|
PINNED_BUN_VERSION,
|
|
119
245
|
OS_CATALOG,
|
|
120
|
-
|
|
246
|
+
BUN_RELEASE_SHA256
|
|
121
247
|
};
|
package/dist/ssh-bootstrap.d.ts
CHANGED
|
@@ -16,9 +16,87 @@ export interface RemoteSshBootstrapClaim {
|
|
|
16
16
|
user: string;
|
|
17
17
|
hostKeySha256: string;
|
|
18
18
|
nodeHostname: string;
|
|
19
|
+
credential: {
|
|
20
|
+
source: 'runner';
|
|
21
|
+
} | {
|
|
22
|
+
source: 'vault';
|
|
23
|
+
privateKey: string;
|
|
24
|
+
};
|
|
19
25
|
};
|
|
20
26
|
enrolmentToken: string;
|
|
21
27
|
}
|
|
28
|
+
export interface RemoteMetalAdmissionClaim {
|
|
29
|
+
jobKey: string;
|
|
30
|
+
claimToken: string;
|
|
31
|
+
claimExpiresAtTs: number;
|
|
32
|
+
attempt: number;
|
|
33
|
+
tenantKey: string;
|
|
34
|
+
projectKey: string;
|
|
35
|
+
environmentKey: string;
|
|
36
|
+
hostname: string;
|
|
37
|
+
label: string;
|
|
38
|
+
regionKey: string;
|
|
39
|
+
runnerNodeKey: string;
|
|
40
|
+
challenge: string;
|
|
41
|
+
provisioning: {
|
|
42
|
+
volumeGroup: string;
|
|
43
|
+
bridge: string;
|
|
44
|
+
subnetPrefix: string;
|
|
45
|
+
addressStart: number;
|
|
46
|
+
addressEnd: number;
|
|
47
|
+
gateway: string;
|
|
48
|
+
nameservers: string[];
|
|
49
|
+
cpuPools: Array<{
|
|
50
|
+
key: string;
|
|
51
|
+
cpus: string;
|
|
52
|
+
physicalCores: number;
|
|
53
|
+
memoryNodes?: string;
|
|
54
|
+
}>;
|
|
55
|
+
housekeepingCpus: string;
|
|
56
|
+
housekeepingMemoryNodes?: string;
|
|
57
|
+
confidential?: {
|
|
58
|
+
cbitpos: number;
|
|
59
|
+
reducedPhysBits: number;
|
|
60
|
+
policy: string;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
target: RemoteSshBootstrapClaim['target'];
|
|
64
|
+
}
|
|
65
|
+
export interface MetalAdmissionProof {
|
|
66
|
+
jobKey: string;
|
|
67
|
+
tenantKey: string;
|
|
68
|
+
projectKey: string;
|
|
69
|
+
hostname: string;
|
|
70
|
+
runnerNodeKey: string;
|
|
71
|
+
challenge: string;
|
|
72
|
+
target: {
|
|
73
|
+
host: string;
|
|
74
|
+
port: number;
|
|
75
|
+
user: string;
|
|
76
|
+
hostKeySha256: string;
|
|
77
|
+
};
|
|
78
|
+
publicKeys: {
|
|
79
|
+
ed25519: string;
|
|
80
|
+
mlDsa: string;
|
|
81
|
+
};
|
|
82
|
+
preflight: {
|
|
83
|
+
vcpu: number;
|
|
84
|
+
memoryGib: number;
|
|
85
|
+
diskGib: number;
|
|
86
|
+
kvm: boolean;
|
|
87
|
+
snpHost: boolean;
|
|
88
|
+
helper: boolean;
|
|
89
|
+
};
|
|
90
|
+
envelope: {
|
|
91
|
+
version: 1;
|
|
92
|
+
suite: 'ed25519+ml-dsa-65';
|
|
93
|
+
nodeKey: string;
|
|
94
|
+
timestamp: number;
|
|
95
|
+
nonce: string;
|
|
96
|
+
edSignature: string;
|
|
97
|
+
mlDsaSignature: string;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
22
100
|
export interface BootstrapCommandResult {
|
|
23
101
|
exitCode: number;
|
|
24
102
|
output: string;
|
|
@@ -70,3 +148,22 @@ export declare function startSshBootstrapPull(options: SshBootstrapPullOptions):
|
|
|
70
148
|
stop(): Promise<void>;
|
|
71
149
|
readonly active: boolean;
|
|
72
150
|
};
|
|
151
|
+
export type MetalAdmissionResult = {
|
|
152
|
+
status: 'idle';
|
|
153
|
+
} | {
|
|
154
|
+
status: 'completed';
|
|
155
|
+
jobKey: string;
|
|
156
|
+
attempt: number;
|
|
157
|
+
} | {
|
|
158
|
+
status: 'failed';
|
|
159
|
+
jobKey: string;
|
|
160
|
+
attempt: number;
|
|
161
|
+
failureCode: string;
|
|
162
|
+
};
|
|
163
|
+
/** Install the fixed metal Agent profile, then ask that host—not the runner—to sign its local preflight. */
|
|
164
|
+
export declare function executeMetalAdmission(claim: RemoteMetalAdmissionClaim, options: Pick<SshBootstrapPullOptions, 'platformApiUrl' | 'sshKeyPath' | 'targetTelemetryEndpoint' | 'fzCliPath' | 'fzAgentPath' | 'exec' | 'resolveHost'>): Promise<MetalAdmissionProof>;
|
|
165
|
+
export declare function pullMetalAdmissionOnce(options: SshBootstrapPullOptions): Promise<MetalAdmissionResult>;
|
|
166
|
+
export declare function startMetalAdmissionPull(options: SshBootstrapPullOptions): {
|
|
167
|
+
stop(): Promise<void>;
|
|
168
|
+
readonly active: boolean;
|
|
169
|
+
};
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** One package version shared by both public binaries. Pinned to package.json by tests. */
|
|
2
|
-
export declare const VERSION = "0.1.
|
|
2
|
+
export declare const VERSION = "0.1.42";
|