@forgezero/agent 0.1.74 → 0.1.76

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.
Files changed (43) hide show
  1. package/README.md +126 -17
  2. package/dist/agent-heartbeat.d.ts +25 -1
  3. package/dist/agent-heartbeat.js +45 -5
  4. package/dist/bootstrap.js +10 -7
  5. package/dist/community-rehearsal-host.js +218 -13
  6. package/dist/credential-schema.d.ts +1 -1
  7. package/dist/credential-schema.js +1 -1
  8. package/dist/definition.js +206 -7
  9. package/dist/deploy-actions.d.ts +7 -0
  10. package/dist/deploy-compiler.js +191 -10
  11. package/dist/deploy-file.js +206 -7
  12. package/dist/deploy-plan-runner.d.ts +3 -0
  13. package/dist/deploy-plan-runner.js +190 -9
  14. package/dist/deploy-plan.js +187 -8
  15. package/dist/deploy-providers.d.ts +19 -0
  16. package/dist/deploy.d.ts +89 -3
  17. package/dist/deploy.js +49 -2
  18. package/dist/deployment-connectivity.d.ts +63 -0
  19. package/dist/deployment-connectivity.js +155 -0
  20. package/dist/deployment-pull.d.ts +2 -0
  21. package/dist/deployment-targets.d.ts +9 -0
  22. package/dist/deployment-targets.js +141 -0
  23. package/dist/deployment.d.ts +69 -0
  24. package/dist/fz-agent.js +1122 -313
  25. package/dist/fz.js +234 -26
  26. package/dist/index.d.ts +1 -0
  27. package/dist/metal-bootstrap.js +1 -1
  28. package/dist/metal-helper-socket.js +210 -11
  29. package/dist/metal-provision.js +210 -11
  30. package/dist/operator-bootstrap.js +11 -9
  31. package/dist/platform-bootstrap-runtime.js +209 -10
  32. package/dist/platform-fleet-verification.js +525 -155
  33. package/dist/platform-genesis.js +206 -7
  34. package/dist/process-input.d.ts +11 -0
  35. package/dist/provision.js +472 -99
  36. package/dist/service-supervisor.d.ts +6 -0
  37. package/dist/software-helper.d.ts +3 -0
  38. package/dist/software-helper.js +472 -98
  39. package/dist/software.d.ts +4 -1
  40. package/dist/software.js +210 -8
  41. package/dist/ubuntu.js +206 -7
  42. package/dist/version.d.ts +1 -1
  43. package/package.json +12 -4
package/README.md CHANGED
@@ -63,6 +63,8 @@ Every row links to the detailed explanation and named-import/example area below.
63
63
  | @forgezero/agent/credential-schema | Canonical systemd credential names and validation shared by bootstrap and services. | Bun/Node host | [Reference + usage](#forgezero-agent-credential-schema) |
64
64
  | @forgezero/agent/definition | Legacy version 3 JSON deployment compatibility contract. | Bun/Node host | [Reference + usage](#forgezero-agent-definition) |
65
65
  | @forgezero/agent/deploy | Typed deployment authoring builders, built-in provider requirements and versioned action constructors. | Bun/Node host | [Reference + usage](#forgezero-agent-deploy) |
66
+ | @forgezero/agent/deployment-targets | Bounded input resolution and stable compute-target assignment for typed plans; emits requirements and slots rather than physical hostnames. | Bun/Node host | [Reference + usage](#forgezero-agent-deployment-targets) |
67
+ | @forgezero/agent/deployment-connectivity | Root-owned application of per-target Cloudflare Tunnel or WARP connector capabilities without exposing the project management token to a compute. | Bun/Node host | [Reference + usage](#forgezero-agent-deployment-connectivity) |
66
68
  | @forgezero/agent/deploy-plan | Runtime validation, canonicalization and semantic digest for inert deployment plans. | Bun/Node host | [Reference + usage](#forgezero-agent-deploy-plan) |
67
69
  | @forgezero/agent/deploy-plan-runner | Conditional DAG execution with bounded retry, compensation and failure policy over injected action handlers. | Bun/Node host | [Reference + usage](#forgezero-agent-deploy-plan-runner) |
68
70
  | @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 | [Reference + usage](#forgezero-agent-deploy-compiler) |
@@ -495,7 +497,7 @@ export const selectedCapability = PipelineError;
495
497
  <a id="forgezero-agent-agent-heartbeat"></a>
496
498
  ## @forgezero/agent/agent-heartbeat
497
499
 
498
- Signed node health publication with bounded renewal and shutdown withdrawal. This entry exposes 3 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.
500
+ Signed node health publication with bounded renewal and shutdown withdrawal. This entry exposes 3 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.
499
501
 
500
502
  ```text
501
503
  import {
@@ -507,6 +509,7 @@ import {
507
509
  import type {
508
510
  AgentHeartbeatOptions,
509
511
  AgentHeartbeatResponse,
512
+ AgentHostMetrics,
510
513
  AgentObservation,
511
514
  } from '@forgezero/agent/agent-heartbeat';
512
515
  ```
@@ -1064,7 +1067,7 @@ export const selectedCapability = DEPLOY_SCHEMA_URL;
1064
1067
  <a id="forgezero-agent-deploy"></a>
1065
1068
  ## @forgezero/agent/deploy
1066
1069
 
1067
- Typed deployment authoring builders, built-in provider requirements and versioned action constructors. This entry exposes 14 named value exports and 35 named type exports. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
1070
+ Typed deployment authoring builders, built-in provider requirements and versioned action constructors. This entry exposes 14 named value exports and 41 named type exports. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
1068
1071
 
1069
1072
  ```text
1070
1073
  import {
@@ -1091,17 +1094,22 @@ import type {
1091
1094
  BunProviderConfig,
1092
1095
  CloudflaredProviderConfig,
1093
1096
  ComponentDefinition,
1097
+ ComputeConnectivityDefinition,
1098
+ ComputePoolDefinition,
1094
1099
  Condition,
1095
1100
  ConditionOperand,
1101
+ ContainerdProviderConfig,
1096
1102
  CredentialDefinition,
1097
1103
  DatabaseComponent,
1098
1104
  DeploymentSource,
1099
1105
  DockerProviderConfig,
1100
1106
  HealthDefinition,
1101
1107
  InputDefinition,
1108
+ IntegerValue,
1102
1109
  JsonObject,
1103
1110
  JsonPrimitive,
1104
1111
  JsonValue,
1112
+ KataProviderConfig,
1105
1113
  NetworkDefinition,
1106
1114
  NginxProviderConfig,
1107
1115
  OpenSshProviderConfig,
@@ -1113,6 +1121,7 @@ import type {
1113
1121
  StageDefinition,
1114
1122
  StepScope,
1115
1123
  StorageMount,
1124
+ StringValue,
1116
1125
  SystemdProviderConfig,
1117
1126
  TargetDefinition,
1118
1127
  UfwProviderConfig,
@@ -1123,16 +1132,19 @@ import type {
1123
1132
  } from '@forgezero/agent/deploy';
1124
1133
  ```
1125
1134
 
1126
- ## @forgezero/agent/deploy — Author a typed native or Docker deployment
1135
+ ## @forgezero/agent/deploy — Author a typed, placed containerd deployment
1127
1136
 
1128
- 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.
1137
+ TypeScript is authoring only. The checked-in canonical plan is what Agents validate and execute. Targets declare bounded resource/cardinality intent, not hostnames. The API reserves eligible project capacity or provisions only under the reviewed plan and spend ceiling, assigns stable slots, and the Agent runs only its exact assignment. runc containers must declare hard CPU, memory and PID limits, bounded storage, loopback ingress and a health-gated rollout; select Kata QEMU SNP when the workload needs host isolation.
1129
1138
 
1130
1139
  ```text
1131
1140
  import {
1132
1141
  actions,
1133
- application,
1134
- defineDeployment,
1135
- providers,
1142
+ application,
1143
+ credential,
1144
+ defineDeployment,
1145
+ input,
1146
+ providers,
1147
+ ref,
1136
1148
  stage,
1137
1149
  target,
1138
1150
  workflow,
@@ -1142,24 +1154,117 @@ export default defineDeployment({
1142
1154
  apiVersion: 'deploy.forgezero.net/v1', kind: 'Deployment',
1143
1155
  metadata: { name: 'orders-api' },
1144
1156
  spec: {
1145
- targets: { api: target.compute({ selector: { profiles: ['api'] }, cardinality: { minimum: 1, desired: 2, maximum: 16 } }) },
1146
- requirements: { docker: providers.docker.require(), nginx: providers.nginx.require() },
1157
+ inputs: {
1158
+ replicas: input.integer({ minimum: 1, maximum: 16, default: 1 }),
1159
+ cpuCores: input.integer({ minimum: 1, maximum: 32, default: 1 }),
1160
+ memoryMiB: input.integer({ minimum: 256, maximum: 131072, default: 512 }),
1161
+ storageGiB: input.integer({ minimum: 4, maximum: 2048, default: 8 })
1162
+ },
1163
+ credentials: {
1164
+ cloudflare: credential.vault({ schema: 'forgezero.cloudflare/management@1', name: 'cloudflare-management' })
1165
+ },
1166
+ targets: { api: target.compute({
1167
+ profiles: ['api'], replicas: { minimum: 1, desired: input.ref('replicas'), maximum: 16 },
1168
+ resources: { cpuCores: input.ref('cpuCores'), memoryMiB: input.ref('memoryMiB'), storageGiB: input.ref('storageGiB') },
1169
+ os: 'ubuntu-24.04', runtime: 'oci-runc', sharing: 'shared', reuse: 'prefer',
1170
+ provisioning: {
1171
+ planKey: 'app-small', regionKey: 'default', imageKey: 'ubuntu-24.04-x64',
1172
+ environmentKey: 'production', ownership: 'platform', maxMonthlySpendMinor: '25000'
1173
+ },
1174
+ connectivity: {
1175
+ private: { mode: 'private-lan' },
1176
+ public: { mode: 'cloudflare-tunnel', credential: 'cloudflare', zone: 'example.com', hostname: { mode: 'indexed', prefix: 'api' } }
1177
+ }
1178
+ }) },
1179
+ requirements: {
1180
+ containerd: providers.containerd.require(), nginx: providers.nginx.require(),
1181
+ cloudflared: providers.cloudflared.require()
1182
+ },
1147
1183
  components: {
1148
1184
  api: application({
1149
1185
  target: 'api',
1150
- 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'] } },
1186
+ runtime: { kind: 'container', provider: 'forgezero.containerd', requirement: 'containerd', runtimeClass: 'runc', image: { source: { kind: 'build', context: '.', dockerfile: 'Dockerfile' } }, security: { privileged: false, noNewPrivileges: true, root: 'read-only', dropCapabilities: ['ALL'] } },
1151
1187
  service: { protocol: 'http', port: 3000, health: { protocol: 'http', method: 'GET', path: '/health', expectedStatus: [200], timeoutMs: 2000 } },
1152
- resources: { cpu: { limit: 2 }, memory: { limitMiB: 2048, swap: 'disabled' }, pids: { limit: 256 } },
1188
+ resources: { cpu: { limit: 1 }, memory: { limitMiB: 512, swap: 'disabled' }, pids: { limit: 256 } },
1153
1189
  storage: [{ class: 'ephemeral', path: '/tmp', type: 'tmpfs', sizeMiB: 128 }],
1154
- network: { ingress: { exposure: 'loopback', stablePort: 3000 }, container: { mode: 'bridge', network: 'api' } },
1190
+ network: {
1191
+ ingress: { exposure: 'public', stablePort: 3000 },
1192
+ public: { mode: 'cloudflare-tunnel' },
1193
+ container: { mode: 'bridge', network: 'api' }
1194
+ },
1155
1195
  rollout: { strategy: 'blue-green', proxy: 'nginx', automaticRollback: true }
1156
1196
  })
1157
1197
  },
1158
- workflows: { deploy: workflow({ stages: { prepare: stage({ strategy: { mode: 'sequential' }, steps: { software: actions.software.ensure({ requirements: ['docker', 'nginx'] }, { scope: { kind: 'each-target', target: 'api' } }) } }) } }) }
1198
+ workflows: { deploy: workflow({ stages: {
1199
+ prepare: stage({ strategy: { mode: 'sequential' }, steps: {
1200
+ software: actions.software.ensure({ requirements: ['containerd', 'nginx', 'cloudflared'] }, { scope: { kind: 'each-target', target: 'api' } }),
1201
+ network: actions.network.ensure({ target: 'api' }, { scope: { kind: 'each-target', target: 'api' } }),
1202
+ image: actions.container.build({ component: 'api' }, { scope: { kind: 'each-target', target: 'api' } })
1203
+ } }),
1204
+ release: stage({ dependsOn: ['prepare'], strategy: { mode: 'blue-green' }, steps: {
1205
+ promote: actions.service.promote({ component: 'api', imageDigest: ref.stepOutput('image', 'digest') }, { scope: { kind: 'each-target', target: 'api' } })
1206
+ } })
1207
+ } }) }
1159
1208
  }
1160
1209
  });
1161
1210
  ```
1162
1211
 
1212
+ <a id="forgezero-agent-deployment-targets"></a>
1213
+ ## @forgezero/agent/deployment-targets
1214
+
1215
+ Bounded input resolution and stable compute-target assignment for typed plans; emits requirements and slots rather than physical hostnames. This entry exposes 3 named value exports and 0 named type exports. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
1216
+
1217
+ ```text
1218
+ import {
1219
+ DeploymentInputError,
1220
+ resolveDeploymentInputs,
1221
+ resolveDeploymentTargets,
1222
+ } from '@forgezero/agent/deployment-targets';
1223
+ ```
1224
+
1225
+ ## @forgezero/agent/deployment-targets — Use this entry point
1226
+
1227
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
1228
+
1229
+ ```text
1230
+ import {
1231
+ DeploymentInputError,
1232
+ } from '@forgezero/agent/deployment-targets';
1233
+
1234
+ export const selectedCapability = DeploymentInputError;
1235
+ ```
1236
+
1237
+ <a id="forgezero-agent-deployment-connectivity"></a>
1238
+ ## @forgezero/agent/deployment-connectivity
1239
+
1240
+ Root-owned application of per-target Cloudflare Tunnel or WARP connector capabilities without exposing the project management token to a compute. This entry exposes 1 named value export and 5 named type exports. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
1241
+
1242
+ ```text
1243
+ import {
1244
+ applyDeploymentConnectivity,
1245
+ } from '@forgezero/agent/deployment-connectivity';
1246
+
1247
+ import type {
1248
+ DeploymentConnectivityCapabilities,
1249
+ DeploymentConnectivityEvidence,
1250
+ DeploymentConnectivityHost,
1251
+ DeploymentConnectivityIntent,
1252
+ DeploymentConnectivityRequest,
1253
+ } from '@forgezero/agent/deployment-connectivity';
1254
+ ```
1255
+
1256
+ ## @forgezero/agent/deployment-connectivity — Use this entry point
1257
+
1258
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
1259
+
1260
+ ```text
1261
+ import {
1262
+ applyDeploymentConnectivity,
1263
+ } from '@forgezero/agent/deployment-connectivity';
1264
+
1265
+ export const selectedCapability = applyDeploymentConnectivity;
1266
+ ```
1267
+
1163
1268
  <a id="forgezero-agent-deploy-plan"></a>
1164
1269
  ## @forgezero/agent/deploy-plan
1165
1270
 
@@ -1540,11 +1645,14 @@ if (!validate(deploymentDefinition)) throw new Error(JSON.stringify(validate.err
1540
1645
  <a id="forgezero-agent-software"></a>
1541
1646
  ## @forgezero/agent/software
1542
1647
 
1543
- Exact managed-software inventory, requirement and evidence contracts. This entry exposes 10 named value exports and 10 named type exports. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
1648
+ Exact managed-software inventory, requirement and evidence contracts. This entry exposes 13 named value exports and 10 named type exports. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
1544
1649
 
1545
1650
  ```text
1546
1651
  import {
1652
+ BUILDKIT_VERSION,
1547
1653
  BUN_RELEASE_SHA256,
1654
+ KATA_CONTAINERS_VERSION,
1655
+ NERDCTL_VERSION,
1548
1656
  OS_CATALOG,
1549
1657
  PINNED_BUN_VERSION,
1550
1658
  SOFTWARE_CATALOG,
@@ -1576,16 +1684,16 @@ This minimal executable use imports one concrete value from this exact entry poi
1576
1684
 
1577
1685
  ```text
1578
1686
  import {
1579
- BUN_RELEASE_SHA256,
1687
+ BUILDKIT_VERSION,
1580
1688
  } from '@forgezero/agent/software';
1581
1689
 
1582
- export const selectedCapability = BUN_RELEASE_SHA256;
1690
+ export const selectedCapability = BUILDKIT_VERSION;
1583
1691
  ```
1584
1692
 
1585
1693
  <a id="forgezero-agent-software-helper"></a>
1586
1694
  ## @forgezero/agent/software-helper
1587
1695
 
1588
- Root-owned fixed installation/update boundary for declared software. This entry exposes 8 named value exports and 0 named type exports. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
1696
+ Root-owned fixed installation/update boundary for declared software. This entry exposes 9 named value exports and 0 named type exports. The generated import block lists one name per line for scanning and copying; keep only the names used by your file.
1589
1697
 
1590
1698
  ```text
1591
1699
  import {
@@ -1594,6 +1702,7 @@ import {
1594
1702
  SOFTWARE_HELPER_UNIT_PATH,
1595
1703
  requestContainerActivation,
1596
1704
  requestContainerBuild,
1705
+ requestDeploymentConnectivity,
1597
1706
  requestServiceActivation,
1598
1707
  requestSoftware,
1599
1708
  startSoftwareHelper,
@@ -10,6 +10,22 @@ export interface AgentObservation {
10
10
  };
11
11
  architecture: string;
12
12
  mode: 'attested' | 'enrolled';
13
+ runtimeCapabilities?: {
14
+ native: true;
15
+ ociRunc: boolean;
16
+ kataQemuSnp: boolean;
17
+ };
18
+ /** Signed host evidence. Placement still reserves declared capacity; it never chases transient utilization. */
19
+ capacity?: {
20
+ logicalCpu: number;
21
+ memoryMiB: number;
22
+ storageGiB: number;
23
+ };
24
+ utilization?: {
25
+ load1mMilli: number;
26
+ memoryUsedMiB: number;
27
+ storageUsedGiB: number;
28
+ };
13
29
  update?: AgentUpdateReceipt;
14
30
  deploymentIntake?: {
15
31
  generation: number;
@@ -32,6 +48,14 @@ export interface AgentHeartbeatResponse {
32
48
  state: 'running' | 'paused';
33
49
  };
34
50
  }
51
+ export interface AgentHostMetrics {
52
+ logicalCpu: number;
53
+ memoryBytes: number;
54
+ memoryFreeBytes: number;
55
+ storageBytes: number;
56
+ storageFreeBytes: number;
57
+ load1m: number;
58
+ }
35
59
  export interface AgentHeartbeatOptions {
36
60
  apiUrl: string;
37
61
  nodeKey: string;
@@ -58,7 +82,7 @@ export interface AgentHeartbeatOptions {
58
82
  onEvent?: (event: string, detail?: unknown) => void;
59
83
  telemetry?: AgentOperationTelemetry;
60
84
  }
61
- export declare function observeAgentHost(version?: string, mode?: AgentObservation['mode'], osRelease?: string, architecture?: NodeJS.Architecture): AgentObservation;
85
+ export declare function observeAgentHost(version?: string, mode?: AgentObservation['mode'], osRelease?: string, architecture?: NodeJS.Architecture, metrics?: AgentHostMetrics): AgentObservation;
62
86
  /** One PQ-authenticated observation and optional supervised update decision. */
63
87
  export declare function heartbeatAgentOnce(options: AgentHeartbeatOptions): Promise<AgentHeartbeatResponse>;
64
88
  /** Server-paced heartbeat; stopping waits for the active PQ request/update handoff. */
@@ -677,7 +677,8 @@ function requestAgentUpdate(request, socketPath = DEFAULT_AGENT_UPDATE_SOCKET, t
677
677
  }
678
678
 
679
679
  // src/agent-heartbeat.ts
680
- import { readFileSync as readFileSync3 } from "node:fs";
680
+ import { existsSync as existsSync3, readFileSync as readFileSync3, statfsSync } from "node:fs";
681
+ import { cpus, freemem, loadavg, totalmem } from "node:os";
681
682
 
682
683
  // src/signed-node-http.ts
683
684
  import {
@@ -749,25 +750,64 @@ async function postSignedNode(options, path, body) {
749
750
  }
750
751
 
751
752
  // src/version.ts
752
- var VERSION3 = "0.1.74";
753
+ var VERSION3 = "0.1.76";
753
754
 
754
755
  // src/agent-heartbeat.ts
756
+ function readAgentHostMetrics() {
757
+ const filesystem = statfsSync("/");
758
+ return {
759
+ logicalCpu: cpus().length,
760
+ memoryBytes: totalmem(),
761
+ memoryFreeBytes: freemem(),
762
+ storageBytes: Number(filesystem.blocks) * Number(filesystem.bsize),
763
+ storageFreeBytes: Number(filesystem.bavail) * Number(filesystem.bsize),
764
+ load1m: loadavg()[0]
765
+ };
766
+ }
755
767
  var unquote = (value) => value.replace(/^['"]|['"]$/g, "");
756
768
  var remoteOutcome = (cause) => cause instanceof SignedNodeHttpError && cause.status < 500 ? "refused" : cause instanceof SignedNodeHttpError ? "retryable" : "failed";
757
769
 
758
770
  class AgentUpdateRefusedError extends Error {
759
771
  }
760
- function observeAgentHost(version = VERSION3, mode = "enrolled", osRelease = readFileSync3("/etc/os-release", "utf8"), architecture = process.arch) {
772
+ function observeAgentHost(version = VERSION3, mode = "enrolled", osRelease = readFileSync3("/etc/os-release", "utf8"), architecture = process.arch, metrics = readAgentHostMetrics()) {
761
773
  const values = Object.fromEntries(osRelease.split(`
762
774
  `).flatMap((line) => {
763
775
  const separator = line.indexOf("=");
764
776
  return separator > 0 ? [[line.slice(0, separator), unquote(line.slice(separator + 1))]] : [];
765
777
  }));
778
+ const osId = (values.ID ?? "unknown").toLowerCase();
779
+ const osVersion = values.VERSION_ID ?? "unknown";
780
+ const ubuntuX64 = osId === "ubuntu" && ["24.04", "26.04"].includes(osVersion) && architecture === "x64";
781
+ const enabled = (path) => {
782
+ try {
783
+ return /^(?:1|y)$/i.test(readFileSync3(path, "utf8").trim());
784
+ } catch {
785
+ return false;
786
+ }
787
+ };
788
+ const gib = 1024 ** 3;
789
+ const mib = 1024 ** 2;
790
+ const storageGiB = Math.max(1, Math.floor(metrics.storageBytes / gib));
766
791
  return {
767
792
  version,
768
- os: { id: (values.ID ?? "unknown").toLowerCase(), versionId: values.VERSION_ID ?? "unknown" },
793
+ os: { id: osId, versionId: osVersion },
769
794
  architecture,
770
- mode
795
+ mode,
796
+ runtimeCapabilities: {
797
+ native: true,
798
+ ociRunc: ubuntuX64,
799
+ kataQemuSnp: ubuntuX64 && osVersion === "26.04" && existsSync3("/dev/kvm") && existsSync3("/dev/sev") && enabled("/sys/module/kvm_amd/parameters/sev") && enabled("/sys/module/kvm_amd/parameters/sev_snp")
800
+ },
801
+ capacity: {
802
+ logicalCpu: Math.max(1, metrics.logicalCpu),
803
+ memoryMiB: Math.max(1, Math.floor(metrics.memoryBytes / mib)),
804
+ storageGiB
805
+ },
806
+ utilization: {
807
+ load1mMilli: Math.max(0, Math.round(metrics.load1m * 1000)),
808
+ memoryUsedMiB: Math.max(0, Math.floor((metrics.memoryBytes - metrics.memoryFreeBytes) / mib)),
809
+ storageUsedGiB: Math.max(0, Math.min(storageGiB, Math.ceil((metrics.storageBytes - metrics.storageFreeBytes) / gib)))
810
+ }
771
811
  };
772
812
  }
773
813
  async function heartbeatAgentOnce(options) {
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.74";
1419
+ var VERSION = "0.1.76";
1420
1420
 
1421
1421
  // src/software.ts
1422
1422
  var PINNED_BUN_VERSION = "1.3.14";
@@ -1549,6 +1549,12 @@ var defaultHost2 = {
1549
1549
  now: Date.now
1550
1550
  };
1551
1551
 
1552
+ // src/process-input.ts
1553
+ async function writeAndCloseProcessInput(input, value) {
1554
+ input.write(value);
1555
+ await input.end();
1556
+ }
1557
+
1552
1558
  // src/software-helper.ts
1553
1559
  var DEFAULT_SOFTWARE_HELPER_SOCKET = "/run/forgezero-software/helper.sock";
1554
1560
  var SOFTWARE_HELPER_GROUP = "forgezero-software";
@@ -2575,10 +2581,8 @@ var fixed = async (argv, stdin) => {
2575
2581
  stderr: "pipe",
2576
2582
  env: { PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", LANG: "C", LC_ALL: "C", DEBIAN_FRONTEND: "noninteractive" }
2577
2583
  });
2578
- if (stdin !== undefined && child.stdin && typeof child.stdin !== "number") {
2579
- child.stdin.write(stdin);
2580
- child.stdin.end();
2581
- }
2584
+ if (stdin !== undefined && child.stdin && typeof child.stdin !== "number")
2585
+ await writeAndCloseProcessInput(child.stdin, stdin);
2582
2586
  const [stdout, stderr, exitCode] = await Promise.all([
2583
2587
  new Response(child.stdout).text(),
2584
2588
  new Response(child.stderr).text(),
@@ -4874,8 +4878,7 @@ function localBootstrapHost() {
4874
4878
  const execute = async (argv, options = {}) => {
4875
4879
  const child = Bun.spawn([...argv], { stdin: options.stdin === undefined ? "ignore" : "pipe", stdout: "pipe", stderr: "pipe" });
4876
4880
  if (options.stdin !== undefined && child.stdin && typeof child.stdin !== "number") {
4877
- child.stdin.write(options.stdin);
4878
- child.stdin.end();
4881
+ await writeAndCloseProcessInput(child.stdin, options.stdin);
4879
4882
  }
4880
4883
  const [stdout, stderr, exitCode] = await Promise.all([
4881
4884
  new Response(child.stdout).text(),