@camstack/system 1.2.13 → 1.2.14

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 (58) 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 +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-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.d.ts +4 -0
  24. package/dist/builtins/loki-logging/index.js +445 -0
  25. package/dist/builtins/loki-logging/index.mjs +431 -0
  26. package/dist/builtins/loki-logging/loki-destination.d.ts +88 -0
  27. package/dist/builtins/loki-logging/loki-logging.addon.d.ts +36 -0
  28. package/dist/builtins/loki-logging/loki-payload.d.ts +46 -0
  29. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  30. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  31. package/dist/builtins/platform-probe/index.js +1 -1
  32. package/dist/builtins/platform-probe/index.mjs +1 -1
  33. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  34. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  35. package/dist/builtins/snapshot/index.js +1 -1
  36. package/dist/builtins/snapshot/index.mjs +1 -1
  37. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  38. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  39. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +1 -1
  40. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
  41. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  42. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  43. package/dist/builtins/system-config/system-config.addon.js +1 -1
  44. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  45. package/dist/builtins/winston-logging/index.js +1 -1
  46. package/dist/builtins/winston-logging/index.mjs +1 -1
  47. package/dist/{dist-DpKeSZel.mjs → dist-eOaraBhT.mjs} +21 -1
  48. package/dist/{dist-BQOCTD5b.js → dist-yttC1aUI.js} +26 -0
  49. package/dist/index.d.ts +2 -0
  50. package/dist/index.js +128 -15
  51. package/dist/index.mjs +127 -16
  52. package/dist/kernel/config-schema.d.ts +38 -1
  53. package/dist/logging/log-manager.d.ts +14 -4
  54. package/dist/logging/log-ring-buffer.d.ts +15 -0
  55. package/dist/logging/partitioned-log-buffer.d.ts +49 -6
  56. package/dist/{manifest-python-deps-1I7WdPDD.js → manifest-python-deps-DSrmih6E.js} +1 -1
  57. package/dist/{manifest-python-deps-I2MGvDvo.mjs → manifest-python-deps-bn3qrsxR.mjs} +1 -1
  58. package/package.json +13 -1
@@ -5,8 +5,8 @@ export declare const DEFAULT_DATA_PATH = "camstack-data";
5
5
  * All other settings live in SQL (system_settings table). */
6
6
  export declare const bootstrapSchema: z.ZodObject<{
7
7
  mode: z.ZodDefault<z.ZodEnum<{
8
- agent: "agent";
9
8
  hub: "hub";
9
+ agent: "agent";
10
10
  }>>;
11
11
  server: z.ZodDefault<z.ZodObject<{
12
12
  port: z.ZodDefault<z.ZodNumber>;
@@ -70,7 +70,44 @@ export type AppConfig = BootstrapConfig & {
70
70
  retentionDays: number;
71
71
  };
72
72
  eventBus: {
73
+ /** System-EVENT ring size. Separate from the log buffer below. */
73
74
  ringBufferSize: number;
75
+ /**
76
+ * Per-addon log ring-buffer capacity, in entries. Default 5000.
77
+ *
78
+ * A HARD bound on EACH addon's bucket, not the total — the buffer is
79
+ * partitioned by `addonId` (`PartitionedLogBuffer`), so a chatty addon evicts
80
+ * only its own lines and a quiet one keeps its sparse history. Worst-case
81
+ * memory is therefore `this × number of addons that have logged`, which is
82
+ * why the total below exists.
83
+ *
84
+ * Hub-only: the agent constructs its `LogManager` with a hardcoded 5000.
85
+ */
86
+ perAddonLogBufferSize?: number;
87
+ /**
88
+ * SOFT ceiling on retained entries across every addon bucket. `null` (the
89
+ * default) means the per-addon rings are the only bound — the historical
90
+ * behaviour, unchanged unless you set this.
91
+ *
92
+ * Set it when `perAddonLogBufferSize × addons` is more than the box can spare:
93
+ * 70 addons × 5000 is 350k retained entries, which is a lot on a mini PC.
94
+ *
95
+ * Soft, and deliberately so: it is met by discarding only entries at or below
96
+ * `logBufferPruneLevel`. If pruning every prunable entry still leaves the total
97
+ * above this number, the buffer stays above it. A cap that could evict an
98
+ * `error` would trade the lines that explain an incident for lines about frame
99
+ * timings.
100
+ */
101
+ maxTotalLogBufferSize?: number | null;
102
+ /**
103
+ * Highest severity the total-cap sweep may discard. Default `debug`.
104
+ *
105
+ * `debug` reclaims nearly all the space on any busy buffer while leaving
106
+ * `info`/`warn`/`error` untouched. Raise it to `info` only if debug alone is
107
+ * not enough; `warn` and `error` are then still safe. Setting it to `error`
108
+ * means "evict anything", which defeats the point.
109
+ */
110
+ logBufferPruneLevel?: 'debug' | 'info' | 'warn' | 'error';
74
111
  };
75
112
  retention: {
76
113
  detectionEventsDays: number;
@@ -1,4 +1,5 @@
1
1
  import { LogEntry, LogFilter } from './log-ring-buffer.js';
2
+ import { PartitionedLogBufferOptions } from './partitioned-log-buffer.js';
2
3
  import { IScopedLogger } from './scoped-logger.js';
3
4
  export interface ILogDestination {
4
5
  initialize(): Promise<void>;
@@ -11,10 +12,19 @@ export declare class LogManager {
11
12
  private readonly destinations;
12
13
  private readonly subscribers;
13
14
  private deviceNameLookup;
14
- /** `perAddonCapacity` bounds EACH addon's bucket, not the total — a chatty
15
- * addon evicts only its own lines, so quiet addons keep their sparse history.
16
- * See {@link PartitionedLogBuffer}. */
17
- constructor(perAddonCapacity?: number);
15
+ /**
16
+ * `perAddonCapacity` bounds EACH addon's bucket, not the total a chatty addon
17
+ * evicts only its own lines, so quiet addons keep their sparse history.
18
+ *
19
+ * `options.maxTotalEntries` adds a soft ceiling across all buckets, because the
20
+ * per-bucket bound scales with addon count. When it is exceeded, only entries at
21
+ * or below `options.pruneLevel` (default `debug`) are discarded — `warn` and
22
+ * `error` are never evicted to satisfy it. Both default to the previous
23
+ * behaviour: no total cap. See {@link PartitionedLogBuffer}.
24
+ */
25
+ constructor(perAddonCapacity?: number, options?: PartitionedLogBufferOptions);
26
+ /** Entries currently retained across every bucket. */
27
+ bufferSize(): number;
18
28
  /**
19
29
  * Register a callback that resolves `deviceId → deviceName`. Called
20
30
  * for every emitted entry that carries `tags.deviceId` but no
@@ -44,4 +44,19 @@ export declare class LogRingBuffer {
44
44
  * gets removed.
45
45
  */
46
46
  clear(filter?: LogFilter): number;
47
+ /** Entries currently retained. */
48
+ size(): number;
49
+ /**
50
+ * Drop up to `max` entries, OLDEST first, whose level is at or below
51
+ * `threshold`. Returns how many were removed.
52
+ *
53
+ * This is what lets a total-memory cap be enforced without losing the entries
54
+ * that matter: sweeping `debug` reclaims almost all of the space (debug is the
55
+ * overwhelming majority of any busy buffer) while `warn` and `error` are never
56
+ * touched. Losing the one error line that explains an incident, in order to keep
57
+ * a thousand debug lines about frame timings, would be the wrong trade.
58
+ *
59
+ * Oldest-first because recent context is what an operator is looking at.
60
+ */
61
+ pruneOldestAtOrBelow(threshold: LogLevel, max: number): number;
47
62
  }
@@ -1,4 +1,15 @@
1
- import { LogEntry, LogFilter } from '@camstack/types';
1
+ import { LogEntry, LogFilter, LogLevel } from '@camstack/types';
2
+ export interface PartitionedLogBufferOptions {
3
+ /** Hard ring size for EACH addon's bucket. */
4
+ readonly perAddonCapacity?: number;
5
+ /**
6
+ * Soft ceiling on entries across all buckets. `null` (the default) means the
7
+ * only bound is the per-addon rings.
8
+ */
9
+ readonly maxTotalEntries?: number | null;
10
+ /** Highest severity the total-cap sweep may discard. Default `debug`. */
11
+ readonly pruneLevel?: LogLevel;
12
+ }
2
13
  /**
3
14
  * A log buffer partitioned into one fixed-capacity {@link LogRingBuffer} per
4
15
  * `addonId`. A chatty addon (e.g. detection-pipeline) only ever evicts its OWN
@@ -6,16 +17,48 @@ import { LogEntry, LogFilter } from '@camstack/types';
6
17
  * survive far longer than they would in a single shared FIFO ring.
7
18
  *
8
19
  * Drop-in for {@link LogRingBuffer} from {@link LogManager}'s perspective —
9
- * exposes the same `push` / `getAll` / `query` / `clear` surface. Total memory
10
- * is bounded by `perAddonCapacity × (number of distinct addons seen)`; quiet
11
- * buckets never grow past the handful of lines they actually emit.
20
+ * exposes the same `push` / `getAll` / `query` / `clear` surface.
21
+ *
22
+ * ## Two bounds, doing different jobs
23
+ *
24
+ * **`perAddonCapacity`** is a HARD bound: each bucket is a fixed-size ring, so
25
+ * total memory can never exceed `perAddonCapacity × buckets` whatever else
26
+ * happens. This is the safety net.
27
+ *
28
+ * **`maxTotalEntries`** is a SOFT target across all buckets, and it exists because
29
+ * the hard bound scales with addon count — 70 addons × 5000 is a lot of retained
30
+ * entries on a small box. When the total is exceeded, the sweep discards only
31
+ * entries at or below `pruneLevel` (default `debug`), oldest first, from the
32
+ * largest buckets first.
33
+ *
34
+ * It is deliberately soft. Meeting a total cap by discarding `warn` and `error`
35
+ * would trade the entries that explain an incident for entries about frame
36
+ * timings; if pruning every prunable entry still leaves the total above the cap,
37
+ * the buffer stays above it and the hard per-bucket bound is what holds. A cap
38
+ * that can evict an error is not a feature.
39
+ *
40
+ * Largest-bucket-first because the noisy addon is the one creating the pressure —
41
+ * the same principle that motivated partitioning in the first place.
12
42
  */
13
43
  export declare class PartitionedLogBuffer {
14
- private readonly perAddonCapacity;
15
44
  private readonly buffers;
16
- constructor(perAddonCapacity?: number);
45
+ private readonly perAddonCapacity;
46
+ private readonly maxTotalEntries;
47
+ private readonly pruneLevel;
48
+ /** Running total, so `push` does not walk every bucket to decide. */
49
+ private totalEntries;
50
+ constructor(perAddonCapacity?: number, options?: PartitionedLogBufferOptions);
17
51
  private bufferFor;
18
52
  push(entry: LogEntry): void;
53
+ /**
54
+ * Bring the total down to `PRUNE_TARGET_RATIO × cap` by discarding prunable
55
+ * entries, largest bucket first. Stops early when nothing prunable is left —
56
+ * see the class docblock on why that is the correct outcome rather than a
57
+ * failure to enforce.
58
+ */
59
+ private enforceTotalCap;
60
+ /** Entries retained across every bucket. */
61
+ size(): number;
19
62
  /** Every retained entry across all buckets, newest-first (mirrors
20
63
  * {@link LogRingBuffer.getAll}). Used to replay history to a destination. */
21
64
  getAll(): LogEntry[];
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- const require_dist = require("./dist-BQOCTD5b.js");
2
+ const require_dist = require("./dist-yttC1aUI.js");
3
3
  let _camstack_types_node = require("@camstack/types/node");
4
4
  let node_http = require("node:http");
5
5
  let node_fs = require("node:fs");
@@ -1,4 +1,4 @@
1
- import { J as readNodePin } from "./dist-DpKeSZel.mjs";
1
+ import { Y as readNodePin } from "./dist-eOaraBhT.mjs";
2
2
  import { ensureBinary, ensureFfmpeg, ensurePython, installPythonPackages, installPythonRequirements } from "@camstack/types/node";
3
3
  import { createServer } from "node:http";
4
4
  import * as fs from "node:fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/system",
3
- "version": "1.2.13",
3
+ "version": "1.2.14",
4
4
  "description": "Core addon for CamStack — builtins, pipeline, process management, auth, logging, events",
5
5
  "keywords": [
6
6
  "camstack",
@@ -81,6 +81,18 @@
81
81
  }
82
82
  ]
83
83
  },
84
+ {
85
+ "id": "loki-logging",
86
+ "category": "system",
87
+ "name": "Loki Logging",
88
+ "description": "Opt-in log destination — batches this node's log entries and pushes them to a Grafana Loki instance via /loki/api/v1/push. Registered always, inert until enabled with a URL.",
89
+ "entry": "./dist/builtins/loki-logging/index.js",
90
+ "capabilities": [
91
+ {
92
+ "name": "log-destination"
93
+ }
94
+ ]
95
+ },
84
96
  {
85
97
  "id": "hub-forwarder",
86
98
  "category": "system",