@camstack/system 1.2.15 → 1.2.16

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 (57) hide show
  1. package/dist/addon-runner.js +2 -2
  2. package/dist/addon-runner.mjs +2 -2
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.js +1 -1
  8. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +73 -2
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +73 -2
  11. package/dist/builtins/console-logging/index.js +1 -1
  12. package/dist/builtins/console-logging/index.mjs +1 -1
  13. package/dist/builtins/device-manager/device-manager.addon.js +1 -1
  14. package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
  15. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  16. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  17. package/dist/builtins/hub-forwarder/index.js +1 -1
  18. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  19. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  20. package/dist/builtins/local-auth/local-auth.addon.mjs +3 -38
  21. package/dist/builtins/local-network/local-network.addon.js +1 -1
  22. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  23. package/dist/builtins/loki-logging/index.js +1 -1
  24. package/dist/builtins/loki-logging/index.mjs +1 -1
  25. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  26. package/dist/builtins/native-metrics/native-metrics.addon.mjs +2 -2
  27. package/dist/builtins/platform-probe/index.js +2 -2
  28. package/dist/builtins/platform-probe/index.mjs +3 -2
  29. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  30. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  31. package/dist/builtins/snapshot/index.js +1 -1
  32. package/dist/builtins/snapshot/index.mjs +1 -1
  33. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  34. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  35. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +1 -1
  36. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
  37. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  38. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  39. package/dist/builtins/system-backup/system-backup.service.d.ts +14 -1
  40. package/dist/builtins/system-config/system-config.addon.js +1 -1
  41. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  42. package/dist/builtins/winston-logging/index.js +1 -1
  43. package/dist/builtins/winston-logging/index.mjs +1 -1
  44. package/dist/chunk-CNf5ZN-e.mjs +37 -0
  45. package/dist/{dist-yttC1aUI.js → dist-3AzhkmeY.js} +1325 -1129
  46. package/dist/{dist-eOaraBhT.mjs → dist-BU-QPlE8.mjs} +1325 -1129
  47. package/dist/framework-resolver-hook.mjs +2 -0
  48. package/dist/index.js +224 -32
  49. package/dist/index.mjs +227 -34
  50. package/dist/kernel/addon-installer.d.ts +86 -7
  51. package/dist/kernel/fs-utils.d.ts +18 -5
  52. package/dist/lib-5THG9eKz.mjs +15545 -0
  53. package/dist/lib-CZ3g2WTS.js +15549 -0
  54. package/dist/{manifest-python-deps-bn3qrsxR.mjs → manifest-python-deps-D7-WBIg_.mjs} +1 -1
  55. package/dist/{manifest-python-deps-DSrmih6E.js → manifest-python-deps-DRN3x53E.js} +1 -1
  56. package/dist/{resource-monitor-Le3l0qS2.mjs → resource-monitor-BkP504Vq.mjs} +1 -1
  57. package/package.json +1 -1
@@ -33,6 +33,13 @@ export interface AddonInstallerConfig {
33
33
  readonly installSource?: InstallSource;
34
34
  }
35
35
  export type { InstalledPackage };
36
+ /** Outcome of {@link AddonInstaller.sweepBackups}. Absolute paths. */
37
+ export interface BackupSweepResult {
38
+ /** Backup dirs reclaimed by the sweep. */
39
+ readonly removed: readonly string[];
40
+ /** Backup dirs deliberately retained (rollback target / last copy). */
41
+ readonly kept: readonly string[];
42
+ }
36
43
  export declare class AddonInstaller {
37
44
  readonly addonsDir: string;
38
45
  private readonly registry;
@@ -222,10 +229,14 @@ export declare class AddonInstaller {
222
229
  * 1. Rename current install dir → {addonsDir}/.backups/{name}/{ts}/
223
230
  * 2. Run a fresh install (npm or local depending on installSource)
224
231
  * 3. Record `lastBackupDir` in the manifest entry
232
+ * 4. Prune every OTHER backup of this addon — the previous one just
233
+ * lost its only pointer and would otherwise be orphaned forever.
225
234
  *
226
- * The post-install caller (kernel.restartAddon + health-check) is
227
- * responsible for verifying the new version actually works and for
228
- * triggering `rollbackAddon()` if the health check fails.
235
+ * The post-install caller (restart + health-check) is responsible for
236
+ * verifying the new version actually works, and for calling either
237
+ * `clearBackup()` on success or `rollbackAddon()` on failure. This
238
+ * method must NOT delete the backup itself: at this point the new
239
+ * version has not been proven to work.
229
240
  *
230
241
  * Throws if there is no installed version to update — caller should
231
242
  * prefer plain `install()` for first-time installs.
@@ -263,12 +274,80 @@ export declare class AddonInstaller {
263
274
  * backup to roll back to.
264
275
  */
265
276
  rollbackAddon(packageName: string): Promise<string | null>;
277
+ /** Root of the pre-update rollback copies: `{addonsDir}/.backups`. */
278
+ private get backupsRoot();
279
+ /**
280
+ * True only when `target` resolves to something strictly INSIDE
281
+ * `{addonsDir}/.backups/`.
282
+ *
283
+ * Every deletion in this section funnels through here. `lastBackupDir`
284
+ * is read back from a manifest file on disk — an operator edit, a
285
+ * restored manifest from another host, or a `..` in the recorded path
286
+ * must never turn "reclaim a backup" into "delete the live install".
287
+ */
288
+ private isInsideBackups;
289
+ /**
290
+ * Best-effort removal of one path under `.backups/`.
291
+ *
292
+ * Never throws: an undeleted backup is a disk-space problem, whereas a
293
+ * throw on the post-update success path would turn a working update into
294
+ * a reported outage. Returns true only when the path is gone.
295
+ */
296
+ private removeBackupPath;
297
+ /** List the timestamped backup dirs recorded for one package. */
298
+ private listBackupsFor;
299
+ /**
300
+ * Keep at most ONE backup per addon: drop every backup dir for
301
+ * `packageName` except `keepDir`.
302
+ *
303
+ * Called right after a new backup is recorded. Without this, a second
304
+ * update overwrites `lastBackupDir` and the previous backup becomes
305
+ * unreachable — no pointer, so neither `rollbackAddon` nor `clearBackup`
306
+ * can ever see it again. That is how the live hub accumulated two 398 MiB
307
+ * copies of one addon. Never throws.
308
+ */
309
+ private pruneBackups;
310
+ /** Drop `.backups/{pkg}` (and its `@scope` parent) once they are empty. */
311
+ private removeEmptyBackupDirs;
312
+ /**
313
+ * Drop the recorded backup for an addon — call once the post-update
314
+ * health check confirms the new version actually works.
315
+ *
316
+ * Never throws and never rejects. The manifest pointer is cleared even
317
+ * when the bytes could not be removed: a pointer at a half-deleted tree
318
+ * would make `rollbackAddon` restore a gutted install, and the boot sweep
319
+ * reclaims the residue on the next start.
320
+ */
321
+ clearBackup(packageName: string): Promise<void>;
322
+ /**
323
+ * Boot-time retention sweep over `{addonsDir}/.backups/`.
324
+ *
325
+ * RULE: for each addon, the ONLY backup that survives is the one the
326
+ * manifest's `lastBackupDir` points at — everything else is unreachable
327
+ * by construction (no pointer ⇒ neither `rollbackAddon` nor `clearBackup`
328
+ * can ever reach it). One exception: when the addon's LIVE install dir is
329
+ * missing, the newest backup is the only surviving copy of that addon (a
330
+ * crash between the rename-aside and the manifest write in `applyUpdate`),
331
+ * so it is kept instead of reclaimed.
332
+ *
333
+ * Why not "delete everything once the addon is healthy": this installer
334
+ * has no health signal, and the pointed-at backup is precisely the one a
335
+ * failed update left for the operator's Rollback button. Bounding to one
336
+ * per addon is enough — the observed 814 MiB was duplicate copies, not a
337
+ * single retained rollback target.
338
+ *
339
+ * Never throws. Safe to run on every boot; runs before any update can be
340
+ * in flight.
341
+ */
342
+ sweepBackups(): Promise<BackupSweepResult>;
266
343
  /**
267
- * Drop the recorded backup for an addon (e.g. after a successful
268
- * post-update health check confirms the new version is healthy).
269
- * Frees disk space without affecting the running install.
344
+ * Which backup of `packageName` survives the sweep: the manifest's
345
+ * rollback target, or when the live install is gone — the newest copy,
346
+ * which is then the only one that exists. `null` means "reclaim them all".
270
347
  */
271
- clearBackup(packageName: string): void;
348
+ private resolveBackupToKeep;
349
+ /** Package names that have a `.backups/{pkg}` directory, scoped or not. */
350
+ private listBackedUpPackages;
272
351
  uninstall(packageName: string): Promise<void>;
273
352
  listInstalled(): InstalledPackage[];
274
353
  isInstalled(packageName: string): boolean;
@@ -15,11 +15,24 @@ export declare function ensureDir(dirPath: string): void;
15
15
  */
16
16
  export declare function copyDirRecursive(src: string, dest: string): Promise<void>;
17
17
  /**
18
- * Runtime natives that must stay installable even when an addon forgets to
19
- * declare them: the mirror of the build preset's `COMMON_NATIVE_DEPS`
20
- * (tools/build/vite-lib.preset.ts — build tooling, not loadable at runtime).
21
- * These are the ONLY packages a self-contained dist genuinely `require()`s
22
- * from node_modules; everything else is already inlined by the bundle.
18
+ * Runtime deps a stripped addon package.json must KEEP so the installer still
19
+ * `npm install`s them into the addon dir.
20
+ *
21
+ * Empty by design: every native an addon can import is now HOST-EXTERNAL
22
+ * (node-av, sharp, node-pty resolved from the framework closure via the
23
+ * resolver hook, see host-externals.ts), and every pure-JS lib (ssh2, werift,
24
+ * @simplewebauthn, mqtt, aws-sdk, …) is inlined by the self-contained vite
25
+ * bundle. So a self-contained dist requires NOTHING from a local node_modules —
26
+ * `addons/` stays node_modules-free, which is what lets the backup tar the whole
27
+ * folder with no exclusion list (see ADR-0027). An addon that genuinely needs an
28
+ * un-bundleable runtime dep declares it explicitly in
29
+ * `camstack.runtimeDependencies`; the debug escape hatch
30
+ * `CAMSTACK_INSTALL_FULL_DEPS=1` restores the legacy install-everything path.
31
+ *
32
+ * (Historically this mirrored the build preset's COMMON_NATIVE_DEPS and
33
+ * per-addon-installed node-av/sharp/onnxruntime-node/ssh2/werift/systeminformation
34
+ * — 74k files / 1.34 GB of redundant node_modules on the live hub. That redundancy
35
+ * is removed: the natives come from the framework, the pure-JS from the bundle.)
23
36
  */
24
37
  export declare const RUNTIME_NATIVE_KEEP: readonly string[];
25
38
  /**