@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.
package/dist/fz-agent.js CHANGED
@@ -7505,10 +7505,28 @@ function createDeploymentManager(options) {
7505
7505
  const inputValues = {};
7506
7506
  for (const [name, definition2] of Object.entries(plan.spec.inputs ?? {})) {
7507
7507
  const raw = options.environment?.[name];
7508
- if (raw === undefined)
7508
+ if (raw === undefined) {
7509
+ if (definition2.default !== undefined)
7510
+ inputValues[name] = definition2.default;
7509
7511
  continue;
7512
+ }
7510
7513
  inputValues[name] = definition2.type === "boolean" ? raw === "true" : definition2.type === "integer" || definition2.type === "number" ? Number(raw) : raw;
7511
7514
  }
7515
+ const resolvedTargets = Object.entries(plan.spec.targets).filter(([, target]) => target.selector.profiles.includes(options.profile)).map(([name, target]) => {
7516
+ const desired = typeof target.cardinality.desired === "number" ? target.cardinality.desired : target.cardinality.desired.$ref.startsWith("inputs.") ? inputValues[target.cardinality.desired.$ref.slice("inputs.".length)] : undefined;
7517
+ if (!Number.isSafeInteger(desired) || Number(desired) < target.cardinality.minimum || Number(desired) > target.cardinality.maximum) {
7518
+ throw new DeploymentError("PIPELINE_FAILED", `target ${name} desired cardinality did not resolve to a bounded integer`);
7519
+ }
7520
+ return {
7521
+ name,
7522
+ profiles: [...target.selector.profiles],
7523
+ confidentialCompute: target.selector.confidentialCompute ?? "disabled",
7524
+ labels: { ...target.selector.labels ?? {} },
7525
+ minimum: target.cardinality.minimum,
7526
+ desired: Number(desired),
7527
+ maximum: target.cardinality.maximum
7528
+ };
7529
+ });
7512
7530
  const projectExec2 = options.projectExec ?? spawnExact;
7513
7531
  const materializePlanArgv = (argv2) => argv2.map((argument) => {
7514
7532
  if (argument === "${FZ_RELEASE}")
@@ -7668,11 +7686,12 @@ function createDeploymentManager(options) {
7668
7686
  branch: options.branch,
7669
7687
  revision: head,
7670
7688
  definitionDigest: plan.sourceDigest,
7671
- definitionVersion: plan.version,
7689
+ definitionVersion: 2,
7672
7690
  profile: options.profile,
7673
7691
  release,
7674
7692
  ok: true,
7675
- phases: [phase]
7693
+ phases: [phase],
7694
+ resolvedTargets
7676
7695
  };
7677
7696
  }
7678
7697
  const definition = parseDeployDefinition(readDefinition(join2(release, ".fz", "deploy.json")));
@@ -7785,7 +7804,7 @@ function createDeploymentManager(options) {
7785
7804
  branch: options.branch,
7786
7805
  revision: head,
7787
7806
  definitionDigest,
7788
- definitionVersion: definition.version,
7807
+ definitionVersion: 2,
7789
7808
  profile: options.profile,
7790
7809
  release,
7791
7810
  ok: true,
@@ -8071,6 +8090,11 @@ async function pullDeploymentOnce(options) {
8071
8090
  definitionDigest: result.definitionDigest,
8072
8091
  definitionVersion: result.definitionVersion,
8073
8092
  profile: result.profile,
8093
+ ...result.resolvedTargets ? { resolvedTargets: result.resolvedTargets.map((target) => ({
8094
+ ...target,
8095
+ profiles: [...target.profiles],
8096
+ labels: { ...target.labels }
8097
+ })) } : {},
8074
8098
  ...result.capacity ? { capacity: {
8075
8099
  recommendedConcurrency: result.capacity.recommendedConcurrency,
8076
8100
  stopReason: result.capacity.stopReason,
@@ -8542,7 +8566,7 @@ function assertSupportedGuestImage(imageKey) {
8542
8566
  }
8543
8567
 
8544
8568
  // src/version.ts
8545
- var VERSION2 = "0.1.63";
8569
+ var VERSION2 = "0.1.65";
8546
8570
 
8547
8571
  // src/ssh-bootstrap.ts
8548
8572
  class SshBootstrapError extends Error {
@@ -14007,17 +14031,16 @@ function agentUnit(options) {
14007
14031
  if (Boolean(options.pullMigrations) !== Boolean(options.lifecycleProfilePath)) {
14008
14032
  throw new Error("migration pull and lifecycle profile must be supplied together");
14009
14033
  }
14010
- const bootstrapEnabled = Boolean(options.pullBootstrap && options.bootstrapSshCredentialPath && options.bootstrapSshPublicKeyPath && options.bootstrapTargetTelemetryEndpoint);
14011
- if ([
14012
- options.pullBootstrap,
14013
- options.bootstrapSshCredentialPath,
14014
- options.bootstrapSshPublicKeyPath,
14015
- options.bootstrapTargetTelemetryEndpoint
14016
- ].some(Boolean) && !bootstrapEnabled) {
14017
- throw new Error("bootstrap pull, SSH credential, public key and target telemetry endpoint must be supplied together");
14034
+ const bootstrapIdentityEnabled = Boolean(options.bootstrapSshCredentialPath && options.bootstrapSshPublicKeyPath);
14035
+ if (Boolean(options.bootstrapSshCredentialPath) !== Boolean(options.bootstrapSshPublicKeyPath)) {
14036
+ throw new Error("bootstrap SSH credential and public key paths must be supplied together");
14037
+ }
14038
+ const bootstrapEnabled = Boolean(options.pullBootstrap);
14039
+ if (bootstrapEnabled && (!bootstrapIdentityEnabled || !options.bootstrapTargetTelemetryEndpoint) || !bootstrapEnabled && options.bootstrapTargetTelemetryEndpoint) {
14040
+ throw new Error("bootstrap pull, SSH identity and target telemetry endpoint must be supplied together");
14018
14041
  }
14019
14042
  const lifecycleHelperSocketPath = lifecycleEnabled ? systemdPath(options.lifecycleHelperSocketPath ?? LIFECYCLE_HELPER_SOCKET, "lifecycle helper socket") : undefined;
14020
- const bootstrapSshCredentialPath = bootstrapEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
14043
+ const bootstrapSshCredentialPath = bootstrapIdentityEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
14021
14044
  const warpValues = [
14022
14045
  options.warpOrganization,
14023
14046
  options.warpClientIdCredentialPath,
@@ -14256,15 +14279,13 @@ function planProvision(options) {
14256
14279
  if (Boolean(options.pullMigrations) !== Boolean(options.lifecycleProfilePath)) {
14257
14280
  throw new Error("migration pull and lifecycle profile must be supplied together");
14258
14281
  }
14259
- const bootstrapEnabled = Boolean(options.pullBootstrap && options.bootstrapSshCredentialPath && options.bootstrapSshPublicKeyPath && options.bootstrapTargetTelemetryEndpoint);
14260
- if ([
14261
- options.pullBootstrap,
14262
- options.bootstrapSshCredentialPath,
14263
- options.bootstrapSshPublicKeyPath,
14264
- options.bootstrapSshSourcePath,
14265
- options.bootstrapTargetTelemetryEndpoint
14266
- ].some(Boolean) && !bootstrapEnabled) {
14267
- throw new Error("bootstrap pull, SSH credential, public key and target telemetry endpoint must be supplied together");
14282
+ const bootstrapIdentityEnabled = Boolean(options.bootstrapSshCredentialPath && options.bootstrapSshPublicKeyPath);
14283
+ if (Boolean(options.bootstrapSshCredentialPath) !== Boolean(options.bootstrapSshPublicKeyPath) || options.bootstrapSshSourcePath && !bootstrapIdentityEnabled) {
14284
+ throw new Error("bootstrap SSH credential and public key paths must be supplied together");
14285
+ }
14286
+ const bootstrapEnabled = Boolean(options.pullBootstrap);
14287
+ if (bootstrapEnabled && (!bootstrapIdentityEnabled || !options.bootstrapTargetTelemetryEndpoint) || !bootstrapEnabled && options.bootstrapTargetTelemetryEndpoint) {
14288
+ throw new Error("bootstrap pull, SSH identity and target telemetry endpoint must be supplied together");
14268
14289
  }
14269
14290
  const warpValues = [
14270
14291
  options.warpOrganization,
@@ -14274,13 +14295,13 @@ function planProvision(options) {
14274
14295
  const warpEnabled = warpValues.every(Boolean);
14275
14296
  if (warpValues.some(Boolean) && !warpEnabled)
14276
14297
  throw new Error("WARP configuration must be supplied together");
14277
- const enrolmentEnabled = Boolean(options.enrolTokenCredentialPath && options.enrolStatePath);
14278
- if (Boolean(options.enrolTokenCredentialPath) !== Boolean(options.enrolStatePath) || options.enrolTokenSourcePath && !enrolmentEnabled) {
14279
- throw new Error("direct enrolment credential and state paths must be supplied together");
14298
+ const enrolmentEnabled = Boolean(options.enrolTokenCredentialPath);
14299
+ if (options.enrolTokenCredentialPath && !options.enrolStatePath || options.enrolTokenSourcePath && (!options.enrolTokenCredentialPath || !options.enrolStatePath)) {
14300
+ throw new Error("direct enrolment credential requires its durable state path");
14280
14301
  }
14281
14302
  const enrolTokenSourcePath = options.enrolTokenSourcePath ? systemdPath(options.enrolTokenSourcePath, "enrolment source") : undefined;
14282
14303
  const enrolTokenCredentialPath = enrolmentEnabled ? systemdPath(options.enrolTokenCredentialPath, "enrolment credential") : undefined;
14283
- const enrolStatePath = enrolmentEnabled ? systemdPath(options.enrolStatePath, "enrolment state") : undefined;
14304
+ const enrolStatePath = options.enrolStatePath ? systemdPath(options.enrolStatePath, "enrolment state") : undefined;
14284
14305
  const enrolStateDir = enrolStatePath?.replace(/\/[^/]+$/, "");
14285
14306
  const sourceBinPath = options.sourceBinPath ? systemdPath(options.sourceBinPath, "agent source binary") : undefined;
14286
14307
  const binPath = options.binPath ? systemdPath(options.binPath, "agent binary") : undefined;
@@ -14292,8 +14313,8 @@ function planProvision(options) {
14292
14313
  const gitPublicKeyDir = gitPublicKeyPath?.replace(/\/[^/]+$/, "");
14293
14314
  const lifecycleProfilePath = lifecycleEnabled ? systemdPath(options.lifecycleProfilePath, "lifecycle profile") : undefined;
14294
14315
  const lifecycleHelperSocketPath = lifecycleEnabled ? systemdPath(options.lifecycleHelperSocketPath ?? LIFECYCLE_HELPER_SOCKET, "lifecycle helper socket") : undefined;
14295
- const bootstrapSshCredentialPath = bootstrapEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
14296
- const bootstrapSshPublicKeyPath = bootstrapEnabled ? systemdPath(options.bootstrapSshPublicKeyPath, "bootstrap SSH public key") : undefined;
14316
+ const bootstrapSshCredentialPath = bootstrapIdentityEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
14317
+ const bootstrapSshPublicKeyPath = bootstrapIdentityEnabled ? systemdPath(options.bootstrapSshPublicKeyPath, "bootstrap SSH public key") : undefined;
14297
14318
  const bootstrapSshSourcePath = options.bootstrapSshSourcePath ? systemdPath(options.bootstrapSshSourcePath, "bootstrap SSH private-key source") : undefined;
14298
14319
  const bootstrapSshPublicKeyDir = bootstrapSshPublicKeyPath?.replace(/\/[^/]+$/, "");
14299
14320
  const warpClientIdCredentialPath = warpEnabled ? systemdPath(options.warpClientIdCredentialPath, "WARP client-id credential") : undefined;
@@ -14393,7 +14414,7 @@ function planProvision(options) {
14393
14414
  step2("Git deploy identity directory", { kind: "directories", directories: [{ path: gitPublicKeyDir, mode: 493, owner: "root", group: "root" }] }),
14394
14415
  step2("unique encrypted Git deploy identity", { kind: "ensure-git-identity", credential: gitCredentialPath, publicKey: gitPublicKeyPath })
14395
14416
  ] : [],
14396
- ...bootstrapEnabled ? [
14417
+ ...bootstrapIdentityEnabled ? [
14397
14418
  step2("bootstrap SSH public identity directory", { kind: "directories", directories: [
14398
14419
  { path: bootstrapSshPublicKeyDir, mode: 493, owner: "root", group: "root" }
14399
14420
  ] }),
@@ -14429,6 +14450,10 @@ function planProvision(options) {
14429
14450
  { argv: ["/usr/bin/rm", "-f", "/etc/systemd/system/forgezero-deploy-runner.socket"] },
14430
14451
  { argv: ["/usr/bin/systemctl", "daemon-reload"] }
14431
14452
  ] })] : [],
14453
+ ...enrolStatePath && !enrolmentEnabled ? [step2("retire consumed enrolment unit", { kind: "commands", commands: [
14454
+ { argv: ["/usr/bin/systemctl", "disable", "--now", "forgezero-agent-enrol.service"], acceptedExitCodes: [0, 1, 5] },
14455
+ { argv: ["/usr/bin/rm", "-f", ENROLMENT_UNIT_PATH] }
14456
+ ] })] : [],
14432
14457
  step2("enable and converge services", { kind: "commands", commands: [
14433
14458
  { argv: ["/usr/bin/systemctl", "enable", ...enabledUnits] },
14434
14459
  { argv: ["/usr/bin/systemctl", "reset-failed", "forgezero-agent.service"], acceptedExitCodes: [0, 1] },
@@ -14945,6 +14970,65 @@ async function bootstrapStatus(host = localBootstrapHost()) {
14945
14970
  problems.push("durable Agent enrolment state is missing");
14946
14971
  return { initialized: problems.length === 0, kind: state.kind, profile: state.profile, services, problems };
14947
14972
  }
14973
+ function planBootstrapAgentInstall(config, phase, context) {
14974
+ const { capabilities, hasBinding, hasEnrolCredential, initialBundle } = context;
14975
+ if (phase === "bootstrap") {
14976
+ if (config.kind !== "platform" || hasBinding || !initialBundle) {
14977
+ throw new Error("release-one Agent install requires an unbound platform host and verified bootstrap bundle");
14978
+ }
14979
+ } else if (phase === "enrol") {
14980
+ if (hasBinding || !hasEnrolCredential) {
14981
+ throw new Error("Agent enrolment requires one unbound host and its sealed one-use credential");
14982
+ }
14983
+ } else if (!hasBinding) {
14984
+ throw new Error("bound Agent convergence requires durable enrolment state");
14985
+ }
14986
+ const bound = phase === "bound";
14987
+ const enrolling = phase === "enrol";
14988
+ const authenticated = enrolling || bound;
14989
+ const bootstrapRunner = platformBootstrapRunner(config) || config.kind === "enrolled-compute" && Boolean(config.bootstrapRunner);
14990
+ const options = {
14991
+ capabilities,
14992
+ socketPath: DEFAULT_SOCKET,
14993
+ seedPath: "/var/lib/forgezero/node.seed",
14994
+ controlSocketPath: CONTROL_SOCKET,
14995
+ repository: phase === "bootstrap" ? initialBundle.path : bound && config.kind === "enrolled-compute" ? config.repository : undefined,
14996
+ branch: phase === "bootstrap" ? initialBundle.manifest.branch : bound && config.kind === "enrolled-compute" ? config.branch : undefined,
14997
+ bootstrapBundlePath: phase === "bootstrap" ? initialBundle.path : undefined,
14998
+ bootstrapBundleManifestPath: phase === "bootstrap" ? initialBundle.manifestPath : undefined,
14999
+ profile: config.profile,
15000
+ deployRoot: config.deployRoot ?? "/opt/forgezero",
15001
+ deploymentCredentials: config.deploymentCredentials,
15002
+ publicApiUrl: config.apiUrl,
15003
+ gitCredentialPath: authenticated && config.kind === "enrolled-compute" && config.gitDeployKey ? "/etc/forgezero/creds/git-deploy-key.cred" : undefined,
15004
+ gitPublicKeyPath: authenticated && config.kind === "enrolled-compute" && config.gitDeployKey ? "/etc/forgezero/git/deploy.pub" : undefined,
15005
+ generateGitIdentity: authenticated && config.kind === "enrolled-compute" && config.gitDeployKey === true,
15006
+ pullDeployments: bound,
15007
+ pullMigrations: bound && config.kind === "platform",
15008
+ pullBootstrap: bound && bootstrapRunner,
15009
+ bootstrapSshCredentialPath: authenticated && bootstrapRunner ? BOOTSTRAP_SSH_CREDENTIAL : undefined,
15010
+ bootstrapSshSourcePath: enrolling && config.kind === "enrolled-compute" ? config.bootstrapRunner?.sshPrivateKeyFile : undefined,
15011
+ bootstrapSshPublicKeyPath: authenticated && bootstrapRunner ? BOOTSTRAP_SSH_PUBLIC_KEY : undefined,
15012
+ bootstrapTargetTelemetryEndpoint: bound && bootstrapRunner ? platformBootstrapRunner(config) ? config.telemetryEndpoint : config.kind === "enrolled-compute" ? config.bootstrapRunner?.targetTelemetryEndpoint : undefined : undefined,
15013
+ lifecycleProfilePath: bound && config.kind === "platform" ? LIFECYCLE_PROFILE : undefined,
15014
+ enforceEgress: true,
15015
+ nodeHostname: config.nodeHostname,
15016
+ telemetryEndpoint: config.telemetryEndpoint,
15017
+ binPath: "/usr/local/lib/forgezero/agent/fz-agent",
15018
+ sourceBinPath: PACKAGED_AGENT_BIN,
15019
+ ...enrolling ? {
15020
+ enrolTokenCredentialPath: ENROL_CREDENTIAL,
15021
+ enrolStatePath: "/var/lib/forgezero/enrolment.json"
15022
+ } : bound ? { enrolStatePath: "/var/lib/forgezero/enrolment.json" } : {},
15023
+ ...authenticated ? {
15024
+ apiUrl: config.apiUrl,
15025
+ project: config.kind === "enrolled-compute" ? config.realm : "platform",
15026
+ environment: config.kind === "enrolled-compute" ? undefined : config.environment,
15027
+ nodeLabel: config.kind === "enrolled-compute" ? config.nodeHostname : config.computeReference
15028
+ } : {}
15029
+ };
15030
+ return planInstall(options);
15031
+ }
14948
15032
  function localBootstrapHost() {
14949
15033
  const execute3 = async (argv2, options = {}) => {
14950
15034
  const child = Bun.spawn([...argv2], { stdin: options.stdin === undefined ? "ignore" : "pipe", stdout: "pipe", stderr: "pipe" });
@@ -14992,10 +15076,10 @@ function localBootstrapHost() {
14992
15076
  throw new Error(`Agent software requirements failed: ${result.output.trim()}`);
14993
15077
  return result;
14994
15078
  },
14995
- async installAgent(config) {
15079
+ async installAgent(config, phase) {
14996
15080
  const capabilities = await readCapabilities(localRunner);
14997
- const deployRoot = config.deployRoot ?? "/opt/forgezero";
14998
15081
  const hasBinding = existsSync20("/var/lib/forgezero/enrolment.json");
15082
+ const hasEnrolCredential = existsSync20(ENROL_CREDENTIAL);
14999
15083
  const initialBundle = config.kind === "platform" && !existsSync20(BOOTSTRAP_RELEASE_EVIDENCE) ? {
15000
15084
  path: config.bootstrapBundle.bundleFile,
15001
15085
  manifestPath: config.bootstrapBundle.manifestFile,
@@ -15016,45 +15100,11 @@ function localBootstrapHost() {
15016
15100
  writeFileSync15(LIFECYCLE_PROFILE, `${JSON.stringify(lifecycle, null, 2)}
15017
15101
  `, { mode: 256 });
15018
15102
  }
15019
- const plan = planInstall({
15103
+ const plan = planBootstrapAgentInstall(config, phase, {
15020
15104
  capabilities,
15021
- socketPath: DEFAULT_SOCKET,
15022
- seedPath: "/var/lib/forgezero/node.seed",
15023
- controlSocketPath: "/run/forgezero/control.sock",
15024
- repository: initialBundle?.path ?? (config.kind === "enrolled-compute" ? config.repository : undefined),
15025
- branch: initialBundle?.manifest.branch ?? (config.kind === "enrolled-compute" ? config.branch : undefined),
15026
- bootstrapBundlePath: initialBundle?.path,
15027
- bootstrapBundleManifestPath: initialBundle?.manifestPath,
15028
- profile: config.kind === "platform" ? config.profile : config.profile,
15029
- deployRoot,
15030
- deploymentCredentials: config.deploymentCredentials,
15031
- publicApiUrl: config.apiUrl,
15032
- gitCredentialPath: config.kind === "enrolled-compute" && config.gitDeployKey ? "/etc/forgezero/creds/git-deploy-key.cred" : undefined,
15033
- gitPublicKeyPath: config.kind === "enrolled-compute" && config.gitDeployKey ? "/etc/forgezero/git/deploy.pub" : undefined,
15034
- generateGitIdentity: config.kind === "enrolled-compute" && config.gitDeployKey === true,
15035
- pullDeployments: hasBinding,
15036
- pullMigrations: config.kind === "platform" && hasBinding,
15037
- pullBootstrap: platformBootstrapRunner(config) || config.kind === "enrolled-compute" && Boolean(config.bootstrapRunner),
15038
- bootstrapSshCredentialPath: platformBootstrapRunner(config) || config.kind === "enrolled-compute" && config.bootstrapRunner ? BOOTSTRAP_SSH_CREDENTIAL : undefined,
15039
- bootstrapSshSourcePath: config.kind === "enrolled-compute" ? config.bootstrapRunner?.sshPrivateKeyFile : undefined,
15040
- bootstrapSshPublicKeyPath: platformBootstrapRunner(config) || config.kind === "enrolled-compute" && config.bootstrapRunner ? BOOTSTRAP_SSH_PUBLIC_KEY : undefined,
15041
- bootstrapTargetTelemetryEndpoint: platformBootstrapRunner(config) ? config.telemetryEndpoint : config.kind === "enrolled-compute" ? config.bootstrapRunner?.targetTelemetryEndpoint : undefined,
15042
- lifecycleProfilePath: config.kind === "platform" ? LIFECYCLE_PROFILE : undefined,
15043
- enforceEgress: true,
15044
- nodeHostname: config.nodeHostname,
15045
- telemetryEndpoint: config.telemetryEndpoint,
15046
- binPath: "/usr/local/lib/forgezero/agent/fz-agent",
15047
- sourceBinPath: PACKAGED_AGENT_BIN,
15048
- ...existsSync20(ENROL_CREDENTIAL) || hasBinding ? {
15049
- enrolTokenCredentialPath: existsSync20(ENROL_CREDENTIAL) ? ENROL_CREDENTIAL : undefined,
15050
- enrolStatePath: "/var/lib/forgezero/enrolment.json"
15051
- } : {},
15052
- ...hasBinding ? {
15053
- apiUrl: config.apiUrl,
15054
- project: config.kind === "enrolled-compute" ? config.realm : "platform",
15055
- environment: config.kind === "enrolled-compute" ? undefined : config.environment,
15056
- nodeLabel: config.kind === "enrolled-compute" ? config.nodeHostname : config.computeReference
15057
- } : {}
15105
+ hasBinding,
15106
+ hasEnrolCredential,
15107
+ initialBundle
15058
15108
  });
15059
15109
  for (const unit3 of [{ path: plan.unitPath, unit: plan.unit }, ...plan.auxiliaryUnits]) {
15060
15110
  mkdirSync15(dirname10(unit3.path), { recursive: true, mode: 493 });