@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/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.15";
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 join12, resolve as resolve12 } from "path";
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 fileURLToPath3 } from "url";
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.81";
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 { dirname as dirname5, isAbsolute, relative, resolve as resolve4, sep as sep3 } from "path";
10370
- import { pathToFileURL } from "url";
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 defaultTypeScriptDeployment(name) {
11522
- return `import { actions, application, defineDeployment, input, providers, stage, target, workflow } from '@forgezero/agent/deploy';
11523
-
11524
- export default defineDeployment({
11525
- apiVersion: 'deploy.forgezero.net/v1',
11526
- kind: 'Deployment',
11527
- metadata: { name: '${safeName2(name)}' },
11528
- spec: {
11529
- security: { attestation: 'preferred' },
11530
- inputs: { replicas: input.integer({ minimum: 1, maximum: 32, default: 1 }) },
11531
- targets: {
11532
- app: target.compute({
11533
- profiles: ['app'],
11534
- replicas: { minimum: 1, desired: input.ref('replicas'), maximum: 32 },
11535
- resources: { cpuCores: 1, memoryMiB: 512, storageGiB: 8 },
11536
- os: 'ubuntu-24.04', runtime: 'native', isolation: 'standard', reuse: 'require'
11537
- })
11538
- },
11539
- requirements: { bun: providers.bun.require() },
11540
- components: {
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
- workflows: {
11550
- deploy: workflow({
11551
- stages: {
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 module = await import(`${pathToFileURL(source).href}?forgezero=${status.mtimeMs}`);
11600
- if (module.default === undefined)
11601
- throw new Error(`${sourceFile} must export one default deployment definition`);
11602
- return module.default;
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 join5 } from "path";
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() || join5(homedir(), ".local", "state");
11755
- return join5(state, "forgezero", "sessions.json");
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) || input.cpuPoolKey !== undefined && pools.has(input.cpuPoolKey)) {
11913
- throw new Error("platform genesis guest names, addresses and explicit CPU pools must be unique");
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 join6, resolve as resolve5 } from "path";
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 join6(`${resolve5(checkpointPath)}.hosts`, `${normalized}.json`);
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(join6(directory, name));
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 = fileURLToPath(new URL("./fz-agent.js", import.meta.url));
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 join7 = db.role === "joiner" ? ` --starter.join=${unitEscape(db.master)}` : "";
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${join7}${agency}
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 join10, resolve as resolve9 } from "path";
15499
- import { fileURLToPath as fileURLToPath2 } from "url";
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 join9, resolve as resolve8 } from "path";
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 join8 } from "path";
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 join7 } from "path";
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(join8(unitDir, "forgezero-guests.slice"), metalGuestSliceUnit(profile), { mode: 420 });
15815
+ writeFileSync10(join9(unitDir, "forgezero-guests.slice"), metalGuestSliceUnit(profile), { mode: 420 });
15723
15816
  for (const unit of ["system.slice", "user.slice"]) {
15724
- const directory = join8(unitDir, `${unit}.d`);
15817
+ const directory = join9(unitDir, `${unit}.d`);
15725
15818
  mkdirSync10(directory, { recursive: true });
15726
- writeFileSync10(join8(directory, "50-forgezero-housekeeping.conf"), metalHousekeepingDropIn(profile, "slice"), { mode: 420 });
15819
+ writeFileSync10(join9(directory, "50-forgezero-housekeeping.conf"), metalHousekeepingDropIn(profile, "slice"), { mode: 420 });
15727
15820
  }
15728
- const initDirectory = join8(unitDir, "init.scope.d");
15821
+ const initDirectory = join9(unitDir, "init.scope.d");
15729
15822
  mkdirSync10(initDirectory, { recursive: true });
15730
- writeFileSync10(join8(initDirectory, "50-forgezero-housekeeping.conf"), metalHousekeepingDropIn(profile, "scope"), { mode: 420 });
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, join9(release, "fz-agent.js"));
16111
- chmodSync5(join9(release, "fz-agent.js"), 493);
16112
- chownSync(join9(release, "fz-agent.js"), 0, 0);
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(join9(UNIT_DIRECTORY, unit), body, 420);
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(join9(UNIT_DIRECTORY, unit)))
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 = join10(directory, "known_hosts");
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 = mkdtempSync(join10(tmpdir(), "forgezero-operator-host-keys-"));
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 = join10(directory, name);
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 = join10(directory, "platform-config.json");
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(join10(directory, name), ownerFile(config.agentSeedFile, SECRET_LIMIT, name), {
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 = join10(directory, "metal-config.json");
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 = join10(directory, "bun.zip");
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 ?? fileURLToPath2(new URL("./fz.js", import.meta.url)), "fz.js"],
17107
- [options.fzAgentPath ?? fileURLToPath2(new URL("./fz-agent.js", import.meta.url)), "fz-agent.js"],
17108
- [options.fzGitSshPath ?? fileURLToPath2(new URL("./fz-git-ssh.js", import.meta.url)), "fz-git-ssh.js"]
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 = mkdtempSync(join10(tmpdir(), "forgezero-operator-bootstrap-"));
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, join10(directory, name), `${remoteTemp}/${name}`, true);
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 = mkdtempSync(join10(tmpdir(), "forgezero-operator-metal-"));
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, join10(directory, name), `${remoteTemp}/${name}`, true);
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 join11, relative as relative2, resolve as resolve10 } from "path";
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 = join11(root, "package.json");
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 = join11(root, ...parts);
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(join11(root, "api"));
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 = join11(root, "tests", "rehearsals", "community-cluster-api");
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 = fileURLToPath3(new URL("./fz-agent.js", import.meta.url));
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 === "--force")
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} exclusive CPU pool key`),
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(join12(output, "metal.json"), config);
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(join12(output, "metal-remote.json"), {
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 = join12(output, "cloudflare-handoff.json");
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(join12(output, "cloudflare.json"), createCloudflareBootstrapDiscoveryCommandConfig({
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 = join12(output, "cloudflare-acceptance.json");
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(join12(output, `${guest.name}-remote.json`), {
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(join12(output, "platform-fleet-remote.json"), requests, { configFile: cloudflareConfig, acceptanceFile: cloudflareAcceptance });
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
- initializeTypeScriptDeployment(options.projectRoot, { name: options.projectName ?? basename3(resolve12(options.projectRoot)), force: options.force });
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
- fz deploy init Create forgezero.deploy.ts and its fail-safe canonical plan
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