rigscope 0.1.1 → 0.1.3

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 (109) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +2 -2
  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_client-reference-manifest.js +1 -1
  29. package/.next/server/app/connections.html +1 -1
  30. package/.next/server/app/connections.rsc +3 -3
  31. package/.next/server/app/connections.segments/_full.segment.rsc +3 -3
  32. package/.next/server/app/connections.segments/_tree.segment.rsc +2 -2
  33. package/.next/server/app/connections.segments/connections/__PAGE__.segment.rsc +3 -3
  34. package/.next/server/app/history/page.js +2 -2
  35. package/.next/server/app/history/page_client-reference-manifest.js +1 -1
  36. package/.next/server/app/history.html +1 -1
  37. package/.next/server/app/history.rsc +6 -6
  38. package/.next/server/app/history.segments/_full.segment.rsc +6 -6
  39. package/.next/server/app/history.segments/_tree.segment.rsc +2 -2
  40. package/.next/server/app/history.segments/history/__PAGE__.segment.rsc +6 -6
  41. package/.next/server/app/index.html +1 -1
  42. package/.next/server/app/index.rsc +3 -3
  43. package/.next/server/app/index.segments/__PAGE__.segment.rsc +3 -3
  44. package/.next/server/app/index.segments/_full.segment.rsc +3 -3
  45. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  46. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  47. package/.next/server/app/login.html +1 -1
  48. package/.next/server/app/login.rsc +3 -3
  49. package/.next/server/app/login.segments/_full.segment.rsc +3 -3
  50. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  51. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +3 -3
  52. package/.next/server/app/machines/[machineId]/page_client-reference-manifest.js +1 -1
  53. package/.next/server/app/page_client-reference-manifest.js +1 -1
  54. package/.next/server/app/processes/page.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 +4 -4
  58. package/.next/server/app/processes.segments/_full.segment.rsc +4 -4
  59. package/.next/server/app/processes.segments/_tree.segment.rsc +2 -2
  60. package/.next/server/app/processes.segments/processes/__PAGE__.segment.rsc +4 -4
  61. package/.next/server/app-paths-manifest.json +2 -2
  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/server/route-cache/APP_PAGE/16d1ab46157a868f438fa01e04bf53e805c71cddec593ea1535905c59625cd42/$/login.html +1 -0
  68. package/.next/server/route-cache/APP_PAGE/16d1ab46157a868f438fa01e04bf53e805c71cddec593ea1535905c59625cd42/$/login.meta +1 -0
  69. package/.next/server/route-cache/APP_PAGE/16d1ab46157a868f438fa01e04bf53e805c71cddec593ea1535905c59625cd42/$/login.rsc +26 -0
  70. package/.next/server/route-cache/APP_PAGE/16d1ab46157a868f438fa01e04bf53e805c71cddec593ea1535905c59625cd42/$/login.segments/_full.segment.rsc +26 -0
  71. package/.next/server/route-cache/APP_PAGE/16d1ab46157a868f438fa01e04bf53e805c71cddec593ea1535905c59625cd42/$/login.segments/_tree.segment.rsc +4 -0
  72. package/.next/server/route-cache/APP_PAGE/16d1ab46157a868f438fa01e04bf53e805c71cddec593ea1535905c59625cd42/$/login.segments/login/__PAGE__.segment.rsc +32 -0
  73. package/.next/server/route-cache/APP_PAGE/41da0ef926a63354a29f601d8e8d0a5951196c45ebcd28b7d4708fb7f56f3bf1/$/_not-found.html +1 -0
  74. package/.next/server/route-cache/APP_PAGE/41da0ef926a63354a29f601d8e8d0a5951196c45ebcd28b7d4708fb7f56f3bf1/$/_not-found.meta +1 -0
  75. package/.next/server/route-cache/APP_PAGE/41da0ef926a63354a29f601d8e8d0a5951196c45ebcd28b7d4708fb7f56f3bf1/$/_not-found.rsc +24 -0
  76. package/.next/server/route-cache/APP_PAGE/41da0ef926a63354a29f601d8e8d0a5951196c45ebcd28b7d4708fb7f56f3bf1/$/_not-found.segments/_full.segment.rsc +24 -0
  77. package/.next/server/route-cache/APP_PAGE/41da0ef926a63354a29f601d8e8d0a5951196c45ebcd28b7d4708fb7f56f3bf1/$/_not-found.segments/_not-found/__PAGE__.segment.rsc +30 -0
  78. package/.next/server/route-cache/APP_PAGE/41da0ef926a63354a29f601d8e8d0a5951196c45ebcd28b7d4708fb7f56f3bf1/$/_not-found.segments/_tree.segment.rsc +4 -0
  79. package/.next/server/route-cache/APP_PAGE/c6377dd9ce6a1c2cd100eb4344e843eb4639b9ed26f84643f77680e9eb454c4b/$/index.html +1 -0
  80. package/.next/server/route-cache/APP_PAGE/c6377dd9ce6a1c2cd100eb4344e843eb4639b9ed26f84643f77680e9eb454c4b/$/index.meta +1 -0
  81. package/.next/server/route-cache/APP_PAGE/c6377dd9ce6a1c2cd100eb4344e843eb4639b9ed26f84643f77680e9eb454c4b/$/index.rsc +26 -0
  82. package/.next/server/route-cache/APP_PAGE/c6377dd9ce6a1c2cd100eb4344e843eb4639b9ed26f84643f77680e9eb454c4b/$/index.segments/__PAGE__.segment.rsc +31 -0
  83. package/.next/server/route-cache/APP_PAGE/c6377dd9ce6a1c2cd100eb4344e843eb4639b9ed26f84643f77680e9eb454c4b/$/index.segments/_full.segment.rsc +26 -0
  84. package/.next/server/route-cache/APP_PAGE/c6377dd9ce6a1c2cd100eb4344e843eb4639b9ed26f84643f77680e9eb454c4b/$/index.segments/_tree.segment.rsc +3 -0
  85. package/.next/static/chunks/408-4fe7147e38981ee1.js +1 -0
  86. package/.next/static/chunks/939-514e554087419c71.js +1 -0
  87. package/.next/static/chunks/app/history/page-69ea64476ee5db85.js +3 -0
  88. package/.next/static/chunks/app/processes/{page-e471ed23a1d5632f.js → page-0e98a4a64e829c5f.js} +1 -1
  89. package/.next/static/css/3fbf75e4c471c6a8.css +1 -0
  90. package/ARCHITECTURE.md +30 -4
  91. package/CHANGELOG.md +47 -2
  92. package/dist/cli.cjs +1627 -1042
  93. package/dist/collector.cjs +138 -226
  94. package/dist/hub.cjs +7 -20
  95. package/dist/ops.cjs +160 -33
  96. package/dist/system.cjs +869 -450
  97. package/docs/bottlenecks.md +4 -6
  98. package/docs/configuration.md +2 -0
  99. package/docs/installation.md +42 -7
  100. package/docs/operations.md +32 -16
  101. package/docs/releasing.md +21 -17
  102. package/docs/system-installation.md +11 -2
  103. package/package.json +1 -2
  104. package/.next/static/chunks/408-dc6158e0bb450ef6.js +0 -1
  105. package/.next/static/chunks/939-2600d7c8e583d5ef.js +0 -1
  106. package/.next/static/chunks/app/history/page-1475454737279ee0.js +0 -3
  107. package/.next/static/css/e45c0c7ef5ba0336.css +0 -1
  108. /package/.next/static/{NE6PGEiJxUnKRceuzUWPc → xjSDCOO8Gpm5iPO9tyRG5}/_buildManifest.js +0 -0
  109. /package/.next/static/{NE6PGEiJxUnKRceuzUWPc → xjSDCOO8Gpm5iPO9tyRG5}/_ssgManifest.js +0 -0
package/dist/ops.cjs CHANGED
@@ -962,7 +962,7 @@ function findHubLock(databasePath) {
962
962
  `${absolute}.hub.lock`,
963
963
  (0, import_node_path2.join)(parent, "hub.lock"),
964
964
  (0, import_node_path2.join)(parent, "hub.pid")
965
- // `run/hub.lock` is the v2 component supervisor lease, not a database
965
+ // `run/hub.lock` is the component supervisor lease, not a database
966
966
  // lease. Treating it as a hub lock would make an upgrade's own held lease
967
967
  // look like a live hub during database backup/restore.
968
968
  ];
@@ -1517,8 +1517,6 @@ var import_node_child_process = require("node:child_process");
1517
1517
  var import_node_os = require("node:os");
1518
1518
  var import_node_path4 = require("node:path");
1519
1519
  var import_node_crypto5 = require("node:crypto");
1520
- var RELEASE_FORMAT_VERSION = 1;
1521
- var INSTALL_STATE_FORMAT_VERSION = 1;
1522
1520
  var HUB_SHUTDOWN_DEADLINE_MS = 5e3;
1523
1521
  var COLLECTOR_SHUTDOWN_DEADLINE_MS = 3e3;
1524
1522
  var RELEASE_ENTRYPOINTS = {
@@ -1581,6 +1579,17 @@ var PRIVATE_FILE_PATTERNS = [
1581
1579
  /(^|\/)id_(rsa|ed25519)(\.pub)?$/,
1582
1580
  /\.(log|pid|lock|tmp)$/
1583
1581
  ];
1582
+ var INSTALL_STATE_KEYS = /* @__PURE__ */ new Set([
1583
+ "prefix",
1584
+ "stateDir",
1585
+ "database",
1586
+ "currentVersion",
1587
+ "previousVersion",
1588
+ "releases",
1589
+ "backups",
1590
+ "lastActivation",
1591
+ "updatedAt"
1592
+ ]);
1584
1593
  function assertSafeRelativePath(path, label) {
1585
1594
  if (typeof path !== "string" || path.length === 0 || path.length > 1024) {
1586
1595
  throw new OperationsError("invalid-manifest", `${label} has an empty or oversized path`);
@@ -1675,7 +1684,6 @@ function manifestForPackage(packageJson, version, now) {
1675
1684
  const name = typeof packageJson.name === "string" && packageJson.name.length > 0 ? packageJson.name : "rigscope";
1676
1685
  const dependencies = packageJson.dependencies !== null && typeof packageJson.dependencies === "object" ? Object.keys(packageJson.dependencies).filter((key) => /^[A-Za-z0-9@/._-]+$/.test(key)) : [];
1677
1686
  return {
1678
- formatVersion: RELEASE_FORMAT_VERSION,
1679
1687
  name,
1680
1688
  version: validateVersion(version),
1681
1689
  createdAt: new Date(now()).toISOString(),
@@ -1777,8 +1785,11 @@ function extractArchive(archive, destination) {
1777
1785
  function validateManifest(root) {
1778
1786
  const manifestPath = (0, import_node_path4.join)(root, "manifest.json");
1779
1787
  const raw = readJsonObject(manifestPath);
1780
- if (raw.formatVersion !== RELEASE_FORMAT_VERSION) {
1781
- throw new OperationsError("invalid-manifest", `unsupported release manifest format ${String(raw.formatVersion)}`);
1788
+ if (Object.prototype.hasOwnProperty.call(raw, "formatVersion")) {
1789
+ throw new OperationsError(
1790
+ "invalid-manifest",
1791
+ "release manifest uses an unfinished pre-canonical format; repackage the release without a format version"
1792
+ );
1782
1793
  }
1783
1794
  if (typeof raw.name !== "string" || raw.name.length === 0 || raw.name.length > 128) {
1784
1795
  throw new OperationsError("invalid-manifest", "manifest name is invalid");
@@ -1839,7 +1850,6 @@ function validateManifest(root) {
1839
1850
  }
1840
1851
  }
1841
1852
  const manifest = {
1842
- formatVersion: 1,
1843
1853
  name: raw.name,
1844
1854
  version,
1845
1855
  createdAt: typeof raw.createdAt === "string" ? raw.createdAt : "",
@@ -2003,7 +2013,6 @@ function missingProductionDependencies(releaseDir, manifest) {
2003
2013
  }
2004
2014
  function installStateDefaults(prefix, stateDir, database) {
2005
2015
  return {
2006
- formatVersion: INSTALL_STATE_FORMAT_VERSION,
2007
2016
  prefix,
2008
2017
  stateDir,
2009
2018
  database,
@@ -2015,40 +2024,158 @@ function installStateDefaults(prefix, stateDir, database) {
2015
2024
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2016
2025
  };
2017
2026
  }
2027
+ function installStateError(path, detail) {
2028
+ throw new OperationsError("invalid-state", `${detail}: ${path}`);
2029
+ }
2030
+ function isPlainRecord(value) {
2031
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2032
+ }
2033
+ function stateObject(value, path, field) {
2034
+ if (!isPlainRecord(value)) installStateError(path, `installation state ${field} must be a JSON object`);
2035
+ return value;
2036
+ }
2037
+ function stateString(value, path, field) {
2038
+ if (typeof value !== "string" || value.length === 0) {
2039
+ installStateError(path, `installation state ${field} must be a nonempty string`);
2040
+ }
2041
+ return value;
2042
+ }
2043
+ function stateInteger(value, path, field) {
2044
+ if (!Number.isSafeInteger(value) || value < 0) {
2045
+ installStateError(path, `installation state ${field} must be a nonnegative integer`);
2046
+ }
2047
+ return value;
2048
+ }
2049
+ function stateNullableInteger(value, path, field) {
2050
+ return value === null ? null : stateInteger(value, path, field);
2051
+ }
2052
+ function stateVersion(value, path, field) {
2053
+ if (value === null) return null;
2054
+ if (typeof value !== "string") installStateError(path, `installation state ${field} must be a version string or null`);
2055
+ try {
2056
+ return validateVersion(value);
2057
+ } catch {
2058
+ return installStateError(path, `installation state ${field} has an invalid version`);
2059
+ }
2060
+ }
2061
+ function requiredStateVersion(value, path, field) {
2062
+ const version = stateVersion(value, path, field);
2063
+ if (version === null) installStateError(path, `installation state ${field} must be a version string`);
2064
+ return version;
2065
+ }
2018
2066
  function readInstallState(path, defaults) {
2019
2067
  if (!(0, import_node_fs4.existsSync)(path)) return defaults;
2020
2068
  const { value } = readJsonFile(path);
2021
- if (value === null || typeof value !== "object" || value.formatVersion !== INSTALL_STATE_FORMAT_VERSION) {
2022
- throw new OperationsError("invalid-state", `installation state has an unsupported format: ${path}`);
2069
+ if (!isPlainRecord(value)) installStateError(path, "installation state must be a JSON object");
2070
+ for (const key of ["formatVersion", "version"]) {
2071
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
2072
+ throw new OperationsError(
2073
+ "invalid-state",
2074
+ `installation state uses an unfinished pre-canonical format; stop the installation and reset or reinstall it without migration: ${path}`
2075
+ );
2076
+ }
2077
+ }
2078
+ for (const key of Object.keys(value)) {
2079
+ if (!INSTALL_STATE_KEYS.has(key)) {
2080
+ installStateError(path, `installation state contains unsupported field ${JSON.stringify(key)}`);
2081
+ }
2023
2082
  }
2024
2083
  if (value.prefix !== defaults.prefix || value.stateDir !== defaults.stateDir) {
2025
2084
  throw new OperationsError("invalid-state", "installation state belongs to a different prefix or state directory");
2026
2085
  }
2027
- const backups = {};
2028
- if (value.backups !== null && typeof value.backups === "object" && !Array.isArray(value.backups)) {
2029
- for (const [key, entry] of Object.entries(value.backups)) {
2030
- if (entry === null || typeof entry !== "object" || Array.isArray(entry)) continue;
2031
- const record = entry;
2032
- if (typeof record.path !== "string" || !Number.isSafeInteger(record.schemaVersion)) continue;
2033
- backups[key] = {
2034
- path: record.path,
2035
- schemaVersion: record.schemaVersion,
2036
- fromSchema: Number.isSafeInteger(record.fromSchema) ? record.fromSchema : null,
2037
- toSchema: Number.isSafeInteger(record.toSchema) ? record.toSchema : record.schemaVersion,
2038
- createdAt: typeof record.createdAt === "string" ? record.createdAt : ""
2039
- };
2086
+ const database = stateString(value.database, path, "database");
2087
+ const currentVersion = stateVersion(value.currentVersion, path, "currentVersion");
2088
+ const previousVersion = stateVersion(value.previousVersion, path, "previousVersion");
2089
+ const updatedAt = stateString(value.updatedAt, path, "updatedAt");
2090
+ const releases = {};
2091
+ for (const [version, rawEntry] of Object.entries(stateObject(value.releases, path, "releases"))) {
2092
+ try {
2093
+ validateVersion(version);
2094
+ } catch {
2095
+ installStateError(path, `installation state releases contains an invalid release version ${JSON.stringify(version)}`);
2040
2096
  }
2097
+ const entry = stateObject(rawEntry, path, `releases.${version}`);
2098
+ releases[version] = {
2099
+ schemaVersion: stateInteger(entry.schemaVersion, path, `releases.${version}.schemaVersion`),
2100
+ installedAt: stateString(entry.installedAt, path, `releases.${version}.installedAt`)
2101
+ };
2102
+ }
2103
+ const backups = {};
2104
+ for (const [version, rawEntry] of Object.entries(stateObject(value.backups, path, "backups"))) {
2105
+ const entry = stateObject(rawEntry, path, `backups.${version}`);
2106
+ backups[version] = {
2107
+ path: stateString(entry.path, path, `backups.${version}.path`),
2108
+ schemaVersion: stateInteger(entry.schemaVersion, path, `backups.${version}.schemaVersion`),
2109
+ fromSchema: stateNullableInteger(entry.fromSchema, path, `backups.${version}.fromSchema`),
2110
+ toSchema: stateInteger(entry.toSchema, path, `backups.${version}.toSchema`),
2111
+ createdAt: stateString(entry.createdAt, path, `backups.${version}.createdAt`)
2112
+ };
2113
+ }
2114
+ let lastActivation = null;
2115
+ if (value.lastActivation !== null) {
2116
+ const entry = stateObject(value.lastActivation, path, "lastActivation");
2117
+ lastActivation = {
2118
+ version: requiredStateVersion(entry.version, path, "lastActivation.version"),
2119
+ previousVersion: stateVersion(entry.previousVersion, path, "lastActivation.previousVersion"),
2120
+ schemaVersion: stateInteger(entry.schemaVersion, path, "lastActivation.schemaVersion"),
2121
+ backupPath: entry.backupPath === null ? null : stateString(entry.backupPath, path, "lastActivation.backupPath"),
2122
+ backupSchemaVersion: stateNullableInteger(
2123
+ entry.backupSchemaVersion,
2124
+ path,
2125
+ "lastActivation.backupSchemaVersion"
2126
+ ),
2127
+ activatedAt: stateString(entry.activatedAt, path, "lastActivation.activatedAt")
2128
+ };
2041
2129
  }
2042
2130
  return {
2043
- ...defaults,
2044
- ...value,
2045
- database: typeof value.database === "string" ? value.database : defaults.database,
2046
- releases: value.releases !== null && typeof value.releases === "object" && !Array.isArray(value.releases) ? value.releases : {},
2047
- backups
2131
+ prefix: defaults.prefix,
2132
+ stateDir: defaults.stateDir,
2133
+ database,
2134
+ currentVersion,
2135
+ previousVersion,
2136
+ releases,
2137
+ backups,
2138
+ lastActivation,
2139
+ updatedAt
2140
+ };
2141
+ }
2142
+ function canonicalInstallState(state) {
2143
+ const releases = {};
2144
+ for (const [version, record] of Object.entries(state.releases)) {
2145
+ releases[version] = { schemaVersion: record.schemaVersion, installedAt: record.installedAt };
2146
+ }
2147
+ const backups = {};
2148
+ for (const [version, record] of Object.entries(state.backups)) {
2149
+ backups[version] = {
2150
+ path: record.path,
2151
+ schemaVersion: record.schemaVersion,
2152
+ fromSchema: record.fromSchema,
2153
+ toSchema: record.toSchema,
2154
+ createdAt: record.createdAt
2155
+ };
2156
+ }
2157
+ return {
2158
+ prefix: state.prefix,
2159
+ stateDir: state.stateDir,
2160
+ database: state.database,
2161
+ currentVersion: state.currentVersion,
2162
+ previousVersion: state.previousVersion,
2163
+ releases,
2164
+ backups,
2165
+ lastActivation: state.lastActivation === null ? null : {
2166
+ version: state.lastActivation.version,
2167
+ previousVersion: state.lastActivation.previousVersion,
2168
+ schemaVersion: state.lastActivation.schemaVersion,
2169
+ backupPath: state.lastActivation.backupPath,
2170
+ backupSchemaVersion: state.lastActivation.backupSchemaVersion,
2171
+ activatedAt: state.lastActivation.activatedAt
2172
+ },
2173
+ updatedAt: state.updatedAt
2048
2174
  };
2049
2175
  }
2050
2176
  function writeInstallState(path, state) {
2051
- writePrivateFile(path, `${JSON.stringify({ ...state, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
2177
+ const canonical = { ...canonicalInstallState(state), updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
2178
+ writePrivateFile(path, `${JSON.stringify(canonical, null, 2)}
2052
2179
  `);
2053
2180
  }
2054
2181
  function releaseDirFor(prefix, version) {
@@ -2268,6 +2395,8 @@ async function installRelease(options) {
2268
2395
  ensurePublicDir(releasesDir);
2269
2396
  const source = resolvePath(options.source, "release source");
2270
2397
  const sourceStat = (0, import_node_fs4.lstatSync)(source);
2398
+ const statePath = (0, import_node_path4.join)(stateDir, "install-state.json");
2399
+ const state = readInstallState(statePath, installStateDefaults(prefix, stateDir, database));
2271
2400
  const stage = (0, import_node_path4.join)(releasesDir, `.stage-${(0, import_node_crypto5.randomUUID)()}`);
2272
2401
  try {
2273
2402
  if (sourceStat.isFile()) {
@@ -2309,8 +2438,6 @@ async function installRelease(options) {
2309
2438
  hubEnv: env.hubEnv,
2310
2439
  collectorEnv: env.collectorEnv
2311
2440
  });
2312
- const statePath = (0, import_node_path4.join)(stateDir, "install-state.json");
2313
- const state = readInstallState(statePath, installStateDefaults(prefix, stateDir, database));
2314
2441
  state.releases[manifest.version] = {
2315
2442
  schemaVersion: manifest.schemaVersion,
2316
2443
  installedAt: new Date(now()).toISOString()
@@ -4266,8 +4393,8 @@ function readGaps(db, machineId, fromMs, toMs) {
4266
4393
  "SELECT from_ms, to_ms, dropped, reason FROM gaps WHERE machine_id = ? AND to_ms >= ? AND from_ms <= ? ORDER BY from_ms LIMIT ?"
4267
4394
  ).all(machineId, fromMs, toMs, MAX_GAPS);
4268
4395
  const missing = db.db.prepare(
4269
- "WITH ordered AS ( SELECT captured_at_ms, LAG(captured_at_ms) OVER (PARTITION BY machine_id ORDER BY captured_at_ms) AS prev_ms, expected_interval_ms FROM sample_receipts WHERE machine_id = ? AND captured_at_ms >= ? AND captured_at_ms <= ? ) SELECT prev_ms AS from_ms, captured_at_ms AS to_ms FROM ordered WHERE prev_ms IS NOT NULL AND captured_at_ms - prev_ms > MAX(15000, 3 * COALESCE(expected_interval_ms, 5000)) ORDER BY captured_at_ms LIMIT ?"
4270
- ).all(machineId, fromMs, toMs, MAX_GAPS);
4396
+ "WITH before_window AS ( SELECT captured_at_ms, expected_interval_ms FROM sample_receipts WHERE machine_id = ? AND captured_at_ms < ? ORDER BY captured_at_ms DESC LIMIT 1 ), in_window AS ( SELECT captured_at_ms, expected_interval_ms FROM sample_receipts WHERE machine_id = ? AND captured_at_ms >= ? AND captured_at_ms <= ? ), ordered AS ( SELECT captured_at_ms, expected_interval_ms, LAG(captured_at_ms) OVER (ORDER BY captured_at_ms) AS prev_ms FROM (SELECT * FROM before_window UNION ALL SELECT * FROM in_window) ) SELECT MAX(prev_ms, ?) AS from_ms, captured_at_ms AS to_ms FROM ordered WHERE prev_ms IS NOT NULL AND captured_at_ms > ? AND captured_at_ms - prev_ms > MAX(15000, 3 * COALESCE(expected_interval_ms, 5000)) ORDER BY captured_at_ms LIMIT ?"
4397
+ ).all(machineId, fromMs, machineId, fromMs, toMs, fromMs, fromMs, MAX_GAPS);
4271
4398
  const gaps = [
4272
4399
  ...dropped.map((row) => ({
4273
4400
  fromMs: Number(row.from_ms),