@forgezero/agent 0.1.82 → 0.1.83
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 +7 -1
- package/dist/agent-heartbeat.js +1 -1
- package/dist/bootstrap.js +1 -1
- package/dist/deploy-compiler.d.ts +15 -2
- package/dist/deploy-compiler.js +130 -34
- package/dist/fz-agent.js +1 -1
- package/dist/fz.js +275 -96
- package/dist/metal-bootstrap.js +1 -1
- package/dist/operator-bootstrap.js +1 -1
- package/dist/platform-fleet-verification.js +1 -1
- package/dist/provision.js +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -85,6 +85,8 @@ Every row links to the detailed explanation and named-import/example area below.
|
|
|
85
85
|
|
|
86
86
|
bun add -g @forgezero/agent — Install the version-matched fz operator CLI and fz-agent daemon.
|
|
87
87
|
fz deploy init — Create a typed forgezero.deploy.ts and its canonical inert execution plan.
|
|
88
|
+
fz deploy init --runtime containerd --os ubuntu-24.04 --replicas 1 --cpu-cores 2 --memory-mib 2048 --storage-gib 20 --reuse require — Create one ordinary containerd/runc compute definition with exact reserved resources; existing eligible project capacity is required.
|
|
89
|
+
fz deploy init --runtime kata-snp --os ubuntu-26.04 --replicas 1 --cpu-cores 2 --memory-mib 4096 --storage-gib 40 --reuse prefer — Create one Kata QEMU SEV-SNP definition; reuse eligible capacity first, then provisioning still requires an explicit approved plan and spend ceiling.
|
|
88
90
|
fz deploy compile — Compile and validate TypeScript deployment intent into .fz/deploy.plan.json.
|
|
89
91
|
fz deploy check — Refuse stale plans, unsafe provider/action coordinates and unresolved deployment blockers.
|
|
90
92
|
fz bootstrap platform bundle --root /absolute/api --branch dev --output /secure/forgezero/development/api.bundle — Build one verified release-generation-one source bundle without a Git credential.
|
|
@@ -97,6 +99,8 @@ fz status — Read installed service and bootstrap evidence.
|
|
|
97
99
|
```text
|
|
98
100
|
bun add -g @forgezero/agent
|
|
99
101
|
fz deploy init
|
|
102
|
+
fz deploy init --runtime containerd --os ubuntu-24.04 --replicas 1 --cpu-cores 2 --memory-mib 2048 --storage-gib 20 --reuse require
|
|
103
|
+
fz deploy init --runtime kata-snp --os ubuntu-26.04 --replicas 1 --cpu-cores 2 --memory-mib 4096 --storage-gib 40 --reuse prefer
|
|
100
104
|
fz deploy compile
|
|
101
105
|
fz deploy check
|
|
102
106
|
fz bootstrap platform bundle --root /absolute/api --branch dev --output /secure/forgezero/development/api.bundle
|
|
@@ -1402,7 +1406,7 @@ await runDeploymentPlan({
|
|
|
1402
1406
|
<a id="forgezero-agent-deploy-compiler"></a>
|
|
1403
1407
|
## @forgezero/agent/deploy-compiler
|
|
1404
1408
|
|
|
1405
|
-
Explicit developer/CI compiler from forgezero.deploy.ts to canonical .fz/deploy.plan.json; never used to execute project TypeScript in production. This entry exposes 7 named value exports and
|
|
1409
|
+
Explicit developer/CI compiler from forgezero.deploy.ts to canonical .fz/deploy.plan.json; never used to execute project TypeScript in production. This entry exposes 7 named value exports and 3 named type exports. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
|
|
1406
1410
|
|
|
1407
1411
|
```text
|
|
1408
1412
|
import {
|
|
@@ -1417,6 +1421,8 @@ import {
|
|
|
1417
1421
|
|
|
1418
1422
|
import type {
|
|
1419
1423
|
DeploymentCompilation,
|
|
1424
|
+
DeploymentInitOptions,
|
|
1425
|
+
DeploymentInitRuntime,
|
|
1420
1426
|
} from '@forgezero/agent/deploy-compiler';
|
|
1421
1427
|
```
|
|
1422
1428
|
|
package/dist/agent-heartbeat.js
CHANGED
package/dist/bootstrap.js
CHANGED
|
@@ -8,11 +8,24 @@ export interface DeploymentCompilation {
|
|
|
8
8
|
digest: `sha256:${string}`;
|
|
9
9
|
changed: boolean;
|
|
10
10
|
}
|
|
11
|
-
export
|
|
11
|
+
export type DeploymentInitRuntime = 'native' | 'containerd' | 'kata-snp';
|
|
12
|
+
export interface DeploymentInitOptions {
|
|
13
|
+
runtime?: DeploymentInitRuntime;
|
|
14
|
+
operatingSystem?: 'ubuntu-24.04' | 'ubuntu-26.04';
|
|
15
|
+
isolation?: 'standard' | 'sev-snp';
|
|
16
|
+
replicas?: number;
|
|
17
|
+
cpuCores?: number;
|
|
18
|
+
memoryMiB?: number;
|
|
19
|
+
storageGiB?: number;
|
|
20
|
+
sharing?: 'exclusive' | 'shared';
|
|
21
|
+
reuse?: 'require' | 'prefer';
|
|
22
|
+
profile?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function defaultTypeScriptDeployment(name: string, options?: DeploymentInitOptions): string;
|
|
12
25
|
export declare function initializeTypeScriptDeployment(rootValue: string, options: {
|
|
13
26
|
name: string;
|
|
14
27
|
force?: boolean;
|
|
15
|
-
}): string;
|
|
28
|
+
} & DeploymentInitOptions): string;
|
|
16
29
|
export declare function loadDeploymentSource(root: string, sourceFile?: string): Promise<unknown>;
|
|
17
30
|
export declare function compileDeploymentProject(rootValue: string, options?: {
|
|
18
31
|
sourceFile?: string;
|
package/dist/deploy-compiler.js
CHANGED
|
@@ -1142,46 +1142,82 @@ function parseDeploymentPlan(value) {
|
|
|
1142
1142
|
}
|
|
1143
1143
|
|
|
1144
1144
|
// src/deploy-compiler.ts
|
|
1145
|
-
import { existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
1146
|
-
import {
|
|
1147
|
-
import {
|
|
1145
|
+
import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
1146
|
+
import { tmpdir } from "os";
|
|
1147
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "path";
|
|
1148
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
1148
1149
|
var DEPLOY_SOURCE_FILE = "forgezero.deploy.ts";
|
|
1149
1150
|
var DEPLOY_PLAN_FILE = ".fz/deploy.plan.json";
|
|
1150
1151
|
function safeName(value) {
|
|
1151
1152
|
const result = value.toLowerCase().replace(/^@[^/]+\//, "").replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 63);
|
|
1152
1153
|
return /^[a-z]/.test(result) ? result : `app-${result || "service"}`;
|
|
1153
1154
|
}
|
|
1154
|
-
function
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1155
|
+
function initCoordinates(options) {
|
|
1156
|
+
const runtime = options.runtime ?? "native";
|
|
1157
|
+
const isolation = options.isolation ?? (runtime === "kata-snp" ? "sev-snp" : "standard");
|
|
1158
|
+
const operatingSystem = options.operatingSystem ?? (isolation === "sev-snp" ? "ubuntu-26.04" : "ubuntu-24.04");
|
|
1159
|
+
const result = {
|
|
1160
|
+
runtime,
|
|
1161
|
+
isolation,
|
|
1162
|
+
operatingSystem,
|
|
1163
|
+
replicas: options.replicas ?? 1,
|
|
1164
|
+
cpuCores: options.cpuCores ?? 1,
|
|
1165
|
+
memoryMiB: options.memoryMiB ?? 512,
|
|
1166
|
+
storageGiB: options.storageGiB ?? 8,
|
|
1167
|
+
sharing: options.sharing ?? "exclusive",
|
|
1168
|
+
reuse: options.reuse ?? "require",
|
|
1169
|
+
profile: options.profile ?? "app"
|
|
1170
|
+
};
|
|
1171
|
+
if (!/^[a-z][a-z0-9-]{0,62}$/.test(result.profile))
|
|
1172
|
+
throw new Error("deployment profile must be a lowercase typed name");
|
|
1173
|
+
if (!Number.isSafeInteger(result.replicas) || result.replicas < 1 || result.replicas > 1024)
|
|
1174
|
+
throw new Error("deployment replicas must be an integer from 1 to 1024");
|
|
1175
|
+
if (!Number.isSafeInteger(result.cpuCores) || result.cpuCores < 1 || result.cpuCores > 1024)
|
|
1176
|
+
throw new Error("deployment CPU cores must be an integer from 1 to 1024");
|
|
1177
|
+
if (!Number.isSafeInteger(result.memoryMiB) || result.memoryMiB < 128 || result.memoryMiB > 4194304)
|
|
1178
|
+
throw new Error("deployment memory must be an integer from 128 to 4194304 MiB");
|
|
1179
|
+
if (!Number.isSafeInteger(result.storageGiB) || result.storageGiB < 1 || result.storageGiB > 1048576)
|
|
1180
|
+
throw new Error("deployment storage must be an integer from 1 to 1048576 GiB");
|
|
1181
|
+
if (runtime === "kata-snp" && isolation !== "sev-snp")
|
|
1182
|
+
throw new Error("kata-snp requires sev-snp isolation");
|
|
1183
|
+
if (runtime === "containerd" && isolation === "sev-snp")
|
|
1184
|
+
throw new Error("containerd runc cannot provide sev-snp; use kata-snp");
|
|
1185
|
+
if (isolation === "sev-snp" && operatingSystem !== "ubuntu-26.04")
|
|
1186
|
+
throw new Error("sev-snp requires ubuntu-26.04");
|
|
1187
|
+
if (runtime === "native" && result.sharing !== "exclusive")
|
|
1188
|
+
throw new Error("native deployments require exclusive compute");
|
|
1189
|
+
return result;
|
|
1190
|
+
}
|
|
1191
|
+
function defaultTypeScriptDeployment(name, options = {}) {
|
|
1192
|
+
const selected = initCoordinates(options);
|
|
1193
|
+
const execution = selected.runtime === "native" ? "native" : selected.runtime === "containerd" ? "oci-runc" : "oci-kata-qemu-snp";
|
|
1194
|
+
const hostPackageVersion = selected.operatingSystem === "ubuntu-24.04" ? "ubuntu-24.04" : "ubuntu-26.04";
|
|
1195
|
+
const requirementBlock = selected.runtime === "native" ? `bun: providers.bun.require()` : selected.runtime === "containerd" ? `containerd: providers.containerd.require({ version: '${hostPackageVersion}' }),
|
|
1196
|
+
nginx: providers.nginx.require({ version: '${hostPackageVersion}' })` : `containerd: providers.containerd.require(),
|
|
1197
|
+
kata: providers.kata.require(),
|
|
1198
|
+
nginx: providers.nginx.require()`;
|
|
1199
|
+
const componentBlock = selected.runtime === "native" ? `
|
|
1174
1200
|
app: application({
|
|
1175
1201
|
target: 'app',
|
|
1176
1202
|
runtime: { kind: 'native', provider: 'forgezero.bun', requirement: 'bun', argv: ['/usr/local/bin/bun', 'run', 'start'] },
|
|
1177
1203
|
service: { protocol: 'http', port: 3000, health: { protocol: 'http', method: 'GET', path: '/health', expectedStatus: [200], timeoutMs: 5_000 } },
|
|
1178
1204
|
resources: {},
|
|
1179
1205
|
rollout: { strategy: 'direct' }
|
|
1180
|
-
})
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1206
|
+
})` : `
|
|
1207
|
+
app: application({
|
|
1208
|
+
target: 'app',
|
|
1209
|
+
runtime: {
|
|
1210
|
+
kind: 'container', provider: '${selected.runtime === "containerd" ? "forgezero.containerd" : "forgezero.kata"}', requirement: '${selected.runtime === "containerd" ? "containerd" : "kata"}', runtimeClass: '${selected.runtime === "containerd" ? "runc" : "kata-qemu-snp"}',
|
|
1211
|
+
image: { source: { kind: 'build', context: '.', dockerfile: 'Dockerfile' } },
|
|
1212
|
+
security: { privileged: false, noNewPrivileges: true, root: 'read-only', dropCapabilities: ['ALL'] }
|
|
1213
|
+
},
|
|
1214
|
+
service: { protocol: 'http', port: 3000, health: { protocol: 'http', method: 'GET', path: '/health', expectedStatus: [200], timeoutMs: 5_000 } },
|
|
1215
|
+
resources: { cpu: { limit: ${selected.cpuCores} }, memory: { limitMiB: ${selected.memoryMiB}, swap: 'disabled' }, pids: { limit: 256 } },
|
|
1216
|
+
storage: [{ class: 'ephemeral', path: '/tmp', type: 'tmpfs', sizeMiB: ${Math.min(128, Math.max(16, Math.floor(selected.memoryMiB / 4)))} }],
|
|
1217
|
+
network: { ingress: { exposure: 'loopback', stablePort: 3000 }, container: { mode: 'bridge', network: 'app' } },
|
|
1218
|
+
rollout: { strategy: 'blue-green', proxy: 'nginx', drainMs: 30_000, automaticRollback: true }
|
|
1219
|
+
})`;
|
|
1220
|
+
const workflowBlock = selected.runtime === "native" ? `
|
|
1185
1221
|
build: stage({ strategy: { mode: 'sequential' }, steps: {
|
|
1186
1222
|
build: actions.exec.argv(
|
|
1187
1223
|
{ component: 'app', argv: ['/usr/local/bin/fz-agent', 'pipeline-todo', 'replace with the project build argv'] },
|
|
@@ -1196,7 +1232,42 @@ export default defineDeployment({
|
|
|
1196
1232
|
} }),
|
|
1197
1233
|
verify: stage({ dependsOn: ['release'], strategy: { mode: 'parallel' }, steps: {
|
|
1198
1234
|
health: actions.service.health({ component: 'app' }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 30_000 })
|
|
1199
|
-
} })
|
|
1235
|
+
} })` : `
|
|
1236
|
+
prepare: stage({ strategy: { mode: 'sequential' }, steps: {
|
|
1237
|
+
software: actions.software.ensure({ requirements: [${selected.runtime === "containerd" ? "'containerd', 'nginx'" : "'containerd', 'kata', 'nginx'"}] }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 900_000 }),
|
|
1238
|
+
review: actions.exec.argv({ component: 'app', argv: ['/usr/local/bin/fz-agent', 'pipeline-todo', 'review the Dockerfile and container entrypoint'] }, { scope: { kind: 'release-executor' } })
|
|
1239
|
+
} }),
|
|
1240
|
+
build: stage({ dependsOn: ['prepare'], strategy: { mode: 'sequential' }, steps: {
|
|
1241
|
+
image: actions.container.build({ component: 'app' }, { scope: { kind: 'release-executor' }, timeoutMs: 900_000 })
|
|
1242
|
+
} }),
|
|
1243
|
+
release: stage({ dependsOn: ['build'], strategy: { mode: 'blue-green', maximumConcurrency: 1, minimumHealthy: 1 }, steps: {
|
|
1244
|
+
promote: actions.service.promote({ component: 'app', imageDigest: { $ref: 'steps.image.outputs.digest' } }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 180_000 })
|
|
1245
|
+
} }),
|
|
1246
|
+
verify: stage({ dependsOn: ['release'], strategy: { mode: 'parallel' }, steps: {
|
|
1247
|
+
health: actions.service.health({ component: 'app' }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 30_000 })
|
|
1248
|
+
} })`;
|
|
1249
|
+
return `import { actions, application, defineDeployment, providers, stage, target, workflow } from '@forgezero/agent/deploy';
|
|
1250
|
+
|
|
1251
|
+
export default defineDeployment({
|
|
1252
|
+
apiVersion: 'deploy.forgezero.net/v1',
|
|
1253
|
+
kind: 'Deployment',
|
|
1254
|
+
metadata: { name: '${safeName(name)}' },
|
|
1255
|
+
spec: {
|
|
1256
|
+
security: { attestation: '${selected.isolation === "sev-snp" ? "required" : "preferred"}' },
|
|
1257
|
+
targets: {
|
|
1258
|
+
app: target.compute({
|
|
1259
|
+
profiles: ['${selected.profile}'], replicas: ${selected.replicas},
|
|
1260
|
+
resources: { cpuCores: ${selected.cpuCores}, memoryMiB: ${selected.memoryMiB}, storageGiB: ${selected.storageGiB} },
|
|
1261
|
+
os: '${selected.operatingSystem}', runtime: '${execution}', isolation: '${selected.isolation}',
|
|
1262
|
+
sharing: '${selected.sharing}', reuse: '${selected.reuse}'
|
|
1263
|
+
})
|
|
1264
|
+
},
|
|
1265
|
+
requirements: { ${requirementBlock} },
|
|
1266
|
+
components: {${componentBlock}
|
|
1267
|
+
},
|
|
1268
|
+
workflows: {
|
|
1269
|
+
deploy: workflow({
|
|
1270
|
+
stages: {${workflowBlock}
|
|
1200
1271
|
}
|
|
1201
1272
|
})
|
|
1202
1273
|
}
|
|
@@ -1208,7 +1279,7 @@ function initializeTypeScriptDeployment(rootValue, options) {
|
|
|
1208
1279
|
const path = localPath(rootValue, DEPLOY_SOURCE_FILE, "deployment source");
|
|
1209
1280
|
if (existsSync(path) && !options.force)
|
|
1210
1281
|
throw new Error(`${DEPLOY_SOURCE_FILE} already exists; use --force only when replacing it deliberately`);
|
|
1211
|
-
writeFileSync(path, defaultTypeScriptDeployment(options.name), { mode: 420, flag: options.force ? "w" : "wx" });
|
|
1282
|
+
writeFileSync(path, defaultTypeScriptDeployment(options.name, options), { mode: 420, flag: options.force ? "w" : "wx" });
|
|
1212
1283
|
return path;
|
|
1213
1284
|
}
|
|
1214
1285
|
function inside(root, path) {
|
|
@@ -1229,10 +1300,35 @@ async function loadDeploymentSource(root, sourceFile = DEPLOY_SOURCE_FILE) {
|
|
|
1229
1300
|
const status = lstatSync(source);
|
|
1230
1301
|
if (!status.isFile() || status.isSymbolicLink() || status.size > 2 * 1024 * 1024)
|
|
1231
1302
|
throw new Error("deployment source must be one bounded regular file");
|
|
1232
|
-
const
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1303
|
+
const builtDeploy = new URL("./deploy.js", import.meta.url);
|
|
1304
|
+
const sourceDeploy = new URL("./deploy.ts", import.meta.url);
|
|
1305
|
+
const deployModule = existsSync(fileURLToPath(builtDeploy)) ? builtDeploy.href : sourceDeploy.href;
|
|
1306
|
+
const result = await Bun.build({
|
|
1307
|
+
entrypoints: [source],
|
|
1308
|
+
target: "bun",
|
|
1309
|
+
format: "esm",
|
|
1310
|
+
minify: false,
|
|
1311
|
+
plugins: [{
|
|
1312
|
+
name: "forgezero-deploy-authoring",
|
|
1313
|
+
setup(builder) {
|
|
1314
|
+
builder.onResolve({ filter: /^@forgezero\/agent\/deploy$/ }, () => ({ path: fileURLToPath(deployModule) }));
|
|
1315
|
+
}
|
|
1316
|
+
}]
|
|
1317
|
+
});
|
|
1318
|
+
if (!result.success || result.outputs.length !== 1) {
|
|
1319
|
+
throw new Error(`deployment source compilation failed: ${result.logs.map((entry) => entry.message).join("; ")}`);
|
|
1320
|
+
}
|
|
1321
|
+
const directory = mkdtempSync(join(tmpdir(), "forgezero-deploy-compile-"));
|
|
1322
|
+
const compiled = join(directory, "deployment.mjs");
|
|
1323
|
+
try {
|
|
1324
|
+
writeFileSync(compiled, Buffer.from(await result.outputs[0].arrayBuffer()), { mode: 384, flag: "wx" });
|
|
1325
|
+
const module = await import(`${pathToFileURL(compiled).href}?forgezero=${status.mtimeMs}`);
|
|
1326
|
+
if (module.default === undefined)
|
|
1327
|
+
throw new Error(`${sourceFile} must export one default deployment definition`);
|
|
1328
|
+
return module.default;
|
|
1329
|
+
} finally {
|
|
1330
|
+
rmSync(directory, { recursive: true, force: true });
|
|
1331
|
+
}
|
|
1236
1332
|
}
|
|
1237
1333
|
async function compileDeploymentProject(rootValue, options = {}) {
|
|
1238
1334
|
const root = resolve(rootValue);
|
package/dist/fz-agent.js
CHANGED
package/dist/fz.js
CHANGED
|
@@ -4634,7 +4634,7 @@ var VaultError, runtimeEnvironment = () => typeof process !== "undefined" && pro
|
|
|
4634
4634
|
} catch {
|
|
4635
4635
|
return;
|
|
4636
4636
|
}
|
|
4637
|
-
}, VERSION = "0.1.
|
|
4637
|
+
}, VERSION = "0.1.16";
|
|
4638
4638
|
var init_dist = __esm(() => {
|
|
4639
4639
|
init_identity();
|
|
4640
4640
|
VaultError = class VaultError extends Error {
|
|
@@ -4811,7 +4811,7 @@ async function spawnWith(command, env, report = () => {}, options = {}) {
|
|
|
4811
4811
|
|
|
4812
4812
|
// src/cli/index.ts
|
|
4813
4813
|
import { existsSync as existsSync12, lstatSync as lstatSync10, mkdirSync as mkdirSync13, readFileSync as readFileSync16, writeFileSync as writeFileSync13 } from "fs";
|
|
4814
|
-
import { basename as basename3, dirname as dirname14, isAbsolute as isAbsolute7, join as
|
|
4814
|
+
import { basename as basename3, dirname as dirname14, isAbsolute as isAbsolute7, join as join13, resolve as resolve12 } from "path";
|
|
4815
4815
|
|
|
4816
4816
|
// src/process-input.ts
|
|
4817
4817
|
async function writeAndCloseProcessInput(input, value) {
|
|
@@ -4821,7 +4821,7 @@ async function writeAndCloseProcessInput(input, value) {
|
|
|
4821
4821
|
|
|
4822
4822
|
// src/cli/index.ts
|
|
4823
4823
|
init_dist();
|
|
4824
|
-
import { fileURLToPath as
|
|
4824
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
4825
4825
|
import { hostname } from "os";
|
|
4826
4826
|
|
|
4827
4827
|
// src/agent-update.ts
|
|
@@ -4837,7 +4837,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
|
|
|
4837
4837
|
var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
|
|
4838
4838
|
|
|
4839
4839
|
// src/version.ts
|
|
4840
|
-
var VERSION2 = "0.1.
|
|
4840
|
+
var VERSION2 = "0.1.83";
|
|
4841
4841
|
|
|
4842
4842
|
// src/software.ts
|
|
4843
4843
|
var PINNED_BUN_VERSION = "1.3.14";
|
|
@@ -10365,9 +10365,10 @@ function initializeDeployFile(root, options = {}) {
|
|
|
10365
10365
|
}
|
|
10366
10366
|
|
|
10367
10367
|
// src/deploy-compiler.ts
|
|
10368
|
-
import { existsSync as existsSync6, lstatSync as lstatSync2, mkdirSync as mkdirSync6, readFileSync as readFileSync6, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
10369
|
-
import {
|
|
10370
|
-
import {
|
|
10368
|
+
import { existsSync as existsSync6, lstatSync as lstatSync2, mkdirSync as mkdirSync6, mkdtempSync, readFileSync as readFileSync6, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
10369
|
+
import { tmpdir } from "os";
|
|
10370
|
+
import { dirname as dirname5, isAbsolute, join as join5, relative, resolve as resolve4, sep as sep3 } from "path";
|
|
10371
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
10371
10372
|
|
|
10372
10373
|
// src/deploy-plan.ts
|
|
10373
10374
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -11518,37 +11519,72 @@ function safeName2(value) {
|
|
|
11518
11519
|
const result = value.toLowerCase().replace(/^@[^/]+\//, "").replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 63);
|
|
11519
11520
|
return /^[a-z]/.test(result) ? result : `app-${result || "service"}`;
|
|
11520
11521
|
}
|
|
11521
|
-
function
|
|
11522
|
-
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
|
|
11528
|
-
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
|
|
11522
|
+
function initCoordinates(options) {
|
|
11523
|
+
const runtime = options.runtime ?? "native";
|
|
11524
|
+
const isolation = options.isolation ?? (runtime === "kata-snp" ? "sev-snp" : "standard");
|
|
11525
|
+
const operatingSystem = options.operatingSystem ?? (isolation === "sev-snp" ? "ubuntu-26.04" : "ubuntu-24.04");
|
|
11526
|
+
const result = {
|
|
11527
|
+
runtime,
|
|
11528
|
+
isolation,
|
|
11529
|
+
operatingSystem,
|
|
11530
|
+
replicas: options.replicas ?? 1,
|
|
11531
|
+
cpuCores: options.cpuCores ?? 1,
|
|
11532
|
+
memoryMiB: options.memoryMiB ?? 512,
|
|
11533
|
+
storageGiB: options.storageGiB ?? 8,
|
|
11534
|
+
sharing: options.sharing ?? "exclusive",
|
|
11535
|
+
reuse: options.reuse ?? "require",
|
|
11536
|
+
profile: options.profile ?? "app"
|
|
11537
|
+
};
|
|
11538
|
+
if (!/^[a-z][a-z0-9-]{0,62}$/.test(result.profile))
|
|
11539
|
+
throw new Error("deployment profile must be a lowercase typed name");
|
|
11540
|
+
if (!Number.isSafeInteger(result.replicas) || result.replicas < 1 || result.replicas > 1024)
|
|
11541
|
+
throw new Error("deployment replicas must be an integer from 1 to 1024");
|
|
11542
|
+
if (!Number.isSafeInteger(result.cpuCores) || result.cpuCores < 1 || result.cpuCores > 1024)
|
|
11543
|
+
throw new Error("deployment CPU cores must be an integer from 1 to 1024");
|
|
11544
|
+
if (!Number.isSafeInteger(result.memoryMiB) || result.memoryMiB < 128 || result.memoryMiB > 4194304)
|
|
11545
|
+
throw new Error("deployment memory must be an integer from 128 to 4194304 MiB");
|
|
11546
|
+
if (!Number.isSafeInteger(result.storageGiB) || result.storageGiB < 1 || result.storageGiB > 1048576)
|
|
11547
|
+
throw new Error("deployment storage must be an integer from 1 to 1048576 GiB");
|
|
11548
|
+
if (runtime === "kata-snp" && isolation !== "sev-snp")
|
|
11549
|
+
throw new Error("kata-snp requires sev-snp isolation");
|
|
11550
|
+
if (runtime === "containerd" && isolation === "sev-snp")
|
|
11551
|
+
throw new Error("containerd runc cannot provide sev-snp; use kata-snp");
|
|
11552
|
+
if (isolation === "sev-snp" && operatingSystem !== "ubuntu-26.04")
|
|
11553
|
+
throw new Error("sev-snp requires ubuntu-26.04");
|
|
11554
|
+
if (runtime === "native" && result.sharing !== "exclusive")
|
|
11555
|
+
throw new Error("native deployments require exclusive compute");
|
|
11556
|
+
return result;
|
|
11557
|
+
}
|
|
11558
|
+
function defaultTypeScriptDeployment(name, options = {}) {
|
|
11559
|
+
const selected = initCoordinates(options);
|
|
11560
|
+
const execution = selected.runtime === "native" ? "native" : selected.runtime === "containerd" ? "oci-runc" : "oci-kata-qemu-snp";
|
|
11561
|
+
const hostPackageVersion = selected.operatingSystem === "ubuntu-24.04" ? "ubuntu-24.04" : "ubuntu-26.04";
|
|
11562
|
+
const requirementBlock = selected.runtime === "native" ? `bun: providers.bun.require()` : selected.runtime === "containerd" ? `containerd: providers.containerd.require({ version: '${hostPackageVersion}' }),
|
|
11563
|
+
nginx: providers.nginx.require({ version: '${hostPackageVersion}' })` : `containerd: providers.containerd.require(),
|
|
11564
|
+
kata: providers.kata.require(),
|
|
11565
|
+
nginx: providers.nginx.require()`;
|
|
11566
|
+
const componentBlock = selected.runtime === "native" ? `
|
|
11541
11567
|
app: application({
|
|
11542
11568
|
target: 'app',
|
|
11543
11569
|
runtime: { kind: 'native', provider: 'forgezero.bun', requirement: 'bun', argv: ['/usr/local/bin/bun', 'run', 'start'] },
|
|
11544
11570
|
service: { protocol: 'http', port: 3000, health: { protocol: 'http', method: 'GET', path: '/health', expectedStatus: [200], timeoutMs: 5_000 } },
|
|
11545
11571
|
resources: {},
|
|
11546
11572
|
rollout: { strategy: 'direct' }
|
|
11547
|
-
})
|
|
11548
|
-
|
|
11549
|
-
|
|
11550
|
-
|
|
11551
|
-
|
|
11573
|
+
})` : `
|
|
11574
|
+
app: application({
|
|
11575
|
+
target: 'app',
|
|
11576
|
+
runtime: {
|
|
11577
|
+
kind: 'container', provider: '${selected.runtime === "containerd" ? "forgezero.containerd" : "forgezero.kata"}', requirement: '${selected.runtime === "containerd" ? "containerd" : "kata"}', runtimeClass: '${selected.runtime === "containerd" ? "runc" : "kata-qemu-snp"}',
|
|
11578
|
+
image: { source: { kind: 'build', context: '.', dockerfile: 'Dockerfile' } },
|
|
11579
|
+
security: { privileged: false, noNewPrivileges: true, root: 'read-only', dropCapabilities: ['ALL'] }
|
|
11580
|
+
},
|
|
11581
|
+
service: { protocol: 'http', port: 3000, health: { protocol: 'http', method: 'GET', path: '/health', expectedStatus: [200], timeoutMs: 5_000 } },
|
|
11582
|
+
resources: { cpu: { limit: ${selected.cpuCores} }, memory: { limitMiB: ${selected.memoryMiB}, swap: 'disabled' }, pids: { limit: 256 } },
|
|
11583
|
+
storage: [{ class: 'ephemeral', path: '/tmp', type: 'tmpfs', sizeMiB: ${Math.min(128, Math.max(16, Math.floor(selected.memoryMiB / 4)))} }],
|
|
11584
|
+
network: { ingress: { exposure: 'loopback', stablePort: 3000 }, container: { mode: 'bridge', network: 'app' } },
|
|
11585
|
+
rollout: { strategy: 'blue-green', proxy: 'nginx', drainMs: 30_000, automaticRollback: true }
|
|
11586
|
+
})`;
|
|
11587
|
+
const workflowBlock = selected.runtime === "native" ? `
|
|
11552
11588
|
build: stage({ strategy: { mode: 'sequential' }, steps: {
|
|
11553
11589
|
build: actions.exec.argv(
|
|
11554
11590
|
{ component: 'app', argv: ['/usr/local/bin/fz-agent', 'pipeline-todo', 'replace with the project build argv'] },
|
|
@@ -11563,7 +11599,42 @@ export default defineDeployment({
|
|
|
11563
11599
|
} }),
|
|
11564
11600
|
verify: stage({ dependsOn: ['release'], strategy: { mode: 'parallel' }, steps: {
|
|
11565
11601
|
health: actions.service.health({ component: 'app' }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 30_000 })
|
|
11566
|
-
} })
|
|
11602
|
+
} })` : `
|
|
11603
|
+
prepare: stage({ strategy: { mode: 'sequential' }, steps: {
|
|
11604
|
+
software: actions.software.ensure({ requirements: [${selected.runtime === "containerd" ? "'containerd', 'nginx'" : "'containerd', 'kata', 'nginx'"}] }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 900_000 }),
|
|
11605
|
+
review: actions.exec.argv({ component: 'app', argv: ['/usr/local/bin/fz-agent', 'pipeline-todo', 'review the Dockerfile and container entrypoint'] }, { scope: { kind: 'release-executor' } })
|
|
11606
|
+
} }),
|
|
11607
|
+
build: stage({ dependsOn: ['prepare'], strategy: { mode: 'sequential' }, steps: {
|
|
11608
|
+
image: actions.container.build({ component: 'app' }, { scope: { kind: 'release-executor' }, timeoutMs: 900_000 })
|
|
11609
|
+
} }),
|
|
11610
|
+
release: stage({ dependsOn: ['build'], strategy: { mode: 'blue-green', maximumConcurrency: 1, minimumHealthy: 1 }, steps: {
|
|
11611
|
+
promote: actions.service.promote({ component: 'app', imageDigest: { $ref: 'steps.image.outputs.digest' } }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 180_000 })
|
|
11612
|
+
} }),
|
|
11613
|
+
verify: stage({ dependsOn: ['release'], strategy: { mode: 'parallel' }, steps: {
|
|
11614
|
+
health: actions.service.health({ component: 'app' }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 30_000 })
|
|
11615
|
+
} })`;
|
|
11616
|
+
return `import { actions, application, defineDeployment, providers, stage, target, workflow } from '@forgezero/agent/deploy';
|
|
11617
|
+
|
|
11618
|
+
export default defineDeployment({
|
|
11619
|
+
apiVersion: 'deploy.forgezero.net/v1',
|
|
11620
|
+
kind: 'Deployment',
|
|
11621
|
+
metadata: { name: '${safeName2(name)}' },
|
|
11622
|
+
spec: {
|
|
11623
|
+
security: { attestation: '${selected.isolation === "sev-snp" ? "required" : "preferred"}' },
|
|
11624
|
+
targets: {
|
|
11625
|
+
app: target.compute({
|
|
11626
|
+
profiles: ['${selected.profile}'], replicas: ${selected.replicas},
|
|
11627
|
+
resources: { cpuCores: ${selected.cpuCores}, memoryMiB: ${selected.memoryMiB}, storageGiB: ${selected.storageGiB} },
|
|
11628
|
+
os: '${selected.operatingSystem}', runtime: '${execution}', isolation: '${selected.isolation}',
|
|
11629
|
+
sharing: '${selected.sharing}', reuse: '${selected.reuse}'
|
|
11630
|
+
})
|
|
11631
|
+
},
|
|
11632
|
+
requirements: { ${requirementBlock} },
|
|
11633
|
+
components: {${componentBlock}
|
|
11634
|
+
},
|
|
11635
|
+
workflows: {
|
|
11636
|
+
deploy: workflow({
|
|
11637
|
+
stages: {${workflowBlock}
|
|
11567
11638
|
}
|
|
11568
11639
|
})
|
|
11569
11640
|
}
|
|
@@ -11575,7 +11646,7 @@ function initializeTypeScriptDeployment(rootValue, options) {
|
|
|
11575
11646
|
const path = localPath(rootValue, DEPLOY_SOURCE_FILE, "deployment source");
|
|
11576
11647
|
if (existsSync6(path) && !options.force)
|
|
11577
11648
|
throw new Error(`${DEPLOY_SOURCE_FILE} already exists; use --force only when replacing it deliberately`);
|
|
11578
|
-
writeFileSync6(path, defaultTypeScriptDeployment(options.name), { mode: 420, flag: options.force ? "w" : "wx" });
|
|
11649
|
+
writeFileSync6(path, defaultTypeScriptDeployment(options.name, options), { mode: 420, flag: options.force ? "w" : "wx" });
|
|
11579
11650
|
return path;
|
|
11580
11651
|
}
|
|
11581
11652
|
function inside(root, path) {
|
|
@@ -11596,10 +11667,35 @@ async function loadDeploymentSource(root, sourceFile = DEPLOY_SOURCE_FILE) {
|
|
|
11596
11667
|
const status = lstatSync2(source);
|
|
11597
11668
|
if (!status.isFile() || status.isSymbolicLink() || status.size > 2 * 1024 * 1024)
|
|
11598
11669
|
throw new Error("deployment source must be one bounded regular file");
|
|
11599
|
-
const
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
|
|
11670
|
+
const builtDeploy = new URL("./deploy.js", import.meta.url);
|
|
11671
|
+
const sourceDeploy = new URL("./deploy.ts", import.meta.url);
|
|
11672
|
+
const deployModule = existsSync6(fileURLToPath(builtDeploy)) ? builtDeploy.href : sourceDeploy.href;
|
|
11673
|
+
const result = await Bun.build({
|
|
11674
|
+
entrypoints: [source],
|
|
11675
|
+
target: "bun",
|
|
11676
|
+
format: "esm",
|
|
11677
|
+
minify: false,
|
|
11678
|
+
plugins: [{
|
|
11679
|
+
name: "forgezero-deploy-authoring",
|
|
11680
|
+
setup(builder) {
|
|
11681
|
+
builder.onResolve({ filter: /^@forgezero\/agent\/deploy$/ }, () => ({ path: fileURLToPath(deployModule) }));
|
|
11682
|
+
}
|
|
11683
|
+
}]
|
|
11684
|
+
});
|
|
11685
|
+
if (!result.success || result.outputs.length !== 1) {
|
|
11686
|
+
throw new Error(`deployment source compilation failed: ${result.logs.map((entry) => entry.message).join("; ")}`);
|
|
11687
|
+
}
|
|
11688
|
+
const directory = mkdtempSync(join5(tmpdir(), "forgezero-deploy-compile-"));
|
|
11689
|
+
const compiled = join5(directory, "deployment.mjs");
|
|
11690
|
+
try {
|
|
11691
|
+
writeFileSync6(compiled, Buffer.from(await result.outputs[0].arrayBuffer()), { mode: 384, flag: "wx" });
|
|
11692
|
+
const module = await import(`${pathToFileURL(compiled).href}?forgezero=${status.mtimeMs}`);
|
|
11693
|
+
if (module.default === undefined)
|
|
11694
|
+
throw new Error(`${sourceFile} must export one default deployment definition`);
|
|
11695
|
+
return module.default;
|
|
11696
|
+
} finally {
|
|
11697
|
+
rmSync4(directory, { recursive: true, force: true });
|
|
11698
|
+
}
|
|
11603
11699
|
}
|
|
11604
11700
|
async function compileDeploymentProject(rootValue, options = {}) {
|
|
11605
11701
|
const root = resolve4(rootValue);
|
|
@@ -11733,7 +11829,7 @@ import {
|
|
|
11733
11829
|
unlinkSync,
|
|
11734
11830
|
writeFileSync as writeFileSync7
|
|
11735
11831
|
} from "fs";
|
|
11736
|
-
import { dirname as dirname6, join as
|
|
11832
|
+
import { dirname as dirname6, join as join6 } from "path";
|
|
11737
11833
|
import { homedir } from "os";
|
|
11738
11834
|
var EMPTY2 = () => ({ version: 1, sessions: {} });
|
|
11739
11835
|
function canonicalApi(value) {
|
|
@@ -11751,8 +11847,8 @@ ${realm.trim() || "platform"}`).toString("base64url");
|
|
|
11751
11847
|
function defaultSessionPath(env = process.env) {
|
|
11752
11848
|
if (env.FZ_SESSION_FILE?.trim())
|
|
11753
11849
|
return env.FZ_SESSION_FILE.trim();
|
|
11754
|
-
const state = env.XDG_STATE_HOME?.trim() ||
|
|
11755
|
-
return
|
|
11850
|
+
const state = env.XDG_STATE_HOME?.trim() || join6(homedir(), ".local", "state");
|
|
11851
|
+
return join6(state, "forgezero", "sessions.json");
|
|
11756
11852
|
}
|
|
11757
11853
|
function assertPrivate(path, kind) {
|
|
11758
11854
|
if (!existsSync7(path))
|
|
@@ -11851,7 +11947,7 @@ import {
|
|
|
11851
11947
|
writeFileSync as writeFileSync9
|
|
11852
11948
|
} from "fs";
|
|
11853
11949
|
import { dirname as dirname9 } from "path";
|
|
11854
|
-
import { fileURLToPath } from "url";
|
|
11950
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
11855
11951
|
|
|
11856
11952
|
// src/ubuntu.ts
|
|
11857
11953
|
var current = OS_CATALOG[0];
|
|
@@ -12427,7 +12523,7 @@ function planLocalOtlpProof(endpoint, collectorUnit) {
|
|
|
12427
12523
|
import { constants } from "fs";
|
|
12428
12524
|
import { createHmac, randomUUID } from "crypto";
|
|
12429
12525
|
import { chmod, lstat, mkdir, open, readdir, rename, rmdir, stat, unlink } from "fs/promises";
|
|
12430
|
-
import { dirname as dirname7, join as
|
|
12526
|
+
import { dirname as dirname7, join as join7, resolve as resolve5 } from "path";
|
|
12431
12527
|
import { isIP as isIP3 } from "net";
|
|
12432
12528
|
|
|
12433
12529
|
// src/cloudflare-edge.ts
|
|
@@ -13099,7 +13195,7 @@ function cloudflareHostHandoffPath(checkpointPath, nodeName) {
|
|
|
13099
13195
|
const normalized = nodeName.trim().toLowerCase();
|
|
13100
13196
|
if (!/^[a-z0-9][a-z0-9-]{0,62}$/.test(normalized))
|
|
13101
13197
|
throw new Error("Cloudflare host handoff node name is invalid");
|
|
13102
|
-
return
|
|
13198
|
+
return join7(`${resolve5(checkpointPath)}.hosts`, `${normalized}.json`);
|
|
13103
13199
|
}
|
|
13104
13200
|
async function readCloudflareHostHandoff(handoffPath, nodeName) {
|
|
13105
13201
|
let parsed;
|
|
@@ -13481,7 +13577,7 @@ async function removeCloudflareBootstrapSecrets(checkpointPath, output) {
|
|
|
13481
13577
|
throw new Error("Cloudflare host handoff directory contains unexpected files; refusing secret cleanup");
|
|
13482
13578
|
}
|
|
13483
13579
|
for (const name of expected)
|
|
13484
|
-
await unlink(
|
|
13580
|
+
await unlink(join7(directory, name));
|
|
13485
13581
|
await rmdir(directory);
|
|
13486
13582
|
}
|
|
13487
13583
|
await unlink(resolve5(checkpointPath));
|
|
@@ -13923,7 +14019,7 @@ var LIFECYCLE_PROFILE = "/etc/forgezero/lifecycle.json";
|
|
|
13923
14019
|
var CONTROL_SOCKET = "/run/forgezero/control.sock";
|
|
13924
14020
|
var CLOUDFLARED_METRICS_ADDRESS = "127.0.0.1:20241";
|
|
13925
14021
|
var CLOUDFLARED_DIAGNOSTICS_URL = `http://${CLOUDFLARED_METRICS_ADDRESS}/diag/tunnel`;
|
|
13926
|
-
var PACKAGED_AGENT_BIN =
|
|
14022
|
+
var PACKAGED_AGENT_BIN = fileURLToPath2(new URL("./fz-agent.js", import.meta.url));
|
|
13927
14023
|
var privateOrigin = (value) => {
|
|
13928
14024
|
let url;
|
|
13929
14025
|
try {
|
|
@@ -14150,7 +14246,7 @@ var unitEscape = (value) => {
|
|
|
14150
14246
|
};
|
|
14151
14247
|
function databaseUnit(config) {
|
|
14152
14248
|
const db = config.database;
|
|
14153
|
-
const
|
|
14249
|
+
const join8 = db.role === "joiner" ? ` --starter.join=${unitEscape(db.master)}` : "";
|
|
14154
14250
|
const agency = db.agency === "none" ? " --cluster.start-agent=false --cluster.start-coordinator=true --cluster.start-dbserver=true" : "";
|
|
14155
14251
|
return `[Unit]
|
|
14156
14252
|
Description=ForgeZero ArangoDB Community 3.11.14 cluster (${db.role}; agency=${db.agency})
|
|
@@ -14162,7 +14258,7 @@ Type=simple
|
|
|
14162
14258
|
User=arangodb
|
|
14163
14259
|
Group=arangodb
|
|
14164
14260
|
LoadCredentialEncrypted=arangodb-jwt:${JWT_CREDENTIAL}
|
|
14165
|
-
ExecStart=/usr/bin/arangodb --starter.mode=cluster --starter.address=${unitEscape(db.address)} --starter.host=${unitEscape(db.address)} --starter.data-dir=/var/lib/forgezero-cluster --auth.jwt-secret=%d/arangodb-jwt${
|
|
14261
|
+
ExecStart=/usr/bin/arangodb --starter.mode=cluster --starter.address=${unitEscape(db.address)} --starter.host=${unitEscape(db.address)} --starter.data-dir=/var/lib/forgezero-cluster --auth.jwt-secret=%d/arangodb-jwt${join8}${agency}
|
|
14166
14262
|
Restart=always
|
|
14167
14263
|
RestartSec=5
|
|
14168
14264
|
UMask=0077
|
|
@@ -15489,11 +15585,11 @@ async function runCloudflareBootstrapFinalizeCommand(configPath, dependencies =
|
|
|
15489
15585
|
|
|
15490
15586
|
// src/operator-bootstrap.ts
|
|
15491
15587
|
import { createHash as createHash6, randomBytes as randomBytes9 } from "crypto";
|
|
15492
|
-
import { chmodSync as chmodSync6, lstatSync as lstatSync7, mkdirSync as mkdirSync12, mkdtempSync, readFileSync as readFileSync12, rmSync as rmSync7, writeFileSync as writeFileSync12 } from "fs";
|
|
15588
|
+
import { chmodSync as chmodSync6, lstatSync as lstatSync7, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync2, readFileSync as readFileSync12, rmSync as rmSync7, writeFileSync as writeFileSync12 } from "fs";
|
|
15493
15589
|
import { isIP as isIP6 } from "net";
|
|
15494
|
-
import { tmpdir } from "os";
|
|
15495
|
-
import { basename as basename2, dirname as dirname13, isAbsolute as isAbsolute5, join as
|
|
15496
|
-
import { fileURLToPath as
|
|
15590
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
15591
|
+
import { basename as basename2, dirname as dirname13, isAbsolute as isAbsolute5, join as join11, resolve as resolve9 } from "path";
|
|
15592
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
15497
15593
|
|
|
15498
15594
|
// src/metal-bootstrap.ts
|
|
15499
15595
|
import { createHash as createHash5, randomBytes as randomBytes8 } from "crypto";
|
|
@@ -15512,15 +15608,15 @@ import {
|
|
|
15512
15608
|
unlinkSync as unlinkSync2,
|
|
15513
15609
|
writeFileSync as writeFileSync11
|
|
15514
15610
|
} from "fs";
|
|
15515
|
-
import { dirname as dirname12, isAbsolute as isAbsolute4, join as
|
|
15611
|
+
import { dirname as dirname12, isAbsolute as isAbsolute4, join as join10, resolve as resolve8 } from "path";
|
|
15516
15612
|
import { isIP as isIP5 } from "net";
|
|
15517
15613
|
|
|
15518
15614
|
// src/metal-isolation.ts
|
|
15519
15615
|
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
15520
|
-
import { join as
|
|
15616
|
+
import { join as join9 } from "path";
|
|
15521
15617
|
|
|
15522
15618
|
// src/metal-provision.ts
|
|
15523
|
-
import { dirname as dirname11, isAbsolute as isAbsolute3, join as
|
|
15619
|
+
import { dirname as dirname11, isAbsolute as isAbsolute3, join as join8 } from "path";
|
|
15524
15620
|
import { isIP as isIP4 } from "net";
|
|
15525
15621
|
var SAFE_NAME2 = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,63}$/;
|
|
15526
15622
|
var DEVICE = /^[a-zA-Z][a-zA-Z0-9_.-]{0,14}$/;
|
|
@@ -15716,15 +15812,15 @@ async function applyMetalIsolation(profile, exec = defaultExec) {
|
|
|
15716
15812
|
await requireGuestsInSlice(exec);
|
|
15717
15813
|
const unitDir = profile.unitDir;
|
|
15718
15814
|
mkdirSync10(unitDir, { recursive: true });
|
|
15719
|
-
writeFileSync10(
|
|
15815
|
+
writeFileSync10(join9(unitDir, "forgezero-guests.slice"), metalGuestSliceUnit(profile), { mode: 420 });
|
|
15720
15816
|
for (const unit of ["system.slice", "user.slice"]) {
|
|
15721
|
-
const directory =
|
|
15817
|
+
const directory = join9(unitDir, `${unit}.d`);
|
|
15722
15818
|
mkdirSync10(directory, { recursive: true });
|
|
15723
|
-
writeFileSync10(
|
|
15819
|
+
writeFileSync10(join9(directory, "50-forgezero-housekeeping.conf"), metalHousekeepingDropIn(profile, "slice"), { mode: 420 });
|
|
15724
15820
|
}
|
|
15725
|
-
const initDirectory =
|
|
15821
|
+
const initDirectory = join9(unitDir, "init.scope.d");
|
|
15726
15822
|
mkdirSync10(initDirectory, { recursive: true });
|
|
15727
|
-
writeFileSync10(
|
|
15823
|
+
writeFileSync10(join9(initDirectory, "50-forgezero-housekeeping.conf"), metalHousekeepingDropIn(profile, "scope"), { mode: 420 });
|
|
15728
15824
|
await checked4(exec, ["systemctl", "daemon-reload"]);
|
|
15729
15825
|
await requireGuestsInSlice(exec);
|
|
15730
15826
|
const properties = [`AllowedCPUs=${profile.housekeepingCpus}`];
|
|
@@ -16104,9 +16200,9 @@ var installAgentBinary = (source, version) => {
|
|
|
16104
16200
|
validateAgentSourcePath(source);
|
|
16105
16201
|
const release = `/opt/forgezero/agent/versions/${version}/dist`;
|
|
16106
16202
|
mkdirSync11(release, { recursive: true, mode: 493 });
|
|
16107
|
-
copyFileSync2(source,
|
|
16108
|
-
chmodSync5(
|
|
16109
|
-
chownSync(
|
|
16203
|
+
copyFileSync2(source, join10(release, "fz-agent.js"));
|
|
16204
|
+
chmodSync5(join10(release, "fz-agent.js"), 493);
|
|
16205
|
+
chownSync(join10(release, "fz-agent.js"), 0, 0);
|
|
16110
16206
|
mkdirSync11("/opt/forgezero/agent", { recursive: true, mode: 493 });
|
|
16111
16207
|
for (const [link, target] of [
|
|
16112
16208
|
["/opt/forgezero/agent/current.next", `versions/${version}`],
|
|
@@ -16256,7 +16352,7 @@ async function applyMetalBootstrap(config, options) {
|
|
|
16256
16352
|
}
|
|
16257
16353
|
await applyMetalIsolation(config.profile, (argv2) => exec(argv2));
|
|
16258
16354
|
for (const [unit, body] of Object.entries(renderMetalUnits(config)))
|
|
16259
|
-
atomicWrite2(
|
|
16355
|
+
atomicWrite2(join10(UNIT_DIRECTORY, unit), body, 420);
|
|
16260
16356
|
await runChecked(exec, ["/usr/bin/systemctl", "daemon-reload"]);
|
|
16261
16357
|
await runChecked(exec, [
|
|
16262
16358
|
"/usr/bin/systemctl",
|
|
@@ -16390,7 +16486,7 @@ async function metalBootstrapStatus(exec = defaultExec2) {
|
|
|
16390
16486
|
"forgezero-metal-agent-egress.service",
|
|
16391
16487
|
"forgezero-metal-agent.service"
|
|
16392
16488
|
]) {
|
|
16393
|
-
if (!existsSync10(
|
|
16489
|
+
if (!existsSync10(join10(UNIT_DIRECTORY, unit)))
|
|
16394
16490
|
units[unit] = "missing";
|
|
16395
16491
|
else
|
|
16396
16492
|
units[unit] = (await exec(["/usr/bin/systemctl", "is-active", "--quiet", unit])).exitCode === 0 ? "active" : "inactive";
|
|
@@ -16919,7 +17015,7 @@ function writeKnownHosts(request, directory) {
|
|
|
16919
17015
|
const lines = [`fz-operator-target ${request.target.hostKey}`];
|
|
16920
17016
|
if (request.target.jump)
|
|
16921
17017
|
lines.push(`${hostLabel(request.target.jump.address, request.target.jump.port)} ${request.target.jump.hostKey}`);
|
|
16922
|
-
const path =
|
|
17018
|
+
const path = join11(directory, "known_hosts");
|
|
16923
17019
|
writeFileSync12(path, `${lines.join(`
|
|
16924
17020
|
`)}
|
|
16925
17021
|
`, { mode: 384, flag: "wx" });
|
|
@@ -17009,7 +17105,7 @@ async function collectOperatorGuestHostKeys(request, options = {}, includeRehear
|
|
|
17009
17105
|
publicIdentity(request.target.identityPublicKeyFile);
|
|
17010
17106
|
socketPath(request.target.agentSocket);
|
|
17011
17107
|
const exec = options.exec ?? defaultExec3;
|
|
17012
|
-
const directory =
|
|
17108
|
+
const directory = mkdtempSync2(join11(tmpdir2(), "forgezero-operator-host-keys-"));
|
|
17013
17109
|
try {
|
|
17014
17110
|
const knownHosts = writeKnownHosts(request, directory);
|
|
17015
17111
|
const nodes = [];
|
|
@@ -17045,7 +17141,7 @@ async function stageConfig(config, directory) {
|
|
|
17045
17141
|
const staged = [];
|
|
17046
17142
|
for (const [name, source] of secretSources(config)) {
|
|
17047
17143
|
const bytes = ownerFile(source, SECRET_LIMIT, name);
|
|
17048
|
-
const local =
|
|
17144
|
+
const local = join11(directory, name);
|
|
17049
17145
|
writeFileSync12(local, bytes, { mode: 384, flag: "wx" });
|
|
17050
17146
|
staged.push(name);
|
|
17051
17147
|
const remotePath = `${REMOTE_STAGE}/${name}`;
|
|
@@ -17065,7 +17161,7 @@ async function stageConfig(config, directory) {
|
|
|
17065
17161
|
manifestFile: `${REMOTE_STAGE}/bootstrap-api.bundle.json`,
|
|
17066
17162
|
branch: bundle.manifest.branch
|
|
17067
17163
|
};
|
|
17068
|
-
const path =
|
|
17164
|
+
const path = join11(directory, "platform-config.json");
|
|
17069
17165
|
writeFileSync12(path, `${JSON.stringify(rewritten, null, 2)}
|
|
17070
17166
|
`, { mode: 384, flag: "wx" });
|
|
17071
17167
|
return { path, files: staged, bundleFiles };
|
|
@@ -17075,14 +17171,14 @@ function stageMetalConfig(config, directory) {
|
|
|
17075
17171
|
const files = [];
|
|
17076
17172
|
if (config.agentSeedFile) {
|
|
17077
17173
|
const name = "metal-agent-seed";
|
|
17078
|
-
writeFileSync12(
|
|
17174
|
+
writeFileSync12(join11(directory, name), ownerFile(config.agentSeedFile, SECRET_LIMIT, name), {
|
|
17079
17175
|
mode: 384,
|
|
17080
17176
|
flag: "wx"
|
|
17081
17177
|
});
|
|
17082
17178
|
rewritten.agentSeedFile = `${REMOTE_STAGE}/${name}`;
|
|
17083
17179
|
files.push(name);
|
|
17084
17180
|
}
|
|
17085
|
-
const path =
|
|
17181
|
+
const path = join11(directory, "metal-config.json");
|
|
17086
17182
|
writeFileSync12(path, `${JSON.stringify(rewritten, null, 2)}
|
|
17087
17183
|
`, { mode: 384, flag: "wx" });
|
|
17088
17184
|
return { path, files };
|
|
@@ -17094,15 +17190,15 @@ async function verifiedBunArchive(directory, fetcher) {
|
|
|
17094
17190
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
17095
17191
|
if (createHash6("sha256").update(bytes).digest("hex") !== BUN_RELEASE_SHA256)
|
|
17096
17192
|
throw new Error("pinned Bun checksum mismatch");
|
|
17097
|
-
const path =
|
|
17193
|
+
const path = join11(directory, "bun.zip");
|
|
17098
17194
|
writeFileSync12(path, bytes, { mode: 384, flag: "wx" });
|
|
17099
17195
|
return path;
|
|
17100
17196
|
}
|
|
17101
17197
|
async function installPackagedAgent(request, knownHosts, exec, directory, remoteTemp, options) {
|
|
17102
17198
|
const artifacts = [
|
|
17103
|
-
[options.fzCliPath ??
|
|
17104
|
-
[options.fzAgentPath ??
|
|
17105
|
-
[options.fzGitSshPath ??
|
|
17199
|
+
[options.fzCliPath ?? fileURLToPath3(new URL("./fz.js", import.meta.url)), "fz.js"],
|
|
17200
|
+
[options.fzAgentPath ?? fileURLToPath3(new URL("./fz-agent.js", import.meta.url)), "fz-agent.js"],
|
|
17201
|
+
[options.fzGitSshPath ?? fileURLToPath3(new URL("./fz-git-ssh.js", import.meta.url)), "fz-git-ssh.js"]
|
|
17106
17202
|
];
|
|
17107
17203
|
for (const [artifact] of artifacts) {
|
|
17108
17204
|
if (!readFileSync12(artifact).length)
|
|
@@ -17145,7 +17241,7 @@ async function applyOperatorPlatformBootstrap(request, mode, options = {}) {
|
|
|
17145
17241
|
publicIdentity(request.target.identityPublicKeyFile);
|
|
17146
17242
|
socketPath(request.target.agentSocket);
|
|
17147
17243
|
const exec = options.exec ?? defaultExec3;
|
|
17148
|
-
const directory =
|
|
17244
|
+
const directory = mkdtempSync2(join11(tmpdir2(), "forgezero-operator-bootstrap-"));
|
|
17149
17245
|
const remoteTemp = `/tmp/forgezero-operator-${randomBytes9(12).toString("hex")}`;
|
|
17150
17246
|
let knownHosts = "";
|
|
17151
17247
|
try {
|
|
@@ -17162,7 +17258,7 @@ async function applyOperatorPlatformBootstrap(request, mode, options = {}) {
|
|
|
17162
17258
|
await installPackagedAgent(request, knownHosts, exec, directory, remoteTemp, options);
|
|
17163
17259
|
await copy(exec, request, knownHosts, staged.path, `${remoteTemp}/platform-config.json`, true);
|
|
17164
17260
|
for (const name of staged.files)
|
|
17165
|
-
await copy(exec, request, knownHosts,
|
|
17261
|
+
await copy(exec, request, knownHosts, join11(directory, name), `${remoteTemp}/${name}`, true);
|
|
17166
17262
|
for (const bundle of staged.bundleFiles)
|
|
17167
17263
|
await copy(exec, request, knownHosts, bundle.source, `${remoteTemp}/${bundle.name}`, true);
|
|
17168
17264
|
for (const name of ["platform-config.json", ...staged.files, ...staged.bundleFiles.map(({ name: name2 }) => name2)])
|
|
@@ -17210,7 +17306,7 @@ async function applyOperatorMetalBootstrap(request, mode, options = {}) {
|
|
|
17210
17306
|
publicIdentity(request.target.identityPublicKeyFile);
|
|
17211
17307
|
socketPath(request.target.agentSocket);
|
|
17212
17308
|
const exec = options.exec ?? defaultExec3;
|
|
17213
|
-
const directory =
|
|
17309
|
+
const directory = mkdtempSync2(join11(tmpdir2(), "forgezero-operator-metal-"));
|
|
17214
17310
|
const remoteTemp = `/tmp/forgezero-operator-${randomBytes9(12).toString("hex")}`;
|
|
17215
17311
|
let knownHosts = "";
|
|
17216
17312
|
try {
|
|
@@ -17243,7 +17339,7 @@ async function applyOperatorMetalBootstrap(request, mode, options = {}) {
|
|
|
17243
17339
|
await installPackagedAgent(request, knownHosts, exec, directory, remoteTemp, options);
|
|
17244
17340
|
await copy(exec, request, knownHosts, staged.path, `${remoteTemp}/metal-config.json`, true);
|
|
17245
17341
|
for (const name of staged.files)
|
|
17246
|
-
await copy(exec, request, knownHosts,
|
|
17342
|
+
await copy(exec, request, knownHosts, join11(directory, name), `${remoteTemp}/${name}`, true);
|
|
17247
17343
|
for (const name of ["metal-config.json", ...staged.files])
|
|
17248
17344
|
await remote(exec, request, knownHosts, ["/usr/bin/sudo", "-n", "/usr/bin/install", "-m", "0600", `${remoteTemp}/${name}`, `${REMOTE_STAGE}/${name}`], "remote metal handoff", true);
|
|
17249
17345
|
const initialized = await remoteRegularFileExists(exec, request, knownHosts, "/etc/forgezero/metal.initialized.json");
|
|
@@ -17512,7 +17608,7 @@ async function applyHostMaintenance(request, runtime = localRuntime()) {
|
|
|
17512
17608
|
|
|
17513
17609
|
// src/cli/maintenance.ts
|
|
17514
17610
|
import { existsSync as existsSync11, lstatSync as lstatSync9, readFileSync as readFileSync14, realpathSync as realpathSync5 } from "fs";
|
|
17515
|
-
import { isAbsolute as isAbsolute6, join as
|
|
17611
|
+
import { isAbsolute as isAbsolute6, join as join12, relative as relative2, resolve as resolve10 } from "path";
|
|
17516
17612
|
var API_OPERATION_ENTRYPOINTS = {
|
|
17517
17613
|
"dev-reset": ["src", "server", "maintenance", "dev-reset.ts"],
|
|
17518
17614
|
"db-backup": ["src", "server", "maintenance", "snapshot-backup.ts"],
|
|
@@ -17581,7 +17677,7 @@ function unsupportedRepositoryCliOption(argv2) {
|
|
|
17581
17677
|
return;
|
|
17582
17678
|
}
|
|
17583
17679
|
function manifestName(root) {
|
|
17584
|
-
const manifestPath =
|
|
17680
|
+
const manifestPath = join12(root, "package.json");
|
|
17585
17681
|
if (!existsSync11(manifestPath)) {
|
|
17586
17682
|
throw new Error(`No package.json exists at repository root ${root}.`);
|
|
17587
17683
|
}
|
|
@@ -17597,7 +17693,7 @@ function manifestName(root) {
|
|
|
17597
17693
|
return name;
|
|
17598
17694
|
}
|
|
17599
17695
|
function checkedEntrypoint(root, parts) {
|
|
17600
|
-
const candidate =
|
|
17696
|
+
const candidate = join12(root, ...parts);
|
|
17601
17697
|
if (!existsSync11(candidate) || !lstatSync9(candidate).isFile()) {
|
|
17602
17698
|
throw new Error(`The reviewed operation entrypoint is missing: ${candidate}`);
|
|
17603
17699
|
}
|
|
@@ -17625,7 +17721,7 @@ function resolveRepositoryOperation(operation, requestedRoot) {
|
|
|
17625
17721
|
};
|
|
17626
17722
|
}
|
|
17627
17723
|
if (name === "forgezero") {
|
|
17628
|
-
const apiRoot = realpathSync5(
|
|
17724
|
+
const apiRoot = realpathSync5(join12(root, "api"));
|
|
17629
17725
|
if (manifestName(apiRoot) !== "@forgezero/api") {
|
|
17630
17726
|
throw new Error(`${apiRoot} is not the ForgeZero API package.`);
|
|
17631
17727
|
}
|
|
@@ -17667,7 +17763,7 @@ function resolveRepositoryOperation(operation, requestedRoot) {
|
|
|
17667
17763
|
])
|
|
17668
17764
|
};
|
|
17669
17765
|
}
|
|
17670
|
-
const rehearsalRoot =
|
|
17766
|
+
const rehearsalRoot = join12(root, "tests", "rehearsals", "community-cluster-api");
|
|
17671
17767
|
return {
|
|
17672
17768
|
operation,
|
|
17673
17769
|
root,
|
|
@@ -17721,7 +17817,7 @@ function planAppBuild(input) {
|
|
|
17721
17817
|
|
|
17722
17818
|
// src/cli/index.ts
|
|
17723
17819
|
var DEFAULT_MODE = (THRESHOLD_MODES.find((mode) => mode.threshold === 1 && mode.total === 1) ?? THRESHOLD_MODES[0]).id;
|
|
17724
|
-
var PACKAGED_AGENT_BIN2 =
|
|
17820
|
+
var PACKAGED_AGENT_BIN2 = fileURLToPath4(new URL("./fz-agent.js", import.meta.url));
|
|
17725
17821
|
function parseOptions(argv2) {
|
|
17726
17822
|
const options = {
|
|
17727
17823
|
api: process.env.FZ_API ?? "http://localhost:8787",
|
|
@@ -17744,6 +17840,14 @@ function parseOptions(argv2) {
|
|
|
17744
17840
|
deploySoftware: [],
|
|
17745
17841
|
deployChannel: "production",
|
|
17746
17842
|
requireAttestation: false,
|
|
17843
|
+
deployInitCustomized: false,
|
|
17844
|
+
deployRuntime: "native",
|
|
17845
|
+
deployReplicas: 1,
|
|
17846
|
+
deployCpuCores: 1,
|
|
17847
|
+
deployMemoryMiB: 512,
|
|
17848
|
+
deployStorageGiB: 8,
|
|
17849
|
+
deploySharing: "exclusive",
|
|
17850
|
+
deployReuse: "require",
|
|
17747
17851
|
force: false,
|
|
17748
17852
|
noBrowser: false,
|
|
17749
17853
|
provider: "github",
|
|
@@ -17795,7 +17899,55 @@ function parseOptions(argv2) {
|
|
|
17795
17899
|
options.optionError = "--channel must be production or development.";
|
|
17796
17900
|
} else if (token === "--attestation")
|
|
17797
17901
|
options.requireAttestation = true;
|
|
17798
|
-
else if (token === "--
|
|
17902
|
+
else if (token === "--runtime") {
|
|
17903
|
+
const value = argv2[++index];
|
|
17904
|
+
options.deployInitCustomized = true;
|
|
17905
|
+
if (value === "native" || value === "containerd" || value === "kata-snp")
|
|
17906
|
+
options.deployRuntime = value;
|
|
17907
|
+
else
|
|
17908
|
+
options.optionError = "--runtime must be native, containerd, or kata-snp.";
|
|
17909
|
+
} else if (token === "--os") {
|
|
17910
|
+
const value = argv2[++index];
|
|
17911
|
+
options.deployInitCustomized = true;
|
|
17912
|
+
if (value === "ubuntu-24.04" || value === "ubuntu-26.04")
|
|
17913
|
+
options.deployOperatingSystem = value;
|
|
17914
|
+
else
|
|
17915
|
+
options.optionError = "--os must be ubuntu-24.04 or ubuntu-26.04.";
|
|
17916
|
+
} else if (token === "--isolation") {
|
|
17917
|
+
const value = argv2[++index];
|
|
17918
|
+
options.deployInitCustomized = true;
|
|
17919
|
+
if (value === "standard" || value === "sev-snp")
|
|
17920
|
+
options.deployIsolation = value;
|
|
17921
|
+
else
|
|
17922
|
+
options.optionError = "--isolation must be standard or sev-snp.";
|
|
17923
|
+
} else if (["--replicas", "--cpu-cores", "--memory-mib", "--storage-gib"].includes(token)) {
|
|
17924
|
+
const value = Number(argv2[++index] ?? "");
|
|
17925
|
+
options.deployInitCustomized = true;
|
|
17926
|
+
if (!Number.isSafeInteger(value) || value < 1)
|
|
17927
|
+
options.optionError = `${token} must be a positive integer.`;
|
|
17928
|
+
else if (token === "--replicas")
|
|
17929
|
+
options.deployReplicas = value;
|
|
17930
|
+
else if (token === "--cpu-cores")
|
|
17931
|
+
options.deployCpuCores = value;
|
|
17932
|
+
else if (token === "--memory-mib")
|
|
17933
|
+
options.deployMemoryMiB = value;
|
|
17934
|
+
else
|
|
17935
|
+
options.deployStorageGiB = value;
|
|
17936
|
+
} else if (token === "--sharing") {
|
|
17937
|
+
const value = argv2[++index];
|
|
17938
|
+
options.deployInitCustomized = true;
|
|
17939
|
+
if (value === "exclusive" || value === "shared")
|
|
17940
|
+
options.deploySharing = value;
|
|
17941
|
+
else
|
|
17942
|
+
options.optionError = "--sharing must be exclusive or shared.";
|
|
17943
|
+
} else if (token === "--reuse") {
|
|
17944
|
+
const value = argv2[++index];
|
|
17945
|
+
options.deployInitCustomized = true;
|
|
17946
|
+
if (value === "require" || value === "prefer")
|
|
17947
|
+
options.deployReuse = value;
|
|
17948
|
+
else
|
|
17949
|
+
options.optionError = "--reuse must be require or prefer.";
|
|
17950
|
+
} else if (token === "--force")
|
|
17799
17951
|
options.force = true;
|
|
17800
17952
|
else if (token === "--browser")
|
|
17801
17953
|
options.noBrowser = false;
|
|
@@ -18631,10 +18783,10 @@ function interactiveGenesisGuests() {
|
|
|
18631
18783
|
function writeMetalOperatorFiles(directory) {
|
|
18632
18784
|
const output = genesisOutputDirectory(directory);
|
|
18633
18785
|
const config = interactiveMetalBootstrap();
|
|
18634
|
-
const metalConfig = writeBootstrapConfig(
|
|
18786
|
+
const metalConfig = writeBootstrapConfig(join13(output, "metal.json"), config);
|
|
18635
18787
|
const identity = operatorIdentityCoordinates();
|
|
18636
18788
|
const target = interactiveOperatorHop("Metal", { user: "root" });
|
|
18637
|
-
const remoteRequest = writeOperatorMetalBootstrapRequest(
|
|
18789
|
+
const remoteRequest = writeOperatorMetalBootstrapRequest(join13(output, "metal-remote.json"), {
|
|
18638
18790
|
kind: "metal-remote",
|
|
18639
18791
|
metalConfigFile: metalConfig,
|
|
18640
18792
|
target: { ...target, ...identity },
|
|
@@ -18652,7 +18804,7 @@ function writePlatformGenesisFleet(directory) {
|
|
|
18652
18804
|
const metalHostname = readMetalBootstrapConfig(metalRequest.metalConfigFile, {
|
|
18653
18805
|
allowHistoricalRelease: true
|
|
18654
18806
|
}).metalHostname;
|
|
18655
|
-
const checkpointPath =
|
|
18807
|
+
const checkpointPath = join13(output, "cloudflare-handoff.json");
|
|
18656
18808
|
const template = interactiveBootstrap("platform", fleet, checkpointPath);
|
|
18657
18809
|
const region = {
|
|
18658
18810
|
label: bootstrapAnswer(`Region ${template.runtime.environment.nodeRegion} display label`),
|
|
@@ -18667,7 +18819,7 @@ function writePlatformGenesisFleet(directory) {
|
|
|
18667
18819
|
cloudflareHandoffFile: cloudflareHostHandoffPath(checkpointPath, guest.name)
|
|
18668
18820
|
});
|
|
18669
18821
|
const realtimeEnabled = bootstrapBoolean("Configure existing Worker realtime fan-out?", "yes");
|
|
18670
|
-
const cloudflareConfig = writeBootstrapConfig(
|
|
18822
|
+
const cloudflareConfig = writeBootstrapConfig(join13(output, "cloudflare.json"), createCloudflareBootstrapDiscoveryCommandConfig({
|
|
18671
18823
|
checkpointPath,
|
|
18672
18824
|
discovery: {
|
|
18673
18825
|
zoneName: bootstrapAnswer("Cloudflare DNS zone name", "forgezero.net"),
|
|
@@ -18685,7 +18837,7 @@ function writePlatformGenesisFleet(directory) {
|
|
|
18685
18837
|
tunnelName: bootstrapAnswer(`${guest.name} Tunnel name`, guest.name)
|
|
18686
18838
|
}))
|
|
18687
18839
|
}));
|
|
18688
|
-
const cloudflareAcceptance =
|
|
18840
|
+
const cloudflareAcceptance = join13(output, "cloudflare-acceptance.json");
|
|
18689
18841
|
const configs = platformGenesisBootstrapConfigs(template, fleet, nodes, metalHostname, region).map((config) => {
|
|
18690
18842
|
const path = `${output}/${config.computeReference}-platform.json`;
|
|
18691
18843
|
writeBootstrapConfig(path, config);
|
|
@@ -18694,7 +18846,7 @@ function writePlatformGenesisFleet(directory) {
|
|
|
18694
18846
|
const requests = configs.map((platformConfigFile, index) => {
|
|
18695
18847
|
const guest = fleet[index];
|
|
18696
18848
|
const host = guestHostKeys.nodes.find(({ name }) => name === guest.name);
|
|
18697
|
-
return writeOperatorPlatformBootstrapRequest(
|
|
18849
|
+
return writeOperatorPlatformBootstrapRequest(join13(output, `${guest.name}-remote.json`), {
|
|
18698
18850
|
kind: "platform-remote",
|
|
18699
18851
|
platformConfigFile,
|
|
18700
18852
|
target: {
|
|
@@ -18715,7 +18867,7 @@ function writePlatformGenesisFleet(directory) {
|
|
|
18715
18867
|
}
|
|
18716
18868
|
});
|
|
18717
18869
|
});
|
|
18718
|
-
const fleetRequest = writeOperatorPlatformBootstrapFleetRequest(
|
|
18870
|
+
const fleetRequest = writeOperatorPlatformBootstrapFleetRequest(join13(output, "platform-fleet-remote.json"), requests, { configFile: cloudflareConfig, acceptanceFile: cloudflareAcceptance });
|
|
18719
18871
|
return { configs, requests, fleetRequest, cloudflareConfig, cloudflareAcceptance };
|
|
18720
18872
|
}
|
|
18721
18873
|
function interactiveBootstrap(kind, genesisGuests, genesisCloudflareCheckpoint) {
|
|
@@ -19454,7 +19606,7 @@ async function cmdDeploy(options, args) {
|
|
|
19454
19606
|
if (options.optionError)
|
|
19455
19607
|
throw new Error(options.optionError);
|
|
19456
19608
|
if (operation === "init") {
|
|
19457
|
-
if (options.deploySoftware.length > 0 || options.deployProfile !== "app" || options.requireAttestation) {
|
|
19609
|
+
if (!options.deployInitCustomized && (options.deploySoftware.length > 0 || options.deployProfile !== "app" || options.requireAttestation)) {
|
|
19458
19610
|
const created = initializeDeployFile(options.projectRoot, {
|
|
19459
19611
|
name: options.projectName,
|
|
19460
19612
|
profile: options.deployProfile,
|
|
@@ -19469,7 +19621,23 @@ async function cmdDeploy(options, args) {
|
|
|
19469
19621
|
out.ok(`Initialized legacy .fz/deploy.json (${created.summary.digest}).`);
|
|
19470
19622
|
return 0;
|
|
19471
19623
|
}
|
|
19472
|
-
|
|
19624
|
+
if (options.deployInitCustomized && (options.deploySoftware.length > 0 || options.requireAttestation)) {
|
|
19625
|
+
throw new Error("typed runtime initialization selects its tested software and attestation from --runtime/--isolation; do not combine --software or --attestation");
|
|
19626
|
+
}
|
|
19627
|
+
initializeTypeScriptDeployment(options.projectRoot, {
|
|
19628
|
+
name: options.projectName ?? basename3(resolve12(options.projectRoot)),
|
|
19629
|
+
force: options.force,
|
|
19630
|
+
runtime: options.deployRuntime,
|
|
19631
|
+
operatingSystem: options.deployOperatingSystem,
|
|
19632
|
+
isolation: options.deployIsolation,
|
|
19633
|
+
replicas: options.deployReplicas,
|
|
19634
|
+
cpuCores: options.deployCpuCores,
|
|
19635
|
+
memoryMiB: options.deployMemoryMiB,
|
|
19636
|
+
storageGiB: options.deployStorageGiB,
|
|
19637
|
+
sharing: options.deploySharing,
|
|
19638
|
+
reuse: options.deployReuse,
|
|
19639
|
+
profile: options.deployProfile
|
|
19640
|
+
});
|
|
19473
19641
|
const compiled = await compileDeploymentProject(options.projectRoot);
|
|
19474
19642
|
if (options.json)
|
|
19475
19643
|
out.line(JSON.stringify({ source: compiled.source, output: compiled.output, digest: compiled.digest }, null, 2));
|
|
@@ -19721,7 +19889,9 @@ function usage() {
|
|
|
19721
19889
|
fz project check Fail when truth sources or generated adapters drift
|
|
19722
19890
|
fz app build Build the static App for a numbered local/development/
|
|
19723
19891
|
production choice, --profile, or an explicit --api origin
|
|
19724
|
-
|
|
19892
|
+
fz deploy init Create forgezero.deploy.ts and its fail-safe canonical plan;
|
|
19893
|
+
select native, containerd runc, or Kata QEMU SNP and
|
|
19894
|
+
explicit per-compute resources with the options below
|
|
19725
19895
|
fz deploy compile Compile TypeScript into .fz/deploy.plan.json
|
|
19726
19896
|
fz deploy check Validate source, plan, actions, providers and topology
|
|
19727
19897
|
fz deploy sync Prove readiness and print the Git synchronization rule
|
|
@@ -19792,6 +19962,15 @@ function usage() {
|
|
|
19792
19962
|
--software <key@ver> Initial tested software coordinate; repeatable
|
|
19793
19963
|
--channel <name> Catalog view: production or development (shows testing)
|
|
19794
19964
|
--attestation Require hardware attestation for every deploy step
|
|
19965
|
+
--runtime <mode> native, containerd, or kata-snp
|
|
19966
|
+
--os <image> ubuntu-24.04 or ubuntu-26.04 (SNP requires 26.04)
|
|
19967
|
+
--isolation <mode> standard or sev-snp
|
|
19968
|
+
--replicas <n> Exact initial compute cardinality (default 1)
|
|
19969
|
+
--cpu-cores <n> Reserved CPU cores per compute
|
|
19970
|
+
--memory-mib <n> Reserved memory MiB per compute
|
|
19971
|
+
--storage-gib <n> Reserved storage GiB per compute
|
|
19972
|
+
--sharing <mode> exclusive or shared (native requires exclusive)
|
|
19973
|
+
--reuse <mode> require existing capacity or prefer reuse then approved provisioning
|
|
19795
19974
|
--force Init may replace an existing generated target
|
|
19796
19975
|
|
|
19797
19976
|
REMOTE DEPLOY OPTIONS
|
package/dist/metal-bootstrap.js
CHANGED
|
@@ -366,7 +366,7 @@ function systemdAgentEgressDirectives(loopbackTcpPorts = []) {
|
|
|
366
366
|
}
|
|
367
367
|
|
|
368
368
|
// src/version.ts
|
|
369
|
-
var VERSION = "0.1.
|
|
369
|
+
var VERSION = "0.1.83";
|
|
370
370
|
|
|
371
371
|
// src/otel-collector.ts
|
|
372
372
|
var FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
|
|
@@ -2647,7 +2647,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
|
|
|
2647
2647
|
}
|
|
2648
2648
|
|
|
2649
2649
|
// src/version.ts
|
|
2650
|
-
var VERSION3 = "0.1.
|
|
2650
|
+
var VERSION3 = "0.1.83";
|
|
2651
2651
|
|
|
2652
2652
|
// src/egress-policy.ts
|
|
2653
2653
|
import { realpathSync as realpathSync3 } from "node:fs";
|
package/dist/provision.js
CHANGED
|
@@ -2647,7 +2647,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
|
|
|
2647
2647
|
}
|
|
2648
2648
|
|
|
2649
2649
|
// src/version.ts
|
|
2650
|
-
var VERSION3 = "0.1.
|
|
2650
|
+
var VERSION3 = "0.1.83";
|
|
2651
2651
|
|
|
2652
2652
|
// src/egress-policy.ts
|
|
2653
2653
|
import { realpathSync as realpathSync3 } from "node:fs";
|
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.83";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgezero/agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.83",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"check": "tsc --noEmit",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"@noble/post-quantum": "^0.6.1"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@forgezero/runtime": "0.1.
|
|
20
|
-
"@forgezero/vault": "0.1.
|
|
19
|
+
"@forgezero/runtime": "0.1.12",
|
|
20
|
+
"@forgezero/vault": "0.1.16",
|
|
21
21
|
"@noble/curves": "2.2.0",
|
|
22
22
|
"@noble/hashes": "2.2.0",
|
|
23
23
|
"@noble/post-quantum": "0.6.1",
|