@camstack/addon-notifiers 1.2.38 → 1.2.40

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 (3) hide show
  1. package/dist/addon.js +255 -25
  2. package/dist/addon.mjs +255 -25
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -29,7 +29,7 @@ let node_fs = require("node:fs");
29
29
  let node_path = require("node:path");
30
30
  node_path = __toESM(node_path);
31
31
  let node_url = require("node:url");
32
- //#region ../types/dist/event-category-CIa_iT6b.mjs
32
+ //#region ../types/dist/event-category-BZL-fdNj.mjs
33
33
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
34
34
  EventCategory["SystemBoot"] = "system.boot";
35
35
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -466,7 +466,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
466
466
  EventCategory["MetricsNodeResourcesSnapshot"] = "metrics.node-resources-snapshot";
467
467
  /**
468
468
  * Periodic per-node process-tree snapshot (camstack-related pids
469
- * with ghost / managed / root classification). Emitted ~0.2 Hz by
469
+ * with root / managed / system classification). Emitted ~0.2 Hz by
470
470
  * the metrics-provider addon. Drives the Cluster → Processes tab
471
471
  * without polling `metricsProvider.listNodeProcesses`.
472
472
  */
@@ -11300,6 +11300,19 @@ var SettingsRecordSchema = object({
11300
11300
  data: record(string(), unknown())
11301
11301
  });
11302
11302
  /**
11303
+ * One record of a BULK insert — {@link SettingsRecordSchema} with the id made
11304
+ * optional.
11305
+ *
11306
+ * A separate schema rather than loosening the shared one: every other method
11307
+ * on this cap addresses a row BY its id, and making that field optional
11308
+ * everywhere would turn a forgotten key into a silently generated one on
11309
+ * `update` and `delete` as well.
11310
+ */
11311
+ var BulkRecordSchema = object({
11312
+ id: string().optional(),
11313
+ data: record(string(), unknown())
11314
+ });
11315
+ /**
11303
11316
  * Column declaration for a structured (SQL-backed) collection.
11304
11317
  *
11305
11318
  * Logical types — the backend translates each to the matching SQLite
@@ -11356,6 +11369,10 @@ method(object({
11356
11369
  collection: string(),
11357
11370
  record: SettingsRecordSchema
11358
11371
  }), _void(), { kind: "mutation" }), method(object({
11372
+ namespace: string().optional(),
11373
+ collection: string(),
11374
+ records: array(BulkRecordSchema).readonly()
11375
+ }), object({ inserted: number().int() }), { kind: "mutation" }), method(object({
11359
11376
  namespace: string().optional(),
11360
11377
  collection: string(),
11361
11378
  id: string(),
@@ -11464,6 +11481,13 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
11464
11481
  }), _void(), {
11465
11482
  kind: "mutation",
11466
11483
  auth: "admin"
11484
+ }), method(object({
11485
+ namespace: string().optional(),
11486
+ collection: string(),
11487
+ records: array(BulkRecordSchema).readonly()
11488
+ }), object({ inserted: number().int() }), {
11489
+ kind: "mutation",
11490
+ auth: "admin"
11467
11491
  }), method(object({
11468
11492
  namespace: string().optional(),
11469
11493
  collection: string(),
@@ -13232,6 +13256,68 @@ method(LogEntrySchema, _void(), { kind: "mutation" }), method(object({
13232
13256
  limit: number().optional(),
13233
13257
  tags: record(string(), string()).optional()
13234
13258
  }), array(LogEntrySchema).readonly());
13259
+ var LoadContributionSchema = object({
13260
+ role: _enum([
13261
+ "decode",
13262
+ "transcode",
13263
+ "recording",
13264
+ "streaming",
13265
+ "detection"
13266
+ ]),
13267
+ /**
13268
+ * The NUMERIC device id — the same value every log line carries as
13269
+ * `tags.deviceId`. `null` means this cost genuinely belongs to no single
13270
+ * camera (a shared pool), NOT that the contributor forgot to look it up: a
13271
+ * contributor that cannot name its camera must not emit the entry at all,
13272
+ * because an unnamed per-camera entry is indistinguishable from a shared one
13273
+ * and would quietly turn one camera's cost into everybody's.
13274
+ */
13275
+ deviceId: number().int().positive().nullable(),
13276
+ attribution: _enum([
13277
+ "measured",
13278
+ "accounted",
13279
+ "unattributable"
13280
+ ]),
13281
+ /**
13282
+ * What ONE entry is, in the contributor's own words — `615/high`,
13283
+ * `617/native`, `cuda:0 shared pool`. Free text because the unit differs per
13284
+ * family and inventing a common one would lose the only information that
13285
+ * makes two entries for the same camera distinguishable.
13286
+ */
13287
+ unit: string(),
13288
+ /**
13289
+ * The OS process this cost lives in, when there is one. Present so a
13290
+ * consumer can (a) tell two generations of the same unit apart across a
13291
+ * restart, and (b) subtract claimed processes from the node's process
13292
+ * snapshot to see what NOBODY claimed. Absent for an entry that owns no
13293
+ * process of its own.
13294
+ */
13295
+ pid: number().int().positive().optional(),
13296
+ /**
13297
+ * When this generation started. The pid's incarnation marker: a consumer
13298
+ * differencing {@link LoadContributionSchema.shape.cpuSeconds} must drop the
13299
+ * window when this changes, because the counter restarted from zero in a new
13300
+ * process.
13301
+ */
13302
+ startedAtMs: number().optional(),
13303
+ /**
13304
+ * CUMULATIVE CPU seconds this unit has consumed since it started — user +
13305
+ * system, read from the child's own `/proc/<pid>/stat` at the moment the
13306
+ * contribution is asked for.
13307
+ *
13308
+ * Cumulative and not a rate on purpose: a rate needs a window, a window
13309
+ * needs a sampler, and a new per-node sampler is the defect half of
13310
+ * `docs/architecture/load-ledger.md` documents. A counter can be differenced
13311
+ * by whoever already keeps a history; a rate cannot be un-averaged.
13312
+ *
13313
+ * Absent — never zero — on a node with no `/proc`, on a read failure, and on
13314
+ * an entry with no process.
13315
+ */
13316
+ cpuSeconds: number().optional(),
13317
+ /** Resident bytes of this unit's process, same source and same rules. */
13318
+ rssBytes: number().optional()
13319
+ });
13320
+ method(_void(), array(LoadContributionSchema).readonly());
13235
13321
  /**
13236
13322
  * `login-method` — collection cap through which auth addons contribute
13237
13323
  * their pre-auth login surfaces to the login page. This is the SINGLE,
@@ -13435,8 +13521,7 @@ var NodeProcessSchema = object({
13435
13521
  classification: _enum([
13436
13522
  "root",
13437
13523
  "managed",
13438
- "system",
13439
- "ghost"
13524
+ "system"
13440
13525
  ]),
13441
13526
  /** `$process` addon binding when `managed`, else null. */
13442
13527
  addonId: string().nullable(),
@@ -13444,22 +13529,39 @@ var NodeProcessSchema = object({
13444
13529
  nodeId: string().nullable(),
13445
13530
  /** Truncated command line. */
13446
13531
  command: string(),
13532
+ /**
13533
+ * `ps pcpu` — CPU averaged over the process's WHOLE LIFETIME, not a rate.
13534
+ * On a runner up for days it barely moves. Fine as a column, useless as a
13535
+ * series: use `cpuMainPercent + cpuGcPercent` for anything time-varying.
13536
+ */
13447
13537
  cpuPercent: number(),
13448
13538
  memoryRssBytes: number(),
13539
+ /**
13540
+ * Instantaneous CPU% of the process's own threads over the last
13541
+ * process-snapshot window, from a `/proc/<pid>/task/*` tick delta.
13542
+ *
13543
+ * `null` = UNKNOWN, never zero: no previous sample yet (first tick after
13544
+ * boot), the pid was recycled, or this node is not Linux.
13545
+ */
13546
+ cpuMainPercent: number().nullable(),
13547
+ /**
13548
+ * Instantaneous CPU% of V8's `V8Worker` platform pool over the same window.
13549
+ *
13550
+ * This is the number that rewrote the 2026-08-27 diagnosis — hub-main 73%,
13551
+ * `stream-broker` 61% (`docs/architecture/load-ledger.md`). A CPU chart that
13552
+ * does not separate it from `cpuMainPercent` shows "busy" where the truth is
13553
+ * "allocating too much".
13554
+ *
13555
+ * Concurrent GC is the dominant tenant of that pool but not the only one
13556
+ * (background compilation runs there too), so it is reported as
13557
+ * "GC / V8 helpers" rather than as pure collection time. `null` has the same
13558
+ * meaning as on `cpuMainPercent`.
13559
+ */
13560
+ cpuGcPercent: number().nullable(),
13561
+ /** Threads seen in the tick scan. `null` under the same conditions. */
13562
+ threadCount: number().nullable(),
13449
13563
  /** Wall-clock uptime (seconds). Parsed from `ps etime`. */
13450
- uptimeSec: number(),
13451
- /** True when ancestor walk reaches `ppid=1` (reparented to init/launchd). */
13452
- orphaned: boolean()
13453
- });
13454
- var KillProcessInputSchema = object({
13455
- pid: number(),
13456
- /** Force = SIGKILL. Default is SIGTERM. */
13457
- force: boolean().optional()
13458
- });
13459
- var KillProcessResultSchema = object({
13460
- success: boolean(),
13461
- reason: string().optional(),
13462
- signal: _enum(["SIGTERM", "SIGKILL"]).optional()
13564
+ uptimeSec: number()
13463
13565
  });
13464
13566
  var DumpHeapSnapshotInputSchema = object({
13465
13567
  /** The addon whose runner should dump a heap snapshot. */
@@ -13472,6 +13574,104 @@ var DumpHeapSnapshotResultSchema = object({
13472
13574
  pid: number().optional(),
13473
13575
  reason: string().optional()
13474
13576
  });
13577
+ /**
13578
+ * One point of one function's series.
13579
+ *
13580
+ * The unsuffixed fields are the bucket's **MAXIMUM**, and that choice is the
13581
+ * point of the whole surface. The two obvious reductions both lie: a mean per
13582
+ * bucket smears a spike away, and taking every Nth sample skips it outright.
13583
+ * Either would give us a tool built to find peaks that does not show peaks.
13584
+ * `...Min` carries the other end, `samples` says how many raw snapshots folded
13585
+ * into the bucket, and a mean stays derivable where it is wanted.
13586
+ *
13587
+ * An UNREDUCED point is a one-sample bucket: `samples === 1` and each `...Min`
13588
+ * equals its unsuffixed twin. Reduced and unreduced are the same shape, so a
13589
+ * caller cannot tell which it received — which is what "one reader" means.
13590
+ */
13591
+ var LoadPointSchema = object({
13592
+ /** Bucket START, or the snapshot's own timestamp when unreduced. */
13593
+ atMs: number(),
13594
+ /** Raw snapshots in this bucket. Never 0 — AN EMPTY BUCKET IS ABSENT. */
13595
+ samples: number().int(),
13596
+ /**
13597
+ * `null` = UNKNOWN and it PROPAGATES: a bucket is null unless every process
13598
+ * of every snapshot in it reported a thread split. A partial sum is a
13599
+ * smaller number that looks exactly as real as a complete one.
13600
+ */
13601
+ cpuMainPercent: number().nullable(),
13602
+ cpuMainPercentMin: number().nullable(),
13603
+ cpuGcPercent: number().nullable(),
13604
+ cpuGcPercentMin: number().nullable(),
13605
+ /** Lifetime-average CPU%, summed. Always known — and never a rate. */
13606
+ cpuLifetimePercent: number(),
13607
+ cpuLifetimePercentMin: number(),
13608
+ memoryRssBytes: number(),
13609
+ memoryRssBytesMin: number(),
13610
+ processCount: number().int(),
13611
+ processCountMin: number().int()
13612
+ });
13613
+ /** One function's series. `key` is an addonId, `__root__` or `__unattributed__`. */
13614
+ var LoadFunctionSeriesSchema = object({
13615
+ key: string(),
13616
+ kind: _enum([
13617
+ "addon",
13618
+ "root",
13619
+ "unattributed"
13620
+ ]),
13621
+ /** Oldest-first. A missing interval is MISSING — never zero-filled. */
13622
+ points: array(LoadPointSchema).readonly()
13623
+ });
13624
+ var NodeLoadSeriesSchema = object({
13625
+ nodeId: string(),
13626
+ /** One entry per function seen in the window, heaviest-first. */
13627
+ series: array(LoadFunctionSeriesSchema).readonly(),
13628
+ /**
13629
+ * Width of one returned bucket, in ms. Equals the sampling cadence when no
13630
+ * reduction was needed — so a caller can always say what one point covers
13631
+ * without having to know whether it was reduced.
13632
+ */
13633
+ bucketMs: number(),
13634
+ /** Raw snapshots that went into this answer, across both tiers. */
13635
+ retainedSamples: number(),
13636
+ /** Oldest snapshot represented, or `null` when nothing is retained. */
13637
+ oldestAtMs: number().nullable(),
13638
+ /** The fixed sampling cadence in force on the cluster, in ms. */
13639
+ cadenceMs: number(),
13640
+ /**
13641
+ * Did the DURABLE tier contribute? `false` means the answer is the hot ring
13642
+ * alone — an agent (which holds no table), or a store that refused.
13643
+ * Reported because "the last hour" and "the last six hours" are different
13644
+ * questions and an operator must not have to guess which was answered.
13645
+ */
13646
+ durable: boolean()
13647
+ });
13648
+ var GetLoadSeriesInputSchema = object({
13649
+ /**
13650
+ * The node whose series is wanted.
13651
+ *
13652
+ * NOT named `nodeId`: the generated cap router strips a top-level
13653
+ * `nodeId` from every method input and uses it to ROUTE the call to
13654
+ * that node's provider (`generated-cap-routers.ts`). A series target
13655
+ * called `nodeId` would silently become a routing pin and never reach
13656
+ * the provider. The hub holds every node it hears from, so the
13657
+ * ordinary call is unpinned — answered by the hub, for any node.
13658
+ */
13659
+ forNodeId: string(),
13660
+ /**
13661
+ * EXCLUSIVE lower bound. A caller passes the newest `atMs` it already
13662
+ * holds and receives only what it is missing, so seeding a live chart
13663
+ * from this method cannot double a point already drawn.
13664
+ */
13665
+ sinceMs: number().optional(),
13666
+ /**
13667
+ * Most points the caller wants PER FUNCTION. The window is reduced to fit,
13668
+ * preserving min and max per bucket.
13669
+ *
13670
+ * Absent means NO reduction — legitimate for a short window and a trap for a
13671
+ * long one, which is why a chart passes its own pixel width.
13672
+ */
13673
+ maxPoints: number().int().positive().optional()
13674
+ });
13475
13675
  var SystemMetricsSchema = object({
13476
13676
  cpuPercent: number(),
13477
13677
  memoryPercent: number(),
@@ -13482,10 +13682,7 @@ var SystemMetricsSchema = object({
13482
13682
  gpuPercent: number().optional(),
13483
13683
  gpuMemoryPercent: number().optional()
13484
13684
  });
13485
- method(_void(), SystemResourceSnapshotSchema), method(_void(), SystemResourceSnapshotSchema.nullable()), method(_void(), SystemMetricsSchema), method(object({ dirPath: string() }), DiskSpaceInfoSchema), method(_void(), MetricsGpuInfoSchema.nullable()), method(_void(), number().nullable()), method(object({ pids: array(number()) }), array(PidResourceStatsSchema)), method(_void(), array(AddonInstanceSchema).readonly()), method(object({ addonId: string() }), PidResourceStatsSchema.nullable()), method(_void(), array(NodeProcessSchema).readonly()), method(KillProcessInputSchema, KillProcessResultSchema, {
13486
- kind: "mutation",
13487
- auth: "admin"
13488
- }), method(DumpHeapSnapshotInputSchema, DumpHeapSnapshotResultSchema, {
13685
+ method(_void(), SystemResourceSnapshotSchema), method(_void(), SystemResourceSnapshotSchema.nullable()), method(_void(), SystemMetricsSchema), method(object({ dirPath: string() }), DiskSpaceInfoSchema), method(_void(), MetricsGpuInfoSchema.nullable()), method(_void(), number().nullable()), method(object({ pids: array(number()) }), array(PidResourceStatsSchema)), method(_void(), array(AddonInstanceSchema).readonly()), method(object({ addonId: string() }), PidResourceStatsSchema.nullable()), method(_void(), array(NodeProcessSchema).readonly()), method(GetLoadSeriesInputSchema, NodeLoadSeriesSchema), method(DumpHeapSnapshotInputSchema, DumpHeapSnapshotResultSchema, {
13489
13686
  kind: "mutation",
13490
13687
  auth: "admin"
13491
13688
  });
@@ -26802,6 +26999,15 @@ var LoggingSettingsPatchSchema = object({
26802
26999
  * authority over the whole hierarchy and answers for every layer, so the
26803
27000
  * layer selector needs a name the transport does not already own.
26804
27001
  */
27002
+ /**
27003
+ * One contribution, plus WHO reported it.
27004
+ *
27005
+ * The addon and node are added by the hub as it enumerates providers, never by
27006
+ * the contributor: an addon reporting its own identity could report somebody
27007
+ * else's, and the whole point of this surface is that no claim is made by
27008
+ * anyone but its owner.
27009
+ */
27010
+ var ReportedLoadContributionSchema = LoadContributionSchema.extend({ addonId: string() });
26805
27011
  var GetLoggingSettingsInputSchema = object({
26806
27012
  scopeNodeId: string().optional(),
26807
27013
  /**
@@ -26860,7 +27066,7 @@ method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), met
26860
27066
  }), method(_void(), SiteLocationStatusSchema, {
26861
27067
  kind: "mutation",
26862
27068
  auth: "admin"
26863
- }), method(_void(), RequestCensusStatusSchema, { auth: "admin" }), method(GetLoggingSettingsInputSchema, LoggingSettingsStateSchema, { auth: "admin" }), method(SetLoggingSettingsInputSchema, LoggingSettingsStateSchema, {
27069
+ }), method(_void(), RequestCensusStatusSchema, { auth: "admin" }), method(_void(), array(ReportedLoadContributionSchema).readonly(), { auth: "admin" }), method(GetLoggingSettingsInputSchema, LoggingSettingsStateSchema, { auth: "admin" }), method(SetLoggingSettingsInputSchema, LoggingSettingsStateSchema, {
26864
27070
  kind: "mutation",
26865
27071
  auth: "admin"
26866
27072
  });
@@ -28476,6 +28682,12 @@ Object.freeze({
28476
28682
  addonId: null,
28477
28683
  access: "create"
28478
28684
  },
28685
+ "dataStoreProvider.insertMany": {
28686
+ capName: "data-store-provider",
28687
+ capScope: "system",
28688
+ addonId: null,
28689
+ access: "create"
28690
+ },
28479
28691
  "dataStoreProvider.isEmpty": {
28480
28692
  capName: "data-store-provider",
28481
28693
  capScope: "system",
@@ -29790,6 +30002,12 @@ Object.freeze({
29790
30002
  addonId: null,
29791
30003
  access: "create"
29792
30004
  },
30005
+ "loadContribution.list": {
30006
+ capName: "load-contribution",
30007
+ capScope: "system",
30008
+ addonId: null,
30009
+ access: "view"
30010
+ },
29793
30011
  "localNetwork.downloadCa": {
29794
30012
  capName: "local-network",
29795
30013
  capScope: "system",
@@ -30090,17 +30308,17 @@ Object.freeze({
30090
30308
  addonId: null,
30091
30309
  access: "view"
30092
30310
  },
30093
- "metricsProvider.getProcessStats": {
30311
+ "metricsProvider.getLoadSeries": {
30094
30312
  capName: "metrics-provider",
30095
30313
  capScope: "system",
30096
30314
  addonId: null,
30097
30315
  access: "view"
30098
30316
  },
30099
- "metricsProvider.killProcess": {
30317
+ "metricsProvider.getProcessStats": {
30100
30318
  capName: "metrics-provider",
30101
30319
  capScope: "system",
30102
30320
  addonId: null,
30103
- access: "create"
30321
+ access: "view"
30104
30322
  },
30105
30323
  "metricsProvider.listAddonInstances": {
30106
30324
  capName: "metrics-provider",
@@ -32112,6 +32330,12 @@ Object.freeze({
32112
32330
  addonId: null,
32113
32331
  access: "create"
32114
32332
  },
32333
+ "settingsStore.insertMany": {
32334
+ capName: "settings-store",
32335
+ capScope: "system",
32336
+ addonId: null,
32337
+ access: "create"
32338
+ },
32115
32339
  "settingsStore.isEmpty": {
32116
32340
  capName: "settings-store",
32117
32341
  capScope: "system",
@@ -32724,6 +32948,12 @@ Object.freeze({
32724
32948
  addonId: null,
32725
32949
  access: "create"
32726
32950
  },
32951
+ "system.getLoadContributions": {
32952
+ capName: "system",
32953
+ capScope: "system",
32954
+ addonId: null,
32955
+ access: "view"
32956
+ },
32727
32957
  "system.getLoggingSettings": {
32728
32958
  capName: "system",
32729
32959
  capScope: "system",
package/dist/addon.mjs CHANGED
@@ -2,7 +2,7 @@ import { brotliCompressSync, deflateSync, gzipSync } from "node:zlib";
2
2
  import { readFileSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
- //#region ../types/dist/event-category-CIa_iT6b.mjs
5
+ //#region ../types/dist/event-category-BZL-fdNj.mjs
6
6
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
7
7
  EventCategory["SystemBoot"] = "system.boot";
8
8
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -439,7 +439,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
439
439
  EventCategory["MetricsNodeResourcesSnapshot"] = "metrics.node-resources-snapshot";
440
440
  /**
441
441
  * Periodic per-node process-tree snapshot (camstack-related pids
442
- * with ghost / managed / root classification). Emitted ~0.2 Hz by
442
+ * with root / managed / system classification). Emitted ~0.2 Hz by
443
443
  * the metrics-provider addon. Drives the Cluster → Processes tab
444
444
  * without polling `metricsProvider.listNodeProcesses`.
445
445
  */
@@ -11273,6 +11273,19 @@ var SettingsRecordSchema = object({
11273
11273
  data: record(string(), unknown())
11274
11274
  });
11275
11275
  /**
11276
+ * One record of a BULK insert — {@link SettingsRecordSchema} with the id made
11277
+ * optional.
11278
+ *
11279
+ * A separate schema rather than loosening the shared one: every other method
11280
+ * on this cap addresses a row BY its id, and making that field optional
11281
+ * everywhere would turn a forgotten key into a silently generated one on
11282
+ * `update` and `delete` as well.
11283
+ */
11284
+ var BulkRecordSchema = object({
11285
+ id: string().optional(),
11286
+ data: record(string(), unknown())
11287
+ });
11288
+ /**
11276
11289
  * Column declaration for a structured (SQL-backed) collection.
11277
11290
  *
11278
11291
  * Logical types — the backend translates each to the matching SQLite
@@ -11329,6 +11342,10 @@ method(object({
11329
11342
  collection: string(),
11330
11343
  record: SettingsRecordSchema
11331
11344
  }), _void(), { kind: "mutation" }), method(object({
11345
+ namespace: string().optional(),
11346
+ collection: string(),
11347
+ records: array(BulkRecordSchema).readonly()
11348
+ }), object({ inserted: number().int() }), { kind: "mutation" }), method(object({
11332
11349
  namespace: string().optional(),
11333
11350
  collection: string(),
11334
11351
  id: string(),
@@ -11437,6 +11454,13 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
11437
11454
  }), _void(), {
11438
11455
  kind: "mutation",
11439
11456
  auth: "admin"
11457
+ }), method(object({
11458
+ namespace: string().optional(),
11459
+ collection: string(),
11460
+ records: array(BulkRecordSchema).readonly()
11461
+ }), object({ inserted: number().int() }), {
11462
+ kind: "mutation",
11463
+ auth: "admin"
11440
11464
  }), method(object({
11441
11465
  namespace: string().optional(),
11442
11466
  collection: string(),
@@ -13205,6 +13229,68 @@ method(LogEntrySchema, _void(), { kind: "mutation" }), method(object({
13205
13229
  limit: number().optional(),
13206
13230
  tags: record(string(), string()).optional()
13207
13231
  }), array(LogEntrySchema).readonly());
13232
+ var LoadContributionSchema = object({
13233
+ role: _enum([
13234
+ "decode",
13235
+ "transcode",
13236
+ "recording",
13237
+ "streaming",
13238
+ "detection"
13239
+ ]),
13240
+ /**
13241
+ * The NUMERIC device id — the same value every log line carries as
13242
+ * `tags.deviceId`. `null` means this cost genuinely belongs to no single
13243
+ * camera (a shared pool), NOT that the contributor forgot to look it up: a
13244
+ * contributor that cannot name its camera must not emit the entry at all,
13245
+ * because an unnamed per-camera entry is indistinguishable from a shared one
13246
+ * and would quietly turn one camera's cost into everybody's.
13247
+ */
13248
+ deviceId: number().int().positive().nullable(),
13249
+ attribution: _enum([
13250
+ "measured",
13251
+ "accounted",
13252
+ "unattributable"
13253
+ ]),
13254
+ /**
13255
+ * What ONE entry is, in the contributor's own words — `615/high`,
13256
+ * `617/native`, `cuda:0 shared pool`. Free text because the unit differs per
13257
+ * family and inventing a common one would lose the only information that
13258
+ * makes two entries for the same camera distinguishable.
13259
+ */
13260
+ unit: string(),
13261
+ /**
13262
+ * The OS process this cost lives in, when there is one. Present so a
13263
+ * consumer can (a) tell two generations of the same unit apart across a
13264
+ * restart, and (b) subtract claimed processes from the node's process
13265
+ * snapshot to see what NOBODY claimed. Absent for an entry that owns no
13266
+ * process of its own.
13267
+ */
13268
+ pid: number().int().positive().optional(),
13269
+ /**
13270
+ * When this generation started. The pid's incarnation marker: a consumer
13271
+ * differencing {@link LoadContributionSchema.shape.cpuSeconds} must drop the
13272
+ * window when this changes, because the counter restarted from zero in a new
13273
+ * process.
13274
+ */
13275
+ startedAtMs: number().optional(),
13276
+ /**
13277
+ * CUMULATIVE CPU seconds this unit has consumed since it started — user +
13278
+ * system, read from the child's own `/proc/<pid>/stat` at the moment the
13279
+ * contribution is asked for.
13280
+ *
13281
+ * Cumulative and not a rate on purpose: a rate needs a window, a window
13282
+ * needs a sampler, and a new per-node sampler is the defect half of
13283
+ * `docs/architecture/load-ledger.md` documents. A counter can be differenced
13284
+ * by whoever already keeps a history; a rate cannot be un-averaged.
13285
+ *
13286
+ * Absent — never zero — on a node with no `/proc`, on a read failure, and on
13287
+ * an entry with no process.
13288
+ */
13289
+ cpuSeconds: number().optional(),
13290
+ /** Resident bytes of this unit's process, same source and same rules. */
13291
+ rssBytes: number().optional()
13292
+ });
13293
+ method(_void(), array(LoadContributionSchema).readonly());
13208
13294
  /**
13209
13295
  * `login-method` — collection cap through which auth addons contribute
13210
13296
  * their pre-auth login surfaces to the login page. This is the SINGLE,
@@ -13408,8 +13494,7 @@ var NodeProcessSchema = object({
13408
13494
  classification: _enum([
13409
13495
  "root",
13410
13496
  "managed",
13411
- "system",
13412
- "ghost"
13497
+ "system"
13413
13498
  ]),
13414
13499
  /** `$process` addon binding when `managed`, else null. */
13415
13500
  addonId: string().nullable(),
@@ -13417,22 +13502,39 @@ var NodeProcessSchema = object({
13417
13502
  nodeId: string().nullable(),
13418
13503
  /** Truncated command line. */
13419
13504
  command: string(),
13505
+ /**
13506
+ * `ps pcpu` — CPU averaged over the process's WHOLE LIFETIME, not a rate.
13507
+ * On a runner up for days it barely moves. Fine as a column, useless as a
13508
+ * series: use `cpuMainPercent + cpuGcPercent` for anything time-varying.
13509
+ */
13420
13510
  cpuPercent: number(),
13421
13511
  memoryRssBytes: number(),
13512
+ /**
13513
+ * Instantaneous CPU% of the process's own threads over the last
13514
+ * process-snapshot window, from a `/proc/<pid>/task/*` tick delta.
13515
+ *
13516
+ * `null` = UNKNOWN, never zero: no previous sample yet (first tick after
13517
+ * boot), the pid was recycled, or this node is not Linux.
13518
+ */
13519
+ cpuMainPercent: number().nullable(),
13520
+ /**
13521
+ * Instantaneous CPU% of V8's `V8Worker` platform pool over the same window.
13522
+ *
13523
+ * This is the number that rewrote the 2026-08-27 diagnosis — hub-main 73%,
13524
+ * `stream-broker` 61% (`docs/architecture/load-ledger.md`). A CPU chart that
13525
+ * does not separate it from `cpuMainPercent` shows "busy" where the truth is
13526
+ * "allocating too much".
13527
+ *
13528
+ * Concurrent GC is the dominant tenant of that pool but not the only one
13529
+ * (background compilation runs there too), so it is reported as
13530
+ * "GC / V8 helpers" rather than as pure collection time. `null` has the same
13531
+ * meaning as on `cpuMainPercent`.
13532
+ */
13533
+ cpuGcPercent: number().nullable(),
13534
+ /** Threads seen in the tick scan. `null` under the same conditions. */
13535
+ threadCount: number().nullable(),
13422
13536
  /** Wall-clock uptime (seconds). Parsed from `ps etime`. */
13423
- uptimeSec: number(),
13424
- /** True when ancestor walk reaches `ppid=1` (reparented to init/launchd). */
13425
- orphaned: boolean()
13426
- });
13427
- var KillProcessInputSchema = object({
13428
- pid: number(),
13429
- /** Force = SIGKILL. Default is SIGTERM. */
13430
- force: boolean().optional()
13431
- });
13432
- var KillProcessResultSchema = object({
13433
- success: boolean(),
13434
- reason: string().optional(),
13435
- signal: _enum(["SIGTERM", "SIGKILL"]).optional()
13537
+ uptimeSec: number()
13436
13538
  });
13437
13539
  var DumpHeapSnapshotInputSchema = object({
13438
13540
  /** The addon whose runner should dump a heap snapshot. */
@@ -13445,6 +13547,104 @@ var DumpHeapSnapshotResultSchema = object({
13445
13547
  pid: number().optional(),
13446
13548
  reason: string().optional()
13447
13549
  });
13550
+ /**
13551
+ * One point of one function's series.
13552
+ *
13553
+ * The unsuffixed fields are the bucket's **MAXIMUM**, and that choice is the
13554
+ * point of the whole surface. The two obvious reductions both lie: a mean per
13555
+ * bucket smears a spike away, and taking every Nth sample skips it outright.
13556
+ * Either would give us a tool built to find peaks that does not show peaks.
13557
+ * `...Min` carries the other end, `samples` says how many raw snapshots folded
13558
+ * into the bucket, and a mean stays derivable where it is wanted.
13559
+ *
13560
+ * An UNREDUCED point is a one-sample bucket: `samples === 1` and each `...Min`
13561
+ * equals its unsuffixed twin. Reduced and unreduced are the same shape, so a
13562
+ * caller cannot tell which it received — which is what "one reader" means.
13563
+ */
13564
+ var LoadPointSchema = object({
13565
+ /** Bucket START, or the snapshot's own timestamp when unreduced. */
13566
+ atMs: number(),
13567
+ /** Raw snapshots in this bucket. Never 0 — AN EMPTY BUCKET IS ABSENT. */
13568
+ samples: number().int(),
13569
+ /**
13570
+ * `null` = UNKNOWN and it PROPAGATES: a bucket is null unless every process
13571
+ * of every snapshot in it reported a thread split. A partial sum is a
13572
+ * smaller number that looks exactly as real as a complete one.
13573
+ */
13574
+ cpuMainPercent: number().nullable(),
13575
+ cpuMainPercentMin: number().nullable(),
13576
+ cpuGcPercent: number().nullable(),
13577
+ cpuGcPercentMin: number().nullable(),
13578
+ /** Lifetime-average CPU%, summed. Always known — and never a rate. */
13579
+ cpuLifetimePercent: number(),
13580
+ cpuLifetimePercentMin: number(),
13581
+ memoryRssBytes: number(),
13582
+ memoryRssBytesMin: number(),
13583
+ processCount: number().int(),
13584
+ processCountMin: number().int()
13585
+ });
13586
+ /** One function's series. `key` is an addonId, `__root__` or `__unattributed__`. */
13587
+ var LoadFunctionSeriesSchema = object({
13588
+ key: string(),
13589
+ kind: _enum([
13590
+ "addon",
13591
+ "root",
13592
+ "unattributed"
13593
+ ]),
13594
+ /** Oldest-first. A missing interval is MISSING — never zero-filled. */
13595
+ points: array(LoadPointSchema).readonly()
13596
+ });
13597
+ var NodeLoadSeriesSchema = object({
13598
+ nodeId: string(),
13599
+ /** One entry per function seen in the window, heaviest-first. */
13600
+ series: array(LoadFunctionSeriesSchema).readonly(),
13601
+ /**
13602
+ * Width of one returned bucket, in ms. Equals the sampling cadence when no
13603
+ * reduction was needed — so a caller can always say what one point covers
13604
+ * without having to know whether it was reduced.
13605
+ */
13606
+ bucketMs: number(),
13607
+ /** Raw snapshots that went into this answer, across both tiers. */
13608
+ retainedSamples: number(),
13609
+ /** Oldest snapshot represented, or `null` when nothing is retained. */
13610
+ oldestAtMs: number().nullable(),
13611
+ /** The fixed sampling cadence in force on the cluster, in ms. */
13612
+ cadenceMs: number(),
13613
+ /**
13614
+ * Did the DURABLE tier contribute? `false` means the answer is the hot ring
13615
+ * alone — an agent (which holds no table), or a store that refused.
13616
+ * Reported because "the last hour" and "the last six hours" are different
13617
+ * questions and an operator must not have to guess which was answered.
13618
+ */
13619
+ durable: boolean()
13620
+ });
13621
+ var GetLoadSeriesInputSchema = object({
13622
+ /**
13623
+ * The node whose series is wanted.
13624
+ *
13625
+ * NOT named `nodeId`: the generated cap router strips a top-level
13626
+ * `nodeId` from every method input and uses it to ROUTE the call to
13627
+ * that node's provider (`generated-cap-routers.ts`). A series target
13628
+ * called `nodeId` would silently become a routing pin and never reach
13629
+ * the provider. The hub holds every node it hears from, so the
13630
+ * ordinary call is unpinned — answered by the hub, for any node.
13631
+ */
13632
+ forNodeId: string(),
13633
+ /**
13634
+ * EXCLUSIVE lower bound. A caller passes the newest `atMs` it already
13635
+ * holds and receives only what it is missing, so seeding a live chart
13636
+ * from this method cannot double a point already drawn.
13637
+ */
13638
+ sinceMs: number().optional(),
13639
+ /**
13640
+ * Most points the caller wants PER FUNCTION. The window is reduced to fit,
13641
+ * preserving min and max per bucket.
13642
+ *
13643
+ * Absent means NO reduction — legitimate for a short window and a trap for a
13644
+ * long one, which is why a chart passes its own pixel width.
13645
+ */
13646
+ maxPoints: number().int().positive().optional()
13647
+ });
13448
13648
  var SystemMetricsSchema = object({
13449
13649
  cpuPercent: number(),
13450
13650
  memoryPercent: number(),
@@ -13455,10 +13655,7 @@ var SystemMetricsSchema = object({
13455
13655
  gpuPercent: number().optional(),
13456
13656
  gpuMemoryPercent: number().optional()
13457
13657
  });
13458
- method(_void(), SystemResourceSnapshotSchema), method(_void(), SystemResourceSnapshotSchema.nullable()), method(_void(), SystemMetricsSchema), method(object({ dirPath: string() }), DiskSpaceInfoSchema), method(_void(), MetricsGpuInfoSchema.nullable()), method(_void(), number().nullable()), method(object({ pids: array(number()) }), array(PidResourceStatsSchema)), method(_void(), array(AddonInstanceSchema).readonly()), method(object({ addonId: string() }), PidResourceStatsSchema.nullable()), method(_void(), array(NodeProcessSchema).readonly()), method(KillProcessInputSchema, KillProcessResultSchema, {
13459
- kind: "mutation",
13460
- auth: "admin"
13461
- }), method(DumpHeapSnapshotInputSchema, DumpHeapSnapshotResultSchema, {
13658
+ method(_void(), SystemResourceSnapshotSchema), method(_void(), SystemResourceSnapshotSchema.nullable()), method(_void(), SystemMetricsSchema), method(object({ dirPath: string() }), DiskSpaceInfoSchema), method(_void(), MetricsGpuInfoSchema.nullable()), method(_void(), number().nullable()), method(object({ pids: array(number()) }), array(PidResourceStatsSchema)), method(_void(), array(AddonInstanceSchema).readonly()), method(object({ addonId: string() }), PidResourceStatsSchema.nullable()), method(_void(), array(NodeProcessSchema).readonly()), method(GetLoadSeriesInputSchema, NodeLoadSeriesSchema), method(DumpHeapSnapshotInputSchema, DumpHeapSnapshotResultSchema, {
13462
13659
  kind: "mutation",
13463
13660
  auth: "admin"
13464
13661
  });
@@ -26775,6 +26972,15 @@ var LoggingSettingsPatchSchema = object({
26775
26972
  * authority over the whole hierarchy and answers for every layer, so the
26776
26973
  * layer selector needs a name the transport does not already own.
26777
26974
  */
26975
+ /**
26976
+ * One contribution, plus WHO reported it.
26977
+ *
26978
+ * The addon and node are added by the hub as it enumerates providers, never by
26979
+ * the contributor: an addon reporting its own identity could report somebody
26980
+ * else's, and the whole point of this surface is that no claim is made by
26981
+ * anyone but its owner.
26982
+ */
26983
+ var ReportedLoadContributionSchema = LoadContributionSchema.extend({ addonId: string() });
26778
26984
  var GetLoggingSettingsInputSchema = object({
26779
26985
  scopeNodeId: string().optional(),
26780
26986
  /**
@@ -26833,7 +27039,7 @@ method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), met
26833
27039
  }), method(_void(), SiteLocationStatusSchema, {
26834
27040
  kind: "mutation",
26835
27041
  auth: "admin"
26836
- }), method(_void(), RequestCensusStatusSchema, { auth: "admin" }), method(GetLoggingSettingsInputSchema, LoggingSettingsStateSchema, { auth: "admin" }), method(SetLoggingSettingsInputSchema, LoggingSettingsStateSchema, {
27042
+ }), method(_void(), RequestCensusStatusSchema, { auth: "admin" }), method(_void(), array(ReportedLoadContributionSchema).readonly(), { auth: "admin" }), method(GetLoggingSettingsInputSchema, LoggingSettingsStateSchema, { auth: "admin" }), method(SetLoggingSettingsInputSchema, LoggingSettingsStateSchema, {
26837
27043
  kind: "mutation",
26838
27044
  auth: "admin"
26839
27045
  });
@@ -28449,6 +28655,12 @@ Object.freeze({
28449
28655
  addonId: null,
28450
28656
  access: "create"
28451
28657
  },
28658
+ "dataStoreProvider.insertMany": {
28659
+ capName: "data-store-provider",
28660
+ capScope: "system",
28661
+ addonId: null,
28662
+ access: "create"
28663
+ },
28452
28664
  "dataStoreProvider.isEmpty": {
28453
28665
  capName: "data-store-provider",
28454
28666
  capScope: "system",
@@ -29763,6 +29975,12 @@ Object.freeze({
29763
29975
  addonId: null,
29764
29976
  access: "create"
29765
29977
  },
29978
+ "loadContribution.list": {
29979
+ capName: "load-contribution",
29980
+ capScope: "system",
29981
+ addonId: null,
29982
+ access: "view"
29983
+ },
29766
29984
  "localNetwork.downloadCa": {
29767
29985
  capName: "local-network",
29768
29986
  capScope: "system",
@@ -30063,17 +30281,17 @@ Object.freeze({
30063
30281
  addonId: null,
30064
30282
  access: "view"
30065
30283
  },
30066
- "metricsProvider.getProcessStats": {
30284
+ "metricsProvider.getLoadSeries": {
30067
30285
  capName: "metrics-provider",
30068
30286
  capScope: "system",
30069
30287
  addonId: null,
30070
30288
  access: "view"
30071
30289
  },
30072
- "metricsProvider.killProcess": {
30290
+ "metricsProvider.getProcessStats": {
30073
30291
  capName: "metrics-provider",
30074
30292
  capScope: "system",
30075
30293
  addonId: null,
30076
- access: "create"
30294
+ access: "view"
30077
30295
  },
30078
30296
  "metricsProvider.listAddonInstances": {
30079
30297
  capName: "metrics-provider",
@@ -32085,6 +32303,12 @@ Object.freeze({
32085
32303
  addonId: null,
32086
32304
  access: "create"
32087
32305
  },
32306
+ "settingsStore.insertMany": {
32307
+ capName: "settings-store",
32308
+ capScope: "system",
32309
+ addonId: null,
32310
+ access: "create"
32311
+ },
32088
32312
  "settingsStore.isEmpty": {
32089
32313
  capName: "settings-store",
32090
32314
  capScope: "system",
@@ -32697,6 +32921,12 @@ Object.freeze({
32697
32921
  addonId: null,
32698
32922
  access: "create"
32699
32923
  },
32924
+ "system.getLoadContributions": {
32925
+ capName: "system",
32926
+ capScope: "system",
32927
+ addonId: null,
32928
+ access: "view"
32929
+ },
32700
32930
  "system.getLoggingSettings": {
32701
32931
  capName: "system",
32702
32932
  capScope: "system",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-notifiers",
3
- "version": "1.2.38",
3
+ "version": "1.2.40",
4
4
  "description": "System notifiers addon for CamStack — a `notification-output` collection provider hosting per-kind notifier adapters (ntfy, pushover, gotify, telegram, discord, webhook, zentik).",
5
5
  "keywords": [
6
6
  "camstack",