@forgezero/agent 0.1.81 → 0.1.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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 3 named type exports. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
1406
1410
 
1407
1411
  ```text
1408
1412
  import {
@@ -1417,6 +1421,8 @@ import {
1417
1421
 
1418
1422
  import type {
1419
1423
  DeploymentCompilation,
1424
+ DeploymentInitOptions,
1425
+ DeploymentInitRuntime,
1420
1426
  } from '@forgezero/agent/deploy-compiler';
1421
1427
  ```
1422
1428
 
@@ -750,7 +750,7 @@ async function postSignedNode(options, path, body) {
750
750
  }
751
751
 
752
752
  // src/version.ts
753
- var VERSION3 = "0.1.81";
753
+ var VERSION3 = "0.1.83";
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.81";
1419
+ var VERSION = "0.1.83";
1420
1420
 
1421
1421
  // src/software.ts
1422
1422
  var PINNED_BUN_VERSION = "1.3.14";
@@ -2879,7 +2879,6 @@ function validatePlatformGenesisGuests(guests, expectedCount = 3) {
2879
2879
  }
2880
2880
  const names = new Set;
2881
2881
  const addresses = new Set;
2882
- const pools = new Set;
2883
2882
  return guests.map((input) => {
2884
2883
  if (!input || typeof input !== "object" || !SAFE_NAME.test(input.name)) {
2885
2884
  throw new Error("platform genesis guest name is malformed");
@@ -2892,13 +2891,11 @@ function validatePlatformGenesisGuests(guests, expectedCount = 3) {
2892
2891
  if (input.cpuPoolKey !== undefined && !SAFE_POOL.test(input.cpuPoolKey)) {
2893
2892
  throw new Error("platform genesis CPU pool key is malformed");
2894
2893
  }
2895
- if (names.has(input.name) || addresses.has(input.address) || input.cpuPoolKey !== undefined && pools.has(input.cpuPoolKey)) {
2896
- throw new Error("platform genesis guest names, addresses and explicit CPU pools must be unique");
2894
+ if (names.has(input.name) || addresses.has(input.address)) {
2895
+ throw new Error("platform genesis guest names and addresses must be unique");
2897
2896
  }
2898
2897
  names.add(input.name);
2899
2898
  addresses.add(input.address);
2900
- if (input.cpuPoolKey !== undefined)
2901
- pools.add(input.cpuPoolKey);
2902
2899
  const physicalCores = integer(input.physicalCores, 1, 256, "physical core count");
2903
2900
  const vcpu = integer(input.vcpu, 1, 512, "vCPU count");
2904
2901
  if (vcpu < physicalCores)
@@ -8,11 +8,24 @@ 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 DeploymentInitOptions {
13
+ runtime?: DeploymentInitRuntime;
14
+ operatingSystem?: 'ubuntu-24.04' | 'ubuntu-26.04';
15
+ isolation?: 'standard' | 'sev-snp';
16
+ replicas?: number;
17
+ cpuCores?: number;
18
+ memoryMiB?: number;
19
+ storageGiB?: number;
20
+ sharing?: 'exclusive' | 'shared';
21
+ reuse?: 'require' | 'prefer';
22
+ profile?: string;
23
+ }
24
+ export declare function defaultTypeScriptDeployment(name: string, options?: DeploymentInitOptions): string;
12
25
  export declare function initializeTypeScriptDeployment(rootValue: string, options: {
13
26
  name: string;
14
27
  force?: boolean;
15
- }): string;
28
+ } & DeploymentInitOptions): string;
16
29
  export declare function loadDeploymentSource(root: string, sourceFile?: string): Promise<unknown>;
17
30
  export declare function compileDeploymentProject(rootValue: string, options?: {
18
31
  sourceFile?: string;
@@ -1142,46 +1142,82 @@ function parseDeploymentPlan(value) {
1142
1142
  }
1143
1143
 
1144
1144
  // src/deploy-compiler.ts
1145
- import { existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
1146
- import { 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
+ return result;
1190
+ }
1191
+ function defaultTypeScriptDeployment(name, options = {}) {
1192
+ const selected = initCoordinates(options);
1193
+ const execution = selected.runtime === "native" ? "native" : selected.runtime === "containerd" ? "oci-runc" : "oci-kata-qemu-snp";
1194
+ const hostPackageVersion = selected.operatingSystem === "ubuntu-24.04" ? "ubuntu-24.04" : "ubuntu-26.04";
1195
+ const requirementBlock = selected.runtime === "native" ? `bun: providers.bun.require()` : selected.runtime === "containerd" ? `containerd: providers.containerd.require({ version: '${hostPackageVersion}' }),
1196
+ nginx: providers.nginx.require({ version: '${hostPackageVersion}' })` : `containerd: providers.containerd.require(),
1197
+ kata: providers.kata.require(),
1198
+ nginx: providers.nginx.require()`;
1199
+ const componentBlock = selected.runtime === "native" ? `
1174
1200
  app: application({
1175
1201
  target: 'app',
1176
1202
  runtime: { kind: 'native', provider: 'forgezero.bun', requirement: 'bun', argv: ['/usr/local/bin/bun', 'run', 'start'] },
1177
1203
  service: { protocol: 'http', port: 3000, health: { protocol: 'http', method: 'GET', path: '/health', expectedStatus: [200], timeoutMs: 5_000 } },
1178
1204
  resources: {},
1179
1205
  rollout: { strategy: 'direct' }
1180
- })
1181
- },
1182
- workflows: {
1183
- deploy: workflow({
1184
- stages: {
1206
+ })` : `
1207
+ app: application({
1208
+ target: 'app',
1209
+ runtime: {
1210
+ kind: 'container', provider: '${selected.runtime === "containerd" ? "forgezero.containerd" : "forgezero.kata"}', requirement: '${selected.runtime === "containerd" ? "containerd" : "kata"}', runtimeClass: '${selected.runtime === "containerd" ? "runc" : "kata-qemu-snp"}',
1211
+ image: { source: { kind: 'build', context: '.', dockerfile: 'Dockerfile' } },
1212
+ security: { privileged: false, noNewPrivileges: true, root: 'read-only', dropCapabilities: ['ALL'] }
1213
+ },
1214
+ service: { protocol: 'http', port: 3000, health: { protocol: 'http', method: 'GET', path: '/health', expectedStatus: [200], timeoutMs: 5_000 } },
1215
+ resources: { cpu: { limit: ${selected.cpuCores} }, memory: { limitMiB: ${selected.memoryMiB}, swap: 'disabled' }, pids: { limit: 256 } },
1216
+ storage: [{ class: 'ephemeral', path: '/tmp', type: 'tmpfs', sizeMiB: ${Math.min(128, Math.max(16, Math.floor(selected.memoryMiB / 4)))} }],
1217
+ network: { ingress: { exposure: 'loopback', stablePort: 3000 }, container: { mode: 'bridge', network: 'app' } },
1218
+ rollout: { strategy: 'blue-green', proxy: 'nginx', drainMs: 30_000, automaticRollback: true }
1219
+ })`;
1220
+ const workflowBlock = selected.runtime === "native" ? `
1185
1221
  build: stage({ strategy: { mode: 'sequential' }, steps: {
1186
1222
  build: actions.exec.argv(
1187
1223
  { component: 'app', argv: ['/usr/local/bin/fz-agent', 'pipeline-todo', 'replace with the project build argv'] },
@@ -1196,7 +1232,42 @@ export default defineDeployment({
1196
1232
  } }),
1197
1233
  verify: stage({ dependsOn: ['release'], strategy: { mode: 'parallel' }, steps: {
1198
1234
  health: actions.service.health({ component: 'app' }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 30_000 })
1199
- } })
1235
+ } })` : `
1236
+ prepare: stage({ strategy: { mode: 'sequential' }, steps: {
1237
+ software: actions.software.ensure({ requirements: [${selected.runtime === "containerd" ? "'containerd', 'nginx'" : "'containerd', 'kata', 'nginx'"}] }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 900_000 }),
1238
+ review: actions.exec.argv({ component: 'app', argv: ['/usr/local/bin/fz-agent', 'pipeline-todo', 'review the Dockerfile and container entrypoint'] }, { scope: { kind: 'release-executor' } })
1239
+ } }),
1240
+ build: stage({ dependsOn: ['prepare'], strategy: { mode: 'sequential' }, steps: {
1241
+ image: actions.container.build({ component: 'app' }, { scope: { kind: 'release-executor' }, timeoutMs: 900_000 })
1242
+ } }),
1243
+ release: stage({ dependsOn: ['build'], strategy: { mode: 'blue-green', maximumConcurrency: 1, minimumHealthy: 1 }, steps: {
1244
+ promote: actions.service.promote({ component: 'app', imageDigest: { $ref: 'steps.image.outputs.digest' } }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 180_000 })
1245
+ } }),
1246
+ verify: stage({ dependsOn: ['release'], strategy: { mode: 'parallel' }, steps: {
1247
+ health: actions.service.health({ component: 'app' }, { scope: { kind: 'each-target', target: 'app' }, timeoutMs: 30_000 })
1248
+ } })`;
1249
+ return `import { actions, application, defineDeployment, providers, stage, target, workflow } from '@forgezero/agent/deploy';
1250
+
1251
+ export default defineDeployment({
1252
+ apiVersion: 'deploy.forgezero.net/v1',
1253
+ kind: 'Deployment',
1254
+ metadata: { name: '${safeName(name)}' },
1255
+ spec: {
1256
+ security: { attestation: '${selected.isolation === "sev-snp" ? "required" : "preferred"}' },
1257
+ targets: {
1258
+ app: target.compute({
1259
+ profiles: ['${selected.profile}'], replicas: ${selected.replicas},
1260
+ resources: { cpuCores: ${selected.cpuCores}, memoryMiB: ${selected.memoryMiB}, storageGiB: ${selected.storageGiB} },
1261
+ os: '${selected.operatingSystem}', runtime: '${execution}', isolation: '${selected.isolation}',
1262
+ sharing: '${selected.sharing}', reuse: '${selected.reuse}'
1263
+ })
1264
+ },
1265
+ requirements: { ${requirementBlock} },
1266
+ components: {${componentBlock}
1267
+ },
1268
+ workflows: {
1269
+ deploy: workflow({
1270
+ stages: {${workflowBlock}
1200
1271
  }
1201
1272
  })
1202
1273
  }
@@ -1208,7 +1279,7 @@ function initializeTypeScriptDeployment(rootValue, options) {
1208
1279
  const path = localPath(rootValue, DEPLOY_SOURCE_FILE, "deployment source");
1209
1280
  if (existsSync(path) && !options.force)
1210
1281
  throw new Error(`${DEPLOY_SOURCE_FILE} already exists; use --force only when replacing it deliberately`);
1211
- writeFileSync(path, defaultTypeScriptDeployment(options.name), { mode: 420, flag: options.force ? "w" : "wx" });
1282
+ writeFileSync(path, defaultTypeScriptDeployment(options.name, options), { mode: 420, flag: options.force ? "w" : "wx" });
1212
1283
  return path;
1213
1284
  }
1214
1285
  function inside(root, path) {
@@ -1229,10 +1300,35 @@ async function loadDeploymentSource(root, sourceFile = DEPLOY_SOURCE_FILE) {
1229
1300
  const status = lstatSync(source);
1230
1301
  if (!status.isFile() || status.isSymbolicLink() || status.size > 2 * 1024 * 1024)
1231
1302
  throw new Error("deployment source must be one bounded regular file");
1232
- const 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;
1303
+ const builtDeploy = new URL("./deploy.js", import.meta.url);
1304
+ const sourceDeploy = new URL("./deploy.ts", import.meta.url);
1305
+ const deployModule = existsSync(fileURLToPath(builtDeploy)) ? builtDeploy.href : sourceDeploy.href;
1306
+ const result = await Bun.build({
1307
+ entrypoints: [source],
1308
+ target: "bun",
1309
+ format: "esm",
1310
+ minify: false,
1311
+ plugins: [{
1312
+ name: "forgezero-deploy-authoring",
1313
+ setup(builder) {
1314
+ builder.onResolve({ filter: /^@forgezero\/agent\/deploy$/ }, () => ({ path: fileURLToPath(deployModule) }));
1315
+ }
1316
+ }]
1317
+ });
1318
+ if (!result.success || result.outputs.length !== 1) {
1319
+ throw new Error(`deployment source compilation failed: ${result.logs.map((entry) => entry.message).join("; ")}`);
1320
+ }
1321
+ const directory = mkdtempSync(join(tmpdir(), "forgezero-deploy-compile-"));
1322
+ const compiled = join(directory, "deployment.mjs");
1323
+ try {
1324
+ writeFileSync(compiled, Buffer.from(await result.outputs[0].arrayBuffer()), { mode: 384, flag: "wx" });
1325
+ const module = await import(`${pathToFileURL(compiled).href}?forgezero=${status.mtimeMs}`);
1326
+ if (module.default === undefined)
1327
+ throw new Error(`${sourceFile} must export one default deployment definition`);
1328
+ return module.default;
1329
+ } finally {
1330
+ rmSync(directory, { recursive: true, force: true });
1331
+ }
1236
1332
  }
1237
1333
  async function compileDeploymentProject(rootValue, options = {}) {
1238
1334
  const root = resolve(rootValue);
package/dist/fz-agent.js CHANGED
@@ -9331,7 +9331,7 @@ async function writeAndCloseProcessInput(input, value) {
9331
9331
  }
9332
9332
 
9333
9333
  // src/version.ts
9334
- var VERSION2 = "0.1.81";
9334
+ var VERSION2 = "0.1.83";
9335
9335
 
9336
9336
  // src/ssh-bootstrap.ts
9337
9337
  class SshBootstrapError extends Error {