@forgezero/agent 0.1.81 → 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 +3 -6
- 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 +278 -102
- package/dist/metal-bootstrap.js +1 -1
- package/dist/operator-bootstrap.js +3 -6
- package/dist/platform-bootstrap-runtime.js +2 -5
- package/dist/platform-fleet-verification.js +3 -6
- package/dist/platform-genesis.js +2 -5
- package/dist/provision.js +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +3 -3
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];
|
|
@@ -11896,7 +11992,6 @@ function validatePlatformGenesisGuests(guests, expectedCount = 3) {
|
|
|
11896
11992
|
}
|
|
11897
11993
|
const names = new Set;
|
|
11898
11994
|
const addresses = new Set;
|
|
11899
|
-
const pools = new Set;
|
|
11900
11995
|
return guests.map((input) => {
|
|
11901
11996
|
if (!input || typeof input !== "object" || !SAFE_NAME.test(input.name)) {
|
|
11902
11997
|
throw new Error("platform genesis guest name is malformed");
|
|
@@ -11909,13 +12004,11 @@ function validatePlatformGenesisGuests(guests, expectedCount = 3) {
|
|
|
11909
12004
|
if (input.cpuPoolKey !== undefined && !SAFE_POOL.test(input.cpuPoolKey)) {
|
|
11910
12005
|
throw new Error("platform genesis CPU pool key is malformed");
|
|
11911
12006
|
}
|
|
11912
|
-
if (names.has(input.name) || addresses.has(input.address)
|
|
11913
|
-
throw new Error("platform genesis guest names
|
|
12007
|
+
if (names.has(input.name) || addresses.has(input.address)) {
|
|
12008
|
+
throw new Error("platform genesis guest names and addresses must be unique");
|
|
11914
12009
|
}
|
|
11915
12010
|
names.add(input.name);
|
|
11916
12011
|
addresses.add(input.address);
|
|
11917
|
-
if (input.cpuPoolKey !== undefined)
|
|
11918
|
-
pools.add(input.cpuPoolKey);
|
|
11919
12012
|
const physicalCores = integer2(input.physicalCores, 1, 256, "physical core count");
|
|
11920
12013
|
const vcpu = integer2(input.vcpu, 1, 512, "vCPU count");
|
|
11921
12014
|
if (vcpu < physicalCores)
|
|
@@ -12430,7 +12523,7 @@ function planLocalOtlpProof(endpoint, collectorUnit) {
|
|
|
12430
12523
|
import { constants } from "fs";
|
|
12431
12524
|
import { createHmac, randomUUID } from "crypto";
|
|
12432
12525
|
import { chmod, lstat, mkdir, open, readdir, rename, rmdir, stat, unlink } from "fs/promises";
|
|
12433
|
-
import { dirname as dirname7, join as
|
|
12526
|
+
import { dirname as dirname7, join as join7, resolve as resolve5 } from "path";
|
|
12434
12527
|
import { isIP as isIP3 } from "net";
|
|
12435
12528
|
|
|
12436
12529
|
// src/cloudflare-edge.ts
|
|
@@ -13102,7 +13195,7 @@ function cloudflareHostHandoffPath(checkpointPath, nodeName) {
|
|
|
13102
13195
|
const normalized = nodeName.trim().toLowerCase();
|
|
13103
13196
|
if (!/^[a-z0-9][a-z0-9-]{0,62}$/.test(normalized))
|
|
13104
13197
|
throw new Error("Cloudflare host handoff node name is invalid");
|
|
13105
|
-
return
|
|
13198
|
+
return join7(`${resolve5(checkpointPath)}.hosts`, `${normalized}.json`);
|
|
13106
13199
|
}
|
|
13107
13200
|
async function readCloudflareHostHandoff(handoffPath, nodeName) {
|
|
13108
13201
|
let parsed;
|
|
@@ -13484,7 +13577,7 @@ async function removeCloudflareBootstrapSecrets(checkpointPath, output) {
|
|
|
13484
13577
|
throw new Error("Cloudflare host handoff directory contains unexpected files; refusing secret cleanup");
|
|
13485
13578
|
}
|
|
13486
13579
|
for (const name of expected)
|
|
13487
|
-
await unlink(
|
|
13580
|
+
await unlink(join7(directory, name));
|
|
13488
13581
|
await rmdir(directory);
|
|
13489
13582
|
}
|
|
13490
13583
|
await unlink(resolve5(checkpointPath));
|
|
@@ -13926,7 +14019,7 @@ var LIFECYCLE_PROFILE = "/etc/forgezero/lifecycle.json";
|
|
|
13926
14019
|
var CONTROL_SOCKET = "/run/forgezero/control.sock";
|
|
13927
14020
|
var CLOUDFLARED_METRICS_ADDRESS = "127.0.0.1:20241";
|
|
13928
14021
|
var CLOUDFLARED_DIAGNOSTICS_URL = `http://${CLOUDFLARED_METRICS_ADDRESS}/diag/tunnel`;
|
|
13929
|
-
var PACKAGED_AGENT_BIN =
|
|
14022
|
+
var PACKAGED_AGENT_BIN = fileURLToPath2(new URL("./fz-agent.js", import.meta.url));
|
|
13930
14023
|
var privateOrigin = (value) => {
|
|
13931
14024
|
let url;
|
|
13932
14025
|
try {
|
|
@@ -14153,7 +14246,7 @@ var unitEscape = (value) => {
|
|
|
14153
14246
|
};
|
|
14154
14247
|
function databaseUnit(config) {
|
|
14155
14248
|
const db = config.database;
|
|
14156
|
-
const
|
|
14249
|
+
const join8 = db.role === "joiner" ? ` --starter.join=${unitEscape(db.master)}` : "";
|
|
14157
14250
|
const agency = db.agency === "none" ? " --cluster.start-agent=false --cluster.start-coordinator=true --cluster.start-dbserver=true" : "";
|
|
14158
14251
|
return `[Unit]
|
|
14159
14252
|
Description=ForgeZero ArangoDB Community 3.11.14 cluster (${db.role}; agency=${db.agency})
|
|
@@ -14165,7 +14258,7 @@ Type=simple
|
|
|
14165
14258
|
User=arangodb
|
|
14166
14259
|
Group=arangodb
|
|
14167
14260
|
LoadCredentialEncrypted=arangodb-jwt:${JWT_CREDENTIAL}
|
|
14168
|
-
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}
|
|
14169
14262
|
Restart=always
|
|
14170
14263
|
RestartSec=5
|
|
14171
14264
|
UMask=0077
|
|
@@ -15492,11 +15585,11 @@ async function runCloudflareBootstrapFinalizeCommand(configPath, dependencies =
|
|
|
15492
15585
|
|
|
15493
15586
|
// src/operator-bootstrap.ts
|
|
15494
15587
|
import { createHash as createHash6, randomBytes as randomBytes9 } from "crypto";
|
|
15495
|
-
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";
|
|
15496
15589
|
import { isIP as isIP6 } from "net";
|
|
15497
|
-
import { tmpdir } from "os";
|
|
15498
|
-
import { basename as basename2, dirname as dirname13, isAbsolute as isAbsolute5, join as
|
|
15499
|
-
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";
|
|
15500
15593
|
|
|
15501
15594
|
// src/metal-bootstrap.ts
|
|
15502
15595
|
import { createHash as createHash5, randomBytes as randomBytes8 } from "crypto";
|
|
@@ -15515,15 +15608,15 @@ import {
|
|
|
15515
15608
|
unlinkSync as unlinkSync2,
|
|
15516
15609
|
writeFileSync as writeFileSync11
|
|
15517
15610
|
} from "fs";
|
|
15518
|
-
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";
|
|
15519
15612
|
import { isIP as isIP5 } from "net";
|
|
15520
15613
|
|
|
15521
15614
|
// src/metal-isolation.ts
|
|
15522
15615
|
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
15523
|
-
import { join as
|
|
15616
|
+
import { join as join9 } from "path";
|
|
15524
15617
|
|
|
15525
15618
|
// src/metal-provision.ts
|
|
15526
|
-
import { dirname as dirname11, isAbsolute as isAbsolute3, join as
|
|
15619
|
+
import { dirname as dirname11, isAbsolute as isAbsolute3, join as join8 } from "path";
|
|
15527
15620
|
import { isIP as isIP4 } from "net";
|
|
15528
15621
|
var SAFE_NAME2 = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,63}$/;
|
|
15529
15622
|
var DEVICE = /^[a-zA-Z][a-zA-Z0-9_.-]{0,14}$/;
|
|
@@ -15719,15 +15812,15 @@ async function applyMetalIsolation(profile, exec = defaultExec) {
|
|
|
15719
15812
|
await requireGuestsInSlice(exec);
|
|
15720
15813
|
const unitDir = profile.unitDir;
|
|
15721
15814
|
mkdirSync10(unitDir, { recursive: true });
|
|
15722
|
-
writeFileSync10(
|
|
15815
|
+
writeFileSync10(join9(unitDir, "forgezero-guests.slice"), metalGuestSliceUnit(profile), { mode: 420 });
|
|
15723
15816
|
for (const unit of ["system.slice", "user.slice"]) {
|
|
15724
|
-
const directory =
|
|
15817
|
+
const directory = join9(unitDir, `${unit}.d`);
|
|
15725
15818
|
mkdirSync10(directory, { recursive: true });
|
|
15726
|
-
writeFileSync10(
|
|
15819
|
+
writeFileSync10(join9(directory, "50-forgezero-housekeeping.conf"), metalHousekeepingDropIn(profile, "slice"), { mode: 420 });
|
|
15727
15820
|
}
|
|
15728
|
-
const initDirectory =
|
|
15821
|
+
const initDirectory = join9(unitDir, "init.scope.d");
|
|
15729
15822
|
mkdirSync10(initDirectory, { recursive: true });
|
|
15730
|
-
writeFileSync10(
|
|
15823
|
+
writeFileSync10(join9(initDirectory, "50-forgezero-housekeeping.conf"), metalHousekeepingDropIn(profile, "scope"), { mode: 420 });
|
|
15731
15824
|
await checked4(exec, ["systemctl", "daemon-reload"]);
|
|
15732
15825
|
await requireGuestsInSlice(exec);
|
|
15733
15826
|
const properties = [`AllowedCPUs=${profile.housekeepingCpus}`];
|
|
@@ -16107,9 +16200,9 @@ var installAgentBinary = (source, version) => {
|
|
|
16107
16200
|
validateAgentSourcePath(source);
|
|
16108
16201
|
const release = `/opt/forgezero/agent/versions/${version}/dist`;
|
|
16109
16202
|
mkdirSync11(release, { recursive: true, mode: 493 });
|
|
16110
|
-
copyFileSync2(source,
|
|
16111
|
-
chmodSync5(
|
|
16112
|
-
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);
|
|
16113
16206
|
mkdirSync11("/opt/forgezero/agent", { recursive: true, mode: 493 });
|
|
16114
16207
|
for (const [link, target] of [
|
|
16115
16208
|
["/opt/forgezero/agent/current.next", `versions/${version}`],
|
|
@@ -16259,7 +16352,7 @@ async function applyMetalBootstrap(config, options) {
|
|
|
16259
16352
|
}
|
|
16260
16353
|
await applyMetalIsolation(config.profile, (argv2) => exec(argv2));
|
|
16261
16354
|
for (const [unit, body] of Object.entries(renderMetalUnits(config)))
|
|
16262
|
-
atomicWrite2(
|
|
16355
|
+
atomicWrite2(join10(UNIT_DIRECTORY, unit), body, 420);
|
|
16263
16356
|
await runChecked(exec, ["/usr/bin/systemctl", "daemon-reload"]);
|
|
16264
16357
|
await runChecked(exec, [
|
|
16265
16358
|
"/usr/bin/systemctl",
|
|
@@ -16393,7 +16486,7 @@ async function metalBootstrapStatus(exec = defaultExec2) {
|
|
|
16393
16486
|
"forgezero-metal-agent-egress.service",
|
|
16394
16487
|
"forgezero-metal-agent.service"
|
|
16395
16488
|
]) {
|
|
16396
|
-
if (!existsSync10(
|
|
16489
|
+
if (!existsSync10(join10(UNIT_DIRECTORY, unit)))
|
|
16397
16490
|
units[unit] = "missing";
|
|
16398
16491
|
else
|
|
16399
16492
|
units[unit] = (await exec(["/usr/bin/systemctl", "is-active", "--quiet", unit])).exitCode === 0 ? "active" : "inactive";
|
|
@@ -16922,7 +17015,7 @@ function writeKnownHosts(request, directory) {
|
|
|
16922
17015
|
const lines = [`fz-operator-target ${request.target.hostKey}`];
|
|
16923
17016
|
if (request.target.jump)
|
|
16924
17017
|
lines.push(`${hostLabel(request.target.jump.address, request.target.jump.port)} ${request.target.jump.hostKey}`);
|
|
16925
|
-
const path =
|
|
17018
|
+
const path = join11(directory, "known_hosts");
|
|
16926
17019
|
writeFileSync12(path, `${lines.join(`
|
|
16927
17020
|
`)}
|
|
16928
17021
|
`, { mode: 384, flag: "wx" });
|
|
@@ -17012,7 +17105,7 @@ async function collectOperatorGuestHostKeys(request, options = {}, includeRehear
|
|
|
17012
17105
|
publicIdentity(request.target.identityPublicKeyFile);
|
|
17013
17106
|
socketPath(request.target.agentSocket);
|
|
17014
17107
|
const exec = options.exec ?? defaultExec3;
|
|
17015
|
-
const directory =
|
|
17108
|
+
const directory = mkdtempSync2(join11(tmpdir2(), "forgezero-operator-host-keys-"));
|
|
17016
17109
|
try {
|
|
17017
17110
|
const knownHosts = writeKnownHosts(request, directory);
|
|
17018
17111
|
const nodes = [];
|
|
@@ -17048,7 +17141,7 @@ async function stageConfig(config, directory) {
|
|
|
17048
17141
|
const staged = [];
|
|
17049
17142
|
for (const [name, source] of secretSources(config)) {
|
|
17050
17143
|
const bytes = ownerFile(source, SECRET_LIMIT, name);
|
|
17051
|
-
const local =
|
|
17144
|
+
const local = join11(directory, name);
|
|
17052
17145
|
writeFileSync12(local, bytes, { mode: 384, flag: "wx" });
|
|
17053
17146
|
staged.push(name);
|
|
17054
17147
|
const remotePath = `${REMOTE_STAGE}/${name}`;
|
|
@@ -17068,7 +17161,7 @@ async function stageConfig(config, directory) {
|
|
|
17068
17161
|
manifestFile: `${REMOTE_STAGE}/bootstrap-api.bundle.json`,
|
|
17069
17162
|
branch: bundle.manifest.branch
|
|
17070
17163
|
};
|
|
17071
|
-
const path =
|
|
17164
|
+
const path = join11(directory, "platform-config.json");
|
|
17072
17165
|
writeFileSync12(path, `${JSON.stringify(rewritten, null, 2)}
|
|
17073
17166
|
`, { mode: 384, flag: "wx" });
|
|
17074
17167
|
return { path, files: staged, bundleFiles };
|
|
@@ -17078,14 +17171,14 @@ function stageMetalConfig(config, directory) {
|
|
|
17078
17171
|
const files = [];
|
|
17079
17172
|
if (config.agentSeedFile) {
|
|
17080
17173
|
const name = "metal-agent-seed";
|
|
17081
|
-
writeFileSync12(
|
|
17174
|
+
writeFileSync12(join11(directory, name), ownerFile(config.agentSeedFile, SECRET_LIMIT, name), {
|
|
17082
17175
|
mode: 384,
|
|
17083
17176
|
flag: "wx"
|
|
17084
17177
|
});
|
|
17085
17178
|
rewritten.agentSeedFile = `${REMOTE_STAGE}/${name}`;
|
|
17086
17179
|
files.push(name);
|
|
17087
17180
|
}
|
|
17088
|
-
const path =
|
|
17181
|
+
const path = join11(directory, "metal-config.json");
|
|
17089
17182
|
writeFileSync12(path, `${JSON.stringify(rewritten, null, 2)}
|
|
17090
17183
|
`, { mode: 384, flag: "wx" });
|
|
17091
17184
|
return { path, files };
|
|
@@ -17097,15 +17190,15 @@ async function verifiedBunArchive(directory, fetcher) {
|
|
|
17097
17190
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
17098
17191
|
if (createHash6("sha256").update(bytes).digest("hex") !== BUN_RELEASE_SHA256)
|
|
17099
17192
|
throw new Error("pinned Bun checksum mismatch");
|
|
17100
|
-
const path =
|
|
17193
|
+
const path = join11(directory, "bun.zip");
|
|
17101
17194
|
writeFileSync12(path, bytes, { mode: 384, flag: "wx" });
|
|
17102
17195
|
return path;
|
|
17103
17196
|
}
|
|
17104
17197
|
async function installPackagedAgent(request, knownHosts, exec, directory, remoteTemp, options) {
|
|
17105
17198
|
const artifacts = [
|
|
17106
|
-
[options.fzCliPath ??
|
|
17107
|
-
[options.fzAgentPath ??
|
|
17108
|
-
[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"]
|
|
17109
17202
|
];
|
|
17110
17203
|
for (const [artifact] of artifacts) {
|
|
17111
17204
|
if (!readFileSync12(artifact).length)
|
|
@@ -17148,7 +17241,7 @@ async function applyOperatorPlatformBootstrap(request, mode, options = {}) {
|
|
|
17148
17241
|
publicIdentity(request.target.identityPublicKeyFile);
|
|
17149
17242
|
socketPath(request.target.agentSocket);
|
|
17150
17243
|
const exec = options.exec ?? defaultExec3;
|
|
17151
|
-
const directory =
|
|
17244
|
+
const directory = mkdtempSync2(join11(tmpdir2(), "forgezero-operator-bootstrap-"));
|
|
17152
17245
|
const remoteTemp = `/tmp/forgezero-operator-${randomBytes9(12).toString("hex")}`;
|
|
17153
17246
|
let knownHosts = "";
|
|
17154
17247
|
try {
|
|
@@ -17165,7 +17258,7 @@ async function applyOperatorPlatformBootstrap(request, mode, options = {}) {
|
|
|
17165
17258
|
await installPackagedAgent(request, knownHosts, exec, directory, remoteTemp, options);
|
|
17166
17259
|
await copy(exec, request, knownHosts, staged.path, `${remoteTemp}/platform-config.json`, true);
|
|
17167
17260
|
for (const name of staged.files)
|
|
17168
|
-
await copy(exec, request, knownHosts,
|
|
17261
|
+
await copy(exec, request, knownHosts, join11(directory, name), `${remoteTemp}/${name}`, true);
|
|
17169
17262
|
for (const bundle of staged.bundleFiles)
|
|
17170
17263
|
await copy(exec, request, knownHosts, bundle.source, `${remoteTemp}/${bundle.name}`, true);
|
|
17171
17264
|
for (const name of ["platform-config.json", ...staged.files, ...staged.bundleFiles.map(({ name: name2 }) => name2)])
|
|
@@ -17213,7 +17306,7 @@ async function applyOperatorMetalBootstrap(request, mode, options = {}) {
|
|
|
17213
17306
|
publicIdentity(request.target.identityPublicKeyFile);
|
|
17214
17307
|
socketPath(request.target.agentSocket);
|
|
17215
17308
|
const exec = options.exec ?? defaultExec3;
|
|
17216
|
-
const directory =
|
|
17309
|
+
const directory = mkdtempSync2(join11(tmpdir2(), "forgezero-operator-metal-"));
|
|
17217
17310
|
const remoteTemp = `/tmp/forgezero-operator-${randomBytes9(12).toString("hex")}`;
|
|
17218
17311
|
let knownHosts = "";
|
|
17219
17312
|
try {
|
|
@@ -17246,7 +17339,7 @@ async function applyOperatorMetalBootstrap(request, mode, options = {}) {
|
|
|
17246
17339
|
await installPackagedAgent(request, knownHosts, exec, directory, remoteTemp, options);
|
|
17247
17340
|
await copy(exec, request, knownHosts, staged.path, `${remoteTemp}/metal-config.json`, true);
|
|
17248
17341
|
for (const name of staged.files)
|
|
17249
|
-
await copy(exec, request, knownHosts,
|
|
17342
|
+
await copy(exec, request, knownHosts, join11(directory, name), `${remoteTemp}/${name}`, true);
|
|
17250
17343
|
for (const name of ["metal-config.json", ...staged.files])
|
|
17251
17344
|
await remote(exec, request, knownHosts, ["/usr/bin/sudo", "-n", "/usr/bin/install", "-m", "0600", `${remoteTemp}/${name}`, `${REMOTE_STAGE}/${name}`], "remote metal handoff", true);
|
|
17252
17345
|
const initialized = await remoteRegularFileExists(exec, request, knownHosts, "/etc/forgezero/metal.initialized.json");
|
|
@@ -17515,7 +17608,7 @@ async function applyHostMaintenance(request, runtime = localRuntime()) {
|
|
|
17515
17608
|
|
|
17516
17609
|
// src/cli/maintenance.ts
|
|
17517
17610
|
import { existsSync as existsSync11, lstatSync as lstatSync9, readFileSync as readFileSync14, realpathSync as realpathSync5 } from "fs";
|
|
17518
|
-
import { isAbsolute as isAbsolute6, join as
|
|
17611
|
+
import { isAbsolute as isAbsolute6, join as join12, relative as relative2, resolve as resolve10 } from "path";
|
|
17519
17612
|
var API_OPERATION_ENTRYPOINTS = {
|
|
17520
17613
|
"dev-reset": ["src", "server", "maintenance", "dev-reset.ts"],
|
|
17521
17614
|
"db-backup": ["src", "server", "maintenance", "snapshot-backup.ts"],
|
|
@@ -17584,7 +17677,7 @@ function unsupportedRepositoryCliOption(argv2) {
|
|
|
17584
17677
|
return;
|
|
17585
17678
|
}
|
|
17586
17679
|
function manifestName(root) {
|
|
17587
|
-
const manifestPath =
|
|
17680
|
+
const manifestPath = join12(root, "package.json");
|
|
17588
17681
|
if (!existsSync11(manifestPath)) {
|
|
17589
17682
|
throw new Error(`No package.json exists at repository root ${root}.`);
|
|
17590
17683
|
}
|
|
@@ -17600,7 +17693,7 @@ function manifestName(root) {
|
|
|
17600
17693
|
return name;
|
|
17601
17694
|
}
|
|
17602
17695
|
function checkedEntrypoint(root, parts) {
|
|
17603
|
-
const candidate =
|
|
17696
|
+
const candidate = join12(root, ...parts);
|
|
17604
17697
|
if (!existsSync11(candidate) || !lstatSync9(candidate).isFile()) {
|
|
17605
17698
|
throw new Error(`The reviewed operation entrypoint is missing: ${candidate}`);
|
|
17606
17699
|
}
|
|
@@ -17628,7 +17721,7 @@ function resolveRepositoryOperation(operation, requestedRoot) {
|
|
|
17628
17721
|
};
|
|
17629
17722
|
}
|
|
17630
17723
|
if (name === "forgezero") {
|
|
17631
|
-
const apiRoot = realpathSync5(
|
|
17724
|
+
const apiRoot = realpathSync5(join12(root, "api"));
|
|
17632
17725
|
if (manifestName(apiRoot) !== "@forgezero/api") {
|
|
17633
17726
|
throw new Error(`${apiRoot} is not the ForgeZero API package.`);
|
|
17634
17727
|
}
|
|
@@ -17670,7 +17763,7 @@ function resolveRepositoryOperation(operation, requestedRoot) {
|
|
|
17670
17763
|
])
|
|
17671
17764
|
};
|
|
17672
17765
|
}
|
|
17673
|
-
const rehearsalRoot =
|
|
17766
|
+
const rehearsalRoot = join12(root, "tests", "rehearsals", "community-cluster-api");
|
|
17674
17767
|
return {
|
|
17675
17768
|
operation,
|
|
17676
17769
|
root,
|
|
@@ -17724,7 +17817,7 @@ function planAppBuild(input) {
|
|
|
17724
17817
|
|
|
17725
17818
|
// src/cli/index.ts
|
|
17726
17819
|
var DEFAULT_MODE = (THRESHOLD_MODES.find((mode) => mode.threshold === 1 && mode.total === 1) ?? THRESHOLD_MODES[0]).id;
|
|
17727
|
-
var PACKAGED_AGENT_BIN2 =
|
|
17820
|
+
var PACKAGED_AGENT_BIN2 = fileURLToPath4(new URL("./fz-agent.js", import.meta.url));
|
|
17728
17821
|
function parseOptions(argv2) {
|
|
17729
17822
|
const options = {
|
|
17730
17823
|
api: process.env.FZ_API ?? "http://localhost:8787",
|
|
@@ -17747,6 +17840,14 @@ function parseOptions(argv2) {
|
|
|
17747
17840
|
deploySoftware: [],
|
|
17748
17841
|
deployChannel: "production",
|
|
17749
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",
|
|
17750
17851
|
force: false,
|
|
17751
17852
|
noBrowser: false,
|
|
17752
17853
|
provider: "github",
|
|
@@ -17798,7 +17899,55 @@ function parseOptions(argv2) {
|
|
|
17798
17899
|
options.optionError = "--channel must be production or development.";
|
|
17799
17900
|
} else if (token === "--attestation")
|
|
17800
17901
|
options.requireAttestation = true;
|
|
17801
|
-
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")
|
|
17802
17951
|
options.force = true;
|
|
17803
17952
|
else if (token === "--browser")
|
|
17804
17953
|
options.noBrowser = false;
|
|
@@ -18614,7 +18763,7 @@ function interactiveGenesisGuests() {
|
|
|
18614
18763
|
name: bootstrapAnswer(`Guest ${index} name`),
|
|
18615
18764
|
address: bootstrapAnswer(`Guest ${index} private IPv4`),
|
|
18616
18765
|
imageKey: bootstrapAnswer(`Guest ${index} OS image key`, SUPPORTED_GUEST_IMAGE.key),
|
|
18617
|
-
cpuPoolKey: bootstrapAnswer(`Guest ${index}
|
|
18766
|
+
cpuPoolKey: bootstrapAnswer(`Guest ${index} preferred NUMA CPU pool key (capacity is sliced by request)`),
|
|
18618
18767
|
physicalCores: bootstrapNumber(`Guest ${index} physical cores`),
|
|
18619
18768
|
vcpu: bootstrapNumber(`Guest ${index} vCPUs`),
|
|
18620
18769
|
memoryGib: bootstrapNumber(`Guest ${index} memory GiB`),
|
|
@@ -18634,10 +18783,10 @@ function interactiveGenesisGuests() {
|
|
|
18634
18783
|
function writeMetalOperatorFiles(directory) {
|
|
18635
18784
|
const output = genesisOutputDirectory(directory);
|
|
18636
18785
|
const config = interactiveMetalBootstrap();
|
|
18637
|
-
const metalConfig = writeBootstrapConfig(
|
|
18786
|
+
const metalConfig = writeBootstrapConfig(join13(output, "metal.json"), config);
|
|
18638
18787
|
const identity = operatorIdentityCoordinates();
|
|
18639
18788
|
const target = interactiveOperatorHop("Metal", { user: "root" });
|
|
18640
|
-
const remoteRequest = writeOperatorMetalBootstrapRequest(
|
|
18789
|
+
const remoteRequest = writeOperatorMetalBootstrapRequest(join13(output, "metal-remote.json"), {
|
|
18641
18790
|
kind: "metal-remote",
|
|
18642
18791
|
metalConfigFile: metalConfig,
|
|
18643
18792
|
target: { ...target, ...identity },
|
|
@@ -18655,7 +18804,7 @@ function writePlatformGenesisFleet(directory) {
|
|
|
18655
18804
|
const metalHostname = readMetalBootstrapConfig(metalRequest.metalConfigFile, {
|
|
18656
18805
|
allowHistoricalRelease: true
|
|
18657
18806
|
}).metalHostname;
|
|
18658
|
-
const checkpointPath =
|
|
18807
|
+
const checkpointPath = join13(output, "cloudflare-handoff.json");
|
|
18659
18808
|
const template = interactiveBootstrap("platform", fleet, checkpointPath);
|
|
18660
18809
|
const region = {
|
|
18661
18810
|
label: bootstrapAnswer(`Region ${template.runtime.environment.nodeRegion} display label`),
|
|
@@ -18670,7 +18819,7 @@ function writePlatformGenesisFleet(directory) {
|
|
|
18670
18819
|
cloudflareHandoffFile: cloudflareHostHandoffPath(checkpointPath, guest.name)
|
|
18671
18820
|
});
|
|
18672
18821
|
const realtimeEnabled = bootstrapBoolean("Configure existing Worker realtime fan-out?", "yes");
|
|
18673
|
-
const cloudflareConfig = writeBootstrapConfig(
|
|
18822
|
+
const cloudflareConfig = writeBootstrapConfig(join13(output, "cloudflare.json"), createCloudflareBootstrapDiscoveryCommandConfig({
|
|
18674
18823
|
checkpointPath,
|
|
18675
18824
|
discovery: {
|
|
18676
18825
|
zoneName: bootstrapAnswer("Cloudflare DNS zone name", "forgezero.net"),
|
|
@@ -18688,7 +18837,7 @@ function writePlatformGenesisFleet(directory) {
|
|
|
18688
18837
|
tunnelName: bootstrapAnswer(`${guest.name} Tunnel name`, guest.name)
|
|
18689
18838
|
}))
|
|
18690
18839
|
}));
|
|
18691
|
-
const cloudflareAcceptance =
|
|
18840
|
+
const cloudflareAcceptance = join13(output, "cloudflare-acceptance.json");
|
|
18692
18841
|
const configs = platformGenesisBootstrapConfigs(template, fleet, nodes, metalHostname, region).map((config) => {
|
|
18693
18842
|
const path = `${output}/${config.computeReference}-platform.json`;
|
|
18694
18843
|
writeBootstrapConfig(path, config);
|
|
@@ -18697,7 +18846,7 @@ function writePlatformGenesisFleet(directory) {
|
|
|
18697
18846
|
const requests = configs.map((platformConfigFile, index) => {
|
|
18698
18847
|
const guest = fleet[index];
|
|
18699
18848
|
const host = guestHostKeys.nodes.find(({ name }) => name === guest.name);
|
|
18700
|
-
return writeOperatorPlatformBootstrapRequest(
|
|
18849
|
+
return writeOperatorPlatformBootstrapRequest(join13(output, `${guest.name}-remote.json`), {
|
|
18701
18850
|
kind: "platform-remote",
|
|
18702
18851
|
platformConfigFile,
|
|
18703
18852
|
target: {
|
|
@@ -18718,7 +18867,7 @@ function writePlatformGenesisFleet(directory) {
|
|
|
18718
18867
|
}
|
|
18719
18868
|
});
|
|
18720
18869
|
});
|
|
18721
|
-
const fleetRequest = writeOperatorPlatformBootstrapFleetRequest(
|
|
18870
|
+
const fleetRequest = writeOperatorPlatformBootstrapFleetRequest(join13(output, "platform-fleet-remote.json"), requests, { configFile: cloudflareConfig, acceptanceFile: cloudflareAcceptance });
|
|
18722
18871
|
return { configs, requests, fleetRequest, cloudflareConfig, cloudflareAcceptance };
|
|
18723
18872
|
}
|
|
18724
18873
|
function interactiveBootstrap(kind, genesisGuests, genesisCloudflareCheckpoint) {
|
|
@@ -19457,7 +19606,7 @@ async function cmdDeploy(options, args) {
|
|
|
19457
19606
|
if (options.optionError)
|
|
19458
19607
|
throw new Error(options.optionError);
|
|
19459
19608
|
if (operation === "init") {
|
|
19460
|
-
if (options.deploySoftware.length > 0 || options.deployProfile !== "app" || options.requireAttestation) {
|
|
19609
|
+
if (!options.deployInitCustomized && (options.deploySoftware.length > 0 || options.deployProfile !== "app" || options.requireAttestation)) {
|
|
19461
19610
|
const created = initializeDeployFile(options.projectRoot, {
|
|
19462
19611
|
name: options.projectName,
|
|
19463
19612
|
profile: options.deployProfile,
|
|
@@ -19472,7 +19621,23 @@ async function cmdDeploy(options, args) {
|
|
|
19472
19621
|
out.ok(`Initialized legacy .fz/deploy.json (${created.summary.digest}).`);
|
|
19473
19622
|
return 0;
|
|
19474
19623
|
}
|
|
19475
|
-
|
|
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
|
+
});
|
|
19476
19641
|
const compiled = await compileDeploymentProject(options.projectRoot);
|
|
19477
19642
|
if (options.json)
|
|
19478
19643
|
out.line(JSON.stringify({ source: compiled.source, output: compiled.output, digest: compiled.digest }, null, 2));
|
|
@@ -19724,7 +19889,9 @@ function usage() {
|
|
|
19724
19889
|
fz project check Fail when truth sources or generated adapters drift
|
|
19725
19890
|
fz app build Build the static App for a numbered local/development/
|
|
19726
19891
|
production choice, --profile, or an explicit --api origin
|
|
19727
|
-
|
|
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
|
|
19728
19895
|
fz deploy compile Compile TypeScript into .fz/deploy.plan.json
|
|
19729
19896
|
fz deploy check Validate source, plan, actions, providers and topology
|
|
19730
19897
|
fz deploy sync Prove readiness and print the Git synchronization rule
|
|
@@ -19795,6 +19962,15 @@ function usage() {
|
|
|
19795
19962
|
--software <key@ver> Initial tested software coordinate; repeatable
|
|
19796
19963
|
--channel <name> Catalog view: production or development (shows testing)
|
|
19797
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
|
|
19798
19974
|
--force Init may replace an existing generated target
|
|
19799
19975
|
|
|
19800
19976
|
REMOTE DEPLOY OPTIONS
|