@forgezero/agent 0.1.53 → 0.1.55

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
@@ -59,7 +59,11 @@ Every row links to the detailed explanation and named-import/example area below.
59
59
  | @forgezero/agent/mesh-connector | Fixed Cloudflare Mesh/WARP connector enrollment using only the unit-loaded connector credential. | Bun/Node host | [Details + example](#forgezero-agent-mesh-connector) |
60
60
  | @forgezero/agent/compute | Compute claim, renewal, execution and completion client contracts. | Bun/Node host | [Details + example](#forgezero-agent-compute) |
61
61
  | @forgezero/agent/credential-schema | Canonical systemd credential names and validation shared by bootstrap and services. | Bun/Node host | [Details + example](#forgezero-agent-credential-schema) |
62
- | @forgezero/agent/definition | Validated project deployment definition and workload profiles. | Bun/Node host | [Details + example](#forgezero-agent-definition) |
62
+ | @forgezero/agent/definition | Legacy version 3 JSON deployment compatibility contract. | Bun/Node host | [Details + example](#forgezero-agent-definition) |
63
+ | @forgezero/agent/deploy | Typed deployment authoring builders, built-in provider requirements and versioned action constructors. | Bun/Node host | [Details + example](#forgezero-agent-deploy) |
64
+ | @forgezero/agent/deploy-plan | Runtime validation, canonicalization and semantic digest for inert deployment plans. | Bun/Node host | [Details + example](#forgezero-agent-deploy-plan) |
65
+ | @forgezero/agent/deploy-plan-runner | Conditional DAG execution with bounded retry, compensation and failure policy over injected action handlers. | Bun/Node host | [Details + example](#forgezero-agent-deploy-plan-runner) |
66
+ | @forgezero/agent/deploy-compiler | Explicit developer/CI compiler from forgezero.deploy.ts to canonical .fz/deploy.plan.json; never used to execute project TypeScript in production. | Bun/Node host | [Details + example](#forgezero-agent-deploy-compiler) |
63
67
  | @forgezero/agent/deploy-file | Read and validate a checked-out .fz deployment file without executing project input. | Bun/Node host | [Details + example](#forgezero-agent-deploy-file) |
64
68
  | @forgezero/agent/metal-bootstrap | Physical-metal bootstrap planning, application and status evidence. | Bun/Node host | [Details + example](#forgezero-agent-metal-bootstrap) |
65
69
  | @forgezero/agent/metal-provision | Validated confidential guest provisioning profiles and cloud-init rendering. | Bun/Node host | [Details + example](#forgezero-agent-metal-provision) |
@@ -75,6 +79,9 @@ Every row links to the detailed explanation and named-import/example area below.
75
79
  ## Commands
76
80
 
77
81
  bun add -g @forgezero/agent — Install the version-matched fz operator CLI and fz-agent daemon.
82
+ fz deploy init — Create a typed forgezero.deploy.ts and its canonical inert execution plan.
83
+ fz deploy compile — Compile and validate TypeScript deployment intent into .fz/deploy.plan.json.
84
+ fz deploy check — Refuse stale plans, unsafe provider/action coordinates and unresolved deployment blockers.
78
85
  fz bootstrap platform remote prepare --bootstrap-config /secure/forgezero/development/dev-fz-n1-remote.json — Dry-plan one pinned operator-to-guest bootstrap; add --apply only after review.
79
86
  fz bootstrap platform --help — Inspect attended platform bootstrap inputs before applying them.
80
87
  fz bootstrap metal --help — Inspect the identity-only physical provisioner bootstrap.
@@ -83,6 +90,9 @@ fz status — Read installed service and bootstrap evidence.
83
90
 
84
91
  ```text
85
92
  bun add -g @forgezero/agent
93
+ fz deploy init
94
+ fz deploy compile
95
+ fz deploy check
86
96
  fz bootstrap platform remote prepare --bootstrap-config /secure/forgezero/development/dev-fz-n1-remote.json
87
97
  fz bootstrap platform --help
88
98
  fz bootstrap metal --help
@@ -393,13 +403,111 @@ import type { AgentCredentialLocation, DeploymentCredentialBinding, DeploymentCr
393
403
  <a id="forgezero-agent-definition"></a>
394
404
  ## @forgezero/agent/definition
395
405
 
396
- Validated project deployment definition and workload profiles. Named value imports: DEPLOY_SCHEMA_URL, DefinitionError, PIPELINE_VERSION, parseDeployDefinition, phasePipeline, validateDeploymentService. Named type imports: DeployDefinition, DeployStep, DeploymentPortAllocation, DeploymentService, PipelineProfile. Import only the names used by this file.
406
+ Legacy version 3 JSON deployment compatibility contract. Named value imports: DEPLOY_SCHEMA_URL, DefinitionError, PIPELINE_VERSION, parseDeployDefinition, phasePipeline, validateDeploymentService. Named type imports: DeployDefinition, DeployStep, DeploymentPortAllocation, DeploymentService, PipelineProfile. Import only the names used by this file.
397
407
 
398
408
  ```text
399
409
  import { DEPLOY_SCHEMA_URL, DefinitionError, PIPELINE_VERSION, parseDeployDefinition, phasePipeline, validateDeploymentService } from '@forgezero/agent/definition';
400
410
  import type { DeployDefinition, DeployStep, DeploymentPortAllocation, DeploymentService, PipelineProfile } from '@forgezero/agent/definition';
401
411
  ```
402
412
 
413
+ <a id="forgezero-agent-deploy"></a>
414
+ ## @forgezero/agent/deploy
415
+
416
+ Typed deployment authoring builders, built-in provider requirements and versioned action constructors. Named value imports: actions, application, conditions, credential, database, defineDeployment, input, providers, ref, requirement, stage, step, target, workflow. Named type imports: ApplicationComponent, ApplicationRuntime, ArangoDbProviderConfig, BunProviderConfig, CloudflaredProviderConfig, ComponentDefinition, Condition, ConditionOperand, CredentialDefinition, DatabaseComponent, DeploymentSource, DockerProviderConfig, HealthDefinition, InputDefinition, JsonObject, JsonPrimitive, JsonValue, NetworkDefinition, NginxProviderConfig, OpenSshProviderConfig, RequirementDefinition, ResolvableJsonObject, ResolvableJsonValue, ResourceDefinition, RetryDefinition, StageDefinition, StepScope, StorageMount, SystemdProviderConfig, TargetDefinition, UfwProviderConfig, ValueReference, WarpProviderConfig, WorkflowDefinition, WorkflowStepDefinition. Import only the names used by this file.
417
+
418
+ ```text
419
+ import { actions, application, conditions, credential, database, defineDeployment } from '@forgezero/agent/deploy';
420
+ import { input, providers, ref, requirement, stage, step } from '@forgezero/agent/deploy';
421
+ import { target, workflow } from '@forgezero/agent/deploy';
422
+ import type { ApplicationComponent, ApplicationRuntime, ArangoDbProviderConfig, BunProviderConfig, CloudflaredProviderConfig, ComponentDefinition } from '@forgezero/agent/deploy';
423
+ import type { Condition, ConditionOperand, CredentialDefinition, DatabaseComponent, DeploymentSource, DockerProviderConfig } from '@forgezero/agent/deploy';
424
+ import type { HealthDefinition, InputDefinition, JsonObject, JsonPrimitive, JsonValue, NetworkDefinition } from '@forgezero/agent/deploy';
425
+ import type { NginxProviderConfig, OpenSshProviderConfig, RequirementDefinition, ResolvableJsonObject, ResolvableJsonValue, ResourceDefinition } from '@forgezero/agent/deploy';
426
+ import type { RetryDefinition, StageDefinition, StepScope, StorageMount, SystemdProviderConfig, TargetDefinition } from '@forgezero/agent/deploy';
427
+ import type { UfwProviderConfig, ValueReference, WarpProviderConfig, WorkflowDefinition, WorkflowStepDefinition } from '@forgezero/agent/deploy';
428
+ ```
429
+
430
+ ## @forgezero/agent/deploy — Author a typed native or Docker deployment
431
+
432
+ TypeScript is authoring only. The checked-in canonical plan is what Agents validate and execute. Docker is additive: native Bun/ArangoDB remains available, while container applications must declare hard CPU, memory and PID limits, storage class, loopback ingress and a fixed health gate.
433
+
434
+ ```text
435
+ import { actions, application, defineDeployment, providers, stage, target, workflow } from '@forgezero/agent/deploy';
436
+
437
+ export default defineDeployment({
438
+ apiVersion: 'deploy.forgezero.net/v1', kind: 'Deployment',
439
+ metadata: { name: 'orders-api' },
440
+ spec: {
441
+ targets: { api: target.compute({ selector: { profiles: ['api'] }, cardinality: { minimum: 1, desired: 2, maximum: 16 } }) },
442
+ requirements: { docker: providers.docker.require(), nginx: providers.nginx.require() },
443
+ components: {
444
+ api: application({
445
+ target: 'api',
446
+ runtime: { kind: 'container', provider: 'forgezero.docker', requirement: 'docker', image: { source: { kind: 'build', context: '.', dockerfile: 'Dockerfile' } }, security: { privileged: false, noNewPrivileges: true, root: 'read-only', dropCapabilities: ['ALL'] } },
447
+ service: { protocol: 'http', port: 3000, health: { protocol: 'http', method: 'GET', path: '/health', expectedStatus: [200], timeoutMs: 2000 } },
448
+ resources: { cpu: { limit: 2 }, memory: { limitMiB: 2048, swap: 'disabled' }, pids: { limit: 256 } },
449
+ storage: [{ class: 'ephemeral', path: '/tmp', type: 'tmpfs', sizeMiB: 128 }],
450
+ network: { ingress: { exposure: 'loopback', stablePort: 3000 }, container: { mode: 'bridge', network: 'api' } },
451
+ rollout: { strategy: 'blue-green', proxy: 'nginx', automaticRollback: true }
452
+ })
453
+ },
454
+ workflows: { deploy: workflow({ stages: { prepare: stage({ strategy: { mode: 'sequential' }, steps: { software: actions.software.ensure({ requirements: ['docker', 'nginx'] }, { scope: { kind: 'each-target', target: 'api' } }) } }) } }) }
455
+ }
456
+ });
457
+ ```
458
+
459
+ <a id="forgezero-agent-deploy-plan"></a>
460
+ ## @forgezero/agent/deploy-plan
461
+
462
+ Runtime validation, canonicalization and semantic digest for inert deployment plans. Named value imports: DEPLOY_PLAN_FORMAT, DEPLOY_PLAN_VERSION, DeploymentPlanError, canonicalJson, compileDeployment, deploymentPlanDigest, deploymentSourceDigest, parseDeploymentPlan. Named type imports: DeploymentPlan, PlannedStage, PlannedStep, PlannedWorkflow. Import only the names used by this file.
463
+
464
+ ```text
465
+ import { DEPLOY_PLAN_FORMAT, DEPLOY_PLAN_VERSION, DeploymentPlanError, canonicalJson, compileDeployment, deploymentPlanDigest } from '@forgezero/agent/deploy-plan';
466
+ import { deploymentSourceDigest, parseDeploymentPlan } from '@forgezero/agent/deploy-plan';
467
+ import type { DeploymentPlan, PlannedStage, PlannedStep, PlannedWorkflow } from '@forgezero/agent/deploy-plan';
468
+ ```
469
+
470
+ <a id="forgezero-agent-deploy-plan-runner"></a>
471
+ ## @forgezero/agent/deploy-plan-runner
472
+
473
+ Conditional DAG execution with bounded retry, compensation and failure policy over injected action handlers. Named value imports: DeploymentActionError, evaluateCondition, runDeploymentPlan. Named type imports: ActionContext, ActionExecutionResult, ActionFailureKind, ActionHandler, PlanRunResult, PlanStepStatus, RunDeploymentPlanOptions, StepExecutionRecord. Import only the names used by this file.
474
+
475
+ ```text
476
+ import { DeploymentActionError, evaluateCondition, runDeploymentPlan } from '@forgezero/agent/deploy-plan-runner';
477
+ import type { ActionContext, ActionExecutionResult, ActionFailureKind, ActionHandler, PlanRunResult, PlanStepStatus } from '@forgezero/agent/deploy-plan-runner';
478
+ import type { RunDeploymentPlanOptions, StepExecutionRecord } from '@forgezero/agent/deploy-plan-runner';
479
+ ```
480
+
481
+ ## @forgezero/agent/deploy-plan-runner — Extend the plan with a namespaced action
482
+
483
+ Public projects can define a versioned action coordinate and install an exact handler. Built-in forgezero.* coordinates stay closed; unknown built-ins fail validation.
484
+
485
+ ```text
486
+ import { actions } from '@forgezero/agent/deploy';
487
+ import { runDeploymentPlan } from '@forgezero/agent/deploy-plan-runner';
488
+
489
+ const notify = actions.extension('example.notifications/publish@1', { channel: 'deployments' }, {
490
+ scope: { kind: 'release-executor' },
491
+ retry: { attempts: 3, backoff: { kind: 'exponential', initialMs: 250, maximumMs: 5000 }, retryOn: ['network'] }
492
+ });
493
+
494
+ await runDeploymentPlan({
495
+ plan, workflow: 'deploy', inputs: {},
496
+ actions: { 'example.notifications/publish@1': async ({ resolved }) => ({ changed: true, evidence: resolved }) }
497
+ });
498
+ ```
499
+
500
+ <a id="forgezero-agent-deploy-compiler"></a>
501
+ ## @forgezero/agent/deploy-compiler
502
+
503
+ Explicit developer/CI compiler from forgezero.deploy.ts to canonical .fz/deploy.plan.json; never used to execute project TypeScript in production. Named value imports: DEPLOY_PLAN_FILE, DEPLOY_SOURCE_FILE, compileDeploymentProject, defaultTypeScriptDeployment, initializeTypeScriptDeployment, inspectCompiledDeployment, loadDeploymentSource. Named type imports: DeploymentCompilation. Import only the names used by this file.
504
+
505
+ ```text
506
+ import { DEPLOY_PLAN_FILE, DEPLOY_SOURCE_FILE, compileDeploymentProject, defaultTypeScriptDeployment, initializeTypeScriptDeployment, inspectCompiledDeployment } from '@forgezero/agent/deploy-compiler';
507
+ import { loadDeploymentSource } from '@forgezero/agent/deploy-compiler';
508
+ import type { DeploymentCompilation } from '@forgezero/agent/deploy-compiler';
509
+ ```
510
+
403
511
  <a id="forgezero-agent-deploy-file"></a>
404
512
  ## @forgezero/agent/deploy-file
405
513
 
@@ -414,11 +522,11 @@ import type { DeployFileSummary, InitializedDeployFile } from '@forgezero/agent/
414
522
  <a id="forgezero-agent-metal-bootstrap"></a>
415
523
  ## @forgezero/agent/metal-bootstrap
416
524
 
417
- Physical-metal bootstrap planning, application and status evidence. Named value imports: METAL_BOOTSTRAP_STATE_PATH, MetalBootstrapError, applyMetalBootstrap, metalBootstrapStatus, planMetalBootstrap, readMetalBootstrapConfig, renderMetalUnits, validateMetalBootstrapConfig, validateOwnerOnlyPath. Named type imports: MetalBootstrapApplyOptions, MetalBootstrapConfig, MetalBootstrapExec, MetalBootstrapPlan, MetalBootstrapResult, MetalBootstrapStatus. Import only the names used by this file.
525
+ Physical-metal bootstrap planning, application and status evidence. Named value imports: METAL_BOOTSTRAP_STATE_PATH, MetalBootstrapError, applyMetalBootstrap, metalBootstrapStatus, normalizeMetalPublicIdentity, planMetalBootstrap, readMetalBootstrapConfig, renderMetalUnits, validateMetalBootstrapConfig, validateOwnerOnlyPath. Named type imports: MetalBootstrapApplyOptions, MetalBootstrapConfig, MetalBootstrapExec, MetalBootstrapPlan, MetalBootstrapResult, MetalBootstrapStatus. Import only the names used by this file.
418
526
 
419
527
  ```text
420
- import { METAL_BOOTSTRAP_STATE_PATH, MetalBootstrapError, applyMetalBootstrap, metalBootstrapStatus, planMetalBootstrap, readMetalBootstrapConfig } from '@forgezero/agent/metal-bootstrap';
421
- import { renderMetalUnits, validateMetalBootstrapConfig, validateOwnerOnlyPath } from '@forgezero/agent/metal-bootstrap';
528
+ import { METAL_BOOTSTRAP_STATE_PATH, MetalBootstrapError, applyMetalBootstrap, metalBootstrapStatus, normalizeMetalPublicIdentity, planMetalBootstrap } from '@forgezero/agent/metal-bootstrap';
529
+ import { readMetalBootstrapConfig, renderMetalUnits, validateMetalBootstrapConfig, validateOwnerOnlyPath } from '@forgezero/agent/metal-bootstrap';
422
530
  import type { MetalBootstrapApplyOptions, MetalBootstrapConfig, MetalBootstrapExec, MetalBootstrapPlan, MetalBootstrapResult, MetalBootstrapStatus } from '@forgezero/agent/metal-bootstrap';
423
531
  ```
424
532
 
@@ -491,11 +599,11 @@ if (!validate(deploymentDefinition)) throw new Error(JSON.stringify(validate.err
491
599
  <a id="forgezero-agent-software"></a>
492
600
  ## @forgezero/agent/software
493
601
 
494
- Exact managed-software inventory, requirement and evidence contracts. Named value imports: BUN_RELEASE_SHA256, OS_CATALOG, PINNED_BUN_VERSION, SOFTWARE_CATALOG, ensureSoftwareRequirements, executeSoftwareOperation, observeSoftwareHost, validateSoftwareRequirements. Named type imports: CatalogStatus, DeploymentChannel, OsCatalogEntry, SoftwareCatalogEntry, SoftwareCommandResult, SoftwareExec, SoftwareId, SoftwareObservation, SoftwareOperation, SoftwareRequirement. Import only the names used by this file.
602
+ Exact managed-software inventory, requirement and evidence contracts. Named value imports: BUN_RELEASE_SHA256, OS_CATALOG, PINNED_BUN_VERSION, SOFTWARE_CATALOG, ensureSoftwareRequirements, executeSoftwareOperation, observeSoftwareHost, runSoftwareCommand, softwareSpawnFailure, validateSoftwareRequirements. Named type imports: CatalogStatus, DeploymentChannel, OsCatalogEntry, SoftwareCatalogEntry, SoftwareCommandResult, SoftwareExec, SoftwareId, SoftwareObservation, SoftwareOperation, SoftwareRequirement. Import only the names used by this file.
495
603
 
496
604
  ```text
497
605
  import { BUN_RELEASE_SHA256, OS_CATALOG, PINNED_BUN_VERSION, SOFTWARE_CATALOG, ensureSoftwareRequirements, executeSoftwareOperation } from '@forgezero/agent/software';
498
- import { observeSoftwareHost, validateSoftwareRequirements } from '@forgezero/agent/software';
606
+ import { observeSoftwareHost, runSoftwareCommand, softwareSpawnFailure, validateSoftwareRequirements } from '@forgezero/agent/software';
499
607
  import type { CatalogStatus, DeploymentChannel, OsCatalogEntry, SoftwareCatalogEntry, SoftwareCommandResult, SoftwareExec } from '@forgezero/agent/software';
500
608
  import type { SoftwareId, SoftwareObservation, SoftwareOperation, SoftwareRequirement } from '@forgezero/agent/software';
501
609
  ```
@@ -503,10 +611,11 @@ import type { SoftwareId, SoftwareObservation, SoftwareOperation, SoftwareRequir
503
611
  <a id="forgezero-agent-software-helper"></a>
504
612
  ## @forgezero/agent/software-helper
505
613
 
506
- Root-owned fixed installation/update boundary for declared software. Named value imports: DEFAULT_SOFTWARE_HELPER_SOCKET, SOFTWARE_HELPER_GROUP, SOFTWARE_HELPER_UNIT_PATH, requestServiceActivation, requestSoftware, startSoftwareHelper. Named type imports: none. Import only the names used by this file.
614
+ Root-owned fixed installation/update boundary for declared software. Named value imports: DEFAULT_SOFTWARE_HELPER_SOCKET, SOFTWARE_HELPER_GROUP, SOFTWARE_HELPER_UNIT_PATH, requestContainerActivation, requestContainerBuild, requestServiceActivation, requestSoftware, startSoftwareHelper. Named type imports: none. Import only the names used by this file.
507
615
 
508
616
  ```text
509
- import { DEFAULT_SOFTWARE_HELPER_SOCKET, SOFTWARE_HELPER_GROUP, SOFTWARE_HELPER_UNIT_PATH, requestServiceActivation, requestSoftware, startSoftwareHelper } from '@forgezero/agent/software-helper';
617
+ import { DEFAULT_SOFTWARE_HELPER_SOCKET, SOFTWARE_HELPER_GROUP, SOFTWARE_HELPER_UNIT_PATH, requestContainerActivation, requestContainerBuild, requestServiceActivation } from '@forgezero/agent/software-helper';
618
+ import { requestSoftware, startSoftwareHelper } from '@forgezero/agent/software-helper';
510
619
  ```
511
620
 
512
621
  <a id="forgezero-agent-ubuntu"></a>
@@ -749,7 +749,7 @@ async function postSignedNode(options, path, body) {
749
749
  }
750
750
 
751
751
  // src/version.ts
752
- var VERSION3 = "0.1.53";
752
+ var VERSION3 = "0.1.55";
753
753
 
754
754
  // src/agent-heartbeat.ts
755
755
  var unquote = (value) => value.replace(/^['"]|['"]$/g, "");
package/dist/bootstrap.js CHANGED
@@ -1180,15 +1180,15 @@ async function finalizeCloudflareBootstrapAcceptance(request, fetcher = fetch) {
1180
1180
  import { createHash, createHmac, randomBytes as randomBytes3 } from "crypto";
1181
1181
  import {
1182
1182
  chmodSync as chmodSync2,
1183
- existsSync as existsSync3,
1183
+ existsSync as existsSync4,
1184
1184
  lstatSync as lstatSync2,
1185
- mkdirSync as mkdirSync3,
1186
- readFileSync as readFileSync3,
1187
- renameSync as renameSync3,
1188
- rmSync as rmSync3,
1189
- writeFileSync as writeFileSync3
1185
+ mkdirSync as mkdirSync4,
1186
+ readFileSync as readFileSync4,
1187
+ renameSync as renameSync4,
1188
+ rmSync as rmSync4,
1189
+ writeFileSync as writeFileSync4
1190
1190
  } from "fs";
1191
- import { dirname as dirname4 } from "path";
1191
+ import { dirname as dirname5 } from "path";
1192
1192
  import { fileURLToPath } from "url";
1193
1193
 
1194
1194
  // src/agent-update-helper.ts
@@ -1207,11 +1207,19 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
1207
1207
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
1208
1208
 
1209
1209
  // src/version.ts
1210
- var VERSION = "0.1.53";
1210
+ var VERSION = "0.1.55";
1211
1211
 
1212
1212
  // src/software.ts
1213
1213
  var PINNED_BUN_VERSION = "1.3.14";
1214
1214
  var BUN_RELEASE_SHA256 = "951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cbccdf7e848f";
1215
+ var DOCKER_DAEMON_CONFIG = `${JSON.stringify({
1216
+ "data-root": "/var/lib/docker",
1217
+ "storage-driver": "overlay2",
1218
+ "live-restore": true,
1219
+ "log-driver": "local",
1220
+ "log-opts": { "max-size": "10m", "max-file": "3" }
1221
+ }, null, 2)}
1222
+ `;
1215
1223
 
1216
1224
  // src/service-supervisor.ts
1217
1225
  import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, writeFileSync } from "fs";
@@ -1273,6 +1281,39 @@ var defaultHost = {
1273
1281
  now: Date.now
1274
1282
  };
1275
1283
 
1284
+ // src/container-supervisor.ts
1285
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync as readdirSync2, realpathSync as realpathSync2, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
1286
+ import { dirname as dirname3, resolve as resolve3, sep as sep2 } from "path";
1287
+ var defaultHost2 = {
1288
+ realpath: realpathSync2,
1289
+ exists: existsSync2,
1290
+ read: (path) => readFileSync2(path, "utf8"),
1291
+ write(path, content, mode) {
1292
+ mkdirSync2(dirname3(path), { recursive: true, mode: 493 });
1293
+ const next = `${path}.next`;
1294
+ writeFileSync2(next, content, { mode });
1295
+ renameSync2(next, path);
1296
+ },
1297
+ remove: (path) => rmSync2(path, { force: true }),
1298
+ list: (path) => existsSync2(path) ? readdirSync2(path) : [],
1299
+ mkdir: (path, mode) => mkdirSync2(path, { recursive: true, mode }),
1300
+ async exec(argv) {
1301
+ const child = Bun.spawn([...argv], { stdout: "pipe", stderr: "pipe", env: { PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", LANG: "C", LC_ALL: "C" } });
1302
+ const [stdout, stderr, exitCode] = await Promise.all([new Response(child.stdout).text(), new Response(child.stderr).text(), child.exited]);
1303
+ return { exitCode, output: `${stdout}${stderr}` };
1304
+ },
1305
+ async health(port, path, timeoutMs, method, expectedStatus) {
1306
+ try {
1307
+ const response = await fetch(`http://127.0.0.1:${port}${path}`, { method, redirect: "manual", signal: AbortSignal.timeout(timeoutMs) });
1308
+ return expectedStatus.includes(response.status);
1309
+ } catch {
1310
+ return false;
1311
+ }
1312
+ },
1313
+ sleep: (ms) => Bun.sleep(ms),
1314
+ now: Date.now
1315
+ };
1316
+
1276
1317
  // src/software-helper.ts
1277
1318
  var DEFAULT_SOFTWARE_HELPER_SOCKET = "/run/forgezero-software/helper.sock";
1278
1319
  var SOFTWARE_HELPER_GROUP = "forgezero-software";
@@ -2255,17 +2296,17 @@ import { randomBytes as randomBytes2 } from "crypto";
2255
2296
  import {
2256
2297
  chmodSync,
2257
2298
  copyFileSync,
2258
- existsSync as existsSync2,
2299
+ existsSync as existsSync3,
2259
2300
  lstatSync,
2260
- mkdirSync as mkdirSync2,
2261
- readFileSync as readFileSync2,
2262
- realpathSync as realpathSync2,
2263
- renameSync as renameSync2,
2264
- rmSync as rmSync2,
2301
+ mkdirSync as mkdirSync3,
2302
+ readFileSync as readFileSync3,
2303
+ realpathSync as realpathSync3,
2304
+ renameSync as renameSync3,
2305
+ rmSync as rmSync3,
2265
2306
  symlinkSync,
2266
- writeFileSync as writeFileSync2
2307
+ writeFileSync as writeFileSync3
2267
2308
  } from "fs";
2268
- import { dirname as dirname3 } from "path";
2309
+ import { dirname as dirname4 } from "path";
2269
2310
  async function readCapabilities(run) {
2270
2311
  const answers = {};
2271
2312
  const checks = Object.entries(CAPABILITY_CHECKS);
@@ -2324,28 +2365,28 @@ var runProvisionOperation = async (operation) => {
2324
2365
  }
2325
2366
  if (operation.kind === "install-runtime") {
2326
2367
  const release = `/opt/forgezero/agent/versions/${operation.version}`;
2327
- mkdirSync2(`${release}/dist`, { recursive: true, mode: 493 });
2328
- mkdirSync2(dirname3(operation.binary), { recursive: true, mode: 493 });
2368
+ mkdirSync3(`${release}/dist`, { recursive: true, mode: 493 });
2369
+ mkdirSync3(dirname4(operation.binary), { recursive: true, mode: 493 });
2329
2370
  copyFileSync(operation.source, `${release}/dist/fz-agent.js`);
2330
2371
  chmodSync(`${release}/dist/fz-agent.js`, 493);
2331
- const gitSshSource = `${dirname3(operation.source)}/fz-git-ssh.js`;
2332
- if (!existsSync2(gitSshSource))
2372
+ const gitSshSource = `${dirname4(operation.source)}/fz-git-ssh.js`;
2373
+ if (!existsSync3(gitSshSource))
2333
2374
  return { stdout: "packaged fz-git-ssh.js is missing", exitCode: 1 };
2334
2375
  copyFileSync(gitSshSource, `${release}/dist/fz-git-ssh.js`);
2335
2376
  chmodSync(`${release}/dist/fz-git-ssh.js`, 493);
2336
2377
  const pending = "/opt/forgezero/agent/current.next";
2337
- rmSync2(pending, { force: true });
2378
+ rmSync3(pending, { force: true });
2338
2379
  symlinkSync(`versions/${operation.version}`, pending);
2339
- renameSync2(pending, "/opt/forgezero/agent/current");
2340
- rmSync2(operation.binary, { force: true });
2380
+ renameSync3(pending, "/opt/forgezero/agent/current");
2381
+ rmSync3(operation.binary, { force: true });
2341
2382
  symlinkSync("/opt/forgezero/agent/current/dist/fz-agent.js", operation.binary);
2342
2383
  const gitSshBinary = "/usr/local/lib/forgezero/agent/fz-git-ssh";
2343
- rmSync2(gitSshBinary, { force: true });
2384
+ rmSync3(gitSshBinary, { force: true });
2344
2385
  symlinkSync("/opt/forgezero/agent/current/dist/fz-git-ssh.js", gitSshBinary);
2345
2386
  return { stdout: "", exitCode: 0 };
2346
2387
  }
2347
2388
  if (operation.kind === "ensure-seed") {
2348
- if (existsSync2(operation.credential) && lstatSync(operation.credential).size > 0)
2389
+ if (existsSync3(operation.credential) && lstatSync(operation.credential).size > 0)
2349
2390
  return { stdout: "", exitCode: 0 };
2350
2391
  const seed = randomBytes2(32).toString("base64url");
2351
2392
  const result = await fixed(["/usr/bin/systemd-creds", "encrypt", "--name=agent-seed", "-", operation.credential], seed);
@@ -2357,9 +2398,9 @@ var runProvisionOperation = async (operation) => {
2357
2398
  const key = "/run/forgezero-git-deploy-key";
2358
2399
  const publicKey = `${key}.pub`;
2359
2400
  try {
2360
- if (!existsSync2(operation.credential) || lstatSync(operation.credential).size < 1) {
2361
- rmSync2(key, { force: true });
2362
- rmSync2(publicKey, { force: true });
2401
+ if (!existsSync3(operation.credential) || lstatSync(operation.credential).size < 1) {
2402
+ rmSync3(key, { force: true });
2403
+ rmSync3(publicKey, { force: true });
2363
2404
  let result = await fixed(["/usr/bin/ssh-keygen", "-q", "-t", "ed25519", "-N", "", "-C", "forgezero-compute", "-f", key]);
2364
2405
  if (result.exitCode !== 0)
2365
2406
  return result;
@@ -2368,8 +2409,8 @@ var runProvisionOperation = async (operation) => {
2368
2409
  return result;
2369
2410
  chmodSync(operation.credential, 256);
2370
2411
  }
2371
- if (!existsSync2(operation.publicKey) || lstatSync(operation.publicKey).size < 1) {
2372
- if (!existsSync2(key)) {
2412
+ if (!existsSync3(operation.publicKey) || lstatSync(operation.publicKey).size < 1) {
2413
+ if (!existsSync3(key)) {
2373
2414
  const decrypted = await fixed(["/usr/bin/systemd-creds", "decrypt", "--name=git-deploy-key", operation.credential, key]);
2374
2415
  if (decrypted.exitCode !== 0)
2375
2416
  return decrypted;
@@ -2377,25 +2418,25 @@ var runProvisionOperation = async (operation) => {
2377
2418
  const derived = await fixed(["/usr/bin/ssh-keygen", "-y", "-f", key]);
2378
2419
  if (derived.exitCode !== 0)
2379
2420
  return derived;
2380
- writeFileSync2(operation.publicKey, `${derived.stdout.trim()} forgezero-compute
2421
+ writeFileSync3(operation.publicKey, `${derived.stdout.trim()} forgezero-compute
2381
2422
  `, { mode: 292 });
2382
2423
  }
2383
2424
  return { stdout: "", exitCode: 0 };
2384
2425
  } finally {
2385
- rmSync2(key, { force: true });
2386
- rmSync2(publicKey, { force: true });
2426
+ rmSync3(key, { force: true });
2427
+ rmSync3(publicKey, { force: true });
2387
2428
  }
2388
2429
  }
2389
2430
  if (operation.kind === "ensure-bootstrap-ssh-identity") {
2390
2431
  const key = "/run/forgezero-bootstrap-ssh-key";
2391
2432
  const generatedPublicKey = `${key}.pub`;
2392
2433
  try {
2393
- if (!existsSync2(operation.credential) || lstatSync(operation.credential).size < 1) {
2394
- rmSync2(key, { force: true });
2395
- rmSync2(generatedPublicKey, { force: true });
2434
+ if (!existsSync3(operation.credential) || lstatSync(operation.credential).size < 1) {
2435
+ rmSync3(key, { force: true });
2436
+ rmSync3(generatedPublicKey, { force: true });
2396
2437
  let result;
2397
2438
  if (operation.source) {
2398
- const source = existsSync2(operation.source) ? lstatSync(operation.source) : undefined;
2439
+ const source = existsSync3(operation.source) ? lstatSync(operation.source) : undefined;
2399
2440
  if (!source?.isFile() || source.isSymbolicLink() || source.uid !== 0 || source.nlink !== 1 || (source.mode & 63) !== 0 || source.size < 32 || source.size > 16 * 1024) {
2400
2441
  return { stdout: "bootstrap SSH private-key source is missing or unsafe", exitCode: 1 };
2401
2442
  }
@@ -2415,8 +2456,8 @@ var runProvisionOperation = async (operation) => {
2415
2456
  return result;
2416
2457
  chmodSync(operation.credential, 256);
2417
2458
  }
2418
- if (!existsSync2(operation.publicKey) || lstatSync(operation.publicKey).size < 1) {
2419
- if (!existsSync2(key)) {
2459
+ if (!existsSync3(operation.publicKey) || lstatSync(operation.publicKey).size < 1) {
2460
+ if (!existsSync3(key)) {
2420
2461
  const decrypted = await fixed(["/usr/bin/systemd-creds", "decrypt", "--name=bootstrap-ssh-key", operation.credential, key]);
2421
2462
  if (decrypted.exitCode !== 0)
2422
2463
  return decrypted;
@@ -2426,28 +2467,28 @@ var runProvisionOperation = async (operation) => {
2426
2467
  if (derived.exitCode !== 0 || !/^ssh-ed25519 [A-Za-z0-9+/]+={0,3}\s*$/.test(derived.stdout)) {
2427
2468
  return { stdout: "bootstrap SSH public key derivation failed", exitCode: 1 };
2428
2469
  }
2429
- mkdirSync2(dirname3(operation.publicKey), { recursive: true, mode: 493 });
2430
- writeFileSync2(operation.publicKey, `${derived.stdout.trim()} forgezero-bootstrap-runner
2470
+ mkdirSync3(dirname4(operation.publicKey), { recursive: true, mode: 493 });
2471
+ writeFileSync3(operation.publicKey, `${derived.stdout.trim()} forgezero-bootstrap-runner
2431
2472
  `, { mode: 292 });
2432
2473
  chmodSync(operation.publicKey, 292);
2433
2474
  }
2434
2475
  if (operation.source)
2435
- rmSync2(operation.source, { force: true });
2476
+ rmSync3(operation.source, { force: true });
2436
2477
  return { stdout: "", exitCode: 0 };
2437
2478
  } finally {
2438
- rmSync2(key, { force: true });
2439
- rmSync2(generatedPublicKey, { force: true });
2479
+ rmSync3(key, { force: true });
2480
+ rmSync3(generatedPublicKey, { force: true });
2440
2481
  }
2441
2482
  }
2442
2483
  if (operation.kind === "ensure-enrolment") {
2443
- if (existsSync2(operation.state) && lstatSync(operation.state).size > 0 || existsSync2(operation.credential) && lstatSync(operation.credential).size > 0)
2484
+ if (existsSync3(operation.state) && lstatSync(operation.state).size > 0 || existsSync3(operation.credential) && lstatSync(operation.credential).size > 0)
2444
2485
  return { stdout: "", exitCode: 0 };
2445
- if (!existsSync2(operation.source))
2486
+ if (!existsSync3(operation.source))
2446
2487
  return { stdout: "enrolment source is missing", exitCode: 1 };
2447
2488
  const result = await fixed(["/usr/bin/systemd-creds", "encrypt", "--name=enrol-token", operation.source, operation.credential]);
2448
2489
  if (result.exitCode === 0) {
2449
2490
  chmodSync(operation.credential, 256);
2450
- rmSync2(operation.source, { force: true });
2491
+ rmSync3(operation.source, { force: true });
2451
2492
  }
2452
2493
  return result;
2453
2494
  }
@@ -2462,7 +2503,7 @@ var runProvisionOperation = async (operation) => {
2462
2503
  return { stdout: `socket did not become ready: ${operation.path}`, exitCode: 1 };
2463
2504
  }
2464
2505
  if (operation.kind === "verify-file")
2465
- return existsSync2(operation.path) && lstatSync(operation.path).size > 0 ? { stdout: "", exitCode: 0 } : { stdout: "required file is empty", exitCode: 1 };
2506
+ return existsSync3(operation.path) && lstatSync(operation.path).size > 0 ? { stdout: "", exitCode: 0 } : { stdout: "required file is empty", exitCode: 1 };
2466
2507
  if (operation.kind === "verify-egress") {
2467
2508
  const active = await fixed(["/usr/bin/systemctl", "is-active", "forgezero-agent-egress.service"]);
2468
2509
  if (active.exitCode !== 0)
@@ -2478,31 +2519,31 @@ var runProvisionOperation = async (operation) => {
2478
2519
  if (operation.kind === "verify-resolved-stub") {
2479
2520
  try {
2480
2521
  const expected = "/run/systemd/resolve/stub-resolv.conf";
2481
- return realpathSync2("/etc/resolv.conf") === expected && realpathSync2(expected) === expected ? { stdout: expected, exitCode: 0 } : { stdout: "resolver stub mismatch", exitCode: 1 };
2522
+ return realpathSync3("/etc/resolv.conf") === expected && realpathSync3(expected) === expected ? { stdout: expected, exitCode: 0 } : { stdout: "resolver stub mismatch", exitCode: 1 };
2482
2523
  } catch {
2483
2524
  return { stdout: "resolver stub missing", exitCode: 1 };
2484
2525
  }
2485
2526
  }
2486
2527
  if (operation.kind === "install-warp") {
2487
- const os = readFileSync2("/etc/os-release", "utf8");
2528
+ const os = readFileSync3("/etc/os-release", "utf8");
2488
2529
  if (!/^ID=ubuntu$/m.test(os) || !/^VERSION_ID="?26\.04"?$/m.test(os))
2489
2530
  return { stdout: "unsupported WARP host OS", exitCode: 1 };
2490
2531
  const response = await fetch("https://pkg.cloudflareclient.com/pubkey.gpg", { signal: AbortSignal.timeout(30000) });
2491
2532
  if (!response.ok)
2492
2533
  return { stdout: `WARP key HTTP ${response.status}`, exitCode: 1 };
2493
- mkdirSync2("/usr/share/keyrings", { recursive: true, mode: 493 });
2494
- mkdirSync2("/etc/apt/sources.list.d", { recursive: true, mode: 493 });
2495
- mkdirSync2("/etc/systemd/system/warp-svc.service.d", { recursive: true, mode: 493 });
2534
+ mkdirSync3("/usr/share/keyrings", { recursive: true, mode: 493 });
2535
+ mkdirSync3("/etc/apt/sources.list.d", { recursive: true, mode: 493 });
2536
+ mkdirSync3("/etc/systemd/system/warp-svc.service.d", { recursive: true, mode: 493 });
2496
2537
  const key = "/run/cloudflare-warp-key.gpg";
2497
- writeFileSync2(key, new Uint8Array(await response.arrayBuffer()), { mode: 384 });
2538
+ writeFileSync3(key, new Uint8Array(await response.arrayBuffer()), { mode: 384 });
2498
2539
  let result = await fixed(["/usr/bin/gpg", "--batch", "--yes", "--dearmor", "-o", "/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg", key]);
2499
- rmSync2(key, { force: true });
2540
+ rmSync3(key, { force: true });
2500
2541
  if (result.exitCode !== 0)
2501
2542
  return result;
2502
2543
  const codename = os.match(/^VERSION_CODENAME=(.+)$/m)?.[1]?.replace(/^"|"$/g, "");
2503
2544
  if (!codename)
2504
2545
  return { stdout: "Ubuntu codename missing", exitCode: 1 };
2505
- writeFileSync2("/etc/apt/sources.list.d/cloudflare-client.list", `deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ ${codename} main
2546
+ writeFileSync3("/etc/apt/sources.list.d/cloudflare-client.list", `deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ ${codename} main
2506
2547
  `, { mode: 420 });
2507
2548
  result = await fixed(["/usr/bin/apt-get", "update", "-qq"]);
2508
2549
  return result.exitCode === 0 ? fixed(["/usr/bin/apt-get", "install", "-y", "cloudflare-warp"]) : result;
@@ -4175,7 +4216,7 @@ function readBootstrapConfig(path) {
4175
4216
  if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.uid !== (process.getuid?.() ?? metadata.uid) || metadata.nlink !== 1 || (metadata.mode & 63) !== 0 || metadata.size > 64 * 1024) {
4176
4217
  throw new Error("bootstrap config must be an owner-only regular file with one link and at most 64 KiB");
4177
4218
  }
4178
- return validateBootstrapConfig(strictBootstrapDocument(JSON.parse(readFileSync3(path, "utf8"))));
4219
+ return validateBootstrapConfig(strictBootstrapDocument(JSON.parse(readFileSync4(path, "utf8"))));
4179
4220
  }
4180
4221
  function localBootstrapHost() {
4181
4222
  const execute = async (argv, options = {}) => {
@@ -4193,17 +4234,17 @@ function localBootstrapHost() {
4193
4234
  };
4194
4235
  return {
4195
4236
  uid: () => process.getuid?.() ?? -1,
4196
- exists: existsSync3,
4197
- read: (path) => readFileSync3(path, "utf8"),
4237
+ exists: existsSync4,
4238
+ read: (path) => readFileSync4(path, "utf8"),
4198
4239
  write(path, content, mode) {
4199
- mkdirSync3(dirname4(path), { recursive: true, mode: 493 });
4240
+ mkdirSync4(dirname5(path), { recursive: true, mode: 493 });
4200
4241
  const temporary = `${path}.next.${process.pid}`;
4201
- writeFileSync3(temporary, content, { mode });
4242
+ writeFileSync4(temporary, content, { mode });
4202
4243
  chmodSync2(temporary, mode);
4203
- renameSync3(temporary, path);
4244
+ renameSync4(temporary, path);
4204
4245
  },
4205
- mkdir: (path, mode) => mkdirSync3(path, { recursive: true, mode }),
4206
- remove: (path) => rmSync3(path, { force: true }),
4246
+ mkdir: (path, mode) => mkdirSync4(path, { recursive: true, mode }),
4247
+ remove: (path) => rmSync4(path, { force: true }),
4207
4248
  inspect(path) {
4208
4249
  const value = lstatSync2(path);
4209
4250
  return { regular: value.isFile(), symbolic: value.isSymbolicLink(), uid: value.uid, mode: value.mode, links: value.nlink, size: value.size };
@@ -4227,7 +4268,7 @@ function localBootstrapHost() {
4227
4268
  async installAgent(config, enrolTokenSourcePath) {
4228
4269
  const capabilities = await readCapabilities(localRunner);
4229
4270
  const deployRoot = config.deployRoot ?? "/opt/forgezero";
4230
- const hasBinding = config.kind === "enrolled-compute" || Boolean(enrolTokenSourcePath) || existsSync3("/var/lib/forgezero/enrolment.json");
4271
+ const hasBinding = config.kind === "enrolled-compute" || Boolean(enrolTokenSourcePath) || existsSync4("/var/lib/forgezero/enrolment.json");
4231
4272
  if (config.kind === "platform") {
4232
4273
  const lifecycle = config.database.role === "none" ? {
4233
4274
  apiUnits: ["forgezero@blue.service", "forgezero@green.service"],
@@ -4239,8 +4280,8 @@ function localBootstrapHost() {
4239
4280
  databaseHealthUrl: `http://${config.database.address}:8529/_api/version`,
4240
4281
  databasePorts: [8529]
4241
4282
  };
4242
- mkdirSync3(dirname4(LIFECYCLE_PROFILE), { recursive: true, mode: 493 });
4243
- writeFileSync3(LIFECYCLE_PROFILE, `${JSON.stringify(lifecycle, null, 2)}
4283
+ mkdirSync4(dirname5(LIFECYCLE_PROFILE), { recursive: true, mode: 493 });
4284
+ writeFileSync4(LIFECYCLE_PROFILE, `${JSON.stringify(lifecycle, null, 2)}
4244
4285
  `, { mode: 256 });
4245
4286
  }
4246
4287
  const plan = planInstall({
@@ -4281,8 +4322,8 @@ function localBootstrapHost() {
4281
4322
  } : {}
4282
4323
  });
4283
4324
  for (const unit of [{ path: plan.unitPath, unit: plan.unit }, ...plan.auxiliaryUnits]) {
4284
- mkdirSync3(dirname4(unit.path), { recursive: true, mode: 493 });
4285
- writeFileSync3(unit.path, unit.unit, { mode: 420 });
4325
+ mkdirSync4(dirname5(unit.path), { recursive: true, mode: 493 });
4326
+ writeFileSync4(unit.path, unit.unit, { mode: 420 });
4286
4327
  }
4287
4328
  await applyPlan(plan, localRunner);
4288
4329
  return plan;