@camstack/system 1.2.25 → 1.2.27

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 (53) hide show
  1. package/dist/addon-runner.js +1 -1
  2. package/dist/addon-runner.mjs +1 -1
  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 +1 -1
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  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-aggregation.d.ts +18 -0
  14. package/dist/builtins/device-manager/device-manager.addon.js +47 -1
  15. package/dist/builtins/device-manager/device-manager.addon.mjs +47 -1
  16. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  17. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  18. package/dist/builtins/hub-forwarder/index.js +1 -1
  19. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  20. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  21. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  22. package/dist/builtins/local-network/local-network.addon.js +1 -1
  23. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  24. package/dist/builtins/loki-logging/index.js +1 -1
  25. package/dist/builtins/loki-logging/index.mjs +1 -1
  26. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  27. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  28. package/dist/builtins/platform-probe/index.js +1 -1
  29. package/dist/builtins/platform-probe/index.mjs +1 -1
  30. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  31. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  32. package/dist/builtins/snapshot/index.js +1 -1
  33. package/dist/builtins/snapshot/index.mjs +1 -1
  34. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  35. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  36. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +1 -1
  37. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
  38. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +0 -19
  39. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +143 -67
  40. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +143 -67
  41. package/dist/builtins/storage-orchestrator/storage-orchestrator.service.d.ts +67 -7
  42. package/dist/builtins/system-config/system-config.addon.js +1 -1
  43. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  44. package/dist/builtins/winston-logging/index.js +1 -1
  45. package/dist/builtins/winston-logging/index.mjs +1 -1
  46. package/dist/{dist-CIek6F9X.mjs → dist-BXmFvn3h.mjs} +174 -6
  47. package/dist/{dist-CS0pdC8l.js → dist-BxtIzexq.js} +174 -6
  48. package/dist/index.js +2 -2
  49. package/dist/index.mjs +2 -2
  50. package/dist/kernel/moleculer/readiness-context.d.ts +2 -2
  51. package/dist/{manifest-python-deps-julrfvyf.mjs → manifest-python-deps-BNOApgK0.mjs} +4 -4
  52. package/dist/{manifest-python-deps-COgal7Jy.js → manifest-python-deps-KZ00cbfP.js} +4 -4
  53. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  let zod = require("zod");
2
- //#region ../types/dist/event-category-BLcNejAE.mjs
2
+ //#region ../types/dist/event-category-Bz24uP1U.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -270,6 +270,19 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
270
270
  */
271
271
  EventCategory["DeviceStateChanged"] = "device.state-changed";
272
272
  /**
273
+ * Frame occupancy for a camera CHANGED — a tracked object was gained or
274
+ * lost. Carries `{ deviceId, totalObjects, byClass, zones }`.
275
+ *
276
+ * Emitted only on a change, so a steady scene is silent. It exists so a
277
+ * client can stop polling `zoneAnalytics.getCurrentSnapshot`: that was the
278
+ * one live badge with no push signal at all, and it cost a request every
279
+ * four seconds per visible camera.
280
+ *
281
+ * Like every event it is telemetry and may be dropped ([D8]) — a consumer
282
+ * keeps a slow reconcile rather than trusting it alone.
283
+ */
284
+ EventCategory["ZoneAnalyticsOccupancyChanged"] = "zone-analytics.occupancy-changed";
285
+ /**
273
286
  * Cap event fired by every device that registers the `battery`
274
287
  * capability. Mirrors the cap definition's `onStatusChanged`. Carries
275
288
  * `{ deviceId, status: BatteryStatus }`. Subscribers (alert center,
@@ -1849,6 +1862,7 @@ var ReadinessRegistry = class {
1849
1862
  logger;
1850
1863
  now;
1851
1864
  generation;
1865
+ verboseDeviceScopeLogs;
1852
1866
  snapshot = /* @__PURE__ */ new Map();
1853
1867
  subscriptions = /* @__PURE__ */ new Set();
1854
1868
  unsubscribeBus;
@@ -1859,6 +1873,7 @@ var ReadinessRegistry = class {
1859
1873
  this.logger = options.logger;
1860
1874
  this.now = options.now ?? (() => Date.now());
1861
1875
  this.generation = options.generation ?? randomGeneration();
1876
+ this.verboseDeviceScopeLogs = options.verboseDeviceScopeLogs ?? false;
1862
1877
  this.unsubscribeBus = this.bus.subscribe({ category: "system.ready-state" }, (event) => this.ingest(event.data));
1863
1878
  this.unsubscribeAgentOffline = this.bus.subscribe({ category: "agent.offline" }, (event) => this.synthesizeDownForNode(event.data.agentId));
1864
1879
  if (typeof this.bus.getRecent === "function") try {
@@ -1912,9 +1927,16 @@ var ReadinessRegistry = class {
1912
1927
  * Every applied entry dispatches a one-shot transition to matching
1913
1928
  * subscriptions so pending `awaitReady` callers unblock without having
1914
1929
  * to wait for a fresh event.
1930
+ *
1931
+ * Returns the number of records APPLIED (added or advanced). Callers
1932
+ * doing periodic reconciles use this to stay silent when the snapshot
1933
+ * changed nothing — the every-60s "hydrated 1446 record(s)" heartbeat
1934
+ * from every child process was pure noise (≈2 800 lines/hour observed
1935
+ * live on 2026-08-01).
1915
1936
  */
1916
1937
  hydrate(records) {
1917
1938
  const now = this.now();
1939
+ let applied = 0;
1918
1940
  for (const record of records) {
1919
1941
  const key = readinessKey(record.capName, record.scope);
1920
1942
  const prev = this.snapshot.get(key) ?? null;
@@ -1940,7 +1962,8 @@ var ReadinessRegistry = class {
1940
1962
  sourceNodeId: record.sourceNodeId
1941
1963
  };
1942
1964
  this.snapshot.set(key, hydrated);
1943
- if (this.logger) this.logger.debug(`readiness: ${record.capName} (${scopeKey(record.scope)}) → ${record.state} (hydrated${prev !== null ? " update" : ""}, gen=${record.generation.slice(0, 6)})`);
1965
+ applied++;
1966
+ this.logPerRecord(record.scope, `readiness: ${record.capName} (${scopeKey(record.scope)}) → ${record.state} (hydrated${prev !== null ? " update" : ""}, gen=${record.generation.slice(0, 6)})`);
1944
1967
  const transition = {
1945
1968
  capName: record.capName,
1946
1969
  scope: record.scope,
@@ -1961,6 +1984,23 @@ var ReadinessRegistry = class {
1961
1984
  }
1962
1985
  }
1963
1986
  }
1987
+ return applied;
1988
+ }
1989
+ /**
1990
+ * Per-record debug line, volume-gated: device-scoped records are logged
1991
+ * only when `verboseDeviceScopeLogs` is set (see the option's doc for
1992
+ * the 21 577-lines-per-boot-minute incident that forced the gate);
1993
+ * node/global records always log. Device lines carry `tags.deviceId`
1994
+ * so they group per-camera (CLAUDE.md logging rule).
1995
+ */
1996
+ logPerRecord(scope, message) {
1997
+ if (!this.logger) return;
1998
+ if (scope.type === "device") {
1999
+ if (!this.verboseDeviceScopeLogs) return;
2000
+ this.logger.debug(message, { tags: { deviceId: scope.deviceId } });
2001
+ return;
2002
+ }
2003
+ this.logger.debug(message);
1964
2004
  }
1965
2005
  /**
1966
2006
  * True when THIS process is the origin authority for `record` — a
@@ -2135,7 +2175,7 @@ var ReadinessRegistry = class {
2135
2175
  ts: payload.ts,
2136
2176
  durationInPrevState
2137
2177
  };
2138
- if (this.logger) this.logger.debug(`readiness: ${payload.capName} (${scopeKey(payload.scope)}) → ${payload.state} epoch=${epoch} gen=${payload.generation.slice(0, 6)} (prev ${durationInPrevState}ms)`);
2178
+ this.logPerRecord(payload.scope, `readiness: ${payload.capName} (${scopeKey(payload.scope)}) → ${payload.state} epoch=${epoch} gen=${payload.generation.slice(0, 6)} (prev ${durationInPrevState}ms)`);
2139
2179
  for (const sub of this.subscriptions) {
2140
2180
  if (sub.capName !== payload.capName) continue;
2141
2181
  if (!scopesEqual(sub.scope, payload.scope)) continue;
@@ -2180,7 +2220,7 @@ var ReadinessRegistry = class {
2180
2220
  ts: now,
2181
2221
  durationInPrevState
2182
2222
  };
2183
- if (this.logger) this.logger.debug(`readiness: ${record.capName} (${scopeKey(record.scope)}) → down (synthesized from agent.offline ${offlineNodeId})`);
2223
+ this.logPerRecord(record.scope, `readiness: ${record.capName} (${scopeKey(record.scope)}) → down (synthesized from agent.offline ${offlineNodeId})`);
2184
2224
  for (const sub of this.subscriptions) {
2185
2225
  if (sub.capName !== record.capName) continue;
2186
2226
  if (!scopesEqual(sub.scope, record.scope)) continue;
@@ -5326,6 +5366,29 @@ var NcOccupancyConditionSchema = zod.z.object({
5326
5366
  count: zod.z.number().int().min(0).default(1),
5327
5367
  sustainSeconds: zod.z.number().int().min(0).max(3600).default(15)
5328
5368
  });
5369
+ /**
5370
+ * Which zone-crossing DIRECTION a rule accepts (`ObjectEvent.zoneCrossing`).
5371
+ *
5372
+ * The values are not symmetric, and deliberately so — the absent value has to
5373
+ * mean exactly what every rule authored before this condition existed already
5374
+ * does:
5375
+ * - `enter` — entries and every NON-crossing record (movement state,
5376
+ * package, sensor). Exits are rejected. **This is the absent behaviour**:
5377
+ * an operator who never asked for exits must not start receiving them.
5378
+ * - `exit` — ONLY an exit crossing. A record that is not a crossing at all
5379
+ * fails closed, because "the car left the drive" is a question about a
5380
+ * boundary, not about a detection.
5381
+ * - `any` — no direction filter; entries, exits and non-crossings alike.
5382
+ *
5383
+ * A rule asking for a direction should normally also scope `zones`, which the
5384
+ * engine evaluates against the crossed zone as well as the current membership
5385
+ * (an exit's membership no longer contains the zone it just left).
5386
+ */
5387
+ var NcCrossingSchema = zod.z.enum([
5388
+ "enter",
5389
+ "exit",
5390
+ "any"
5391
+ ]);
5329
5392
  /** Admin-zone membership condition (zone IDs as stamped by the ZoneEngine). */
5330
5393
  var NcZoneConditionSchema = zod.z.object({
5331
5394
  ids: zod.z.array(zod.z.string().min(1)).min(1),
@@ -5350,6 +5413,13 @@ var NcConditionsSchema = zod.z.object({
5350
5413
  /** Veto zones — any hit fails the rule. */
5351
5414
  zonesExclude: zod.z.array(zod.z.string().min(1)).optional(),
5352
5415
  /**
5416
+ * Zone-crossing direction. IMMEDIATE only — a crossing is a per-event fact
5417
+ * and a closed track carries none, so a `track-end` rule asking for one
5418
+ * fails closed (use `zones`, which tests `zonesVisited`). ABSENT = `enter`,
5419
+ * which is exactly today's behaviour. See {@link NcCrossingSchema}.
5420
+ */
5421
+ crossing: NcCrossingSchema.optional(),
5422
+ /**
5353
5423
  * Exact (case-insensitive) match on the record's collapsed `label`
5354
5424
  * (identity name / plate text / subclass).
5355
5425
  */
@@ -5538,11 +5608,31 @@ var NcMediaPolicySchema = zod.z.object({
5538
5608
  */
5539
5609
  profile: CamProfileSchema.optional()
5540
5610
  });
5611
+ /**
5612
+ * Cooldown GRANULARITY over the subject's class — how much a fired
5613
+ * notification suppresses.
5614
+ * - `shared` (default, and the absent value) — one window for the whole
5615
+ * rule/scope: a cat silences the next dog for `cooldownSec`.
5616
+ * - `per-class` — an independent window per detected class, so cat→dog fires
5617
+ * at once and cat→cat still waits.
5618
+ *
5619
+ * AUDIO subjects are ALWAYS per-class regardless of this setting: a scream
5620
+ * must not be swallowed by a bark's window (the precedent this generalizes —
5621
+ * see `cooldownKey` in the rule engine).
5622
+ */
5623
+ var NcThrottleGranularitySchema = zod.z.enum(["shared", "per-class"]);
5541
5624
  /** Throttle — cooldown survives restarts (rebuilt from the outbox on boot). */
5542
5625
  var NcThrottleSchema = zod.z.object({
5543
5626
  cooldownSec: zod.z.number().int().min(0).max(86400).default(60),
5544
5627
  /** `rule` = one shared cooldown; `rule-device` = per-camera cooldown. */
5545
- scope: zod.z.enum(["rule", "rule-device"]).default("rule-device")
5628
+ scope: zod.z.enum(["rule", "rule-device"]).default("rule-device"),
5629
+ /**
5630
+ * Class granularity of the cooldown key. Optional rather than defaulted:
5631
+ * a Zod default does NOT run on the addon→addon cap path, so a persisted
5632
+ * rule authored before this field simply carries none — and the engine
5633
+ * reads absent as `shared`, the pre-existing behaviour.
5634
+ */
5635
+ granularity: NcThrottleGranularitySchema.optional()
5546
5636
  });
5547
5637
  /** Client-supplied rule fields (server stamps id/createdBy/createdAt/updatedAt). */
5548
5638
  var NcRuleInputSchema = zod.z.object({
@@ -5650,6 +5740,7 @@ var NcConditionDescriptorSchema = zod.z.object({
5650
5740
  "schedule",
5651
5741
  "plateMatcher",
5652
5742
  "packagePhase",
5743
+ "crossingSelect",
5653
5744
  "polygonDraw",
5654
5745
  "occupancy"
5655
5746
  ]),
@@ -10422,6 +10513,18 @@ var motionCapability = {
10422
10513
  name: "motion",
10423
10514
  scope: "device",
10424
10515
  mode: "singleton",
10516
+ /**
10517
+ * Providers register per-device natives via `ctx.registerNativeCap`
10518
+ * (Hikvision/Reolink/Amcrest/Wyze/HA/Homematic/Alexa/Matter) — there is
10519
+ * NO system singleton provider. Without this flag `resolveCapMount`
10520
+ * derived `{ kind: 'singleton' }`, so `motion.getStatus`/`isDetected`
10521
+ * resolved via `registry.getSingleton('motion')` (always null) and every
10522
+ * call 412'd "provider not available" while bindings listed a live
10523
+ * `motion` native (2026-08-02). The flag routes the router through
10524
+ * `requireDeviceScoped` → `getProviderForDevice`, like `motion-trigger`,
10525
+ * `snapshot` and every other per-device native cap.
10526
+ */
10527
+ deviceNative: true,
10425
10528
  deviceTypes: [DeviceType.Camera, DeviceType.Sensor],
10426
10529
  methods: {
10427
10530
  /**
@@ -15673,7 +15776,29 @@ var deviceManagerCapability = {
15673
15776
  getDeviceStatusAggregate: method(zod.z.object({
15674
15777
  deviceId: zod.z.number(),
15675
15778
  caps: zod.z.array(zod.z.string()).readonly().optional()
15676
- }), zod.z.record(zod.z.string(), zod.z.unknown().nullable()))
15779
+ }), zod.z.record(zod.z.string(), zod.z.unknown().nullable())),
15780
+ /**
15781
+ * The same aggregate, for a LIST of devices in one round-trip.
15782
+ *
15783
+ * The viewer's camera list is the caller this exists for. Each card asked
15784
+ * for its own badges — battery here, recording mode there — so a ten-camera
15785
+ * install issued N separate procedures per reconcile, each its own message
15786
+ * on the one WebSocket that also carries the WebRTC signalling (the client
15787
+ * has no `httpBatchLink`, so there is no transport-level batching to lean
15788
+ * on: collapsing has to happen at the procedure level, which is here).
15789
+ *
15790
+ * Keyed by device id as a STRING — a JSON object cannot key by number, and
15791
+ * pretending otherwise is how a record survives the wire only in tests.
15792
+ * The per-device value is exactly what `getDeviceStatusAggregate` returns,
15793
+ * so a caller can move between the two without reshaping anything.
15794
+ *
15795
+ * `caps` matters more here than in the single call: a client that needs two
15796
+ * caps should not pay for every status-carrying cap × every device.
15797
+ */
15798
+ getDeviceStatusAggregateBatch: method(zod.z.object({
15799
+ deviceIds: zod.z.array(zod.z.number()).readonly(),
15800
+ caps: zod.z.array(zod.z.string()).readonly().optional()
15801
+ }), zod.z.record(zod.z.string(), zod.z.record(zod.z.string(), zod.z.unknown().nullable())))
15677
15802
  }
15678
15803
  };
15679
15804
  /**
@@ -17399,6 +17524,37 @@ var MotionEventSchema = zod.z.object({
17399
17524
  * Absent on legacy rows ⇒ treat as `pipeline`.
17400
17525
  */
17401
17526
  var DetectionSourceSchema = zod.z.enum(["pipeline", "onboard"]);
17527
+ /**
17528
+ * Which WAY a subject crossed a zone boundary. An entry and an exit are two
17529
+ * different notifications ("the parcel arrived" / "the car left the drive"),
17530
+ * and until this existed they were indistinguishable on the persisted record —
17531
+ * which is why exits were computed and dropped at the emit boundary rather
17532
+ * than doubling every zone-scoped rule.
17533
+ */
17534
+ var ZoneCrossingDirectionSchema = zod.z.enum(["enter", "exit"]);
17535
+ /**
17536
+ * The confirmed zone crossing that produced an object event. Present ONLY on
17537
+ * an event emitted BY a crossing (`zone.enter` / `zone.exit`); a movement-state
17538
+ * event (`object.entering` / `leaving` / `stationary` / `loitering`) and an
17539
+ * appearance event carry none, so a rule asking for a direction fails closed
17540
+ * on them.
17541
+ *
17542
+ * Exactly ONE crossing per event: the emitter turns each confirmed crossing
17543
+ * into its own event, so a frame in which a track enters A while leaving B
17544
+ * produces two events with two directions — never one ambiguous row.
17545
+ *
17546
+ * `zoneId` is load-bearing for an EXIT: the event's `zones` list is the
17547
+ * membership the box has NOW, and by definition it no longer contains the zone
17548
+ * that was just left. Without the id here, a zone-scoped rule could never match
17549
+ * the exit it asked for.
17550
+ */
17551
+ var ZoneCrossingSchema = zod.z.object({
17552
+ direction: ZoneCrossingDirectionSchema,
17553
+ /** Admin zone id crossed. */
17554
+ zoneId: zod.z.string(),
17555
+ /** Zone display name at crossing time (falls back to the id). */
17556
+ zoneName: zod.z.string().optional()
17557
+ });
17402
17558
  var ObjectEventSchema = zod.z.object({
17403
17559
  ...BaseEventFields,
17404
17560
  kind: zod.z.literal("object"),
@@ -17425,6 +17581,12 @@ var ObjectEventSchema = zod.z.object({
17425
17581
  zones: zod.z.array(zod.z.string()).readonly().optional(),
17426
17582
  /** Omitted in slim projection. */
17427
17583
  state: TrackStateSchema.optional(),
17584
+ /**
17585
+ * The zone crossing this event IS, when it is one. Absent on every other
17586
+ * event kind (movement state, appearance, package) — see
17587
+ * {@link ZoneCrossingSchema}. Omitted in slim projection.
17588
+ */
17589
+ zoneCrossing: ZoneCrossingSchema.optional(),
17428
17590
  /** Detection-frame dimensions in pixels — let consumers normalize the
17429
17591
  * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
17430
17592
  frameWidth: zod.z.number().optional(),
@@ -24958,6 +25120,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
24958
25120
  addonId: null,
24959
25121
  access: "view"
24960
25122
  },
25123
+ "deviceManager.getDeviceStatusAggregateBatch": {
25124
+ capName: "device-manager",
25125
+ capScope: "system",
25126
+ addonId: null,
25127
+ access: "view"
25128
+ },
24961
25129
  "deviceManager.getLinkedDevices": {
24962
25130
  capName: "device-manager",
24963
25131
  capScope: "system",
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("./chunk-Cek0wNdY.js");
3
- const require_dist = require("./dist-CS0pdC8l.js");
3
+ const require_dist = require("./dist-BxtIzexq.js");
4
4
  const require_model_download_service = require("./model-download-service-hf0ookyy.js");
5
- const require_manifest_python_deps = require("./manifest-python-deps-COgal7Jy.js");
5
+ const require_manifest_python_deps = require("./manifest-python-deps-KZ00cbfP.js");
6
6
  const require_resource_monitor = require("./resource-monitor-DNNomR-i.js");
7
7
  const require_builtins_sqlite_storage_filesystem_storage_addon = require("./builtins/sqlite-storage/filesystem-storage.addon.js");
8
8
  const require_builtins_sqlite_storage_sqlite_settings_addon = require("./builtins/sqlite-storage/sqlite-settings.addon.js");
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { a as __toCommonJS, i as __require, n as __esmMin, o as __toESM$1, r as __exportAll, t as __commonJSMin$1 } from "./chunk-CNf5ZN-e.mjs";
2
- import { B as logLevelAtMost, Dt as readinessKey, Et as parseJsonUnknown$1, F as isVoidInput, I as kebabToCamel, L as lifecycleJobSchema, M as isArrayOutputSchema, N as isCollectionArrayMethod, Ot as resolveCapMount, P as isObjectInput, Tt as parseJsonObject, V as looseSchema, W as objectInputDeclaresAddonId, _t as asString$1, c as RUNTIME_DEFAULTS, ct as DEVICE_STATUS_METHOD, ft as ReadinessRegistry, gt as asNumber, ht as asJsonObject$1, it as errMsg$1, jt as EventCategory$1, k as extractNestedAddonId, kt as scopeKey, m as addonSettingsCapability, ot as DATAPLANE_SECRET_HEADER$1, pt as ReadinessTimeoutError, q as procedureAuthKey, s as METHOD_ACCESS_MAP, st as DEVICE_SETTINGS_CONTRIBUTION_METHODS, t as ALL_CAPABILITY_DEFINITIONS, vt as createEvent, xt as expandCapMethods, yt as emitDownForOwnedCaps } from "./dist-CIek6F9X.mjs";
2
+ import { B as logLevelAtMost, Dt as readinessKey, Et as parseJsonUnknown$1, F as isVoidInput, I as kebabToCamel, L as lifecycleJobSchema, M as isArrayOutputSchema, N as isCollectionArrayMethod, Ot as resolveCapMount, P as isObjectInput, Tt as parseJsonObject, V as looseSchema, W as objectInputDeclaresAddonId, _t as asString$1, c as RUNTIME_DEFAULTS, ct as DEVICE_STATUS_METHOD, ft as ReadinessRegistry, gt as asNumber, ht as asJsonObject$1, it as errMsg$1, jt as EventCategory$1, k as extractNestedAddonId, kt as scopeKey, m as addonSettingsCapability, ot as DATAPLANE_SECRET_HEADER$1, pt as ReadinessTimeoutError, q as procedureAuthKey, s as METHOD_ACCESS_MAP, st as DEVICE_SETTINGS_CONTRIBUTION_METHODS, t as ALL_CAPABILITY_DEFINITIONS, vt as createEvent, xt as expandCapMethods, yt as emitDownForOwnedCaps } from "./dist-BXmFvn3h.mjs";
3
3
  import { a as downloadModel, c as getModelFilePath, d as contentTypeFor, f as createAuthenticatedFileServer, h as resolveFilePath, i as downloadFile, l as isModelDownloaded, m as parseTokenizedUrl, n as collectModelFiles, o as ensureModel, p as parseRangeHeader, r as deleteModelFromDisk, s as fetchJson, t as ModelDownloadService, u as createFileDataPlaneHandler } from "./model-download-service-Cp9f4dk6.mjs";
4
- import { $ as buildNativeCapProxy, A as createHubCapForwardService, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, Ct as resolveAddonClass, D as localProviderLink, E as ipcParentLink, F as createUdsLogger, G as callWithServiceDiscovery, H as CapRouteResolver, I as createUdsLoggerWithControl, J as UdsLocalTransportServer, K as createLocalTransport, L as LocalChildClient, M as createUdsEventBridge, N as createUdsEventBus, O as HUB_CAP_FWD_ACTION, P as udsChildLogToWorkerEntry, Q as encodeFrame, R as LocalChildRegistry, S as brokerCallForCap, St as runNpm, T as ipcChildLink, U as CapRouteError, V as AGENT_CAP_FWD_SERVICE, W as classifyCapRoute, X as localEndpointPath, Y as SocketChannel, Z as FrameDecoder, _ as createKernelHwAccel, _t as CapabilityHandle, a as getWorkerDeviceRegistry, b as __resetCapUsageRegistryForTests, bt as installManifestNativeDeps, c as setHubConnected, ct as NATIVE_PROVIDER_SERVICE_INFIX, d as getBrokerEventBus, dt as capBareAction, et as buildUdsNativeCapProxy, f as getMoleculerEventStats, ft as capServiceName, g as AddonDepsManager, gt as DeviceRegistry, h as subscribePassthrough, ht as serializeTypedArrays, i as createUdsAddonContext, it as mountNativeCapService, j as createParentUnownedCallHandler, k as HUB_CAP_FWD_SERVICE, l as EVENT_TOPIC_PREFIX, lt as capActionName, m as setNodeEventInterest, mt as deserializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as createAddonService, p as registerEventBusService, pt as parseCapAction, q as UdsLocalTransportClient, r as createAddonContext, s as getOrInitReadinessRegistryForClient, st as validateProviderRegistrations, t as installManifestPythonDeps, tt as createBrokerDeviceManagerApi, u as clusterEventTopic, ut as capActionSuffix, v as resolveHwAccel, vt as CapabilityUnavailableError, w as buildLinkChain, wt as createAddonDataPlaneFacility, x as getCapUsageRegistry, xt as resolveNpmInvocation, y as CapUsageRegistry, yt as copyBundledNativeModules, z as UDS_NO_ROUTE_PREFIX } from "./manifest-python-deps-julrfvyf.mjs";
4
+ import { $ as buildNativeCapProxy, A as createHubCapForwardService, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, Ct as resolveAddonClass, D as localProviderLink, E as ipcParentLink, F as createUdsLogger, G as callWithServiceDiscovery, H as CapRouteResolver, I as createUdsLoggerWithControl, J as UdsLocalTransportServer, K as createLocalTransport, L as LocalChildClient, M as createUdsEventBridge, N as createUdsEventBus, O as HUB_CAP_FWD_ACTION, P as udsChildLogToWorkerEntry, Q as encodeFrame, R as LocalChildRegistry, S as brokerCallForCap, St as runNpm, T as ipcChildLink, U as CapRouteError, V as AGENT_CAP_FWD_SERVICE, W as classifyCapRoute, X as localEndpointPath, Y as SocketChannel, Z as FrameDecoder, _ as createKernelHwAccel, _t as CapabilityHandle, a as getWorkerDeviceRegistry, b as __resetCapUsageRegistryForTests, bt as installManifestNativeDeps, c as setHubConnected, ct as NATIVE_PROVIDER_SERVICE_INFIX, d as getBrokerEventBus, dt as capBareAction, et as buildUdsNativeCapProxy, f as getMoleculerEventStats, ft as capServiceName, g as AddonDepsManager, gt as DeviceRegistry, h as subscribePassthrough, ht as serializeTypedArrays, i as createUdsAddonContext, it as mountNativeCapService, j as createParentUnownedCallHandler, k as HUB_CAP_FWD_SERVICE, l as EVENT_TOPIC_PREFIX, lt as capActionName, m as setNodeEventInterest, mt as deserializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as createAddonService, p as registerEventBusService, pt as parseCapAction, q as UdsLocalTransportClient, r as createAddonContext, s as getOrInitReadinessRegistryForClient, st as validateProviderRegistrations, t as installManifestPythonDeps, tt as createBrokerDeviceManagerApi, u as clusterEventTopic, ut as capActionSuffix, v as resolveHwAccel, vt as CapabilityUnavailableError, w as buildLinkChain, wt as createAddonDataPlaneFacility, x as getCapUsageRegistry, xt as resolveNpmInvocation, y as CapUsageRegistry, yt as copyBundledNativeModules, z as UDS_NO_ROUTE_PREFIX } from "./manifest-python-deps-BNOApgK0.mjs";
5
5
  import { n as getSinglePidStats, t as getPidStats } from "./resource-monitor-BkP504Vq.mjs";
6
6
  import { FilesystemStorageAddon, t as FilesystemStorageProvider } from "./builtins/sqlite-storage/filesystem-storage.addon.mjs";
7
7
  import { SqliteSettingsAddon, t as SqliteSettingsBackend } from "./builtins/sqlite-storage/sqlite-settings.addon.mjs";
@@ -1,6 +1,6 @@
1
- import { ServiceBroker } from 'moleculer';
2
1
  import { IEventBus, IScopedLogger } from '@camstack/types';
3
2
  import { ReadinessRegistry } from '@camstack/types/addon';
3
+ import { ServiceBroker } from 'moleculer';
4
4
  /**
5
5
  * Minimal interface for the `LocalChildClient` surface used in UDS-mode
6
6
  * readiness hydration. Declared here (rather than importing the concrete
@@ -63,5 +63,5 @@ declare function getOrInitReadinessRegistry(broker: ServiceBroker, eventBus: IEv
63
63
  * Keep `$readiness.getSnapshot` (broker path) intact — Phase F removes it.
64
64
  */
65
65
  declare function getOrInitReadinessRegistryForClient(client: UdsReadinessClient, eventBus: IEventBus, logger: IScopedLogger, nodeId: string): ReadinessRegistry;
66
- export { getOrInitReadinessRegistry, getOrInitReadinessRegistryForClient };
67
66
  export type { UdsReadinessClient };
67
+ export { getOrInitReadinessRegistry, getOrInitReadinessRegistryForClient };
@@ -6813,8 +6813,8 @@ function getOrInitReadinessRegistry(broker, eventBus, logger) {
6813
6813
  try {
6814
6814
  const snapshot = await bkr.call("$readiness.getSnapshot", {});
6815
6815
  if (Array.isArray(snapshot) && snapshot.length > 0) {
6816
- registry.hydrate(snapshot);
6817
- logger.debug?.(`readiness: hydrated ${snapshot.length} record(s) from hub`);
6816
+ const applied = registry.hydrate(snapshot);
6817
+ if (applied > 0) logger.debug?.(`readiness: hydrated ${applied}/${snapshot.length} record(s) from hub`);
6818
6818
  }
6819
6819
  } catch (err) {
6820
6820
  const msg = err instanceof Error ? err.message : String(err);
@@ -6887,8 +6887,8 @@ function getOrInitReadinessRegistryForClient(client, eventBus, logger, nodeId) {
6887
6887
  try {
6888
6888
  const snapshot = await client.requestReadinessSnapshot();
6889
6889
  if (Array.isArray(snapshot) && snapshot.length > 0) {
6890
- registry.hydrate(snapshot);
6891
- logger.debug?.(`readiness: hydrated ${snapshot.length} record(s) from parent over UDS`);
6890
+ const applied = registry.hydrate(snapshot);
6891
+ if (applied > 0) logger.debug?.(`readiness: hydrated ${applied}/${snapshot.length} record(s) from parent over UDS`);
6892
6892
  }
6893
6893
  } catch (err) {
6894
6894
  const msg = err instanceof Error ? err.message : String(err);
@@ -6815,8 +6815,8 @@ function getOrInitReadinessRegistry(broker, eventBus, logger) {
6815
6815
  try {
6816
6816
  const snapshot = await bkr.call("$readiness.getSnapshot", {});
6817
6817
  if (Array.isArray(snapshot) && snapshot.length > 0) {
6818
- registry.hydrate(snapshot);
6819
- logger.debug?.(`readiness: hydrated ${snapshot.length} record(s) from hub`);
6818
+ const applied = registry.hydrate(snapshot);
6819
+ if (applied > 0) logger.debug?.(`readiness: hydrated ${applied}/${snapshot.length} record(s) from hub`);
6820
6820
  }
6821
6821
  } catch (err) {
6822
6822
  const msg = err instanceof Error ? err.message : String(err);
@@ -6889,8 +6889,8 @@ function getOrInitReadinessRegistryForClient(client, eventBus, logger, nodeId) {
6889
6889
  try {
6890
6890
  const snapshot = await client.requestReadinessSnapshot();
6891
6891
  if (Array.isArray(snapshot) && snapshot.length > 0) {
6892
- registry.hydrate(snapshot);
6893
- logger.debug?.(`readiness: hydrated ${snapshot.length} record(s) from parent over UDS`);
6892
+ const applied = registry.hydrate(snapshot);
6893
+ if (applied > 0) logger.debug?.(`readiness: hydrated ${applied}/${snapshot.length} record(s) from parent over UDS`);
6894
6894
  }
6895
6895
  } catch (err) {
6896
6896
  const msg = err instanceof Error ? err.message : String(err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/system",
3
- "version": "1.2.25",
3
+ "version": "1.2.27",
4
4
  "description": "Core addon for CamStack — builtins, pipeline, process management, auth, logging, events",
5
5
  "keywords": [
6
6
  "camstack",
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "camstack": {
34
34
  "system": true,
35
- "displayName": "CamStack Core",
35
+ "displayName": "Core",
36
36
  "addons": [
37
37
  {
38
38
  "id": "filesystem-storage",