@forgezero/agent 0.1.12 → 0.1.13

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
@@ -2045,15 +2045,48 @@ async function provisionMetalGuest(profile, claim, exec) {
2045
2045
  save();
2046
2046
  return { guestAddress: address };
2047
2047
  }
2048
+ function legacyPlatformManifest(profile, claim, name) {
2049
+ if (claim.bootstrap?.kind !== "platform-genesis" || claim.computeKey !== `platform:${name}` || claim.spec.reference !== `platform:${name}` || claim.spec.guestAddress === undefined)
2050
+ return;
2051
+ const legacyDir = profile.legacyStateDir ?? "/etc/forgezero/guests";
2052
+ const legacyPath = join2(legacyDir, `${name}.conf`);
2053
+ if (!existsSync5(legacyPath))
2054
+ return;
2055
+ const values = new Map;
2056
+ for (const line of readFileSync3(legacyPath, "utf8").split(/\r?\n/)) {
2057
+ if (!line || line.startsWith("#"))
2058
+ continue;
2059
+ const match = /^([A-Z][A-Z0-9_]*)=([^\s'"`$;|&<>]+)$/.exec(line);
2060
+ if (!match)
2061
+ throw new MetalProvisionError("legacy guest inventory contains unsafe syntax");
2062
+ values.set(match[1], match[2]);
2063
+ }
2064
+ const disk = `/dev/${profile.volumeGroup}/${name}`;
2065
+ if (values.get("NAME") !== name || values.get("DISK") !== disk || values.get("IP") !== claim.spec.guestAddress)
2066
+ throw new MetalProvisionError("legacy guest inventory does not match the platform claim");
2067
+ return {
2068
+ computeKey: claim.computeKey,
2069
+ reference: claim.spec.reference,
2070
+ name,
2071
+ address: claim.spec.guestAddress,
2072
+ mac: values.get("MAC") ?? "",
2073
+ cpuPoolKey: claim.spec.cpuPoolKey ?? "",
2074
+ allowedCpus: values.get("CPUSET") ?? "",
2075
+ allowedMemoryNodes: values.get("NUMA"),
2076
+ phase: "running",
2077
+ disk,
2078
+ unit: `forgezero-guest@${name}.service`
2079
+ };
2080
+ }
2048
2081
  async function removeMetalGuest(profile, claim, exec) {
2049
2082
  validateMetalProfile(profile);
2050
2083
  if (claim.action !== "delete")
2051
2084
  throw new MetalProvisionError("create claim cannot remove a guest");
2052
2085
  const name = claim.spec.guestName ?? guestNameFor(claim.computeKey);
2053
2086
  const manifestPath = join2(profile.stateDir, `${name}.json`);
2054
- if (!existsSync5(manifestPath))
2087
+ const manifest = existsSync5(manifestPath) ? JSON.parse(readFileSync3(manifestPath, "utf8")) : legacyPlatformManifest(profile, claim, name);
2088
+ if (!manifest)
2055
2089
  return {};
2056
- const manifest = JSON.parse(readFileSync3(manifestPath, "utf8"));
2057
2090
  const currentIdentity = manifest.computeKey === claim.computeKey && manifest.reference === claim.spec.reference && manifest.name === name;
2058
2091
  const legacyPlatformIdentity = claim.bootstrap?.kind === "platform-genesis" && claim.computeKey === `platform:${name}` && claim.spec.reference === `platform:${name}` && manifest.name === name && manifest.disk === `/dev/${profile.volumeGroup}/${name}` && manifest.address === claim.spec.guestAddress && (manifest.unit === `fz-guest@${name}.service` || manifest.unit === `forgezero-guest@${name}.service`);
2059
2092
  if (!currentIdentity && !legacyPlatformIdentity) {
@@ -2082,7 +2115,7 @@ async function removeMetalGuest(profile, claim, exec) {
2082
2115
  if (existsSync5(path))
2083
2116
  unlinkSync4(path);
2084
2117
  if (legacyPlatformIdentity) {
2085
- const legacyConfig = `/etc/forgezero/guests/${name}.conf`;
2118
+ const legacyConfig = join2(profile.legacyStateDir ?? "/etc/forgezero/guests", `${name}.conf`);
2086
2119
  const legacyDropIn = join2(profile.unitDir, `${service}.d`);
2087
2120
  if (existsSync5(legacyConfig))
2088
2121
  unlinkSync4(legacyConfig);
@@ -2679,7 +2712,7 @@ async function applyMetalIsolation(profile, exec = defaultExec) {
2679
2712
  }
2680
2713
 
2681
2714
  // src/version.ts
2682
- var VERSION = "0.1.12";
2715
+ var VERSION = "0.1.13";
2683
2716
 
2684
2717
  // src/index.ts
2685
2718
  function loadOrCreateSeed(path) {
package/dist/fz.js CHANGED
@@ -781,7 +781,7 @@ async function resolveIdentity(selector, socketPath) {
781
781
  }
782
782
 
783
783
  // src/version.ts
784
- var VERSION = "0.1.12";
784
+ var VERSION = "0.1.13";
785
785
 
786
786
  // src/cli/index.ts
787
787
  var DEFAULT_MODE = THRESHOLD_MODES[0].id;
@@ -1041,15 +1041,48 @@ async function provisionMetalGuest(profile, claim, exec) {
1041
1041
  save();
1042
1042
  return { guestAddress: address };
1043
1043
  }
1044
+ function legacyPlatformManifest(profile, claim, name) {
1045
+ if (claim.bootstrap?.kind !== "platform-genesis" || claim.computeKey !== `platform:${name}` || claim.spec.reference !== `platform:${name}` || claim.spec.guestAddress === undefined)
1046
+ return;
1047
+ const legacyDir = profile.legacyStateDir ?? "/etc/forgezero/guests";
1048
+ const legacyPath = join(legacyDir, `${name}.conf`);
1049
+ if (!existsSync(legacyPath))
1050
+ return;
1051
+ const values = new Map;
1052
+ for (const line of readFileSync(legacyPath, "utf8").split(/\r?\n/)) {
1053
+ if (!line || line.startsWith("#"))
1054
+ continue;
1055
+ const match = /^([A-Z][A-Z0-9_]*)=([^\s'"`$;|&<>]+)$/.exec(line);
1056
+ if (!match)
1057
+ throw new MetalProvisionError("legacy guest inventory contains unsafe syntax");
1058
+ values.set(match[1], match[2]);
1059
+ }
1060
+ const disk = `/dev/${profile.volumeGroup}/${name}`;
1061
+ if (values.get("NAME") !== name || values.get("DISK") !== disk || values.get("IP") !== claim.spec.guestAddress)
1062
+ throw new MetalProvisionError("legacy guest inventory does not match the platform claim");
1063
+ return {
1064
+ computeKey: claim.computeKey,
1065
+ reference: claim.spec.reference,
1066
+ name,
1067
+ address: claim.spec.guestAddress,
1068
+ mac: values.get("MAC") ?? "",
1069
+ cpuPoolKey: claim.spec.cpuPoolKey ?? "",
1070
+ allowedCpus: values.get("CPUSET") ?? "",
1071
+ allowedMemoryNodes: values.get("NUMA"),
1072
+ phase: "running",
1073
+ disk,
1074
+ unit: `forgezero-guest@${name}.service`
1075
+ };
1076
+ }
1044
1077
  async function removeMetalGuest(profile, claim, exec) {
1045
1078
  validateMetalProfile(profile);
1046
1079
  if (claim.action !== "delete")
1047
1080
  throw new MetalProvisionError("create claim cannot remove a guest");
1048
1081
  const name = claim.spec.guestName ?? guestNameFor(claim.computeKey);
1049
1082
  const manifestPath = join(profile.stateDir, `${name}.json`);
1050
- if (!existsSync(manifestPath))
1083
+ const manifest = existsSync(manifestPath) ? JSON.parse(readFileSync(manifestPath, "utf8")) : legacyPlatformManifest(profile, claim, name);
1084
+ if (!manifest)
1051
1085
  return {};
1052
- const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
1053
1086
  const currentIdentity = manifest.computeKey === claim.computeKey && manifest.reference === claim.spec.reference && manifest.name === name;
1054
1087
  const legacyPlatformIdentity = claim.bootstrap?.kind === "platform-genesis" && claim.computeKey === `platform:${name}` && claim.spec.reference === `platform:${name}` && manifest.name === name && manifest.disk === `/dev/${profile.volumeGroup}/${name}` && manifest.address === claim.spec.guestAddress && (manifest.unit === `fz-guest@${name}.service` || manifest.unit === `forgezero-guest@${name}.service`);
1055
1088
  if (!currentIdentity && !legacyPlatformIdentity) {
@@ -1078,7 +1111,7 @@ async function removeMetalGuest(profile, claim, exec) {
1078
1111
  if (existsSync(path))
1079
1112
  unlinkSync(path);
1080
1113
  if (legacyPlatformIdentity) {
1081
- const legacyConfig = `/etc/forgezero/guests/${name}.conf`;
1114
+ const legacyConfig = join(profile.legacyStateDir ?? "/etc/forgezero/guests", `${name}.conf`);
1082
1115
  const legacyDropIn = join(profile.unitDir, `${service}.d`);
1083
1116
  if (existsSync(legacyConfig))
1084
1117
  unlinkSync(legacyConfig);
@@ -24,6 +24,8 @@ export interface MetalProvisionProfile {
24
24
  stateDir: string;
25
25
  seedDir: string;
26
26
  unitDir: string;
27
+ /** One-time compatibility source for pre-Agent platform guests. */
28
+ legacyStateDir?: string;
27
29
  apiUrl: string;
28
30
  images: Record<string, MetalImage>;
29
31
  /** Explicit host-observed pools. No pool means no dynamic guest authority. */
@@ -81,5 +83,4 @@ export declare function cloudInit(profile: MetalProvisionProfile, claim: CreateR
81
83
  };
82
84
  /** The only root operation the metal agent may request. */
83
85
  export declare function provisionMetalGuest(profile: MetalProvisionProfile, claim: CreateRemoteProvisionClaim, exec: MetalExec): Promise<ProvisionResult>;
84
- /** Idempotently remove every host-owned artifact before capacity is returned. */
85
86
  export declare function removeMetalGuest(profile: MetalProvisionProfile, claim: RemoteProvisionClaim, exec: MetalExec): Promise<ProvisionResult>;
@@ -1041,15 +1041,48 @@ async function provisionMetalGuest(profile, claim, exec) {
1041
1041
  save();
1042
1042
  return { guestAddress: address };
1043
1043
  }
1044
+ function legacyPlatformManifest(profile, claim, name) {
1045
+ if (claim.bootstrap?.kind !== "platform-genesis" || claim.computeKey !== `platform:${name}` || claim.spec.reference !== `platform:${name}` || claim.spec.guestAddress === undefined)
1046
+ return;
1047
+ const legacyDir = profile.legacyStateDir ?? "/etc/forgezero/guests";
1048
+ const legacyPath = join(legacyDir, `${name}.conf`);
1049
+ if (!existsSync(legacyPath))
1050
+ return;
1051
+ const values = new Map;
1052
+ for (const line of readFileSync(legacyPath, "utf8").split(/\r?\n/)) {
1053
+ if (!line || line.startsWith("#"))
1054
+ continue;
1055
+ const match = /^([A-Z][A-Z0-9_]*)=([^\s'"`$;|&<>]+)$/.exec(line);
1056
+ if (!match)
1057
+ throw new MetalProvisionError("legacy guest inventory contains unsafe syntax");
1058
+ values.set(match[1], match[2]);
1059
+ }
1060
+ const disk = `/dev/${profile.volumeGroup}/${name}`;
1061
+ if (values.get("NAME") !== name || values.get("DISK") !== disk || values.get("IP") !== claim.spec.guestAddress)
1062
+ throw new MetalProvisionError("legacy guest inventory does not match the platform claim");
1063
+ return {
1064
+ computeKey: claim.computeKey,
1065
+ reference: claim.spec.reference,
1066
+ name,
1067
+ address: claim.spec.guestAddress,
1068
+ mac: values.get("MAC") ?? "",
1069
+ cpuPoolKey: claim.spec.cpuPoolKey ?? "",
1070
+ allowedCpus: values.get("CPUSET") ?? "",
1071
+ allowedMemoryNodes: values.get("NUMA"),
1072
+ phase: "running",
1073
+ disk,
1074
+ unit: `forgezero-guest@${name}.service`
1075
+ };
1076
+ }
1044
1077
  async function removeMetalGuest(profile, claim, exec) {
1045
1078
  validateMetalProfile(profile);
1046
1079
  if (claim.action !== "delete")
1047
1080
  throw new MetalProvisionError("create claim cannot remove a guest");
1048
1081
  const name = claim.spec.guestName ?? guestNameFor(claim.computeKey);
1049
1082
  const manifestPath = join(profile.stateDir, `${name}.json`);
1050
- if (!existsSync(manifestPath))
1083
+ const manifest = existsSync(manifestPath) ? JSON.parse(readFileSync(manifestPath, "utf8")) : legacyPlatformManifest(profile, claim, name);
1084
+ if (!manifest)
1051
1085
  return {};
1052
- const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
1053
1086
  const currentIdentity = manifest.computeKey === claim.computeKey && manifest.reference === claim.spec.reference && manifest.name === name;
1054
1087
  const legacyPlatformIdentity = claim.bootstrap?.kind === "platform-genesis" && claim.computeKey === `platform:${name}` && claim.spec.reference === `platform:${name}` && manifest.name === name && manifest.disk === `/dev/${profile.volumeGroup}/${name}` && manifest.address === claim.spec.guestAddress && (manifest.unit === `fz-guest@${name}.service` || manifest.unit === `forgezero-guest@${name}.service`);
1055
1088
  if (!currentIdentity && !legacyPlatformIdentity) {
@@ -1078,7 +1111,7 @@ async function removeMetalGuest(profile, claim, exec) {
1078
1111
  if (existsSync(path))
1079
1112
  unlinkSync(path);
1080
1113
  if (legacyPlatformIdentity) {
1081
- const legacyConfig = `/etc/forgezero/guests/${name}.conf`;
1114
+ const legacyConfig = join(profile.legacyStateDir ?? "/etc/forgezero/guests", `${name}.conf`);
1082
1115
  const legacyDropIn = join(profile.unitDir, `${service}.d`);
1083
1116
  if (existsSync(legacyConfig))
1084
1117
  unlinkSync(legacyConfig);
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.12";
2
+ export declare const VERSION = "0.1.13";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "//": "Publishing happens from an operator's machine, not CI \u2014 CLAUDE.md records that the absence of CI is deliberate. npm's `provenance` attests a tarball was built by a recognised CI provider from a named commit, so it cannot be produced here: it was set, and the first publish failed with `Automatic provenance generation not supported for provider: null`. A setting that can never be satisfied is worse than none, because it reads as a guarantee nobody is getting. Restore it the day this publishes from CI, and not before.",
3
3
  "name": "@forgezero/agent",
4
- "version": "0.1.12",
4
+ "version": "0.1.13",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "check": "tsc --noEmit",