@forgezero/agent 0.1.63 → 0.1.65

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.
@@ -2088,7 +2088,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
2088
2088
  }
2089
2089
 
2090
2090
  // src/version.ts
2091
- var VERSION3 = "0.1.63";
2091
+ var VERSION3 = "0.1.65";
2092
2092
 
2093
2093
  // src/egress-policy.ts
2094
2094
  import { realpathSync as realpathSync3 } from "node:fs";
@@ -2627,17 +2627,16 @@ function agentUnit(options) {
2627
2627
  if (Boolean(options.pullMigrations) !== Boolean(options.lifecycleProfilePath)) {
2628
2628
  throw new Error("migration pull and lifecycle profile must be supplied together");
2629
2629
  }
2630
- const bootstrapEnabled = Boolean(options.pullBootstrap && options.bootstrapSshCredentialPath && options.bootstrapSshPublicKeyPath && options.bootstrapTargetTelemetryEndpoint);
2631
- if ([
2632
- options.pullBootstrap,
2633
- options.bootstrapSshCredentialPath,
2634
- options.bootstrapSshPublicKeyPath,
2635
- options.bootstrapTargetTelemetryEndpoint
2636
- ].some(Boolean) && !bootstrapEnabled) {
2637
- throw new Error("bootstrap pull, SSH credential, public key and target telemetry endpoint must be supplied together");
2630
+ const bootstrapIdentityEnabled = Boolean(options.bootstrapSshCredentialPath && options.bootstrapSshPublicKeyPath);
2631
+ if (Boolean(options.bootstrapSshCredentialPath) !== Boolean(options.bootstrapSshPublicKeyPath)) {
2632
+ throw new Error("bootstrap SSH credential and public key paths must be supplied together");
2633
+ }
2634
+ const bootstrapEnabled = Boolean(options.pullBootstrap);
2635
+ if (bootstrapEnabled && (!bootstrapIdentityEnabled || !options.bootstrapTargetTelemetryEndpoint) || !bootstrapEnabled && options.bootstrapTargetTelemetryEndpoint) {
2636
+ throw new Error("bootstrap pull, SSH identity and target telemetry endpoint must be supplied together");
2638
2637
  }
2639
2638
  const lifecycleHelperSocketPath = lifecycleEnabled ? systemdPath(options.lifecycleHelperSocketPath ?? LIFECYCLE_HELPER_SOCKET, "lifecycle helper socket") : undefined;
2640
- const bootstrapSshCredentialPath = bootstrapEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
2639
+ const bootstrapSshCredentialPath = bootstrapIdentityEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
2641
2640
  const warpValues = [
2642
2641
  options.warpOrganization,
2643
2642
  options.warpClientIdCredentialPath,
@@ -2876,15 +2875,13 @@ function planProvision(options) {
2876
2875
  if (Boolean(options.pullMigrations) !== Boolean(options.lifecycleProfilePath)) {
2877
2876
  throw new Error("migration pull and lifecycle profile must be supplied together");
2878
2877
  }
2879
- const bootstrapEnabled = Boolean(options.pullBootstrap && options.bootstrapSshCredentialPath && options.bootstrapSshPublicKeyPath && options.bootstrapTargetTelemetryEndpoint);
2880
- if ([
2881
- options.pullBootstrap,
2882
- options.bootstrapSshCredentialPath,
2883
- options.bootstrapSshPublicKeyPath,
2884
- options.bootstrapSshSourcePath,
2885
- options.bootstrapTargetTelemetryEndpoint
2886
- ].some(Boolean) && !bootstrapEnabled) {
2887
- throw new Error("bootstrap pull, SSH credential, public key and target telemetry endpoint must be supplied together");
2878
+ const bootstrapIdentityEnabled = Boolean(options.bootstrapSshCredentialPath && options.bootstrapSshPublicKeyPath);
2879
+ if (Boolean(options.bootstrapSshCredentialPath) !== Boolean(options.bootstrapSshPublicKeyPath) || options.bootstrapSshSourcePath && !bootstrapIdentityEnabled) {
2880
+ throw new Error("bootstrap SSH credential and public key paths must be supplied together");
2881
+ }
2882
+ const bootstrapEnabled = Boolean(options.pullBootstrap);
2883
+ if (bootstrapEnabled && (!bootstrapIdentityEnabled || !options.bootstrapTargetTelemetryEndpoint) || !bootstrapEnabled && options.bootstrapTargetTelemetryEndpoint) {
2884
+ throw new Error("bootstrap pull, SSH identity and target telemetry endpoint must be supplied together");
2888
2885
  }
2889
2886
  const warpValues = [
2890
2887
  options.warpOrganization,
@@ -2894,13 +2891,13 @@ function planProvision(options) {
2894
2891
  const warpEnabled = warpValues.every(Boolean);
2895
2892
  if (warpValues.some(Boolean) && !warpEnabled)
2896
2893
  throw new Error("WARP configuration must be supplied together");
2897
- const enrolmentEnabled = Boolean(options.enrolTokenCredentialPath && options.enrolStatePath);
2898
- if (Boolean(options.enrolTokenCredentialPath) !== Boolean(options.enrolStatePath) || options.enrolTokenSourcePath && !enrolmentEnabled) {
2899
- throw new Error("direct enrolment credential and state paths must be supplied together");
2894
+ const enrolmentEnabled = Boolean(options.enrolTokenCredentialPath);
2895
+ if (options.enrolTokenCredentialPath && !options.enrolStatePath || options.enrolTokenSourcePath && (!options.enrolTokenCredentialPath || !options.enrolStatePath)) {
2896
+ throw new Error("direct enrolment credential requires its durable state path");
2900
2897
  }
2901
2898
  const enrolTokenSourcePath = options.enrolTokenSourcePath ? systemdPath(options.enrolTokenSourcePath, "enrolment source") : undefined;
2902
2899
  const enrolTokenCredentialPath = enrolmentEnabled ? systemdPath(options.enrolTokenCredentialPath, "enrolment credential") : undefined;
2903
- const enrolStatePath = enrolmentEnabled ? systemdPath(options.enrolStatePath, "enrolment state") : undefined;
2900
+ const enrolStatePath = options.enrolStatePath ? systemdPath(options.enrolStatePath, "enrolment state") : undefined;
2904
2901
  const enrolStateDir = enrolStatePath?.replace(/\/[^/]+$/, "");
2905
2902
  const sourceBinPath = options.sourceBinPath ? systemdPath(options.sourceBinPath, "agent source binary") : undefined;
2906
2903
  const binPath = options.binPath ? systemdPath(options.binPath, "agent binary") : undefined;
@@ -2912,8 +2909,8 @@ function planProvision(options) {
2912
2909
  const gitPublicKeyDir = gitPublicKeyPath?.replace(/\/[^/]+$/, "");
2913
2910
  const lifecycleProfilePath = lifecycleEnabled ? systemdPath(options.lifecycleProfilePath, "lifecycle profile") : undefined;
2914
2911
  const lifecycleHelperSocketPath = lifecycleEnabled ? systemdPath(options.lifecycleHelperSocketPath ?? LIFECYCLE_HELPER_SOCKET, "lifecycle helper socket") : undefined;
2915
- const bootstrapSshCredentialPath = bootstrapEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
2916
- const bootstrapSshPublicKeyPath = bootstrapEnabled ? systemdPath(options.bootstrapSshPublicKeyPath, "bootstrap SSH public key") : undefined;
2912
+ const bootstrapSshCredentialPath = bootstrapIdentityEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
2913
+ const bootstrapSshPublicKeyPath = bootstrapIdentityEnabled ? systemdPath(options.bootstrapSshPublicKeyPath, "bootstrap SSH public key") : undefined;
2917
2914
  const bootstrapSshSourcePath = options.bootstrapSshSourcePath ? systemdPath(options.bootstrapSshSourcePath, "bootstrap SSH private-key source") : undefined;
2918
2915
  const bootstrapSshPublicKeyDir = bootstrapSshPublicKeyPath?.replace(/\/[^/]+$/, "");
2919
2916
  const warpClientIdCredentialPath = warpEnabled ? systemdPath(options.warpClientIdCredentialPath, "WARP client-id credential") : undefined;
@@ -3013,7 +3010,7 @@ function planProvision(options) {
3013
3010
  step("Git deploy identity directory", { kind: "directories", directories: [{ path: gitPublicKeyDir, mode: 493, owner: "root", group: "root" }] }),
3014
3011
  step("unique encrypted Git deploy identity", { kind: "ensure-git-identity", credential: gitCredentialPath, publicKey: gitPublicKeyPath })
3015
3012
  ] : [],
3016
- ...bootstrapEnabled ? [
3013
+ ...bootstrapIdentityEnabled ? [
3017
3014
  step("bootstrap SSH public identity directory", { kind: "directories", directories: [
3018
3015
  { path: bootstrapSshPublicKeyDir, mode: 493, owner: "root", group: "root" }
3019
3016
  ] }),
@@ -3049,6 +3046,10 @@ function planProvision(options) {
3049
3046
  { argv: ["/usr/bin/rm", "-f", "/etc/systemd/system/forgezero-deploy-runner.socket"] },
3050
3047
  { argv: ["/usr/bin/systemctl", "daemon-reload"] }
3051
3048
  ] })] : [],
3049
+ ...enrolStatePath && !enrolmentEnabled ? [step("retire consumed enrolment unit", { kind: "commands", commands: [
3050
+ { argv: ["/usr/bin/systemctl", "disable", "--now", "forgezero-agent-enrol.service"], acceptedExitCodes: [0, 1, 5] },
3051
+ { argv: ["/usr/bin/rm", "-f", ENROLMENT_UNIT_PATH] }
3052
+ ] })] : [],
3052
3053
  step("enable and converge services", { kind: "commands", commands: [
3053
3054
  { argv: ["/usr/bin/systemctl", "enable", ...enabledUnits] },
3054
3055
  { argv: ["/usr/bin/systemctl", "reset-failed", "forgezero-agent.service"], acceptedExitCodes: [0, 1] },
@@ -4167,6 +4168,65 @@ async function bootstrapStatus(host = localBootstrapHost()) {
4167
4168
  problems.push("durable Agent enrolment state is missing");
4168
4169
  return { initialized: problems.length === 0, kind: state.kind, profile: state.profile, services, problems };
4169
4170
  }
4171
+ function planBootstrapAgentInstall(config, phase, context) {
4172
+ const { capabilities, hasBinding, hasEnrolCredential, initialBundle } = context;
4173
+ if (phase === "bootstrap") {
4174
+ if (config.kind !== "platform" || hasBinding || !initialBundle) {
4175
+ throw new Error("release-one Agent install requires an unbound platform host and verified bootstrap bundle");
4176
+ }
4177
+ } else if (phase === "enrol") {
4178
+ if (hasBinding || !hasEnrolCredential) {
4179
+ throw new Error("Agent enrolment requires one unbound host and its sealed one-use credential");
4180
+ }
4181
+ } else if (!hasBinding) {
4182
+ throw new Error("bound Agent convergence requires durable enrolment state");
4183
+ }
4184
+ const bound = phase === "bound";
4185
+ const enrolling = phase === "enrol";
4186
+ const authenticated = enrolling || bound;
4187
+ const bootstrapRunner = platformBootstrapRunner(config) || config.kind === "enrolled-compute" && Boolean(config.bootstrapRunner);
4188
+ const options = {
4189
+ capabilities,
4190
+ socketPath: DEFAULT_SOCKET2,
4191
+ seedPath: "/var/lib/forgezero/node.seed",
4192
+ controlSocketPath: CONTROL_SOCKET,
4193
+ repository: phase === "bootstrap" ? initialBundle.path : bound && config.kind === "enrolled-compute" ? config.repository : undefined,
4194
+ branch: phase === "bootstrap" ? initialBundle.manifest.branch : bound && config.kind === "enrolled-compute" ? config.branch : undefined,
4195
+ bootstrapBundlePath: phase === "bootstrap" ? initialBundle.path : undefined,
4196
+ bootstrapBundleManifestPath: phase === "bootstrap" ? initialBundle.manifestPath : undefined,
4197
+ profile: config.profile,
4198
+ deployRoot: config.deployRoot ?? "/opt/forgezero",
4199
+ deploymentCredentials: config.deploymentCredentials,
4200
+ publicApiUrl: config.apiUrl,
4201
+ gitCredentialPath: authenticated && config.kind === "enrolled-compute" && config.gitDeployKey ? "/etc/forgezero/creds/git-deploy-key.cred" : undefined,
4202
+ gitPublicKeyPath: authenticated && config.kind === "enrolled-compute" && config.gitDeployKey ? "/etc/forgezero/git/deploy.pub" : undefined,
4203
+ generateGitIdentity: authenticated && config.kind === "enrolled-compute" && config.gitDeployKey === true,
4204
+ pullDeployments: bound,
4205
+ pullMigrations: bound && config.kind === "platform",
4206
+ pullBootstrap: bound && bootstrapRunner,
4207
+ bootstrapSshCredentialPath: authenticated && bootstrapRunner ? BOOTSTRAP_SSH_CREDENTIAL : undefined,
4208
+ bootstrapSshSourcePath: enrolling && config.kind === "enrolled-compute" ? config.bootstrapRunner?.sshPrivateKeyFile : undefined,
4209
+ bootstrapSshPublicKeyPath: authenticated && bootstrapRunner ? BOOTSTRAP_SSH_PUBLIC_KEY : undefined,
4210
+ bootstrapTargetTelemetryEndpoint: bound && bootstrapRunner ? platformBootstrapRunner(config) ? config.telemetryEndpoint : config.kind === "enrolled-compute" ? config.bootstrapRunner?.targetTelemetryEndpoint : undefined : undefined,
4211
+ lifecycleProfilePath: bound && config.kind === "platform" ? LIFECYCLE_PROFILE : undefined,
4212
+ enforceEgress: true,
4213
+ nodeHostname: config.nodeHostname,
4214
+ telemetryEndpoint: config.telemetryEndpoint,
4215
+ binPath: "/usr/local/lib/forgezero/agent/fz-agent",
4216
+ sourceBinPath: PACKAGED_AGENT_BIN,
4217
+ ...enrolling ? {
4218
+ enrolTokenCredentialPath: ENROL_CREDENTIAL,
4219
+ enrolStatePath: "/var/lib/forgezero/enrolment.json"
4220
+ } : bound ? { enrolStatePath: "/var/lib/forgezero/enrolment.json" } : {},
4221
+ ...authenticated ? {
4222
+ apiUrl: config.apiUrl,
4223
+ project: config.kind === "enrolled-compute" ? config.realm : "platform",
4224
+ environment: config.kind === "enrolled-compute" ? undefined : config.environment,
4225
+ nodeLabel: config.kind === "enrolled-compute" ? config.nodeHostname : config.computeReference
4226
+ } : {}
4227
+ };
4228
+ return planInstall(options);
4229
+ }
4170
4230
  function localBootstrapHost() {
4171
4231
  const execute = async (argv2, options = {}) => {
4172
4232
  const child = Bun.spawn([...argv2], { stdin: options.stdin === undefined ? "ignore" : "pipe", stdout: "pipe", stderr: "pipe" });
@@ -4214,10 +4274,10 @@ function localBootstrapHost() {
4214
4274
  throw new Error(`Agent software requirements failed: ${result.output.trim()}`);
4215
4275
  return result;
4216
4276
  },
4217
- async installAgent(config) {
4277
+ async installAgent(config, phase) {
4218
4278
  const capabilities = await readCapabilities(localRunner);
4219
- const deployRoot = config.deployRoot ?? "/opt/forgezero";
4220
4279
  const hasBinding = existsSync10("/var/lib/forgezero/enrolment.json");
4280
+ const hasEnrolCredential = existsSync10(ENROL_CREDENTIAL);
4221
4281
  const initialBundle = config.kind === "platform" && !existsSync10(BOOTSTRAP_RELEASE_EVIDENCE) ? {
4222
4282
  path: config.bootstrapBundle.bundleFile,
4223
4283
  manifestPath: config.bootstrapBundle.manifestFile,
@@ -4238,45 +4298,11 @@ function localBootstrapHost() {
4238
4298
  writeFileSync9(LIFECYCLE_PROFILE, `${JSON.stringify(lifecycle, null, 2)}
4239
4299
  `, { mode: 256 });
4240
4300
  }
4241
- const plan = planInstall({
4301
+ const plan = planBootstrapAgentInstall(config, phase, {
4242
4302
  capabilities,
4243
- socketPath: DEFAULT_SOCKET2,
4244
- seedPath: "/var/lib/forgezero/node.seed",
4245
- controlSocketPath: "/run/forgezero/control.sock",
4246
- repository: initialBundle?.path ?? (config.kind === "enrolled-compute" ? config.repository : undefined),
4247
- branch: initialBundle?.manifest.branch ?? (config.kind === "enrolled-compute" ? config.branch : undefined),
4248
- bootstrapBundlePath: initialBundle?.path,
4249
- bootstrapBundleManifestPath: initialBundle?.manifestPath,
4250
- profile: config.kind === "platform" ? config.profile : config.profile,
4251
- deployRoot,
4252
- deploymentCredentials: config.deploymentCredentials,
4253
- publicApiUrl: config.apiUrl,
4254
- gitCredentialPath: config.kind === "enrolled-compute" && config.gitDeployKey ? "/etc/forgezero/creds/git-deploy-key.cred" : undefined,
4255
- gitPublicKeyPath: config.kind === "enrolled-compute" && config.gitDeployKey ? "/etc/forgezero/git/deploy.pub" : undefined,
4256
- generateGitIdentity: config.kind === "enrolled-compute" && config.gitDeployKey === true,
4257
- pullDeployments: hasBinding,
4258
- pullMigrations: config.kind === "platform" && hasBinding,
4259
- pullBootstrap: platformBootstrapRunner(config) || config.kind === "enrolled-compute" && Boolean(config.bootstrapRunner),
4260
- bootstrapSshCredentialPath: platformBootstrapRunner(config) || config.kind === "enrolled-compute" && config.bootstrapRunner ? BOOTSTRAP_SSH_CREDENTIAL : undefined,
4261
- bootstrapSshSourcePath: config.kind === "enrolled-compute" ? config.bootstrapRunner?.sshPrivateKeyFile : undefined,
4262
- bootstrapSshPublicKeyPath: platformBootstrapRunner(config) || config.kind === "enrolled-compute" && config.bootstrapRunner ? BOOTSTRAP_SSH_PUBLIC_KEY : undefined,
4263
- bootstrapTargetTelemetryEndpoint: platformBootstrapRunner(config) ? config.telemetryEndpoint : config.kind === "enrolled-compute" ? config.bootstrapRunner?.targetTelemetryEndpoint : undefined,
4264
- lifecycleProfilePath: config.kind === "platform" ? LIFECYCLE_PROFILE : undefined,
4265
- enforceEgress: true,
4266
- nodeHostname: config.nodeHostname,
4267
- telemetryEndpoint: config.telemetryEndpoint,
4268
- binPath: "/usr/local/lib/forgezero/agent/fz-agent",
4269
- sourceBinPath: PACKAGED_AGENT_BIN,
4270
- ...existsSync10(ENROL_CREDENTIAL) || hasBinding ? {
4271
- enrolTokenCredentialPath: existsSync10(ENROL_CREDENTIAL) ? ENROL_CREDENTIAL : undefined,
4272
- enrolStatePath: "/var/lib/forgezero/enrolment.json"
4273
- } : {},
4274
- ...hasBinding ? {
4275
- apiUrl: config.apiUrl,
4276
- project: config.kind === "enrolled-compute" ? config.realm : "platform",
4277
- environment: config.kind === "enrolled-compute" ? undefined : config.environment,
4278
- nodeLabel: config.kind === "enrolled-compute" ? config.nodeHostname : config.computeReference
4279
- } : {}
4303
+ hasBinding,
4304
+ hasEnrolCredential,
4305
+ initialBundle
4280
4306
  });
4281
4307
  for (const unit2 of [{ path: plan.unitPath, unit: plan.unit }, ...plan.auxiliaryUnits]) {
4282
4308
  mkdirSync10(dirname9(unit2.path), { recursive: true, mode: 493 });
package/dist/provision.js CHANGED
@@ -2088,7 +2088,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
2088
2088
  }
2089
2089
 
2090
2090
  // src/version.ts
2091
- var VERSION3 = "0.1.63";
2091
+ var VERSION3 = "0.1.65";
2092
2092
 
2093
2093
  // src/egress-policy.ts
2094
2094
  import { realpathSync as realpathSync3 } from "node:fs";
@@ -2627,17 +2627,16 @@ function agentUnit(options) {
2627
2627
  if (Boolean(options.pullMigrations) !== Boolean(options.lifecycleProfilePath)) {
2628
2628
  throw new Error("migration pull and lifecycle profile must be supplied together");
2629
2629
  }
2630
- const bootstrapEnabled = Boolean(options.pullBootstrap && options.bootstrapSshCredentialPath && options.bootstrapSshPublicKeyPath && options.bootstrapTargetTelemetryEndpoint);
2631
- if ([
2632
- options.pullBootstrap,
2633
- options.bootstrapSshCredentialPath,
2634
- options.bootstrapSshPublicKeyPath,
2635
- options.bootstrapTargetTelemetryEndpoint
2636
- ].some(Boolean) && !bootstrapEnabled) {
2637
- throw new Error("bootstrap pull, SSH credential, public key and target telemetry endpoint must be supplied together");
2630
+ const bootstrapIdentityEnabled = Boolean(options.bootstrapSshCredentialPath && options.bootstrapSshPublicKeyPath);
2631
+ if (Boolean(options.bootstrapSshCredentialPath) !== Boolean(options.bootstrapSshPublicKeyPath)) {
2632
+ throw new Error("bootstrap SSH credential and public key paths must be supplied together");
2633
+ }
2634
+ const bootstrapEnabled = Boolean(options.pullBootstrap);
2635
+ if (bootstrapEnabled && (!bootstrapIdentityEnabled || !options.bootstrapTargetTelemetryEndpoint) || !bootstrapEnabled && options.bootstrapTargetTelemetryEndpoint) {
2636
+ throw new Error("bootstrap pull, SSH identity and target telemetry endpoint must be supplied together");
2638
2637
  }
2639
2638
  const lifecycleHelperSocketPath = lifecycleEnabled ? systemdPath(options.lifecycleHelperSocketPath ?? LIFECYCLE_HELPER_SOCKET, "lifecycle helper socket") : undefined;
2640
- const bootstrapSshCredentialPath = bootstrapEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
2639
+ const bootstrapSshCredentialPath = bootstrapIdentityEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
2641
2640
  const warpValues = [
2642
2641
  options.warpOrganization,
2643
2642
  options.warpClientIdCredentialPath,
@@ -2876,15 +2875,13 @@ function planProvision(options) {
2876
2875
  if (Boolean(options.pullMigrations) !== Boolean(options.lifecycleProfilePath)) {
2877
2876
  throw new Error("migration pull and lifecycle profile must be supplied together");
2878
2877
  }
2879
- const bootstrapEnabled = Boolean(options.pullBootstrap && options.bootstrapSshCredentialPath && options.bootstrapSshPublicKeyPath && options.bootstrapTargetTelemetryEndpoint);
2880
- if ([
2881
- options.pullBootstrap,
2882
- options.bootstrapSshCredentialPath,
2883
- options.bootstrapSshPublicKeyPath,
2884
- options.bootstrapSshSourcePath,
2885
- options.bootstrapTargetTelemetryEndpoint
2886
- ].some(Boolean) && !bootstrapEnabled) {
2887
- throw new Error("bootstrap pull, SSH credential, public key and target telemetry endpoint must be supplied together");
2878
+ const bootstrapIdentityEnabled = Boolean(options.bootstrapSshCredentialPath && options.bootstrapSshPublicKeyPath);
2879
+ if (Boolean(options.bootstrapSshCredentialPath) !== Boolean(options.bootstrapSshPublicKeyPath) || options.bootstrapSshSourcePath && !bootstrapIdentityEnabled) {
2880
+ throw new Error("bootstrap SSH credential and public key paths must be supplied together");
2881
+ }
2882
+ const bootstrapEnabled = Boolean(options.pullBootstrap);
2883
+ if (bootstrapEnabled && (!bootstrapIdentityEnabled || !options.bootstrapTargetTelemetryEndpoint) || !bootstrapEnabled && options.bootstrapTargetTelemetryEndpoint) {
2884
+ throw new Error("bootstrap pull, SSH identity and target telemetry endpoint must be supplied together");
2888
2885
  }
2889
2886
  const warpValues = [
2890
2887
  options.warpOrganization,
@@ -2894,13 +2891,13 @@ function planProvision(options) {
2894
2891
  const warpEnabled = warpValues.every(Boolean);
2895
2892
  if (warpValues.some(Boolean) && !warpEnabled)
2896
2893
  throw new Error("WARP configuration must be supplied together");
2897
- const enrolmentEnabled = Boolean(options.enrolTokenCredentialPath && options.enrolStatePath);
2898
- if (Boolean(options.enrolTokenCredentialPath) !== Boolean(options.enrolStatePath) || options.enrolTokenSourcePath && !enrolmentEnabled) {
2899
- throw new Error("direct enrolment credential and state paths must be supplied together");
2894
+ const enrolmentEnabled = Boolean(options.enrolTokenCredentialPath);
2895
+ if (options.enrolTokenCredentialPath && !options.enrolStatePath || options.enrolTokenSourcePath && (!options.enrolTokenCredentialPath || !options.enrolStatePath)) {
2896
+ throw new Error("direct enrolment credential requires its durable state path");
2900
2897
  }
2901
2898
  const enrolTokenSourcePath = options.enrolTokenSourcePath ? systemdPath(options.enrolTokenSourcePath, "enrolment source") : undefined;
2902
2899
  const enrolTokenCredentialPath = enrolmentEnabled ? systemdPath(options.enrolTokenCredentialPath, "enrolment credential") : undefined;
2903
- const enrolStatePath = enrolmentEnabled ? systemdPath(options.enrolStatePath, "enrolment state") : undefined;
2900
+ const enrolStatePath = options.enrolStatePath ? systemdPath(options.enrolStatePath, "enrolment state") : undefined;
2904
2901
  const enrolStateDir = enrolStatePath?.replace(/\/[^/]+$/, "");
2905
2902
  const sourceBinPath = options.sourceBinPath ? systemdPath(options.sourceBinPath, "agent source binary") : undefined;
2906
2903
  const binPath = options.binPath ? systemdPath(options.binPath, "agent binary") : undefined;
@@ -2912,8 +2909,8 @@ function planProvision(options) {
2912
2909
  const gitPublicKeyDir = gitPublicKeyPath?.replace(/\/[^/]+$/, "");
2913
2910
  const lifecycleProfilePath = lifecycleEnabled ? systemdPath(options.lifecycleProfilePath, "lifecycle profile") : undefined;
2914
2911
  const lifecycleHelperSocketPath = lifecycleEnabled ? systemdPath(options.lifecycleHelperSocketPath ?? LIFECYCLE_HELPER_SOCKET, "lifecycle helper socket") : undefined;
2915
- const bootstrapSshCredentialPath = bootstrapEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
2916
- const bootstrapSshPublicKeyPath = bootstrapEnabled ? systemdPath(options.bootstrapSshPublicKeyPath, "bootstrap SSH public key") : undefined;
2912
+ const bootstrapSshCredentialPath = bootstrapIdentityEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
2913
+ const bootstrapSshPublicKeyPath = bootstrapIdentityEnabled ? systemdPath(options.bootstrapSshPublicKeyPath, "bootstrap SSH public key") : undefined;
2917
2914
  const bootstrapSshSourcePath = options.bootstrapSshSourcePath ? systemdPath(options.bootstrapSshSourcePath, "bootstrap SSH private-key source") : undefined;
2918
2915
  const bootstrapSshPublicKeyDir = bootstrapSshPublicKeyPath?.replace(/\/[^/]+$/, "");
2919
2916
  const warpClientIdCredentialPath = warpEnabled ? systemdPath(options.warpClientIdCredentialPath, "WARP client-id credential") : undefined;
@@ -3013,7 +3010,7 @@ function planProvision(options) {
3013
3010
  step("Git deploy identity directory", { kind: "directories", directories: [{ path: gitPublicKeyDir, mode: 493, owner: "root", group: "root" }] }),
3014
3011
  step("unique encrypted Git deploy identity", { kind: "ensure-git-identity", credential: gitCredentialPath, publicKey: gitPublicKeyPath })
3015
3012
  ] : [],
3016
- ...bootstrapEnabled ? [
3013
+ ...bootstrapIdentityEnabled ? [
3017
3014
  step("bootstrap SSH public identity directory", { kind: "directories", directories: [
3018
3015
  { path: bootstrapSshPublicKeyDir, mode: 493, owner: "root", group: "root" }
3019
3016
  ] }),
@@ -3049,6 +3046,10 @@ function planProvision(options) {
3049
3046
  { argv: ["/usr/bin/rm", "-f", "/etc/systemd/system/forgezero-deploy-runner.socket"] },
3050
3047
  { argv: ["/usr/bin/systemctl", "daemon-reload"] }
3051
3048
  ] })] : [],
3049
+ ...enrolStatePath && !enrolmentEnabled ? [step("retire consumed enrolment unit", { kind: "commands", commands: [
3050
+ { argv: ["/usr/bin/systemctl", "disable", "--now", "forgezero-agent-enrol.service"], acceptedExitCodes: [0, 1, 5] },
3051
+ { argv: ["/usr/bin/rm", "-f", ENROLMENT_UNIT_PATH] }
3052
+ ] })] : [],
3052
3053
  step("enable and converge services", { kind: "commands", commands: [
3053
3054
  { argv: ["/usr/bin/systemctl", "enable", ...enabledUnits] },
3054
3055
  { argv: ["/usr/bin/systemctl", "reset-failed", "forgezero-agent.service"], acceptedExitCodes: [0, 1] },
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.63";
2
+ export declare const VERSION = "0.1.65";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgezero/agent",
3
- "version": "0.1.63",
3
+ "version": "0.1.65",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "check": "tsc --noEmit",