rigscope 0.1.8 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +1 -1
  3. package/.next/build-manifest.json +2 -2
  4. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  5. package/.next/server/app/_global-error.html +1 -1
  6. package/.next/server/app/_global-error.rsc +1 -1
  7. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  10. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  11. package/.next/server/app/_not-found.html +1 -1
  12. package/.next/server/app/_not-found.rsc +3 -3
  13. package/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  14. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +3 -3
  15. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  16. package/.next/server/app/administration/page_client-reference-manifest.js +1 -1
  17. package/.next/server/app/administration.html +1 -1
  18. package/.next/server/app/administration.rsc +3 -3
  19. package/.next/server/app/administration.segments/_full.segment.rsc +3 -3
  20. package/.next/server/app/administration.segments/_tree.segment.rsc +2 -2
  21. package/.next/server/app/administration.segments/administration/__PAGE__.segment.rsc +3 -3
  22. package/.next/server/app/alerts/page_client-reference-manifest.js +1 -1
  23. package/.next/server/app/alerts.html +1 -1
  24. package/.next/server/app/alerts.rsc +3 -3
  25. package/.next/server/app/alerts.segments/_full.segment.rsc +3 -3
  26. package/.next/server/app/alerts.segments/_tree.segment.rsc +2 -2
  27. package/.next/server/app/alerts.segments/alerts/__PAGE__.segment.rsc +3 -3
  28. package/.next/server/app/connections/page.js +1 -1
  29. package/.next/server/app/connections/page_client-reference-manifest.js +1 -1
  30. package/.next/server/app/connections.html +1 -1
  31. package/.next/server/app/connections.rsc +4 -4
  32. package/.next/server/app/connections.segments/_full.segment.rsc +4 -4
  33. package/.next/server/app/connections.segments/_tree.segment.rsc +2 -2
  34. package/.next/server/app/connections.segments/connections/__PAGE__.segment.rsc +4 -4
  35. package/.next/server/app/history/page.js +2 -2
  36. package/.next/server/app/history/page_client-reference-manifest.js +1 -1
  37. package/.next/server/app/history.html +1 -1
  38. package/.next/server/app/history.rsc +6 -6
  39. package/.next/server/app/history.segments/_full.segment.rsc +6 -6
  40. package/.next/server/app/history.segments/_tree.segment.rsc +2 -2
  41. package/.next/server/app/history.segments/history/__PAGE__.segment.rsc +6 -6
  42. package/.next/server/app/index.html +1 -1
  43. package/.next/server/app/index.rsc +3 -3
  44. package/.next/server/app/index.segments/__PAGE__.segment.rsc +3 -3
  45. package/.next/server/app/index.segments/_full.segment.rsc +3 -3
  46. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  47. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  48. package/.next/server/app/login.html +1 -1
  49. package/.next/server/app/login.rsc +3 -3
  50. package/.next/server/app/login.segments/_full.segment.rsc +3 -3
  51. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  52. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +3 -3
  53. package/.next/server/app/machines/[machineId]/page_client-reference-manifest.js +1 -1
  54. package/.next/server/app/page_client-reference-manifest.js +1 -1
  55. package/.next/server/app/processes/page_client-reference-manifest.js +1 -1
  56. package/.next/server/app/processes.html +1 -1
  57. package/.next/server/app/processes.rsc +3 -3
  58. package/.next/server/app/processes.segments/_full.segment.rsc +3 -3
  59. package/.next/server/app/processes.segments/_tree.segment.rsc +2 -2
  60. package/.next/server/app/processes.segments/processes/__PAGE__.segment.rsc +3 -3
  61. package/.next/server/app-paths-manifest.json +1 -1
  62. package/.next/server/chunks/161.js +1 -1
  63. package/.next/server/chunks/168.js +1 -1
  64. package/.next/server/middleware-build-manifest.js +1 -1
  65. package/.next/server/pages/404.html +1 -1
  66. package/.next/server/pages/500.html +1 -1
  67. package/.next/static/chunks/408-351fe4a1e9a2d556.js +1 -0
  68. package/.next/static/chunks/939-92930fde48061e20.js +1 -0
  69. package/.next/static/chunks/app/connections/page-7d18b901f2338f20.js +1 -0
  70. package/.next/static/chunks/app/history/page-adc25194b30c689d.js +3 -0
  71. package/.next/static/css/{f5d3162842dcac31.css → 2abef6d49c1c2970.css} +1 -1
  72. package/ARCHITECTURE.md +43 -15
  73. package/CHANGELOG.md +62 -0
  74. package/README.md +10 -3
  75. package/dist/cli.cjs +868 -511
  76. package/dist/collector.cjs +603 -120
  77. package/dist/hub.cjs +3429 -1451
  78. package/dist/ops.cjs +1178 -888
  79. package/dist/system.cjs +1322 -363
  80. package/docs/configuration.md +34 -1
  81. package/docs/installation.md +12 -6
  82. package/docs/monitoring.md +60 -17
  83. package/docs/operations.md +78 -22
  84. package/docs/system-installation.md +11 -4
  85. package/docs/telemetry-storage.md +258 -0
  86. package/package.json +3 -1
  87. package/packaging/collector.env.example +6 -0
  88. package/packaging/system/rigscope-collector.service.example +3 -0
  89. package/.next/static/chunks/408-9fdba9e4e956ef08.js +0 -1
  90. package/.next/static/chunks/939-514e554087419c71.js +0 -1
  91. package/.next/static/chunks/app/connections/page-75abd16fc495fcbd.js +0 -1
  92. package/.next/static/chunks/app/history/page-a25ba8fab7bd7cb9.js +0 -3
  93. /package/.next/static/{ip43TIgmaByEBn2GFtvfO → JbaLjudQSxrMUD_VHPrnD}/_buildManifest.js +0 -0
  94. /package/.next/static/{ip43TIgmaByEBn2GFtvfO → JbaLjudQSxrMUD_VHPrnD}/_ssgManifest.js +0 -0
package/dist/cli.cjs CHANGED
@@ -23,8 +23,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  ));
24
24
 
25
25
  // src/cli/main.ts
26
- var import_node_fs19 = require("node:fs");
27
- var import_node_path20 = require("node:path");
26
+ var import_node_fs20 = require("node:fs");
27
+ var import_node_path21 = require("node:path");
28
28
  var import_node_util2 = require("node:util");
29
29
  var import_node_readline = require("node:readline");
30
30
  var import_node_stream = require("node:stream");
@@ -42,6 +42,9 @@ var SHARING_KEYS = [
42
42
  "processArguments"
43
43
  ];
44
44
 
45
+ // collector/destinations/queue.ts
46
+ var DEFAULT_COLLECTOR_QUEUE_MODE = "memory";
47
+
45
48
  // collector/config.ts
46
49
  var ConfigError = class extends Error {
47
50
  issues;
@@ -247,6 +250,18 @@ function parseDestinations(env, issues, destinations) {
247
250
  }
248
251
  }
249
252
  }
253
+ var QUEUE_MODES = ["memory", "durable"];
254
+ function parseQueueMode(env, issues) {
255
+ const raw = env["COLLECTOR_QUEUE_MODE"]?.trim();
256
+ if (raw === void 0 || raw === "") {
257
+ return DEFAULT_COLLECTOR_QUEUE_MODE;
258
+ }
259
+ if (!QUEUE_MODES.includes(raw)) {
260
+ issues.push(`COLLECTOR_QUEUE_MODE must be one of ${QUEUE_MODES.join(" or ")}`);
261
+ return DEFAULT_COLLECTOR_QUEUE_MODE;
262
+ }
263
+ return raw;
264
+ }
250
265
  function parseIntervalMs(env, issues) {
251
266
  const raw = env["COLLECTOR_INTERVAL_MS"]?.trim();
252
267
  if (raw === void 0 || raw === "") {
@@ -277,6 +292,7 @@ function parseCollectorConfig(env) {
277
292
  }
278
293
  const destinations = [];
279
294
  const intervalMs = parseIntervalMs(env, issues);
295
+ const queueMode = parseQueueMode(env, issues);
280
296
  parseDestinations(env, issues, destinations);
281
297
  if (issues.length > 0) {
282
298
  throw new ConfigError(issues);
@@ -286,7 +302,8 @@ function parseCollectorConfig(env) {
286
302
  hostname,
287
303
  hostnameIsMockDefault: rawHostname === "",
288
304
  intervalMs,
289
- destinations
305
+ destinations,
306
+ queueMode
290
307
  };
291
308
  }
292
309
 
@@ -661,6 +678,13 @@ function verifyPassword(password, hash2) {
661
678
  // src/hub/config.ts
662
679
  var import_node_net = require("node:net");
663
680
  var import_node_path3 = require("node:path");
681
+
682
+ // src/contracts/telemetry.ts
683
+ var MAX_PERSISTENT_FLUSH_MS = 5e3;
684
+ var MIN_EPOCH = Date.parse("2000-01-01T00:00:00Z");
685
+ var MAX_EPOCH = Date.parse("2200-01-01T00:00:00Z");
686
+
687
+ // src/hub/config.ts
664
688
  var MAX_HUB_COLLECTORS_BYTES = 1024 * 1024;
665
689
  var MAX_COLLECTOR_ID_LENGTH = 128;
666
690
  var MAX_MACHINE_ID_LENGTH = 128;
@@ -688,6 +712,7 @@ function parseHubConfig(env) {
688
712
  maxCollectorConnections = parsed;
689
713
  }
690
714
  const connectionBudget = maxCollectorConnections === void 0 ? {} : { maxCollectorConnections };
715
+ const history = parseHistoryConfig(env);
691
716
  if (publicOrigin === void 0 !== (socketPath === void 0)) {
692
717
  throw new HubConfigError(
693
718
  "HUB_PUBLIC_ORIGIN and HUB_SOCKET_PATH must be set together: remote mode needs both the exact HTTPS origin and the Unix socket path"
@@ -707,6 +732,7 @@ function parseHubConfig(env) {
707
732
  collectors,
708
733
  development,
709
734
  remote: parseRemoteConfig(publicOrigin, socketPath),
735
+ history,
710
736
  ...connectionBudget
711
737
  };
712
738
  }
@@ -716,9 +742,65 @@ function parseHubConfig(env) {
716
742
  collectors,
717
743
  development,
718
744
  remote: null,
745
+ history,
719
746
  ...connectionBudget
720
747
  };
721
748
  }
749
+ var DEFAULT_MEMORY_BUDGET_BYTES = 128 * 1024 * 1024;
750
+ var DEFAULT_PERSISTENT_FLUSH_MS = MAX_PERSISTENT_FLUSH_MS;
751
+ var DEFAULT_RECORDING_MAX_DURATION_MS = 60 * 6e4;
752
+ var DEFAULT_RECORDING_MAX_BYTES = 64 * 1024 * 1024;
753
+ var DEFAULT_RECORDING_BACKLOG_MS = 10 * 6e4;
754
+ var DEFAULT_MAX_ACTIVE_RECORDINGS = 4;
755
+ var HOUR_MS = 36e5;
756
+ var DAY_MS = 864e5;
757
+ function parseHistoryConfig(env) {
758
+ const modeValue = env.HUB_TELEMETRY_MODE ?? "memory";
759
+ if (modeValue !== "memory" && modeValue !== "persistent") {
760
+ throw new HubConfigError("HUB_TELEMETRY_MODE must be memory or persistent");
761
+ }
762
+ const memoryBudgetBytes = envInteger(
763
+ env.HUB_HISTORY_MEMORY_BYTES,
764
+ DEFAULT_MEMORY_BUDGET_BYTES,
765
+ 1 * 1024 * 1024,
766
+ 16 * 1024 * 1024 * 1024,
767
+ "HUB_HISTORY_MEMORY_BYTES"
768
+ );
769
+ const flushIntervalMs = envInteger(
770
+ env.HUB_TELEMETRY_FLUSH_MS,
771
+ DEFAULT_PERSISTENT_FLUSH_MS,
772
+ 1e3,
773
+ MAX_PERSISTENT_FLUSH_MS,
774
+ "HUB_TELEMETRY_FLUSH_MS"
775
+ );
776
+ const retention = {
777
+ rawMs: envInteger(env.HUB_RETENTION_RAW_MS, 2 * DAY_MS, 0, 400 * DAY_MS, "HUB_RETENTION_RAW_MS"),
778
+ tenSecondMs: envInteger(env.HUB_RETENTION_10S_MS, 2 * HOUR_MS, 0, 30 * DAY_MS, "HUB_RETENTION_10S_MS"),
779
+ oneMinuteMs: envInteger(env.HUB_RETENTION_1M_MS, 30 * DAY_MS, 0, 400 * DAY_MS, "HUB_RETENTION_1M_MS"),
780
+ oneHourMs: envInteger(env.HUB_RETENTION_1H_MS, 400 * DAY_MS, 0, 800 * DAY_MS, "HUB_RETENTION_1H_MS"),
781
+ receiptsMs: envInteger(env.HUB_RECEIPT_RETENTION_MS, 7 * DAY_MS, HOUR_MS, 400 * DAY_MS, "HUB_RECEIPT_RETENTION_MS"),
782
+ gapsMs: envInteger(env.HUB_GAP_RETENTION_MS, 400 * DAY_MS, 0, 800 * DAY_MS, "HUB_GAP_RETENTION_MS")
783
+ };
784
+ const recordings = {
785
+ directory: nonEmpty(env.HUB_RECORDINGS_DIR) ?? null,
786
+ maxDurationMs: envInteger(env.HUB_RECORDING_MAX_DURATION_MS, DEFAULT_RECORDING_MAX_DURATION_MS, 1e3, 24 * 60 * 6e4, "HUB_RECORDING_MAX_DURATION_MS"),
787
+ maxBytes: envInteger(env.HUB_RECORDING_MAX_BYTES, DEFAULT_RECORDING_MAX_BYTES, 64 * 1024, 1024 * 1024 * 1024, "HUB_RECORDING_MAX_BYTES"),
788
+ backlogMs: envInteger(env.HUB_RECORDING_BACKLOG_MS, DEFAULT_RECORDING_BACKLOG_MS, 0, 24 * 60 * 6e4, "HUB_RECORDING_BACKLOG_MS"),
789
+ maxActive: envInteger(env.HUB_RECORDING_MAX_ACTIVE, DEFAULT_MAX_ACTIVE_RECORDINGS, 1, 32, "HUB_RECORDING_MAX_ACTIVE")
790
+ };
791
+ return { mode: modeValue, memoryBudgetBytes, flushIntervalMs, retention, recordings };
792
+ }
793
+ function envInteger(raw, fallback, min, max, name) {
794
+ if (raw === void 0 || raw === "") return fallback;
795
+ if (!/^\d+$/.test(raw)) {
796
+ throw new HubConfigError(`${name} must be a nonnegative integer`);
797
+ }
798
+ const value = Number(raw);
799
+ if (!Number.isSafeInteger(value) || value < min || value > max) {
800
+ throw new HubConfigError(`${name} must be between ${min} and ${max}`);
801
+ }
802
+ return value;
803
+ }
722
804
  function nonEmpty(value) {
723
805
  return value === void 0 || value === "" ? void 0 : value;
724
806
  }
@@ -1446,8 +1528,45 @@ var MIGRATIONS = [
1446
1528
  },
1447
1529
  { version: 3, name: "admin", up: adminSchema },
1448
1530
  { version: 4, name: "alerts", up: installAlertSchema },
1449
- { version: 5, name: "collector_registry", up: installRegistrySchema }
1531
+ { version: 5, name: "collector_registry", up: installRegistrySchema },
1532
+ { version: 6, name: "telemetry_modes_and_recordings", up: telemetryModesAndRecordings }
1450
1533
  ];
1534
+ function telemetryModesAndRecordings(db) {
1535
+ db.exec(`
1536
+ CREATE TABLE IF NOT EXISTS points_10s (
1537
+ series_id INTEGER NOT NULL,
1538
+ bucket_ms INTEGER NOT NULL,
1539
+ min REAL,
1540
+ max REAL,
1541
+ sum REAL NOT NULL DEFAULT 0,
1542
+ count INTEGER NOT NULL DEFAULT 0,
1543
+ null_count INTEGER NOT NULL DEFAULT 0,
1544
+ PRIMARY KEY (series_id, bucket_ms)
1545
+ ) WITHOUT ROWID;
1546
+
1547
+ CREATE TABLE IF NOT EXISTS recordings (
1548
+ id TEXT PRIMARY KEY,
1549
+ created_by TEXT NOT NULL,
1550
+ status TEXT NOT NULL,
1551
+ machines_json TEXT NOT NULL,
1552
+ metrics_json TEXT NOT NULL,
1553
+ resolution_ms INTEGER NOT NULL,
1554
+ created_at_ms INTEGER NOT NULL,
1555
+ started_at_ms INTEGER NOT NULL,
1556
+ stopped_at_ms INTEGER,
1557
+ deadline_ms INTEGER NOT NULL,
1558
+ max_bytes INTEGER NOT NULL,
1559
+ bytes_written INTEGER NOT NULL DEFAULT 0,
1560
+ file_name TEXT NOT NULL,
1561
+ include_backlog INTEGER NOT NULL DEFAULT 0,
1562
+ reason TEXT,
1563
+ error TEXT
1564
+ );
1565
+
1566
+ CREATE INDEX IF NOT EXISTS recordings_created
1567
+ ON recordings (created_at_ms DESC);
1568
+ `);
1569
+ }
1451
1570
 
1452
1571
  // src/hub/storage/db.ts
1453
1572
  var BUSY_TIMEOUT_MS = 5e3;
@@ -1524,7 +1643,7 @@ var SqliteHubDatabase = class {
1524
1643
  const walBudgetBytes = Math.max(64 * 1024, Math.min(HUB_WAL_RESERVE_BYTES, Math.floor(limitBytes / 16)));
1525
1644
  const mainBudgetBytes = Math.max(pageSize, limitBytes - walBudgetBytes);
1526
1645
  const requestedPages = Math.max(1, Math.floor(mainBudgetBytes / pageSize));
1527
- const checkpointPages = Math.max(16, Math.min(1e3, Math.floor(walBudgetBytes / pageSize)));
1646
+ const checkpointPages = Math.max(16, Math.min(16384, Math.floor(walBudgetBytes / pageSize)));
1528
1647
  this.db.exec(`PRAGMA journal_size_limit = ${walBudgetBytes}`);
1529
1648
  this.db.exec(`PRAGMA wal_autocheckpoint = ${checkpointPages}`);
1530
1649
  let maxPageCount = requestedPages;
@@ -3522,17 +3641,17 @@ function uninstallInstallation(dir, purge = true, injected = {}) {
3522
3641
 
3523
3642
  // src/cli/mutation.ts
3524
3643
  var import_node_child_process3 = require("node:child_process");
3525
- var import_node_fs13 = require("node:fs");
3526
- var import_node_path14 = require("node:path");
3644
+ var import_node_fs14 = require("node:fs");
3645
+ var import_node_path15 = require("node:path");
3527
3646
  var import_promises = require("node:readline/promises");
3528
3647
 
3529
3648
  // src/cli/lifecycle.ts
3530
- var import_node_fs12 = require("node:fs");
3531
- var import_node_path13 = require("node:path");
3649
+ var import_node_fs13 = require("node:fs");
3650
+ var import_node_path14 = require("node:path");
3532
3651
 
3533
3652
  // src/cli/upgrade.ts
3534
- var import_node_fs11 = require("node:fs");
3535
- var import_node_path12 = require("node:path");
3653
+ var import_node_fs12 = require("node:fs");
3654
+ var import_node_path13 = require("node:path");
3536
3655
 
3537
3656
  // src/operations/database.ts
3538
3657
  var import_node_fs10 = require("node:fs");
@@ -3555,7 +3674,9 @@ var COUNTED_TABLES = /* @__PURE__ */ new Set([
3555
3674
  "machine_meta",
3556
3675
  "machine_latest",
3557
3676
  "inventory",
3677
+ "points_10s",
3558
3678
  "gaps",
3679
+ "recordings",
3559
3680
  "alert_rules",
3560
3681
  "alert_incidents",
3561
3682
  "alert_jobs",
@@ -3878,6 +3999,187 @@ async function restoreDatabase(options) {
3878
3999
  throw error2;
3879
4000
  }
3880
4001
  }
4002
+ var MACHINE_HISTORY_TABLES = [
4003
+ "points_raw",
4004
+ "points_10s",
4005
+ "points_1m",
4006
+ "points_1h",
4007
+ "sample_receipts",
4008
+ "gaps",
4009
+ "machine_latest",
4010
+ "inventory",
4011
+ "alert_observed_resources",
4012
+ "alert_machine_progress",
4013
+ "alert_incidents",
4014
+ "series"
4015
+ ];
4016
+ function clearMachineHistory(database) {
4017
+ const path2 = resolvePath(database, "database");
4018
+ if (!(0, import_node_fs10.existsSync)(path2)) return { deleted: {}, integrity: "absent" };
4019
+ assertPrivateRegularFile(path2, "database");
4020
+ assertNoActiveHubLock(path2);
4021
+ const db = new import_node_sqlite2.DatabaseSync(path2, { timeout: BUSY_TIMEOUT_MS2 });
4022
+ const deleted = {};
4023
+ try {
4024
+ db.exec("PRAGMA busy_timeout = 5000");
4025
+ const present = new Set(
4026
+ db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map((row) => row.name)
4027
+ );
4028
+ db.exec("BEGIN IMMEDIATE");
4029
+ try {
4030
+ for (const table of MACHINE_HISTORY_TABLES) {
4031
+ if (!present.has(table)) continue;
4032
+ const result = db.prepare(`DELETE FROM ${table}`).run();
4033
+ deleted[table] = Number(result.changes);
4034
+ }
4035
+ db.exec("COMMIT");
4036
+ } catch (error2) {
4037
+ try {
4038
+ db.exec("ROLLBACK");
4039
+ } catch {
4040
+ }
4041
+ throw error2;
4042
+ }
4043
+ const autoVacuum = db.prepare("PRAGMA auto_vacuum").get();
4044
+ if (autoVacuum && Object.values(autoVacuum)[0] === 2) db.exec("PRAGMA incremental_vacuum(1024)");
4045
+ try {
4046
+ db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
4047
+ } catch {
4048
+ }
4049
+ } finally {
4050
+ db.close();
4051
+ }
4052
+ return { deleted, integrity: inspectDatabase(path2).integrity };
4053
+ }
4054
+
4055
+ // src/cli/machine-transition.ts
4056
+ var import_node_fs11 = require("node:fs");
4057
+ var import_node_path12 = require("node:path");
4058
+ var import_node_crypto11 = require("node:crypto");
4059
+ var MACHINE_DATA_TRANSITION_FILE = "machine-data-transition.json";
4060
+ var STAGED_QUEUE_PREFIX = "outbox.pre-transition-";
4061
+ function machineDataTransitionPath(stateRoot) {
4062
+ return (0, import_node_path12.join)(stateRoot, MACHINE_DATA_TRANSITION_FILE);
4063
+ }
4064
+ function readMachineDataTransition(stateRoot) {
4065
+ const path2 = machineDataTransitionPath(stateRoot);
4066
+ try {
4067
+ const stat = (0, import_node_fs11.lstatSync)(path2);
4068
+ if (!stat.isFile() || stat.isSymbolicLink() || (stat.mode & 63) !== 0) {
4069
+ throw new Error("marker must be a private regular file");
4070
+ }
4071
+ const value = JSON.parse((0, import_node_fs11.readFileSync)(path2, "utf8"));
4072
+ if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("invalid marker");
4073
+ const record = value;
4074
+ if (typeof record.transitionedAt !== "string" || !Number.isFinite(Date.parse(record.transitionedAt))) {
4075
+ throw new Error("invalid transition date");
4076
+ }
4077
+ return { transitionedAt: record.transitionedAt, release: typeof record.release === "string" ? record.release : null };
4078
+ } catch (error2) {
4079
+ if (error2.code === "ENOENT") return null;
4080
+ throw new OperationsError(
4081
+ "invalid-transition-marker",
4082
+ `Cannot determine whether the one-time reset completed; preserving machine data. Repair ${path2}: ${errorMessage(error2)}`,
4083
+ { cause: error2 }
4084
+ );
4085
+ }
4086
+ }
4087
+ function recordMachineDataTransition(stateRoot, release, write = (path2, content) => writePrivateFile(path2, content)) {
4088
+ const marker = { transitionedAt: (/* @__PURE__ */ new Date()).toISOString(), release };
4089
+ write(machineDataTransitionPath(stateRoot), `${JSON.stringify(marker, null, 2)}
4090
+ `);
4091
+ }
4092
+ function newMachineDataCleanup(transitioned) {
4093
+ return {
4094
+ report: { transitioned, databaseRows: null, collectorQueuesRemoved: false, warning: null },
4095
+ stagedQueues: null,
4096
+ databaseCleared: false
4097
+ };
4098
+ }
4099
+ function performMachineDataCleanup(stateRoot, paths, now = Date.now, deps = {}) {
4100
+ if (readMachineDataTransition(stateRoot) !== null) return newMachineDataCleanup(false);
4101
+ const cleanup = newMachineDataCleanup(true);
4102
+ const clear = deps.clear ?? clearMachineHistory;
4103
+ const warnings = [];
4104
+ try {
4105
+ if ((0, import_node_fs11.existsSync)(paths.queueTree)) {
4106
+ const staged = (0, import_node_path12.join)((0, import_node_path12.dirname)(paths.queueTree), `${STAGED_QUEUE_PREFIX}${timestampSlug(now())}-${(0, import_node_crypto11.randomUUID)()}`);
4107
+ (0, import_node_fs11.renameSync)(paths.queueTree, staged);
4108
+ cleanup.stagedQueues = staged;
4109
+ cleanup.report.collectorQueuesRemoved = true;
4110
+ }
4111
+ } catch (error2) {
4112
+ warnings.push(`collector queues: ${errorMessage(error2)}`);
4113
+ }
4114
+ try {
4115
+ if ((0, import_node_fs11.existsSync)(paths.database)) {
4116
+ cleanup.databaseCleared = true;
4117
+ cleanup.report.databaseRows = clear(paths.database).deleted;
4118
+ }
4119
+ } catch (error2) {
4120
+ warnings.push(`hub database: ${errorMessage(error2)}`);
4121
+ }
4122
+ cleanup.report.warning = warnings.length > 0 ? warnings.join("; ") : null;
4123
+ return cleanup;
4124
+ }
4125
+ function undoMachineDataCleanup(paths, stagedQueues) {
4126
+ if (stagedQueues === null || !(0, import_node_fs11.existsSync)(stagedQueues)) return [];
4127
+ try {
4128
+ if ((0, import_node_fs11.existsSync)(paths.queueTree)) {
4129
+ const failedQueue = `${paths.queueTree}.failed-transition-${(0, import_node_crypto11.randomUUID)()}`;
4130
+ (0, import_node_fs11.renameSync)(paths.queueTree, failedQueue);
4131
+ try {
4132
+ (0, import_node_fs11.renameSync)(stagedQueues, paths.queueTree);
4133
+ } catch (error2) {
4134
+ (0, import_node_fs11.renameSync)(failedQueue, paths.queueTree);
4135
+ throw error2;
4136
+ }
4137
+ return [`restored the collector queue tree; replacement runtime queue preserved at ${failedQueue}`];
4138
+ }
4139
+ (0, import_node_fs11.renameSync)(stagedQueues, paths.queueTree);
4140
+ return ["restored the collector queue tree the transition had staged aside"];
4141
+ } catch (error2) {
4142
+ return [`machine data rollback could not restore the collector queue: ${errorMessage(error2)}`];
4143
+ }
4144
+ }
4145
+ function completeMachineDataCleanup(stateRoot, cleanup, release, write) {
4146
+ if (!cleanup.report.transitioned) return;
4147
+ if (cleanup.report.warning !== null) return;
4148
+ try {
4149
+ recordMachineDataTransition(stateRoot, release, write);
4150
+ } catch (error2) {
4151
+ cleanup.report.warning = `could not record transition completion: ${errorMessage(error2)}`;
4152
+ return;
4153
+ }
4154
+ if (cleanup.stagedQueues !== null) {
4155
+ try {
4156
+ (0, import_node_fs11.rmSync)(cleanup.stagedQueues, { recursive: true, force: true });
4157
+ } catch (error2) {
4158
+ cleanup.report.warning = `transition completed; staged queue remains at ${cleanup.stagedQueues}: ${errorMessage(error2)}`;
4159
+ }
4160
+ }
4161
+ }
4162
+ function machineDataReportRows(report) {
4163
+ if (report.databaseRows === null) return null;
4164
+ return Object.values(report.databaseRows).reduce((sum, rows) => sum + rows, 0);
4165
+ }
4166
+ function machineDataKeptNote() {
4167
+ return "accounts, sessions, enrollment and grants, settings, alert rules/jobs/webhook settings, machine names/tags and recordings were kept";
4168
+ }
4169
+ function describeMachineDataReport(report) {
4170
+ const summary = report.transitioned ? (() => {
4171
+ const rows = machineDataReportRows(report);
4172
+ const parts = [
4173
+ rows === null ? "no hub database" : `${rows} database row(s)`,
4174
+ ...report.collectorQueuesRemoved ? ["the collector queue tree"] : []
4175
+ ];
4176
+ return `Machine data: one-time RAM-first transition discarded ${parts.join(" and ")}. The ${machineDataKeptNote()}.`;
4177
+ })() : "Machine data: one-time transition already completed; durable history and collector queues preserved.";
4178
+ return {
4179
+ summary,
4180
+ warning: report.warning === null ? null : `Machine-data cleanup warning (${report.warning}). The upgrade is healthy; cleanup without a completion marker is retried on the next upgrade.`
4181
+ };
4182
+ }
3881
4183
 
3882
4184
  // src/cli/health.ts
3883
4185
  var import_node_http2 = require("node:http");
@@ -4071,6 +4373,7 @@ function dependencies(deps = {}) {
4071
4373
  restore: deps.restore ?? restoreDatabase,
4072
4374
  inspect: deps.inspect ?? inspectDatabase,
4073
4375
  inspectRuntime: deps.inspectRuntime ?? inspectDatabaseWithRuntime,
4376
+ clearHistory: deps.clearHistory ?? clearMachineHistory,
4074
4377
  waitHealth: deps.waitHealth ?? waitForHealth,
4075
4378
  activate: deps.activate ?? activatePackage,
4076
4379
  swap: deps.swap ?? swapRuntimes,
@@ -4081,10 +4384,10 @@ function dependencies(deps = {}) {
4081
4384
  function acquireInstallLocks(dir, acquireLease) {
4082
4385
  const releases = [];
4083
4386
  try {
4084
- releases.push(acquireLease((0, import_node_path12.join)(dir, "run", "hub.lock")));
4085
- releases.push(acquireLease((0, import_node_path12.join)(dir, "run", "collector.lock")));
4086
- releases.push(acquireLease((0, import_node_path12.join)(dir, "run", "rigscope.lock")));
4087
- releases.push(acquireLease((0, import_node_path12.join)(dir, "collector", "collector-runtime.lock")));
4387
+ releases.push(acquireLease((0, import_node_path13.join)(dir, "run", "hub.lock")));
4388
+ releases.push(acquireLease((0, import_node_path13.join)(dir, "run", "collector.lock")));
4389
+ releases.push(acquireLease((0, import_node_path13.join)(dir, "run", "rigscope.lock")));
4390
+ releases.push(acquireLease((0, import_node_path13.join)(dir, "collector", "collector-runtime.lock")));
4088
4391
  } catch (error2) {
4089
4392
  for (const release of releases.reverse()) {
4090
4393
  try {
@@ -4150,8 +4453,11 @@ function assertComponentsStopped(dir, deps, components) {
4150
4453
  }
4151
4454
  }
4152
4455
  }
4456
+ function machineDataPaths(dir) {
4457
+ return { database: databasePath(dir), queueTree: (0, import_node_path13.join)(dir, "collector", "outbox") };
4458
+ }
4153
4459
  function collectorStateCheck(dir) {
4154
- const path2 = (0, import_node_path12.join)(dir, "collector", "state.json");
4460
+ const path2 = (0, import_node_path13.join)(dir, "collector", "state.json");
4155
4461
  let raw;
4156
4462
  try {
4157
4463
  raw = readPrivate(path2);
@@ -4175,13 +4481,13 @@ function hubTarget(config, dir) {
4175
4481
  role: "hub",
4176
4482
  port: config.port,
4177
4483
  origin: config.origin,
4178
- socket: config.socket ?? (0, import_node_path12.join)(dir, "run", "hub.sock")
4484
+ socket: config.socket ?? (0, import_node_path13.join)(dir, "run", "hub.sock")
4179
4485
  });
4180
4486
  }
4181
4487
  async function verifyRunningInstallation(options) {
4182
4488
  const d = dependencies(options.deps);
4183
4489
  const deadline = Date.now() + (options.deadlineMs ?? DEFAULT_HEALTH_DEADLINE_MS);
4184
- const runtime = (0, import_node_path12.join)(options.dir, "runtime");
4490
+ const runtime = (0, import_node_path13.join)(options.dir, "runtime");
4185
4491
  const version = runtimeVersion(runtime);
4186
4492
  if (version !== options.expectedVersion) {
4187
4493
  throw new OperationsError(
@@ -4222,7 +4528,7 @@ async function verifyRunningInstallation(options) {
4222
4528
  }
4223
4529
  }
4224
4530
  const path2 = databasePath(options.dir);
4225
- if (!(0, import_node_fs11.existsSync)(path2)) {
4531
+ if (!(0, import_node_fs12.existsSync)(path2)) {
4226
4532
  if (status.active) {
4227
4533
  throw new OperationsError("database-missing", `hub database is missing after activation: ${path2}`);
4228
4534
  }
@@ -4262,8 +4568,8 @@ async function verifyRunningInstallation(options) {
4262
4568
  let problem = "collector has not sampled";
4263
4569
  while (Date.now() < deadline) {
4264
4570
  try {
4265
- const marker = JSON.parse(readPrivate((0, import_node_path12.join)(options.dir, "run", "collector-health.json")));
4266
- const owner = JSON.parse(readPrivate((0, import_node_path12.join)(options.dir, "collector", "collector-runtime.lock")));
4571
+ const marker = JSON.parse(readPrivate((0, import_node_path13.join)(options.dir, "run", "collector-health.json")));
4572
+ const owner = JSON.parse(readPrivate((0, import_node_path13.join)(options.dir, "collector", "collector-runtime.lock")));
4267
4573
  if (marker.version !== options.expectedVersion) throw new Error("collector version does not match the activated release");
4268
4574
  if (marker.pid !== owner.pid || !Number.isSafeInteger(marker.pid)) throw new Error("collector readiness belongs to another process");
4269
4575
  process.kill(marker.pid, 0);
@@ -4281,7 +4587,7 @@ async function verifyRunningInstallation(options) {
4281
4587
  return { version, health, database };
4282
4588
  }
4283
4589
  async function recoverActivation(options) {
4284
- const runtime = (0, import_node_path12.join)(options.dir, "runtime");
4590
+ const runtime = (0, import_node_path13.join)(options.dir, "runtime");
4285
4591
  const messages = [];
4286
4592
  let releaseLocks = null;
4287
4593
  try {
@@ -4290,12 +4596,16 @@ async function recoverActivation(options) {
4290
4596
  if (status.active) options.deps.service.stop(options.dir, component);
4291
4597
  }
4292
4598
  assertComponentsStopped(options.dir, options.deps, COMPONENT_LIST);
4293
- if ((0, import_node_fs11.existsSync)(databasePath(options.dir))) assertNoActiveHubLock(databasePath(options.dir));
4599
+ if ((0, import_node_fs12.existsSync)(databasePath(options.dir))) assertNoActiveHubLock(databasePath(options.dir));
4294
4600
  releaseLocks = acquireInstallLocks(options.dir, options.deps.acquireLease);
4601
+ messages.push(...undoMachineDataCleanup(machineDataPaths(options.dir), options.machineData?.stagedQueues ?? null));
4602
+ if (options.machineData?.report.warning) {
4603
+ messages.push(`machine-data cleanup was incomplete: ${options.machineData.report.warning}`);
4604
+ }
4295
4605
  if (options.activated) {
4296
4606
  try {
4297
- if ((0, import_node_fs11.existsSync)(runtime)) options.deps.swap(options.dir);
4298
- const restoredVersion = runtimeVersion((0, import_node_path12.join)(options.dir, "runtime"));
4607
+ if ((0, import_node_fs12.existsSync)(runtime)) options.deps.swap(options.dir);
4608
+ const restoredVersion = runtimeVersion((0, import_node_path13.join)(options.dir, "runtime"));
4299
4609
  if (restoredVersion !== options.previousVersion) {
4300
4610
  throw new Error(`restored runtime reports ${restoredVersion}, expected ${options.previousVersion}`);
4301
4611
  }
@@ -4306,10 +4616,11 @@ async function recoverActivation(options) {
4306
4616
  }
4307
4617
  }
4308
4618
  const path2 = databasePath(options.dir);
4309
- if (options.backupPath !== null && (0, import_node_fs11.existsSync)(options.backupPath) && (0, import_node_fs11.existsSync)(path2)) {
4619
+ const transitionClearedHistory = options.machineData?.databaseCleared === true;
4620
+ if (options.backupPath !== null && (0, import_node_fs12.existsSync)(options.backupPath) && (0, import_node_fs12.existsSync)(path2)) {
4310
4621
  try {
4311
- const old = options.deps.inspectRuntime((0, import_node_path12.join)(options.dir, "runtime"), path2);
4312
- if (!old.integrityOk || old.newerSchema || old.ledgerProblem !== null) {
4622
+ const old = options.deps.inspectRuntime((0, import_node_path13.join)(options.dir, "runtime"), path2);
4623
+ if (transitionClearedHistory || !old.integrityOk || old.newerSchema || old.ledgerProblem !== null) {
4313
4624
  const restored = await options.deps.restore({
4314
4625
  input: options.backupPath,
4315
4626
  database: path2,
@@ -4326,16 +4637,16 @@ async function recoverActivation(options) {
4326
4637
  messages.push(`database recovery failed: ${errorMessage(error2)}; pre-upgrade copy is ${options.backupPath}`);
4327
4638
  return messages.join("; ");
4328
4639
  }
4329
- } else if (options.backupPath === null && (0, import_node_fs11.existsSync)(path2)) {
4640
+ } else if (options.backupPath === null && (0, import_node_fs12.existsSync)(path2)) {
4330
4641
  try {
4331
- const old = options.deps.inspectRuntime((0, import_node_path12.join)(options.dir, "runtime"), path2);
4642
+ const old = options.deps.inspectRuntime((0, import_node_path13.join)(options.dir, "runtime"), path2);
4332
4643
  if (!old.integrityOk || old.newerSchema || old.ledgerProblem !== null) {
4333
- const backups = (0, import_node_path12.join)(options.dir, "backups");
4644
+ const backups = (0, import_node_path13.join)(options.dir, "backups");
4334
4645
  ensurePrivateDir(backups);
4335
- const preserved = (0, import_node_path12.join)(backups, `failed-activation-${timestampSlug(options.deps.now())}.sqlite`);
4336
- (0, import_node_fs11.renameSync)(path2, preserved);
4646
+ const preserved = (0, import_node_path13.join)(backups, `failed-activation-${timestampSlug(options.deps.now())}.sqlite`);
4647
+ (0, import_node_fs12.renameSync)(path2, preserved);
4337
4648
  for (const sidecar of [`${path2}-wal`, `${path2}-shm`]) {
4338
- if ((0, import_node_fs11.existsSync)(sidecar)) (0, import_node_fs11.renameSync)(sidecar, `${preserved}${sidecar.slice(path2.length)}`);
4649
+ if ((0, import_node_fs12.existsSync)(sidecar)) (0, import_node_fs12.renameSync)(sidecar, `${preserved}${sidecar.slice(path2.length)}`);
4339
4650
  }
4340
4651
  messages.push(`preserved the failed activation database at ${preserved}; the previous runtime will create a fresh database`);
4341
4652
  } else {
@@ -4379,8 +4690,8 @@ async function upgradeInstallation(options, injected = {}) {
4379
4690
  const deps = dependencies(injected);
4380
4691
  const config = readSetup(options.dir);
4381
4692
  if (!config) throw new OperationsError("not-installed", "No installation found. Run rigscope install first.");
4382
- const runtime = (0, import_node_path12.join)(options.dir, "runtime");
4383
- if (!(0, import_node_fs11.existsSync)(runtime)) {
4693
+ const runtime = (0, import_node_path13.join)(options.dir, "runtime");
4694
+ if (!(0, import_node_fs12.existsSync)(runtime)) {
4384
4695
  throw new OperationsError("not-installed", "Install a managed runtime before upgrading.");
4385
4696
  }
4386
4697
  const currentVersion = runtimeVersion(runtime);
@@ -4393,7 +4704,7 @@ async function upgradeInstallation(options, injected = {}) {
4393
4704
  expectedVersion = runtimeVersion(stage);
4394
4705
  assertRuntimeArtifacts(stage, "staged runtime");
4395
4706
  const path2 = databasePath(options.dir);
4396
- if ((0, import_node_fs11.existsSync)(path2)) {
4707
+ if ((0, import_node_fs12.existsSync)(path2)) {
4397
4708
  const staged = deps.inspectRuntime(stage, path2);
4398
4709
  if (!staged.integrityOk) {
4399
4710
  throw new OperationsError("database-integrity", `database integrity check failed before upgrade: ${path2}`);
@@ -4410,26 +4721,27 @@ async function upgradeInstallation(options, injected = {}) {
4410
4721
  expectedSchema = staged.supportedSchemaVersion;
4411
4722
  }
4412
4723
  } catch (error2) {
4413
- (0, import_node_fs11.rmSync)(stage, { recursive: true, force: true });
4724
+ (0, import_node_fs12.rmSync)(stage, { recursive: true, force: true });
4414
4725
  throw error2;
4415
4726
  }
4416
4727
  const snapshot = snapshotComponents(options.dir, deps);
4417
4728
  const running = snapshot.running;
4418
4729
  let releaseLocks = null;
4419
- const releaseOperation = deps.acquireLease((0, import_node_path12.join)(options.dir, "run", "operation.lock"));
4730
+ const releaseOperation = deps.acquireLease((0, import_node_path13.join)(options.dir, "run", "operation.lock"));
4420
4731
  let backupPath = null;
4421
4732
  let activated = false;
4733
+ let machineData = null;
4422
4734
  try {
4423
4735
  stopComponents(options.dir, deps, running);
4424
4736
  assertComponentsStopped(options.dir, deps, running);
4425
- if (running.includes("hub") || componentEnabled(config, "hub") || (0, import_node_fs11.existsSync)(databasePath(options.dir))) {
4737
+ if (running.includes("hub") || componentEnabled(config, "hub") || (0, import_node_fs12.existsSync)(databasePath(options.dir))) {
4426
4738
  assertNoActiveHubLock(databasePath(options.dir));
4427
4739
  }
4428
4740
  releaseLocks = acquireInstallLocks(options.dir, deps.acquireLease);
4429
- if ((0, import_node_fs11.existsSync)(databasePath(options.dir))) {
4430
- const backups = (0, import_node_path12.join)(options.dir, "backups");
4741
+ if ((0, import_node_fs12.existsSync)(databasePath(options.dir))) {
4742
+ const backups = (0, import_node_path13.join)(options.dir, "backups");
4431
4743
  ensurePrivateDir(backups);
4432
- backupPath = (0, import_node_path12.join)(
4744
+ backupPath = (0, import_node_path13.join)(
4433
4745
  backups,
4434
4746
  `pre-upgrade-${currentVersion}-to-${expectedVersion}-${timestampSlug(deps.now())}.sqlite`
4435
4747
  );
@@ -4458,6 +4770,9 @@ async function upgradeInstallation(options, injected = {}) {
4458
4770
  }
4459
4771
  deps.activate(options.dir, stage);
4460
4772
  activated = true;
4773
+ machineData = performMachineDataCleanup(options.dir, machineDataPaths(options.dir), deps.now, {
4774
+ clear: deps.clearHistory
4775
+ });
4461
4776
  releaseLocks();
4462
4777
  releaseLocks = null;
4463
4778
  for (const component of running) deps.service.start(options.dir, component);
@@ -4473,7 +4788,7 @@ async function upgradeInstallation(options, injected = {}) {
4473
4788
  });
4474
4789
  restoreStartup(options.dir, deps, snapshot.enabled, false);
4475
4790
  const now = new Date(deps.now()).toISOString();
4476
- if (backupPath !== null && (0, import_node_fs11.existsSync)(backupPath)) {
4791
+ if (backupPath !== null && (0, import_node_fs12.existsSync)(backupPath)) {
4477
4792
  const info = deps.inspect(backupPath);
4478
4793
  const record = {
4479
4794
  path: backupPath,
@@ -4497,13 +4812,15 @@ async function upgradeInstallation(options, injected = {}) {
4497
4812
  activatedAt: now
4498
4813
  }
4499
4814
  });
4815
+ completeMachineDataCleanup(options.dir, machineData, expectedVersion);
4500
4816
  return {
4501
4817
  version: expectedVersion,
4502
4818
  previousVersion: currentVersion,
4503
4819
  backupPath,
4504
4820
  schemaVersion: verification.database?.schemaVersion ?? expectedSchema,
4505
4821
  serviceRestarted: running.length > 0,
4506
- verification
4822
+ verification,
4823
+ machineHistory: machineData.report
4507
4824
  };
4508
4825
  } catch (error2) {
4509
4826
  releaseLocks?.();
@@ -4513,6 +4830,7 @@ async function upgradeInstallation(options, injected = {}) {
4513
4830
  config,
4514
4831
  previousVersion: currentVersion,
4515
4832
  backupPath,
4833
+ machineData,
4516
4834
  running,
4517
4835
  startup: snapshot.enabled,
4518
4836
  activated,
@@ -4526,13 +4844,13 @@ async function upgradeInstallation(options, injected = {}) {
4526
4844
  } finally {
4527
4845
  releaseLocks?.();
4528
4846
  releaseOperation();
4529
- if ((0, import_node_fs11.existsSync)(stage)) (0, import_node_fs11.rmSync)(stage, { recursive: true, force: true });
4847
+ if ((0, import_node_fs12.existsSync)(stage)) (0, import_node_fs12.rmSync)(stage, { recursive: true, force: true });
4530
4848
  }
4531
4849
  }
4532
4850
  function rollbackBackup(options) {
4533
4851
  if (options.explicitBackup !== void 0) {
4534
4852
  const path2 = options.explicitBackup;
4535
- if (!(0, import_node_fs11.existsSync)(path2)) {
4853
+ if (!(0, import_node_fs12.existsSync)(path2)) {
4536
4854
  throw new OperationsError("backup-missing", `rollback backup does not exist: ${path2}`);
4537
4855
  }
4538
4856
  const info2 = options.deps.inspect(path2);
@@ -4551,7 +4869,7 @@ function rollbackBackup(options) {
4551
4869
  `database schema ${options.previousSupport.schemaVersion} is newer than previous runtime ${options.previousVersion} (schema ${options.previousSupport.supportedSchemaVersion}); no paired pre-upgrade backup is recorded for ${options.currentVersion}. Re-run with --backup PATH naming a copy that matches schema ${options.previousSupport.supportedSchemaVersion} or older`
4552
4870
  );
4553
4871
  }
4554
- if (!(0, import_node_fs11.existsSync)(recorded.path)) {
4872
+ if (!(0, import_node_fs12.existsSync)(recorded.path)) {
4555
4873
  throw new OperationsError(
4556
4874
  "backup-missing",
4557
4875
  `paired pre-upgrade backup for ${options.currentVersion} is missing: ${recorded.path}`
@@ -4573,10 +4891,10 @@ async function rollbackInstallation(options, injected = {}) {
4573
4891
  const deps = dependencies(injected);
4574
4892
  const config = readSetup(options.dir);
4575
4893
  if (!config) throw new OperationsError("not-installed", "No installation found. Run rigscope install first.");
4576
- const runtime = (0, import_node_path12.join)(options.dir, "runtime");
4577
- const previousRuntime = (0, import_node_path12.join)(options.dir, "runtime-previous");
4578
- if (!(0, import_node_fs11.existsSync)(runtime)) throw new OperationsError("not-installed", "No active runtime is installed.");
4579
- if (!(0, import_node_fs11.existsSync)(previousRuntime)) {
4894
+ const runtime = (0, import_node_path13.join)(options.dir, "runtime");
4895
+ const previousRuntime = (0, import_node_path13.join)(options.dir, "runtime-previous");
4896
+ if (!(0, import_node_fs12.existsSync)(runtime)) throw new OperationsError("not-installed", "No active runtime is installed.");
4897
+ if (!(0, import_node_fs12.existsSync)(previousRuntime)) {
4580
4898
  throw new OperationsError("no-previous-release", "No previous runtime is available for rollback.");
4581
4899
  }
4582
4900
  const currentVersion = runtimeVersion(runtime);
@@ -4587,19 +4905,19 @@ async function rollbackInstallation(options, injected = {}) {
4587
4905
  const snapshot = snapshotComponents(options.dir, deps);
4588
4906
  const running = snapshot.running;
4589
4907
  let releaseLocks = null;
4590
- const releaseOperation = deps.acquireLease((0, import_node_path12.join)(options.dir, "run", "operation.lock"));
4908
+ const releaseOperation = deps.acquireLease((0, import_node_path13.join)(options.dir, "run", "operation.lock"));
4591
4909
  let restoredBackup = null;
4592
4910
  let preRestoreBackup = null;
4593
4911
  let swapped = false;
4594
4912
  try {
4595
4913
  stopComponents(options.dir, deps, running);
4596
4914
  assertComponentsStopped(options.dir, deps, running);
4597
- if (running.includes("hub") || componentEnabled(config, "hub") || (0, import_node_fs11.existsSync)(databasePath(options.dir))) {
4915
+ if (running.includes("hub") || componentEnabled(config, "hub") || (0, import_node_fs12.existsSync)(databasePath(options.dir))) {
4598
4916
  assertNoActiveHubLock(databasePath(options.dir));
4599
4917
  }
4600
4918
  releaseLocks = acquireInstallLocks(options.dir, deps.acquireLease);
4601
4919
  const path2 = databasePath(options.dir);
4602
- if ((0, import_node_fs11.existsSync)(path2)) {
4920
+ if ((0, import_node_fs12.existsSync)(path2)) {
4603
4921
  const previousSupport = deps.inspectRuntime(previousRuntime, path2);
4604
4922
  if (!previousSupport.integrityOk || previousSupport.newerSchema || previousSupport.ledgerProblem !== null) {
4605
4923
  const backup3 = rollbackBackup({
@@ -4670,7 +4988,7 @@ async function rollbackInstallation(options, injected = {}) {
4670
4988
  if (status.active) deps.service.stop(options.dir, component);
4671
4989
  }
4672
4990
  assertComponentsStopped(options.dir, deps, COMPONENT_LIST);
4673
- if ((0, import_node_fs11.existsSync)(databasePath(options.dir))) assertNoActiveHubLock(databasePath(options.dir));
4991
+ if ((0, import_node_fs12.existsSync)(databasePath(options.dir))) assertNoActiveHubLock(databasePath(options.dir));
4674
4992
  releaseLocks = acquireInstallLocks(options.dir, deps.acquireLease);
4675
4993
  } catch (stopError) {
4676
4994
  throw new OperationsError("rollback-failed", `rollback failed: ${errorMessage(error2)}. Recovery refused while runtime ownership is uncertain: ${errorMessage(stopError)}`, { cause: error2 });
@@ -4684,7 +5002,7 @@ async function rollbackInstallation(options, injected = {}) {
4684
5002
  safeToRestart = false;
4685
5003
  }
4686
5004
  }
4687
- if (preRestoreBackup !== null && (0, import_node_fs11.existsSync)(preRestoreBackup)) {
5005
+ if (preRestoreBackup !== null && (0, import_node_fs12.existsSync)(preRestoreBackup)) {
4688
5006
  try {
4689
5007
  await deps.restore({
4690
5008
  input: preRestoreBackup,
@@ -4699,7 +5017,7 @@ async function rollbackInstallation(options, injected = {}) {
4699
5017
  safeToRestart = false;
4700
5018
  }
4701
5019
  }
4702
- if (safeToRestart && running.includes("hub") && (0, import_node_fs11.existsSync)(databasePath(options.dir))) {
5020
+ if (safeToRestart && running.includes("hub") && (0, import_node_fs12.existsSync)(databasePath(options.dir))) {
4703
5021
  try {
4704
5022
  const info = deps.inspectRuntime(runtime, databasePath(options.dir));
4705
5023
  if (!info.integrityOk || info.newerSchema || info.ledgerProblem !== null) throw new Error("runtime/database compatibility is not established");
@@ -4746,17 +5064,17 @@ async function rollbackInstallation(options, injected = {}) {
4746
5064
 
4747
5065
  // src/cli/lifecycle.ts
4748
5066
  function collectorEnrolled(dir) {
4749
- const path2 = (0, import_node_path13.join)(dir, "collector", "state.json");
4750
- if (!(0, import_node_fs12.existsSync)(path2)) return false;
5067
+ const path2 = (0, import_node_path14.join)(dir, "collector", "state.json");
5068
+ if (!(0, import_node_fs13.existsSync)(path2)) return false;
4751
5069
  const state = JSON.parse(readPrivate(path2));
4752
5070
  return Array.isArray(state.destinations) && state.destinations.length > 0;
4753
5071
  }
4754
5072
  var AWAITING_CONNECTION = "Collector is awaiting connection. Run rigscope connect https://your-hub first.";
4755
5073
  function assertNoCompetingRuntime(dir, component) {
4756
5074
  const locks = [
4757
- (0, import_node_path13.join)(dir, "run", component === "hub" ? "hub.lock" : "collector.lock"),
4758
- (0, import_node_path13.join)(dir, "run", "rigscope.lock"),
4759
- ...component === "collector" ? [(0, import_node_path13.join)(dir, "collector", "collector-runtime.lock")] : []
5075
+ (0, import_node_path14.join)(dir, "run", component === "hub" ? "hub.lock" : "collector.lock"),
5076
+ (0, import_node_path14.join)(dir, "run", "rigscope.lock"),
5077
+ ...component === "collector" ? [(0, import_node_path14.join)(dir, "collector", "collector-runtime.lock")] : []
4760
5078
  ];
4761
5079
  const releases = [];
4762
5080
  try {
@@ -4775,11 +5093,11 @@ function assertNoCompetingRuntime(dir, component) {
4775
5093
  async function startInstallation(dir, component, injected = {}) {
4776
5094
  const config = readSetup(dir);
4777
5095
  if (!config) throw new Error("No background service installed. Run rigscope install first.");
4778
- if (!(0, import_node_fs12.existsSync)((0, import_node_path13.join)(dir, "runtime"))) throw new Error("No background service installed. Run rigscope install first.");
5096
+ if (!(0, import_node_fs13.existsSync)((0, import_node_path14.join)(dir, "runtime"))) throw new Error("No background service installed. Run rigscope install first.");
4779
5097
  const selected = component ? [component] : enabledComponents(config);
4780
5098
  if (selected.length === 0) throw new Error("No enabled components. Run rigscope enable hub or rigscope enable collector.");
4781
5099
  for (const target of selected) {
4782
- if (!(0, import_node_fs12.existsSync)(unitPath(dir, target))) throw new Error("No background service installed. Run rigscope install first.");
5100
+ if (!(0, import_node_fs13.existsSync)(unitPath(dir, target))) throw new Error("No background service installed. Run rigscope install first.");
4783
5101
  }
4784
5102
  const status = injected.status ?? serviceStatus;
4785
5103
  const start = injected.start ?? systemctl;
@@ -4807,7 +5125,7 @@ async function startInstallation(dir, component, injected = {}) {
4807
5125
  await (injected.verify ?? verifyRunningInstallation)({
4808
5126
  dir,
4809
5127
  config,
4810
- expectedVersion: runtimeVersion((0, import_node_path13.join)(dir, "runtime")),
5128
+ expectedVersion: runtimeVersion((0, import_node_path14.join)(dir, "runtime")),
4811
5129
  components: started,
4812
5130
  requireActive: started
4813
5131
  });
@@ -4891,11 +5209,11 @@ function printHelp(message, options = {}) {
4891
5209
 
4892
5210
  // src/cli/mutation.ts
4893
5211
  async function resumeComponent(dir, component) {
4894
- if ((0, import_node_fs13.existsSync)((0, import_node_path14.join)(dir, "runtime"))) return startInstallation(dir, component);
4895
- const run = (0, import_node_path14.join)(dir, "run");
4896
- (0, import_node_fs13.mkdirSync)(run, { recursive: true, mode: 448 });
4897
- const log = (0, import_node_path14.join)(run, `${component}.log`);
4898
- const fd = (0, import_node_fs13.openSync)(log, "a", 384);
5212
+ if ((0, import_node_fs14.existsSync)((0, import_node_path15.join)(dir, "runtime"))) return startInstallation(dir, component);
5213
+ const run = (0, import_node_path15.join)(dir, "run");
5214
+ (0, import_node_fs14.mkdirSync)(run, { recursive: true, mode: 448 });
5215
+ const log = (0, import_node_path15.join)(run, `${component}.log`);
5216
+ const fd = (0, import_node_fs14.openSync)(log, "a", 384);
4899
5217
  let child;
4900
5218
  try {
4901
5219
  child = (0, import_node_child_process3.spawn)(process.execPath, [process.argv[1], "run", component, "--state-dir", dir], {
@@ -4904,7 +5222,7 @@ async function resumeComponent(dir, component) {
4904
5222
  env: process.env
4905
5223
  });
4906
5224
  } finally {
4907
- (0, import_node_fs13.closeSync)(fd);
5225
+ (0, import_node_fs14.closeSync)(fd);
4908
5226
  }
4909
5227
  let failure;
4910
5228
  child.on("error", (error2) => {
@@ -4915,7 +5233,7 @@ async function resumeComponent(dir, component) {
4915
5233
  while (Date.now() < deadline) {
4916
5234
  if (failure || child.exitCode !== null || child.signalCode !== null) break;
4917
5235
  try {
4918
- const owner = JSON.parse((0, import_node_fs13.readFileSync)((0, import_node_path14.join)(run, `${component}.lock`), "utf8"));
5236
+ const owner = JSON.parse((0, import_node_fs14.readFileSync)((0, import_node_path15.join)(run, `${component}.lock`), "utf8"));
4919
5237
  if (owner.pid === child.pid) return "started";
4920
5238
  } catch {
4921
5239
  }
@@ -4971,12 +5289,20 @@ async function prepareMutation(dir, selected, options, deps = {}) {
4971
5289
 
4972
5290
  // src/cli/runtime.ts
4973
5291
  var import_node_child_process4 = require("node:child_process");
4974
- var import_node_path15 = require("node:path");
5292
+ var import_node_path16 = require("node:path");
5293
+ function collectorQueueModeEnvironment(env) {
5294
+ const raw = env["COLLECTOR_QUEUE_MODE"]?.trim();
5295
+ if (raw === void 0 || raw === "") return {};
5296
+ const issues = [];
5297
+ const queueMode = parseQueueMode(env, issues);
5298
+ if (issues.length > 0) throw new Error(issues.join("\n"));
5299
+ return { COLLECTOR_QUEUE_MODE: queueMode };
5300
+ }
4975
5301
  function componentLeasePath(dir, component) {
4976
- return (0, import_node_path15.join)(dir, "run", `${component}.lock`);
5302
+ return (0, import_node_path16.join)(dir, "run", `${component}.lock`);
4977
5303
  }
4978
5304
  function combinedLeasePath(dir) {
4979
- return (0, import_node_path15.join)(dir, "run", "rigscope.lock");
5305
+ return (0, import_node_path16.join)(dir, "run", "rigscope.lock");
4980
5306
  }
4981
5307
  function label(component) {
4982
5308
  return component === "hub" ? "Hub" : "Collector";
@@ -4993,6 +5319,7 @@ async function runInstallation(root, dir, component) {
4993
5319
  console.error("No components are enabled. Run rigscope enable hub or rigscope enable collector.");
4994
5320
  return 0;
4995
5321
  }
5322
+ const collectorEnvironment = selected.includes("collector") ? collectorQueueModeEnvironment(process.env) : {};
4996
5323
  if (selected.includes("collector") && !collectorHasDestinations(dir)) {
4997
5324
  console.log("Collector awaiting connection. Run rigscope connect https://your-hub.");
4998
5325
  const remaining = selected.filter((role) => role !== "collector");
@@ -5032,13 +5359,14 @@ async function runInstallation(root, dir, component) {
5032
5359
  process.on("SIGTERM", signal);
5033
5360
  process.on("SIGINT", signal);
5034
5361
  function launch(role) {
5035
- const child = (0, import_node_child_process4.spawn)(process.execPath, [(0, import_node_path15.join)(root, "dist", `${role}.cjs`)], { cwd: root, stdio: "inherit", env: {
5362
+ const child = (0, import_node_child_process4.spawn)(process.execPath, [(0, import_node_path16.join)(root, "dist", `${role}.cjs`)], { cwd: root, stdio: "inherit", env: {
5036
5363
  ...environment,
5037
5364
  NODE_ENV: "production",
5038
5365
  ...role === "hub" ? hubEnvironment(dir, config) : {
5039
5366
  COLLECTOR_MODE: "real",
5040
- COLLECTOR_STATE_FILE: (0, import_node_path15.join)(dir, "collector", "state.json"),
5041
- COLLECTOR_HEALTH_FILE: (0, import_node_path15.join)(dir, "run", "collector-health.json")
5367
+ COLLECTOR_STATE_FILE: (0, import_node_path16.join)(dir, "collector", "state.json"),
5368
+ COLLECTOR_HEALTH_FILE: (0, import_node_path16.join)(dir, "run", "collector-health.json"),
5369
+ ...collectorEnvironment
5042
5370
  }
5043
5371
  } });
5044
5372
  children.add(child);
@@ -5065,8 +5393,8 @@ async function runInstallation(root, dir, component) {
5065
5393
  }
5066
5394
 
5067
5395
  // src/cli/doctor.ts
5068
- var import_node_fs14 = require("node:fs");
5069
- var import_node_path16 = require("node:path");
5396
+ var import_node_fs15 = require("node:fs");
5397
+ var import_node_path17 = require("node:path");
5070
5398
  var DEFAULT_DEPENDENCIES = {
5071
5399
  serviceStatus,
5072
5400
  probeHealth,
@@ -5076,7 +5404,7 @@ var DEFAULT_DEPENDENCIES = {
5076
5404
  inspectDatabase,
5077
5405
  diskFree: (path2) => {
5078
5406
  try {
5079
- const stats = (0, import_node_fs14.statfsSync)(path2);
5407
+ const stats = (0, import_node_fs15.statfsSync)(path2);
5080
5408
  return { free: Number(stats.bavail) * Number(stats.bsize), total: Number(stats.blocks) * Number(stats.bsize) };
5081
5409
  } catch {
5082
5410
  return null;
@@ -5085,13 +5413,13 @@ var DEFAULT_DEPENDENCIES = {
5085
5413
  sensorReadable: (paths) => paths.filter((path2) => {
5086
5414
  let fd;
5087
5415
  try {
5088
- fd = (0, import_node_fs14.openSync)(path2, "r");
5089
- (0, import_node_fs14.readSync)(fd, Buffer.alloc(1), 0, 1, 0);
5416
+ fd = (0, import_node_fs15.openSync)(path2, "r");
5417
+ (0, import_node_fs15.readSync)(fd, Buffer.alloc(1), 0, 1, 0);
5090
5418
  return true;
5091
5419
  } catch {
5092
5420
  return false;
5093
5421
  } finally {
5094
- if (fd !== void 0) (0, import_node_fs14.closeSync)(fd);
5422
+ if (fd !== void 0) (0, import_node_fs15.closeSync)(fd);
5095
5423
  }
5096
5424
  })
5097
5425
  };
@@ -5153,10 +5481,10 @@ async function diagnoseInstallation(dir, injected = {}) {
5153
5481
  }
5154
5482
  for (const [id, label3, path2] of [
5155
5483
  ["state-dir", "State directory", dir],
5156
- ["config-mode", "Configuration permissions", (0, import_node_path16.join)(dir, "config.json")]
5484
+ ["config-mode", "Configuration permissions", (0, import_node_path17.join)(dir, "config.json")]
5157
5485
  ]) {
5158
5486
  try {
5159
- const stat = (0, import_node_fs14.lstatSync)(path2);
5487
+ const stat = (0, import_node_fs15.lstatSync)(path2);
5160
5488
  const privateEntry = id === "state-dir" ? stat.isDirectory() : stat.isFile();
5161
5489
  const privateMode = (stat.mode & 63) === 0;
5162
5490
  add({
@@ -5171,7 +5499,7 @@ async function diagnoseInstallation(dir, injected = {}) {
5171
5499
  }
5172
5500
  }
5173
5501
  let runtime = null;
5174
- if (!(0, import_node_fs14.existsSync)((0, import_node_path16.join)(dir, "runtime"))) {
5502
+ if (!(0, import_node_fs15.existsSync)((0, import_node_path17.join)(dir, "runtime"))) {
5175
5503
  add({
5176
5504
  id: "runtime",
5177
5505
  label: "Runtime",
@@ -5181,9 +5509,9 @@ async function diagnoseInstallation(dir, injected = {}) {
5181
5509
  });
5182
5510
  } else {
5183
5511
  try {
5184
- assertRuntimeArtifacts((0, import_node_path16.join)(dir, "runtime"), "runtime");
5185
- const version2 = runtimeVersion((0, import_node_path16.join)(dir, "runtime"));
5186
- runtime = (0, import_node_path16.join)(dir, "runtime");
5512
+ assertRuntimeArtifacts((0, import_node_path17.join)(dir, "runtime"), "runtime");
5513
+ const version2 = runtimeVersion((0, import_node_path17.join)(dir, "runtime"));
5514
+ runtime = (0, import_node_path17.join)(dir, "runtime");
5187
5515
  add({ id: "runtime", label: "Runtime", status: "ok", detail: `rigscope ${version2}` });
5188
5516
  } catch (error2) {
5189
5517
  add({
@@ -5195,9 +5523,9 @@ async function diagnoseInstallation(dir, injected = {}) {
5195
5523
  });
5196
5524
  }
5197
5525
  }
5198
- if ((0, import_node_fs14.existsSync)((0, import_node_path16.join)(dir, "runtime-previous"))) {
5526
+ if ((0, import_node_fs15.existsSync)((0, import_node_path17.join)(dir, "runtime-previous"))) {
5199
5527
  try {
5200
- add({ id: "previous-runtime", label: "Previous runtime", status: "ok", detail: `rigscope ${runtimeVersion((0, import_node_path16.join)(dir, "runtime-previous"))}` });
5528
+ add({ id: "previous-runtime", label: "Previous runtime", status: "ok", detail: `rigscope ${runtimeVersion((0, import_node_path17.join)(dir, "runtime-previous"))}` });
5201
5529
  } catch (error2) {
5202
5530
  add({ id: "previous-runtime", label: "Previous runtime", status: "warn", detail: errorText2(error2) });
5203
5531
  }
@@ -5259,7 +5587,7 @@ async function diagnoseInstallation(dir, injected = {}) {
5259
5587
  const collectorActive = services.collector.active;
5260
5588
  if (hubEnabled || hubActive) {
5261
5589
  const path2 = databasePath(dir);
5262
- if (!(0, import_node_fs14.existsSync)(path2)) {
5590
+ if (!(0, import_node_fs15.existsSync)(path2)) {
5263
5591
  add({
5264
5592
  id: "database",
5265
5593
  label: "Database",
@@ -5288,7 +5616,7 @@ async function diagnoseInstallation(dir, injected = {}) {
5288
5616
  });
5289
5617
  }
5290
5618
  }
5291
- const space = deps.diskFree((0, import_node_path16.join)(dir, "hub"));
5619
+ const space = deps.diskFree((0, import_node_path17.join)(dir, "hub"));
5292
5620
  if (space !== null) {
5293
5621
  const free = space.free;
5294
5622
  add({
@@ -5302,9 +5630,9 @@ async function diagnoseInstallation(dir, injected = {}) {
5302
5630
  }
5303
5631
  if (hubEnabled || hubActive) {
5304
5632
  if (config.origin) {
5305
- const socket = config.socket ?? (0, import_node_path16.join)(dir, "run", "hub.sock");
5633
+ const socket = config.socket ?? (0, import_node_path17.join)(dir, "run", "hub.sock");
5306
5634
  try {
5307
- const stat = (0, import_node_fs14.lstatSync)(socket);
5635
+ const stat = (0, import_node_fs15.lstatSync)(socket);
5308
5636
  add({
5309
5637
  id: "socket",
5310
5638
  label: "Unix socket",
@@ -5346,7 +5674,7 @@ async function diagnoseInstallation(dir, injected = {}) {
5346
5674
  }
5347
5675
  if (hubActive) {
5348
5676
  try {
5349
- const target = healthTarget({ component: "hub", port: config.port, origin: config.origin, socket: config.socket ?? (0, import_node_path16.join)(dir, "run", "hub.sock") });
5677
+ const target = healthTarget({ component: "hub", port: config.port, origin: config.origin, socket: config.socket ?? (0, import_node_path17.join)(dir, "run", "hub.sock") });
5350
5678
  const health = await deps.probeHealth(target, 3e3);
5351
5679
  const expected = runtime ? runtimeVersion(runtime) : null;
5352
5680
  const reported = typeof health.body?.version === "string" ? health.body.version : null;
@@ -5364,7 +5692,7 @@ async function diagnoseInstallation(dir, injected = {}) {
5364
5692
  }
5365
5693
  }
5366
5694
  if (collectorEnabled || collectorActive) {
5367
- const statePath = (0, import_node_path16.join)(dir, "collector", "state.json");
5695
+ const statePath = (0, import_node_path17.join)(dir, "collector", "state.json");
5368
5696
  try {
5369
5697
  const raw = readPrivate(statePath);
5370
5698
  const state = JSON.parse(raw);
@@ -5439,17 +5767,17 @@ async function diagnoseInstallation(dir, injected = {}) {
5439
5767
  function hubEnvironment2(dir, config) {
5440
5768
  return {
5441
5769
  ...!config.origin ? { HUB_HOST: "127.0.0.1", HUB_PORT: String(config.port) } : {},
5442
- HUB_STATE_FILE: (0, import_node_path16.join)(dir, "hub", "enrollment.json"),
5770
+ HUB_STATE_FILE: (0, import_node_path17.join)(dir, "hub", "enrollment.json"),
5443
5771
  HUB_DATABASE_PATH: databasePath(dir),
5444
5772
  HUB_BROWSER_USERS: JSON.stringify(config.users),
5445
5773
  HUB_COLLECTORS: JSON.stringify(config.collectors),
5446
5774
  ...config.maxCollectorConnections === void 0 ? {} : { HUB_MAX_COLLECTOR_CONNECTIONS: String(config.maxCollectorConnections) },
5447
- ...config.origin ? { HUB_PUBLIC_ORIGIN: config.origin, HUB_SOCKET_PATH: config.socket ?? (0, import_node_path16.join)(dir, "run", "hub.sock") } : {}
5775
+ ...config.origin ? { HUB_PUBLIC_ORIGIN: config.origin, HUB_SOCKET_PATH: config.socket ?? (0, import_node_path17.join)(dir, "run", "hub.sock") } : {}
5448
5776
  };
5449
5777
  }
5450
5778
  function collectorAwaiting(dir) {
5451
5779
  try {
5452
- const state = JSON.parse(readPrivate((0, import_node_path16.join)(dir, "collector", "state.json")));
5780
+ const state = JSON.parse(readPrivate((0, import_node_path17.join)(dir, "collector", "state.json")));
5453
5781
  return !Array.isArray(state.destinations) || state.destinations.length === 0;
5454
5782
  } catch (error2) {
5455
5783
  return error2.code === "ENOENT";
@@ -5461,13 +5789,13 @@ function powerCounterPaths() {
5461
5789
  if (depth > 3 || paths.length >= 64) return;
5462
5790
  let entries;
5463
5791
  try {
5464
- entries = (0, import_node_fs14.readdirSync)(directory, { withFileTypes: true });
5792
+ entries = (0, import_node_fs15.readdirSync)(directory, { withFileTypes: true });
5465
5793
  } catch {
5466
5794
  return;
5467
5795
  }
5468
5796
  for (const entry2 of entries) {
5469
5797
  if (paths.length >= 64) return;
5470
- const path2 = (0, import_node_path16.join)(directory, entry2.name);
5798
+ const path2 = (0, import_node_path17.join)(directory, entry2.name);
5471
5799
  if (entry2.name === "energy_uj") {
5472
5800
  paths.push(path2);
5473
5801
  continue;
@@ -5511,8 +5839,8 @@ function errorText2(error2) {
5511
5839
  }
5512
5840
 
5513
5841
  // src/operations/recovery.ts
5514
- var import_node_fs15 = require("node:fs");
5515
- var import_node_path17 = require("node:path");
5842
+ var import_node_fs16 = require("node:fs");
5843
+ var import_node_path18 = require("node:path");
5516
5844
  var import_node_sqlite3 = require("node:sqlite");
5517
5845
  var MAX_AUDIT_EVENTS = 1e3;
5518
5846
  var AUDIT_ACTOR = "local-recovery";
@@ -5527,13 +5855,13 @@ async function recoverAdminPassword(options) {
5527
5855
  if (typeof options.password !== "string" || options.password.length === 0 || options.password.length > MAX_BROWSER_PASSWORD_CHARS) {
5528
5856
  throw new OperationsError("invalid-password", `password must be 1..${MAX_BROWSER_PASSWORD_CHARS} characters`);
5529
5857
  }
5530
- if (!(0, import_node_fs15.existsSync)(database)) {
5858
+ if (!(0, import_node_fs16.existsSync)(database)) {
5531
5859
  throw new OperationsError("missing-database", `hub database does not exist: ${database}`);
5532
5860
  }
5533
5861
  assertPrivateRegularFile(database, "database");
5534
- const ownLock = (0, import_node_path17.resolve)(`${database}.hub.lock`);
5862
+ const ownLock = (0, import_node_path18.resolve)(`${database}.hub.lock`);
5535
5863
  const otherLock = findHubLock(database);
5536
- if (otherLock !== null && (0, import_node_path17.resolve)(otherLock) !== ownLock) {
5864
+ if (otherLock !== null && (0, import_node_path18.resolve)(otherLock) !== ownLock) {
5537
5865
  throw new OperationsError(
5538
5866
  "hub-active",
5539
5867
  `a hub lock exists at ${otherLock}; stop the hub and inspect that lock before recovery`
@@ -5631,10 +5959,10 @@ async function recoverAdminPassword(options) {
5631
5959
  }
5632
5960
 
5633
5961
  // src/cli/system.ts
5634
- var import_node_fs16 = require("node:fs");
5962
+ var import_node_fs17 = require("node:fs");
5635
5963
  var import_node_child_process5 = require("node:child_process");
5636
- var import_node_crypto11 = require("node:crypto");
5637
- var import_node_path18 = require("node:path");
5964
+ var import_node_crypto12 = require("node:crypto");
5965
+ var import_node_path19 = require("node:path");
5638
5966
  var import_node_sqlite4 = require("node:sqlite");
5639
5967
  var import_node_util = require("node:util");
5640
5968
  var SYSTEM_SCOPE = "system";
@@ -5744,24 +6072,24 @@ function createSystemHost(overrides = {}) {
5744
6072
  }
5745
6073
  function defaultSystemPaths(overrides = {}) {
5746
6074
  return {
5747
- prefix: (0, import_node_path18.resolve)(overrides.prefix ?? "/opt/rigscope"),
5748
- configDir: (0, import_node_path18.resolve)(overrides.configDir ?? "/etc/rigscope"),
5749
- stateDir: (0, import_node_path18.resolve)(overrides.stateDir ?? "/var/lib/rigscope"),
5750
- unitsDir: (0, import_node_path18.resolve)(overrides.unitsDir ?? "/etc/systemd/system"),
5751
- sysusersDir: (0, import_node_path18.resolve)(overrides.sysusersDir ?? "/etc/sysusers.d"),
5752
- udevRulesDir: (0, import_node_path18.resolve)(overrides.udevRulesDir ?? "/etc/udev/rules.d"),
5753
- powercapRoot: (0, import_node_path18.resolve)(overrides.powercapRoot ?? "/sys/class/powercap")
6075
+ prefix: (0, import_node_path19.resolve)(overrides.prefix ?? "/opt/rigscope"),
6076
+ configDir: (0, import_node_path19.resolve)(overrides.configDir ?? "/etc/rigscope"),
6077
+ stateDir: (0, import_node_path19.resolve)(overrides.stateDir ?? "/var/lib/rigscope"),
6078
+ unitsDir: (0, import_node_path19.resolve)(overrides.unitsDir ?? "/etc/systemd/system"),
6079
+ sysusersDir: (0, import_node_path19.resolve)(overrides.sysusersDir ?? "/etc/sysusers.d"),
6080
+ udevRulesDir: (0, import_node_path19.resolve)(overrides.udevRulesDir ?? "/etc/udev/rules.d"),
6081
+ powercapRoot: (0, import_node_path19.resolve)(overrides.powercapRoot ?? "/sys/class/powercap")
5754
6082
  };
5755
6083
  }
5756
6084
  function unitNamesForComponents(components) {
5757
6085
  return COMPONENTS3.filter((component) => componentSet(components)[component]).map(unitFor);
5758
6086
  }
5759
6087
  function isWithin(parent, candidate) {
5760
- const rel = (0, import_node_path18.relative)((0, import_node_path18.resolve)(parent), (0, import_node_path18.resolve)(candidate));
5761
- return rel === "" || rel !== ".." && !rel.startsWith(`..${import_node_path18.sep}`) && !(0, import_node_path18.isAbsolute)(rel);
6088
+ const rel = (0, import_node_path19.relative)((0, import_node_path19.resolve)(parent), (0, import_node_path19.resolve)(candidate));
6089
+ return rel === "" || rel !== ".." && !rel.startsWith(`..${import_node_path19.sep}`) && !(0, import_node_path19.isAbsolute)(rel);
5762
6090
  }
5763
6091
  function ensureAbsolute(path2, label3) {
5764
- const absolute = (0, import_node_path18.resolve)(path2);
6092
+ const absolute = (0, import_node_path19.resolve)(path2);
5765
6093
  if (/[\0\r\n]/.test(absolute)) throw new SystemError("unsafe-path", `${label3} contains control characters`);
5766
6094
  if (absolute === "/") throw new SystemError("unsafe-path", `${label3} must not be the filesystem root`);
5767
6095
  for (const forbidden of ["/home", "/root", "/proc", "/sys", "/dev", "/boot"]) {
@@ -5775,7 +6103,7 @@ function validateSystemPaths(input = {}) {
5775
6103
  const paths = defaultSystemPaths(input);
5776
6104
  for (const [label3, value] of Object.entries(paths)) {
5777
6105
  if (label3 === "powercapRoot") {
5778
- if (!(0, import_node_path18.isAbsolute)(value) || /[\0\r\n]/.test(value) || (0, import_node_path18.resolve)(value) === "/") {
6106
+ if (!(0, import_node_path19.isAbsolute)(value) || /[\0\r\n]/.test(value) || (0, import_node_path19.resolve)(value) === "/") {
5779
6107
  throw new SystemError("unsafe-path", `powercap root is unsafe: ${value}`);
5780
6108
  }
5781
6109
  } else {
@@ -5831,18 +6159,18 @@ function runMaybe(host, command, args, options) {
5831
6159
  return host.run(command, args, options);
5832
6160
  }
5833
6161
  function sha256File2(path2) {
5834
- return (0, import_node_crypto11.createHash)("sha256").update((0, import_node_fs16.readFileSync)(path2)).digest("hex");
6162
+ return (0, import_node_crypto12.createHash)("sha256").update((0, import_node_fs17.readFileSync)(path2)).digest("hex");
5835
6163
  }
5836
6164
  function atomicWrite(path2, content, mode) {
5837
- (0, import_node_fs16.mkdirSync)((0, import_node_path18.dirname)(path2), { recursive: true, mode: 493 });
5838
- const temporary = (0, import_node_path18.join)((0, import_node_path18.dirname)(path2), `.${(0, import_node_path18.basename)(path2)}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
6165
+ (0, import_node_fs17.mkdirSync)((0, import_node_path19.dirname)(path2), { recursive: true, mode: 493 });
6166
+ const temporary = (0, import_node_path19.join)((0, import_node_path19.dirname)(path2), `.${(0, import_node_path19.basename)(path2)}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
5839
6167
  try {
5840
- (0, import_node_fs16.writeFileSync)(temporary, content, { mode });
5841
- (0, import_node_fs16.chmodSync)(temporary, mode);
5842
- (0, import_node_fs16.renameSync)(temporary, path2);
6168
+ (0, import_node_fs17.writeFileSync)(temporary, content, { mode });
6169
+ (0, import_node_fs17.chmodSync)(temporary, mode);
6170
+ (0, import_node_fs17.renameSync)(temporary, path2);
5843
6171
  } catch (error2) {
5844
6172
  try {
5845
- (0, import_node_fs16.unlinkSync)(temporary);
6173
+ (0, import_node_fs17.unlinkSync)(temporary);
5846
6174
  } catch {
5847
6175
  }
5848
6176
  throw error2;
@@ -5851,12 +6179,12 @@ function atomicWrite(path2, content, mode) {
5851
6179
  function ensureDirectory(path2, mode, privateDirectory2 = false) {
5852
6180
  let stat;
5853
6181
  try {
5854
- stat = (0, import_node_fs16.lstatSync)(path2);
6182
+ stat = (0, import_node_fs17.lstatSync)(path2);
5855
6183
  } catch (error2) {
5856
6184
  if (error2.code !== "ENOENT") throw error2;
5857
- (0, import_node_fs16.mkdirSync)(path2, { recursive: true, mode });
5858
- (0, import_node_fs16.chmodSync)(path2, mode);
5859
- stat = (0, import_node_fs16.lstatSync)(path2);
6185
+ (0, import_node_fs17.mkdirSync)(path2, { recursive: true, mode });
6186
+ (0, import_node_fs17.chmodSync)(path2, mode);
6187
+ stat = (0, import_node_fs17.lstatSync)(path2);
5860
6188
  }
5861
6189
  if (!stat.isDirectory() || stat.isSymbolicLink()) {
5862
6190
  throw new SystemError("unsafe-path", `${path2} must be a real directory`);
@@ -5871,8 +6199,8 @@ function isDefaultSystemLayout(paths) {
5871
6199
  }
5872
6200
  function assertManagedPathSafety(paths, enforceRoot) {
5873
6201
  for (const path2 of [paths.prefix, paths.configDir, paths.stateDir, paths.unitsDir, paths.sysusersDir, paths.udevRulesDir]) {
5874
- if (!(0, import_node_fs16.existsSync)(path2)) continue;
5875
- const stat = (0, import_node_fs16.lstatSync)(path2);
6202
+ if (!(0, import_node_fs17.existsSync)(path2)) continue;
6203
+ const stat = (0, import_node_fs17.lstatSync)(path2);
5876
6204
  if (!stat.isDirectory() || stat.isSymbolicLink()) {
5877
6205
  throw new SystemError("unsafe-path", `${path2} must be a real directory`);
5878
6206
  }
@@ -5885,8 +6213,8 @@ function assertManagedPathSafety(paths, enforceRoot) {
5885
6213
  }
5886
6214
  }
5887
6215
  function writeManagedFile(path2, content, mode, replace) {
5888
- if ((0, import_node_fs16.existsSync)(path2)) {
5889
- const stat = (0, import_node_fs16.lstatSync)(path2);
6216
+ if ((0, import_node_fs17.existsSync)(path2)) {
6217
+ const stat = (0, import_node_fs17.lstatSync)(path2);
5890
6218
  if (!stat.isFile() || stat.isSymbolicLink()) {
5891
6219
  throw new SystemError("unsafe-path", `${path2} must be a regular file, not a symlink`);
5892
6220
  }
@@ -5925,7 +6253,7 @@ function parseEnvValue(value) {
5925
6253
  function readEnvDocument(path2) {
5926
6254
  let raw;
5927
6255
  try {
5928
- raw = (0, import_node_fs16.readFileSync)(path2, "utf8");
6256
+ raw = (0, import_node_fs17.readFileSync)(path2, "utf8");
5929
6257
  } catch (error2) {
5930
6258
  if (error2.code === "ENOENT") return null;
5931
6259
  throw error2;
@@ -5972,43 +6300,43 @@ function connectDestinationUrl(value) {
5972
6300
  return url.href;
5973
6301
  }
5974
6302
  function hubDatabasePath(paths) {
5975
- return (0, import_node_path18.join)(paths.stateDir, "hub", "telemetry.sqlite");
6303
+ return (0, import_node_path19.join)(paths.stateDir, "hub", "telemetry.sqlite");
5976
6304
  }
5977
6305
  function hubEnrollmentPath(paths) {
5978
- return (0, import_node_path18.join)(paths.stateDir, "hub", "enrollment.json");
6306
+ return (0, import_node_path19.join)(paths.stateDir, "hub", "enrollment.json");
5979
6307
  }
5980
6308
  function hubSocketPath(paths) {
5981
- return (0, import_node_path18.join)(paths.stateDir, "hub", "run", "hub.sock");
6309
+ return (0, import_node_path19.join)(paths.stateDir, "hub", "run", "hub.sock");
5982
6310
  }
5983
6311
  function collectorStatePath2(paths) {
5984
- return (0, import_node_path18.join)(paths.stateDir, "collector", "state.json");
6312
+ return (0, import_node_path19.join)(paths.stateDir, "collector", "state.json");
5985
6313
  }
5986
6314
  function hubEnvPath(paths) {
5987
- return (0, import_node_path18.join)(paths.configDir, "hub.env");
6315
+ return (0, import_node_path19.join)(paths.configDir, "hub.env");
5988
6316
  }
5989
6317
  function collectorEnvPath(paths) {
5990
- return (0, import_node_path18.join)(paths.configDir, "collector.env");
6318
+ return (0, import_node_path19.join)(paths.configDir, "collector.env");
5991
6319
  }
5992
6320
  function sysusersPath(paths) {
5993
- return (0, import_node_path18.join)(paths.sysusersDir, SYSUSERS_NAME);
6321
+ return (0, import_node_path19.join)(paths.sysusersDir, SYSUSERS_NAME);
5994
6322
  }
5995
6323
  function powerSysusersPath(paths) {
5996
- return (0, import_node_path18.join)(paths.sysusersDir, POWER_SYSUSERS_NAME);
6324
+ return (0, import_node_path19.join)(paths.sysusersDir, POWER_SYSUSERS_NAME);
5997
6325
  }
5998
6326
  function raplRulePath(paths) {
5999
- return (0, import_node_path18.join)(paths.udevRulesDir, RAPL_RULE_NAME);
6327
+ return (0, import_node_path19.join)(paths.udevRulesDir, RAPL_RULE_NAME);
6000
6328
  }
6001
6329
  function stateFilePath(paths) {
6002
- return (0, import_node_path18.join)(paths.stateDir, STATE_FILE_NAME);
6330
+ return (0, import_node_path19.join)(paths.stateDir, STATE_FILE_NAME);
6003
6331
  }
6004
6332
  function readCollectorIdentity(path2) {
6005
- if (!(0, import_node_fs16.existsSync)(path2)) return null;
6006
- const stat = (0, import_node_fs16.lstatSync)(path2);
6333
+ if (!(0, import_node_fs17.existsSync)(path2)) return null;
6334
+ const stat = (0, import_node_fs17.lstatSync)(path2);
6007
6335
  if (!stat.isFile() || stat.isSymbolicLink()) throw new SystemError("unsafe-path", `${path2} must be a regular file`);
6008
6336
  if ((stat.mode & 63) !== 0) throw new SystemError("insecure-permissions", `${path2} must be private (0600)`);
6009
6337
  let raw;
6010
6338
  try {
6011
- raw = JSON.parse((0, import_node_fs16.readFileSync)(path2, "utf8"));
6339
+ raw = JSON.parse((0, import_node_fs17.readFileSync)(path2, "utf8"));
6012
6340
  } catch {
6013
6341
  throw new SystemError("invalid-file", `${path2} is not valid JSON`);
6014
6342
  }
@@ -6042,7 +6370,7 @@ function readCollectorIdentity(path2) {
6042
6370
  return { collectorId: record.collectorId, destinations };
6043
6371
  }
6044
6372
  function writeCollectorIdentity(path2, identity) {
6045
- ensureDirectory((0, import_node_path18.dirname)(path2), 448, true);
6373
+ ensureDirectory((0, import_node_path19.dirname)(path2), 448, true);
6046
6374
  atomicWrite(path2, `${JSON.stringify(identity)}
6047
6375
  `, 384);
6048
6376
  }
@@ -6082,7 +6410,7 @@ function ensureHubOwner(paths) {
6082
6410
  if (!Array.isArray(parsed)) throw new SystemError("invalid-config", "HUB_BROWSER_USERS must be a JSON array");
6083
6411
  if (parsed.length > 0) return void 0;
6084
6412
  }
6085
- const password = (0, import_node_crypto11.randomBytes)(18).toString("base64url");
6413
+ const password = (0, import_node_crypto12.randomBytes)(18).toString("base64url");
6086
6414
  const users = [{ username: "owner", passwordHash: hashPassword(password), machineIds: "*", admin: true }];
6087
6415
  writeEnvValue(hubEnvPath(paths), "HUB_BROWSER_USERS", JSON.stringify(users));
6088
6416
  return password;
@@ -6133,7 +6461,7 @@ function updateHubCollectorRegistry(paths, identity) {
6133
6461
  }
6134
6462
  function registerCollectorInHubDatabase(paths, identity, host) {
6135
6463
  const database = hubDatabasePath(paths);
6136
- if (!(0, import_node_fs16.existsSync)(database)) return { inserted: false };
6464
+ if (!(0, import_node_fs17.existsSync)(database)) return { inserted: false };
6137
6465
  const db = new import_node_sqlite4.DatabaseSync(database, { timeout: 5e3 });
6138
6466
  try {
6139
6467
  const table = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='collector_registry'").get();
@@ -6148,7 +6476,7 @@ function registerCollectorInHubDatabase(paths, identity, host) {
6148
6476
  }
6149
6477
  function findCollectorInHubDatabase(paths, collectorId) {
6150
6478
  const database = hubDatabasePath(paths);
6151
- if (!(0, import_node_fs16.existsSync)(database)) return null;
6479
+ if (!(0, import_node_fs17.existsSync)(database)) return null;
6152
6480
  const db = new import_node_sqlite4.DatabaseSync(database, { readOnly: true, timeout: 5e3 });
6153
6481
  try {
6154
6482
  const table = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='collector_registry'").get();
@@ -6161,9 +6489,9 @@ function findCollectorInHubDatabase(paths, collectorId) {
6161
6489
  }
6162
6490
  function createLocalEnrollmentGrant(paths, host) {
6163
6491
  const database = hubDatabasePath(paths);
6164
- if (!(0, import_node_fs16.existsSync)(database)) throw new SystemError("hub-unavailable", "the hub database does not exist yet");
6165
- const token = (0, import_node_crypto11.randomBytes)(32).toString("base64url");
6166
- const tokenHash = (0, import_node_crypto11.createHash)("sha256").update(token).digest("hex");
6492
+ if (!(0, import_node_fs17.existsSync)(database)) throw new SystemError("hub-unavailable", "the hub database does not exist yet");
6493
+ const token = (0, import_node_crypto12.randomBytes)(32).toString("base64url");
6494
+ const tokenHash = (0, import_node_crypto12.createHash)("sha256").update(token).digest("hex");
6167
6495
  const expiresAt = host.now() + 10 * 60 * 1e3;
6168
6496
  const db = new import_node_sqlite4.DatabaseSync(database, { timeout: 5e3 });
6169
6497
  try {
@@ -6215,7 +6543,7 @@ async function pairCollectorWithHub(options) {
6215
6543
  note: "the local hub has no reachable loopback or HTTPS destination"
6216
6544
  };
6217
6545
  }
6218
- if (!(0, import_node_fs16.existsSync)((0, import_node_path18.join)(paths.unitsDir, UNIT_NAMES.hub))) {
6546
+ if (!(0, import_node_fs17.existsSync)((0, import_node_path19.join)(paths.unitsDir, UNIT_NAMES.hub))) {
6219
6547
  return { paired: false, awaiting: true, hubWasActive: false, note: "the local hub unit is not installed" };
6220
6548
  }
6221
6549
  const hubWasActive = systemdActive(host, UNIT_NAMES.hub);
@@ -6228,7 +6556,7 @@ async function pairCollectorWithHub(options) {
6228
6556
  destinations: [{ url: destination.url, token: registered.token, sharing: { ...SYSTEM_DEFAULT_SHARING } }]
6229
6557
  };
6230
6558
  writeCollectorIdentity(collectorStatePath2(paths), adopted);
6231
- chownAccount([(0, import_node_path18.join)(paths.stateDir, "collector")], accounts.collector, accounts.collector, host);
6559
+ chownAccount([(0, import_node_path19.join)(paths.stateDir, "collector")], accounts.collector, accounts.collector, host);
6232
6560
  return { paired: true, awaiting: false, hubWasActive, destination: destination.url };
6233
6561
  }
6234
6562
  }
@@ -6236,7 +6564,7 @@ async function pairCollectorWithHub(options) {
6236
6564
  try {
6237
6565
  grant = createLocalEnrollmentGrant(paths, host);
6238
6566
  } catch (error2) {
6239
- chownAccount([(0, import_node_path18.join)(paths.stateDir, "collector")], accounts.collector, accounts.collector, host);
6567
+ chownAccount([(0, import_node_path19.join)(paths.stateDir, "collector")], accounts.collector, accounts.collector, host);
6240
6568
  return {
6241
6569
  paired: false,
6242
6570
  awaiting: true,
@@ -6254,18 +6582,18 @@ async function pairCollectorWithHub(options) {
6254
6582
  note: `enrollment through the live hub failed: ${error2 instanceof Error ? error2.message : String(error2)}`
6255
6583
  };
6256
6584
  } finally {
6257
- chownAccount([(0, import_node_path18.join)(paths.stateDir, "collector")], accounts.collector, accounts.collector, host);
6585
+ chownAccount([(0, import_node_path19.join)(paths.stateDir, "collector")], accounts.collector, accounts.collector, host);
6258
6586
  }
6259
6587
  if (!collectorConfigured(paths).configured) {
6260
6588
  return { paired: false, awaiting: true, hubWasActive, note: "the hub accepted enrollment but no destination was saved" };
6261
6589
  }
6262
- chownAccount([(0, import_node_path18.join)(paths.stateDir, "collector")], accounts.collector, accounts.collector, host);
6590
+ chownAccount([(0, import_node_path19.join)(paths.stateDir, "collector")], accounts.collector, accounts.collector, host);
6263
6591
  return { paired: true, awaiting: false, hubWasActive, destination: destination.url };
6264
6592
  }
6265
6593
  const identity = {
6266
- collectorId: existing?.collectorId ?? (0, import_node_crypto11.randomUUID)(),
6267
- machineId: (0, import_node_crypto11.randomUUID)(),
6268
- token: (0, import_node_crypto11.randomBytes)(32).toString("base64url")
6594
+ collectorId: existing?.collectorId ?? (0, import_node_crypto12.randomUUID)(),
6595
+ machineId: (0, import_node_crypto12.randomUUID)(),
6596
+ token: (0, import_node_crypto12.randomBytes)(32).toString("base64url")
6269
6597
  };
6270
6598
  const registry = registerCollectorInHubDatabase(paths, identity, host);
6271
6599
  let token = identity.token;
@@ -6292,7 +6620,7 @@ async function pairCollectorWithHub(options) {
6292
6620
  }
6293
6621
  ];
6294
6622
  writeCollectorIdentity(collectorStatePath2(paths), next);
6295
- chownAccount([(0, import_node_path18.join)(paths.stateDir, "collector")], accounts.collector, accounts.collector, host);
6623
+ chownAccount([(0, import_node_path19.join)(paths.stateDir, "collector")], accounts.collector, accounts.collector, host);
6296
6624
  return { paired: true, awaiting: false, hubWasActive, destination: destination.url };
6297
6625
  }
6298
6626
  function unitCommon(name, description, paths, account, envPath, execStart, extraService = []) {
@@ -6309,7 +6637,7 @@ function unitCommon(name, description, paths, account, envPath, execStart, extra
6309
6637
  "Type=simple",
6310
6638
  `User=${account}`,
6311
6639
  `Group=${account}`,
6312
- `WorkingDirectory=${(0, import_node_path18.join)(paths.prefix, "current").replace(/%/g, "%%")}`,
6640
+ `WorkingDirectory=${(0, import_node_path19.join)(paths.prefix, "current").replace(/%/g, "%%")}`,
6313
6641
  `EnvironmentFile=${envPath.replace(/%/g, "%%")}`,
6314
6642
  "Environment=NODE_ENV=production",
6315
6643
  "Restart=on-failure",
@@ -6344,13 +6672,13 @@ function unitCommon(name, description, paths, account, envPath, execStart, extra
6344
6672
  }
6345
6673
  function renderSystemHubUnit(options) {
6346
6674
  const { paths, accounts } = options;
6347
- const hubScript = (0, import_node_path18.join)(paths.prefix, "current", "dist", "hub.cjs");
6675
+ const hubScript = (0, import_node_path19.join)(paths.prefix, "current", "dist", "hub.cjs");
6348
6676
  const extra = [
6349
6677
  `Environment=${systemdQuote(`HUB_DATABASE_PATH=${options.database}`)}`,
6350
6678
  `Environment=${systemdQuote(`HUB_STATE_FILE=${options.enrollment}`)}`,
6351
6679
  ...options.remote ? [`Environment=${systemdQuote(`HUB_SOCKET_PATH=${options.socket}`)}`] : [],
6352
- `ReadWritePaths=${systemdQuote((0, import_node_path18.join)(paths.stateDir, "hub"))}`,
6353
- `ReadOnlyPaths=${systemdQuote((0, import_node_path18.join)(paths.prefix, "current"))}`,
6680
+ `ReadWritePaths=${systemdQuote((0, import_node_path19.join)(paths.stateDir, "hub"))}`,
6681
+ `ReadOnlyPaths=${systemdQuote((0, import_node_path19.join)(paths.prefix, "current"))}`,
6354
6682
  "PrivateDevices=yes",
6355
6683
  "TimeoutStopSec=15s"
6356
6684
  ];
@@ -6369,14 +6697,14 @@ function renderSystemHubUnit(options) {
6369
6697
  }
6370
6698
  function renderSystemCollectorUnit(options) {
6371
6699
  const { paths, accounts } = options;
6372
- const collectorScript = (0, import_node_path18.join)(paths.prefix, "current", "dist", "collector.cjs");
6700
+ const collectorScript = (0, import_node_path19.join)(paths.prefix, "current", "dist", "collector.cjs");
6373
6701
  const extra = [
6374
6702
  ...options.powerAccess ? [`SupplementaryGroups=${accounts.powerGroup}`] : [],
6375
6703
  `Environment=${systemdQuote(`COLLECTOR_MODE=real`)}`,
6376
6704
  `Environment=${systemdQuote(`COLLECTOR_STATE_FILE=${options.collectorState}`)}`,
6377
- `Environment=${systemdQuote(`COLLECTOR_HEALTH_FILE=${(0, import_node_path18.join)(paths.stateDir, "collector", "health.json")}`)}`,
6378
- `ReadWritePaths=${systemdQuote((0, import_node_path18.join)(paths.stateDir, "collector"))}`,
6379
- `ReadOnlyPaths=${systemdQuote((0, import_node_path18.join)(paths.prefix, "current"))}`,
6705
+ `Environment=${systemdQuote(`COLLECTOR_HEALTH_FILE=${(0, import_node_path19.join)(paths.stateDir, "collector", "health.json")}`)}`,
6706
+ `ReadWritePaths=${systemdQuote((0, import_node_path19.join)(paths.stateDir, "collector"))}`,
6707
+ `ReadOnlyPaths=${systemdQuote((0, import_node_path19.join)(paths.prefix, "current"))}`,
6380
6708
  "TimeoutStopSec=10s"
6381
6709
  ];
6382
6710
  return [
@@ -6449,11 +6777,15 @@ function collectorEnvTemplate() {
6449
6777
  "# COLLECTOR_ENROLL_URL=ws://127.0.0.1:8081/api/collector/connect",
6450
6778
  "# COLLECTOR_ENROLL_TOKEN=<one-use grant>",
6451
6779
  "# COLLECTOR_MODE=real",
6780
+ "# Unacknowledged samples wait in RAM by default: no queue is written to disk",
6781
+ "# and a collector restart loses what was never acknowledged. Set this to durable",
6782
+ "# to keep them in a per-hub SQLite outbox across collector restarts.",
6783
+ "# COLLECTOR_QUEUE_MODE=durable",
6452
6784
  ""
6453
6785
  ].join("\n");
6454
6786
  }
6455
6787
  function assertPrivateSourceFile(path2, label3) {
6456
- const stat = (0, import_node_fs16.lstatSync)(path2);
6788
+ const stat = (0, import_node_fs17.lstatSync)(path2);
6457
6789
  if (!stat.isFile() || stat.isSymbolicLink()) {
6458
6790
  throw new SystemError("unsafe-adoption", `${label3} must be a regular file: ${path2}`);
6459
6791
  }
@@ -6464,7 +6796,7 @@ function assertPrivateSourceFile(path2, label3) {
6464
6796
  function readJsonObject(path2) {
6465
6797
  let raw;
6466
6798
  try {
6467
- raw = (0, import_node_fs16.readFileSync)(path2, "utf8");
6799
+ raw = (0, import_node_fs17.readFileSync)(path2, "utf8");
6468
6800
  } catch (error2) {
6469
6801
  throw new SystemError("missing-file", `cannot read ${path2}: ${error2.message}`);
6470
6802
  }
@@ -6487,8 +6819,8 @@ function isCanonicalComponents(value) {
6487
6819
  }
6488
6820
  function readSystemState(paths) {
6489
6821
  const path2 = stateFilePath(paths);
6490
- if (!(0, import_node_fs16.existsSync)(path2)) return null;
6491
- const stat = (0, import_node_fs16.lstatSync)(path2);
6822
+ if (!(0, import_node_fs17.existsSync)(path2)) return null;
6823
+ const stat = (0, import_node_fs17.lstatSync)(path2);
6492
6824
  if (!stat.isFile() || stat.isSymbolicLink()) throw new SystemError("unsafe-path", `${path2} must be a regular file`);
6493
6825
  if ((stat.mode & 63) !== 0) throw new SystemError("insecure-permissions", `${path2} must be 0600`);
6494
6826
  const raw = readJsonObject(path2);
@@ -6520,7 +6852,7 @@ function readSystemState(paths) {
6520
6852
  }
6521
6853
  function systemStateIsLegacy(paths) {
6522
6854
  const path2 = stateFilePath(paths);
6523
- if (!(0, import_node_fs16.existsSync)(path2)) return false;
6855
+ if (!(0, import_node_fs17.existsSync)(path2)) return false;
6524
6856
  let raw;
6525
6857
  try {
6526
6858
  raw = readJsonObject(path2);
@@ -6571,24 +6903,24 @@ function validateVersion2(value) {
6571
6903
  return value;
6572
6904
  }
6573
6905
  function releaseVersion(root) {
6574
- const metadata = readJsonObject((0, import_node_path18.join)(root, "package.json"));
6906
+ const metadata = readJsonObject((0, import_node_path19.join)(root, "package.json"));
6575
6907
  if (Object.hasOwn(metadata, "rigscopeConfigVersion")) {
6576
6908
  throw new SystemError("invalid-release", "release uses unfinished configuration metadata; select a canonical release");
6577
6909
  }
6578
6910
  return validateVersion2(metadata.version);
6579
6911
  }
6580
6912
  function releaseSchemaVersion(root) {
6581
- const manifestPath = (0, import_node_path18.join)(root, "manifest.json");
6582
- if (!(0, import_node_fs16.existsSync)(manifestPath)) return null;
6913
+ const manifestPath = (0, import_node_path19.join)(root, "manifest.json");
6914
+ if (!(0, import_node_fs17.existsSync)(manifestPath)) return null;
6583
6915
  const manifest = readJsonObject(manifestPath);
6584
6916
  return Number.isSafeInteger(manifest.schemaVersion) ? Number(manifest.schemaVersion) : null;
6585
6917
  }
6586
6918
  function assertReleaseArtifacts(root) {
6587
6919
  for (const artifact of REQUIRED_ARTIFACTS) {
6588
- const path2 = (0, import_node_path18.join)(root, artifact);
6920
+ const path2 = (0, import_node_path19.join)(root, artifact);
6589
6921
  let stat;
6590
6922
  try {
6591
- stat = (0, import_node_fs16.lstatSync)(path2);
6923
+ stat = (0, import_node_fs17.lstatSync)(path2);
6592
6924
  } catch {
6593
6925
  throw new SystemError("missing-artifact", `release is missing ${artifact}`);
6594
6926
  }
@@ -6596,12 +6928,12 @@ function assertReleaseArtifacts(root) {
6596
6928
  throw new SystemError("invalid-artifact", `release artifact is not a non-empty regular file: ${artifact}`);
6597
6929
  }
6598
6930
  }
6599
- const buildId = (0, import_node_fs16.readFileSync)((0, import_node_path18.join)(root, ".next", "BUILD_ID"), "utf8").trim();
6931
+ const buildId = (0, import_node_fs17.readFileSync)((0, import_node_path19.join)(root, ".next", "BUILD_ID"), "utf8").trim();
6600
6932
  if (buildId === "" || /\s/.test(buildId)) throw new SystemError("invalid-artifact", ".next/BUILD_ID is invalid");
6601
6933
  for (const directory of [".next/server", ".next/static"]) {
6602
6934
  let stat;
6603
6935
  try {
6604
- stat = (0, import_node_fs16.lstatSync)((0, import_node_path18.join)(root, directory));
6936
+ stat = (0, import_node_fs17.lstatSync)((0, import_node_path19.join)(root, directory));
6605
6937
  } catch {
6606
6938
  throw new SystemError("missing-artifact", `release is missing ${directory}/`);
6607
6939
  }
@@ -6609,10 +6941,10 @@ function assertReleaseArtifacts(root) {
6609
6941
  }
6610
6942
  }
6611
6943
  function verifyReleaseManifest(root) {
6612
- const manifestPath = (0, import_node_path18.join)(root, "manifest.json");
6613
- if (!(0, import_node_fs16.existsSync)(manifestPath)) return;
6944
+ const manifestPath = (0, import_node_path19.join)(root, "manifest.json");
6945
+ if (!(0, import_node_fs17.existsSync)(manifestPath)) return;
6614
6946
  const manifest = readJsonObject(manifestPath);
6615
- const packageJson = readJsonObject((0, import_node_path18.join)(root, "package.json"));
6947
+ const packageJson = readJsonObject((0, import_node_path19.join)(root, "package.json"));
6616
6948
  if (manifest.name !== void 0 && manifest.name !== packageJson.name) {
6617
6949
  throw new SystemError("manifest-mismatch", "release manifest name does not match package.json");
6618
6950
  }
@@ -6631,17 +6963,17 @@ function verifyReleaseManifest(root) {
6631
6963
  if (typeof relativePath !== "string" || relativePath === "" || relativePath.startsWith("/") || relativePath.includes("\\") || relativePath.split("/").includes("..")) {
6632
6964
  throw new SystemError("invalid-manifest", `release manifest contains an unsafe path: ${String(relativePath)}`);
6633
6965
  }
6634
- const absolute = (0, import_node_path18.join)(root, relativePath);
6635
- const stat = (0, import_node_fs16.lstatSync)(absolute);
6966
+ const absolute = (0, import_node_path19.join)(root, relativePath);
6967
+ const stat = (0, import_node_fs17.lstatSync)(absolute);
6636
6968
  if (!stat.isFile() || stat.isSymbolicLink()) throw new SystemError("invalid-manifest", `release artifact is not a regular file: ${relativePath}`);
6637
6969
  if (entry2.bytes !== stat.size) throw new SystemError("artifact-mismatch", `release artifact size changed: ${relativePath}`);
6638
6970
  if (entry2.sha256 !== sha256File2(absolute)) throw new SystemError("artifact-mismatch", `release artifact hash changed: ${relativePath}`);
6639
6971
  listed.add(relativePath);
6640
6972
  }
6641
6973
  const visit = (current) => {
6642
- for (const entry2 of (0, import_node_fs16.readdirSync)(current, { withFileTypes: true })) {
6643
- const absolute = (0, import_node_path18.join)(current, entry2.name);
6644
- const rel = (0, import_node_path18.relative)(root, absolute).split(import_node_path18.sep).join("/");
6974
+ for (const entry2 of (0, import_node_fs17.readdirSync)(current, { withFileTypes: true })) {
6975
+ const absolute = (0, import_node_path19.join)(current, entry2.name);
6976
+ const rel = (0, import_node_path19.relative)(root, absolute).split(import_node_path19.sep).join("/");
6645
6977
  if (rel === "node_modules" || rel.startsWith("node_modules/") || rel === ".next/cache" || rel.startsWith(".next/cache/")) continue;
6646
6978
  if (entry2.isSymbolicLink()) throw new SystemError("unsafe-release", `release contains a symlink: ${rel}`);
6647
6979
  if (entry2.isDirectory()) {
@@ -6656,49 +6988,49 @@ function verifyReleaseManifest(root) {
6656
6988
  }
6657
6989
  function assertNoSymlinks(root) {
6658
6990
  const visit = (path2) => {
6659
- const stat = (0, import_node_fs16.lstatSync)(path2);
6660
- if (stat.isSymbolicLink()) throw new SystemError("unsafe-release", `release contains a symlink: ${(0, import_node_path18.relative)(root, path2)}`);
6991
+ const stat = (0, import_node_fs17.lstatSync)(path2);
6992
+ if (stat.isSymbolicLink()) throw new SystemError("unsafe-release", `release contains a symlink: ${(0, import_node_path19.relative)(root, path2)}`);
6661
6993
  if (!stat.isDirectory()) return;
6662
- for (const entry2 of (0, import_node_fs16.readdirSync)(path2)) visit((0, import_node_path18.join)(path2, entry2));
6994
+ for (const entry2 of (0, import_node_fs17.readdirSync)(path2)) visit((0, import_node_path19.join)(path2, entry2));
6663
6995
  };
6664
6996
  visit(root);
6665
6997
  }
6666
6998
  function copyReleaseTree(source, destination) {
6667
6999
  const visit = (current, target) => {
6668
- const stat = (0, import_node_fs16.lstatSync)(current);
6669
- if (stat.isSymbolicLink()) throw new SystemError("unsafe-release", `source contains a symlink: ${(0, import_node_path18.relative)(source, current)}`);
7000
+ const stat = (0, import_node_fs17.lstatSync)(current);
7001
+ if (stat.isSymbolicLink()) throw new SystemError("unsafe-release", `source contains a symlink: ${(0, import_node_path19.relative)(source, current)}`);
6670
7002
  if (stat.isDirectory()) {
6671
- (0, import_node_fs16.mkdirSync)(target, { recursive: true, mode: 493 });
6672
- for (const entry2 of (0, import_node_fs16.readdirSync)(current)) {
6673
- const child = (0, import_node_path18.join)(current, entry2);
6674
- const rel = (0, import_node_path18.relative)(source, child);
7003
+ (0, import_node_fs17.mkdirSync)(target, { recursive: true, mode: 493 });
7004
+ for (const entry2 of (0, import_node_fs17.readdirSync)(current)) {
7005
+ const child = (0, import_node_path19.join)(current, entry2);
7006
+ const rel = (0, import_node_path19.relative)(source, child);
6675
7007
  if (rel === ".git" || rel === ".next-dev" || rel === ".next/cache" || rel === "node_modules" || rel.startsWith("node_modules/")) {
6676
7008
  continue;
6677
7009
  }
6678
- visit(child, (0, import_node_path18.join)(target, entry2));
7010
+ visit(child, (0, import_node_path19.join)(target, entry2));
6679
7011
  }
6680
7012
  return;
6681
7013
  }
6682
- if (!stat.isFile()) throw new SystemError("unsafe-release", `source contains a non-regular file: ${(0, import_node_path18.relative)(source, current)}`);
6683
- (0, import_node_fs16.mkdirSync)((0, import_node_path18.dirname)(target), { recursive: true, mode: 493 });
6684
- (0, import_node_fs16.copyFileSync)(current, target);
6685
- (0, import_node_fs16.chmodSync)(target, 420);
7014
+ if (!stat.isFile()) throw new SystemError("unsafe-release", `source contains a non-regular file: ${(0, import_node_path19.relative)(source, current)}`);
7015
+ (0, import_node_fs17.mkdirSync)((0, import_node_path19.dirname)(target), { recursive: true, mode: 493 });
7016
+ (0, import_node_fs17.copyFileSync)(current, target);
7017
+ (0, import_node_fs17.chmodSync)(target, 420);
6686
7018
  };
6687
7019
  visit(source, destination);
6688
7020
  }
6689
7021
  function copyReleaseAllowlisted(source, destination) {
6690
- for (const entry2 of (0, import_node_fs16.readdirSync)(source)) {
7022
+ for (const entry2 of (0, import_node_fs17.readdirSync)(source)) {
6691
7023
  if (!PACKAGE_TOP_LEVEL.has(entry2)) continue;
6692
7024
  if (entry2 === "node_modules") continue;
6693
- const child = (0, import_node_path18.join)(source, entry2);
6694
- const target = (0, import_node_path18.join)(destination, entry2);
6695
- const stat = (0, import_node_fs16.lstatSync)(child);
7025
+ const child = (0, import_node_path19.join)(source, entry2);
7026
+ const target = (0, import_node_path19.join)(destination, entry2);
7027
+ const stat = (0, import_node_fs17.lstatSync)(child);
6696
7028
  if (stat.isSymbolicLink()) throw new SystemError("unsafe-release", `source contains a symlink: ${entry2}`);
6697
7029
  if (stat.isDirectory()) {
6698
7030
  copyReleaseTree(child, target);
6699
7031
  } else if (stat.isFile()) {
6700
- (0, import_node_fs16.copyFileSync)(child, target);
6701
- (0, import_node_fs16.chmodSync)(target, 420);
7032
+ (0, import_node_fs17.copyFileSync)(child, target);
7033
+ (0, import_node_fs17.chmodSync)(target, 420);
6702
7034
  }
6703
7035
  }
6704
7036
  }
@@ -6733,28 +7065,28 @@ function extractArchive(archive, stage, host) {
6733
7065
  }
6734
7066
  function discardStagedRelease(staged) {
6735
7067
  if (staged === null) return;
6736
- (0, import_node_fs16.rmSync)(staged.stageDir, { recursive: true, force: true });
7068
+ (0, import_node_fs17.rmSync)(staged.stageDir, { recursive: true, force: true });
6737
7069
  }
6738
7070
  function releaseTargetDir(paths, version) {
6739
- return (0, import_node_path18.join)(paths.prefix, "releases", version);
7071
+ return (0, import_node_path19.join)(paths.prefix, "releases", version);
6740
7072
  }
6741
7073
  function assertReleaseTargetAvailable(paths, releaseDir, replaceRelease) {
6742
- if (!(0, import_node_fs16.existsSync)(releaseDir)) return;
7074
+ if (!(0, import_node_fs17.existsSync)(releaseDir)) return;
6743
7075
  const current = currentReleaseDir(paths);
6744
7076
  const previous = previousReleaseDir(paths);
6745
7077
  if (!replaceRelease || current === releaseDir || previous === releaseDir) {
6746
7078
  throw new SystemError(
6747
7079
  "release-exists",
6748
- `release ${(0, import_node_path18.basename)(releaseDir)} is already installed${current === releaseDir || previous === releaseDir ? " or retained" : ""}; choose a new version`
7080
+ `release ${(0, import_node_path19.basename)(releaseDir)} is already installed${current === releaseDir || previous === releaseDir ? " or retained" : ""}; choose a new version`
6749
7081
  );
6750
7082
  }
6751
7083
  }
6752
7084
  function stageRelease(options) {
6753
- const source = (0, import_node_fs16.realpathSync)((0, import_node_path18.resolve)(options.source));
6754
- const stat = (0, import_node_fs16.lstatSync)(source);
7085
+ const source = (0, import_node_fs17.realpathSync)((0, import_node_path19.resolve)(options.source));
7086
+ const stat = (0, import_node_fs17.lstatSync)(source);
6755
7087
  ensureDirectory(options.paths.prefix, 493);
6756
- const stageDir = (0, import_node_path18.join)(options.paths.prefix, `.stage-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
6757
- (0, import_node_fs16.mkdirSync)(stageDir, { recursive: true, mode: 448 });
7088
+ const stageDir = (0, import_node_path19.join)(options.paths.prefix, `.stage-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
7089
+ (0, import_node_fs17.mkdirSync)(stageDir, { recursive: true, mode: 448 });
6758
7090
  try {
6759
7091
  if (stat.isDirectory() && !stat.isSymbolicLink()) {
6760
7092
  copyReleaseAllowlisted(source, stageDir);
@@ -6772,7 +7104,7 @@ function stageRelease(options) {
6772
7104
  artifacts: [...REQUIRED_ARTIFACTS]
6773
7105
  };
6774
7106
  } catch (error2) {
6775
- (0, import_node_fs16.rmSync)(stageDir, { recursive: true, force: true });
7107
+ (0, import_node_fs17.rmSync)(stageDir, { recursive: true, force: true });
6776
7108
  throw error2;
6777
7109
  }
6778
7110
  }
@@ -6798,13 +7130,13 @@ function prepareStagedRelease(options) {
6798
7130
  }
6799
7131
  }
6800
7132
  function commitStagedRelease(options) {
6801
- const releasesDir = (0, import_node_path18.join)(options.paths.prefix, "releases");
7133
+ const releasesDir = (0, import_node_path19.join)(options.paths.prefix, "releases");
6802
7134
  ensureDirectory(releasesDir, 493);
6803
7135
  const releaseDir = releaseTargetDir(options.paths, options.staged.version);
6804
7136
  assertReleaseTargetAvailable(options.paths, releaseDir, options.replaceRelease);
6805
- if ((0, import_node_fs16.existsSync)(releaseDir)) (0, import_node_fs16.rmSync)(releaseDir, { recursive: true, force: true });
6806
- (0, import_node_fs16.renameSync)(options.staged.stageDir, releaseDir);
6807
- ensureDirectory((0, import_node_path18.join)(releaseDir, ".next", "cache"), 493);
7137
+ if ((0, import_node_fs17.existsSync)(releaseDir)) (0, import_node_fs17.rmSync)(releaseDir, { recursive: true, force: true });
7138
+ (0, import_node_fs17.renameSync)(options.staged.stageDir, releaseDir);
7139
+ ensureDirectory((0, import_node_path19.join)(releaseDir, ".next", "cache"), 493);
6808
7140
  return {
6809
7141
  version: options.staged.version,
6810
7142
  releaseDir,
@@ -6813,20 +7145,20 @@ function commitStagedRelease(options) {
6813
7145
  };
6814
7146
  }
6815
7147
  function dependencyNames(root) {
6816
- const pkg = readJsonObject((0, import_node_path18.join)(root, "package.json"));
7148
+ const pkg = readJsonObject((0, import_node_path19.join)(root, "package.json"));
6817
7149
  const dependencies2 = pkg.dependencies;
6818
7150
  if (dependencies2 === null || typeof dependencies2 !== "object" || Array.isArray(dependencies2)) return [];
6819
7151
  return Object.keys(dependencies2).filter((name) => /^[A-Za-z0-9@/._-]+$/.test(name));
6820
7152
  }
6821
7153
  function missingDependencies(root) {
6822
- return dependencyNames(root).filter((name) => !(0, import_node_fs16.existsSync)((0, import_node_path18.join)(root, "node_modules", name, "package.json")));
7154
+ return dependencyNames(root).filter((name) => !(0, import_node_fs17.existsSync)((0, import_node_path19.join)(root, "node_modules", name, "package.json")));
6823
7155
  }
6824
7156
  function installDependencies(root, host, command = "npm") {
6825
- const hasLock = (0, import_node_fs16.existsSync)((0, import_node_path18.join)(root, "package-lock.json"));
7157
+ const hasLock = (0, import_node_fs17.existsSync)((0, import_node_path19.join)(root, "package-lock.json"));
6826
7158
  const args = hasLock ? ["ci", "--omit=dev", "--ignore-scripts", "--no-audit", "--no-fund"] : ["install", "--omit=dev", "--ignore-scripts", "--no-audit", "--no-fund"];
6827
- const siblingNpm = (0, import_node_path18.join)((0, import_node_path18.dirname)(process.execPath), "npm");
6828
- const executable = command === "npm" && (0, import_node_fs16.existsSync)(siblingNpm) ? siblingNpm : command;
6829
- const nodeBin = (0, import_node_path18.dirname)(process.execPath);
7159
+ const siblingNpm = (0, import_node_path19.join)((0, import_node_path19.dirname)(process.execPath), "npm");
7160
+ const executable = command === "npm" && (0, import_node_fs17.existsSync)(siblingNpm) ? siblingNpm : command;
7161
+ const nodeBin = (0, import_node_path19.dirname)(process.execPath);
6830
7162
  const path2 = [nodeBin, process.env.PATH ?? ""].filter(Boolean).join(":");
6831
7163
  runRequired(host, executable, args, "production dependency installation", {
6832
7164
  cwd: root,
@@ -6835,21 +7167,21 @@ function installDependencies(root, host, command = "npm") {
6835
7167
  });
6836
7168
  }
6837
7169
  function currentReleaseDir(paths) {
6838
- const link = (0, import_node_path18.join)(paths.prefix, "current");
7170
+ const link = (0, import_node_path19.join)(paths.prefix, "current");
6839
7171
  try {
6840
- const target = (0, import_node_fs16.readlinkSync)(link);
6841
- const absolute = (0, import_node_path18.isAbsolute)(target) ? target : (0, import_node_path18.resolve)(paths.prefix, target);
6842
- return isWithin((0, import_node_path18.join)(paths.prefix, "releases"), absolute) ? absolute : null;
7172
+ const target = (0, import_node_fs17.readlinkSync)(link);
7173
+ const absolute = (0, import_node_path19.isAbsolute)(target) ? target : (0, import_node_path19.resolve)(paths.prefix, target);
7174
+ return isWithin((0, import_node_path19.join)(paths.prefix, "releases"), absolute) ? absolute : null;
6843
7175
  } catch {
6844
7176
  return null;
6845
7177
  }
6846
7178
  }
6847
7179
  function previousReleaseDir(paths) {
6848
- const link = (0, import_node_path18.join)(paths.prefix, "previous");
7180
+ const link = (0, import_node_path19.join)(paths.prefix, "previous");
6849
7181
  try {
6850
- const target = (0, import_node_fs16.readlinkSync)(link);
6851
- const absolute = (0, import_node_path18.isAbsolute)(target) ? target : (0, import_node_path18.resolve)(paths.prefix, target);
6852
- return isWithin((0, import_node_path18.join)(paths.prefix, "releases"), absolute) ? absolute : null;
7182
+ const target = (0, import_node_fs17.readlinkSync)(link);
7183
+ const absolute = (0, import_node_path19.isAbsolute)(target) ? target : (0, import_node_path19.resolve)(paths.prefix, target);
7184
+ return isWithin((0, import_node_path19.join)(paths.prefix, "releases"), absolute) ? absolute : null;
6853
7185
  } catch {
6854
7186
  return null;
6855
7187
  }
@@ -6857,11 +7189,11 @@ function previousReleaseDir(paths) {
6857
7189
  function replaceSymlink(link, target, host) {
6858
7190
  const temporary = `${link}.tmp-${process.pid}-${Date.now()}`;
6859
7191
  try {
6860
- (0, import_node_fs16.symlinkSync)(target, temporary);
6861
- (0, import_node_fs16.renameSync)(temporary, link);
7192
+ (0, import_node_fs17.symlinkSync)(target, temporary);
7193
+ (0, import_node_fs17.renameSync)(temporary, link);
6862
7194
  } catch (error2) {
6863
7195
  try {
6864
- (0, import_node_fs16.unlinkSync)(temporary);
7196
+ (0, import_node_fs17.unlinkSync)(temporary);
6865
7197
  } catch {
6866
7198
  }
6867
7199
  throw error2;
@@ -6870,8 +7202,8 @@ function replaceSymlink(link, target, host) {
6870
7202
  }
6871
7203
  function removeSymlink(link) {
6872
7204
  try {
6873
- const stat = (0, import_node_fs16.lstatSync)(link);
6874
- if (stat.isSymbolicLink()) (0, import_node_fs16.unlinkSync)(link);
7205
+ const stat = (0, import_node_fs17.lstatSync)(link);
7206
+ if (stat.isSymbolicLink()) (0, import_node_fs17.unlinkSync)(link);
6875
7207
  } catch {
6876
7208
  }
6877
7209
  }
@@ -6884,18 +7216,18 @@ function nodeVersion(host, nodePath) {
6884
7216
  return output;
6885
7217
  }
6886
7218
  function ensureRootOwnedNode(options) {
6887
- const selected = (0, import_node_path18.resolve)(options.nodePath ?? process.execPath);
6888
- const real = (0, import_node_fs16.realpathSync)(selected);
7219
+ const selected = (0, import_node_path19.resolve)(options.nodePath ?? process.execPath);
7220
+ const real = (0, import_node_fs17.realpathSync)(selected);
6889
7221
  nodeVersion(options.host, real);
6890
- const stat = (0, import_node_fs16.statSync)(real);
6891
- const insideRelease = isWithin((0, import_node_path18.join)(options.paths.prefix, "releases"), real);
7222
+ const stat = (0, import_node_fs17.statSync)(real);
7223
+ const insideRelease = isWithin((0, import_node_path19.join)(options.paths.prefix, "releases"), real);
6892
7224
  const unsafe = stat.uid !== 0 || (stat.mode & 18) !== 0 || options.copyNode === true || insideRelease;
6893
7225
  if (!unsafe || options.trustedNode === true) return real;
6894
- const target = (0, import_node_path18.join)(options.paths.prefix, "node", "node");
7226
+ const target = (0, import_node_path19.join)(options.paths.prefix, "node", "node");
6895
7227
  if (!options.apply) return target;
6896
- ensureDirectory((0, import_node_path18.join)(options.paths.prefix, "node"), 493);
6897
- (0, import_node_fs16.copyFileSync)(real, target);
6898
- (0, import_node_fs16.chmodSync)(target, 493);
7228
+ ensureDirectory((0, import_node_path19.join)(options.paths.prefix, "node"), 493);
7229
+ (0, import_node_fs17.copyFileSync)(real, target);
7230
+ (0, import_node_fs17.chmodSync)(target, 493);
6899
7231
  runRequired(options.host, "chown", ["root:root", target], "node ownership");
6900
7232
  nodeVersion(options.host, target);
6901
7233
  return target;
@@ -6924,8 +7256,8 @@ function validatedOrigin(value) {
6924
7256
  return value;
6925
7257
  }
6926
7258
  function readAdoptionSetup(source) {
6927
- const configPath = (0, import_node_path18.join)(source, "config.json");
6928
- if (!(0, import_node_fs16.existsSync)(configPath)) {
7259
+ const configPath = (0, import_node_path19.join)(source, "config.json");
7260
+ if (!(0, import_node_fs17.existsSync)(configPath)) {
6929
7261
  throw new SystemError("invalid-adoption", "adopted installation has no config.json");
6930
7262
  }
6931
7263
  assertPrivateSourceFile(configPath, "adopted setup");
@@ -6973,35 +7305,35 @@ function detectUserInstallations(options = {}) {
6973
7305
  const roots = options.homeRoots ?? ["/home", "/root"];
6974
7306
  const found = [];
6975
7307
  for (const homeRoot of roots) {
6976
- if (!(0, import_node_fs16.existsSync)(homeRoot)) continue;
6977
- const homes = homeRoot === "/root" ? ["/root"] : (0, import_node_fs16.readdirSync)(homeRoot).map((entry2) => (0, import_node_path18.join)(homeRoot, entry2));
7308
+ if (!(0, import_node_fs17.existsSync)(homeRoot)) continue;
7309
+ const homes = homeRoot === "/root" ? ["/root"] : (0, import_node_fs17.readdirSync)(homeRoot).map((entry2) => (0, import_node_path19.join)(homeRoot, entry2));
6978
7310
  for (const home of homes) {
6979
7311
  let stat;
6980
7312
  try {
6981
- stat = (0, import_node_fs16.lstatSync)(home);
7313
+ stat = (0, import_node_fs17.lstatSync)(home);
6982
7314
  } catch {
6983
7315
  continue;
6984
7316
  }
6985
7317
  if (!stat.isDirectory()) continue;
6986
- const unitDir = (0, import_node_path18.join)(home, ".config", "systemd", "user");
7318
+ const unitDir = (0, import_node_path19.join)(home, ".config", "systemd", "user");
6987
7319
  let units = [];
6988
7320
  try {
6989
- units = (0, import_node_fs16.readdirSync)(unitDir);
7321
+ units = (0, import_node_fs17.readdirSync)(unitDir);
6990
7322
  } catch {
6991
7323
  units = [];
6992
7324
  }
6993
7325
  for (const unitName2 of units) {
6994
7326
  if (!/^rigscope(?:-[0-9a-f]{12})?(?:-(?:hub|collector))?\.service$/.test(unitName2)) continue;
6995
- const unitPath2 = (0, import_node_path18.join)(unitDir, unitName2);
7327
+ const unitPath2 = (0, import_node_path19.join)(unitDir, unitName2);
6996
7328
  let execStart = null;
6997
7329
  try {
6998
- const line = (0, import_node_fs16.readFileSync)(unitPath2, "utf8").split("\n").find((item) => item.startsWith("ExecStart="));
7330
+ const line = (0, import_node_fs17.readFileSync)(unitPath2, "utf8").split("\n").find((item) => item.startsWith("ExecStart="));
6999
7331
  execStart = line ? line.slice("ExecStart=".length) : null;
7000
7332
  } catch {
7001
7333
  }
7002
- const stateCandidates = [(0, import_node_path18.join)(home, ".local", "state", "rigscope")];
7003
- const stateDir = stateCandidates.find((candidate) => (0, import_node_fs16.existsSync)(candidate)) ?? null;
7004
- found.push({ user: (0, import_node_path18.basename)(home), home, unitPath: unitPath2, unitName: unitName2, execStart, stateDir });
7334
+ const stateCandidates = [(0, import_node_path19.join)(home, ".local", "state", "rigscope")];
7335
+ const stateDir = stateCandidates.find((candidate) => (0, import_node_fs17.existsSync)(candidate)) ?? null;
7336
+ found.push({ user: (0, import_node_path19.basename)(home), home, unitPath: unitPath2, unitName: unitName2, execStart, stateDir });
7005
7337
  }
7006
7338
  }
7007
7339
  }
@@ -7009,9 +7341,9 @@ function detectUserInstallations(options = {}) {
7009
7341
  }
7010
7342
  function lockIsLive(path2) {
7011
7343
  try {
7012
- const stat = (0, import_node_fs16.lstatSync)(path2);
7344
+ const stat = (0, import_node_fs17.lstatSync)(path2);
7013
7345
  if (!stat.isFile() || stat.size > 4096) return { exists: true, live: false, pid: null, ambiguous: true };
7014
- const owner = JSON.parse((0, import_node_fs16.readFileSync)(path2, "utf8"));
7346
+ const owner = JSON.parse((0, import_node_fs17.readFileSync)(path2, "utf8"));
7015
7347
  const pid = owner.pid;
7016
7348
  if (!Number.isSafeInteger(pid) || pid < 1) return { exists: true, live: false, pid: null, ambiguous: true };
7017
7349
  try {
@@ -7029,9 +7361,9 @@ function lockIsLive(path2) {
7029
7361
  }
7030
7362
  function directoryLocks(directory) {
7031
7363
  return [
7032
- (0, import_node_path18.join)(directory, "hub", "telemetry.sqlite.hub.lock"),
7033
- (0, import_node_path18.join)(directory, "run", "rigscope.lock"),
7034
- (0, import_node_path18.join)(directory, "collector", "collector-runtime.lock")
7364
+ (0, import_node_path19.join)(directory, "hub", "telemetry.sqlite.hub.lock"),
7365
+ (0, import_node_path19.join)(directory, "run", "rigscope.lock"),
7366
+ (0, import_node_path19.join)(directory, "collector", "collector-runtime.lock")
7035
7367
  ];
7036
7368
  }
7037
7369
  function assertDirectoryStopped(directory, scope = "local") {
@@ -7062,13 +7394,13 @@ function assertSystemStartAllowed(paths, components) {
7062
7394
  }
7063
7395
  function detectPowerDriver(moduleRoot = "/sys/module") {
7064
7396
  const candidates = ["intel_rapl_msr", "intel_rapl_common", "intel_rapl_tpmi", "intel_rapl", "amd_energy"];
7065
- return candidates.filter((name) => (0, import_node_fs16.existsSync)((0, import_node_path18.join)(moduleRoot, name)));
7397
+ return candidates.filter((name) => (0, import_node_fs17.existsSync)((0, import_node_path19.join)(moduleRoot, name)));
7066
7398
  }
7067
7399
  function readPasswd(path2 = "/etc/passwd") {
7068
7400
  const users = /* @__PURE__ */ new Map();
7069
7401
  let lines;
7070
7402
  try {
7071
- lines = (0, import_node_fs16.readFileSync)(path2, "utf8").split("\n");
7403
+ lines = (0, import_node_fs17.readFileSync)(path2, "utf8").split("\n");
7072
7404
  } catch {
7073
7405
  return users;
7074
7406
  }
@@ -7085,7 +7417,7 @@ function readGroups(path2 = "/etc/group") {
7085
7417
  const groups = /* @__PURE__ */ new Map();
7086
7418
  let lines;
7087
7419
  try {
7088
- lines = (0, import_node_fs16.readFileSync)(path2, "utf8").split("\n");
7420
+ lines = (0, import_node_fs17.readFileSync)(path2, "utf8").split("\n");
7089
7421
  } catch {
7090
7422
  return groups;
7091
7423
  }
@@ -7113,7 +7445,7 @@ function inspectPowerAccess(options = {}) {
7113
7445
  const sysusers = options.sysusersPath ?? powerSysusersPath(paths);
7114
7446
  let ruleText = null;
7115
7447
  try {
7116
- ruleText = (0, import_node_fs16.readFileSync)(rulePath, "utf8");
7448
+ ruleText = (0, import_node_fs17.readFileSync)(rulePath, "utf8");
7117
7449
  } catch {
7118
7450
  ruleText = null;
7119
7451
  }
@@ -7130,27 +7462,27 @@ function inspectPowerAccess(options = {}) {
7130
7462
  const domains = [];
7131
7463
  let entries = [];
7132
7464
  try {
7133
- entries = (0, import_node_fs16.readdirSync)(paths.powercapRoot);
7465
+ entries = (0, import_node_fs17.readdirSync)(paths.powercapRoot);
7134
7466
  } catch {
7135
7467
  entries = [];
7136
7468
  }
7137
7469
  for (const entry2 of entries.filter((name) => /^intel-rapl:\d+$/.test(name)).sort()) {
7138
- const domainPath = (0, import_node_path18.join)(paths.powercapRoot, entry2);
7470
+ const domainPath = (0, import_node_path19.join)(paths.powercapRoot, entry2);
7139
7471
  let name = entry2;
7140
7472
  try {
7141
- name = (0, import_node_fs16.readFileSync)((0, import_node_path18.join)(domainPath, "name"), "utf8").trim() || entry2;
7473
+ name = (0, import_node_fs17.readFileSync)((0, import_node_path19.join)(domainPath, "name"), "utf8").trim() || entry2;
7142
7474
  } catch {
7143
7475
  }
7144
7476
  if (!/^package/.test(name)) continue;
7145
- const energyPath = (0, import_node_path18.join)(domainPath, "energy_uj");
7146
- const maxRangePath = (0, import_node_path18.join)(domainPath, "max_energy_range_uj");
7477
+ const energyPath = (0, import_node_path19.join)(domainPath, "energy_uj");
7478
+ const maxRangePath = (0, import_node_path19.join)(domainPath, "max_energy_range_uj");
7147
7479
  const readStat = (path2) => {
7148
7480
  if (options.statOverride) {
7149
7481
  const override = options.statOverride(path2);
7150
7482
  return override === null ? null : { mode: modeString(override.mode), uid: override.uid, gid: override.gid };
7151
7483
  }
7152
7484
  try {
7153
- const stat = (0, import_node_fs16.statSync)(path2);
7485
+ const stat = (0, import_node_fs17.statSync)(path2);
7154
7486
  return { mode: modeString(stat.mode), uid: stat.uid, gid: stat.gid };
7155
7487
  } catch {
7156
7488
  return null;
@@ -7184,7 +7516,7 @@ function inspectPowerAccess(options = {}) {
7184
7516
  problems.push(`collector account ${accounts.collector} is not a member of ${accounts.powerGroup}`);
7185
7517
  }
7186
7518
  if (!compliant) problems.push(`persistent udev rule is missing or not compliant: ${rulePath}`);
7187
- if (!(0, import_node_fs16.existsSync)(sysusers)) problems.push(`power sysusers definition is missing: ${sysusers}`);
7519
+ if (!(0, import_node_fs17.existsSync)(sysusers)) problems.push(`power sysusers definition is missing: ${sysusers}`);
7188
7520
  for (const domain of domains) {
7189
7521
  if (!domain.energyExists) problems.push(`${domain.name}: energy_uj is unavailable`);
7190
7522
  else if (domain.energy?.worldReadable) problems.push(`${domain.name}: energy_uj is world-readable`);
@@ -7203,7 +7535,7 @@ function inspectPowerAccess(options = {}) {
7203
7535
  collectorGids,
7204
7536
  group: { name: accounts.powerGroup, exists: group !== null, gid: group?.gid ?? null, members: group?.members ?? [] },
7205
7537
  rule: { path: rulePath, present: ruleText !== null, compliant },
7206
- sysusers: { path: sysusers, present: (0, import_node_fs16.existsSync)(sysusers) },
7538
+ sysusers: { path: sysusers, present: (0, import_node_fs17.existsSync)(sysusers) },
7207
7539
  collectorCanRead,
7208
7540
  persistent: compliant && group !== null && collector !== null && group.members.includes(accounts.collector),
7209
7541
  worldReadable,
@@ -7212,14 +7544,14 @@ function inspectPowerAccess(options = {}) {
7212
7544
  };
7213
7545
  }
7214
7546
  async function backupDatabase2(source, output, host) {
7215
- ensureDirectory((0, import_node_path18.dirname)(output), 448, true);
7547
+ ensureDirectory((0, import_node_path19.dirname)(output), 448, true);
7216
7548
  const db = new import_node_sqlite4.DatabaseSync(source, { readOnly: true, timeout: 5e3 });
7217
7549
  try {
7218
7550
  await (0, import_node_sqlite4.backup)(db, output);
7219
7551
  } finally {
7220
7552
  db.close();
7221
7553
  }
7222
- (0, import_node_fs16.chmodSync)(output, 384);
7554
+ (0, import_node_fs17.chmodSync)(output, 384);
7223
7555
  const check = new import_node_sqlite4.DatabaseSync(output, { readOnly: true });
7224
7556
  try {
7225
7557
  const result = check.prepare("PRAGMA integrity_check").get();
@@ -7230,7 +7562,7 @@ async function backupDatabase2(source, output, host) {
7230
7562
  void host;
7231
7563
  }
7232
7564
  function databaseSchema(path2) {
7233
- if (!(0, import_node_fs16.existsSync)(path2)) return null;
7565
+ if (!(0, import_node_fs17.existsSync)(path2)) return null;
7234
7566
  const db = new import_node_sqlite4.DatabaseSync(path2, { readOnly: true, timeout: 5e3 });
7235
7567
  try {
7236
7568
  const table = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='schema_migrations'").get();
@@ -7249,28 +7581,28 @@ function replaceDatabaseFromBackup(backupPath, target, safetyBackup, host) {
7249
7581
  } finally {
7250
7582
  backup3.close();
7251
7583
  }
7252
- ensureDirectory((0, import_node_path18.dirname)(target), 448, true);
7253
- if (safetyBackup !== null && (0, import_node_fs16.existsSync)(target)) {
7584
+ ensureDirectory((0, import_node_path19.dirname)(target), 448, true);
7585
+ if (safetyBackup !== null && (0, import_node_fs17.existsSync)(target)) {
7254
7586
  void host;
7255
7587
  }
7256
- const temporary = (0, import_node_path18.join)((0, import_node_path18.dirname)(target), `.restore-${process.pid}-${Date.now()}.sqlite`);
7257
- (0, import_node_fs16.copyFileSync)(backupPath, temporary);
7258
- (0, import_node_fs16.chmodSync)(temporary, 384);
7588
+ const temporary = (0, import_node_path19.join)((0, import_node_path19.dirname)(target), `.restore-${process.pid}-${Date.now()}.sqlite`);
7589
+ (0, import_node_fs17.copyFileSync)(backupPath, temporary);
7590
+ (0, import_node_fs17.chmodSync)(temporary, 384);
7259
7591
  for (const sidecar of [`${target}-wal`, `${target}-shm`]) {
7260
7592
  try {
7261
- (0, import_node_fs16.unlinkSync)(sidecar);
7593
+ (0, import_node_fs17.unlinkSync)(sidecar);
7262
7594
  } catch {
7263
7595
  }
7264
7596
  }
7265
- (0, import_node_fs16.renameSync)(temporary, target);
7266
- (0, import_node_fs16.chmodSync)(target, 384);
7597
+ (0, import_node_fs17.renameSync)(temporary, target);
7598
+ (0, import_node_fs17.chmodSync)(target, 384);
7267
7599
  }
7268
7600
  function newestTimestamp() {
7269
7601
  return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
7270
7602
  }
7271
7603
  function adoptUserState(options) {
7272
- const source = (0, import_node_path18.resolve)(options.source);
7273
- if (!(0, import_node_fs16.existsSync)(source)) throw new SystemError("invalid-adoption", `user installation does not exist: ${source}`);
7604
+ const source = (0, import_node_path19.resolve)(options.source);
7605
+ if (!(0, import_node_fs17.existsSync)(source)) throw new SystemError("invalid-adoption", `user installation does not exist: ${source}`);
7274
7606
  if (isWithin(options.paths.prefix, source) || isWithin(options.paths.stateDir, source) || isWithin(source, options.paths.stateDir)) {
7275
7607
  throw new SystemError("invalid-adoption", "adoption source and system paths must not overlap");
7276
7608
  }
@@ -7280,8 +7612,8 @@ function adoptUserState(options) {
7280
7612
  const { port, origin } = setup;
7281
7613
  if (!Number.isInteger(port) || port < 1 || port > 65535) throw new SystemError("invalid-adoption", "adopted hub port is invalid");
7282
7614
  if (options.role !== "collector") {
7283
- const sourceDatabase = (0, import_node_path18.join)(source, "hub", "telemetry.sqlite");
7284
- if ((0, import_node_fs16.existsSync)(sourceDatabase)) {
7615
+ const sourceDatabase = (0, import_node_path19.join)(source, "hub", "telemetry.sqlite");
7616
+ if ((0, import_node_fs17.existsSync)(sourceDatabase)) {
7285
7617
  assertPrivateSourceFile(sourceDatabase, "adopted database");
7286
7618
  const database = new import_node_sqlite4.DatabaseSync(sourceDatabase, { readOnly: true });
7287
7619
  try {
@@ -7291,12 +7623,12 @@ function adoptUserState(options) {
7291
7623
  database.close();
7292
7624
  }
7293
7625
  }
7294
- const sourceEnrollment = (0, import_node_path18.join)(source, "hub", "enrollment.json");
7295
- if ((0, import_node_fs16.existsSync)(sourceEnrollment)) assertPrivateSourceFile(sourceEnrollment, "adopted enrollment");
7626
+ const sourceEnrollment = (0, import_node_path19.join)(source, "hub", "enrollment.json");
7627
+ if ((0, import_node_fs17.existsSync)(sourceEnrollment)) assertPrivateSourceFile(sourceEnrollment, "adopted enrollment");
7296
7628
  }
7297
7629
  if (options.role !== "hub") {
7298
- const sourceCollectorState = (0, import_node_path18.join)(source, "collector", "state.json");
7299
- if ((0, import_node_fs16.existsSync)(sourceCollectorState)) {
7630
+ const sourceCollectorState = (0, import_node_path19.join)(source, "collector", "state.json");
7631
+ if ((0, import_node_fs17.existsSync)(sourceCollectorState)) {
7300
7632
  assertPrivateSourceFile(sourceCollectorState, "adopted collector state");
7301
7633
  readCollectorIdentity(sourceCollectorState);
7302
7634
  }
@@ -7305,7 +7637,7 @@ function adoptUserState(options) {
7305
7637
  const warnings = [];
7306
7638
  const targetDatabase = hubDatabasePath(options.paths);
7307
7639
  const targetCollector = collectorStatePath2(options.paths);
7308
- if (options.apply && (0, import_node_fs16.existsSync)(targetDatabase)) {
7640
+ if (options.apply && (0, import_node_fs17.existsSync)(targetDatabase)) {
7309
7641
  throw new SystemError("destination-exists", `refusing to overwrite ${targetDatabase}`);
7310
7642
  }
7311
7643
  const hubEnv = options.role === "collector" ? null : [
@@ -7340,62 +7672,42 @@ function adoptUserState(options) {
7340
7672
  port
7341
7673
  };
7342
7674
  }
7343
- function copyAdoptedTree(source, target, copied, skipLocks = true) {
7344
- const visit = (current, destination) => {
7345
- const stat = (0, import_node_fs16.lstatSync)(current);
7346
- if (stat.isSymbolicLink()) return;
7347
- if (stat.isDirectory()) {
7348
- (0, import_node_fs16.mkdirSync)(destination, { recursive: true, mode: 448 });
7349
- for (const entry2 of (0, import_node_fs16.readdirSync)(current)) visit((0, import_node_path18.join)(current, entry2), (0, import_node_path18.join)(destination, entry2));
7350
- return;
7351
- }
7352
- if (!stat.isFile()) return;
7353
- if (skipLocks && /\.(lock|sock)$/.test(current)) return;
7354
- (0, import_node_fs16.mkdirSync)((0, import_node_path18.dirname)(destination), { recursive: true, mode: 448 });
7355
- (0, import_node_fs16.copyFileSync)(current, destination);
7356
- (0, import_node_fs16.chmodSync)(destination, 384);
7357
- copied.push({ source: current, target: destination, sha256: sha256File2(destination), bytes: stat.size });
7358
- };
7359
- visit(source, target);
7360
- }
7361
7675
  async function performAdoptionCopyAsync(options) {
7362
7676
  const { source, paths, role, host, copy } = options;
7363
7677
  assertDirectoryStopped(source, role);
7364
7678
  const targetDatabase = hubDatabasePath(paths);
7365
- const sourceDatabase = (0, import_node_path18.join)(source, "hub", "telemetry.sqlite");
7366
- if (role !== "collector" && (0, import_node_fs16.existsSync)(sourceDatabase) && !(0, import_node_fs16.existsSync)(targetDatabase)) {
7679
+ const sourceDatabase = (0, import_node_path19.join)(source, "hub", "telemetry.sqlite");
7680
+ if (role !== "collector" && (0, import_node_fs17.existsSync)(sourceDatabase) && !(0, import_node_fs17.existsSync)(targetDatabase)) {
7367
7681
  assertPrivateSourceFile(sourceDatabase, "adopted database");
7368
- ensureDirectory((0, import_node_path18.join)(paths.stateDir, "hub"), 448, true);
7682
+ ensureDirectory((0, import_node_path19.join)(paths.stateDir, "hub"), 448, true);
7369
7683
  await backupDatabase2(sourceDatabase, targetDatabase, host);
7370
7684
  copy.record.files.push({
7371
7685
  source: sourceDatabase,
7372
7686
  target: targetDatabase,
7373
7687
  sha256: sha256File2(targetDatabase),
7374
- bytes: (0, import_node_fs16.statSync)(targetDatabase).size
7688
+ bytes: (0, import_node_fs17.statSync)(targetDatabase).size
7375
7689
  });
7376
7690
  }
7377
- const sourceCollectorState = (0, import_node_path18.join)(source, "collector", "state.json");
7378
- if (role !== "hub" && (0, import_node_fs16.existsSync)(sourceCollectorState)) {
7691
+ const sourceCollectorState = (0, import_node_path19.join)(source, "collector", "state.json");
7692
+ if (role !== "hub" && (0, import_node_fs17.existsSync)(sourceCollectorState)) {
7379
7693
  assertPrivateSourceFile(sourceCollectorState, "adopted collector state");
7380
- ensureDirectory((0, import_node_path18.join)(paths.stateDir, "collector"), 448, true);
7694
+ ensureDirectory((0, import_node_path19.join)(paths.stateDir, "collector"), 448, true);
7381
7695
  const targetCollector = collectorStatePath2(paths);
7382
- (0, import_node_fs16.copyFileSync)(sourceCollectorState, targetCollector);
7383
- (0, import_node_fs16.chmodSync)(targetCollector, 384);
7696
+ (0, import_node_fs17.copyFileSync)(sourceCollectorState, targetCollector);
7697
+ (0, import_node_fs17.chmodSync)(targetCollector, 384);
7384
7698
  copy.record.files.push({
7385
7699
  source: sourceCollectorState,
7386
7700
  target: targetCollector,
7387
7701
  sha256: sha256File2(targetCollector),
7388
- bytes: (0, import_node_fs16.statSync)(targetCollector).size
7702
+ bytes: (0, import_node_fs17.statSync)(targetCollector).size
7389
7703
  });
7390
- const outbox = (0, import_node_path18.join)(source, "collector", "outbox");
7391
- if ((0, import_node_fs16.existsSync)(outbox)) copyAdoptedTree(outbox, (0, import_node_path18.join)(paths.stateDir, "collector", "outbox"), copy.record.files);
7392
7704
  }
7393
- const enrollment = (0, import_node_path18.join)(source, "hub", "enrollment.json");
7394
- if (role !== "collector" && (0, import_node_fs16.existsSync)(enrollment)) {
7705
+ const enrollment = (0, import_node_path19.join)(source, "hub", "enrollment.json");
7706
+ if (role !== "collector" && (0, import_node_fs17.existsSync)(enrollment)) {
7395
7707
  const target = hubEnrollmentPath(paths);
7396
- (0, import_node_fs16.copyFileSync)(enrollment, target);
7397
- (0, import_node_fs16.chmodSync)(target, 384);
7398
- copy.record.files.push({ source: enrollment, target, sha256: sha256File2(target), bytes: (0, import_node_fs16.statSync)(target).size });
7708
+ (0, import_node_fs17.copyFileSync)(enrollment, target);
7709
+ (0, import_node_fs17.chmodSync)(target, 384);
7710
+ copy.record.files.push({ source: enrollment, target, sha256: sha256File2(target), bytes: (0, import_node_fs17.statSync)(target).size });
7399
7711
  }
7400
7712
  }
7401
7713
  function writeSysusersFiles(paths, accounts, role, power, host) {
@@ -7435,15 +7747,15 @@ function writePowerRule(paths, accounts) {
7435
7747
  writeManagedFile(raplRulePath(paths), renderRaplRule(accounts), 420, true);
7436
7748
  }
7437
7749
  function powerOriginalPath(paths) {
7438
- return (0, import_node_path18.join)(paths.stateDir, POWER_STATE_FILE_NAME);
7750
+ return (0, import_node_path19.join)(paths.stateDir, POWER_STATE_FILE_NAME);
7439
7751
  }
7440
7752
  function capturePowerOriginals(paths) {
7441
7753
  const files = {};
7442
7754
  for (const domain of packageDomainPaths(paths.powercapRoot)) {
7443
7755
  for (const name of ["energy_uj", "max_energy_range_uj"]) {
7444
- const path2 = (0, import_node_path18.join)(domain, name);
7756
+ const path2 = (0, import_node_path19.join)(domain, name);
7445
7757
  try {
7446
- const stat = (0, import_node_fs16.statSync)(path2);
7758
+ const stat = (0, import_node_fs17.statSync)(path2);
7447
7759
  files[path2] = { mode: stat.mode & 511, uid: stat.uid, gid: stat.gid };
7448
7760
  } catch {
7449
7761
  }
@@ -7455,8 +7767,8 @@ function capturePowerOriginals(paths) {
7455
7767
  function applyPowerAccess(paths, accounts, host) {
7456
7768
  for (const domain of packageDomainPaths(paths.powercapRoot)) {
7457
7769
  for (const name of ["energy_uj", "max_energy_range_uj"]) {
7458
- const path2 = (0, import_node_path18.join)(domain, name);
7459
- if (!(0, import_node_fs16.existsSync)(path2)) continue;
7770
+ const path2 = (0, import_node_path19.join)(domain, name);
7771
+ if (!(0, import_node_fs17.existsSync)(path2)) continue;
7460
7772
  runRequired(host, "chgrp", [accounts.powerGroup, path2], `power counter group ${path2}`);
7461
7773
  runRequired(host, "chmod", ["0440", path2], `power counter mode ${path2}`);
7462
7774
  }
@@ -7469,9 +7781,9 @@ function resumePowerRule(paths, host) {
7469
7781
  }
7470
7782
  function packageDomainPaths(root) {
7471
7783
  try {
7472
- return (0, import_node_fs16.readdirSync)(root).filter((entry2) => /^intel-rapl:\d+$/.test(entry2)).map((entry2) => (0, import_node_path18.join)(root, entry2)).filter((path2) => {
7784
+ return (0, import_node_fs17.readdirSync)(root).filter((entry2) => /^intel-rapl:\d+$/.test(entry2)).map((entry2) => (0, import_node_path19.join)(root, entry2)).filter((path2) => {
7473
7785
  try {
7474
- return /^package/.test((0, import_node_fs16.readFileSync)((0, import_node_path18.join)(path2, "name"), "utf8").trim());
7786
+ return /^package/.test((0, import_node_fs17.readFileSync)((0, import_node_path19.join)(path2, "name"), "utf8").trim());
7475
7787
  } catch {
7476
7788
  return false;
7477
7789
  }
@@ -7491,8 +7803,8 @@ function restorePowerOriginals(paths, host) {
7491
7803
  const files = { ...originals?.files ?? {} };
7492
7804
  for (const domain of packageDomainPaths(paths.powercapRoot)) {
7493
7805
  for (const name of ["energy_uj", "max_energy_range_uj"]) {
7494
- const path2 = (0, import_node_path18.join)(domain, name);
7495
- if (!(0, import_node_fs16.existsSync)(path2)) continue;
7806
+ const path2 = (0, import_node_path19.join)(domain, name);
7807
+ if (!(0, import_node_fs17.existsSync)(path2)) continue;
7496
7808
  const original = files[path2];
7497
7809
  if (original) {
7498
7810
  runRequired(host, "chown", [`${original.uid}:${original.gid}`, path2], "restore counter ownership");
@@ -7508,7 +7820,7 @@ function restorePowerOriginals(paths, host) {
7508
7820
  }
7509
7821
  }
7510
7822
  try {
7511
- (0, import_node_fs16.unlinkSync)(powerOriginalPath(paths));
7823
+ (0, import_node_fs17.unlinkSync)(powerOriginalPath(paths));
7512
7824
  } catch {
7513
7825
  }
7514
7826
  return restored;
@@ -7532,7 +7844,7 @@ function stopUnits(host, units) {
7532
7844
  if (units.some((unit) => systemdActive(host, unit))) throw new SystemError("service-active", "system services did not stop; no files were restored");
7533
7845
  }
7534
7846
  function unitsForCleanup(paths, host) {
7535
- const legacy = LEGACY_UNIT_NAMES.filter((unit) => (0, import_node_fs16.existsSync)((0, import_node_path18.join)(paths.unitsDir, unit)) || systemdActive(host, unit));
7847
+ const legacy = LEGACY_UNIT_NAMES.filter((unit) => (0, import_node_fs17.existsSync)((0, import_node_path19.join)(paths.unitsDir, unit)) || systemdActive(host, unit));
7536
7848
  return [...Object.values(UNIT_NAMES), ...legacy];
7537
7849
  }
7538
7850
  function startUnits(host, units) {
@@ -7570,7 +7882,7 @@ async function verifySystemHealth(paths, state, host, timeoutMs, components = en
7570
7882
  const report = runRequired(
7571
7883
  host,
7572
7884
  state.nodePath,
7573
- [(0, import_node_path18.join)(paths.prefix, "current/dist/ops.cjs"), "inspect", "--database", hubDatabasePath(paths), "--json"],
7885
+ [(0, import_node_path19.join)(paths.prefix, "current/dist/ops.cjs"), "inspect", "--database", hubDatabasePath(paths), "--json"],
7574
7886
  "activated database verification",
7575
7887
  { timeoutMs: Math.max(100, deadline - Date.now()) }
7576
7888
  );
@@ -7586,8 +7898,8 @@ async function verifySystemHealth(paths, state, host, timeoutMs, components = en
7586
7898
  let problem = "no sampling marker";
7587
7899
  while (Date.now() < deadline) {
7588
7900
  try {
7589
- const marker = readJsonObject((0, import_node_path18.join)(paths.stateDir, "collector/health.json"));
7590
- const lease = readJsonObject((0, import_node_path18.join)(paths.stateDir, "collector/collector-runtime.lock"));
7901
+ const marker = readJsonObject((0, import_node_path19.join)(paths.stateDir, "collector/health.json"));
7902
+ const lease = readJsonObject((0, import_node_path19.join)(paths.stateDir, "collector/collector-runtime.lock"));
7591
7903
  if (marker.pid !== lease.pid || !Number.isSafeInteger(marker.pid)) throw new Error("sampling marker belongs to another process");
7592
7904
  process.kill(Number(marker.pid), 0);
7593
7905
  if (marker.version !== state.currentVersion || !Number.isFinite(marker.sampledAt) || Date.now() - Number(marker.sampledAt) > 15e3) throw new Error("collector version or sampling freshness is wrong");
@@ -7602,12 +7914,12 @@ async function verifySystemHealth(paths, state, host, timeoutMs, components = en
7602
7914
  }
7603
7915
  function chownAccount(paths, owner, group, host) {
7604
7916
  for (const path2 of paths) {
7605
- if (!(0, import_node_fs16.existsSync)(path2)) continue;
7917
+ if (!(0, import_node_fs17.existsSync)(path2)) continue;
7606
7918
  runRequired(host, "chown", ["-R", `${owner}:${group}`, path2], `ownership ${path2}`);
7607
7919
  }
7608
7920
  }
7609
7921
  function chmodTree(path2, host) {
7610
- if (!(0, import_node_fs16.existsSync)(path2)) return;
7922
+ if (!(0, import_node_fs17.existsSync)(path2)) return;
7611
7923
  runRequired(host, "chmod", ["-R", "u=rwX,go=rX", path2], `release permissions ${path2}`);
7612
7924
  }
7613
7925
  function pickRollbackBackup(state, previousSchema) {
@@ -7618,9 +7930,12 @@ function pickRollbackBackup(state, previousSchema) {
7618
7930
  async function recoverFailedUpgrade(options) {
7619
7931
  stopUnits(options.host, options.units);
7620
7932
  assertDirectoryStopped(options.paths.stateDir);
7621
- replaceSymlink((0, import_node_path18.join)(options.paths.prefix, "current"), options.oldCurrent, options.host);
7622
- if (options.oldPrevious === null) removeSymlink((0, import_node_path18.join)(options.paths.prefix, "previous"));
7623
- else replaceSymlink((0, import_node_path18.join)(options.paths.prefix, "previous"), options.oldPrevious, options.host);
7933
+ for (const message of undoMachineDataCleanup(systemMachineDataPaths(options.paths), options.machineData?.stagedQueues ?? null)) {
7934
+ console.warn(`rigscope system upgrade recovery: ${message}`);
7935
+ }
7936
+ replaceSymlink((0, import_node_path19.join)(options.paths.prefix, "current"), options.oldCurrent, options.host);
7937
+ if (options.oldPrevious === null) removeSymlink((0, import_node_path19.join)(options.paths.prefix, "previous"));
7938
+ else replaceSymlink((0, import_node_path19.join)(options.paths.prefix, "previous"), options.oldPrevious, options.host);
7624
7939
  const database = hubDatabasePath(options.paths);
7625
7940
  let afterSchema = null;
7626
7941
  try {
@@ -7628,13 +7943,13 @@ async function recoverFailedUpgrade(options) {
7628
7943
  } catch {
7629
7944
  afterSchema = null;
7630
7945
  }
7631
- if (options.backupPath !== null && (0, import_node_fs16.existsSync)(options.backupPath) && (afterSchema === null || options.beforeSchema === null || afterSchema > options.beforeSchema)) {
7632
- await backupDatabase2(database, (0, import_node_path18.join)(options.paths.stateDir, "backups", `failed-upgrade-${newestTimestamp()}.sqlite`), options.host);
7946
+ if (options.backupPath !== null && (0, import_node_fs17.existsSync)(options.backupPath) && (afterSchema === null || options.beforeSchema === null || afterSchema > options.beforeSchema || options.machineData?.databaseCleared === true)) {
7947
+ await backupDatabase2(database, (0, import_node_path19.join)(options.paths.stateDir, "backups", `failed-upgrade-${newestTimestamp()}.sqlite`), options.host);
7633
7948
  replaceDatabaseFromBackup(options.backupPath, database, null, options.host);
7634
7949
  runRequired(options.host, "chown", [`${options.state.accounts.hub}:${options.state.accounts.hub}`, database], "restored database ownership");
7635
7950
  }
7636
- options.state.previousVersion = options.oldPrevious === null ? null : (0, import_node_path18.basename)(options.oldPrevious);
7637
- options.state.currentVersion = (0, import_node_path18.basename)(options.oldCurrent);
7951
+ options.state.previousVersion = options.oldPrevious === null ? null : (0, import_node_path19.basename)(options.oldPrevious);
7952
+ options.state.currentVersion = (0, import_node_path19.basename)(options.oldCurrent);
7638
7953
  writeSystemState(options.paths, options.state);
7639
7954
  if (options.activeComponents.length > 0) {
7640
7955
  startUnits(options.host, options.units);
@@ -7643,14 +7958,14 @@ async function recoverFailedUpgrade(options) {
7643
7958
  }
7644
7959
  function assertManagedFileOwnership(paths, path2) {
7645
7960
  if (!isDefaultSystemLayout(paths)) return;
7646
- if (!(0, import_node_fs16.existsSync)(path2)) return;
7647
- const stat = (0, import_node_fs16.lstatSync)(path2);
7961
+ if (!(0, import_node_fs17.existsSync)(path2)) return;
7962
+ const stat = (0, import_node_fs17.lstatSync)(path2);
7648
7963
  if (stat.isSymbolicLink() || !stat.isFile()) throw new SystemError("unsafe-path", `${path2} must be a regular managed file`);
7649
7964
  if (stat.uid !== 0) throw new SystemError("insecure-ownership", `${path2} must be root-owned before replacement`);
7650
7965
  if ((stat.mode & 18) !== 0) throw new SystemError("insecure-permissions", `${path2} must not be group/world-writable`);
7651
7966
  }
7652
7967
  function writeUnitFile(paths, unit, content, replace) {
7653
- const path2 = (0, import_node_path18.join)(paths.unitsDir, unit);
7968
+ const path2 = (0, import_node_path19.join)(paths.unitsDir, unit);
7654
7969
  assertManagedFileOwnership(paths, path2);
7655
7970
  writeManagedFile(path2, content, 420, replace);
7656
7971
  }
@@ -7665,19 +7980,19 @@ function ensureComponentSetup(options) {
7665
7980
  ensureDirectory(paths.configDir, 493);
7666
7981
  ensureDirectory(paths.stateDir, 493);
7667
7982
  ensureDirectory(paths.unitsDir, 493);
7668
- ensureDirectory((0, import_node_path18.join)(paths.stateDir, "hub"), 448, true);
7669
- ensureDirectory((0, import_node_path18.join)(paths.stateDir, "hub", "run"), 448, true);
7670
- ensureDirectory((0, import_node_path18.join)(paths.stateDir, "collector"), 448, true);
7671
- ensureDirectory((0, import_node_path18.join)(paths.stateDir, "backups"), 448, true);
7983
+ ensureDirectory((0, import_node_path19.join)(paths.stateDir, "hub"), 448, true);
7984
+ ensureDirectory((0, import_node_path19.join)(paths.stateDir, "hub", "run"), 448, true);
7985
+ ensureDirectory((0, import_node_path19.join)(paths.stateDir, "collector"), 448, true);
7986
+ ensureDirectory((0, import_node_path19.join)(paths.stateDir, "backups"), 448, true);
7672
7987
  const release = currentReleaseOrThrow(paths);
7673
7988
  void release;
7674
7989
  writeSysusersFiles(paths, accounts, "local", power, host);
7675
7990
  if (component === "hub") {
7676
- if (!(0, import_node_fs16.existsSync)(hubEnvPath(paths))) {
7991
+ if (!(0, import_node_fs17.existsSync)(hubEnvPath(paths))) {
7677
7992
  const env = hubEnvTemplate(paths);
7678
7993
  writeManagedFile(hubEnvPath(paths), env, 384, false);
7679
7994
  }
7680
- if (!(0, import_node_fs16.existsSync)((0, import_node_path18.join)(paths.unitsDir, UNIT_NAMES.hub))) {
7995
+ if (!(0, import_node_fs17.existsSync)((0, import_node_path19.join)(paths.unitsDir, UNIT_NAMES.hub))) {
7681
7996
  const env = readHubEnvironment(paths);
7682
7997
  const remote = env.HUB_PUBLIC_ORIGIN !== void 0 && env.HUB_PUBLIC_ORIGIN !== "";
7683
7998
  const unit = renderSystemHubUnit({
@@ -7694,7 +8009,7 @@ function ensureComponentSetup(options) {
7694
8009
  `, false);
7695
8010
  }
7696
8011
  } else {
7697
- if (!(0, import_node_fs16.existsSync)(collectorEnvPath(paths))) {
8012
+ if (!(0, import_node_fs17.existsSync)(collectorEnvPath(paths))) {
7698
8013
  writeManagedFile(collectorEnvPath(paths), collectorEnvTemplate(), 384, false);
7699
8014
  }
7700
8015
  const unit = renderSystemCollectorUnit({
@@ -7710,17 +8025,17 @@ function ensureComponentSetup(options) {
7710
8025
  const validation = runRequired(
7711
8026
  host,
7712
8027
  "systemd-analyze",
7713
- ["verify", "--man=no", (0, import_node_path18.join)(paths.unitsDir, unitFor(component))],
8028
+ ["verify", "--man=no", (0, import_node_path19.join)(paths.unitsDir, unitFor(component))],
7714
8029
  "native systemd unit validation"
7715
8030
  );
7716
8031
  if (/path is not absolute|bad unit file|fatal error|unknown (?:key|lvalue)|ignoring/i.test(validation.stderr)) {
7717
8032
  throw new SystemError("invalid-unit", "native systemd validation rejected a generated directive");
7718
8033
  }
7719
- if (component === "hub") chownAccount([(0, import_node_path18.join)(paths.stateDir, "hub")], accounts.hub, accounts.hub, host);
7720
- if (component === "collector") chownAccount([(0, import_node_path18.join)(paths.stateDir, "collector")], accounts.collector, accounts.collector, host);
8034
+ if (component === "hub") chownAccount([(0, import_node_path19.join)(paths.stateDir, "hub")], accounts.hub, accounts.hub, host);
8035
+ if (component === "collector") chownAccount([(0, import_node_path19.join)(paths.stateDir, "collector")], accounts.collector, accounts.collector, host);
7721
8036
  }
7722
8037
  function ensurePowerAccess(paths, accounts, host) {
7723
- if (!(0, import_node_fs16.existsSync)(powerOriginalPath(paths))) capturePowerOriginals(paths);
8038
+ if (!(0, import_node_fs17.existsSync)(powerOriginalPath(paths))) capturePowerOriginals(paths);
7724
8039
  writePowerRule(paths, accounts);
7725
8040
  resumePowerRule(paths, host);
7726
8041
  applyPowerAccess(paths, accounts, host);
@@ -7738,7 +8053,7 @@ function resetUnfinishedSystemState(options) {
7738
8053
  }
7739
8054
  function installPreset(options, role) {
7740
8055
  if (options.adoptFrom !== void 0) {
7741
- return componentSet(readAdoptionSetup((0, import_node_path18.resolve)(options.adoptFrom)).components);
8056
+ return componentSet(readAdoptionSetup((0, import_node_path19.resolve)(options.adoptFrom)).components);
7742
8057
  }
7743
8058
  return componentsForRole(role);
7744
8059
  }
@@ -7750,11 +8065,11 @@ function planSystemInstall(options) {
7750
8065
  const power = options.power !== false && preset.collector;
7751
8066
  const initial = options.enable === true ? preset : { hub: false, collector: false };
7752
8067
  const files = [
7753
- { path: (0, import_node_path18.join)(paths.prefix, "current"), mode: "symlink", purpose: "active root-owned release" },
8068
+ { path: (0, import_node_path19.join)(paths.prefix, "current"), mode: "symlink", purpose: "active root-owned release" },
7754
8069
  { path: hubEnvPath(paths), mode: "0600", purpose: "root-owned hub configuration" },
7755
8070
  { path: collectorEnvPath(paths), mode: "0600", purpose: "root-owned collector configuration" },
7756
8071
  { path: sysusersPath(paths), mode: "0644", purpose: "dedicated nologin accounts" },
7757
- ...COMPONENTS3.map((component) => ({ path: (0, import_node_path18.join)(paths.unitsDir, unitFor(component)), mode: "0644", purpose: `${component} boot unit` }))
8072
+ ...COMPONENTS3.map((component) => ({ path: (0, import_node_path19.join)(paths.unitsDir, unitFor(component)), mode: "0644", purpose: `${component} boot unit` }))
7758
8073
  ];
7759
8074
  if (power) {
7760
8075
  files.push({ path: raplRulePath(paths), mode: "0644", purpose: "package RAPL read rule" });
@@ -7769,7 +8084,7 @@ function planSystemInstall(options) {
7769
8084
  ];
7770
8085
  const notes = [
7771
8086
  "Nothing is changed without --apply and root.",
7772
- options.adoptFrom ? `Adopts ${(0, import_node_path18.resolve)(options.adoptFrom)} by copy only; the original user setup is preserved.` : "Existing user services are not modified.",
8087
+ options.adoptFrom ? `Adopts ${(0, import_node_path19.resolve)(options.adoptFrom)} by copy only; the original user setup is preserved.` : "Existing user services are not modified.",
7773
8088
  "One complete package: both runtimes, units and accounts are installed; --role only sets the initial enabled preset.",
7774
8089
  "Hub and collector use separate unprivileged accounts; Node gets no capabilities.",
7775
8090
  ...options.enable && preset.hub ? ["Enabling the hub creates its owner account once and prints the password once."] : [],
@@ -7802,8 +8117,8 @@ async function installSystemUnlocked(options) {
7802
8117
  `existing user service(s) detected (${detail}); stop them and use --adopt-user-state for explicit copy-based adoption`
7803
8118
  );
7804
8119
  }
7805
- const source = (0, import_node_path18.resolve)(options.source);
7806
- if (!(0, import_node_fs16.existsSync)(source)) throw new SystemError("missing-source", `release source does not exist: ${source}`);
8120
+ const source = (0, import_node_path19.resolve)(options.source);
8121
+ if (!(0, import_node_fs17.existsSync)(source)) throw new SystemError("missing-source", `release source does not exist: ${source}`);
7807
8122
  const port = options.port ?? 8081;
7808
8123
  if (!Number.isInteger(port) || port < 1 || port > 65535) throw new SystemError("invalid-argument", "--port must be 1-65535");
7809
8124
  parseHubConfig(options.origin === void 0 ? { HUB_HOST: "127.0.0.1", HUB_PORT: String(port) } : { HUB_PUBLIC_ORIGIN: options.origin, HUB_SOCKET_PATH: hubSocketPath(paths) });
@@ -7824,14 +8139,14 @@ async function installSystemUnlocked(options) {
7824
8139
  });
7825
8140
  try {
7826
8141
  if (legacy) resetUnfinishedSystemState({ paths, host, now });
7827
- ensureDirectory((0, import_node_path18.join)(paths.prefix, "releases"), 493);
8142
+ ensureDirectory((0, import_node_path19.join)(paths.prefix, "releases"), 493);
7828
8143
  ensureDirectory(paths.stateDir, 493);
7829
8144
  ensureDirectory(paths.configDir, 493);
7830
8145
  ensureDirectory(paths.unitsDir, 493);
7831
- ensureDirectory((0, import_node_path18.join)(paths.stateDir, "hub"), 448, true);
7832
- ensureDirectory((0, import_node_path18.join)(paths.stateDir, "collector"), 448, true);
7833
- ensureDirectory((0, import_node_path18.join)(paths.stateDir, "backups"), 448, true);
7834
- ensureDirectory((0, import_node_path18.join)(paths.stateDir, "hub", "run"), 448, true);
8146
+ ensureDirectory((0, import_node_path19.join)(paths.stateDir, "hub"), 448, true);
8147
+ ensureDirectory((0, import_node_path19.join)(paths.stateDir, "collector"), 448, true);
8148
+ ensureDirectory((0, import_node_path19.join)(paths.stateDir, "backups"), 448, true);
8149
+ ensureDirectory((0, import_node_path19.join)(paths.stateDir, "hub", "run"), 448, true);
7835
8150
  const release = commitStagedRelease({ staged, paths, replaceRelease: options.replaceRelease });
7836
8151
  let adoption = null;
7837
8152
  if (options.adoptFrom) {
@@ -7864,13 +8179,13 @@ async function installSystemUnlocked(options) {
7864
8179
  `, true);
7865
8180
  writeUnitFile(paths, UNIT_NAMES.collector, `${collectorUnit}
7866
8181
  `, true);
7867
- const validation = runRequired(host, "systemd-analyze", ["verify", "--man=no", ...COMPONENTS3.map((component) => (0, import_node_path18.join)(paths.unitsDir, unitFor(component)))], "native systemd unit validation");
8182
+ const validation = runRequired(host, "systemd-analyze", ["verify", "--man=no", ...COMPONENTS3.map((component) => (0, import_node_path19.join)(paths.unitsDir, unitFor(component)))], "native systemd unit validation");
7868
8183
  if (/path is not absolute|bad unit file|fatal error|unknown (?:key|lvalue)|ignoring/i.test(validation.stderr)) {
7869
8184
  throw new SystemError("invalid-unit", "native systemd validation rejected a generated directive");
7870
8185
  }
7871
8186
  if (power) ensurePowerAccess(paths, accounts, host);
7872
- chownAccount([(0, import_node_path18.join)(paths.stateDir, "hub")], accounts.hub, accounts.hub, host);
7873
- chownAccount([(0, import_node_path18.join)(paths.stateDir, "collector")], accounts.collector, accounts.collector, host);
8187
+ chownAccount([(0, import_node_path19.join)(paths.stateDir, "hub")], accounts.hub, accounts.hub, host);
8188
+ chownAccount([(0, import_node_path19.join)(paths.stateDir, "collector")], accounts.collector, accounts.collector, host);
7874
8189
  const state = emptyState({ paths, accounts, role, components: { hub: false, collector: false }, nodePath, now });
7875
8190
  state.currentVersion = release.version;
7876
8191
  state.releases[release.version] = {
@@ -7880,7 +8195,7 @@ async function installSystemUnlocked(options) {
7880
8195
  };
7881
8196
  state.adoption = adoption?.record ?? null;
7882
8197
  state.power = power ? { enabled: true, rulePath: raplRulePath(paths), group: accounts.powerGroup } : null;
7883
- replaceSymlink((0, import_node_path18.join)(paths.prefix, "current"), release.releaseDir, host);
8198
+ replaceSymlink((0, import_node_path19.join)(paths.prefix, "current"), release.releaseDir, host);
7884
8199
  writeSystemState(paths, state);
7885
8200
  const wantsHub = preset.hub && (options.enable === true || options.start === true);
7886
8201
  const wantsCollector = preset.collector && (options.enable === true || options.start === true);
@@ -7906,6 +8221,7 @@ async function installSystemUnlocked(options) {
7906
8221
  await verifySystemHealth(paths, state, host, options.startupTimeoutMs ?? 2e4, started);
7907
8222
  }
7908
8223
  }
8224
+ recordMachineDataTransition(paths.stateDir, release.version, (path2, content) => atomicWrite(path2, content, 384));
7909
8225
  return {
7910
8226
  operation: "install",
7911
8227
  ok: true,
@@ -7934,14 +8250,14 @@ function systemStatus(options = {}) {
7934
8250
  } catch (error2) {
7935
8251
  stateError = error2 instanceof Error ? error2.message : String(error2);
7936
8252
  }
7937
- const presentUnits = [UNIT_NAMES.hub, UNIT_NAMES.collector].filter((unit) => (0, import_node_fs16.existsSync)((0, import_node_path18.join)(paths.unitsDir, unit)));
8253
+ const presentUnits = [UNIT_NAMES.hub, UNIT_NAMES.collector].filter((unit) => (0, import_node_fs17.existsSync)((0, import_node_path19.join)(paths.unitsDir, unit)));
7938
8254
  const publicRole = presentUnits.length === 2 ? "local" : presentUnits[0] === UNIT_NAMES.hub ? "hub" : presentUnits[0] === UNIT_NAMES.collector ? "collector" : null;
7939
8255
  const role = state?.role ?? publicRole ?? "local";
7940
8256
  const current = currentReleaseDir(paths);
7941
8257
  let publicVersion = null;
7942
8258
  if (current) {
7943
8259
  try {
7944
- const manifest = JSON.parse((0, import_node_fs16.readFileSync)((0, import_node_path18.join)(current, "package.json"), "utf8"));
8260
+ const manifest = JSON.parse((0, import_node_fs17.readFileSync)((0, import_node_path19.join)(current, "package.json"), "utf8"));
7945
8261
  if (typeof manifest.version === "string") publicVersion = manifest.version;
7946
8262
  } catch {
7947
8263
  }
@@ -7952,7 +8268,7 @@ function systemStatus(options = {}) {
7952
8268
  const componentReports = {};
7953
8269
  for (const component of COMPONENTS3) {
7954
8270
  const unit = unitFor(component);
7955
- const present = (0, import_node_fs16.existsSync)((0, import_node_path18.join)(paths.unitsDir, unit));
8271
+ const present = (0, import_node_fs17.existsSync)((0, import_node_path19.join)(paths.unitsDir, unit));
7956
8272
  const active = present && systemdActive(host, unit);
7957
8273
  const unitEnabled2 = present && systemdEnabled(host, unit);
7958
8274
  const enabled = state !== null ? state.components[component].enabled : unitEnabled2;
@@ -7961,7 +8277,7 @@ function systemStatus(options = {}) {
7961
8277
  componentReports[component] = {
7962
8278
  component,
7963
8279
  unit,
7964
- path: (0, import_node_path18.join)(paths.unitsDir, unit),
8280
+ path: (0, import_node_path19.join)(paths.unitsDir, unit),
7965
8281
  present,
7966
8282
  enabled,
7967
8283
  unitEnabled: unitEnabled2,
@@ -8005,16 +8321,16 @@ function systemStatus(options = {}) {
8005
8321
  })),
8006
8322
  database: {
8007
8323
  path: database,
8008
- exists: (0, import_node_fs16.existsSync)(database),
8324
+ exists: (0, import_node_fs17.existsSync)(database),
8009
8325
  schema,
8010
- mode: (0, import_node_fs16.existsSync)(database) ? modeString((0, import_node_fs16.statSync)(database).mode) : null,
8326
+ mode: (0, import_node_fs17.existsSync)(database) ? modeString((0, import_node_fs17.statSync)(database).mode) : null,
8011
8327
  lock
8012
8328
  },
8013
- socket: { path: hubSocketPath(paths), exists: (0, import_node_fs16.existsSync)(hubSocketPath(paths)) },
8329
+ socket: { path: hubSocketPath(paths), exists: (0, import_node_fs17.existsSync)(hubSocketPath(paths)) },
8014
8330
  config: {
8015
8331
  dir: paths.configDir,
8016
- hubEnv: { path: hubEnvPath(paths), present: (0, import_node_fs16.existsSync)(hubEnvPath(paths)) },
8017
- collectorEnv: { path: collectorEnvPath(paths), present: (0, import_node_fs16.existsSync)(collectorEnvPath(paths)) }
8332
+ hubEnv: { path: hubEnvPath(paths), present: (0, import_node_fs17.existsSync)(hubEnvPath(paths)) },
8333
+ collectorEnv: { path: collectorEnvPath(paths), present: (0, import_node_fs17.existsSync)(collectorEnvPath(paths)) }
8018
8334
  },
8019
8335
  adoption: state?.adoption ?? null,
8020
8336
  power,
@@ -8183,7 +8499,7 @@ async function enableSystem(options) {
8183
8499
  ],
8184
8500
  files: [
8185
8501
  { path: component === "hub" ? hubEnvPath(paths) : collectorEnvPath(paths), mode: "0600", purpose: `${component} configuration` },
8186
- { path: (0, import_node_path18.join)(paths.unitsDir, unitFor(component)), mode: "0644", purpose: `${component} boot unit` }
8502
+ { path: (0, import_node_path19.join)(paths.unitsDir, unitFor(component)), mode: "0644", purpose: `${component} boot unit` }
8187
8503
  ],
8188
8504
  notes: [
8189
8505
  "Enables only the requested component; the other component keeps running.",
@@ -8251,7 +8567,7 @@ function disableSystem(options) {
8251
8567
  accounts: state.accounts,
8252
8568
  commands: [`systemctl stop ${unitFor(component)}`, `systemctl disable ${unitFor(component)}`],
8253
8569
  files: [],
8254
- notes: ["Settings, credentials, history and outboxes are retained while the component is disabled."]
8570
+ notes: ["Settings, credentials and stored history are retained while the component is disabled; unacknowledged samples held in the collector's RAM delivery queue drain with the process."]
8255
8571
  };
8256
8572
  if (options.apply !== true) return { operation: "disable", ok: true, apply: false, message: "plan only", plan };
8257
8573
  assertApply(host);
@@ -8273,7 +8589,7 @@ function disableSystem(options) {
8273
8589
  function readPrivateToken(path2, read) {
8274
8590
  let stat;
8275
8591
  try {
8276
- stat = (0, import_node_fs16.lstatSync)(path2);
8592
+ stat = (0, import_node_fs17.lstatSync)(path2);
8277
8593
  } catch (error2) {
8278
8594
  if (error2.code === "ENOENT") {
8279
8595
  throw new SystemError("missing-grant", `grant file does not exist: ${path2}`);
@@ -8284,7 +8600,7 @@ function readPrivateToken(path2, read) {
8284
8600
  if ((stat.mode & 63) !== 0) throw new SystemError("insecure-permissions", `grant file must be private (0600): ${path2}`);
8285
8601
  if (stat.size < 1 || stat.size > 4096) throw new SystemError("invalid-token", `grant file must contain 1-4096 bytes: ${path2}`);
8286
8602
  if (!read) return void 0;
8287
- const token = (0, import_node_fs16.readFileSync)(path2, "utf8").trim();
8603
+ const token = (0, import_node_fs17.readFileSync)(path2, "utf8").trim();
8288
8604
  if (token === "" || /[\r\n\0]/.test(token) || !/^[\x21-\x7e]{1,256}$/.test(token)) {
8289
8605
  throw new SystemError("invalid-token", "grant file contains an invalid token");
8290
8606
  }
@@ -8296,7 +8612,7 @@ async function connectSystem(options) {
8296
8612
  const state = readSystemState(paths);
8297
8613
  if (state === null || !state.active) throw new SystemError("not-installed", "no active system installation; run install first");
8298
8614
  const destination = connectDestinationUrl(options.url);
8299
- const tokenFile = (0, import_node_path18.resolve)(options.tokenFile);
8615
+ const tokenFile = (0, import_node_path19.resolve)(options.tokenFile);
8300
8616
  const configured = collectorConfigured(paths);
8301
8617
  if (configured.configured) {
8302
8618
  throw new SystemError(
@@ -8344,12 +8660,12 @@ async function connectSystem(options) {
8344
8660
  `collector enrollment failed: ${error2 instanceof Error ? error2.message : String(error2)}`
8345
8661
  );
8346
8662
  } finally {
8347
- chownAccount([(0, import_node_path18.join)(paths.stateDir, "collector")], state.accounts.collector, state.accounts.collector, host);
8663
+ chownAccount([(0, import_node_path19.join)(paths.stateDir, "collector")], state.accounts.collector, state.accounts.collector, host);
8348
8664
  }
8349
8665
  if (!collectorConfigured(paths).configured) {
8350
8666
  throw new SystemError("enrollment-failed", "the hub accepted enrollment but no destination was saved; check private state");
8351
8667
  }
8352
- chownAccount([(0, import_node_path18.join)(paths.stateDir, "collector")], state.accounts.collector, state.accounts.collector, host);
8668
+ chownAccount([(0, import_node_path19.join)(paths.stateDir, "collector")], state.accounts.collector, state.accounts.collector, host);
8353
8669
  writeSystemState(paths, state);
8354
8670
  const enabled = state.components.collector.enabled;
8355
8671
  if (enabled || wasActive) {
@@ -8366,13 +8682,16 @@ async function connectSystem(options) {
8366
8682
  state
8367
8683
  };
8368
8684
  }
8685
+ function systemMachineDataPaths(paths) {
8686
+ return { database: hubDatabasePath(paths), queueTree: (0, import_node_path19.join)(paths.stateDir, "collector", "outbox") };
8687
+ }
8369
8688
  async function upgradeSystemUnlocked(options) {
8370
8689
  const host = options.host ?? createSystemHost();
8371
8690
  const now = options.now ?? Date.now;
8372
8691
  const paths = validateSystemPaths(options.paths);
8373
8692
  if (systemStateIsLegacy(paths)) {
8374
- const source2 = (0, import_node_path18.resolve)(options.source);
8375
- if (!(0, import_node_fs16.existsSync)(source2)) throw new SystemError("missing-source", `release source does not exist: ${source2}`);
8693
+ const source2 = (0, import_node_path19.resolve)(options.source);
8694
+ if (!(0, import_node_fs17.existsSync)(source2)) throw new SystemError("missing-source", `release source does not exist: ${source2}`);
8376
8695
  const role = legacySystemRole(paths);
8377
8696
  const preset = componentsForRole(role);
8378
8697
  const enable = options.enable ?? (options.start === true || unitNamesForComponents(preset).some((unit) => systemdEnabled(host, unit)));
@@ -8395,7 +8714,7 @@ async function upgradeSystemUnlocked(options) {
8395
8714
  ],
8396
8715
  files: [
8397
8716
  { path: stateFilePath(paths), mode: "0600", purpose: "canonical replacement state" },
8398
- { path: (0, import_node_path18.join)(paths.stateDir, "backups"), mode: "0700", purpose: "removed with unfinished history" }
8717
+ { path: (0, import_node_path19.join)(paths.stateDir, "backups"), mode: "0700", purpose: "removed with unfinished history" }
8399
8718
  ],
8400
8719
  notes: [
8401
8720
  "An unfinished installation is reset, not migrated; its history and credentials are removed.",
@@ -8428,6 +8747,7 @@ async function upgradeSystemUnlocked(options) {
8428
8747
  if (state === null || !state.active) throw new SystemError("not-installed", "no active system installation; run install first");
8429
8748
  const activeComponents = COMPONENTS3.filter((component) => systemdActive(host, unitFor(component)));
8430
8749
  const activeUnits = activeComponents.map(unitFor);
8750
+ const transitionPending = readMachineDataTransition(paths.stateDir) === null;
8431
8751
  const plan = {
8432
8752
  operation: "upgrade",
8433
8753
  apply: options.apply === true,
@@ -8435,19 +8755,34 @@ async function upgradeSystemUnlocked(options) {
8435
8755
  components: { hub: state.components.hub.enabled, collector: state.components.collector.enabled },
8436
8756
  paths,
8437
8757
  accounts: state.accounts,
8438
- commands: ["stage new release", ...activeUnits.map((unit) => `systemctl stop ${unit}`), "backup database", "switch current", ...activeUnits.map((unit) => `systemctl start ${unit}`)],
8439
- files: [{ path: (0, import_node_path18.join)(paths.stateDir, "backups"), mode: "0700", purpose: "pre-upgrade database backup" }],
8758
+ commands: [
8759
+ "stage new release",
8760
+ ...activeUnits.map((unit) => `systemctl stop ${unit}`),
8761
+ "backup database",
8762
+ ...transitionPending ? ["discard legacy machine data and collector queues once (irreversible after success)"] : [],
8763
+ "switch current",
8764
+ ...activeUnits.map((unit) => `systemctl start ${unit}`)
8765
+ ],
8766
+ files: [
8767
+ { path: (0, import_node_path19.join)(paths.stateDir, "backups"), mode: "0700", purpose: "pre-upgrade database backup" },
8768
+ ...transitionPending ? [{ path: (0, import_node_path19.join)(paths.stateDir, "machine-data-transition.json"), mode: "0600", purpose: "written once after the transition completes" }] : []
8769
+ ],
8440
8770
  notes: [
8441
8771
  "Staging happens before any service is stopped.",
8442
8772
  "The exact active component set is restored; --start does not start a component that was stopped.",
8443
- "A failed post-upgrade start restores the previous release and matching database backup."
8773
+ "A failed post-upgrade start restores the previous release and matching database backup.",
8774
+ ...transitionPending ? [
8775
+ "This upgrade completes the one-time RAM-first machine-data transition: telemetry rows, summaries, receipts, gaps, latest snapshots, inventory and machine-derived alert history are discarded, plus the pre-RAM-first durable collector queue. The transition's database copy restores the rows and the staged queue is renamed back if activation fails.",
8776
+ `The transition always keeps ${machineDataKeptNote()}.`,
8777
+ "Later upgrades keep the marker and preserve durable history and queues."
8778
+ ] : ["The one-time machine-data transition already completed; durable history and collector queues are preserved."]
8444
8779
  ]
8445
8780
  };
8446
8781
  if (options.apply !== true) return { operation: "upgrade", ok: true, apply: false, message: "plan only", plan };
8447
8782
  assertApply(host);
8448
8783
  assertManagedPathSafety(paths, isDefaultSystemLayout(paths));
8449
- const source = (0, import_node_path18.resolve)(options.source);
8450
- if (!(0, import_node_fs16.existsSync)(source)) throw new SystemError("missing-source", `release source does not exist: ${source}`);
8784
+ const source = (0, import_node_path19.resolve)(options.source);
8785
+ if (!(0, import_node_fs17.existsSync)(source)) throw new SystemError("missing-source", `release source does not exist: ${source}`);
8451
8786
  const staged = prepareStagedRelease({
8452
8787
  source,
8453
8788
  paths,
@@ -8466,21 +8801,22 @@ async function upgradeSystemUnlocked(options) {
8466
8801
  let backupPath = null;
8467
8802
  let beforeSchema = null;
8468
8803
  let release = null;
8804
+ let machineData = null;
8469
8805
  try {
8470
8806
  stopUnits(host, activeUnits);
8471
8807
  assertDirectoryStopped(paths.stateDir);
8472
8808
  const lock = systemLockState(paths);
8473
8809
  if (lock.live) throw new SystemError("duplicate-runtime", `hub did not stop cleanly; lock remains at ${lock.path}`);
8474
8810
  const database = hubDatabasePath(paths);
8475
- if ((0, import_node_fs16.existsSync)(database)) {
8476
- ensureDirectory((0, import_node_path18.join)(paths.stateDir, "backups"), 448, true);
8811
+ if ((0, import_node_fs17.existsSync)(database)) {
8812
+ ensureDirectory((0, import_node_path19.join)(paths.stateDir, "backups"), 448, true);
8477
8813
  beforeSchema = databaseSchema(database);
8478
- backupPath = (0, import_node_path18.join)(paths.stateDir, "backups", `pre-upgrade-${staged.version}-${newestTimestamp()}.sqlite`);
8814
+ backupPath = (0, import_node_path19.join)(paths.stateDir, "backups", `pre-upgrade-${staged.version}-${newestTimestamp()}.sqlite`);
8479
8815
  await backupDatabase2(database, backupPath, host);
8480
8816
  }
8481
8817
  release = commitStagedRelease({ staged, paths, replaceRelease: options.replaceRelease });
8482
- replaceSymlink((0, import_node_path18.join)(paths.prefix, "previous"), oldCurrent, host);
8483
- replaceSymlink((0, import_node_path18.join)(paths.prefix, "current"), release.releaseDir, host);
8818
+ replaceSymlink((0, import_node_path19.join)(paths.prefix, "previous"), oldCurrent, host);
8819
+ replaceSymlink((0, import_node_path19.join)(paths.prefix, "current"), release.releaseDir, host);
8484
8820
  state.previousVersion = state.currentVersion;
8485
8821
  state.currentVersion = release.version;
8486
8822
  state.releases[release.version] = {
@@ -8489,7 +8825,7 @@ async function upgradeSystemUnlocked(options) {
8489
8825
  installedAt: new Date(now()).toISOString()
8490
8826
  };
8491
8827
  if (backupPath !== null) {
8492
- state.backups[(0, import_node_path18.basename)(backupPath)] = {
8828
+ state.backups[(0, import_node_path19.basename)(backupPath)] = {
8493
8829
  path: backupPath,
8494
8830
  schemaVersion: beforeSchema,
8495
8831
  fromSchema: beforeSchema,
@@ -8500,6 +8836,7 @@ async function upgradeSystemUnlocked(options) {
8500
8836
  };
8501
8837
  }
8502
8838
  writeSystemState(paths, state);
8839
+ machineData = performMachineDataCleanup(paths.stateDir, systemMachineDataPaths(paths), now);
8503
8840
  if (activeComponents.length > 0) {
8504
8841
  startUnits(host, activeUnits);
8505
8842
  await verifySystemHealth(paths, state, host, options.startupTimeoutMs ?? 2e4, activeComponents);
@@ -8512,6 +8849,7 @@ async function upgradeSystemUnlocked(options) {
8512
8849
  oldPrevious,
8513
8850
  beforeSchema,
8514
8851
  backupPath,
8852
+ machineData,
8515
8853
  host,
8516
8854
  activeComponents,
8517
8855
  units: activeUnits
@@ -8519,6 +8857,10 @@ async function upgradeSystemUnlocked(options) {
8519
8857
  throw error2;
8520
8858
  }
8521
8859
  const upgraded = release;
8860
+ if (machineData !== null) {
8861
+ completeMachineDataCleanup(paths.stateDir, machineData, upgraded.version, (path2, content) => atomicWrite(path2, content, 384));
8862
+ }
8863
+ const machineHistory = machineData?.report;
8522
8864
  return {
8523
8865
  operation: "upgrade",
8524
8866
  ok: true,
@@ -8528,6 +8870,7 @@ async function upgradeSystemUnlocked(options) {
8528
8870
  current: upgraded.releaseDir,
8529
8871
  previous: oldCurrent,
8530
8872
  backupPath,
8873
+ ...machineHistory ? { machineHistory } : {},
8531
8874
  state
8532
8875
  };
8533
8876
  } finally {
@@ -8535,7 +8878,7 @@ async function upgradeSystemUnlocked(options) {
8535
8878
  }
8536
8879
  }
8537
8880
  function systemOperationLockPath(paths) {
8538
- return (0, import_node_path18.join)(paths.prefix, "run", "operation.lock");
8881
+ return (0, import_node_path19.join)(paths.prefix, "run", "operation.lock");
8539
8882
  }
8540
8883
  async function withSystemOperation(options, operation) {
8541
8884
  if (!options.apply) return operation();
@@ -8543,7 +8886,7 @@ async function withSystemOperation(options, operation) {
8543
8886
  assertApply(host);
8544
8887
  const paths = validateSystemPaths(options.paths);
8545
8888
  ensureDirectory(paths.prefix, 493);
8546
- ensureDirectory((0, import_node_path18.join)(paths.prefix, "run"), 448, true);
8889
+ ensureDirectory((0, import_node_path19.join)(paths.prefix, "run"), 448, true);
8547
8890
  const release = acquireProcessLease(systemOperationLockPath(paths));
8548
8891
  try {
8549
8892
  return await operation();
@@ -8595,24 +8938,24 @@ async function rollbackSystemUnlocked(options = {}) {
8595
8938
  try {
8596
8939
  stopUnits(host, units);
8597
8940
  assertDirectoryStopped(paths.stateDir);
8598
- if (systemLockState(paths).live || lockIsLive((0, import_node_path18.join)(paths.stateDir, "collector/collector-runtime.lock")).live) {
8941
+ if (systemLockState(paths).live || lockIsLive((0, import_node_path19.join)(paths.stateDir, "collector/collector-runtime.lock")).live) {
8599
8942
  throw new SystemError("runtime-active", "runtime lease remains after stop; rollback refused");
8600
8943
  }
8601
8944
  const database = hubDatabasePath(paths);
8602
- const currentSchema = (0, import_node_fs16.existsSync)(database) ? databaseSchema(database) : null;
8945
+ const currentSchema = (0, import_node_fs17.existsSync)(database) ? databaseSchema(database) : null;
8603
8946
  let backupToRestore = null;
8604
8947
  if (currentSchema !== null && (previousSchema === null || currentSchema > previousSchema)) {
8605
8948
  backupToRestore = pickRollbackBackup(state, previousSchema);
8606
- if (backupToRestore === null || !(0, import_node_fs16.existsSync)(backupToRestore.path)) {
8949
+ if (backupToRestore === null || !(0, import_node_fs17.existsSync)(backupToRestore.path)) {
8607
8950
  throw new SystemError(
8608
8951
  "incompatible-schema",
8609
8952
  `current database schema ${currentSchema} is newer than release ${previousVersion}; no matching pre-upgrade backup is recorded`
8610
8953
  );
8611
8954
  }
8612
8955
  }
8613
- if ((0, import_node_fs16.existsSync)(database)) {
8614
- ensureDirectory((0, import_node_path18.join)(paths.stateDir, "backups"), 448, true);
8615
- safetyBackup = (0, import_node_path18.join)(paths.stateDir, "backups", `pre-rollback-${newestTimestamp()}.sqlite`);
8956
+ if ((0, import_node_fs17.existsSync)(database)) {
8957
+ ensureDirectory((0, import_node_path19.join)(paths.stateDir, "backups"), 448, true);
8958
+ safetyBackup = (0, import_node_path19.join)(paths.stateDir, "backups", `pre-rollback-${newestTimestamp()}.sqlite`);
8616
8959
  await backupDatabase2(database, safetyBackup, host);
8617
8960
  }
8618
8961
  if (backupToRestore !== null) {
@@ -8620,8 +8963,8 @@ async function rollbackSystemUnlocked(options = {}) {
8620
8963
  restoredDatabase = true;
8621
8964
  runRequired(host, "chown", [`${state.accounts.hub}:${state.accounts.hub}`, database], "restored database ownership");
8622
8965
  }
8623
- replaceSymlink((0, import_node_path18.join)(paths.prefix, "previous"), oldCurrent, host);
8624
- replaceSymlink((0, import_node_path18.join)(paths.prefix, "current"), previous, host);
8966
+ replaceSymlink((0, import_node_path19.join)(paths.prefix, "previous"), oldCurrent, host);
8967
+ replaceSymlink((0, import_node_path19.join)(paths.prefix, "current"), previous, host);
8625
8968
  state.previousVersion = state.currentVersion;
8626
8969
  state.currentVersion = previousVersion;
8627
8970
  state.updatedAt = new Date(now()).toISOString();
@@ -8643,10 +8986,10 @@ async function rollbackSystemUnlocked(options = {}) {
8643
8986
  };
8644
8987
  } catch (error2) {
8645
8988
  stopUnits(host, units);
8646
- replaceSymlink((0, import_node_path18.join)(paths.prefix, "current"), oldCurrent, host);
8647
- replaceSymlink((0, import_node_path18.join)(paths.prefix, "previous"), previous, host);
8989
+ replaceSymlink((0, import_node_path19.join)(paths.prefix, "current"), oldCurrent, host);
8990
+ replaceSymlink((0, import_node_path19.join)(paths.prefix, "previous"), previous, host);
8648
8991
  if (restoredDatabase && safetyBackup) {
8649
- const failed = (0, import_node_path18.join)(paths.stateDir, "backups", `failed-rollback-${newestTimestamp()}.sqlite`);
8992
+ const failed = (0, import_node_path19.join)(paths.stateDir, "backups", `failed-rollback-${newestTimestamp()}.sqlite`);
8650
8993
  await backupDatabase2(hubDatabasePath(paths), failed, host);
8651
8994
  replaceDatabaseFromBackup(safetyBackup, hubDatabasePath(paths), null, host);
8652
8995
  runRequired(host, "chown", [`${state.accounts.hub}:${state.accounts.hub}`, hubDatabasePath(paths)], "restored database ownership");
@@ -8703,31 +9046,31 @@ function uninstallSystem(options = {}) {
8703
9046
  assertDirectoryStopped(paths.stateDir);
8704
9047
  for (const unit of units) runMaybe(host, "systemctl", ["disable", unit]);
8705
9048
  for (const unit of units) {
8706
- const path2 = (0, import_node_path18.join)(paths.unitsDir, unit);
9049
+ const path2 = (0, import_node_path19.join)(paths.unitsDir, unit);
8707
9050
  assertManagedFileOwnership(paths, path2);
8708
9051
  try {
8709
- (0, import_node_fs16.unlinkSync)(path2);
9052
+ (0, import_node_fs17.unlinkSync)(path2);
8710
9053
  } catch {
8711
9054
  }
8712
9055
  }
8713
9056
  runMaybe(host, "systemctl", ["daemon-reload"]);
8714
- const hadPowerAccess = state?.power?.enabled === true || (0, import_node_fs16.existsSync)(raplRulePath(paths)) || (0, import_node_fs16.existsSync)(powerOriginalPath(paths));
9057
+ const hadPowerAccess = state?.power?.enabled === true || (0, import_node_fs17.existsSync)(raplRulePath(paths)) || (0, import_node_fs17.existsSync)(powerOriginalPath(paths));
8715
9058
  if (hadPowerAccess) {
8716
9059
  try {
8717
- (0, import_node_fs16.unlinkSync)(raplRulePath(paths));
9060
+ (0, import_node_fs17.unlinkSync)(raplRulePath(paths));
8718
9061
  } catch {
8719
9062
  }
8720
9063
  try {
8721
- (0, import_node_fs16.unlinkSync)(powerSysusersPath(paths));
9064
+ (0, import_node_fs17.unlinkSync)(powerSysusersPath(paths));
8722
9065
  } catch {
8723
9066
  }
8724
9067
  restorePowerOriginals(paths, host);
8725
9068
  resumePowerRule(paths, host);
8726
9069
  }
8727
- removeSymlink((0, import_node_path18.join)(paths.prefix, "current"));
8728
- removeSymlink((0, import_node_path18.join)(paths.prefix, "previous"));
8729
- (0, import_node_fs16.rmSync)((0, import_node_path18.join)(paths.prefix, "releases"), { recursive: true, force: true });
8730
- if (options.preserveNode !== true) (0, import_node_fs16.rmSync)((0, import_node_path18.join)(paths.prefix, "node"), { recursive: true, force: true });
9070
+ removeSymlink((0, import_node_path19.join)(paths.prefix, "current"));
9071
+ removeSymlink((0, import_node_path19.join)(paths.prefix, "previous"));
9072
+ (0, import_node_fs17.rmSync)((0, import_node_path19.join)(paths.prefix, "releases"), { recursive: true, force: true });
9073
+ if (options.preserveNode !== true) (0, import_node_fs17.rmSync)((0, import_node_path19.join)(paths.prefix, "node"), { recursive: true, force: true });
8731
9074
  if (state !== null) {
8732
9075
  state.active = false;
8733
9076
  state.power = null;
@@ -8740,8 +9083,8 @@ function uninstallSystem(options = {}) {
8740
9083
  for (const group of [accounts.powerGroup, accounts.hub, accounts.collector]) runMaybe(host, "groupdel", [group]);
8741
9084
  }
8742
9085
  if (options.purgeData === true) {
8743
- (0, import_node_fs16.rmSync)(paths.stateDir, { recursive: true, force: true });
8744
- (0, import_node_fs16.rmSync)(paths.configDir, { recursive: true, force: true });
9086
+ (0, import_node_fs17.rmSync)(paths.stateDir, { recursive: true, force: true });
9087
+ (0, import_node_fs17.rmSync)(paths.configDir, { recursive: true, force: true });
8745
9088
  }
8746
9089
  return {
8747
9090
  operation: "uninstall",
@@ -8791,7 +9134,7 @@ function grantPower(options = {}) {
8791
9134
  writeManagedFile(powerSysusersPath(paths), renderPowerSysusers(paths, accounts), 420, true);
8792
9135
  runRequired(host, "systemd-sysusers", [], "power group creation");
8793
9136
  assertSystemAccounts(host, accounts, true, ["collector"]);
8794
- if (!(0, import_node_fs16.existsSync)(powerOriginalPath(paths))) capturePowerOriginals(paths);
9137
+ if (!(0, import_node_fs17.existsSync)(powerOriginalPath(paths))) capturePowerOriginals(paths);
8795
9138
  writePowerRule(paths, accounts);
8796
9139
  resumePowerRule(paths, host);
8797
9140
  applyPowerAccess(paths, accounts, host);
@@ -8820,11 +9163,11 @@ function revokePower(options = {}) {
8820
9163
  if (options.apply !== true) return { operation: "power-revoke", ok: true, apply: false, message: "plan only", plan };
8821
9164
  assertApply(host);
8822
9165
  try {
8823
- (0, import_node_fs16.unlinkSync)(raplRulePath(paths));
9166
+ (0, import_node_fs17.unlinkSync)(raplRulePath(paths));
8824
9167
  } catch {
8825
9168
  }
8826
9169
  try {
8827
- (0, import_node_fs16.unlinkSync)(powerSysusersPath(paths));
9170
+ (0, import_node_fs17.unlinkSync)(powerSysusersPath(paths));
8828
9171
  } catch {
8829
9172
  }
8830
9173
  restorePowerOriginals(paths, host);
@@ -9040,6 +9383,12 @@ function printMutationReport(report) {
9040
9383
  console.log("then run `rigscope system connect https://<hub> --token-file <file> --apply`.");
9041
9384
  }
9042
9385
  }
9386
+ function printMachineDataOutcome(machineHistory) {
9387
+ if (machineHistory === void 0) return;
9388
+ const described = describeMachineDataReport(machineHistory);
9389
+ console.log(described.summary);
9390
+ if (described.warning) console.log(`Warning: ${described.warning}`);
9391
+ }
9043
9392
  async function runSystemCommand(argv) {
9044
9393
  const parsed = parseSystemArgs(argv);
9045
9394
  if (parsed.command === "help") {
@@ -9111,7 +9460,7 @@ ${humanPlan(report.plan)}`);
9111
9460
  if (parsed.command === "logs") {
9112
9461
  const selected = validateSystemPaths(paths);
9113
9462
  const component = componentFrom(parsed, false);
9114
- const units = (component ? [unitFor(component)] : [UNIT_NAMES.hub, UNIT_NAMES.collector]).filter((unit) => (0, import_node_fs16.existsSync)((0, import_node_path18.join)(selected.unitsDir, unit)));
9463
+ const units = (component ? [unitFor(component)] : [UNIT_NAMES.hub, UNIT_NAMES.collector]).filter((unit) => (0, import_node_fs17.existsSync)((0, import_node_path19.join)(selected.unitsDir, unit)));
9115
9464
  if (units.length === 0) throw new SystemError("not-installed", component ? `${component} logs are unavailable; its unit is not installed` : "no system services installed");
9116
9465
  const result = (0, import_node_child_process5.spawnSync)("journalctl", [...units.flatMap((unit) => ["--unit", unit]), "--lines=100", "--no-pager", ...boolValue(values, "follow") ? ["--follow"] : []], { stdio: "inherit" });
9117
9466
  if (result.error) throw result.error;
@@ -9187,6 +9536,7 @@ ${humanPlan(report.plan)}`);
9187
9536
  else {
9188
9537
  printMutationReport(report);
9189
9538
  console.log(`Backup: ${report.backupPath ?? "none"}`);
9539
+ printMachineDataOutcome(report.machineHistory);
9190
9540
  }
9191
9541
  return 0;
9192
9542
  }
@@ -9292,7 +9642,7 @@ if (isMainModule()) {
9292
9642
  }
9293
9643
 
9294
9644
  // src/cli/selection.ts
9295
- var import_node_fs17 = require("node:fs");
9645
+ var import_node_fs18 = require("node:fs");
9296
9646
  function selectInstallation(options) {
9297
9647
  if (options.scope !== void 0 && options.scope !== "user" && options.scope !== "system") {
9298
9648
  throw new Error("--scope must be user or system.");
@@ -9308,16 +9658,16 @@ function selectInstallation(options) {
9308
9658
  return options.systemPresent ? "system" : "user";
9309
9659
  }
9310
9660
  function systemInstallationPresent() {
9311
- return (0, import_node_fs17.existsSync)("/opt/rigscope/current") || (0, import_node_fs17.existsSync)("/etc/systemd/system/rigscope-hub.service") || (0, import_node_fs17.existsSync)("/etc/systemd/system/rigscope-collector.service");
9661
+ return (0, import_node_fs18.existsSync)("/opt/rigscope/current") || (0, import_node_fs18.existsSync)("/etc/systemd/system/rigscope-hub.service") || (0, import_node_fs18.existsSync)("/etc/systemd/system/rigscope-collector.service");
9312
9662
  }
9313
9663
 
9314
9664
  // src/cli/access.ts
9315
- var import_node_fs18 = require("node:fs");
9316
- var import_node_path19 = require("node:path");
9317
- var import_node_crypto13 = require("node:crypto");
9665
+ var import_node_fs19 = require("node:fs");
9666
+ var import_node_path20 = require("node:path");
9667
+ var import_node_crypto14 = require("node:crypto");
9318
9668
 
9319
9669
  // collector/destinations/outbox.ts
9320
- var import_node_crypto12 = require("node:crypto");
9670
+ var import_node_crypto13 = require("node:crypto");
9321
9671
  var path = __toESM(require("node:path"));
9322
9672
  var import_node_sqlite5 = require("node:sqlite");
9323
9673
  var DEFAULT_OUTBOX_LIMITS = {
@@ -9329,7 +9679,7 @@ var DEFAULT_OUTBOX_LIMITS = {
9329
9679
  var DEFAULT_OUTBOX_DISK_BYTES = 80 * 1024 * 1024;
9330
9680
  var WRITE_HEADROOM_BYTES = 16 * 1024;
9331
9681
  function outboxDirFor(stateDir, url) {
9332
- const digest = (0, import_node_crypto12.createHash)("sha256").update(url, "utf8").digest("hex").slice(0, 16);
9682
+ const digest = (0, import_node_crypto13.createHash)("sha256").update(url, "utf8").digest("hex").slice(0, 16);
9333
9683
  return path.join(stateDir, "outbox", digest);
9334
9684
  }
9335
9685
 
@@ -9339,7 +9689,7 @@ function endpointFor(config) {
9339
9689
  }
9340
9690
  function readCollectorState(dir) {
9341
9691
  try {
9342
- return JSON.parse(readPrivate((0, import_node_path19.join)(dir, "collector", "state.json")));
9692
+ return JSON.parse(readPrivate((0, import_node_path20.join)(dir, "collector", "state.json")));
9343
9693
  } catch (error2) {
9344
9694
  if (error2.code === "ENOENT") return null;
9345
9695
  throw error2;
@@ -9364,7 +9714,7 @@ function planAccessChange(dir, options) {
9364
9714
  delete next.socket;
9365
9715
  } else if (options.origin) {
9366
9716
  next.origin = options.origin;
9367
- next.socket = options.socket ?? (0, import_node_path19.join)(dir, "run", "hub.sock");
9717
+ next.socket = options.socket ?? (0, import_node_path20.join)(dir, "run", "hub.sock");
9368
9718
  }
9369
9719
  parseHubConfig(hubEnvironment(dir, next));
9370
9720
  const oldEndpoint = endpointFor(previous);
@@ -9380,8 +9730,8 @@ function planAccessChange(dir, options) {
9380
9730
  collectorChanged,
9381
9731
  oldEndpoint,
9382
9732
  newEndpoint,
9383
- oldQueue: collectorChanged ? outboxDirFor((0, import_node_path19.join)(dir, "collector"), oldEndpoint) : null,
9384
- newQueue: collectorChanged ? outboxDirFor((0, import_node_path19.join)(dir, "collector"), newEndpoint) : null,
9733
+ oldQueue: collectorChanged ? outboxDirFor((0, import_node_path20.join)(dir, "collector"), oldEndpoint) : null,
9734
+ newQueue: collectorChanged ? outboxDirFor((0, import_node_path20.join)(dir, "collector"), newEndpoint) : null,
9385
9735
  state,
9386
9736
  self
9387
9737
  };
@@ -9389,22 +9739,22 @@ function planAccessChange(dir, options) {
9389
9739
  function configureAccess(dir, options) {
9390
9740
  const plan = planAccessChange(dir, options);
9391
9741
  if (!plan.changed) return { changed: false, backup: null, url: plan.url };
9392
- const configPath = (0, import_node_path19.join)(dir, "config.json");
9393
- const statePath = (0, import_node_path19.join)(dir, "collector", "state.json");
9742
+ const configPath = (0, import_node_path20.join)(dir, "config.json");
9743
+ const statePath = (0, import_node_path20.join)(dir, "collector", "state.json");
9394
9744
  const beforeConfig = readPrivate(configPath);
9395
9745
  const beforeState = plan.state === null ? null : readPrivate(statePath);
9396
- if (plan.collectorChanged && plan.oldQueue && plan.newQueue && plan.oldQueue !== plan.newQueue && (0, import_node_fs18.existsSync)(plan.newQueue)) {
9746
+ if (plan.collectorChanged && plan.oldQueue && plan.newQueue && plan.oldQueue !== plan.newQueue && (0, import_node_fs19.existsSync)(plan.newQueue)) {
9397
9747
  throw new Error("The destination already has an outbox. Preserve or replay it before changing access.");
9398
9748
  }
9399
- if (plan.oldQueue && (0, import_node_fs18.existsSync)(plan.oldQueue)) {
9400
- const stat = (0, import_node_fs18.lstatSync)(plan.oldQueue);
9749
+ if (plan.oldQueue && (0, import_node_fs19.existsSync)(plan.oldQueue)) {
9750
+ const stat = (0, import_node_fs19.lstatSync)(plan.oldQueue);
9401
9751
  if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error("Collector outbox must be a real directory.");
9402
9752
  }
9403
- const backup3 = (0, import_node_path19.join)(dir, "backups", `access-${(0, import_node_crypto13.randomUUID)()}`);
9753
+ const backup3 = (0, import_node_path20.join)(dir, "backups", `access-${(0, import_node_crypto14.randomUUID)()}`);
9404
9754
  privateDirectory(backup3);
9405
- writePrivateFile((0, import_node_path19.join)(backup3, "config.json"), beforeConfig);
9406
- if (beforeState !== null) writePrivateFile((0, import_node_path19.join)(backup3, "collector-state.json"), beforeState);
9407
- writePrivateFile((0, import_node_path19.join)(backup3, "change.json"), JSON.stringify({
9755
+ writePrivateFile((0, import_node_path20.join)(backup3, "config.json"), beforeConfig);
9756
+ if (beforeState !== null) writePrivateFile((0, import_node_path20.join)(backup3, "collector-state.json"), beforeState);
9757
+ writePrivateFile((0, import_node_path20.join)(backup3, "change.json"), JSON.stringify({
9408
9758
  oldEndpoint: plan.collectorChanged ? plan.oldEndpoint : null,
9409
9759
  newEndpoint: plan.collectorChanged ? plan.newEndpoint : null,
9410
9760
  oldQueue: plan.oldQueue,
@@ -9412,8 +9762,8 @@ function configureAccess(dir, options) {
9412
9762
  }));
9413
9763
  let movedQueue = false;
9414
9764
  try {
9415
- if (plan.oldQueue && plan.newQueue && plan.oldQueue !== plan.newQueue && (0, import_node_fs18.existsSync)(plan.oldQueue)) {
9416
- (0, import_node_fs18.renameSync)(plan.oldQueue, plan.newQueue);
9765
+ if (plan.oldQueue && plan.newQueue && plan.oldQueue !== plan.newQueue && (0, import_node_fs19.existsSync)(plan.oldQueue)) {
9766
+ (0, import_node_fs19.renameSync)(plan.oldQueue, plan.newQueue);
9417
9767
  movedQueue = true;
9418
9768
  }
9419
9769
  if (plan.collectorChanged && plan.state && plan.self) {
@@ -9429,7 +9779,7 @@ function configureAccess(dir, options) {
9429
9779
  try {
9430
9780
  writePrivateFile(configPath, beforeConfig);
9431
9781
  if (beforeState !== null) writePrivateFile(statePath, beforeState);
9432
- if (movedQueue) (0, import_node_fs18.renameSync)(plan.newQueue, plan.oldQueue);
9782
+ if (movedQueue) (0, import_node_fs19.renameSync)(plan.newQueue, plan.oldQueue);
9433
9783
  } catch {
9434
9784
  throw new Error(`Access change could not be recovered. Keep the service stopped and restore configuration from ${backup3}.`);
9435
9785
  }
@@ -9439,7 +9789,7 @@ function configureAccess(dir, options) {
9439
9789
  }
9440
9790
 
9441
9791
  // src/cli/main.ts
9442
- var ROOT = (0, import_node_path20.resolve)(__dirname, "..");
9792
+ var ROOT = (0, import_node_path21.resolve)(__dirname, "..");
9443
9793
  var HELP2 = `Rigscope \u2014 your machines, in view
9444
9794
 
9445
9795
  Usage: rigscope <command> [options]
@@ -9560,7 +9910,7 @@ async function readPassword() {
9560
9910
  }
9561
9911
  }
9562
9912
  function passwordFromFile(path2) {
9563
- const raw = readPrivate((0, import_node_path20.resolve)(path2));
9913
+ const raw = readPrivate((0, import_node_path21.resolve)(path2));
9564
9914
  return raw.replace(/\r?\n$/, "");
9565
9915
  }
9566
9916
  function healthDeadline(value) {
@@ -9601,7 +9951,12 @@ function announce(setup, dir) {
9601
9951
  Password: ${setup.password}`);
9602
9952
  printWarning("Save this password; it is shown once.");
9603
9953
  }
9604
- console.log(`Configuration: ${(0, import_node_path20.join)(dir, "config.json")}`);
9954
+ console.log(`Configuration: ${(0, import_node_path21.join)(dir, "config.json")}`);
9955
+ }
9956
+ function printMachineDataOutcome2(machineHistory) {
9957
+ const described = describeMachineDataReport(machineHistory);
9958
+ console.log(described.summary);
9959
+ if (described.warning !== null) printWarning(described.warning);
9605
9960
  }
9606
9961
  async function main() {
9607
9962
  const argv = process.argv.slice(2);
@@ -9641,7 +9996,7 @@ async function main() {
9641
9996
  return 0;
9642
9997
  }
9643
9998
  if (values.version) {
9644
- console.log(JSON.parse((0, import_node_fs19.readFileSync)((0, import_node_path20.join)(ROOT, "package.json"), "utf8")).version);
9999
+ console.log(JSON.parse((0, import_node_fs20.readFileSync)((0, import_node_path21.join)(ROOT, "package.json"), "utf8")).version);
9645
10000
  return 0;
9646
10001
  }
9647
10002
  if (process.platform !== "linux") throw new Error("Rigscope currently supports Linux.");
@@ -9673,13 +10028,13 @@ async function main() {
9673
10028
  if (value !== void 0 && key !== "state-dir" && key !== "scope" && !(permitted[command] ?? []).includes(key)) throw new Error(`--${key} is not available for ${command}.`);
9674
10029
  }
9675
10030
  if (values.origin?.endsWith("/")) values.origin = values.origin.slice(0, -1);
9676
- const managedState = (0, import_node_path20.basename)(ROOT) === "rigscope" && (0, import_node_path20.basename)((0, import_node_path20.dirname)(ROOT)) === "node_modules" && (0, import_node_path20.basename)((0, import_node_path20.resolve)(ROOT, "../..")) === "runtime" ? (0, import_node_path20.resolve)(ROOT, "../../..") : void 0;
10031
+ const managedState = (0, import_node_path21.basename)(ROOT) === "rigscope" && (0, import_node_path21.basename)((0, import_node_path21.dirname)(ROOT)) === "node_modules" && (0, import_node_path21.basename)((0, import_node_path21.resolve)(ROOT, "../..")) === "runtime" ? (0, import_node_path21.resolve)(ROOT, "../../..") : void 0;
9677
10032
  const dir = stateDirectory(values["state-dir"] ?? (command === "try" ? `${stateDirectory()}-trial` : process.env.RIGSCOPE_STATE_DIR ?? managedState));
9678
10033
  const explicitState = Boolean(values["state-dir"] || process.env.RIGSCOPE_STATE_DIR || managedState || command === "try" || command === "run");
9679
10034
  const scope = selectInstallation({
9680
10035
  scope: values.scope ?? (explicitState ? "user" : void 0),
9681
10036
  explicitState: Boolean(values["state-dir"]),
9682
- userPresent: (0, import_node_fs19.existsSync)((0, import_node_path20.join)(dir, "config.json")),
10037
+ userPresent: (0, import_node_fs20.existsSync)((0, import_node_path21.join)(dir, "config.json")),
9683
10038
  systemPresent: systemInstallationPresent()
9684
10039
  });
9685
10040
  if (scope === "system") {
@@ -9689,7 +10044,7 @@ async function main() {
9689
10044
  throw new Error(`System installation selected. Use rigscope system ${command} (mutations require sudo and --apply), or --scope user for a user installation.`);
9690
10045
  }
9691
10046
  if (command === "config") {
9692
- console.log((0, import_node_path20.join)(dir, "config.json"));
10047
+ console.log((0, import_node_path21.join)(dir, "config.json"));
9693
10048
  return 0;
9694
10049
  }
9695
10050
  if (command === "upgrade" && installationNeedsReset(dir)) {
@@ -9720,7 +10075,7 @@ async function main() {
9720
10075
  if (values.enable && values["no-enable"]) throw new Error("Choose --enable or --no-enable.");
9721
10076
  if (values.boot && values["no-enable"]) throw new Error("--boot requires startup to be enabled.");
9722
10077
  }
9723
- const grant = command === "connect" ? values["token-file"] ? readPrivate((0, import_node_path20.resolve)(values["token-file"])).trim() : await readGrant() : void 0;
10078
+ const grant = command === "connect" ? values["token-file"] ? readPrivate((0, import_node_path21.resolve)(values["token-file"])).trim() : await readGrant() : void 0;
9724
10079
  let recoveryPassword;
9725
10080
  if (command === "recover-password") {
9726
10081
  const saved = readSetup(dir);
@@ -9758,7 +10113,7 @@ async function main() {
9758
10113
  }
9759
10114
  if (command === "try") {
9760
10115
  if (values.role && values.role !== "local") throw new Error("Use install for a hub or collector.");
9761
- if ((0, import_node_fs19.existsSync)((0, import_node_path20.join)(dir, "runtime"))) throw new Error("This is an installed runtime. Use rigscope start, or choose a separate --state-dir for a trial.");
10116
+ if ((0, import_node_fs20.existsSync)((0, import_node_path21.join)(dir, "runtime"))) throw new Error("This is an installed runtime. Use rigscope start, or choose a separate --state-dir for a trial.");
9762
10117
  const setup = initialize(dir, { role: "local", port: values.port ?? "8082" });
9763
10118
  console.log("Trial: foreground hub + collector. Ctrl+C stops both.");
9764
10119
  announce(setup, dir);
@@ -9774,20 +10129,20 @@ async function main() {
9774
10129
  const releases = [];
9775
10130
  try {
9776
10131
  releases.push(acquireProcessLease(componentLeasePath(dir, "collector")));
9777
- releases.push(acquireProcessLease((0, import_node_path20.join)(dir, "collector", "collector-runtime.lock")));
9778
- await enrollCollector((0, import_node_path20.join)(dir, "collector", "state.json"), url, token, DEFAULT_SHARING, values.replace);
10132
+ releases.push(acquireProcessLease((0, import_node_path21.join)(dir, "collector", "collector-runtime.lock")));
10133
+ await enrollCollector((0, import_node_path21.join)(dir, "collector", "state.json"), url, token, DEFAULT_SHARING, values.replace);
9779
10134
  } finally {
9780
10135
  for (const release of releases.reverse()) release();
9781
10136
  }
9782
10137
  configureComponent(dir, "collector", { enabled: true, pairColocated: false });
9783
- if ((0, import_node_fs19.existsSync)((0, import_node_path20.join)(dir, "runtime"))) {
10138
+ if ((0, import_node_fs20.existsSync)((0, import_node_path21.join)(dir, "runtime"))) {
9784
10139
  writeService(dir, "collector");
9785
10140
  systemctl(dir, "enable", false, "collector");
9786
10141
  }
9787
- if (!values["no-start"] && !values["no-restart"] && (0, import_node_fs19.existsSync)(unitPath(dir, "collector"))) {
10142
+ if (!values["no-start"] && !values["no-restart"] && (0, import_node_fs20.existsSync)(unitPath(dir, "collector"))) {
9788
10143
  console.log(`Hub connected. Background service ${await startInstallation(dir, "collector")}.`);
9789
10144
  } else {
9790
- console.log(`Hub connected. Run rigscope ${(0, import_node_fs19.existsSync)(unitPath(dir, "collector")) ? "start collector" : `install --state-dir ${shellQuote2(dir)}`}.`);
10145
+ console.log(`Hub connected. Run rigscope ${(0, import_node_fs20.existsSync)(unitPath(dir, "collector")) ? "start collector" : `install --state-dir ${shellQuote2(dir)}`}.`);
9791
10146
  }
9792
10147
  return 0;
9793
10148
  }
@@ -9796,10 +10151,10 @@ async function main() {
9796
10151
  const existing = readSetup(dir);
9797
10152
  if (!existing) throw new Error("No saved installation. Run rigscope install first.");
9798
10153
  if (command === "enable") {
9799
- if (!(0, import_node_fs19.existsSync)((0, import_node_path20.join)(dir, "runtime"))) throw new Error("Install the runtime first: rigscope install --no-start.");
10154
+ if (!(0, import_node_fs20.existsSync)((0, import_node_path21.join)(dir, "runtime"))) throw new Error("Install the runtime first: rigscope install --no-start.");
9800
10155
  if (target === "hub" && (existing.users.length > 0 || existing.origin) && (values.origin !== void 0 || values.socket !== void 0 || values.port !== void 0)) {
9801
10156
  const nextOrigin = values.origin !== void 0 ? values.origin : existing.origin;
9802
- const nextSocket = values.origin !== void 0 ? values.socket ?? (0, import_node_path20.join)(dir, "run", "hub.sock") : values.socket ?? existing.socket;
10157
+ const nextSocket = values.origin !== void 0 ? values.socket ?? (0, import_node_path21.join)(dir, "run", "hub.sock") : values.socket ?? existing.socket;
9803
10158
  const nextPort = values.port !== void 0 ? Number(values.port) : existing.port;
9804
10159
  if (nextOrigin !== existing.origin || nextSocket !== existing.socket || nextPort !== existing.port) {
9805
10160
  throw new Error("Change hub access with rigscope configure.");
@@ -9837,7 +10192,7 @@ Save this password; it is shown once.`);
9837
10192
  if (values.boot && values["no-enable"]) throw new Error("--boot requires startup to be enabled.");
9838
10193
  const resetting = installationNeedsReset(dir);
9839
10194
  if (resetting) values.role = values.role ?? installationRole(dir);
9840
- const release = resetting || serviceActive(dir) && (0, import_node_fs19.existsSync)((0, import_node_path20.join)(dir, "runtime")) ? () => {
10195
+ const release = resetting || serviceActive(dir) && (0, import_node_fs20.existsSync)((0, import_node_path21.join)(dir, "runtime")) ? () => {
9841
10196
  } : acquireProcessLease(combinedLeasePath(dir));
9842
10197
  let alias;
9843
10198
  let installedVersion;
@@ -9845,7 +10200,7 @@ Save this password; it is shown once.`);
9845
10200
  if (resetting) {
9846
10201
  console.log("Old installation state detected. Resetting configuration and history, then installing the replacement\u2026");
9847
10202
  validateSetupOptions({ role: values.role, port: values.port, origin: values.origin, socket: values.socket });
9848
- const source = values.from ? /^[./]/.test(values.from) ? (0, import_node_path20.resolve)(values.from) : values.from : void 0;
10203
+ const source = values.from ? /^[./]/.test(values.from) ? (0, import_node_path21.resolve)(values.from) : values.from : void 0;
9849
10204
  const stage = stagePackage(ROOT, dir, source);
9850
10205
  let setup;
9851
10206
  try {
@@ -9855,26 +10210,27 @@ Save this password; it is shown once.`);
9855
10210
  setup: { role: values.role, port: values.port, origin: values.origin, socket: values.socket }
9856
10211
  });
9857
10212
  } catch (error2) {
9858
- (0, import_node_fs19.rmSync)(stage, { recursive: true, force: true });
10213
+ (0, import_node_fs20.rmSync)(stage, { recursive: true, force: true });
9859
10214
  throw error2;
9860
10215
  }
9861
10216
  announce(setup, dir);
9862
- installedVersion = runtimeVersion((0, import_node_path20.join)(dir, "runtime"));
10217
+ installedVersion = runtimeVersion((0, import_node_path21.join)(dir, "runtime"));
9863
10218
  writeRuntimeState(dir, { ...readRuntimeState(dir) ?? emptyRuntimeState(dir), currentVersion: installedVersion });
9864
10219
  } else {
9865
10220
  const setup = initialize(dir, { role: values.role, port: values.port, origin: values.origin, socket: values.socket });
9866
10221
  announce(setup, dir);
9867
- if ((0, import_node_fs19.existsSync)((0, import_node_path20.join)(dir, "runtime"))) {
10222
+ if ((0, import_node_fs20.existsSync)((0, import_node_path21.join)(dir, "runtime"))) {
9868
10223
  if (values.from) throw new Error("Runtime already installed. Use rigscope upgrade --from <package>.");
9869
10224
  } else {
9870
10225
  console.log("Installing runtime\u2026");
9871
- const source = values.from ? /^[./]/.test(values.from) ? (0, import_node_path20.resolve)(values.from) : values.from : void 0;
10226
+ const source = values.from ? /^[./]/.test(values.from) ? (0, import_node_path21.resolve)(values.from) : values.from : void 0;
9872
10227
  const stage = stagePackage(ROOT, dir, source);
9873
10228
  activatePackage(dir, stage);
9874
10229
  }
9875
- installedVersion = runtimeVersion((0, import_node_path20.join)(dir, "runtime"));
10230
+ installedVersion = runtimeVersion((0, import_node_path21.join)(dir, "runtime"));
9876
10231
  writeRuntimeState(dir, { ...readRuntimeState(dir) ?? emptyRuntimeState(dir), currentVersion: installedVersion });
9877
10232
  }
10233
+ recordMachineDataTransition(dir, installedVersion);
9878
10234
  writeService(dir);
9879
10235
  alias = installAlias(dir, ROOT);
9880
10236
  const installedConfig = readSetup(dir);
@@ -9920,7 +10276,7 @@ Services: ${components2.map((target) => unitName(dir, target)).join(", ")}`);
9920
10276
  }
9921
10277
  const startup = startupStatus(dir);
9922
10278
  console.log(`Startup: ${!startup.enabled ? "manual" : startup.linger ? "at boot; continues after logout" : "at login; use install --boot to continue after logout"}`);
9923
- if (!(process.env.PATH ?? "").split(":").includes((0, import_node_path20.resolve)(alias, ".."))) console.log(`Add ${(0, import_node_path20.resolve)(alias, "..")} to PATH, or use the command path above.`);
10279
+ if (!(process.env.PATH ?? "").split(":").includes((0, import_node_path21.resolve)(alias, ".."))) console.log(`Add ${(0, import_node_path21.resolve)(alias, "..")} to PATH, or use the command path above.`);
9924
10280
  return 0;
9925
10281
  }
9926
10282
  if (command === "doctor") {
@@ -9936,7 +10292,7 @@ Services: ${components2.map((target) => unitName(dir, target)).join(", ")}`);
9936
10292
  const config = readSetup(dir);
9937
10293
  if (!config) {
9938
10294
  if (command === "status") {
9939
- console.log(`Rigscope ${JSON.parse((0, import_node_fs19.readFileSync)((0, import_node_path20.join)(ROOT, "package.json"), "utf8")).version}
10295
+ console.log(`Rigscope ${JSON.parse((0, import_node_fs20.readFileSync)((0, import_node_path21.join)(ROOT, "package.json"), "utf8")).version}
9940
10296
  No user installation.
9941
10297
  Install background monitoring: rigscope install --boot
9942
10298
  Connect to a hub: rigscope install --role collector --boot
@@ -9957,25 +10313,25 @@ ${bold("Components")}:`);
9957
10313
  console.log(` ${bold(label2(target))}: ${componentEnabled(config, target) ? "enabled" : "disabled"}, ${colorize(state2, service.active ? "green" : "gray")}, startup ${service.enabled ? "on" : "off"}${service.installed ? "" : " (not installed)"}`);
9958
10314
  if (service.active && service.execMainPid) console.log(` PID ${service.execMainPid}`);
9959
10315
  }
9960
- console.log(`Configuration: ${(0, import_node_path20.join)(dir, "config.json")}
10316
+ console.log(`Configuration: ${(0, import_node_path21.join)(dir, "config.json")}
9961
10317
  Startup: ${!startup.enabled ? "manual" : startup.linger ? "at boot; continues after logout" : "at login only"}
9962
- CLI version: ${JSON.parse((0, import_node_fs19.readFileSync)((0, import_node_path20.join)(ROOT, "package.json"), "utf8")).version}`);
9963
- if ((0, import_node_fs19.existsSync)((0, import_node_path20.join)(dir, "runtime"))) {
10318
+ CLI version: ${JSON.parse((0, import_node_fs20.readFileSync)((0, import_node_path21.join)(ROOT, "package.json"), "utf8")).version}`);
10319
+ if ((0, import_node_fs20.existsSync)((0, import_node_path21.join)(dir, "runtime"))) {
9964
10320
  try {
9965
- console.log(`Runtime: rigscope ${runtimeVersion((0, import_node_path20.join)(dir, "runtime"))}`);
10321
+ console.log(`Runtime: rigscope ${runtimeVersion((0, import_node_path21.join)(dir, "runtime"))}`);
9966
10322
  } catch {
9967
10323
  console.log("Runtime: invalid (run rigscope doctor)");
9968
10324
  }
9969
10325
  }
9970
- if ((0, import_node_fs19.existsSync)((0, import_node_path20.join)(dir, "runtime-previous"))) {
10326
+ if ((0, import_node_fs20.existsSync)((0, import_node_path21.join)(dir, "runtime-previous"))) {
9971
10327
  try {
9972
- console.log(`Previous runtime: rigscope ${runtimeVersion((0, import_node_path20.join)(dir, "runtime-previous"))}`);
10328
+ console.log(`Previous runtime: rigscope ${runtimeVersion((0, import_node_path21.join)(dir, "runtime-previous"))}`);
9973
10329
  } catch {
9974
10330
  console.log("Previous runtime: invalid");
9975
10331
  }
9976
10332
  }
9977
10333
  const releaseState = readRuntimeState(dir);
9978
- const paired = releaseState ? Object.values(releaseState.backups).filter((record) => (0, import_node_fs19.existsSync)(record.path)) : [];
10334
+ const paired = releaseState ? Object.values(releaseState.backups).filter((record) => (0, import_node_fs20.existsSync)(record.path)) : [];
9979
10335
  if (paired.length > 0) console.log(`Paired database backups: ${paired.length}`);
9980
10336
  const hubConfigured2 = config.users.length > 0;
9981
10337
  if (hubConfigured2) console.log(`Hub: ${config.origin ?? `http://127.0.0.1:${config.port}`}`);
@@ -9984,8 +10340,8 @@ CLI version: ${JSON.parse((0, import_node_fs19.readFileSync)((0, import_node_pat
9984
10340
  console.log(`Health: ${health.ok ? "ok" : health.error}
9985
10341
  Running version: ${health.body?.version ?? "unavailable"}`);
9986
10342
  }
9987
- const state = (0, import_node_path20.join)(dir, "collector", "state.json");
9988
- if ((0, import_node_fs19.existsSync)(state)) {
10343
+ const state = (0, import_node_path21.join)(dir, "collector", "state.json");
10344
+ if ((0, import_node_fs20.existsSync)(state)) {
9989
10345
  const saved = JSON.parse(readPrivate(state));
9990
10346
  console.log(`Destinations: ${saved.destinations.length}/8${saved.destinations.length === 0 ? " (awaiting connection)" : ""}`);
9991
10347
  for (const destination of saved.destinations) console.log(` ${destination.url}`);
@@ -10035,9 +10391,9 @@ Running version: ${health.body?.version ?? "unavailable"}`);
10035
10391
  return 0;
10036
10392
  }
10037
10393
  if (command === "upgrade") {
10038
- if (!(0, import_node_fs19.existsSync)((0, import_node_path20.join)(dir, "runtime"))) throw new Error("Install a managed runtime before upgrading.");
10394
+ if (!(0, import_node_fs20.existsSync)((0, import_node_path21.join)(dir, "runtime"))) throw new Error("Install a managed runtime before upgrading.");
10039
10395
  console.log("Staging upgrade\u2026");
10040
- const source = values.from ? /^[./]/.test(values.from) ? (0, import_node_path20.resolve)(values.from) : values.from : "rigscope@latest";
10396
+ const source = values.from ? /^[./]/.test(values.from) ? (0, import_node_path21.resolve)(values.from) : values.from : "rigscope@latest";
10041
10397
  const report = await upgradeInstallation({
10042
10398
  root: ROOT,
10043
10399
  dir,
@@ -10049,13 +10405,14 @@ Running version: ${health.body?.version ?? "unavailable"}`);
10049
10405
  if (report.schemaVersion !== null) console.log(`Database schema: ${report.schemaVersion}`);
10050
10406
  if (report.verification.health) console.log(`Health: ${report.verification.health.attempts} probe(s) in ${report.verification.health.elapsedMs}ms`);
10051
10407
  else console.log("Health: skipped because the service was inactive; start it explicitly.");
10408
+ printMachineDataOutcome2(report.machineHistory);
10052
10409
  return 0;
10053
10410
  }
10054
10411
  if (command === "rollback") {
10055
10412
  console.log("Rolling back\u2026");
10056
10413
  const report = await rollbackInstallation({
10057
10414
  dir,
10058
- backup: values.backup ? (0, import_node_path20.resolve)(values.backup) : void 0,
10415
+ backup: values.backup ? (0, import_node_path21.resolve)(values.backup) : void 0,
10059
10416
  healthDeadlineMs: healthDeadline(values.timeout)
10060
10417
  });
10061
10418
  printSuccess(`Rolled back: rigscope ${report.version} (previous ${report.previousVersion}).`);
@@ -10073,7 +10430,7 @@ Running version: ${health.body?.version ?? "unavailable"}`);
10073
10430
  if (!componentEnabled(config, "hub")) throw new Error("This installation has no hub component.");
10074
10431
  const password = recoveryPassword;
10075
10432
  const report = await recoverAdminPassword({
10076
- database: (0, import_node_path20.join)(dir, "hub", "telemetry.sqlite"),
10433
+ database: (0, import_node_path21.join)(dir, "hub", "telemetry.sqlite"),
10077
10434
  username: values.user ?? "owner",
10078
10435
  password,
10079
10436
  enable: values.enable === true