@forgezero/agent 0.1.82 → 0.1.84

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 CHANGED
@@ -85,6 +85,8 @@ Every row links to the detailed explanation and named-import/example area below.
85
85
 
86
86
  bun add -g @forgezero/agent — Install the version-matched fz operator CLI and fz-agent daemon.
87
87
  fz deploy init — Create a typed forgezero.deploy.ts and its canonical inert execution plan.
88
+ fz deploy init --runtime containerd --os ubuntu-24.04 --replicas 1 --cpu-cores 2 --memory-mib 2048 --storage-gib 20 --reuse require — Create one ordinary containerd/runc compute definition with exact reserved resources; existing eligible project capacity is required.
89
+ fz deploy init --runtime kata-snp --os ubuntu-26.04 --replicas 1 --cpu-cores 2 --memory-mib 4096 --storage-gib 40 --reuse prefer — Create one Kata QEMU SEV-SNP definition; reuse eligible capacity first, then provisioning still requires an explicit approved plan and spend ceiling.
88
90
  fz deploy compile — Compile and validate TypeScript deployment intent into .fz/deploy.plan.json.
89
91
  fz deploy check — Refuse stale plans, unsafe provider/action coordinates and unresolved deployment blockers.
90
92
  fz bootstrap platform bundle --root /absolute/api --branch dev --output /secure/forgezero/development/api.bundle — Build one verified release-generation-one source bundle without a Git credential.
@@ -97,6 +99,8 @@ fz status — Read installed service and bootstrap evidence.
97
99
  ```text
98
100
  bun add -g @forgezero/agent
99
101
  fz deploy init
102
+ fz deploy init --runtime containerd --os ubuntu-24.04 --replicas 1 --cpu-cores 2 --memory-mib 2048 --storage-gib 20 --reuse require
103
+ fz deploy init --runtime kata-snp --os ubuntu-26.04 --replicas 1 --cpu-cores 2 --memory-mib 4096 --storage-gib 40 --reuse prefer
100
104
  fz deploy compile
101
105
  fz deploy check
102
106
  fz bootstrap platform bundle --root /absolute/api --branch dev --output /secure/forgezero/development/api.bundle
@@ -1402,7 +1406,7 @@ await runDeploymentPlan({
1402
1406
  <a id="forgezero-agent-deploy-compiler"></a>
1403
1407
  ## @forgezero/agent/deploy-compiler
1404
1408
 
1405
- Explicit developer/CI compiler from forgezero.deploy.ts to canonical .fz/deploy.plan.json; never used to execute project TypeScript in production. This entry exposes 7 named value exports and 1 named type export. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
1409
+ Explicit developer/CI compiler from forgezero.deploy.ts to canonical .fz/deploy.plan.json; never used to execute project TypeScript in production. This entry exposes 7 named value exports and 4 named type exports. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
1406
1410
 
1407
1411
  ```text
1408
1412
  import {
@@ -1417,6 +1421,9 @@ import {
1417
1421
 
1418
1422
  import type {
1419
1423
  DeploymentCompilation,
1424
+ DeploymentInitOptions,
1425
+ DeploymentInitProvisioning,
1426
+ DeploymentInitRuntime,
1420
1427
  } from '@forgezero/agent/deploy-compiler';
1421
1428
  ```
1422
1429
 
@@ -750,7 +750,7 @@ async function postSignedNode(options, path, body) {
750
750
  }
751
751
 
752
752
  // src/version.ts
753
- var VERSION3 = "0.1.82";
753
+ var VERSION3 = "0.1.84";
754
754
 
755
755
  // src/agent-heartbeat.ts
756
756
  function readAgentHostMetrics() {
package/dist/bootstrap.js CHANGED
@@ -1416,7 +1416,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
1416
1416
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
1417
1417
 
1418
1418
  // src/version.ts
1419
- var VERSION = "0.1.82";
1419
+ var VERSION = "0.1.84";
1420
1420
 
1421
1421
  // src/software.ts
1422
1422
  var PINNED_BUN_VERSION = "1.3.14";
@@ -8,11 +8,34 @@ export interface DeploymentCompilation {
8
8
  digest: `sha256:${string}`;
9
9
  changed: boolean;
10
10
  }
11
- export declare function defaultTypeScriptDeployment(name: string): string;
11
+ export type DeploymentInitRuntime = 'native' | 'containerd' | 'kata-snp';
12
+ export interface DeploymentInitProvisioning {
13
+ planKey: string;
14
+ regionKey: string;
15
+ imageKey: string;
16
+ environmentKey: string;
17
+ ownership: 'platform' | 'tenant-metal';
18
+ metalHostname?: string;
19
+ maxMonthlySpendMinor: string;
20
+ }
21
+ export interface DeploymentInitOptions {
22
+ runtime?: DeploymentInitRuntime;
23
+ operatingSystem?: 'ubuntu-24.04' | 'ubuntu-26.04';
24
+ isolation?: 'standard' | 'sev-snp';
25
+ replicas?: number;
26
+ cpuCores?: number;
27
+ memoryMiB?: number;
28
+ storageGiB?: number;
29
+ sharing?: 'exclusive' | 'shared';
30
+ reuse?: 'require' | 'prefer';
31
+ profile?: string;
32
+ provisioning?: DeploymentInitProvisioning;
33
+ }
34
+ export declare function defaultTypeScriptDeployment(name: string, options?: DeploymentInitOptions): string;
12
35
  export declare function initializeTypeScriptDeployment(rootValue: string, options: {
13
36
  name: string;
14
37
  force?: boolean;
15
- }): string;
38
+ } & DeploymentInitOptions): string;
16
39
  export declare function loadDeploymentSource(root: string, sourceFile?: string): Promise<unknown>;
17
40
  export declare function compileDeploymentProject(rootValue: string, options?: {
18
41
  sourceFile?: string;
@@ -1142,46 +1142,106 @@ function parseDeploymentPlan(value) {
1142
1142
  }
1143
1143
 
1144
1144
  // src/deploy-compiler.ts
1145
- import { existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
1146
- import { dirname, isAbsolute, relative, resolve, sep } from "path";
1147
- import { pathToFileURL } from "url";
1145
+ import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
1146
+ import { tmpdir } from "os";
1147
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "path";
1148
+ import { fileURLToPath, pathToFileURL } from "url";
1148
1149
  var DEPLOY_SOURCE_FILE = "forgezero.deploy.ts";
1149
1150
  var DEPLOY_PLAN_FILE = ".fz/deploy.plan.json";
1150
1151
  function safeName(value) {
1151
1152
  const result = value.toLowerCase().replace(/^@[^/]+\//, "").replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 63);
1152
1153
  return /^[a-z]/.test(result) ? result : `app-${result || "service"}`;
1153
1154
  }
1154
- function defaultTypeScriptDeployment(name) {
1155
- return `import { actions, application, defineDeployment, input, providers, stage, target, workflow } from '@forgezero/agent/deploy';
1156
-
1157
- export default defineDeployment({
1158
- apiVersion: 'deploy.forgezero.net/v1',
1159
- kind: 'Deployment',
1160
- metadata: { name: '${safeName(name)}' },
1161
- spec: {
1162
- security: { attestation: 'preferred' },
1163
- inputs: { replicas: input.integer({ minimum: 1, maximum: 32, default: 1 }) },
1164
- targets: {
1165
- app: target.compute({
1166
- profiles: ['app'],
1167
- replicas: { minimum: 1, desired: input.ref('replicas'), maximum: 32 },
1168
- resources: { cpuCores: 1, memoryMiB: 512, storageGiB: 8 },
1169
- os: 'ubuntu-24.04', runtime: 'native', isolation: 'standard', reuse: 'require'
1170
- })
1171
- },
1172
- requirements: { bun: providers.bun.require() },
1173
- components: {
1155
+ function initCoordinates(options) {
1156
+ const runtime = options.runtime ?? "native";
1157
+ const isolation = options.isolation ?? (runtime === "kata-snp" ? "sev-snp" : "standard");
1158
+ const operatingSystem = options.operatingSystem ?? (isolation === "sev-snp" ? "ubuntu-26.04" : "ubuntu-24.04");
1159
+ const result = {
1160
+ runtime,
1161
+ isolation,
1162
+ operatingSystem,
1163
+ replicas: options.replicas ?? 1,
1164
+ cpuCores: options.cpuCores ?? 1,
1165
+ memoryMiB: options.memoryMiB ?? 512,
1166
+ storageGiB: options.storageGiB ?? 8,
1167
+ sharing: options.sharing ?? "exclusive",
1168
+ reuse: options.reuse ?? "require",
1169
+ profile: options.profile ?? "app"
1170
+ };
1171
+ if (!/^[a-z][a-z0-9-]{0,62}$/.test(result.profile))
1172
+ throw new Error("deployment profile must be a lowercase typed name");
1173
+ if (!Number.isSafeInteger(result.replicas) || result.replicas < 1 || result.replicas > 1024)
1174
+ throw new Error("deployment replicas must be an integer from 1 to 1024");
1175
+ if (!Number.isSafeInteger(result.cpuCores) || result.cpuCores < 1 || result.cpuCores > 1024)
1176
+ throw new Error("deployment CPU cores must be an integer from 1 to 1024");
1177
+ if (!Number.isSafeInteger(result.memoryMiB) || result.memoryMiB < 128 || result.memoryMiB > 4194304)
1178
+ throw new Error("deployment memory must be an integer from 128 to 4194304 MiB");
1179
+ if (!Number.isSafeInteger(result.storageGiB) || result.storageGiB < 1 || result.storageGiB > 1048576)
1180
+ throw new Error("deployment storage must be an integer from 1 to 1048576 GiB");
1181
+ if (runtime === "kata-snp" && isolation !== "sev-snp")
1182
+ throw new Error("kata-snp requires sev-snp isolation");
1183
+ if (runtime === "containerd" && isolation === "sev-snp")
1184
+ throw new Error("containerd runc cannot provide sev-snp; use kata-snp");
1185
+ if (isolation === "sev-snp" && operatingSystem !== "ubuntu-26.04")
1186
+ throw new Error("sev-snp requires ubuntu-26.04");
1187
+ if (runtime === "native" && result.sharing !== "exclusive")
1188
+ throw new Error("native deployments require exclusive compute");
1189
+ if (result.reuse === "prefer" && options.provisioning === undefined)
1190
+ throw new Error("reuse prefer requires approved provisioning coordinates");
1191
+ if (result.reuse === "require" && options.provisioning !== undefined)
1192
+ throw new Error("reuse require cannot include provisioning coordinates");
1193
+ if (options.provisioning !== undefined) {
1194
+ for (const [label, value] of Object.entries({
1195
+ plan: options.provisioning.planKey,
1196
+ region: options.provisioning.regionKey,
1197
+ image: options.provisioning.imageKey,
1198
+ environment: options.provisioning.environmentKey
1199
+ }))
1200
+ if (!/^[a-z][a-z0-9-]{0,62}$/.test(value))
1201
+ throw new Error(`deployment provisioning ${label} must be a lowercase typed name`);
1202
+ if (!/^\d{1,18}$/.test(options.provisioning.maxMonthlySpendMinor))
1203
+ throw new Error("deployment provisioning monthly spend must be a bounded decimal minor-unit amount");
1204
+ if (options.provisioning.ownership === "tenant-metal" && !options.provisioning.metalHostname)
1205
+ throw new Error("tenant-metal provisioning requires a metal hostname");
1206
+ if (options.provisioning.ownership === "platform" && options.provisioning.metalHostname)
1207
+ throw new Error("platform provisioning cannot bind a tenant metal hostname");
1208
+ if (options.provisioning.metalHostname && !/^(?=.{1,253}$)[A-Za-z0-9](?:[A-Za-z0-9.-]*[A-Za-z0-9])?$/.test(options.provisioning.metalHostname))
1209
+ throw new Error("deployment provisioning metal hostname is invalid");
1210
+ }
1211
+ return { ...result, ...options.provisioning ? { provisioning: options.provisioning } : {} };
1212
+ }
1213
+ function defaultTypeScriptDeployment(name, options = {}) {
1214
+ const selected = initCoordinates(options);
1215
+ const execution = selected.runtime === "native" ? "native" : selected.runtime === "containerd" ? "oci-runc" : "oci-kata-qemu-snp";
1216
+ const hostPackageVersion = selected.operatingSystem === "ubuntu-24.04" ? "ubuntu-24.04" : "ubuntu-26.04";
1217
+ const provisioningBlock = selected.provisioning === undefined ? "" : `,
1218
+ provisioning: { planKey: '${selected.provisioning.planKey}', regionKey: '${selected.provisioning.regionKey}', imageKey: '${selected.provisioning.imageKey}', environmentKey: '${selected.provisioning.environmentKey}', ownership: '${selected.provisioning.ownership}',${selected.provisioning.metalHostname ? ` metalHostname: '${selected.provisioning.metalHostname}',` : ""} maxMonthlySpendMinor: '${selected.provisioning.maxMonthlySpendMinor}' }`;
1219
+ const requirementBlock = selected.runtime === "native" ? `bun: providers.bun.require()` : selected.runtime === "containerd" ? `containerd: providers.containerd.require({ version: '${hostPackageVersion}' }),
1220
+ nginx: providers.nginx.require({ version: '${hostPackageVersion}' })` : `containerd: providers.containerd.require(),
1221
+ kata: providers.kata.require(),
1222
+ nginx: providers.nginx.require()`;
1223
+ const componentBlock = selected.runtime === "native" ? `
1174
1224
  app: application({
1175
1225
  target: 'app',
1176
1226
  runtime: { kind: 'native', provider: 'forgezero.bun', requirement: 'bun', argv: ['/usr/local/bin/bun', 'run', 'start'] },
1177
1227
  service: { protocol: 'http', port: 3000, health: { protocol: 'http', method: 'GET', path: '/health', expectedStatus: [200], timeoutMs: 5_000 } },
1178
1228
  resources: {},
1179
1229
  rollout: { strategy: 'direct' }
1180
- })
1181
- },
1182
- workflows: {
1183
- deploy: workflow({
1184
- stages: {
1230
+ })` : `
1231
+ app: application({
1232
+ target: 'app',
1233
+ runtime: {
1234
+ kind: 'container', provider: '${selected.runtime === "containerd" ? "forgezero.containerd" : "forgezero.kata"}', requirement: '${selected.runtime === "containerd" ? "containerd" : "kata"}', runtimeClass: '${selected.runtime === "containerd" ? "runc" : "kata-qemu-snp"}',
1235
+ image: { source: { kind: 'build', context: '.', dockerfile: 'Dockerfile' } },
1236
+ security: { privileged: false, noNewPrivileges: true, root: 'read-only', dropCapabilities: ['ALL'] }
1237
+ },
1238
+ service: { protocol: 'http', port: 3000, health: { protocol: 'http', method: 'GET', path: '/health', expectedStatus: [200], timeoutMs: 5_000 } },
1239
+ resources: { cpu: { limit: ${selected.cpuCores} }, memory: { limitMiB: ${selected.memoryMiB}, swap: 'disabled' }, pids: { limit: 256 } },
1240
+ storage: [{ class: 'ephemeral', path: '/tmp', type: 'tmpfs', sizeMiB: ${Math.min(128, Math.max(16, Math.floor(selected.memoryMiB / 4)))} }],
1241
+ network: { ingress: { exposure: 'loopback', stablePort: 3000 }, container: { mode: 'bridge', network: 'app' } },
1242
+ rollout: { strategy: 'blue-green', proxy: 'nginx', drainMs: 30_000, automaticRollback: true }
1243
+ })`;
1244
+ const workflowBlock = selected.runtime === "native" ? `
1185
1245
  build: stage({ strategy: { mode: 'sequential' }, steps: {
1186
1246
  build: actions.exec.argv(
1187
1247
  { component: 'app', argv: ['/usr/local/bin/fz-agent', 'pipeline-todo', 'replace with the project build argv'] },
@@ -1196,7 +1256,42 @@ export default defineDeployment({
1196
1256
  } }),
1197
1257
  verify: stage({ dependsOn: ['release'], strategy: { mode: 'parallel' }, steps: {
1198
1258
  health: actions.service.health({ component: 'app' }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 30_000 })
1199
- } })
1259
+ } })` : `
1260
+ prepare: stage({ strategy: { mode: 'sequential' }, steps: {
1261
+ software: actions.software.ensure({ requirements: [${selected.runtime === "containerd" ? "'containerd', 'nginx'" : "'containerd', 'kata', 'nginx'"}] }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 900_000 }),
1262
+ review: actions.exec.argv({ component: 'app', argv: ['/usr/local/bin/fz-agent', 'pipeline-todo', 'review the Dockerfile and container entrypoint'] }, { scope: { kind: 'release-executor' } })
1263
+ } }),
1264
+ build: stage({ dependsOn: ['prepare'], strategy: { mode: 'sequential' }, steps: {
1265
+ image: actions.container.build({ component: 'app' }, { scope: { kind: 'release-executor' }, timeoutMs: 900_000 })
1266
+ } }),
1267
+ release: stage({ dependsOn: ['build'], strategy: { mode: 'blue-green', maximumConcurrency: 1, minimumHealthy: 1 }, steps: {
1268
+ promote: actions.service.promote({ component: 'app', imageDigest: { $ref: 'steps.image.outputs.digest' } }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 180_000 })
1269
+ } }),
1270
+ verify: stage({ dependsOn: ['release'], strategy: { mode: 'parallel' }, steps: {
1271
+ health: actions.service.health({ component: 'app' }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 30_000 })
1272
+ } })`;
1273
+ return `import { actions, application, defineDeployment, providers, stage, target, workflow } from '@forgezero/agent/deploy';
1274
+
1275
+ export default defineDeployment({
1276
+ apiVersion: 'deploy.forgezero.net/v1',
1277
+ kind: 'Deployment',
1278
+ metadata: { name: '${safeName(name)}' },
1279
+ spec: {
1280
+ security: { attestation: '${selected.isolation === "sev-snp" ? "required" : "preferred"}' },
1281
+ targets: {
1282
+ app: target.compute({
1283
+ profiles: ['${selected.profile}'], replicas: ${selected.replicas},
1284
+ resources: { cpuCores: ${selected.cpuCores}, memoryMiB: ${selected.memoryMiB}, storageGiB: ${selected.storageGiB} },
1285
+ os: '${selected.operatingSystem}', runtime: '${execution}', isolation: '${selected.isolation}',
1286
+ sharing: '${selected.sharing}', reuse: '${selected.reuse}'${provisioningBlock}
1287
+ })
1288
+ },
1289
+ requirements: { ${requirementBlock} },
1290
+ components: {${componentBlock}
1291
+ },
1292
+ workflows: {
1293
+ deploy: workflow({
1294
+ stages: {${workflowBlock}
1200
1295
  }
1201
1296
  })
1202
1297
  }
@@ -1208,7 +1303,7 @@ function initializeTypeScriptDeployment(rootValue, options) {
1208
1303
  const path = localPath(rootValue, DEPLOY_SOURCE_FILE, "deployment source");
1209
1304
  if (existsSync(path) && !options.force)
1210
1305
  throw new Error(`${DEPLOY_SOURCE_FILE} already exists; use --force only when replacing it deliberately`);
1211
- writeFileSync(path, defaultTypeScriptDeployment(options.name), { mode: 420, flag: options.force ? "w" : "wx" });
1306
+ writeFileSync(path, defaultTypeScriptDeployment(options.name, options), { mode: 420, flag: options.force ? "w" : "wx" });
1212
1307
  return path;
1213
1308
  }
1214
1309
  function inside(root, path) {
@@ -1229,10 +1324,35 @@ async function loadDeploymentSource(root, sourceFile = DEPLOY_SOURCE_FILE) {
1229
1324
  const status = lstatSync(source);
1230
1325
  if (!status.isFile() || status.isSymbolicLink() || status.size > 2 * 1024 * 1024)
1231
1326
  throw new Error("deployment source must be one bounded regular file");
1232
- const module = await import(`${pathToFileURL(source).href}?forgezero=${status.mtimeMs}`);
1233
- if (module.default === undefined)
1234
- throw new Error(`${sourceFile} must export one default deployment definition`);
1235
- return module.default;
1327
+ const builtDeploy = new URL("./deploy.js", import.meta.url);
1328
+ const sourceDeploy = new URL("./deploy.ts", import.meta.url);
1329
+ const deployModule = existsSync(fileURLToPath(builtDeploy)) ? builtDeploy.href : sourceDeploy.href;
1330
+ const result = await Bun.build({
1331
+ entrypoints: [source],
1332
+ target: "bun",
1333
+ format: "esm",
1334
+ minify: false,
1335
+ plugins: [{
1336
+ name: "forgezero-deploy-authoring",
1337
+ setup(builder) {
1338
+ builder.onResolve({ filter: /^@forgezero\/agent\/deploy$/ }, () => ({ path: fileURLToPath(deployModule) }));
1339
+ }
1340
+ }]
1341
+ });
1342
+ if (!result.success || result.outputs.length !== 1) {
1343
+ throw new Error(`deployment source compilation failed: ${result.logs.map((entry) => entry.message).join("; ")}`);
1344
+ }
1345
+ const directory = mkdtempSync(join(tmpdir(), "forgezero-deploy-compile-"));
1346
+ const compiled = join(directory, "deployment.mjs");
1347
+ try {
1348
+ writeFileSync(compiled, Buffer.from(await result.outputs[0].arrayBuffer()), { mode: 384, flag: "wx" });
1349
+ const module = await import(`${pathToFileURL(compiled).href}?forgezero=${status.mtimeMs}`);
1350
+ if (module.default === undefined)
1351
+ throw new Error(`${sourceFile} must export one default deployment definition`);
1352
+ return module.default;
1353
+ } finally {
1354
+ rmSync(directory, { recursive: true, force: true });
1355
+ }
1236
1356
  }
1237
1357
  async function compileDeploymentProject(rootValue, options = {}) {
1238
1358
  const root = resolve(rootValue);
package/dist/fz-agent.js CHANGED
@@ -9331,7 +9331,7 @@ async function writeAndCloseProcessInput(input, value) {
9331
9331
  }
9332
9332
 
9333
9333
  // src/version.ts
9334
- var VERSION2 = "0.1.82";
9334
+ var VERSION2 = "0.1.84";
9335
9335
 
9336
9336
  // src/ssh-bootstrap.ts
9337
9337
  class SshBootstrapError extends Error {
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.17";
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.82";
4840
+ var VERSION2 = "0.1.84";
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,96 @@ 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
+ if (result.reuse === "prefer" && options.provisioning === undefined)
11557
+ throw new Error("reuse prefer requires approved provisioning coordinates");
11558
+ if (result.reuse === "require" && options.provisioning !== undefined)
11559
+ throw new Error("reuse require cannot include provisioning coordinates");
11560
+ if (options.provisioning !== undefined) {
11561
+ for (const [label, value] of Object.entries({
11562
+ plan: options.provisioning.planKey,
11563
+ region: options.provisioning.regionKey,
11564
+ image: options.provisioning.imageKey,
11565
+ environment: options.provisioning.environmentKey
11566
+ }))
11567
+ if (!/^[a-z][a-z0-9-]{0,62}$/.test(value))
11568
+ throw new Error(`deployment provisioning ${label} must be a lowercase typed name`);
11569
+ if (!/^\d{1,18}$/.test(options.provisioning.maxMonthlySpendMinor))
11570
+ throw new Error("deployment provisioning monthly spend must be a bounded decimal minor-unit amount");
11571
+ if (options.provisioning.ownership === "tenant-metal" && !options.provisioning.metalHostname)
11572
+ throw new Error("tenant-metal provisioning requires a metal hostname");
11573
+ if (options.provisioning.ownership === "platform" && options.provisioning.metalHostname)
11574
+ throw new Error("platform provisioning cannot bind a tenant metal hostname");
11575
+ if (options.provisioning.metalHostname && !/^(?=.{1,253}$)[A-Za-z0-9](?:[A-Za-z0-9.-]*[A-Za-z0-9])?$/.test(options.provisioning.metalHostname))
11576
+ throw new Error("deployment provisioning metal hostname is invalid");
11577
+ }
11578
+ return { ...result, ...options.provisioning ? { provisioning: options.provisioning } : {} };
11579
+ }
11580
+ function defaultTypeScriptDeployment(name, options = {}) {
11581
+ const selected = initCoordinates(options);
11582
+ const execution = selected.runtime === "native" ? "native" : selected.runtime === "containerd" ? "oci-runc" : "oci-kata-qemu-snp";
11583
+ const hostPackageVersion = selected.operatingSystem === "ubuntu-24.04" ? "ubuntu-24.04" : "ubuntu-26.04";
11584
+ const provisioningBlock = selected.provisioning === undefined ? "" : `,
11585
+ provisioning: { planKey: '${selected.provisioning.planKey}', regionKey: '${selected.provisioning.regionKey}', imageKey: '${selected.provisioning.imageKey}', environmentKey: '${selected.provisioning.environmentKey}', ownership: '${selected.provisioning.ownership}',${selected.provisioning.metalHostname ? ` metalHostname: '${selected.provisioning.metalHostname}',` : ""} maxMonthlySpendMinor: '${selected.provisioning.maxMonthlySpendMinor}' }`;
11586
+ const requirementBlock = selected.runtime === "native" ? `bun: providers.bun.require()` : selected.runtime === "containerd" ? `containerd: providers.containerd.require({ version: '${hostPackageVersion}' }),
11587
+ nginx: providers.nginx.require({ version: '${hostPackageVersion}' })` : `containerd: providers.containerd.require(),
11588
+ kata: providers.kata.require(),
11589
+ nginx: providers.nginx.require()`;
11590
+ const componentBlock = selected.runtime === "native" ? `
11541
11591
  app: application({
11542
11592
  target: 'app',
11543
11593
  runtime: { kind: 'native', provider: 'forgezero.bun', requirement: 'bun', argv: ['/usr/local/bin/bun', 'run', 'start'] },
11544
11594
  service: { protocol: 'http', port: 3000, health: { protocol: 'http', method: 'GET', path: '/health', expectedStatus: [200], timeoutMs: 5_000 } },
11545
11595
  resources: {},
11546
11596
  rollout: { strategy: 'direct' }
11547
- })
11548
- },
11549
- workflows: {
11550
- deploy: workflow({
11551
- stages: {
11597
+ })` : `
11598
+ app: application({
11599
+ target: 'app',
11600
+ runtime: {
11601
+ kind: 'container', provider: '${selected.runtime === "containerd" ? "forgezero.containerd" : "forgezero.kata"}', requirement: '${selected.runtime === "containerd" ? "containerd" : "kata"}', runtimeClass: '${selected.runtime === "containerd" ? "runc" : "kata-qemu-snp"}',
11602
+ image: { source: { kind: 'build', context: '.', dockerfile: 'Dockerfile' } },
11603
+ security: { privileged: false, noNewPrivileges: true, root: 'read-only', dropCapabilities: ['ALL'] }
11604
+ },
11605
+ service: { protocol: 'http', port: 3000, health: { protocol: 'http', method: 'GET', path: '/health', expectedStatus: [200], timeoutMs: 5_000 } },
11606
+ resources: { cpu: { limit: ${selected.cpuCores} }, memory: { limitMiB: ${selected.memoryMiB}, swap: 'disabled' }, pids: { limit: 256 } },
11607
+ storage: [{ class: 'ephemeral', path: '/tmp', type: 'tmpfs', sizeMiB: ${Math.min(128, Math.max(16, Math.floor(selected.memoryMiB / 4)))} }],
11608
+ network: { ingress: { exposure: 'loopback', stablePort: 3000 }, container: { mode: 'bridge', network: 'app' } },
11609
+ rollout: { strategy: 'blue-green', proxy: 'nginx', drainMs: 30_000, automaticRollback: true }
11610
+ })`;
11611
+ const workflowBlock = selected.runtime === "native" ? `
11552
11612
  build: stage({ strategy: { mode: 'sequential' }, steps: {
11553
11613
  build: actions.exec.argv(
11554
11614
  { component: 'app', argv: ['/usr/local/bin/fz-agent', 'pipeline-todo', 'replace with the project build argv'] },
@@ -11563,7 +11623,42 @@ export default defineDeployment({
11563
11623
  } }),
11564
11624
  verify: stage({ dependsOn: ['release'], strategy: { mode: 'parallel' }, steps: {
11565
11625
  health: actions.service.health({ component: 'app' }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 30_000 })
11566
- } })
11626
+ } })` : `
11627
+ prepare: stage({ strategy: { mode: 'sequential' }, steps: {
11628
+ software: actions.software.ensure({ requirements: [${selected.runtime === "containerd" ? "'containerd', 'nginx'" : "'containerd', 'kata', 'nginx'"}] }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 900_000 }),
11629
+ review: actions.exec.argv({ component: 'app', argv: ['/usr/local/bin/fz-agent', 'pipeline-todo', 'review the Dockerfile and container entrypoint'] }, { scope: { kind: 'release-executor' } })
11630
+ } }),
11631
+ build: stage({ dependsOn: ['prepare'], strategy: { mode: 'sequential' }, steps: {
11632
+ image: actions.container.build({ component: 'app' }, { scope: { kind: 'release-executor' }, timeoutMs: 900_000 })
11633
+ } }),
11634
+ release: stage({ dependsOn: ['build'], strategy: { mode: 'blue-green', maximumConcurrency: 1, minimumHealthy: 1 }, steps: {
11635
+ promote: actions.service.promote({ component: 'app', imageDigest: { $ref: 'steps.image.outputs.digest' } }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 180_000 })
11636
+ } }),
11637
+ verify: stage({ dependsOn: ['release'], strategy: { mode: 'parallel' }, steps: {
11638
+ health: actions.service.health({ component: 'app' }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 30_000 })
11639
+ } })`;
11640
+ return `import { actions, application, defineDeployment, providers, stage, target, workflow } from '@forgezero/agent/deploy';
11641
+
11642
+ export default defineDeployment({
11643
+ apiVersion: 'deploy.forgezero.net/v1',
11644
+ kind: 'Deployment',
11645
+ metadata: { name: '${safeName2(name)}' },
11646
+ spec: {
11647
+ security: { attestation: '${selected.isolation === "sev-snp" ? "required" : "preferred"}' },
11648
+ targets: {
11649
+ app: target.compute({
11650
+ profiles: ['${selected.profile}'], replicas: ${selected.replicas},
11651
+ resources: { cpuCores: ${selected.cpuCores}, memoryMiB: ${selected.memoryMiB}, storageGiB: ${selected.storageGiB} },
11652
+ os: '${selected.operatingSystem}', runtime: '${execution}', isolation: '${selected.isolation}',
11653
+ sharing: '${selected.sharing}', reuse: '${selected.reuse}'${provisioningBlock}
11654
+ })
11655
+ },
11656
+ requirements: { ${requirementBlock} },
11657
+ components: {${componentBlock}
11658
+ },
11659
+ workflows: {
11660
+ deploy: workflow({
11661
+ stages: {${workflowBlock}
11567
11662
  }
11568
11663
  })
11569
11664
  }
@@ -11575,7 +11670,7 @@ function initializeTypeScriptDeployment(rootValue, options) {
11575
11670
  const path = localPath(rootValue, DEPLOY_SOURCE_FILE, "deployment source");
11576
11671
  if (existsSync6(path) && !options.force)
11577
11672
  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" });
11673
+ writeFileSync6(path, defaultTypeScriptDeployment(options.name, options), { mode: 420, flag: options.force ? "w" : "wx" });
11579
11674
  return path;
11580
11675
  }
11581
11676
  function inside(root, path) {
@@ -11596,10 +11691,35 @@ async function loadDeploymentSource(root, sourceFile = DEPLOY_SOURCE_FILE) {
11596
11691
  const status = lstatSync2(source);
11597
11692
  if (!status.isFile() || status.isSymbolicLink() || status.size > 2 * 1024 * 1024)
11598
11693
  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;
11694
+ const builtDeploy = new URL("./deploy.js", import.meta.url);
11695
+ const sourceDeploy = new URL("./deploy.ts", import.meta.url);
11696
+ const deployModule = existsSync6(fileURLToPath(builtDeploy)) ? builtDeploy.href : sourceDeploy.href;
11697
+ const result = await Bun.build({
11698
+ entrypoints: [source],
11699
+ target: "bun",
11700
+ format: "esm",
11701
+ minify: false,
11702
+ plugins: [{
11703
+ name: "forgezero-deploy-authoring",
11704
+ setup(builder) {
11705
+ builder.onResolve({ filter: /^@forgezero\/agent\/deploy$/ }, () => ({ path: fileURLToPath(deployModule) }));
11706
+ }
11707
+ }]
11708
+ });
11709
+ if (!result.success || result.outputs.length !== 1) {
11710
+ throw new Error(`deployment source compilation failed: ${result.logs.map((entry) => entry.message).join("; ")}`);
11711
+ }
11712
+ const directory = mkdtempSync(join5(tmpdir(), "forgezero-deploy-compile-"));
11713
+ const compiled = join5(directory, "deployment.mjs");
11714
+ try {
11715
+ writeFileSync6(compiled, Buffer.from(await result.outputs[0].arrayBuffer()), { mode: 384, flag: "wx" });
11716
+ const module = await import(`${pathToFileURL(compiled).href}?forgezero=${status.mtimeMs}`);
11717
+ if (module.default === undefined)
11718
+ throw new Error(`${sourceFile} must export one default deployment definition`);
11719
+ return module.default;
11720
+ } finally {
11721
+ rmSync4(directory, { recursive: true, force: true });
11722
+ }
11603
11723
  }
11604
11724
  async function compileDeploymentProject(rootValue, options = {}) {
11605
11725
  const root = resolve4(rootValue);
@@ -11733,7 +11853,7 @@ import {
11733
11853
  unlinkSync,
11734
11854
  writeFileSync as writeFileSync7
11735
11855
  } from "fs";
11736
- import { dirname as dirname6, join as join5 } from "path";
11856
+ import { dirname as dirname6, join as join6 } from "path";
11737
11857
  import { homedir } from "os";
11738
11858
  var EMPTY2 = () => ({ version: 1, sessions: {} });
11739
11859
  function canonicalApi(value) {
@@ -11751,8 +11871,8 @@ ${realm.trim() || "platform"}`).toString("base64url");
11751
11871
  function defaultSessionPath(env = process.env) {
11752
11872
  if (env.FZ_SESSION_FILE?.trim())
11753
11873
  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");
11874
+ const state = env.XDG_STATE_HOME?.trim() || join6(homedir(), ".local", "state");
11875
+ return join6(state, "forgezero", "sessions.json");
11756
11876
  }
11757
11877
  function assertPrivate(path, kind) {
11758
11878
  if (!existsSync7(path))
@@ -11851,7 +11971,7 @@ import {
11851
11971
  writeFileSync as writeFileSync9
11852
11972
  } from "fs";
11853
11973
  import { dirname as dirname9 } from "path";
11854
- import { fileURLToPath } from "url";
11974
+ import { fileURLToPath as fileURLToPath2 } from "url";
11855
11975
 
11856
11976
  // src/ubuntu.ts
11857
11977
  var current = OS_CATALOG[0];
@@ -12427,7 +12547,7 @@ function planLocalOtlpProof(endpoint, collectorUnit) {
12427
12547
  import { constants } from "fs";
12428
12548
  import { createHmac, randomUUID } from "crypto";
12429
12549
  import { chmod, lstat, mkdir, open, readdir, rename, rmdir, stat, unlink } from "fs/promises";
12430
- import { dirname as dirname7, join as join6, resolve as resolve5 } from "path";
12550
+ import { dirname as dirname7, join as join7, resolve as resolve5 } from "path";
12431
12551
  import { isIP as isIP3 } from "net";
12432
12552
 
12433
12553
  // src/cloudflare-edge.ts
@@ -13099,7 +13219,7 @@ function cloudflareHostHandoffPath(checkpointPath, nodeName) {
13099
13219
  const normalized = nodeName.trim().toLowerCase();
13100
13220
  if (!/^[a-z0-9][a-z0-9-]{0,62}$/.test(normalized))
13101
13221
  throw new Error("Cloudflare host handoff node name is invalid");
13102
- return join6(`${resolve5(checkpointPath)}.hosts`, `${normalized}.json`);
13222
+ return join7(`${resolve5(checkpointPath)}.hosts`, `${normalized}.json`);
13103
13223
  }
13104
13224
  async function readCloudflareHostHandoff(handoffPath, nodeName) {
13105
13225
  let parsed;
@@ -13481,7 +13601,7 @@ async function removeCloudflareBootstrapSecrets(checkpointPath, output) {
13481
13601
  throw new Error("Cloudflare host handoff directory contains unexpected files; refusing secret cleanup");
13482
13602
  }
13483
13603
  for (const name of expected)
13484
- await unlink(join6(directory, name));
13604
+ await unlink(join7(directory, name));
13485
13605
  await rmdir(directory);
13486
13606
  }
13487
13607
  await unlink(resolve5(checkpointPath));
@@ -13923,7 +14043,7 @@ var LIFECYCLE_PROFILE = "/etc/forgezero/lifecycle.json";
13923
14043
  var CONTROL_SOCKET = "/run/forgezero/control.sock";
13924
14044
  var CLOUDFLARED_METRICS_ADDRESS = "127.0.0.1:20241";
13925
14045
  var CLOUDFLARED_DIAGNOSTICS_URL = `http://${CLOUDFLARED_METRICS_ADDRESS}/diag/tunnel`;
13926
- var PACKAGED_AGENT_BIN = fileURLToPath(new URL("./fz-agent.js", import.meta.url));
14046
+ var PACKAGED_AGENT_BIN = fileURLToPath2(new URL("./fz-agent.js", import.meta.url));
13927
14047
  var privateOrigin = (value) => {
13928
14048
  let url;
13929
14049
  try {
@@ -14150,7 +14270,7 @@ var unitEscape = (value) => {
14150
14270
  };
14151
14271
  function databaseUnit(config) {
14152
14272
  const db = config.database;
14153
- const join7 = db.role === "joiner" ? ` --starter.join=${unitEscape(db.master)}` : "";
14273
+ const join8 = db.role === "joiner" ? ` --starter.join=${unitEscape(db.master)}` : "";
14154
14274
  const agency = db.agency === "none" ? " --cluster.start-agent=false --cluster.start-coordinator=true --cluster.start-dbserver=true" : "";
14155
14275
  return `[Unit]
14156
14276
  Description=ForgeZero ArangoDB Community 3.11.14 cluster (${db.role}; agency=${db.agency})
@@ -14162,7 +14282,7 @@ Type=simple
14162
14282
  User=arangodb
14163
14283
  Group=arangodb
14164
14284
  LoadCredentialEncrypted=arangodb-jwt:${JWT_CREDENTIAL}
14165
- ExecStart=/usr/bin/arangodb --starter.mode=cluster --starter.address=${unitEscape(db.address)} --starter.host=${unitEscape(db.address)} --starter.data-dir=/var/lib/forgezero-cluster --auth.jwt-secret=%d/arangodb-jwt${join7}${agency}
14285
+ ExecStart=/usr/bin/arangodb --starter.mode=cluster --starter.address=${unitEscape(db.address)} --starter.host=${unitEscape(db.address)} --starter.data-dir=/var/lib/forgezero-cluster --auth.jwt-secret=%d/arangodb-jwt${join8}${agency}
14166
14286
  Restart=always
14167
14287
  RestartSec=5
14168
14288
  UMask=0077
@@ -15489,11 +15609,11 @@ async function runCloudflareBootstrapFinalizeCommand(configPath, dependencies =
15489
15609
 
15490
15610
  // src/operator-bootstrap.ts
15491
15611
  import { createHash as createHash6, randomBytes as randomBytes9 } from "crypto";
15492
- import { chmodSync as chmodSync6, lstatSync as lstatSync7, mkdirSync as mkdirSync12, mkdtempSync, readFileSync as readFileSync12, rmSync as rmSync7, writeFileSync as writeFileSync12 } from "fs";
15612
+ import { chmodSync as chmodSync6, lstatSync as lstatSync7, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync2, readFileSync as readFileSync12, rmSync as rmSync7, writeFileSync as writeFileSync12 } from "fs";
15493
15613
  import { isIP as isIP6 } from "net";
15494
- import { tmpdir } from "os";
15495
- import { basename as basename2, dirname as dirname13, isAbsolute as isAbsolute5, join as join10, resolve as resolve9 } from "path";
15496
- import { fileURLToPath as fileURLToPath2 } from "url";
15614
+ import { tmpdir as tmpdir2 } from "os";
15615
+ import { basename as basename2, dirname as dirname13, isAbsolute as isAbsolute5, join as join11, resolve as resolve9 } from "path";
15616
+ import { fileURLToPath as fileURLToPath3 } from "url";
15497
15617
 
15498
15618
  // src/metal-bootstrap.ts
15499
15619
  import { createHash as createHash5, randomBytes as randomBytes8 } from "crypto";
@@ -15512,15 +15632,15 @@ import {
15512
15632
  unlinkSync as unlinkSync2,
15513
15633
  writeFileSync as writeFileSync11
15514
15634
  } from "fs";
15515
- import { dirname as dirname12, isAbsolute as isAbsolute4, join as join9, resolve as resolve8 } from "path";
15635
+ import { dirname as dirname12, isAbsolute as isAbsolute4, join as join10, resolve as resolve8 } from "path";
15516
15636
  import { isIP as isIP5 } from "net";
15517
15637
 
15518
15638
  // src/metal-isolation.ts
15519
15639
  import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "fs";
15520
- import { join as join8 } from "path";
15640
+ import { join as join9 } from "path";
15521
15641
 
15522
15642
  // src/metal-provision.ts
15523
- import { dirname as dirname11, isAbsolute as isAbsolute3, join as join7 } from "path";
15643
+ import { dirname as dirname11, isAbsolute as isAbsolute3, join as join8 } from "path";
15524
15644
  import { isIP as isIP4 } from "net";
15525
15645
  var SAFE_NAME2 = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,63}$/;
15526
15646
  var DEVICE = /^[a-zA-Z][a-zA-Z0-9_.-]{0,14}$/;
@@ -15716,15 +15836,15 @@ async function applyMetalIsolation(profile, exec = defaultExec) {
15716
15836
  await requireGuestsInSlice(exec);
15717
15837
  const unitDir = profile.unitDir;
15718
15838
  mkdirSync10(unitDir, { recursive: true });
15719
- writeFileSync10(join8(unitDir, "forgezero-guests.slice"), metalGuestSliceUnit(profile), { mode: 420 });
15839
+ writeFileSync10(join9(unitDir, "forgezero-guests.slice"), metalGuestSliceUnit(profile), { mode: 420 });
15720
15840
  for (const unit of ["system.slice", "user.slice"]) {
15721
- const directory = join8(unitDir, `${unit}.d`);
15841
+ const directory = join9(unitDir, `${unit}.d`);
15722
15842
  mkdirSync10(directory, { recursive: true });
15723
- writeFileSync10(join8(directory, "50-forgezero-housekeeping.conf"), metalHousekeepingDropIn(profile, "slice"), { mode: 420 });
15843
+ writeFileSync10(join9(directory, "50-forgezero-housekeeping.conf"), metalHousekeepingDropIn(profile, "slice"), { mode: 420 });
15724
15844
  }
15725
- const initDirectory = join8(unitDir, "init.scope.d");
15845
+ const initDirectory = join9(unitDir, "init.scope.d");
15726
15846
  mkdirSync10(initDirectory, { recursive: true });
15727
- writeFileSync10(join8(initDirectory, "50-forgezero-housekeeping.conf"), metalHousekeepingDropIn(profile, "scope"), { mode: 420 });
15847
+ writeFileSync10(join9(initDirectory, "50-forgezero-housekeeping.conf"), metalHousekeepingDropIn(profile, "scope"), { mode: 420 });
15728
15848
  await checked4(exec, ["systemctl", "daemon-reload"]);
15729
15849
  await requireGuestsInSlice(exec);
15730
15850
  const properties = [`AllowedCPUs=${profile.housekeepingCpus}`];
@@ -16104,9 +16224,9 @@ var installAgentBinary = (source, version) => {
16104
16224
  validateAgentSourcePath(source);
16105
16225
  const release = `/opt/forgezero/agent/versions/${version}/dist`;
16106
16226
  mkdirSync11(release, { recursive: true, mode: 493 });
16107
- copyFileSync2(source, join9(release, "fz-agent.js"));
16108
- chmodSync5(join9(release, "fz-agent.js"), 493);
16109
- chownSync(join9(release, "fz-agent.js"), 0, 0);
16227
+ copyFileSync2(source, join10(release, "fz-agent.js"));
16228
+ chmodSync5(join10(release, "fz-agent.js"), 493);
16229
+ chownSync(join10(release, "fz-agent.js"), 0, 0);
16110
16230
  mkdirSync11("/opt/forgezero/agent", { recursive: true, mode: 493 });
16111
16231
  for (const [link, target] of [
16112
16232
  ["/opt/forgezero/agent/current.next", `versions/${version}`],
@@ -16256,7 +16376,7 @@ async function applyMetalBootstrap(config, options) {
16256
16376
  }
16257
16377
  await applyMetalIsolation(config.profile, (argv2) => exec(argv2));
16258
16378
  for (const [unit, body] of Object.entries(renderMetalUnits(config)))
16259
- atomicWrite2(join9(UNIT_DIRECTORY, unit), body, 420);
16379
+ atomicWrite2(join10(UNIT_DIRECTORY, unit), body, 420);
16260
16380
  await runChecked(exec, ["/usr/bin/systemctl", "daemon-reload"]);
16261
16381
  await runChecked(exec, [
16262
16382
  "/usr/bin/systemctl",
@@ -16390,7 +16510,7 @@ async function metalBootstrapStatus(exec = defaultExec2) {
16390
16510
  "forgezero-metal-agent-egress.service",
16391
16511
  "forgezero-metal-agent.service"
16392
16512
  ]) {
16393
- if (!existsSync10(join9(UNIT_DIRECTORY, unit)))
16513
+ if (!existsSync10(join10(UNIT_DIRECTORY, unit)))
16394
16514
  units[unit] = "missing";
16395
16515
  else
16396
16516
  units[unit] = (await exec(["/usr/bin/systemctl", "is-active", "--quiet", unit])).exitCode === 0 ? "active" : "inactive";
@@ -16919,7 +17039,7 @@ function writeKnownHosts(request, directory) {
16919
17039
  const lines = [`fz-operator-target ${request.target.hostKey}`];
16920
17040
  if (request.target.jump)
16921
17041
  lines.push(`${hostLabel(request.target.jump.address, request.target.jump.port)} ${request.target.jump.hostKey}`);
16922
- const path = join10(directory, "known_hosts");
17042
+ const path = join11(directory, "known_hosts");
16923
17043
  writeFileSync12(path, `${lines.join(`
16924
17044
  `)}
16925
17045
  `, { mode: 384, flag: "wx" });
@@ -17009,7 +17129,7 @@ async function collectOperatorGuestHostKeys(request, options = {}, includeRehear
17009
17129
  publicIdentity(request.target.identityPublicKeyFile);
17010
17130
  socketPath(request.target.agentSocket);
17011
17131
  const exec = options.exec ?? defaultExec3;
17012
- const directory = mkdtempSync(join10(tmpdir(), "forgezero-operator-host-keys-"));
17132
+ const directory = mkdtempSync2(join11(tmpdir2(), "forgezero-operator-host-keys-"));
17013
17133
  try {
17014
17134
  const knownHosts = writeKnownHosts(request, directory);
17015
17135
  const nodes = [];
@@ -17045,7 +17165,7 @@ async function stageConfig(config, directory) {
17045
17165
  const staged = [];
17046
17166
  for (const [name, source] of secretSources(config)) {
17047
17167
  const bytes = ownerFile(source, SECRET_LIMIT, name);
17048
- const local = join10(directory, name);
17168
+ const local = join11(directory, name);
17049
17169
  writeFileSync12(local, bytes, { mode: 384, flag: "wx" });
17050
17170
  staged.push(name);
17051
17171
  const remotePath = `${REMOTE_STAGE}/${name}`;
@@ -17065,7 +17185,7 @@ async function stageConfig(config, directory) {
17065
17185
  manifestFile: `${REMOTE_STAGE}/bootstrap-api.bundle.json`,
17066
17186
  branch: bundle.manifest.branch
17067
17187
  };
17068
- const path = join10(directory, "platform-config.json");
17188
+ const path = join11(directory, "platform-config.json");
17069
17189
  writeFileSync12(path, `${JSON.stringify(rewritten, null, 2)}
17070
17190
  `, { mode: 384, flag: "wx" });
17071
17191
  return { path, files: staged, bundleFiles };
@@ -17075,14 +17195,14 @@ function stageMetalConfig(config, directory) {
17075
17195
  const files = [];
17076
17196
  if (config.agentSeedFile) {
17077
17197
  const name = "metal-agent-seed";
17078
- writeFileSync12(join10(directory, name), ownerFile(config.agentSeedFile, SECRET_LIMIT, name), {
17198
+ writeFileSync12(join11(directory, name), ownerFile(config.agentSeedFile, SECRET_LIMIT, name), {
17079
17199
  mode: 384,
17080
17200
  flag: "wx"
17081
17201
  });
17082
17202
  rewritten.agentSeedFile = `${REMOTE_STAGE}/${name}`;
17083
17203
  files.push(name);
17084
17204
  }
17085
- const path = join10(directory, "metal-config.json");
17205
+ const path = join11(directory, "metal-config.json");
17086
17206
  writeFileSync12(path, `${JSON.stringify(rewritten, null, 2)}
17087
17207
  `, { mode: 384, flag: "wx" });
17088
17208
  return { path, files };
@@ -17094,15 +17214,15 @@ async function verifiedBunArchive(directory, fetcher) {
17094
17214
  const bytes = new Uint8Array(await response.arrayBuffer());
17095
17215
  if (createHash6("sha256").update(bytes).digest("hex") !== BUN_RELEASE_SHA256)
17096
17216
  throw new Error("pinned Bun checksum mismatch");
17097
- const path = join10(directory, "bun.zip");
17217
+ const path = join11(directory, "bun.zip");
17098
17218
  writeFileSync12(path, bytes, { mode: 384, flag: "wx" });
17099
17219
  return path;
17100
17220
  }
17101
17221
  async function installPackagedAgent(request, knownHosts, exec, directory, remoteTemp, options) {
17102
17222
  const artifacts = [
17103
- [options.fzCliPath ?? fileURLToPath2(new URL("./fz.js", import.meta.url)), "fz.js"],
17104
- [options.fzAgentPath ?? fileURLToPath2(new URL("./fz-agent.js", import.meta.url)), "fz-agent.js"],
17105
- [options.fzGitSshPath ?? fileURLToPath2(new URL("./fz-git-ssh.js", import.meta.url)), "fz-git-ssh.js"]
17223
+ [options.fzCliPath ?? fileURLToPath3(new URL("./fz.js", import.meta.url)), "fz.js"],
17224
+ [options.fzAgentPath ?? fileURLToPath3(new URL("./fz-agent.js", import.meta.url)), "fz-agent.js"],
17225
+ [options.fzGitSshPath ?? fileURLToPath3(new URL("./fz-git-ssh.js", import.meta.url)), "fz-git-ssh.js"]
17106
17226
  ];
17107
17227
  for (const [artifact] of artifacts) {
17108
17228
  if (!readFileSync12(artifact).length)
@@ -17145,7 +17265,7 @@ async function applyOperatorPlatformBootstrap(request, mode, options = {}) {
17145
17265
  publicIdentity(request.target.identityPublicKeyFile);
17146
17266
  socketPath(request.target.agentSocket);
17147
17267
  const exec = options.exec ?? defaultExec3;
17148
- const directory = mkdtempSync(join10(tmpdir(), "forgezero-operator-bootstrap-"));
17268
+ const directory = mkdtempSync2(join11(tmpdir2(), "forgezero-operator-bootstrap-"));
17149
17269
  const remoteTemp = `/tmp/forgezero-operator-${randomBytes9(12).toString("hex")}`;
17150
17270
  let knownHosts = "";
17151
17271
  try {
@@ -17162,7 +17282,7 @@ async function applyOperatorPlatformBootstrap(request, mode, options = {}) {
17162
17282
  await installPackagedAgent(request, knownHosts, exec, directory, remoteTemp, options);
17163
17283
  await copy(exec, request, knownHosts, staged.path, `${remoteTemp}/platform-config.json`, true);
17164
17284
  for (const name of staged.files)
17165
- await copy(exec, request, knownHosts, join10(directory, name), `${remoteTemp}/${name}`, true);
17285
+ await copy(exec, request, knownHosts, join11(directory, name), `${remoteTemp}/${name}`, true);
17166
17286
  for (const bundle of staged.bundleFiles)
17167
17287
  await copy(exec, request, knownHosts, bundle.source, `${remoteTemp}/${bundle.name}`, true);
17168
17288
  for (const name of ["platform-config.json", ...staged.files, ...staged.bundleFiles.map(({ name: name2 }) => name2)])
@@ -17210,7 +17330,7 @@ async function applyOperatorMetalBootstrap(request, mode, options = {}) {
17210
17330
  publicIdentity(request.target.identityPublicKeyFile);
17211
17331
  socketPath(request.target.agentSocket);
17212
17332
  const exec = options.exec ?? defaultExec3;
17213
- const directory = mkdtempSync(join10(tmpdir(), "forgezero-operator-metal-"));
17333
+ const directory = mkdtempSync2(join11(tmpdir2(), "forgezero-operator-metal-"));
17214
17334
  const remoteTemp = `/tmp/forgezero-operator-${randomBytes9(12).toString("hex")}`;
17215
17335
  let knownHosts = "";
17216
17336
  try {
@@ -17243,7 +17363,7 @@ async function applyOperatorMetalBootstrap(request, mode, options = {}) {
17243
17363
  await installPackagedAgent(request, knownHosts, exec, directory, remoteTemp, options);
17244
17364
  await copy(exec, request, knownHosts, staged.path, `${remoteTemp}/metal-config.json`, true);
17245
17365
  for (const name of staged.files)
17246
- await copy(exec, request, knownHosts, join10(directory, name), `${remoteTemp}/${name}`, true);
17366
+ await copy(exec, request, knownHosts, join11(directory, name), `${remoteTemp}/${name}`, true);
17247
17367
  for (const name of ["metal-config.json", ...staged.files])
17248
17368
  await remote(exec, request, knownHosts, ["/usr/bin/sudo", "-n", "/usr/bin/install", "-m", "0600", `${remoteTemp}/${name}`, `${REMOTE_STAGE}/${name}`], "remote metal handoff", true);
17249
17369
  const initialized = await remoteRegularFileExists(exec, request, knownHosts, "/etc/forgezero/metal.initialized.json");
@@ -17512,7 +17632,7 @@ async function applyHostMaintenance(request, runtime = localRuntime()) {
17512
17632
 
17513
17633
  // src/cli/maintenance.ts
17514
17634
  import { existsSync as existsSync11, lstatSync as lstatSync9, readFileSync as readFileSync14, realpathSync as realpathSync5 } from "fs";
17515
- import { isAbsolute as isAbsolute6, join as join11, relative as relative2, resolve as resolve10 } from "path";
17635
+ import { isAbsolute as isAbsolute6, join as join12, relative as relative2, resolve as resolve10 } from "path";
17516
17636
  var API_OPERATION_ENTRYPOINTS = {
17517
17637
  "dev-reset": ["src", "server", "maintenance", "dev-reset.ts"],
17518
17638
  "db-backup": ["src", "server", "maintenance", "snapshot-backup.ts"],
@@ -17581,7 +17701,7 @@ function unsupportedRepositoryCliOption(argv2) {
17581
17701
  return;
17582
17702
  }
17583
17703
  function manifestName(root) {
17584
- const manifestPath = join11(root, "package.json");
17704
+ const manifestPath = join12(root, "package.json");
17585
17705
  if (!existsSync11(manifestPath)) {
17586
17706
  throw new Error(`No package.json exists at repository root ${root}.`);
17587
17707
  }
@@ -17597,7 +17717,7 @@ function manifestName(root) {
17597
17717
  return name;
17598
17718
  }
17599
17719
  function checkedEntrypoint(root, parts) {
17600
- const candidate = join11(root, ...parts);
17720
+ const candidate = join12(root, ...parts);
17601
17721
  if (!existsSync11(candidate) || !lstatSync9(candidate).isFile()) {
17602
17722
  throw new Error(`The reviewed operation entrypoint is missing: ${candidate}`);
17603
17723
  }
@@ -17625,7 +17745,7 @@ function resolveRepositoryOperation(operation, requestedRoot) {
17625
17745
  };
17626
17746
  }
17627
17747
  if (name === "forgezero") {
17628
- const apiRoot = realpathSync5(join11(root, "api"));
17748
+ const apiRoot = realpathSync5(join12(root, "api"));
17629
17749
  if (manifestName(apiRoot) !== "@forgezero/api") {
17630
17750
  throw new Error(`${apiRoot} is not the ForgeZero API package.`);
17631
17751
  }
@@ -17667,7 +17787,7 @@ function resolveRepositoryOperation(operation, requestedRoot) {
17667
17787
  ])
17668
17788
  };
17669
17789
  }
17670
- const rehearsalRoot = join11(root, "tests", "rehearsals", "community-cluster-api");
17790
+ const rehearsalRoot = join12(root, "tests", "rehearsals", "community-cluster-api");
17671
17791
  return {
17672
17792
  operation,
17673
17793
  root,
@@ -17721,7 +17841,7 @@ function planAppBuild(input) {
17721
17841
 
17722
17842
  // src/cli/index.ts
17723
17843
  var DEFAULT_MODE = (THRESHOLD_MODES.find((mode) => mode.threshold === 1 && mode.total === 1) ?? THRESHOLD_MODES[0]).id;
17724
- var PACKAGED_AGENT_BIN2 = fileURLToPath3(new URL("./fz-agent.js", import.meta.url));
17844
+ var PACKAGED_AGENT_BIN2 = fileURLToPath4(new URL("./fz-agent.js", import.meta.url));
17725
17845
  function parseOptions(argv2) {
17726
17846
  const options = {
17727
17847
  api: process.env.FZ_API ?? "http://localhost:8787",
@@ -17744,6 +17864,14 @@ function parseOptions(argv2) {
17744
17864
  deploySoftware: [],
17745
17865
  deployChannel: "production",
17746
17866
  requireAttestation: false,
17867
+ deployInitCustomized: false,
17868
+ deployRuntime: "native",
17869
+ deployReplicas: 1,
17870
+ deployCpuCores: 1,
17871
+ deployMemoryMiB: 512,
17872
+ deployStorageGiB: 8,
17873
+ deploySharing: "exclusive",
17874
+ deployReuse: "require",
17747
17875
  force: false,
17748
17876
  noBrowser: false,
17749
17877
  provider: "github",
@@ -17795,7 +17923,80 @@ function parseOptions(argv2) {
17795
17923
  options.optionError = "--channel must be production or development.";
17796
17924
  } else if (token === "--attestation")
17797
17925
  options.requireAttestation = true;
17798
- else if (token === "--force")
17926
+ else if (token === "--runtime") {
17927
+ const value = argv2[++index];
17928
+ options.deployInitCustomized = true;
17929
+ if (value === "native" || value === "containerd" || value === "kata-snp")
17930
+ options.deployRuntime = value;
17931
+ else
17932
+ options.optionError = "--runtime must be native, containerd, or kata-snp.";
17933
+ } else if (token === "--os") {
17934
+ const value = argv2[++index];
17935
+ options.deployInitCustomized = true;
17936
+ if (value === "ubuntu-24.04" || value === "ubuntu-26.04")
17937
+ options.deployOperatingSystem = value;
17938
+ else
17939
+ options.optionError = "--os must be ubuntu-24.04 or ubuntu-26.04.";
17940
+ } else if (token === "--isolation") {
17941
+ const value = argv2[++index];
17942
+ options.deployInitCustomized = true;
17943
+ if (value === "standard" || value === "sev-snp")
17944
+ options.deployIsolation = value;
17945
+ else
17946
+ options.optionError = "--isolation must be standard or sev-snp.";
17947
+ } else if (["--replicas", "--cpu-cores", "--memory-mib", "--storage-gib"].includes(token)) {
17948
+ const value = Number(argv2[++index] ?? "");
17949
+ options.deployInitCustomized = true;
17950
+ if (!Number.isSafeInteger(value) || value < 1)
17951
+ options.optionError = `${token} must be a positive integer.`;
17952
+ else if (token === "--replicas")
17953
+ options.deployReplicas = value;
17954
+ else if (token === "--cpu-cores")
17955
+ options.deployCpuCores = value;
17956
+ else if (token === "--memory-mib")
17957
+ options.deployMemoryMiB = value;
17958
+ else
17959
+ options.deployStorageGiB = value;
17960
+ } else if (token === "--sharing") {
17961
+ const value = argv2[++index];
17962
+ options.deployInitCustomized = true;
17963
+ if (value === "exclusive" || value === "shared")
17964
+ options.deploySharing = value;
17965
+ else
17966
+ options.optionError = "--sharing must be exclusive or shared.";
17967
+ } else if (token === "--reuse") {
17968
+ const value = argv2[++index];
17969
+ options.deployInitCustomized = true;
17970
+ if (value === "require" || value === "prefer")
17971
+ options.deployReuse = value;
17972
+ else
17973
+ options.optionError = "--reuse must be require or prefer.";
17974
+ } else if (token === "--provision-plan") {
17975
+ options.deployProvisionPlan = argv2[++index];
17976
+ options.deployInitCustomized = true;
17977
+ } else if (token === "--provision-region") {
17978
+ options.deployProvisionRegion = argv2[++index];
17979
+ options.deployInitCustomized = true;
17980
+ } else if (token === "--provision-image") {
17981
+ options.deployProvisionImage = argv2[++index];
17982
+ options.deployInitCustomized = true;
17983
+ } else if (token === "--provision-environment") {
17984
+ options.deployProvisionEnvironment = argv2[++index];
17985
+ options.deployInitCustomized = true;
17986
+ } else if (token === "--provision-ownership") {
17987
+ const value = argv2[++index];
17988
+ options.deployInitCustomized = true;
17989
+ if (value === "platform" || value === "tenant-metal")
17990
+ options.deployProvisionOwnership = value;
17991
+ else
17992
+ options.optionError = "--provision-ownership must be platform or tenant-metal.";
17993
+ } else if (token === "--provision-metal") {
17994
+ options.deployProvisionMetal = argv2[++index];
17995
+ options.deployInitCustomized = true;
17996
+ } else if (token === "--max-monthly-spend-minor") {
17997
+ options.deployMaxMonthlySpendMinor = argv2[++index];
17998
+ options.deployInitCustomized = true;
17999
+ } else if (token === "--force")
17799
18000
  options.force = true;
17800
18001
  else if (token === "--browser")
17801
18002
  options.noBrowser = false;
@@ -18631,10 +18832,10 @@ function interactiveGenesisGuests() {
18631
18832
  function writeMetalOperatorFiles(directory) {
18632
18833
  const output = genesisOutputDirectory(directory);
18633
18834
  const config = interactiveMetalBootstrap();
18634
- const metalConfig = writeBootstrapConfig(join12(output, "metal.json"), config);
18835
+ const metalConfig = writeBootstrapConfig(join13(output, "metal.json"), config);
18635
18836
  const identity = operatorIdentityCoordinates();
18636
18837
  const target = interactiveOperatorHop("Metal", { user: "root" });
18637
- const remoteRequest = writeOperatorMetalBootstrapRequest(join12(output, "metal-remote.json"), {
18838
+ const remoteRequest = writeOperatorMetalBootstrapRequest(join13(output, "metal-remote.json"), {
18638
18839
  kind: "metal-remote",
18639
18840
  metalConfigFile: metalConfig,
18640
18841
  target: { ...target, ...identity },
@@ -18652,7 +18853,7 @@ function writePlatformGenesisFleet(directory) {
18652
18853
  const metalHostname = readMetalBootstrapConfig(metalRequest.metalConfigFile, {
18653
18854
  allowHistoricalRelease: true
18654
18855
  }).metalHostname;
18655
- const checkpointPath = join12(output, "cloudflare-handoff.json");
18856
+ const checkpointPath = join13(output, "cloudflare-handoff.json");
18656
18857
  const template = interactiveBootstrap("platform", fleet, checkpointPath);
18657
18858
  const region = {
18658
18859
  label: bootstrapAnswer(`Region ${template.runtime.environment.nodeRegion} display label`),
@@ -18667,7 +18868,7 @@ function writePlatformGenesisFleet(directory) {
18667
18868
  cloudflareHandoffFile: cloudflareHostHandoffPath(checkpointPath, guest.name)
18668
18869
  });
18669
18870
  const realtimeEnabled = bootstrapBoolean("Configure existing Worker realtime fan-out?", "yes");
18670
- const cloudflareConfig = writeBootstrapConfig(join12(output, "cloudflare.json"), createCloudflareBootstrapDiscoveryCommandConfig({
18871
+ const cloudflareConfig = writeBootstrapConfig(join13(output, "cloudflare.json"), createCloudflareBootstrapDiscoveryCommandConfig({
18671
18872
  checkpointPath,
18672
18873
  discovery: {
18673
18874
  zoneName: bootstrapAnswer("Cloudflare DNS zone name", "forgezero.net"),
@@ -18685,7 +18886,7 @@ function writePlatformGenesisFleet(directory) {
18685
18886
  tunnelName: bootstrapAnswer(`${guest.name} Tunnel name`, guest.name)
18686
18887
  }))
18687
18888
  }));
18688
- const cloudflareAcceptance = join12(output, "cloudflare-acceptance.json");
18889
+ const cloudflareAcceptance = join13(output, "cloudflare-acceptance.json");
18689
18890
  const configs = platformGenesisBootstrapConfigs(template, fleet, nodes, metalHostname, region).map((config) => {
18690
18891
  const path = `${output}/${config.computeReference}-platform.json`;
18691
18892
  writeBootstrapConfig(path, config);
@@ -18694,7 +18895,7 @@ function writePlatformGenesisFleet(directory) {
18694
18895
  const requests = configs.map((platformConfigFile, index) => {
18695
18896
  const guest = fleet[index];
18696
18897
  const host = guestHostKeys.nodes.find(({ name }) => name === guest.name);
18697
- return writeOperatorPlatformBootstrapRequest(join12(output, `${guest.name}-remote.json`), {
18898
+ return writeOperatorPlatformBootstrapRequest(join13(output, `${guest.name}-remote.json`), {
18698
18899
  kind: "platform-remote",
18699
18900
  platformConfigFile,
18700
18901
  target: {
@@ -18715,7 +18916,7 @@ function writePlatformGenesisFleet(directory) {
18715
18916
  }
18716
18917
  });
18717
18918
  });
18718
- const fleetRequest = writeOperatorPlatformBootstrapFleetRequest(join12(output, "platform-fleet-remote.json"), requests, { configFile: cloudflareConfig, acceptanceFile: cloudflareAcceptance });
18919
+ const fleetRequest = writeOperatorPlatformBootstrapFleetRequest(join13(output, "platform-fleet-remote.json"), requests, { configFile: cloudflareConfig, acceptanceFile: cloudflareAcceptance });
18719
18920
  return { configs, requests, fleetRequest, cloudflareConfig, cloudflareAcceptance };
18720
18921
  }
18721
18922
  function interactiveBootstrap(kind, genesisGuests, genesisCloudflareCheckpoint) {
@@ -19454,7 +19655,7 @@ async function cmdDeploy(options, args) {
19454
19655
  if (options.optionError)
19455
19656
  throw new Error(options.optionError);
19456
19657
  if (operation === "init") {
19457
- if (options.deploySoftware.length > 0 || options.deployProfile !== "app" || options.requireAttestation) {
19658
+ if (!options.deployInitCustomized && (options.deploySoftware.length > 0 || options.deployProfile !== "app" || options.requireAttestation)) {
19458
19659
  const created = initializeDeployFile(options.projectRoot, {
19459
19660
  name: options.projectName,
19460
19661
  profile: options.deployProfile,
@@ -19469,7 +19670,41 @@ async function cmdDeploy(options, args) {
19469
19670
  out.ok(`Initialized legacy .fz/deploy.json (${created.summary.digest}).`);
19470
19671
  return 0;
19471
19672
  }
19472
- initializeTypeScriptDeployment(options.projectRoot, { name: options.projectName ?? basename3(resolve12(options.projectRoot)), force: options.force });
19673
+ if (options.deployInitCustomized && (options.deploySoftware.length > 0 || options.requireAttestation)) {
19674
+ throw new Error("typed runtime initialization selects its tested software and attestation from --runtime/--isolation; do not combine --software or --attestation");
19675
+ }
19676
+ initializeTypeScriptDeployment(options.projectRoot, {
19677
+ name: options.projectName ?? basename3(resolve12(options.projectRoot)),
19678
+ force: options.force,
19679
+ runtime: options.deployRuntime,
19680
+ operatingSystem: options.deployOperatingSystem,
19681
+ isolation: options.deployIsolation,
19682
+ replicas: options.deployReplicas,
19683
+ cpuCores: options.deployCpuCores,
19684
+ memoryMiB: options.deployMemoryMiB,
19685
+ storageGiB: options.deployStorageGiB,
19686
+ sharing: options.deploySharing,
19687
+ reuse: options.deployReuse,
19688
+ profile: options.deployProfile,
19689
+ ...[
19690
+ options.deployProvisionPlan,
19691
+ options.deployProvisionRegion,
19692
+ options.deployProvisionImage,
19693
+ options.deployProvisionEnvironment,
19694
+ options.deployProvisionOwnership,
19695
+ options.deployMaxMonthlySpendMinor
19696
+ ].some((value) => value !== undefined) ? {
19697
+ provisioning: {
19698
+ planKey: options.deployProvisionPlan ?? "",
19699
+ regionKey: options.deployProvisionRegion ?? "",
19700
+ imageKey: options.deployProvisionImage ?? "",
19701
+ environmentKey: options.deployProvisionEnvironment ?? "",
19702
+ ownership: options.deployProvisionOwnership ?? "platform",
19703
+ ...options.deployProvisionMetal ? { metalHostname: options.deployProvisionMetal } : {},
19704
+ maxMonthlySpendMinor: options.deployMaxMonthlySpendMinor ?? ""
19705
+ }
19706
+ } : {}
19707
+ });
19473
19708
  const compiled = await compileDeploymentProject(options.projectRoot);
19474
19709
  if (options.json)
19475
19710
  out.line(JSON.stringify({ source: compiled.source, output: compiled.output, digest: compiled.digest }, null, 2));
@@ -19721,7 +19956,9 @@ function usage() {
19721
19956
  fz project check Fail when truth sources or generated adapters drift
19722
19957
  fz app build Build the static App for a numbered local/development/
19723
19958
  production choice, --profile, or an explicit --api origin
19724
- fz deploy init Create forgezero.deploy.ts and its fail-safe canonical plan
19959
+ fz deploy init Create forgezero.deploy.ts and its fail-safe canonical plan;
19960
+ select native, containerd runc, or Kata QEMU SNP and
19961
+ explicit per-compute resources with the options below
19725
19962
  fz deploy compile Compile TypeScript into .fz/deploy.plan.json
19726
19963
  fz deploy check Validate source, plan, actions, providers and topology
19727
19964
  fz deploy sync Prove readiness and print the Git synchronization rule
@@ -19792,6 +20029,19 @@ function usage() {
19792
20029
  --software <key@ver> Initial tested software coordinate; repeatable
19793
20030
  --channel <name> Catalog view: production or development (shows testing)
19794
20031
  --attestation Require hardware attestation for every deploy step
20032
+ --runtime <mode> native, containerd, or kata-snp
20033
+ --os <image> ubuntu-24.04 or ubuntu-26.04 (SNP requires 26.04)
20034
+ --isolation <mode> standard or sev-snp
20035
+ --replicas <n> Exact initial compute cardinality (default 1)
20036
+ --cpu-cores <n> Reserved CPU cores per compute
20037
+ --memory-mib <n> Reserved memory MiB per compute
20038
+ --storage-gib <n> Reserved storage GiB per compute
20039
+ --sharing <mode> exclusive or shared (native requires exclusive)
20040
+ --reuse <mode> require existing capacity or prefer reuse then approved provisioning
20041
+ --provision-plan <key> --provision-region <key> --provision-image <key>
20042
+ --provision-environment <key> --provision-ownership <platform|tenant-metal>
20043
+ --provision-metal <host> Required only for tenant-metal ownership
20044
+ --max-monthly-spend-minor <n> Hard approved scale-out spend ceiling
19795
20045
  --force Init may replace an existing generated target
19796
20046
 
19797
20047
  REMOTE DEPLOY OPTIONS
@@ -366,7 +366,7 @@ function systemdAgentEgressDirectives(loopbackTcpPorts = []) {
366
366
  }
367
367
 
368
368
  // src/version.ts
369
- var VERSION = "0.1.82";
369
+ var VERSION = "0.1.84";
370
370
 
371
371
  // src/otel-collector.ts
372
372
  var FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
@@ -1416,7 +1416,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
1416
1416
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
1417
1417
 
1418
1418
  // src/version.ts
1419
- var VERSION = "0.1.82";
1419
+ var VERSION = "0.1.84";
1420
1420
 
1421
1421
  // src/software.ts
1422
1422
  var PINNED_BUN_VERSION = "1.3.14";
@@ -2647,7 +2647,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
2647
2647
  }
2648
2648
 
2649
2649
  // src/version.ts
2650
- var VERSION3 = "0.1.82";
2650
+ var VERSION3 = "0.1.84";
2651
2651
 
2652
2652
  // src/egress-policy.ts
2653
2653
  import { realpathSync as realpathSync3 } from "node:fs";
package/dist/provision.js CHANGED
@@ -2647,7 +2647,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
2647
2647
  }
2648
2648
 
2649
2649
  // src/version.ts
2650
- var VERSION3 = "0.1.82";
2650
+ var VERSION3 = "0.1.84";
2651
2651
 
2652
2652
  // src/egress-policy.ts
2653
2653
  import { realpathSync as realpathSync3 } from "node:fs";
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** One package version shared by both public binaries. Pinned to package.json by tests. */
2
- export declare const VERSION = "0.1.82";
2
+ export declare const VERSION = "0.1.84";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgezero/agent",
3
- "version": "0.1.82",
3
+ "version": "0.1.84",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "check": "tsc --noEmit",
@@ -16,8 +16,8 @@
16
16
  "@noble/post-quantum": "^0.6.1"
17
17
  },
18
18
  "dependencies": {
19
- "@forgezero/runtime": "0.1.11",
20
- "@forgezero/vault": "0.1.15",
19
+ "@forgezero/runtime": "0.1.12",
20
+ "@forgezero/vault": "0.1.17",
21
21
  "@noble/curves": "2.2.0",
22
22
  "@noble/hashes": "2.2.0",
23
23
  "@noble/post-quantum": "0.6.1",