@forgezero/agent 0.1.91 → 0.1.94

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.
@@ -917,7 +917,7 @@ async function postSignedNode(options, path, body) {
917
917
  }
918
918
 
919
919
  // src/version.ts
920
- var VERSION3 = "0.1.91";
920
+ var VERSION3 = "0.1.94";
921
921
 
922
922
  // src/agent-heartbeat.ts
923
923
  function readAgentHostMetrics() {
package/dist/bootstrap.js CHANGED
@@ -1420,7 +1420,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
1420
1420
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
1421
1421
 
1422
1422
  // src/version.ts
1423
- var VERSION = "0.1.91";
1423
+ var VERSION = "0.1.94";
1424
1424
 
1425
1425
  // src/software.ts
1426
1426
  var PINNED_BUN_VERSION = "1.3.14";
@@ -3088,6 +3088,7 @@ var httpsOrigin = (name, raw) => {
3088
3088
  }
3089
3089
  return value.origin;
3090
3090
  };
3091
+ var agentTelemetryOrigin = (raw) => raw === "http://127.0.0.1:4318" ? raw : httpsOrigin("agentOtlpEndpoint", raw);
3091
3092
  function validatePlatformInitialInventory(value) {
3092
3093
  if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !["metalHostname", "region", "computes", "deployment"].includes(key)) || !/^[A-Za-z0-9][A-Za-z0-9.-]{1,252}$/.test(value.metalHostname) || !value.region || typeof value.region !== "object" || Array.isArray(value.region) || Object.keys(value.region).some((key) => !["key", "label", "country", "city", "confidentialCapable"].includes(key)) || !/^[a-z0-9][a-z0-9-]{0,62}$/.test(value.region.key) || !/^[A-Z]{2}$/.test(value.region.country) || value.region.confidentialCapable !== true || !Array.isArray(value.computes)) {
3093
3094
  throw new Error("initial platform inventory coordinates are invalid");
@@ -3193,7 +3194,7 @@ function validatePlatformSharedEnvironment(input) {
3193
3194
  if (input.otlpEndpoint !== "http://127.0.0.1:4318")
3194
3195
  throw new Error("OTLP must use the exact local collector endpoint.");
3195
3196
  validateCollectorUnit(input.otlpCollectorUnit);
3196
- httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint);
3197
+ agentTelemetryOrigin(input.agentOtlpEndpoint);
3197
3198
  for (const [name, value] of Object.entries({
3198
3199
  nodeHostname: input.nodeHostname,
3199
3200
  nodeRegion: input.nodeRegion,
@@ -3238,7 +3239,7 @@ function validatePlatformSharedEnvironment(input) {
3238
3239
  databaseReadPreferredCoordinators: readPreferred,
3239
3240
  appOrigin: httpsOrigin("appOrigin", input.appOrigin),
3240
3241
  apiOrigin: httpsOrigin("apiOrigin", input.apiOrigin),
3241
- agentOtlpEndpoint: httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint),
3242
+ agentOtlpEndpoint: agentTelemetryOrigin(input.agentOtlpEndpoint),
3242
3243
  ...initialInventory ? { initialInventory } : {}
3243
3244
  };
3244
3245
  }
@@ -3514,6 +3515,7 @@ function planLocalOtlpProof(endpoint2, collectorUnit) {
3514
3515
  var FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
3515
3516
  var FORGEZERO_OTEL_COLLECTOR_VERSION = "0.157.0";
3516
3517
  var FORGEZERO_OTEL_COLLECTOR_SHA256 = "2937cf24892af55b143c072fddece17862239cf78280620029276493eb81beae";
3518
+ var LOCAL_DEBUG_OTEL_EXPORT = "local-debug";
3517
3519
  var ARCHIVE = "/run/forgezero-otelcol.tar.gz";
3518
3520
  var EXTRACT = "/run/forgezero-otelcol-release";
3519
3521
  var BINARY = "/usr/local/lib/forgezero/otelcol/otelcol";
@@ -3539,7 +3541,9 @@ var destination = (value) => {
3539
3541
  return url.toString().replace(/\/$/, "");
3540
3542
  };
3541
3543
  function renderForgeZeroOtelCollector(exportEndpoint) {
3542
- const endpoint2 = destination(exportEndpoint);
3544
+ const exporter = exportEndpoint === LOCAL_DEBUG_OTEL_EXPORT ? { definition: ` debug:
3545
+ verbosity: basic`, name: "debug" } : { definition: ` otlp_http:
3546
+ endpoint: ${JSON.stringify(destination(exportEndpoint))}`, name: "otlp_http" };
3543
3547
  return {
3544
3548
  config: `receivers:
3545
3549
  otlp:
@@ -3547,19 +3551,18 @@ function renderForgeZeroOtelCollector(exportEndpoint) {
3547
3551
  http:
3548
3552
  endpoint: 127.0.0.1:4318
3549
3553
  exporters:
3550
- otlp_http:
3551
- endpoint: ${JSON.stringify(endpoint2)}
3554
+ ${exporter.definition}
3552
3555
  service:
3553
3556
  pipelines:
3554
3557
  traces:
3555
3558
  receivers: [otlp]
3556
- exporters: [otlp_http]
3559
+ exporters: [${exporter.name}]
3557
3560
  metrics:
3558
3561
  receivers: [otlp]
3559
- exporters: [otlp_http]
3562
+ exporters: [${exporter.name}]
3560
3563
  logs:
3561
3564
  receivers: [otlp]
3562
- exporters: [otlp_http]
3565
+ exporters: [${exporter.name}]
3563
3566
  `,
3564
3567
  unit: `[Unit]
3565
3568
  Description=ForgeZero independently supervised OpenTelemetry Collector
@@ -3639,7 +3642,8 @@ async function ensureForgeZeroOtelCollector(host, exportEndpoint) {
3639
3642
  host.write(CONFIG, rendered.config, 420);
3640
3643
  host.write(UNIT, rendered.unit, 420);
3641
3644
  await checked2(host, ["/usr/bin/systemctl", "daemon-reload"]);
3642
- await checked2(host, ["/usr/bin/systemctl", "enable", "--now", FORGEZERO_OTEL_COLLECTOR_UNIT]);
3645
+ await checked2(host, ["/usr/bin/systemctl", "enable", FORGEZERO_OTEL_COLLECTOR_UNIT]);
3646
+ await checked2(host, ["/usr/bin/systemctl", "restart", FORGEZERO_OTEL_COLLECTOR_UNIT]);
3643
3647
  let ready = false;
3644
3648
  for (let attempt = 0;attempt < 100; attempt += 1) {
3645
3649
  const probe = await host.exec([
@@ -3816,14 +3820,20 @@ function validateBootstrapConfig(value) {
3816
3820
  if (!/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?){2,}$/.test(value.nodeHostname)) {
3817
3821
  throw new Error("node hostname must be a lowercase public FQDN");
3818
3822
  }
3819
- let telemetry;
3820
- try {
3821
- telemetry = new URL(value.telemetryEndpoint);
3822
- } catch {
3823
- throw new Error("telemetry endpoint is malformed");
3824
- }
3825
- if (telemetry.protocol !== "https:" || telemetry.port && telemetry.port !== "443" || telemetry.username || telemetry.password || telemetry.search || telemetry.hash) {
3826
- throw new Error("telemetry endpoint must be public HTTPS on port 443 without credentials, query or fragment");
3823
+ if (value.telemetryEndpoint === LOCAL_DEBUG_OTEL_EXPORT) {
3824
+ if (value.kind !== "platform" || value.environment !== "development") {
3825
+ throw new Error("local debug telemetry is allowed only for the development platform profile");
3826
+ }
3827
+ } else {
3828
+ let telemetry;
3829
+ try {
3830
+ telemetry = new URL(value.telemetryEndpoint);
3831
+ } catch {
3832
+ throw new Error("telemetry endpoint is malformed");
3833
+ }
3834
+ if (telemetry.protocol !== "https:" || telemetry.port && telemetry.port !== "443" || telemetry.username || telemetry.password || telemetry.search || telemetry.hash) {
3835
+ throw new Error("telemetry endpoint must be public HTTPS on port 443 without credentials, query or fragment");
3836
+ }
3827
3837
  }
3828
3838
  const deploymentCredentials = value.deploymentCredentials ?? {};
3829
3839
  if (!deploymentCredentials || typeof deploymentCredentials !== "object" || Array.isArray(deploymentCredentials) || Object.keys(deploymentCredentials).length > 64) {
package/dist/fz-agent.js CHANGED
@@ -9496,7 +9496,7 @@ async function writeAndCloseProcessInput(input, value) {
9496
9496
  }
9497
9497
 
9498
9498
  // src/version.ts
9499
- var VERSION2 = "0.1.91";
9499
+ var VERSION2 = "0.1.94";
9500
9500
 
9501
9501
  // src/ssh-bootstrap.ts
9502
9502
  class SshBootstrapError extends Error {
package/dist/fz.js CHANGED
@@ -4810,7 +4810,8 @@ async function spawnWith(command, env, report = () => {}, options = {}) {
4810
4810
  }
4811
4811
 
4812
4812
  // src/cli/index.ts
4813
- import { existsSync as existsSync13, lstatSync as lstatSync10, mkdirSync as mkdirSync13, readFileSync as readFileSync16, writeFileSync as writeFileSync13 } from "fs";
4813
+ import { existsSync as existsSync13, lstatSync as lstatSync10, mkdirSync as mkdirSync13, readFileSync as readFileSync16, renameSync as renameSync10, rmSync as rmSync8, writeFileSync as writeFileSync13 } from "fs";
4814
+ import { randomBytes as randomBytes10 } from "crypto";
4814
4815
  import { basename as basename3, dirname as dirname14, isAbsolute as isAbsolute7, join as join13, resolve as resolve12 } from "path";
4815
4816
 
4816
4817
  // src/process-input.ts
@@ -4840,7 +4841,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
4840
4841
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
4841
4842
 
4842
4843
  // src/version.ts
4843
- var VERSION2 = "0.1.91";
4844
+ var VERSION2 = "0.1.94";
4844
4845
 
4845
4846
  // src/software.ts
4846
4847
  var PINNED_BUN_VERSION = "1.3.14";
@@ -12236,6 +12237,7 @@ var httpsOrigin = (name, raw) => {
12236
12237
  }
12237
12238
  return value.origin;
12238
12239
  };
12240
+ var agentTelemetryOrigin = (raw) => raw === "http://127.0.0.1:4318" ? raw : httpsOrigin("agentOtlpEndpoint", raw);
12239
12241
  function validatePlatformInitialInventory(value) {
12240
12242
  if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !["metalHostname", "region", "computes", "deployment"].includes(key)) || !/^[A-Za-z0-9][A-Za-z0-9.-]{1,252}$/.test(value.metalHostname) || !value.region || typeof value.region !== "object" || Array.isArray(value.region) || Object.keys(value.region).some((key) => !["key", "label", "country", "city", "confidentialCapable"].includes(key)) || !/^[a-z0-9][a-z0-9-]{0,62}$/.test(value.region.key) || !/^[A-Z]{2}$/.test(value.region.country) || value.region.confidentialCapable !== true || !Array.isArray(value.computes)) {
12241
12243
  throw new Error("initial platform inventory coordinates are invalid");
@@ -12341,7 +12343,7 @@ function validatePlatformSharedEnvironment(input) {
12341
12343
  if (input.otlpEndpoint !== "http://127.0.0.1:4318")
12342
12344
  throw new Error("OTLP must use the exact local collector endpoint.");
12343
12345
  validateCollectorUnit(input.otlpCollectorUnit);
12344
- httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint);
12346
+ agentTelemetryOrigin(input.agentOtlpEndpoint);
12345
12347
  for (const [name, value] of Object.entries({
12346
12348
  nodeHostname: input.nodeHostname,
12347
12349
  nodeRegion: input.nodeRegion,
@@ -12386,7 +12388,7 @@ function validatePlatformSharedEnvironment(input) {
12386
12388
  databaseReadPreferredCoordinators: readPreferred,
12387
12389
  appOrigin: httpsOrigin("appOrigin", input.appOrigin),
12388
12390
  apiOrigin: httpsOrigin("apiOrigin", input.apiOrigin),
12389
- agentOtlpEndpoint: httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint),
12391
+ agentOtlpEndpoint: agentTelemetryOrigin(input.agentOtlpEndpoint),
12390
12392
  ...initialInventory ? { initialInventory } : {}
12391
12393
  };
12392
12394
  }
@@ -13740,6 +13742,7 @@ async function finalizeCloudflareBootstrapAcceptance(request, fetcher = fetch) {
13740
13742
  var FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
13741
13743
  var FORGEZERO_OTEL_COLLECTOR_VERSION = "0.157.0";
13742
13744
  var FORGEZERO_OTEL_COLLECTOR_SHA256 = "2937cf24892af55b143c072fddece17862239cf78280620029276493eb81beae";
13745
+ var LOCAL_DEBUG_OTEL_EXPORT = "local-debug";
13743
13746
  var ARCHIVE = "/run/forgezero-otelcol.tar.gz";
13744
13747
  var EXTRACT = "/run/forgezero-otelcol-release";
13745
13748
  var BINARY = "/usr/local/lib/forgezero/otelcol/otelcol";
@@ -13765,7 +13768,9 @@ var destination = (value) => {
13765
13768
  return url.toString().replace(/\/$/, "");
13766
13769
  };
13767
13770
  function renderForgeZeroOtelCollector(exportEndpoint) {
13768
- const endpoint2 = destination(exportEndpoint);
13771
+ const exporter = exportEndpoint === LOCAL_DEBUG_OTEL_EXPORT ? { definition: ` debug:
13772
+ verbosity: basic`, name: "debug" } : { definition: ` otlp_http:
13773
+ endpoint: ${JSON.stringify(destination(exportEndpoint))}`, name: "otlp_http" };
13769
13774
  return {
13770
13775
  config: `receivers:
13771
13776
  otlp:
@@ -13773,19 +13778,18 @@ function renderForgeZeroOtelCollector(exportEndpoint) {
13773
13778
  http:
13774
13779
  endpoint: 127.0.0.1:4318
13775
13780
  exporters:
13776
- otlp_http:
13777
- endpoint: ${JSON.stringify(endpoint2)}
13781
+ ${exporter.definition}
13778
13782
  service:
13779
13783
  pipelines:
13780
13784
  traces:
13781
13785
  receivers: [otlp]
13782
- exporters: [otlp_http]
13786
+ exporters: [${exporter.name}]
13783
13787
  metrics:
13784
13788
  receivers: [otlp]
13785
- exporters: [otlp_http]
13789
+ exporters: [${exporter.name}]
13786
13790
  logs:
13787
13791
  receivers: [otlp]
13788
- exporters: [otlp_http]
13792
+ exporters: [${exporter.name}]
13789
13793
  `,
13790
13794
  unit: `[Unit]
13791
13795
  Description=ForgeZero independently supervised OpenTelemetry Collector
@@ -13865,7 +13869,8 @@ async function ensureForgeZeroOtelCollector(host, exportEndpoint) {
13865
13869
  host.write(CONFIG, rendered.config, 420);
13866
13870
  host.write(UNIT, rendered.unit, 420);
13867
13871
  await checked(host, ["/usr/bin/systemctl", "daemon-reload"]);
13868
- await checked(host, ["/usr/bin/systemctl", "enable", "--now", FORGEZERO_OTEL_COLLECTOR_UNIT]);
13872
+ await checked(host, ["/usr/bin/systemctl", "enable", FORGEZERO_OTEL_COLLECTOR_UNIT]);
13873
+ await checked(host, ["/usr/bin/systemctl", "restart", FORGEZERO_OTEL_COLLECTOR_UNIT]);
13869
13874
  let ready = false;
13870
13875
  for (let attempt = 0;attempt < 100; attempt += 1) {
13871
13876
  const probe = await host.exec([
@@ -14200,14 +14205,20 @@ function validateBootstrapConfig(value) {
14200
14205
  if (!/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?){2,}$/.test(value.nodeHostname)) {
14201
14206
  throw new Error("node hostname must be a lowercase public FQDN");
14202
14207
  }
14203
- let telemetry;
14204
- try {
14205
- telemetry = new URL(value.telemetryEndpoint);
14206
- } catch {
14207
- throw new Error("telemetry endpoint is malformed");
14208
- }
14209
- if (telemetry.protocol !== "https:" || telemetry.port && telemetry.port !== "443" || telemetry.username || telemetry.password || telemetry.search || telemetry.hash) {
14210
- throw new Error("telemetry endpoint must be public HTTPS on port 443 without credentials, query or fragment");
14208
+ if (value.telemetryEndpoint === LOCAL_DEBUG_OTEL_EXPORT) {
14209
+ if (value.kind !== "platform" || value.environment !== "development") {
14210
+ throw new Error("local debug telemetry is allowed only for the development platform profile");
14211
+ }
14212
+ } else {
14213
+ let telemetry;
14214
+ try {
14215
+ telemetry = new URL(value.telemetryEndpoint);
14216
+ } catch {
14217
+ throw new Error("telemetry endpoint is malformed");
14218
+ }
14219
+ if (telemetry.protocol !== "https:" || telemetry.port && telemetry.port !== "443" || telemetry.username || telemetry.password || telemetry.search || telemetry.hash) {
14220
+ throw new Error("telemetry endpoint must be public HTTPS on port 443 without credentials, query or fragment");
14221
+ }
14211
14222
  }
14212
14223
  const deploymentCredentials = value.deploymentCredentials ?? {};
14213
14224
  if (!deploymentCredentials || typeof deploymentCredentials !== "object" || Array.isArray(deploymentCredentials) || Object.keys(deploymentCredentials).length > 64) {
@@ -17918,7 +17929,8 @@ var PROFILES = {
17918
17929
  workerScriptName: "forgezero-api-edge-development",
17919
17930
  region: { key: "in-south", label: "India South", country: "IN" },
17920
17931
  seedSyncEpoch: "development-23c19168773d69ea",
17921
- agentOtlpEndpoint: "https://otel.forgezero.net",
17932
+ collectorExport: LOCAL_DEBUG_OTEL_EXPORT,
17933
+ agentOtlpEndpoint: "http://127.0.0.1:4318",
17922
17934
  privateCidrs: ["10.42.0.0/24"]
17923
17935
  },
17924
17936
  production: {
@@ -17931,6 +17943,7 @@ var PROFILES = {
17931
17943
  workerScriptName: "forgezero-api-edge-production",
17932
17944
  region: { key: "in-south", label: "India South", country: "IN" },
17933
17945
  seedSyncEpoch: "production-genesis-v1",
17946
+ collectorExport: "https://otel.forgezero.net",
17934
17947
  agentOtlpEndpoint: "https://otel.forgezero.net",
17935
17948
  privateCidrs: ["10.42.0.0/24"]
17936
17949
  }
@@ -17968,7 +17981,7 @@ function forgeZeroLaunchTemplate(profile, guests, bundle, owner) {
17968
17981
  manifestFile: bundle.manifestPath,
17969
17982
  branch: profile.branch
17970
17983
  },
17971
- telemetryEndpoint: profile.agentOtlpEndpoint,
17984
+ telemetryEndpoint: profile.collectorExport,
17972
17985
  database: {
17973
17986
  role: "master",
17974
17987
  agency: "member",
@@ -19429,6 +19442,17 @@ function writeMetalOperatorFiles(directory) {
19429
19442
  });
19430
19443
  return { metalConfig, remoteRequest };
19431
19444
  }
19445
+ function writeDerivedLaunchFile(path, write) {
19446
+ const temporary = `${path}.next.${process.pid}.${randomBytes10(6).toString("hex")}`;
19447
+ try {
19448
+ write(temporary);
19449
+ renameSync10(temporary, path);
19450
+ return path;
19451
+ } catch (cause) {
19452
+ rmSync8(temporary, { force: true });
19453
+ throw cause;
19454
+ }
19455
+ }
19432
19456
  function writePlatformGenesisFleet(directory, preset) {
19433
19457
  const output = genesisOutputDirectory(directory);
19434
19458
  const metalRequestFile = preset?.metalRequestFile ?? bootstrapAnswer("Owner-only metal remote request containing the reviewed genesis guests");
@@ -19451,7 +19475,8 @@ function writePlatformGenesisFleet(directory, preset) {
19451
19475
  cloudflareHandoffFile: cloudflareHostHandoffPath(checkpointPath, guest.name)
19452
19476
  }));
19453
19477
  const realtimeEnabled = preset ? true : bootstrapBoolean("Configure existing Worker realtime fan-out?", "yes");
19454
- const cloudflareConfig = writeBootstrapConfig(join13(output, "cloudflare.json"), createCloudflareBootstrapDiscoveryCommandConfig({
19478
+ const cloudflareConfigPath = join13(output, "cloudflare.json");
19479
+ const cloudflareConfigValue = createCloudflareBootstrapDiscoveryCommandConfig({
19455
19480
  checkpointPath,
19456
19481
  discovery: {
19457
19482
  zoneName: preset?.profile.zoneName ?? bootstrapAnswer("Cloudflare DNS zone name", "forgezero.net"),
@@ -19468,7 +19493,8 @@ function writePlatformGenesisFleet(directory, preset) {
19468
19493
  service: "http://127.0.0.1:3000",
19469
19494
  tunnelName: preset ? guest.name : bootstrapAnswer(`${guest.name} Tunnel name`, guest.name)
19470
19495
  }))
19471
- }));
19496
+ });
19497
+ const cloudflareConfig = preset ? writeDerivedLaunchFile(cloudflareConfigPath, (path) => writeBootstrapConfig(path, cloudflareConfigValue)) : writeBootstrapConfig(cloudflareConfigPath, cloudflareConfigValue);
19472
19498
  const cloudflareAcceptance = join13(output, "cloudflare-acceptance.json");
19473
19499
  const configs = platformGenesisBootstrapConfigs(template, fleet, nodes, metalHostname, region, preset ? {
19474
19500
  source: "bootstrap-bundle",
@@ -19477,13 +19503,17 @@ function writePlatformGenesisFleet(directory, preset) {
19477
19503
  bundleSha256: preset.bundle.manifest.sha256
19478
19504
  } : undefined).map((config) => {
19479
19505
  const path = `${output}/${config.computeReference}-platform.json`;
19480
- writeBootstrapConfig(path, config);
19506
+ if (preset)
19507
+ writeDerivedLaunchFile(path, (temporary) => writeBootstrapConfig(temporary, config));
19508
+ else
19509
+ writeBootstrapConfig(path, config);
19481
19510
  return path;
19482
19511
  });
19483
19512
  const requests = configs.map((platformConfigFile, index) => {
19484
19513
  const guest = fleet[index];
19485
19514
  const host = guestHostKeys.nodes.find(({ name }) => name === guest.name);
19486
- return writeOperatorPlatformBootstrapRequest(join13(output, `${guest.name}-remote.json`), {
19515
+ const path = join13(output, `${guest.name}-remote.json`);
19516
+ const request = {
19487
19517
  kind: "platform-remote",
19488
19518
  platformConfigFile,
19489
19519
  target: {
@@ -19502,9 +19532,12 @@ function writePlatformGenesisFleet(directory, preset) {
19502
19532
  hostKeySha256: metalRequest.target.hostKeySha256
19503
19533
  }
19504
19534
  }
19505
- });
19535
+ };
19536
+ return preset ? writeDerivedLaunchFile(path, (temporary) => writeOperatorPlatformBootstrapRequest(temporary, request)) : writeOperatorPlatformBootstrapRequest(path, request);
19506
19537
  });
19507
- const fleetRequest = writeOperatorPlatformBootstrapFleetRequest(join13(output, "platform-fleet-remote.json"), requests, { configFile: cloudflareConfig, acceptanceFile: cloudflareAcceptance });
19538
+ const fleetPath = join13(output, "platform-fleet-remote.json");
19539
+ const writeFleet = (path) => writeOperatorPlatformBootstrapFleetRequest(path, requests, { configFile: cloudflareConfig, acceptanceFile: cloudflareAcceptance });
19540
+ const fleetRequest = preset ? writeDerivedLaunchFile(fleetPath, writeFleet) : writeFleet(fleetPath);
19508
19541
  return { configs, requests, fleetRequest, cloudflareConfig, cloudflareAcceptance };
19509
19542
  }
19510
19543
  function selectedForgeZeroLaunchEnvironment(value) {
@@ -19766,8 +19799,7 @@ async function cmdBootstrap(options, args) {
19766
19799
  }
19767
19800
  const outputDirectory = options.outputPath ?? join13(homedir2(), ".forgezero-secure", environment, "genesis");
19768
19801
  const environmentInput = options.bootstrapSecretsEnvFile ? readPlatformLaunchEnvironment(options.bootstrapSecretsEnvFile) : undefined;
19769
- const existingFleetPath = join13(outputDirectory, "platform-fleet-remote.json");
19770
- const fleet = existsSync13(existingFleetPath) ? readOperatorPlatformBootstrapFleetRequest(existingFleetPath) : readOperatorPlatformBootstrapFleetRequest((await prepareForgeZeroPlatformLaunch(environment, outputDirectory, options.projectRoot, environmentInput?.owner)).fleetRequest);
19802
+ const fleet = readOperatorPlatformBootstrapFleetRequest((await prepareForgeZeroPlatformLaunch(environment, outputDirectory, options.projectRoot, environmentInput?.owner)).fleetRequest);
19771
19803
  if (planOperatorPlatformFleetBootstrap(fleet, "apply").environment !== environment) {
19772
19804
  throw new Error("existing platform fleet does not match the selected launch profile");
19773
19805
  }
@@ -366,12 +366,13 @@ function systemdAgentEgressDirectives(loopbackTcpPorts = []) {
366
366
  }
367
367
 
368
368
  // src/version.ts
369
- var VERSION = "0.1.91";
369
+ var VERSION = "0.1.94";
370
370
 
371
371
  // src/otel-collector.ts
372
372
  var FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
373
373
  var FORGEZERO_OTEL_COLLECTOR_VERSION = "0.157.0";
374
374
  var FORGEZERO_OTEL_COLLECTOR_SHA256 = "2937cf24892af55b143c072fddece17862239cf78280620029276493eb81beae";
375
+ var LOCAL_DEBUG_OTEL_EXPORT = "local-debug";
375
376
  var ARCHIVE = "/run/forgezero-otelcol.tar.gz";
376
377
  var EXTRACT = "/run/forgezero-otelcol-release";
377
378
  var BINARY = "/usr/local/lib/forgezero/otelcol/otelcol";
@@ -397,7 +398,9 @@ var destination = (value) => {
397
398
  return url.toString().replace(/\/$/, "");
398
399
  };
399
400
  function renderForgeZeroOtelCollector(exportEndpoint) {
400
- const endpoint = destination(exportEndpoint);
401
+ const exporter = exportEndpoint === LOCAL_DEBUG_OTEL_EXPORT ? { definition: ` debug:
402
+ verbosity: basic`, name: "debug" } : { definition: ` otlp_http:
403
+ endpoint: ${JSON.stringify(destination(exportEndpoint))}`, name: "otlp_http" };
401
404
  return {
402
405
  config: `receivers:
403
406
  otlp:
@@ -405,19 +408,18 @@ function renderForgeZeroOtelCollector(exportEndpoint) {
405
408
  http:
406
409
  endpoint: 127.0.0.1:4318
407
410
  exporters:
408
- otlp_http:
409
- endpoint: ${JSON.stringify(endpoint)}
411
+ ${exporter.definition}
410
412
  service:
411
413
  pipelines:
412
414
  traces:
413
415
  receivers: [otlp]
414
- exporters: [otlp_http]
416
+ exporters: [${exporter.name}]
415
417
  metrics:
416
418
  receivers: [otlp]
417
- exporters: [otlp_http]
419
+ exporters: [${exporter.name}]
418
420
  logs:
419
421
  receivers: [otlp]
420
- exporters: [otlp_http]
422
+ exporters: [${exporter.name}]
421
423
  `,
422
424
  unit: `[Unit]
423
425
  Description=ForgeZero independently supervised OpenTelemetry Collector
@@ -497,7 +499,8 @@ async function ensureForgeZeroOtelCollector(host, exportEndpoint) {
497
499
  host.write(CONFIG, rendered.config, 420);
498
500
  host.write(UNIT, rendered.unit, 420);
499
501
  await checked2(host, ["/usr/bin/systemctl", "daemon-reload"]);
500
- await checked2(host, ["/usr/bin/systemctl", "enable", "--now", FORGEZERO_OTEL_COLLECTOR_UNIT]);
502
+ await checked2(host, ["/usr/bin/systemctl", "enable", FORGEZERO_OTEL_COLLECTOR_UNIT]);
503
+ await checked2(host, ["/usr/bin/systemctl", "restart", FORGEZERO_OTEL_COLLECTOR_UNIT]);
501
504
  let ready = false;
502
505
  for (let attempt = 0;attempt < 100; attempt += 1) {
503
506
  const probe = await host.exec([
@@ -1420,7 +1420,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
1420
1420
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
1421
1421
 
1422
1422
  // src/version.ts
1423
- var VERSION = "0.1.91";
1423
+ var VERSION = "0.1.94";
1424
1424
 
1425
1425
  // src/software.ts
1426
1426
  var PINNED_BUN_VERSION = "1.3.14";
@@ -3088,6 +3088,7 @@ var httpsOrigin = (name, raw) => {
3088
3088
  }
3089
3089
  return value.origin;
3090
3090
  };
3091
+ var agentTelemetryOrigin = (raw) => raw === "http://127.0.0.1:4318" ? raw : httpsOrigin("agentOtlpEndpoint", raw);
3091
3092
  function validatePlatformInitialInventory(value) {
3092
3093
  if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !["metalHostname", "region", "computes", "deployment"].includes(key)) || !/^[A-Za-z0-9][A-Za-z0-9.-]{1,252}$/.test(value.metalHostname) || !value.region || typeof value.region !== "object" || Array.isArray(value.region) || Object.keys(value.region).some((key) => !["key", "label", "country", "city", "confidentialCapable"].includes(key)) || !/^[a-z0-9][a-z0-9-]{0,62}$/.test(value.region.key) || !/^[A-Z]{2}$/.test(value.region.country) || value.region.confidentialCapable !== true || !Array.isArray(value.computes)) {
3093
3094
  throw new Error("initial platform inventory coordinates are invalid");
@@ -3193,7 +3194,7 @@ function validatePlatformSharedEnvironment(input) {
3193
3194
  if (input.otlpEndpoint !== "http://127.0.0.1:4318")
3194
3195
  throw new Error("OTLP must use the exact local collector endpoint.");
3195
3196
  validateCollectorUnit(input.otlpCollectorUnit);
3196
- httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint);
3197
+ agentTelemetryOrigin(input.agentOtlpEndpoint);
3197
3198
  for (const [name, value] of Object.entries({
3198
3199
  nodeHostname: input.nodeHostname,
3199
3200
  nodeRegion: input.nodeRegion,
@@ -3238,7 +3239,7 @@ function validatePlatformSharedEnvironment(input) {
3238
3239
  databaseReadPreferredCoordinators: readPreferred,
3239
3240
  appOrigin: httpsOrigin("appOrigin", input.appOrigin),
3240
3241
  apiOrigin: httpsOrigin("apiOrigin", input.apiOrigin),
3241
- agentOtlpEndpoint: httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint),
3242
+ agentOtlpEndpoint: agentTelemetryOrigin(input.agentOtlpEndpoint),
3242
3243
  ...initialInventory ? { initialInventory } : {}
3243
3244
  };
3244
3245
  }
@@ -3514,6 +3515,7 @@ function planLocalOtlpProof(endpoint2, collectorUnit) {
3514
3515
  var FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
3515
3516
  var FORGEZERO_OTEL_COLLECTOR_VERSION = "0.157.0";
3516
3517
  var FORGEZERO_OTEL_COLLECTOR_SHA256 = "2937cf24892af55b143c072fddece17862239cf78280620029276493eb81beae";
3518
+ var LOCAL_DEBUG_OTEL_EXPORT = "local-debug";
3517
3519
  var ARCHIVE = "/run/forgezero-otelcol.tar.gz";
3518
3520
  var EXTRACT = "/run/forgezero-otelcol-release";
3519
3521
  var BINARY = "/usr/local/lib/forgezero/otelcol/otelcol";
@@ -3539,7 +3541,9 @@ var destination = (value) => {
3539
3541
  return url.toString().replace(/\/$/, "");
3540
3542
  };
3541
3543
  function renderForgeZeroOtelCollector(exportEndpoint) {
3542
- const endpoint2 = destination(exportEndpoint);
3544
+ const exporter = exportEndpoint === LOCAL_DEBUG_OTEL_EXPORT ? { definition: ` debug:
3545
+ verbosity: basic`, name: "debug" } : { definition: ` otlp_http:
3546
+ endpoint: ${JSON.stringify(destination(exportEndpoint))}`, name: "otlp_http" };
3543
3547
  return {
3544
3548
  config: `receivers:
3545
3549
  otlp:
@@ -3547,19 +3551,18 @@ function renderForgeZeroOtelCollector(exportEndpoint) {
3547
3551
  http:
3548
3552
  endpoint: 127.0.0.1:4318
3549
3553
  exporters:
3550
- otlp_http:
3551
- endpoint: ${JSON.stringify(endpoint2)}
3554
+ ${exporter.definition}
3552
3555
  service:
3553
3556
  pipelines:
3554
3557
  traces:
3555
3558
  receivers: [otlp]
3556
- exporters: [otlp_http]
3559
+ exporters: [${exporter.name}]
3557
3560
  metrics:
3558
3561
  receivers: [otlp]
3559
- exporters: [otlp_http]
3562
+ exporters: [${exporter.name}]
3560
3563
  logs:
3561
3564
  receivers: [otlp]
3562
- exporters: [otlp_http]
3565
+ exporters: [${exporter.name}]
3563
3566
  `,
3564
3567
  unit: `[Unit]
3565
3568
  Description=ForgeZero independently supervised OpenTelemetry Collector
@@ -3639,7 +3642,8 @@ async function ensureForgeZeroOtelCollector(host, exportEndpoint) {
3639
3642
  host.write(CONFIG, rendered.config, 420);
3640
3643
  host.write(UNIT, rendered.unit, 420);
3641
3644
  await checked2(host, ["/usr/bin/systemctl", "daemon-reload"]);
3642
- await checked2(host, ["/usr/bin/systemctl", "enable", "--now", FORGEZERO_OTEL_COLLECTOR_UNIT]);
3645
+ await checked2(host, ["/usr/bin/systemctl", "enable", FORGEZERO_OTEL_COLLECTOR_UNIT]);
3646
+ await checked2(host, ["/usr/bin/systemctl", "restart", FORGEZERO_OTEL_COLLECTOR_UNIT]);
3643
3647
  let ready = false;
3644
3648
  for (let attempt = 0;attempt < 100; attempt += 1) {
3645
3649
  const probe = await host.exec([
@@ -3816,14 +3820,20 @@ function validateBootstrapConfig(value) {
3816
3820
  if (!/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?){2,}$/.test(value.nodeHostname)) {
3817
3821
  throw new Error("node hostname must be a lowercase public FQDN");
3818
3822
  }
3819
- let telemetry;
3820
- try {
3821
- telemetry = new URL(value.telemetryEndpoint);
3822
- } catch {
3823
- throw new Error("telemetry endpoint is malformed");
3824
- }
3825
- if (telemetry.protocol !== "https:" || telemetry.port && telemetry.port !== "443" || telemetry.username || telemetry.password || telemetry.search || telemetry.hash) {
3826
- throw new Error("telemetry endpoint must be public HTTPS on port 443 without credentials, query or fragment");
3823
+ if (value.telemetryEndpoint === LOCAL_DEBUG_OTEL_EXPORT) {
3824
+ if (value.kind !== "platform" || value.environment !== "development") {
3825
+ throw new Error("local debug telemetry is allowed only for the development platform profile");
3826
+ }
3827
+ } else {
3828
+ let telemetry;
3829
+ try {
3830
+ telemetry = new URL(value.telemetryEndpoint);
3831
+ } catch {
3832
+ throw new Error("telemetry endpoint is malformed");
3833
+ }
3834
+ if (telemetry.protocol !== "https:" || telemetry.port && telemetry.port !== "443" || telemetry.username || telemetry.password || telemetry.search || telemetry.hash) {
3835
+ throw new Error("telemetry endpoint must be public HTTPS on port 443 without credentials, query or fragment");
3836
+ }
3827
3837
  }
3828
3838
  const deploymentCredentials = value.deploymentCredentials ?? {};
3829
3839
  if (!deploymentCredentials || typeof deploymentCredentials !== "object" || Array.isArray(deploymentCredentials) || Object.keys(deploymentCredentials).length > 64) {
@@ -1,6 +1,7 @@
1
1
  export declare const FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
2
2
  export declare const FORGEZERO_OTEL_COLLECTOR_VERSION = "0.157.0";
3
3
  export declare const FORGEZERO_OTEL_COLLECTOR_SHA256 = "2937cf24892af55b143c072fddece17862239cf78280620029276493eb81beae";
4
+ export declare const LOCAL_DEBUG_OTEL_EXPORT = "local-debug";
4
5
  export interface OtelCollectorHost {
5
6
  write(path: string, content: string, mode: number): void;
6
7
  exec(argv: readonly string[]): Promise<{
@@ -728,6 +728,7 @@ var httpsOrigin = (name, raw) => {
728
728
  }
729
729
  return value.origin;
730
730
  };
731
+ var agentTelemetryOrigin = (raw) => raw === "http://127.0.0.1:4318" ? raw : httpsOrigin("agentOtlpEndpoint", raw);
731
732
  function validatePlatformInitialInventory(value) {
732
733
  if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !["metalHostname", "region", "computes", "deployment"].includes(key)) || !/^[A-Za-z0-9][A-Za-z0-9.-]{1,252}$/.test(value.metalHostname) || !value.region || typeof value.region !== "object" || Array.isArray(value.region) || Object.keys(value.region).some((key) => !["key", "label", "country", "city", "confidentialCapable"].includes(key)) || !/^[a-z0-9][a-z0-9-]{0,62}$/.test(value.region.key) || !/^[A-Z]{2}$/.test(value.region.country) || value.region.confidentialCapable !== true || !Array.isArray(value.computes)) {
733
734
  throw new Error("initial platform inventory coordinates are invalid");
@@ -833,7 +834,7 @@ function validatePlatformSharedEnvironment(input) {
833
834
  if (input.otlpEndpoint !== "http://127.0.0.1:4318")
834
835
  throw new Error("OTLP must use the exact local collector endpoint.");
835
836
  validateCollectorUnit(input.otlpCollectorUnit);
836
- httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint);
837
+ agentTelemetryOrigin(input.agentOtlpEndpoint);
837
838
  for (const [name, value] of Object.entries({
838
839
  nodeHostname: input.nodeHostname,
839
840
  nodeRegion: input.nodeRegion,
@@ -878,7 +879,7 @@ function validatePlatformSharedEnvironment(input) {
878
879
  databaseReadPreferredCoordinators: readPreferred,
879
880
  appOrigin: httpsOrigin("appOrigin", input.appOrigin),
880
881
  apiOrigin: httpsOrigin("apiOrigin", input.apiOrigin),
881
- agentOtlpEndpoint: httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint),
882
+ agentOtlpEndpoint: agentTelemetryOrigin(input.agentOtlpEndpoint),
882
883
  ...initialInventory ? { initialInventory } : {}
883
884
  };
884
885
  }
@@ -2908,7 +2908,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
2908
2908
  }
2909
2909
 
2910
2910
  // src/version.ts
2911
- var VERSION3 = "0.1.91";
2911
+ var VERSION3 = "0.1.94";
2912
2912
 
2913
2913
  // src/egress-policy.ts
2914
2914
  import { realpathSync as realpathSync3 } from "node:fs";
@@ -4140,6 +4140,7 @@ var httpsOrigin = (name, raw) => {
4140
4140
  }
4141
4141
  return value.origin;
4142
4142
  };
4143
+ var agentTelemetryOrigin = (raw) => raw === "http://127.0.0.1:4318" ? raw : httpsOrigin("agentOtlpEndpoint", raw);
4143
4144
  function validatePlatformInitialInventory(value) {
4144
4145
  if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !["metalHostname", "region", "computes", "deployment"].includes(key)) || !/^[A-Za-z0-9][A-Za-z0-9.-]{1,252}$/.test(value.metalHostname) || !value.region || typeof value.region !== "object" || Array.isArray(value.region) || Object.keys(value.region).some((key) => !["key", "label", "country", "city", "confidentialCapable"].includes(key)) || !/^[a-z0-9][a-z0-9-]{0,62}$/.test(value.region.key) || !/^[A-Z]{2}$/.test(value.region.country) || value.region.confidentialCapable !== true || !Array.isArray(value.computes)) {
4145
4146
  throw new Error("initial platform inventory coordinates are invalid");
@@ -4245,7 +4246,7 @@ function validatePlatformSharedEnvironment(input) {
4245
4246
  if (input.otlpEndpoint !== "http://127.0.0.1:4318")
4246
4247
  throw new Error("OTLP must use the exact local collector endpoint.");
4247
4248
  validateCollectorUnit(input.otlpCollectorUnit);
4248
- httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint);
4249
+ agentTelemetryOrigin(input.agentOtlpEndpoint);
4249
4250
  for (const [name, value] of Object.entries({
4250
4251
  nodeHostname: input.nodeHostname,
4251
4252
  nodeRegion: input.nodeRegion,
@@ -4290,7 +4291,7 @@ function validatePlatformSharedEnvironment(input) {
4290
4291
  databaseReadPreferredCoordinators: readPreferred,
4291
4292
  appOrigin: httpsOrigin("appOrigin", input.appOrigin),
4292
4293
  apiOrigin: httpsOrigin("apiOrigin", input.apiOrigin),
4293
- agentOtlpEndpoint: httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint),
4294
+ agentOtlpEndpoint: agentTelemetryOrigin(input.agentOtlpEndpoint),
4294
4295
  ...initialInventory ? { initialInventory } : {}
4295
4296
  };
4296
4297
  }
@@ -16,6 +16,7 @@ export interface ForgeZeroLaunchProfile {
16
16
  country: string;
17
17
  city?: string;
18
18
  };
19
+ collectorExport: string;
19
20
  agentOtlpEndpoint: string;
20
21
  seedSyncEpoch: string;
21
22
  privateCidrs: readonly string[];
package/dist/provision.js CHANGED
@@ -2908,7 +2908,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
2908
2908
  }
2909
2909
 
2910
2910
  // src/version.ts
2911
- var VERSION3 = "0.1.91";
2911
+ var VERSION3 = "0.1.94";
2912
2912
 
2913
2913
  // src/egress-policy.ts
2914
2914
  import { realpathSync as realpathSync3 } from "node:fs";
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.91";
2
+ export declare const VERSION = "0.1.94";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgezero/agent",
3
- "version": "0.1.91",
3
+ "version": "0.1.94",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "check": "tsc --noEmit",