@camstack/system 1.2.20 → 1.2.22

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 (48) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.d.ts +26 -13
  8. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1366 -1098
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1366 -1098
  10. package/dist/builtins/backup-orchestrator/schedule-store.d.ts +33 -0
  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 +1 -1
  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 +1 -1
  27. package/dist/builtins/platform-probe/index.js +1 -1
  28. package/dist/builtins/platform-probe/index.mjs +1 -1
  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 +5 -3
  38. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +5 -3
  39. package/dist/builtins/storage-orchestrator/storage-orchestrator.service.d.ts +6 -0
  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/{dist-DmQ6thmF.mjs → dist-C9_XdXl5.mjs} +100 -4
  45. package/dist/{dist-OQ1yU92D.js → dist-j49ewCPY.js} +100 -4
  46. package/dist/index.js +1 -1
  47. package/dist/index.mjs +1 -1
  48. package/package.json +3 -2
@@ -0,0 +1,33 @@
1
+ import { ISettingsBackend, TableSchema } from '@camstack/types';
2
+ /**
3
+ * A backup schedule row.
4
+ *
5
+ * `locationIds` is the fan-out set (destination `backups` locations).
6
+ * `retentionCount` applies to every one of them for THIS schedule's
7
+ * runs. `dataSources` optionally narrows which top-level state
8
+ * locations get archived; empty = the orchestrator's full default set.
9
+ */
10
+ export interface BackupSchedule {
11
+ readonly id: string;
12
+ readonly label: string;
13
+ readonly cron: string;
14
+ readonly enabled: boolean;
15
+ readonly locationIds: readonly string[];
16
+ readonly retentionCount: number;
17
+ readonly dataSources?: readonly string[];
18
+ /** ms-epoch of the last successful run. */
19
+ readonly lastRunAt?: number;
20
+ }
21
+ export declare const BACKUP_SCHEDULES_SCHEMA: TableSchema;
22
+ export declare class BackupScheduleService {
23
+ private readonly backend;
24
+ private tableEnsured;
25
+ constructor(backend: ISettingsBackend);
26
+ initialize(): Promise<void>;
27
+ private ensureTable;
28
+ list(): Promise<readonly BackupSchedule[]>;
29
+ get(id: string): Promise<BackupSchedule | null>;
30
+ /** Replace-or-insert (delete + insert — mirrors the policy store). */
31
+ upsert(s: BackupSchedule): Promise<void>;
32
+ delete(id: string): Promise<void>;
33
+ }
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-OQ1yU92D.js");
6
+ const require_dist = require("../../dist-j49ewCPY.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/console-logging/console-destination.ts
9
9
  var LEVEL_RANK = {
@@ -1,4 +1,4 @@
1
- import { at as BaseAddon, z as logDestinationCapability } from "../../dist-DmQ6thmF.mjs";
1
+ import { at as BaseAddon, z as logDestinationCapability } from "../../dist-C9_XdXl5.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/console-logging/console-destination.ts
4
4
  var LEVEL_RANK = {
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-OQ1yU92D.js");
6
+ const require_dist = require("../../dist-j49ewCPY.js");
7
7
  let _camstack_types_node = require("@camstack/types/node");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
@@ -1,4 +1,4 @@
1
- import { At as sleep, C as deviceStatusCapability, Ct as isDeviceConfigCap, D as enumerateSchemaFields, E as enumerateItemArrayFields, G as parseStreamParamsFormPatch, O as evaluateLinkExpression, S as deviceStateCapability, U as normalizeUnit, Y as setByPath, _ as applyTransform, a as CAP_NAMES_WITH_STATUS, at as BaseAddon, b as buildStreamParamsConfigSchema, dt as DeviceType, it as errMsg, j as getByPath, jt as EventCategory, l as STREAM_PROFILE_META, lt as DeviceFeature, mt as WELL_KNOWN_TAB_MAP, o as DeviceStatusSchema, rt as validateExpressionSource, t as ALL_CAPABILITY_DEFINITIONS, tt as toExpressionValue, ut as DeviceRole, x as deviceManagerCapability } from "../../dist-DmQ6thmF.mjs";
1
+ import { At as sleep, C as deviceStatusCapability, Ct as isDeviceConfigCap, D as enumerateSchemaFields, E as enumerateItemArrayFields, G as parseStreamParamsFormPatch, O as evaluateLinkExpression, S as deviceStateCapability, U as normalizeUnit, Y as setByPath, _ as applyTransform, a as CAP_NAMES_WITH_STATUS, at as BaseAddon, b as buildStreamParamsConfigSchema, dt as DeviceType, it as errMsg, j as getByPath, jt as EventCategory, l as STREAM_PROFILE_META, lt as DeviceFeature, mt as WELL_KNOWN_TAB_MAP, o as DeviceStatusSchema, rt as validateExpressionSource, t as ALL_CAPABILITY_DEFINITIONS, tt as toExpressionValue, ut as DeviceRole, x as deviceManagerCapability } from "../../dist-C9_XdXl5.mjs";
2
2
  import { canonicalDeviceFingerprint } from "@camstack/types/node";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-OQ1yU92D.js");
6
+ const require_dist = require("../../dist-j49ewCPY.js");
7
7
  //#region src/builtins/doorbell/trigger-engine.ts
8
8
  /**
9
9
  * Pure trigger logic for the virtual-doorbell builtin — edge detection over
@@ -1,4 +1,4 @@
1
- import { at as BaseAddon, dt as DeviceType, it as errMsg, jt as EventCategory, vt as createEvent, w as doorbellCapability } from "../../dist-DmQ6thmF.mjs";
1
+ import { at as BaseAddon, dt as DeviceType, it as errMsg, jt as EventCategory, vt as createEvent, w as doorbellCapability } from "../../dist-C9_XdXl5.mjs";
2
2
  //#region src/builtins/doorbell/trigger-engine.ts
3
3
  /**
4
4
  * Pure trigger logic for the virtual-doorbell builtin — edge detection over
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-OQ1yU92D.js");
6
+ const require_dist = require("../../dist-j49ewCPY.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
9
9
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -1,4 +1,4 @@
1
- import { at as BaseAddon, z as logDestinationCapability } from "../../dist-DmQ6thmF.mjs";
1
+ import { at as BaseAddon, z as logDestinationCapability } from "../../dist-C9_XdXl5.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  //#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
4
4
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-OQ1yU92D.js");
6
+ const require_dist = require("../../dist-j49ewCPY.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  node_crypto = require_chunk.__toESM(node_crypto);
9
9
  let crypto$1 = require("crypto");
@@ -1,5 +1,5 @@
1
1
  import { i as __require, o as __toESM, t as __commonJSMin } from "../../chunk-CNf5ZN-e.mjs";
2
- import { at as BaseAddon, f as UserRecordSchema, nt as userManagementCapability, r as ApiKeyRecordSchema, u as ScopedTokenSchema, v as authProviderCapability } from "../../dist-DmQ6thmF.mjs";
2
+ import { at as BaseAddon, f as UserRecordSchema, nt as userManagementCapability, r as ApiKeyRecordSchema, u as ScopedTokenSchema, v as authProviderCapability } from "../../dist-C9_XdXl5.mjs";
3
3
  import * as crypto$1 from "node:crypto";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import nodeCrypto from "crypto";
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-OQ1yU92D.js");
3
+ const require_dist = require("../../dist-j49ewCPY.js");
4
4
  let node_os = require("node:os");
5
5
  node_os = require_chunk.__toESM(node_os);
6
6
  //#region src/builtins/local-network/local-network.addon.ts
@@ -1,4 +1,4 @@
1
- import { R as localNetworkCapability, at as BaseAddon, jt as EventCategory } from "../../dist-DmQ6thmF.mjs";
1
+ import { R as localNetworkCapability, at as BaseAddon, jt as EventCategory } from "../../dist-C9_XdXl5.mjs";
2
2
  import * as os from "node:os";
3
3
  //#region src/builtins/local-network/local-network.addon.ts
4
4
  /**
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-OQ1yU92D.js");
6
+ const require_dist = require("../../dist-j49ewCPY.js");
7
7
  //#region src/builtins/loki-logging/loki-payload.ts
8
8
  /**
9
9
  * Loki rejects a label name that is not a valid Prometheus label
@@ -1,4 +1,4 @@
1
- import { at as BaseAddon, z as logDestinationCapability } from "../../dist-DmQ6thmF.mjs";
1
+ import { at as BaseAddon, z as logDestinationCapability } from "../../dist-C9_XdXl5.mjs";
2
2
  //#region src/builtins/loki-logging/loki-payload.ts
3
3
  /**
4
4
  * Loki rejects a label name that is not a valid Prometheus label
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-OQ1yU92D.js");
6
+ const require_dist = require("../../dist-j49ewCPY.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_chunk.__toESM(node_fs);
9
9
  let node_child_process = require("node:child_process");
@@ -1,4 +1,4 @@
1
- import { H as metricsProviderCapability, at as BaseAddon, jt as EventCategory, vt as createEvent } from "../../dist-DmQ6thmF.mjs";
1
+ import { H as metricsProviderCapability, at as BaseAddon, jt as EventCategory, vt as createEvent } from "../../dist-C9_XdXl5.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import { execFile, execFileSync } from "node:child_process";
4
4
  import { promisify } from "node:util";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-OQ1yU92D.js");
6
+ const require_dist = require("../../dist-j49ewCPY.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_chunk.__toESM(node_fs);
9
9
  let node_child_process = require("node:child_process");
@@ -1,5 +1,5 @@
1
1
  import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
2
- import { J as scoreRuntimes, K as platformProbeCapability, T as enumerateInferenceDevices, at as BaseAddon, bt as emitReadiness, it as errMsg, jt as EventCategory } from "../../dist-DmQ6thmF.mjs";
2
+ import { J as scoreRuntimes, K as platformProbeCapability, T as enumerateInferenceDevices, at as BaseAddon, bt as emitReadiness, it as errMsg, jt as EventCategory } from "../../dist-C9_XdXl5.mjs";
3
3
  import * as fs from "node:fs";
4
4
  import { execFile } from "node:child_process";
5
5
  import { promisify } from "node:util";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-OQ1yU92D.js");
6
+ const require_dist = require("../../dist-j49ewCPY.js");
7
7
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
8
8
  /**
9
9
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -1,4 +1,4 @@
1
- import { at as BaseAddon, jt as EventCategory } from "../../dist-DmQ6thmF.mjs";
1
+ import { at as BaseAddon, jt as EventCategory } from "../../dist-C9_XdXl5.mjs";
2
2
  //#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
3
3
  /**
4
4
  * Reconcile the durable `enabledProviders` set against authoritative
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-OQ1yU92D.js");
6
+ const require_dist = require("../../dist-j49ewCPY.js");
7
7
  let node_child_process = require("node:child_process");
8
8
  //#region src/builtins/snapshot/snapshot-coalescing.ts
9
9
  /**
@@ -1,4 +1,4 @@
1
- import { Z as snapshotCapability, at as BaseAddon, dt as DeviceType, et as streamQualityLabel, i as BatteryStatusSchema, it as errMsg, lt as DeviceFeature, wt as nodePin } from "../../dist-DmQ6thmF.mjs";
1
+ import { Z as snapshotCapability, at as BaseAddon, dt as DeviceType, et as streamQualityLabel, i as BatteryStatusSchema, it as errMsg, lt as DeviceFeature, wt as nodePin } from "../../dist-C9_XdXl5.mjs";
2
2
  import { execFile } from "node:child_process";
3
3
  //#region src/builtins/snapshot/snapshot-coalescing.ts
4
4
  /**
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-OQ1yU92D.js");
6
+ const require_dist = require("../../dist-j49ewCPY.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_chunk.__toESM(node_fs);
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { $ as storageProviderCapability, A as filesystemBrowseCapability, at as BaseAddon } from "../../dist-DmQ6thmF.mjs";
1
+ import { $ as storageProviderCapability, A as filesystemBrowseCapability, at as BaseAddon } from "../../dist-C9_XdXl5.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import * as path$1 from "node:path";
4
4
  import { basename, dirname, join, resolve, sep } from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-OQ1yU92D.js");
6
+ const require_dist = require("../../dist-j49ewCPY.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let better_sqlite3 = require("better-sqlite3");
9
9
  better_sqlite3 = require_chunk.__toESM(better_sqlite3);
@@ -1,4 +1,4 @@
1
- import { Et as parseJsonUnknown, X as settingsStoreCapability, at as BaseAddon, c as RUNTIME_DEFAULTS, ht as asJsonObject, it as errMsg } from "../../dist-DmQ6thmF.mjs";
1
+ import { Et as parseJsonUnknown, X as settingsStoreCapability, at as BaseAddon, c as RUNTIME_DEFAULTS, ht as asJsonObject, it as errMsg } from "../../dist-C9_XdXl5.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import Database from "better-sqlite3";
4
4
  //#region src/builtins/sqlite-storage/sqlite-settings-backend.ts
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-OQ1yU92D.js");
6
+ const require_dist = require("../../dist-j49ewCPY.js");
7
7
  let node_path = require("node:path");
8
8
  node_path = require_chunk.__toESM(node_path);
9
9
  let node_fs_promises = require("node:fs/promises");
@@ -378,8 +378,8 @@ var StorageOrchestratorService = class {
378
378
  let added = 0;
379
379
  const declById = new Map(input.declarations.map((d) => [d.id, d]));
380
380
  const rootFor = (d) => {
381
- const useMedia = d.defaultRoot === "media" && input.mediaBasePath;
382
- return node_path.join(useMedia ? input.mediaBasePath : input.basePath, d.id);
381
+ const dedicated = d.defaultRoot === "media" ? input.mediaBasePath : d.defaultRoot === "backup" ? input.backupBasePath : void 0;
382
+ return node_path.join(dedicated ?? input.basePath, d.id);
383
383
  };
384
384
  for (const d of input.declarations) {
385
385
  if (this.hasAnyLocationOfType(d.id)) continue;
@@ -1120,10 +1120,12 @@ var StorageOrchestratorAddon = class extends require_dist.BaseAddon {
1120
1120
  this.service.setRegistry(registry);
1121
1121
  this.service.pruneUndeclaredSystemLocations();
1122
1122
  const mediaRoot = process.env.CAMSTACK_MEDIA_ROOT?.trim();
1123
+ const backupRoot = process.env.CAMSTACK_BACKUP_ROOT?.trim();
1123
1124
  this.service.seedDefaults({
1124
1125
  providerId: "filesystem-storage",
1125
1126
  basePath: this.ctx.nodeDataDir,
1126
1127
  mediaBasePath: mediaRoot && mediaRoot.length > 0 ? mediaRoot : void 0,
1128
+ backupBasePath: backupRoot && backupRoot.length > 0 ? backupRoot : void 0,
1127
1129
  declarations: registry.list()
1128
1130
  });
1129
1131
  await this.service.backfillNodeIds(HUB_NODE_ID);
@@ -1,4 +1,4 @@
1
- import { Q as storageCapability, Tt as parseJsonObject, at as BaseAddon, d as StorageLocationTypeSchema } from "../../dist-DmQ6thmF.mjs";
1
+ import { Q as storageCapability, Tt as parseJsonObject, at as BaseAddon, d as StorageLocationTypeSchema } from "../../dist-C9_XdXl5.mjs";
2
2
  import * as path$1 from "node:path";
3
3
  import * as fs from "node:fs/promises";
4
4
  import { buildStorageLocationRegistry } from "@camstack/system";
@@ -371,8 +371,8 @@ var StorageOrchestratorService = class {
371
371
  let added = 0;
372
372
  const declById = new Map(input.declarations.map((d) => [d.id, d]));
373
373
  const rootFor = (d) => {
374
- const useMedia = d.defaultRoot === "media" && input.mediaBasePath;
375
- return path$1.join(useMedia ? input.mediaBasePath : input.basePath, d.id);
374
+ const dedicated = d.defaultRoot === "media" ? input.mediaBasePath : d.defaultRoot === "backup" ? input.backupBasePath : void 0;
375
+ return path$1.join(dedicated ?? input.basePath, d.id);
376
376
  };
377
377
  for (const d of input.declarations) {
378
378
  if (this.hasAnyLocationOfType(d.id)) continue;
@@ -1113,10 +1113,12 @@ var StorageOrchestratorAddon = class extends BaseAddon {
1113
1113
  this.service.setRegistry(registry);
1114
1114
  this.service.pruneUndeclaredSystemLocations();
1115
1115
  const mediaRoot = process.env.CAMSTACK_MEDIA_ROOT?.trim();
1116
+ const backupRoot = process.env.CAMSTACK_BACKUP_ROOT?.trim();
1116
1117
  this.service.seedDefaults({
1117
1118
  providerId: "filesystem-storage",
1118
1119
  basePath: this.ctx.nodeDataDir,
1119
1120
  mediaBasePath: mediaRoot && mediaRoot.length > 0 ? mediaRoot : void 0,
1121
+ backupBasePath: backupRoot && backupRoot.length > 0 ? backupRoot : void 0,
1120
1122
  declarations: registry.list()
1121
1123
  });
1122
1124
  await this.service.backfillNodeIds(HUB_NODE_ID);
@@ -235,6 +235,12 @@ export declare class StorageOrchestratorService {
235
235
  * `basePath` like everything else.
236
236
  */
237
237
  readonly mediaBasePath?: string;
238
+ /**
239
+ * Root for declarations marked `defaultRoot: 'backup'` (the dedicated
240
+ * backup volume, `CAMSTACK_BACKUP_ROOT`, default `/backups`). Absent →
241
+ * backup declarations fall back to `basePath`.
242
+ */
243
+ readonly backupBasePath?: string;
238
244
  readonly declarations: readonly StorageLocationDeclaration[];
239
245
  }): void;
240
246
  /** Internal: check whether any location of the given type exists. */
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-OQ1yU92D.js");
6
+ const require_dist = require("../../dist-j49ewCPY.js");
7
7
  //#region src/builtins/system-config/system-config.addon.ts
8
8
  /**
9
9
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -1,4 +1,4 @@
1
- import { St as hydrateSchema, at as BaseAddon, it as errMsg } from "../../dist-DmQ6thmF.mjs";
1
+ import { St as hydrateSchema, at as BaseAddon, it as errMsg } from "../../dist-C9_XdXl5.mjs";
2
2
  //#region src/builtins/system-config/system-config.addon.ts
3
3
  /**
4
4
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-OQ1yU92D.js");
6
+ const require_dist = require("../../dist-j49ewCPY.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  let node_path = require("node:path");
9
9
  node_path = require_chunk.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { at as BaseAddon, z as logDestinationCapability } from "../../dist-DmQ6thmF.mjs";
1
+ import { at as BaseAddon, z as logDestinationCapability } from "../../dist-C9_XdXl5.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  import * as path$1 from "node:path";
4
4
  import path from "node:path";
@@ -3567,16 +3567,23 @@ var StorageLocationDeclarationSchema = z.object({
3567
3567
  * Which node root the seeded `<id>:default` instance is placed under on a
3568
3568
  * FRESH install:
3569
3569
  * - `'data'` (default) — the node's data dir (`CAMSTACK_DATA` / boot dir),
3570
- * the appData volume. Right for small/durable data (backups, logs, models).
3570
+ * the appData volume. Right for small/durable data (logs, models).
3571
3571
  * - `'media'` — the dedicated media volume (`CAMSTACK_MEDIA_ROOT`) when that
3572
3572
  * env is set, else falls back to the data root. Right for bulky, hot media
3573
3573
  * (recordings, event media) that should stay off the appData disk.
3574
+ * - `'backup'` — the dedicated backup volume (`CAMSTACK_BACKUP_ROOT`, default
3575
+ * `/backups` in the image) so archives live on their own mount rather than
3576
+ * filling the appData disk. Falls back to the data root when unset.
3574
3577
  *
3575
3578
  * Only affects the seeded default's `basePath`; operators can repoint any
3576
3579
  * location afterwards, and a `defaultsTo` slot inherits its parent's root
3577
3580
  * regardless of this field. Absent (the common case) is treated as `'data'`.
3578
3581
  */
3579
- defaultRoot: z.enum(["data", "media"]).optional()
3582
+ defaultRoot: z.enum([
3583
+ "data",
3584
+ "media",
3585
+ "backup"
3586
+ ]).optional()
3580
3587
  });
3581
3588
  /** Friendly display labels for stream quality IDs. */
3582
3589
  var STREAM_QUALITY_LABELS = {
@@ -13573,6 +13580,41 @@ var LocationStatSchema = z.object({
13573
13580
  present: z.boolean()
13574
13581
  });
13575
13582
  /**
13583
+ * A backup schedule — the N:M "entry" that binds one cron cadence to a
13584
+ * SET of destination locations. Supersedes the per-location cron on
13585
+ * `BackupDestinationPolicy`: an operator creates a schedule, picks the
13586
+ * `backups` locations it should write to, and the orchestrator fans a
13587
+ * single archive out to all of them when the cron fires.
13588
+ *
13589
+ * `retentionCount` is per-schedule (D-decision 2026-07-28): every
13590
+ * location targeted by this schedule keeps this many archives from
13591
+ * this schedule's runs.
13592
+ *
13593
+ * `dataSources` optionally narrows which top-level state locations
13594
+ * (db, addons, tls, …) are archived; omitted = the orchestrator's
13595
+ * default full set.
13596
+ */
13597
+ var BackupScheduleSchema = z.object({
13598
+ /** Stable id. Generated by the orchestrator on first upsert if absent. */
13599
+ id: z.string(),
13600
+ /** Operator-facing display name. */
13601
+ label: z.string(),
13602
+ /** 5-field POSIX cron. Empty = disabled cadence (kept for editing). */
13603
+ cron: z.string(),
13604
+ /** Master on/off toggle for the whole schedule. */
13605
+ enabled: z.boolean(),
13606
+ /** `backups`-location ids this schedule writes to (fan-out set). */
13607
+ locationIds: z.array(z.string()).readonly(),
13608
+ /** Archives kept per targeted location for this schedule. */
13609
+ retentionCount: z.number().int().min(1).max(1e3),
13610
+ /** Optional subset of source locations to include; omitted = all. */
13611
+ dataSources: z.array(z.string()).readonly().optional(),
13612
+ /** ms-epoch of last successful run. */
13613
+ lastRunAt: z.number().optional(),
13614
+ /** ms-epoch of next computed firing (read-only, filled on list). */
13615
+ nextRunAt: z.number().optional()
13616
+ });
13617
+ /**
13576
13618
  * backup — singleton capability for backup management.
13577
13619
  *
13578
13620
  * Implemented by `local-backup` addon. Future providers (S3, rsync)
@@ -13599,7 +13641,14 @@ var backupCapability = {
13599
13641
  /** Subset of registered `backup-destination` addon ids to write to. */
13600
13642
  destinations: z.array(z.string()).optional(),
13601
13643
  locations: z.array(z.string()).optional(),
13602
- label: z.string().optional()
13644
+ label: z.string().optional(),
13645
+ /**
13646
+ * Per-run retention override applied to every targeted
13647
+ * destination. Used by schedule-driven runs (per-entry
13648
+ * retention). Omitted = each destination's own policy
13649
+ * retention (manual runs).
13650
+ */
13651
+ retentionCount: z.number().int().min(1).max(1e3).optional()
13603
13652
  }).optional(), z.array(BackupEntrySchema).readonly(), {
13604
13653
  kind: "mutation",
13605
13654
  auth: "admin"
@@ -13686,7 +13735,36 @@ var backupCapability = {
13686
13735
  ok: z.boolean(),
13687
13736
  error: z.string().optional(),
13688
13737
  nextRuns: z.array(z.number()).readonly()
13689
- }))
13738
+ })),
13739
+ /**
13740
+ * List every backup schedule (the N:M entries), each with its
13741
+ * computed `nextRunAt`. Powers the Schedules section of the
13742
+ * admin-UI Backups page.
13743
+ */
13744
+ listSchedules: method(z.void(), z.array(BackupScheduleSchema).readonly(), { auth: "admin" }),
13745
+ /**
13746
+ * Create or replace a schedule. An empty / absent `id` mints a new
13747
+ * one; a present `id` replaces in place. `cron` is validated
13748
+ * server-side; `locationIds` must reference existing `backups`
13749
+ * locations (unknown ids are dropped with a warning).
13750
+ */
13751
+ upsertSchedule: method(z.object({
13752
+ id: z.string().optional(),
13753
+ label: z.string(),
13754
+ cron: z.string(),
13755
+ enabled: z.boolean(),
13756
+ locationIds: z.array(z.string()).readonly(),
13757
+ retentionCount: z.number().int().min(1).max(1e3),
13758
+ dataSources: z.array(z.string()).readonly().optional()
13759
+ }), BackupScheduleSchema, {
13760
+ kind: "mutation",
13761
+ auth: "admin"
13762
+ }),
13763
+ /** Delete a schedule by id. Does not touch the target locations. */
13764
+ deleteSchedule: method(z.object({ id: z.string() }), z.void(), {
13765
+ kind: "mutation",
13766
+ auth: "admin"
13767
+ })
13690
13768
  }
13691
13769
  };
13692
13770
  /**
@@ -23904,6 +23982,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
23904
23982
  addonId: null,
23905
23983
  access: "delete"
23906
23984
  },
23985
+ "backup.deleteSchedule": {
23986
+ capName: "backup",
23987
+ capScope: "system",
23988
+ addonId: null,
23989
+ access: "delete"
23990
+ },
23907
23991
  "backup.getEntries": {
23908
23992
  capName: "backup",
23909
23993
  capScope: "system",
@@ -23934,6 +24018,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
23934
24018
  addonId: null,
23935
24019
  access: "view"
23936
24020
  },
24021
+ "backup.listSchedules": {
24022
+ capName: "backup",
24023
+ capScope: "system",
24024
+ addonId: null,
24025
+ access: "view"
24026
+ },
23937
24027
  "backup.previewSchedule": {
23938
24028
  capName: "backup",
23939
24029
  capScope: "system",
@@ -23958,6 +24048,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
23958
24048
  addonId: null,
23959
24049
  access: "create"
23960
24050
  },
24051
+ "backup.upsertSchedule": {
24052
+ capName: "backup",
24053
+ capScope: "system",
24054
+ addonId: null,
24055
+ access: "create"
24056
+ },
23961
24057
  "battery.wakeForStream": {
23962
24058
  capName: "battery",
23963
24059
  capScope: "device",