@camstack/addon-terminal 0.1.126 → 0.1.127

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.
package/dist/addon.js CHANGED
@@ -31,7 +31,7 @@ let node_module = require("node:module");
31
31
  let sharp = require("sharp");
32
32
  sharp = __toESM(sharp);
33
33
  let node_http = require("node:http");
34
- //#region ../types/dist/event-category-BGmz_oxW.mjs
34
+ //#region ../types/dist/event-category-DaIZi00R.mjs
35
35
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
36
36
  EventCategory["SystemBoot"] = "system.boot";
37
37
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -110,6 +110,13 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
110
110
  */
111
111
  EventCategory["AddonLoadFailed"] = "addon.load-failed";
112
112
  /**
113
+ * A keychain value was stored or removed (D787). Carries the ref, its
114
+ * owner and the new version — NEVER the value. A process holding the ref
115
+ * drops its cached copy and the owning `BaseAddon` re-resolves its config.
116
+ * Telemetry (D8): a process that misses it still revalidates by version.
117
+ */
118
+ EventCategory["KeychainChanged"] = "keychain.changed";
119
+ /**
113
120
  * Addon recovered from a previous failure. Emitted when an addon
114
121
  * transitions from `failed` back to `healthy` (typically via the
115
122
  * monitor's auto-retry loop, or after manual `addons.retryLoad`).
@@ -532,15 +539,6 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
532
539
  EventCategory["AgentWsDisconnected"] = "agent.ws.disconnected";
533
540
  EventCategory["AgentBackupActivated"] = "agent.backup.activated";
534
541
  EventCategory["OrchestrationSettingsUpdated"] = "orchestration.settings-updated";
535
- /**
536
- * Per-agent hwaccel preference changed (user override set, cleared, or
537
- * re-probed). Observability event — decoders pull the current pref at
538
- * `createSession`, so running sessions keep their current backend until
539
- * they rotate naturally (camera add/remove, stream restart). Future
540
- * work can wire a listener in stream-broker that force-rotates live
541
- * sessions; for now this event powers logs + admin-UI toast feedback.
542
- */
543
- EventCategory["PipelineAgentHwaccelChanged"] = "pipeline.agent-hwaccel-changed";
544
542
  EventCategory["MotionAnalysis"] = "detection.motion-analysis";
545
543
  /** All raw motion zones from CCL before minArea filter — for UI debug overlay. */
546
544
  EventCategory["MotionZonesRaw"] = "detection.motion-zones-raw";
@@ -5411,7 +5409,7 @@ var ZodIssueCode = {
5411
5409
  var ZodFirstPartyTypeKind;
5412
5410
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5413
5411
  //#endregion
5414
- //#region ../types/dist/sleep-DXKLNFvA.mjs
5412
+ //#region ../types/dist/sleep-DujUvOcb.mjs
5415
5413
  /**
5416
5414
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5417
5415
  * window to float samples (D455).
@@ -5490,6 +5488,97 @@ function buildAlawTable() {
5490
5488
  }
5491
5489
  buildUlawTable();
5492
5490
  buildAlawTable();
5491
+ /** Is this field's stored value a credential? */
5492
+ function isSecretConfigField(field) {
5493
+ if (field.type === "password") return true;
5494
+ return "secret" in field && field.secret === true;
5495
+ }
5496
+ /**
5497
+ * Is this field's value stored in the KEYCHAIN (D787)? Every secret field is,
5498
+ * by its TYPE, unless it opts out with `storage: 'inline'` — never by its name.
5499
+ */
5500
+ function isKeychainStoredField(field) {
5501
+ if (!isSecretConfigField(field)) return false;
5502
+ if (field.type === "textarea" && field.isJson === true) return false;
5503
+ return !("storage" in field && field.storage === "inline");
5504
+ }
5505
+ /**
5506
+ * What a settings blob holds where a secret used to be.
5507
+ *
5508
+ * A keychain-stored field never holds its value. It holds a self-describing
5509
+ * MARKER, `{"$secret":"secret://…","v":1}` — an object, never a string, so no
5510
+ * code can mistake it for a credential and no form can send it to a camera as
5511
+ * a password. The empty state is `null` / absent, never `''`.
5512
+ *
5513
+ * The same shapes travel to and from the admin UI:
5514
+ *
5515
+ * - read: `hydrateSchema` hands a secret field a {@link SecretFieldView}
5516
+ * (`{ $secret, v, set: true }`) or `null` — the browser learns whether a
5517
+ * value is set, never the value.
5518
+ * - write: an untouched field submits the view back unchanged (KEEP); a typed
5519
+ * value is a plain non-empty string (CHANGE); the Clear button submits
5520
+ * {@link SECRET_CLEAR} (CLEAR). `''`, `null` and the legacy redaction tokens
5521
+ * are KEEP — an empty string is what a defaults-fallback pushes back, and
5522
+ * treating it as "clear" is how a save destroyed a stored credential before.
5523
+ *
5524
+ * Pure and browser-safe.
5525
+ */
5526
+ var SECRET_CLEAR = { $secretClear: true };
5527
+ /** The `'***'` token D634 redaction writes for non-admins. */
5528
+ var LEGACY_DEVICE_REDACTION = "***";
5529
+ function isRecord$1(value) {
5530
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5531
+ }
5532
+ function makeSecretMarker(ref) {
5533
+ return {
5534
+ $secret: ref,
5535
+ v: 1
5536
+ };
5537
+ }
5538
+ /** A stored marker: an object whose `$secret` is a non-empty string. */
5539
+ function isSecretMarker(value) {
5540
+ if (!isRecord$1(value)) return false;
5541
+ const ref = value["$secret"];
5542
+ return typeof ref === "string" && ref.length > 0;
5543
+ }
5544
+ function isSecretClear(value) {
5545
+ return isRecord$1(value) && value["$secretClear"] === true;
5546
+ }
5547
+ /**
5548
+ * The UI view of a stored secret field value: a marker becomes "set", inline
5549
+ * plaintext becomes "set (inline)", nothing becomes `null`. A value never
5550
+ * survives this function.
5551
+ */
5552
+ function toSecretFieldView(stored) {
5553
+ if (isSecretMarker(stored)) return {
5554
+ $secret: stored.$secret,
5555
+ v: 1,
5556
+ set: true
5557
+ };
5558
+ if (typeof stored === "string" && stored.length > 0) return {
5559
+ $secret: null,
5560
+ v: 1,
5561
+ set: true,
5562
+ inline: true
5563
+ };
5564
+ return null;
5565
+ }
5566
+ /**
5567
+ * Classify an incoming value for a secret path. Only a non-empty string that is
5568
+ * not a redaction token sets a value; only {@link SECRET_CLEAR} clears.
5569
+ * Everything else — a marker (ours or anyone's), a UI view, `''`, `null`,
5570
+ * `undefined`, a redaction token, a non-string — keeps what is stored.
5571
+ */
5572
+ function classifySecretWrite(incoming) {
5573
+ if (isSecretClear(incoming)) return { kind: "clear" };
5574
+ if (typeof incoming !== "string") return { kind: "keep" };
5575
+ if (incoming.length === 0) return { kind: "keep" };
5576
+ if (incoming === "__camstack_redacted__" || incoming === LEGACY_DEVICE_REDACTION) return { kind: "keep" };
5577
+ return {
5578
+ kind: "set",
5579
+ value: incoming
5580
+ };
5581
+ }
5493
5582
  Object.fromEntries([
5494
5583
  {
5495
5584
  id: "overview",
@@ -5661,33 +5750,19 @@ Object.fromEntries([
5661
5750
  function isValuelessField(field) {
5662
5751
  return field.type === "separator" || field.type === "info" || field.type === "qr-code" || field.type === "button" || field.type === "object-array" || field.type === "widget" || field.type === "addon-action-button" || field.type === "device-action-button";
5663
5752
  }
5664
- /**
5665
- * Merge a `ConfigUISchema` with a raw `values` record into a
5666
- * `ConfigUISchemaWithValues`. Used by every `get*Settings` backend endpoint
5667
- * before returning to the admin UI. Groups are walked recursively.
5668
- *
5669
- * For each leaf field:
5670
- * - structural fields (`separator`, `info`, `button`) pass through unchanged
5671
- * - other fields get `value = values[key] ?? field.default ?? null`
5672
- *
5673
- * Unknown keys in `values` (keys not declared in the schema) are silently
5674
- * ignored — they don't contribute to the output. Missing keys fall back to
5675
- * the schema default or `null`. This mirrors the old backend behaviour where
5676
- * `FormBuilder` rendered `values[key] ?? field.default`.
5677
- */
5678
- function hydrateSchema(schema, values) {
5753
+ function hydrateSchema(schema, values, options = {}) {
5679
5754
  return {
5680
5755
  ...schema.tabs ? { tabs: [...schema.tabs] } : {},
5681
5756
  sections: schema.sections.map((section) => ({
5682
5757
  ...section,
5683
- fields: section.fields.map((field) => hydrateField(field, values))
5758
+ fields: section.fields.map((field) => hydrateField(field, values, options))
5684
5759
  }))
5685
5760
  };
5686
5761
  }
5687
- function hydrateField(field, values) {
5762
+ function hydrateField(field, values, options) {
5688
5763
  if (isValuelessField(field)) return field;
5689
5764
  if (field.type === "group") {
5690
- const hydratedChildren = field.fields.map((child) => hydrateField(child, values));
5765
+ const hydratedChildren = field.fields.map((child) => hydrateField(child, values, options));
5691
5766
  return {
5692
5767
  ...field,
5693
5768
  fields: hydratedChildren
@@ -5696,7 +5771,7 @@ function hydrateField(field, values) {
5696
5771
  if (field.type === "sub-tabs") {
5697
5772
  const hydratedTabs = field.tabs.map((tab) => ({
5698
5773
  ...tab,
5699
- fields: tab.fields.map((child) => hydrateField(child, values))
5774
+ fields: tab.fields.map((child) => hydrateField(child, values, options))
5700
5775
  }));
5701
5776
  return {
5702
5777
  ...field,
@@ -5706,6 +5781,33 @@ function hydrateField(field, values) {
5706
5781
  const key = field.key;
5707
5782
  const storedValue = Object.prototype.hasOwnProperty.call(values, key) ? values[key] : void 0;
5708
5783
  const defaultValue = field.default;
5784
+ const asView = (v) => isSecretMarker(v) || options.inlineSecretsAsViews === true && v !== void 0;
5785
+ if (isKeychainStoredField(field) && asView(storedValue)) return {
5786
+ ...field,
5787
+ value: toSecretFieldView(storedValue)
5788
+ };
5789
+ if (isKeychainStoredField(field) && options.inlineSecretsAsViews === true) return {
5790
+ ...field,
5791
+ value: null
5792
+ };
5793
+ if (field.type === "editable-array" && Array.isArray(storedValue)) {
5794
+ const secretItemKeys = field.itemFields.filter((item) => "key" in item && isKeychainStoredField(item)).map((item) => "key" in item ? item.key : "");
5795
+ if (secretItemKeys.length > 0) {
5796
+ const rows = storedValue.map((row) => {
5797
+ if (row === null || typeof row !== "object" || Array.isArray(row)) return row;
5798
+ const out = { ...row };
5799
+ for (const k of secretItemKeys) {
5800
+ const v = Reflect.get(row, k);
5801
+ if (asView(v)) out[k] = toSecretFieldView(v);
5802
+ }
5803
+ return out;
5804
+ });
5805
+ return {
5806
+ ...field,
5807
+ value: rows
5808
+ };
5809
+ }
5810
+ }
5709
5811
  if (field.multiple) {
5710
5812
  const stored = Array.isArray(storedValue) ? storedValue : storedValue !== void 0 && storedValue !== null ? [storedValue] : [];
5711
5813
  const itemFallback = field.multiple.itemDefault !== void 0 ? field.multiple.itemDefault : defaultValue !== void 0 ? defaultValue : typeOf(field) === "string" ? "" : null;
@@ -5948,6 +6050,431 @@ function projectStore(store, perNodeKeys, nodeId) {
5948
6050
  }
5949
6051
  return out;
5950
6052
  }
6053
+ var GLOBAL_SCOPE = { kind: "global" };
6054
+ /** The scope every addon-wide (not per-node, not per-device) setting lives in. */
6055
+ function globalSecretScope() {
6056
+ return GLOBAL_SCOPE;
6057
+ }
6058
+ function encodeSegment(value) {
6059
+ return encodeURIComponent(value);
6060
+ }
6061
+ /**
6062
+ * The field path of a value inside an array item, addressed by the item's id:
6063
+ * `arrayItemFieldPath('providers', 'authentik', 'clientSecret')` →
6064
+ * `providers[authentik].clientSecret`.
6065
+ */
6066
+ function arrayItemFieldPath(arrayKey, itemId, fieldKey) {
6067
+ return `${arrayKey}[${encodeSegment(itemId)}].${fieldKey}`;
6068
+ }
6069
+ /** A write to a secret path could not be stored in the keychain, so it was refused. */
6070
+ var KeychainWriteRefusedError = class extends Error {
6071
+ fieldPath;
6072
+ constructor(fieldPath, cause) {
6073
+ super(`secret "${fieldPath}" was not saved: the keychain could not store it (${cause instanceof Error ? cause.message : String(cause)}). Nothing was written.`);
6074
+ this.fieldPath = fieldPath;
6075
+ this.name = "KeychainWriteRefusedError";
6076
+ }
6077
+ };
6078
+ var KEEP = { kind: "keep" };
6079
+ function isRecord(value) {
6080
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6081
+ }
6082
+ function defaultMintId() {
6083
+ return globalThis.crypto.randomUUID();
6084
+ }
6085
+ /** Every marker ref anywhere under `value`. */
6086
+ function collectSecretMarkerRefs(value, out = []) {
6087
+ if (isSecretMarker(value)) {
6088
+ out.push(value.$secret);
6089
+ return out;
6090
+ }
6091
+ if (Array.isArray(value)) for (const item of value) collectSecretMarkerRefs(item, out);
6092
+ else if (isRecord(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
6093
+ return out;
6094
+ }
6095
+ async function storedValueEquals(secrets, marker, candidate) {
6096
+ const resolved = (await secrets.resolve([marker.$secret]))[marker.$secret];
6097
+ return resolved?.kind === "value" && resolved.value === candidate;
6098
+ }
6099
+ async function writeLeaf(ctx, incoming, stored, fieldPath) {
6100
+ const intent = classifySecretWrite(incoming);
6101
+ const ref = ctx.secrets.ref(ctx.scope, fieldPath);
6102
+ const storedIsOurs = isSecretMarker(stored) && stored.$secret === ref;
6103
+ if (intent.kind === "keep") return KEEP;
6104
+ if (intent.kind === "set" && !ctx.operatorWrite && typeof stored === "string" && stored === intent.value) return KEEP;
6105
+ if (intent.kind === "clear") {
6106
+ if (storedIsOurs) ctx.deletes.push(ref);
6107
+ return {
6108
+ kind: "value",
6109
+ value: null
6110
+ };
6111
+ }
6112
+ if (storedIsOurs && await storedValueEquals(ctx.secrets, stored, intent.value)) return {
6113
+ kind: "value",
6114
+ value: stored
6115
+ };
6116
+ try {
6117
+ await ctx.secrets.put(ref, intent.value);
6118
+ } catch (err) {
6119
+ throw new KeychainWriteRefusedError(fieldPath, err);
6120
+ }
6121
+ return {
6122
+ kind: "value",
6123
+ value: makeSecretMarker(ref)
6124
+ };
6125
+ }
6126
+ /** Apply a member result onto `out`: KEEP restores the stored member, or removes it. */
6127
+ function applyMember(out, key, result, storedParent) {
6128
+ if (result.kind === "value") {
6129
+ out[key] = result.value;
6130
+ return;
6131
+ }
6132
+ if (Object.prototype.hasOwnProperty.call(storedParent, key)) out[key] = storedParent[key];
6133
+ else delete out[key];
6134
+ }
6135
+ async function writeMembers(ctx, children, incoming, stored, pathOf) {
6136
+ const out = { ...incoming };
6137
+ for (const [member, node] of children) applyMember(out, member, await writeNode(ctx, node, incoming[member], stored[member], pathOf(member)), stored);
6138
+ return out;
6139
+ }
6140
+ function rowId(row, idKey) {
6141
+ const raw = row[idKey];
6142
+ if (typeof raw === "string" && raw.length > 0) return raw;
6143
+ if (typeof raw === "number" && Number.isFinite(raw)) return String(raw);
6144
+ return null;
6145
+ }
6146
+ async function writeArray(ctx, node, incoming, stored, fieldPath) {
6147
+ const storedRows = Array.isArray(stored) ? stored.filter(isRecord) : [];
6148
+ if (!Array.isArray(incoming)) {
6149
+ ctx.deletes.push(...collectSecretMarkerRefs(storedRows));
6150
+ return {
6151
+ kind: "value",
6152
+ value: incoming
6153
+ };
6154
+ }
6155
+ const storedById = /* @__PURE__ */ new Map();
6156
+ for (const row of storedRows) {
6157
+ const id = rowId(row, node.idKey);
6158
+ if (id !== null) storedById.set(id, row);
6159
+ }
6160
+ const kept = /* @__PURE__ */ new Set();
6161
+ const rows = [];
6162
+ for (const row of incoming) {
6163
+ if (!isRecord(row)) {
6164
+ rows.push(row);
6165
+ continue;
6166
+ }
6167
+ const id = rowId(row, node.idKey) ?? ctx.mintId();
6168
+ kept.add(id);
6169
+ const withId = {
6170
+ ...row,
6171
+ [node.idKey]: row[node.idKey] ?? id
6172
+ };
6173
+ if (rowId(withId, node.idKey) === null) withId[node.idKey] = id;
6174
+ const storedRow = storedById.get(id) ?? {};
6175
+ rows.push(await writeMembers(ctx, node.item, withId, storedRow, (member) => arrayItemFieldPath(fieldPath, id, member)));
6176
+ }
6177
+ for (const [id, row] of storedById) if (!kept.has(id)) ctx.deletes.push(...collectSecretMarkerRefs(row));
6178
+ return {
6179
+ kind: "value",
6180
+ value: rows
6181
+ };
6182
+ }
6183
+ async function writeNode(ctx, node, incoming, stored, fieldPath) {
6184
+ switch (node.kind) {
6185
+ case "leaf": return writeLeaf(ctx, incoming, stored, fieldPath);
6186
+ case "array": return writeArray(ctx, node, incoming, stored, fieldPath);
6187
+ case "object": {
6188
+ if (!isRecord(incoming)) {
6189
+ if (incoming === void 0) return KEEP;
6190
+ ctx.deletes.push(...collectSecretMarkerRefs(stored));
6191
+ return {
6192
+ kind: "value",
6193
+ value: incoming
6194
+ };
6195
+ }
6196
+ const storedObj = isRecord(stored) ? stored : {};
6197
+ return {
6198
+ kind: "value",
6199
+ value: await writeMembers(ctx, node.children, incoming, storedObj, (m) => `${fieldPath}.${m}`)
6200
+ };
6201
+ }
6202
+ }
6203
+ }
6204
+ /** Split a perNode-scoped key (`key@nodeId`). */
6205
+ function splitNodeKey(key) {
6206
+ const at = key.indexOf("@");
6207
+ return at <= 0 ? {
6208
+ base: key,
6209
+ nodeId: null
6210
+ } : {
6211
+ base: key.slice(0, at),
6212
+ nodeId: key.slice(at + 1)
6213
+ };
6214
+ }
6215
+ /** Rewrite a settings patch so no secret value reaches the blob. See the file doc. */
6216
+ async function transformSecretWrite(input) {
6217
+ const deletes = [];
6218
+ const base = {
6219
+ secrets: input.secrets,
6220
+ mintId: input.mintId ?? defaultMintId,
6221
+ deletes,
6222
+ operatorWrite: input.operatorWrite === true
6223
+ };
6224
+ const out = {};
6225
+ for (const [key, incoming] of Object.entries(input.patch)) {
6226
+ const { base: baseKey, nodeId } = splitNodeKey(key);
6227
+ const node = input.tree.get(baseKey);
6228
+ if (node === void 0) {
6229
+ out[key] = incoming;
6230
+ continue;
6231
+ }
6232
+ const scope = nodeId !== null && nodeId.length > 0 ? {
6233
+ kind: "node",
6234
+ nodeId
6235
+ } : input.scope ?? globalSecretScope();
6236
+ const result = await writeNode({
6237
+ ...base,
6238
+ scope
6239
+ }, node, incoming, input.stored[key], baseKey);
6240
+ if (result.kind === "value") out[key] = result.value;
6241
+ }
6242
+ return {
6243
+ patch: out,
6244
+ afterWrite: async () => {
6245
+ if (deletes.length === 0) return { orphaned: [] };
6246
+ try {
6247
+ await input.secrets.delete([...new Set(deletes)]);
6248
+ return { orphaned: [] };
6249
+ } catch {
6250
+ return { orphaned: [...new Set(deletes)] };
6251
+ }
6252
+ }
6253
+ };
6254
+ }
6255
+ /**
6256
+ * Replace every marker in `value` with its keychain value. Returns the SAME
6257
+ * reference when there is no marker (no allocation, no keychain call).
6258
+ */
6259
+ async function resolveSecretMarkers(value, secrets) {
6260
+ const refs = collectSecretMarkerRefs(value);
6261
+ if (refs.length === 0) return {
6262
+ value,
6263
+ unresolved: []
6264
+ };
6265
+ const answers = await secrets.resolve(refs);
6266
+ const unresolved = [];
6267
+ for (const ref of new Set(refs)) {
6268
+ const answer = answers[ref];
6269
+ if (answer === void 0) unresolved.push({
6270
+ ref,
6271
+ reason: "not-initialized"
6272
+ });
6273
+ else if (answer.kind === "unavailable") unresolved.push({
6274
+ ref,
6275
+ reason: answer.reason
6276
+ });
6277
+ else if (answer.kind === "missing" || answer.kind === "forbidden") unresolved.push({
6278
+ ref,
6279
+ reason: answer.kind
6280
+ });
6281
+ }
6282
+ const substitute = (v) => {
6283
+ if (isSecretMarker(v)) {
6284
+ const answer = answers[v.$secret];
6285
+ return answer?.kind === "value" ? {
6286
+ keep: true,
6287
+ value: answer.value
6288
+ } : {
6289
+ keep: false,
6290
+ value: void 0
6291
+ };
6292
+ }
6293
+ if (Array.isArray(v)) {
6294
+ const items = [];
6295
+ for (const item of v) {
6296
+ const s = substitute(item);
6297
+ if (s.keep) items.push(s.value);
6298
+ }
6299
+ return {
6300
+ keep: true,
6301
+ value: items
6302
+ };
6303
+ }
6304
+ if (isRecord(v)) {
6305
+ const out = {};
6306
+ for (const [k, child] of Object.entries(v)) {
6307
+ const s = substitute(child);
6308
+ if (s.keep) out[k] = s.value;
6309
+ }
6310
+ return {
6311
+ keep: true,
6312
+ value: out
6313
+ };
6314
+ }
6315
+ return {
6316
+ keep: true,
6317
+ value: v
6318
+ };
6319
+ };
6320
+ return {
6321
+ value: substitute(value).value,
6322
+ unresolved
6323
+ };
6324
+ }
6325
+ function touchesTree(tree, patch) {
6326
+ if (tree.size === 0) return false;
6327
+ for (const key of Object.keys(patch)) {
6328
+ const at = key.indexOf("@");
6329
+ if (tree.has(at > 0 ? key.slice(0, at) : key)) return true;
6330
+ }
6331
+ return false;
6332
+ }
6333
+ /** The blob as stored now; a failed read REFUSES the write instead of guessing. */
6334
+ async function readStoredAddonBlob(raw) {
6335
+ const threeWay = raw.readAddonStoreResult;
6336
+ if (threeWay === void 0) return raw.readAddonStore();
6337
+ const result = await threeWay.call(raw);
6338
+ if (result.kind === "failed") throw new Error(`settings write refused: the stored blob could not be read (${result.error})`);
6339
+ return result.kind === "missing" ? {} : result.value;
6340
+ }
6341
+ function createSecretAwareSettings(raw, opts) {
6342
+ const chains = /* @__PURE__ */ new Map();
6343
+ const serialize = (key, fn) => {
6344
+ const next = (chains.get(key) ?? Promise.resolve()).then(fn, fn);
6345
+ const tail = next.catch(() => void 0);
6346
+ chains.set(key, tail);
6347
+ tail.then(() => {
6348
+ if (chains.get(key) === tail) chains.delete(key);
6349
+ });
6350
+ return next;
6351
+ };
6352
+ const write = async (chainKey, tree, scope, patch, readStored, rawWrite) => {
6353
+ if (!touchesTree(tree, patch)) return rawWrite(patch);
6354
+ return serialize(chainKey, async () => {
6355
+ const stored = await readStored();
6356
+ const result = await transformSecretWrite({
6357
+ tree,
6358
+ secrets: opts.secrets,
6359
+ patch,
6360
+ stored,
6361
+ ...scope !== void 0 ? { scope } : {},
6362
+ operatorWrite: opts.isOperatorWrite?.() === true
6363
+ });
6364
+ if (Object.keys(result.patch).length > 0) await rawWrite(result.patch);
6365
+ const { orphaned } = await result.afterWrite();
6366
+ if (orphaned.length > 0) opts.onOrphaned(orphaned);
6367
+ });
6368
+ };
6369
+ const wrapped = {
6370
+ writeAddonStore: (patch) => write("addon", opts.addonTree(), void 0, patch, () => readStoredAddonBlob(raw), (p) => raw.writeAddonStore(p)),
6371
+ writeDeviceStore: (deviceId, patch) => write(`device:${deviceId}`, opts.deviceTree(), {
6372
+ kind: "device",
6373
+ deviceId
6374
+ }, patch, () => raw.readDeviceStore(deviceId), (p) => raw.writeDeviceStore(deviceId, p))
6375
+ };
6376
+ return new Proxy(raw, { get(target, prop, receiver) {
6377
+ if (prop === "writeAddonStore") return wrapped.writeAddonStore;
6378
+ if (prop === "writeDeviceStore") return wrapped.writeDeviceStore;
6379
+ const value = Reflect.get(target, prop, receiver);
6380
+ return typeof value === "function" ? value.bind(target) : value;
6381
+ } });
6382
+ }
6383
+ /** The context the addon sees: identical, except that its settings writes are secret-aware. */
6384
+ function withSecretAwareSettings(context, opts) {
6385
+ const raw = context.settings;
6386
+ if (raw === void 0) return context;
6387
+ const settings = createSecretAwareSettings(raw, opts);
6388
+ return new Proxy(context, { get(target, prop, receiver) {
6389
+ if (prop === "settings") return settings;
6390
+ return Reflect.get(target, prop, receiver);
6391
+ } });
6392
+ }
6393
+ var LEAF = { kind: "leaf" };
6394
+ var DEFAULT_ITEM_ID_KEY = "id";
6395
+ function collectFields(fields, out) {
6396
+ for (const field of fields) {
6397
+ if (field.type === "group") {
6398
+ collectFields(field.fields, out);
6399
+ continue;
6400
+ }
6401
+ if (field.type === "sub-tabs") {
6402
+ for (const tab of field.tabs) collectFields(tab.fields, out);
6403
+ continue;
6404
+ }
6405
+ if (field.type === "editable-array") {
6406
+ const item = /* @__PURE__ */ new Map();
6407
+ for (const itemField of field.itemFields) if ("key" in itemField && isKeychainStoredField(itemField)) item.set(itemField.key, LEAF);
6408
+ if (item.size > 0) out.set(field.key, {
6409
+ kind: "array",
6410
+ idKey: field.itemIdKey ?? DEFAULT_ITEM_ID_KEY,
6411
+ item
6412
+ });
6413
+ continue;
6414
+ }
6415
+ if (!("key" in field) || typeof field.key !== "string") continue;
6416
+ if (isKeychainStoredField(field)) out.set(field.key, LEAF);
6417
+ }
6418
+ }
6419
+ /** The secret tree a settings schema declares. */
6420
+ function secretTreeFromSchema(schema) {
6421
+ const out = /* @__PURE__ */ new Map();
6422
+ if (schema === null) return out;
6423
+ for (const section of schema.sections) collectFields(section.fields, out);
6424
+ return out;
6425
+ }
6426
+ /** Parse `a.b[id].c` into segments; throws on a malformed path (a declaration bug). */
6427
+ function parseSecretStatePath(path) {
6428
+ const segments = [];
6429
+ for (const raw of path.split(".")) {
6430
+ const m = /^([^[\]]+)(?:\[([^[\]]+)\])?$/.exec(raw);
6431
+ if (m === null || m[1] === void 0) throw new Error(`secretStatePaths: malformed path "${path}"`);
6432
+ segments.push(m[2] !== void 0 ? {
6433
+ key: m[1],
6434
+ idKey: m[2]
6435
+ } : { key: m[1] });
6436
+ }
6437
+ if (segments.length === 0) throw new Error(`secretStatePaths: empty path`);
6438
+ if (segments[segments.length - 1]?.idKey !== void 0) throw new Error(`secretStatePaths: "${path}" ends on an array — name the member that is secret`);
6439
+ return segments;
6440
+ }
6441
+ function insert(into, segments) {
6442
+ const [head, ...rest] = segments;
6443
+ if (head === void 0) return;
6444
+ if (rest.length === 0) {
6445
+ into.set(head.key, LEAF);
6446
+ return;
6447
+ }
6448
+ const existing = into.get(head.key);
6449
+ if (head.idKey !== void 0) {
6450
+ const item = new Map(existing?.kind === "array" ? existing.item : []);
6451
+ insert(item, rest);
6452
+ into.set(head.key, {
6453
+ kind: "array",
6454
+ idKey: head.idKey,
6455
+ item
6456
+ });
6457
+ return;
6458
+ }
6459
+ const children = new Map(existing?.kind === "object" ? existing.children : []);
6460
+ insert(children, rest);
6461
+ into.set(head.key, {
6462
+ kind: "object",
6463
+ children
6464
+ });
6465
+ }
6466
+ /** Add hidden-state paths (`secretStatePaths`) to a schema-derived tree. */
6467
+ function mergeSecretStatePaths(tree, paths) {
6468
+ if (paths.length === 0) return tree;
6469
+ const out = new Map(tree);
6470
+ for (const path of paths) insert(out, parseSecretStatePath(path));
6471
+ return out;
6472
+ }
6473
+ /** First retry of a secret the keychain could not serve; doubles to {@link SECRET_RETRY_MAX_MS}. */
6474
+ var SECRET_RETRY_FIRST_MS = 5e3;
6475
+ var SECRET_RETRY_MAX_MS = 5 * 6e4;
6476
+ /** Coalesces a burst of `keychain.changed` events into one re-resolve. */
6477
+ var SECRET_CHANGE_DEBOUNCE_MS = 250;
5951
6478
  /**
5952
6479
  * Base class for CamStack addons. Eliminates settings boilerplate:
5953
6480
  *
@@ -6017,6 +6544,25 @@ var BaseAddon = class {
6017
6544
  settingsStoreReady = false;
6018
6545
  /** Default config values. Provided via constructor. */
6019
6546
  defaults;
6547
+ /**
6548
+ * Hidden-state paths that hold secrets (D787) — values the addon writes
6549
+ * itself (`updateGlobalSettings`, `state()`) that no form field declares.
6550
+ * Stored in the keychain exactly like a `password` field. Syntax:
6551
+ * `'snapshotSigningKey'`, `'identity.pincode'`, `'tokens[userId].accessToken'`
6552
+ * (`key[<id field>]` addresses array items by id, never by index).
6553
+ */
6554
+ secretStatePaths = [];
6555
+ _addonSecretTree = null;
6556
+ _deviceSecretTree = null;
6557
+ /** Refs the last resolved config pointed at — a change to one re-resolves. */
6558
+ _configSecretRefs = /* @__PURE__ */ new Set();
6559
+ _unresolvedSecrets = [];
6560
+ _secretRetryTimer = null;
6561
+ _secretRetryDelayMs = SECRET_RETRY_FIRST_MS;
6562
+ _secretChangeTimer = null;
6563
+ _secretChangeUnsub = null;
6564
+ /** >0 while an OPERATOR's settings write is being applied. */
6565
+ _operatorWriteDepth = 0;
6020
6566
  constructor(defaults) {
6021
6567
  this.defaults = defaults;
6022
6568
  this._config = { ...defaults };
@@ -6052,8 +6598,18 @@ var BaseAddon = class {
6052
6598
  return this._config;
6053
6599
  }
6054
6600
  async initialize(context) {
6055
- this._ctx = context;
6601
+ const secrets = context.secrets;
6602
+ this._ctx = secrets !== void 0 ? withSecretAwareSettings(context, {
6603
+ secrets,
6604
+ addonTree: () => this.addonSecretTree(),
6605
+ deviceTree: () => this.deviceSecretTree(),
6606
+ onOrphaned: (refs) => context.logger.warn("keychain refs dropped by a settings write could not be deleted", { meta: { refs: [...refs] } }),
6607
+ isOperatorWrite: () => this._operatorWriteDepth > 0
6608
+ }) : context;
6056
6609
  await this.resolveConfig();
6610
+ if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
6611
+ if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
6612
+ });
6057
6613
  const result = await this.onInitialize();
6058
6614
  this.emitLifecycle(EventCategory.AddonStarted);
6059
6615
  const normalized = normalizeAddonInitResult(result);
@@ -6086,6 +6642,12 @@ var BaseAddon = class {
6086
6642
  */
6087
6643
  async onHubReachable() {}
6088
6644
  async shutdown() {
6645
+ this._secretChangeUnsub?.();
6646
+ this._secretChangeUnsub = null;
6647
+ if (this._secretRetryTimer !== null) clearTimeout(this._secretRetryTimer);
6648
+ if (this._secretChangeTimer !== null) clearTimeout(this._secretChangeTimer);
6649
+ this._secretRetryTimer = null;
6650
+ this._secretChangeTimer = null;
6089
6651
  this.emitLifecycle(EventCategory.AddonStopped);
6090
6652
  if (this.autoEmitReadiness) this.emitReadinessForProviders("down");
6091
6653
  await this.onShutdown();
@@ -6164,7 +6726,7 @@ var BaseAddon = class {
6164
6726
  return hydrateSchema(schema, overlay ? {
6165
6727
  ...projected,
6166
6728
  ...overlay
6167
- } : projected);
6729
+ } : projected, { inlineSecretsAsViews: true });
6168
6730
  }
6169
6731
  /**
6170
6732
  * The integration-level view of this addon's settings: exactly the sections
@@ -6213,7 +6775,7 @@ var BaseAddon = class {
6213
6775
  return hydrateSchema({
6214
6776
  ...schema,
6215
6777
  sections
6216
- }, projected);
6778
+ }, projected, { inlineSecretsAsViews: true });
6217
6779
  }
6218
6780
  /**
6219
6781
  * The raw addon store PROJECTED onto the target node's bare per-node keys:
@@ -6267,18 +6829,50 @@ var BaseAddon = class {
6267
6829
  value: result.value[key]
6268
6830
  };
6269
6831
  }
6832
+ /**
6833
+ * The OPERATOR's write — a settings form or an admin API call, dispatched by
6834
+ * the kernel. Identical to {@link updateGlobalSettings}, except that a secret
6835
+ * the operator sends is stored as sent even when it equals a pre-keychain
6836
+ * inline value: the operator re-entering a password is a change, an addon
6837
+ * writing back what it read is not (D787).
6838
+ */
6839
+ async updateGlobalSettingsAsOperator(patch, nodeId) {
6840
+ this._operatorWriteDepth++;
6841
+ try {
6842
+ await this.applyGlobalPatch(patch, nodeId);
6843
+ } finally {
6844
+ this._operatorWriteDepth--;
6845
+ }
6846
+ }
6270
6847
  async updateGlobalSettings(patch, nodeId) {
6848
+ await this.applyGlobalPatch(patch, nodeId);
6849
+ }
6850
+ /**
6851
+ * REMOVE stored secrets — their keychain values and their markers (D787).
6852
+ *
6853
+ * Writing `''` to a secret field KEEPS what is stored (an empty string is
6854
+ * what a defaults fallback writes back, and it must never destroy a
6855
+ * credential), so code that means "forget this token" says so here. Without
6856
+ * a keychain (an older kernel) it writes `''`, as such code always did.
6857
+ */
6858
+ async clearSecrets(keys, nodeId) {
6859
+ const tree = this.addonSecretTree();
6860
+ const keychain = this._ctx?.secrets !== void 0;
6861
+ const patch = {};
6862
+ for (const key of keys) patch[key] = keychain && tree.has(key) ? SECRET_CLEAR : "";
6863
+ await this.applyGlobalPatch(patch, nodeId);
6864
+ }
6865
+ async applyGlobalPatch(barePatch, nodeId) {
6271
6866
  const keys = this.perNodeKeys();
6272
6867
  const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
6273
6868
  const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
6274
- const barePatch = patch;
6275
6869
  const scoped = keys.size > 0 ? scopePatch(barePatch, keys, target) : barePatch;
6276
6870
  await this._ctx?.settings?.writeAddonStore(scoped);
6277
6871
  if (target !== localNode) return;
6278
6872
  await this.resolveConfig();
6279
6873
  await this.onConfigChanged();
6280
6874
  this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
6281
- this.maybeAutoRestart(patch, this.globalSettingsSchema());
6875
+ this.maybeAutoRestart(Object.keys(barePatch), this.globalSettingsSchema());
6282
6876
  }
6283
6877
  /**
6284
6878
  * The set of field keys the global settings schema declares `perNode: true`
@@ -6303,7 +6897,7 @@ var BaseAddon = class {
6303
6897
  * time to return its response before the addon is torn down and
6304
6898
  * re-initialised by `AddonRegistryService.restartAddon`.
6305
6899
  */
6306
- maybeAutoRestart(patch, schema) {
6900
+ maybeAutoRestart(patchKeys, schema) {
6307
6901
  if (!schema) return;
6308
6902
  const restartKeys = /* @__PURE__ */ new Set();
6309
6903
  for (const section of schema.sections) for (const field of section.fields) {
@@ -6311,13 +6905,13 @@ var BaseAddon = class {
6311
6905
  if (field.requiresRestart) restartKeys.add(field.key);
6312
6906
  }
6313
6907
  if (restartKeys.size === 0) return;
6314
- if (!Object.keys(patch).some((k) => restartKeys.has(k))) return;
6908
+ if (!patchKeys.some((k) => restartKeys.has(k))) return;
6315
6909
  const ctx = this._ctx;
6316
6910
  if (!ctx) return;
6317
6911
  const addonId = ctx.id;
6318
6912
  setImmediate(() => {
6319
6913
  ctx.api.addons?.restartAddon?.mutate({ addonId }).then(() => {
6320
- ctx.logger.info("addon auto-restart triggered by restart-required setting change", { meta: { changedFields: Object.keys(patch).filter((k) => restartKeys.has(k)) } });
6914
+ ctx.logger.info("addon auto-restart triggered by restart-required setting change", { meta: { changedFields: patchKeys.filter((k) => restartKeys.has(k)) } });
6321
6915
  }).catch((err) => {
6322
6916
  ctx.logger.error("addon auto-restart failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
6323
6917
  });
@@ -6326,7 +6920,7 @@ var BaseAddon = class {
6326
6920
  async getDeviceSettings(deviceId) {
6327
6921
  const schema = this.deviceSettingsSchema();
6328
6922
  if (!schema) return { sections: [] };
6329
- return hydrateSchema(schema, await this._ctx?.settings?.readDeviceStore(deviceId) ?? {});
6923
+ return hydrateSchema(schema, await this._ctx?.settings?.readDeviceStore(deviceId) ?? {}, { inlineSecretsAsViews: true });
6330
6924
  }
6331
6925
  async updateDeviceSettings(deviceId, patch) {
6332
6926
  await this._ctx?.settings?.writeDeviceStore(deviceId, patch);
@@ -6456,7 +7050,7 @@ var BaseAddon = class {
6456
7050
  * selection at boot (absent scoped key ⇒ the constructor default wins).
6457
7051
  */
6458
7052
  async resolveConfig() {
6459
- const stored = await this.readAddonStoreWithRetry();
7053
+ const stored = await this.resolveStoredSecrets(await this.readAddonStoreWithRetry());
6460
7054
  const perNode = this.perNodeKeys();
6461
7055
  const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
6462
7056
  const resolved = { ...this.defaults };
@@ -6481,7 +7075,7 @@ var BaseAddon = class {
6481
7075
  key,
6482
7076
  schema,
6483
7077
  fallback,
6484
- read: () => this.readAddonStoreWithRetry(),
7078
+ read: async () => this.resolveStoredSecrets(await this.readAddonStoreWithRetry()),
6485
7079
  write: async (patch) => {
6486
7080
  await this._ctx?.settings?.writeAddonStore(patch);
6487
7081
  },
@@ -6498,7 +7092,7 @@ var BaseAddon = class {
6498
7092
  key,
6499
7093
  schema,
6500
7094
  fallback,
6501
- read: async () => await this._ctx?.settings?.readDeviceStore(deviceId) ?? {},
7095
+ read: async () => this.resolveStoredSecrets(await this._ctx?.settings?.readDeviceStore(deviceId) ?? {}, false),
6502
7096
  write: async (patch) => {
6503
7097
  await this._ctx?.settings?.writeDeviceStore(deviceId, patch);
6504
7098
  },
@@ -6510,6 +7104,70 @@ var BaseAddon = class {
6510
7104
  } })
6511
7105
  });
6512
7106
  }
7107
+ /** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
7108
+ addonSecretTree() {
7109
+ this._addonSecretTree ??= mergeSecretStatePaths(secretTreeFromSchema(this.globalSettingsSchema()), this.secretStatePaths);
7110
+ return this._addonSecretTree;
7111
+ }
7112
+ deviceSecretTree() {
7113
+ this._deviceSecretTree ??= secretTreeFromSchema(this.deviceSettingsSchema());
7114
+ return this._deviceSecretTree;
7115
+ }
7116
+ /**
7117
+ * Secrets of this addon's stored config that could not be resolved on the
7118
+ * last read, with why. Non-empty means the addon is running WITHOUT them —
7119
+ * an addon whose function needs one should report itself degraded rather
7120
+ * than start on an empty credential.
7121
+ */
7122
+ get unresolvedSecrets() {
7123
+ return this._unresolvedSecrets;
7124
+ }
7125
+ /**
7126
+ * Substitute every keychain marker in a stored blob with its value. With no
7127
+ * keychain (no `ctx.secrets`) or no marker, the blob comes back unchanged.
7128
+ * An unresolvable value is LEFT OUT (never `''`), logged by ref — never by
7129
+ * value — and, when the cause can pass, retried with backoff. Nothing is
7130
+ * ever written back from here.
7131
+ */
7132
+ async resolveStoredSecrets(blob, trackForConfig = true) {
7133
+ const secrets = this._ctx?.secrets;
7134
+ if (secrets === void 0) return blob;
7135
+ const refs = collectSecretMarkerRefs(blob);
7136
+ if (trackForConfig) this._configSecretRefs = new Set(refs);
7137
+ if (refs.length === 0) {
7138
+ if (trackForConfig) this._unresolvedSecrets = [];
7139
+ return blob;
7140
+ }
7141
+ const { value, unresolved } = await resolveSecretMarkers(blob, secrets);
7142
+ if (trackForConfig) {
7143
+ this._unresolvedSecrets = unresolved;
7144
+ if (unresolved.length > 0) {
7145
+ this._ctx?.logger?.error?.("secret unavailable — running without it, will retry", { meta: { secrets: unresolved.map((u) => ({
7146
+ ref: u.ref,
7147
+ reason: u.reason
7148
+ })) } });
7149
+ if (unresolved.some((u) => u.reason !== "missing" && u.reason !== "forbidden")) {
7150
+ this.scheduleSecretReresolve(this._secretRetryDelayMs);
7151
+ this._secretRetryDelayMs = Math.min(this._secretRetryDelayMs * 2, SECRET_RETRY_MAX_MS);
7152
+ }
7153
+ } else this._secretRetryDelayMs = SECRET_RETRY_FIRST_MS;
7154
+ }
7155
+ return value;
7156
+ }
7157
+ /** Re-resolve the config (a keychain-only change, or a retry) and apply it. */
7158
+ scheduleSecretReresolve(delayMs) {
7159
+ const timerOf = delayMs === SECRET_CHANGE_DEBOUNCE_MS ? "_secretChangeTimer" : "_secretRetryTimer";
7160
+ if (this[timerOf] !== null) return;
7161
+ const timer = setTimeout(() => {
7162
+ this[timerOf] = null;
7163
+ if (this._ctx === null) return;
7164
+ this.resolveConfig().then(() => this.onConfigChanged()).catch((err) => {
7165
+ this._ctx?.logger?.error?.("re-resolving secrets failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
7166
+ });
7167
+ }, delayMs);
7168
+ timer.unref?.();
7169
+ this[timerOf] = timer;
7170
+ }
6513
7171
  /**
6514
7172
  * Wrap `ctx.settings.readAddonStore()` with a short retry budget so a
6515
7173
  * transient settings-store outage (mid-restart of sqlite-settings, tsx-watch
@@ -6777,32 +7435,12 @@ object({
6777
7435
  audioCodec: string().optional(),
6778
7436
  metadata: record(string(), unknown()).readonly().optional()
6779
7437
  });
6780
- var EncodedPacketSchema = object({
6781
- type: _enum(["video", "audio"]),
6782
- data: _instanceof(Uint8Array),
6783
- pts: number(),
6784
- dts: number(),
6785
- keyframe: boolean(),
6786
- codec: string()
6787
- });
6788
- var DecodedFrameSchema = object({
6789
- data: _instanceof(Uint8Array),
6790
- width: number(),
6791
- height: number(),
6792
- format: _enum([
6793
- "jpeg",
6794
- "rgb",
6795
- "bgr",
6796
- "yuv420",
6797
- "gray"
6798
- ]),
6799
- timestamp: number()
6800
- });
6801
7438
  /**
6802
7439
  * Wire schema for `FrameHandle` — a zero-pixel, serialisable reference to a
6803
7440
  * frame in a shared-memory ring (Phase 5 / D9). Mirrors the `FrameHandle` TS
6804
- * interface in `interfaces/frame-handle.ts` field-for-field so a decoder cap
6805
- * method (`pullHandles`) can carry handles over tRPC / Moleculer.
7441
+ * interface in `interfaces/frame-handle.ts` field-for-field so a cap method
7442
+ * (the runner's native-crop and pipeline-executor inputs) can carry handles
7443
+ * over tRPC / Moleculer.
6806
7444
  *
6807
7445
  * The `satisfies` assertion below pins this schema to the interface: if a
6808
7446
  * field is added to / removed from `FrameHandle` without a matching schema
@@ -6828,50 +7466,6 @@ var FrameHandleSchema = object({
6828
7466
  slotCount: number().int().positive()
6829
7467
  });
6830
7468
  /**
6831
- * Pixel format a frame-handle subscriber can request from the broker
6832
- * (Phase 5 / D9). The packed, raster subset of `FrameFormat` — the formats a
6833
- * `frameSink: 'shm'` decoder session can write into a `FrameRing` slot.
6834
- * `jpeg` is excluded: a variable-length stream is not a fixed-stride ring
6835
- * payload, and no decoded-frame consumer requests it over the shm plane.
6836
- */
6837
- var FrameHandleFormatSchema = _enum([
6838
- "rgb",
6839
- "bgr",
6840
- "yuv420",
6841
- "gray"
6842
- ]);
6843
- /**
6844
- * Input for `stream-broker.subscribeFrames` (Phase 5 / D9). A consumer asks the
6845
- * broker for a `FrameHandle` stream of one `format`; the broker maintains one
6846
- * shared-memory ring per `(brokerId, format)` actually requested, so a `gray`
6847
- * subscriber (motion) and an `rgb` subscriber (detection) each read the format
6848
- * they asked for with no broker-side conversion.
6849
- */
6850
- var SubscribeFramesInputSchema = object({
6851
- brokerId: string(),
6852
- format: FrameHandleFormatSchema,
6853
- /**
6854
- * Optional reader-side cadence hint in frames per second. The broker does
6855
- * NOT throttle — latest-wins ring reads drop frames implicitly for a slow
6856
- * consumer. The value is echoed back in `SubscribeFramesResult.maxFps` so
6857
- * the consumer can pace its own `pullFrameHandles` polling.
6858
- */
6859
- maxFps: number().positive().optional(),
6860
- /** Short caller-identity tag (`motion`, `detection`, …) for diagnostics. */
6861
- tag: string().optional()
6862
- });
6863
- /**
6864
- * Result of `stream-broker.subscribeFrames`. The consumer then polls
6865
- * `pullFrameHandles({ subscriptionId, maxCount })` and feeds each returned
6866
- * `FrameHandle` to a `FrameRingReader`.
6867
- */
6868
- var SubscribeFramesResultSchema = object({
6869
- /** Opaque id the consumer passes to `pullFrameHandles` / `unsubscribeFrames`. */
6870
- subscriptionId: string(),
6871
- /** Reader-side cadence hint (frames/s) — echoes `SubscribeFramesInput.maxFps`. */
6872
- maxFps: number().nonnegative()
6873
- });
6874
- /**
6875
7469
  * Wire schema for a decoded audio chunk (Phase 5 / D9). Mirrors the
6876
7470
  * `DecodedAudioChunk` TS interface in `interfaces/stream-broker.ts`: PCM
6877
7471
  * sample bytes plus the track parameters a consumer needs to interpret them.
@@ -6879,8 +7473,8 @@ var SubscribeFramesResultSchema = object({
6879
7473
  * Audio chunks are tiny (a ~500ms AudioCodecSession window is a few KB of
6880
7474
  * Float32 PCM), so unlike video frames they ship their bytes INLINE over
6881
7475
  * tRPC / Moleculer — no shared-memory plane. `data` is typed `Uint8Array`
6882
- * (the wire-serialisable supertype of `Buffer`) to match `DecodedFrameSchema`
6883
- * / `EncodedPacketSchema`'s precedent; a `Buffer` is assignable to it.
7476
+ * (the wire-serialisable supertype of `Buffer`); a `Buffer` is assignable to
7477
+ * it.
6884
7478
  */
6885
7479
  var AudioChunkFormatSchema = _enum(AUDIO_CHUNK_FORMATS);
6886
7480
  var DecodedAudioChunkSchema = object({
@@ -6898,11 +7492,10 @@ var DecodedAudioChunkSchema = object({
6898
7492
  format: AudioChunkFormatSchema.optional()
6899
7493
  });
6900
7494
  /**
6901
- * Input for `stream-broker.subscribeAudioChunks` (Phase 5 / D9). The
6902
- * handle-free, ship-bytes-inline analogue of `subscribeFrames`: a consumer
6903
- * asks the broker for the decoded audio-chunk stream of one broker; the
6904
- * broker keeps a per-subscription bounded FIFO queue the consumer drains via
6905
- * `pullAudioChunks`.
7495
+ * Input for `stream-broker.subscribeAudioChunks` (Phase 5 / D9). Audio ships
7496
+ * its bytes inline: a consumer asks the broker for the decoded audio-chunk
7497
+ * stream of one broker; the broker keeps a per-subscription bounded FIFO queue
7498
+ * the consumer drains via `pullAudioChunks`.
6906
7499
  */
6907
7500
  var SubscribeAudioChunksInputSchema = object({
6908
7501
  brokerId: string(),
@@ -6933,9 +7526,7 @@ var BrokerStatsSchema = object({
6933
7526
  "stopped"
6934
7527
  ]),
6935
7528
  inputFps: number(),
6936
- decodeFps: number(),
6937
7529
  encodedSubscribers: number(),
6938
- decodedSubscribers: number(),
6939
7530
  uptimeMs: number(),
6940
7531
  bitrateKbps: number(),
6941
7532
  idrIntervalMs: number(),
@@ -6948,14 +7539,6 @@ var BrokerStatsSchema = object({
6948
7539
  preBufferMs: number(),
6949
7540
  preBufferPackets: number(),
6950
7541
  /**
6951
- * Moleculer node id of the decoder provider currently servicing this
6952
- * stream's decoded subscribers. `null` until the deferred decoder is
6953
- * created (no decoded clients yet, or codec not detected). Surfaces the
6954
- * runtime decoder placement so the UI can show "Decoder: <agent>" without
6955
- * a separate cap call per broker.
6956
- */
6957
- decoderNodeId: string().nullable(),
6958
- /**
6959
7542
  * Detected audio track parameters from the RTSP DESCRIBE / SDP. `null`
6960
7543
  * when the stream has no audio track or the broker is in cold start.
6961
7544
  * `supported = false` means the codec was detected but the local
@@ -7219,6 +7802,69 @@ method(object({ deviceId: number() }), array(StreamSourceEntrySchema)), method(o
7219
7802
  kind: "mutation",
7220
7803
  auth: "admin"
7221
7804
  }), method(object({ deviceId: number() }), unknown().nullable()), method(object({ deviceId: number() }), RawStateResultSchema.nullable(), { auth: "protected" });
7805
+ function defOf(schema) {
7806
+ return schema._zod?.def ?? {};
7807
+ }
7808
+ function declaredHere(schema) {
7809
+ return globalRegistry.get(schema)?.secret === true;
7810
+ }
7811
+ /** Wrapper kinds whose `innerType` is the field itself. */
7812
+ var WRAPPERS = new Set([
7813
+ "optional",
7814
+ "nullable",
7815
+ "default",
7816
+ "prefault",
7817
+ "catch",
7818
+ "readonly",
7819
+ "nonoptional"
7820
+ ]);
7821
+ /**
7822
+ * The schema layers that make up ONE field: the schema itself and every
7823
+ * wrapper below it (optional / default / nullable / readonly / catch), plus
7824
+ * both sides of a pipe (`z.preprocess` puts the real schema on `out`,
7825
+ * `.transform` on `in`).
7826
+ */
7827
+ function layersOf(schema) {
7828
+ const out = [];
7829
+ const queue = [schema];
7830
+ while (queue.length > 0) {
7831
+ const s = queue.shift();
7832
+ if (!s || out.includes(s)) continue;
7833
+ out.push(s);
7834
+ const def = defOf(s);
7835
+ if (def.type !== void 0 && WRAPPERS.has(def.type) && def.innerType) queue.push(def.innerType);
7836
+ if (def.type === "pipe") {
7837
+ if (def.in) queue.push(def.in);
7838
+ if (def.out) queue.push(def.out);
7839
+ }
7840
+ }
7841
+ return out;
7842
+ }
7843
+ /** True when any layer of this field declares `.meta({ secret: true })`. */
7844
+ function isZodSecret(schema) {
7845
+ return layersOf(schema).some(declaredHere);
7846
+ }
7847
+ /**
7848
+ * Every declared secret path in a config schema: dotted keys, `[]` for "each
7849
+ * element of this array" (`connection.password`, `rows[].token`). A declared
7850
+ * object or array is reported as one path and not descended into — everything
7851
+ * under it is secret.
7852
+ */
7853
+ function collectZodSecretPaths(schema, prefix = "") {
7854
+ if (isZodSecret(schema)) return prefix.length > 0 ? [prefix] : [];
7855
+ const paths = [];
7856
+ for (const layer of layersOf(schema)) {
7857
+ const def = defOf(layer);
7858
+ if (def.type === "object" && def.shape) for (const [key, child] of Object.entries(def.shape)) paths.push(...collectZodSecretPaths(child, prefix ? `${prefix}.${key}` : key));
7859
+ else if (def.type === "array" && def.element) paths.push(...collectZodSecretPaths(def.element, `${prefix}[]`));
7860
+ else if (def.type === "union" && def.options) for (const option of def.options) paths.push(...collectZodSecretPaths(option, prefix));
7861
+ else if (def.type === "intersection") {
7862
+ if (def.left) paths.push(...collectZodSecretPaths(def.left, prefix));
7863
+ if (def.right) paths.push(...collectZodSecretPaths(def.right, prefix));
7864
+ }
7865
+ }
7866
+ return [...new Set(paths)];
7867
+ }
7222
7868
  //#endregion
7223
7869
  //#region ../types/dist/err-msg-DX6i_MY4.mjs
7224
7870
  var DeviceType = /* @__PURE__ */ function(DeviceType) {
@@ -10382,70 +11028,6 @@ var StorageLocationDeclarationSchema = object({
10382
11028
  "backup"
10383
11029
  ]).optional()
10384
11030
  });
10385
- var DecoderStatsSchema = object({
10386
- inputFps: number(),
10387
- outputFps: number(),
10388
- avgDecodeTimeMs: number(),
10389
- droppedFrames: number(),
10390
- /**
10391
- * Pull-mode adaptive-fps telemetry (optional — only pull sessions run the
10392
- * lag-driven controller; push sessions omit these). `lagMs` is the EWMA of
10393
- * the decoder's real-time drift (rising = falling behind live); `adaptiveFps`
10394
- * is the current lag-throttled emit rate (≤ `effectiveFps` ceiling).
10395
- */
10396
- lagMs: number().optional(),
10397
- effectiveFps: number().optional(),
10398
- adaptiveFps: number().optional()
10399
- });
10400
- var DecoderSessionConfigSchema = object({
10401
- codec: string(),
10402
- maxFps: number().default(0),
10403
- outputFormat: _enum([
10404
- "jpeg",
10405
- "rgb",
10406
- "bgr",
10407
- "yuv420",
10408
- "gray"
10409
- ]).default("jpeg"),
10410
- scale: number().default(1),
10411
- width: number().optional(),
10412
- height: number().optional(),
10413
- /**
10414
- * Identifier of the camera this decoder session serves. Optional
10415
- * because the cap is generic (any caller could request decode), but
10416
- * stream-broker passes it so decoder logs include `deviceId` for
10417
- * per-camera filtering when diagnosing failures (e.g. node-av
10418
- * sendPacket errors on a single hung camera).
10419
- */
10420
- deviceId: number().int().nonnegative().optional(),
10421
- /**
10422
- * Free-form tag for log scoping. Stream-broker uses
10423
- * `broker:<deviceId>/<profile>`. Decoder session logger surfaces it
10424
- * on every line so `grep tag=broker:5/high` filters one camera
10425
- * profile cleanly.
10426
- */
10427
- tag: string().optional(),
10428
- /**
10429
- * Where the session delivers decoded frames (Phase 5 / D9):
10430
- *
10431
- * - `'callback'` (default) — the legacy pixel path: decoded frames are
10432
- * buffered as `DecodedFrame`s and drained via `pullFrames`.
10433
- * - `'shm'` — the shared-memory frame plane: decoded frames are written
10434
- * into an OS shared-memory ring and drained as zero-pixel
10435
- * `FrameHandle`s via `pullHandles`. A session is one mode or the
10436
- * other — `pullFrames` returns nothing for an `'shm'` session and
10437
- * `pullHandles` returns nothing for a `'callback'` session.
10438
- */
10439
- frameSink: _enum(["callback", "shm"]).default("callback"),
10440
- /**
10441
- * Per-camera decoder DEBUG facility. When `true`, a pull-mode session emits
10442
- * a throttled (~1Hz) structured `decoder debug` line (effective/adaptive fps,
10443
- * real-time lag, dropped-frame delta, avg decode time, hwaccel). Mirrors the
10444
- * stream-broker's `streamingDebug` gate — off by default so production logs
10445
- * stay quiet and the emit path pays zero per-frame cost when disabled.
10446
- */
10447
- debug: boolean().optional()
10448
- });
10449
11031
  /**
10450
11032
  * Per-component log CHANNELS — the gate a hot path consults, and the registry
10451
11033
  * an addon declares its channels in.
@@ -10588,6 +11170,26 @@ var ClipSearchStrictnessSchema = _enum([
10588
11170
  "balanced",
10589
11171
  "strict"
10590
11172
  ]);
11173
+ /** Steps whose effect a root label can BE. */
11174
+ var LABEL_SUPPLY_STEP_IDS = [
11175
+ "animal-classifier",
11176
+ "vehicle-classifier",
11177
+ "package-detection"
11178
+ ];
11179
+ /** Steps whose box a root detection can BE. */
11180
+ var CROP_SUPPLY_STEP_IDS = ["plate-detection", "face-detection"];
11181
+ /**
11182
+ * Steps whose per-detection MASK a root detection can carry (D737). The live
11183
+ * mask step is `segmentation` — a frame-plane refiner that runs its own model
11184
+ * once per detection crop. (`instance-segmentation` is a dormant step id that
11185
+ * no camera tree carries.)
11186
+ */
11187
+ var MASK_SUPPLY_STEP_IDS = ["segmentation"];
11188
+ [
11189
+ ...LABEL_SUPPLY_STEP_IDS,
11190
+ ...CROP_SUPPLY_STEP_IDS,
11191
+ ...MASK_SUPPLY_STEP_IDS
11192
+ ];
10591
11193
  var MODEL_FORMATS = [
10592
11194
  "onnx",
10593
11195
  "coreml",
@@ -10616,7 +11218,53 @@ var ModelFormatEntrySchema = object({
10616
11218
  /** Multi-file payload (directory members or sibling files). */
10617
11219
  files: array(string()).readonly().optional(),
10618
11220
  /** Runtime(s) that can use this format. If omitted, inferred from ModelFormat key */
10619
- runtimes: array(_enum(["python"])).readonly().optional()
11221
+ runtimes: array(_enum(["python"])).readonly().optional(),
11222
+ /**
11223
+ * Content revision of the artefact, for one hosted OFF the CamStack mirror.
11224
+ * A format on the mirror leaves it unset: its revision (the sha256 of its
11225
+ * weight file) comes from the downloader's generated table, kept in step
11226
+ * with the mirror by `scripts/check-hosted-model-revisions.ts` (D744). A
11227
+ * node records the revision it downloaded next to the file
11228
+ * (`<file>.revision`); a copy with no record or a different one is stale
11229
+ * unless its weight file hashes to the revision, and a stale copy is
11230
+ * downloaded again, the old one serving until the new one is complete. No
11231
+ * revision at all ⇒ a file on disk is current, whatever it holds (D743).
11232
+ */
11233
+ revision: string().min(1).optional()
11234
+ });
11235
+ /**
11236
+ * What a node holds for one (model, format) (D746). `current` is a complete
11237
+ * copy at its catalog revision — exactly what `downloaded: true` means.
11238
+ * `stale` is bytes on disk that are not (yet) proven current under D743/D744:
11239
+ * no or a different revision record, or a missing sibling. `missing` is no
11240
+ * copy. `downloaded` alone folds `stale` into "not downloaded", which hid
11241
+ * reclaimable bytes and a copy about to be replaced.
11242
+ */
11243
+ var ModelOnDiskStateSchema = _enum([
11244
+ "current",
11245
+ "stale",
11246
+ "missing"
11247
+ ]);
11248
+ /**
11249
+ * The numeric precision of one format's BUILD (D746) — reported only when the
11250
+ * build states it (file-name token, an INT8 variant, an EdgeTPU compile, a
11251
+ * CamStack Core ML export). Absent means "not stated", never "fp32".
11252
+ */
11253
+ var ModelFormatPrecisionSchema = _enum([
11254
+ "fp32",
11255
+ "fp16",
11256
+ "int8"
11257
+ ]);
11258
+ /**
11259
+ * One format of one model as a node's `pipelineExecutor.getSchema` reports it.
11260
+ * `onDisk` / `precision` are optional for version skew: a runner that predates
11261
+ * them omits them, and a reader falls back to `downloaded`.
11262
+ */
11263
+ var PipelineModelFormatStatusSchema = object({
11264
+ downloaded: boolean(),
11265
+ sizeMB: number(),
11266
+ onDisk: ModelOnDiskStateSchema.optional(),
11267
+ precision: ModelFormatPrecisionSchema.optional()
10620
11268
  });
10621
11269
  /**
10622
11270
  * Extra file that must be downloaded alongside the model (e.g., labels JSON, dict.txt).
@@ -10720,6 +11368,46 @@ var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { wei
10720
11368
  /** True when the weights or their data forbid commercial use. */
10721
11369
  nonCommercial: boolean().optional()
10722
11370
  }));
11371
+ /**
11372
+ * One step a ROOT detector can supply from its own output (D730) — see
11373
+ * `ModelCatalogEntry.suppliesSteps`.
11374
+ *
11375
+ * - `label`: the root's raw class IS the step's answer. `labelToEffect` maps
11376
+ * the root's label to the step's vocabulary; `uncovered` is the vocabulary
11377
+ * the root cannot produce (shown as unavailable, never a blocker).
11378
+ * - `crop`: the root draws the box the step would have found (`license_plate`
11379
+ * for plate-detection, `face` for face-detection). The box becomes a detail
11380
+ * child of the `parentMacro` detection that contains it — never a track.
11381
+ * - `mask`: the root's decode emits an instance mask with every detection (a
11382
+ * YOLO26-seg root, D737) — the `segmentation` step's result, carried on the
11383
+ * detection in the shape that step produces. Derived from the model's
11384
+ * postprocessor on read, never stored by an import.
11385
+ */
11386
+ var RootStepSupplySchema = discriminatedUnion("kind", [
11387
+ object({
11388
+ kind: literal("label"),
11389
+ stepId: _enum(LABEL_SUPPLY_STEP_IDS),
11390
+ labelToEffect: record(string(), string()),
11391
+ uncovered: array(string()).readonly()
11392
+ }),
11393
+ object({
11394
+ kind: literal("crop"),
11395
+ stepId: _enum(CROP_SUPPLY_STEP_IDS),
11396
+ rootLabels: array(string()).readonly(),
11397
+ parentMacro: _enum(["vehicle", "person"]),
11398
+ childClass: _enum(["plate", "face"]),
11399
+ /**
11400
+ * The root's smaller input side, stamped on read by `rootSuppliesOf` from
11401
+ * the model entry — what a step's `minRootInputPx` gate judges. Never
11402
+ * stored by an import.
11403
+ */
11404
+ rootInputPx: number().int().positive().optional()
11405
+ }),
11406
+ object({
11407
+ kind: literal("mask"),
11408
+ stepId: _enum(MASK_SUPPLY_STEP_IDS)
11409
+ })
11410
+ ]);
10723
11411
  var ModelCatalogEntrySchema = object({
10724
11412
  id: string(),
10725
11413
  name: string(),
@@ -10785,6 +11473,35 @@ var ModelCatalogEntrySchema = object({
10785
11473
  */
10786
11474
  defaultConfidence: number().min(0).max(1).optional(),
10787
11475
  /**
11476
+ * Per-MODEL default of the per-class confidence floors (`minConfidence<Macro>`
11477
+ * on a detector step), keyed by macro class: `{ person: 0.45 }`. A class
11478
+ * absent here runs at the step slider's global default. The same row shape
11479
+ * the floor measurement produces (`model-floors.json`: modelId → floors), so
11480
+ * a measured table is copied in as-is. An operator override is bound to the
11481
+ * model it was made for and does not survive a model change (D738). A custom
11482
+ * / imported model may declare it; without it, the global defaults apply.
11483
+ */
11484
+ defaultClassFloors: record(string(), number().min(0).max(1)).optional(),
11485
+ /**
11486
+ * The inference precision this model's OpenVINO IR needs on an Intel GPU.
11487
+ * Absent ⇒ the device default (f16 on a GPU). Set to `'f32'` for an FP32 IR
11488
+ * that produces invalid output at f16 — measured 2026-10-07 for YOLO26
11489
+ * base IRs (`yolo26s`: 0 true / 146 false positives; `yolo26m*`: NaN
11490
+ * scores) on the Core Ultra iGPU and the N100. Threaded to the pool as
11491
+ * `PoolModelConfig.gpuInferencePrecision`; the pool sets
11492
+ * INFERENCE_PRECISION_HINT on a GPU only. Costs ~60% latency on the GPU.
11493
+ */
11494
+ gpuInferencePrecision: _enum(["f32", "f16"]).optional(),
11495
+ /**
11496
+ * ROOT detectors only: per supplied step (`animal-classifier`, …), the
11497
+ * minimum DETECTION score from which this root's own fine label (species,
11498
+ * vehicle type — D736 root supply) is taken (D741). Below it the detection
11499
+ * keeps its macro class without the fine label. `1` = never take it from
11500
+ * this root. Absent ⇒ no floor beyond the macro floor. The supplied step's
11501
+ * `minConfidence` slider, bound to the ROOT model, overrides it.
11502
+ */
11503
+ suppliedLabelFloors: record(string(), number().min(0).max(1)).optional(),
11504
+ /**
10788
11505
  * When true, the executor produces a landmark-aligned crop (similarity warp
10789
11506
  * onto the canonical template) before this step runs, instead of a plain
10790
11507
  * axis-aligned bbox crop. Required for face-recognition embedders (ArcFace):
@@ -10846,6 +11563,16 @@ var ModelCatalogEntrySchema = object({
10846
11563
  */
10847
11564
  classMap: DetectionCatalogClassMapSchema.optional(),
10848
11565
  /**
11566
+ * The pipeline steps this ROOT detector can supply from its own output
11567
+ * (D730): its labels name a step's effect (`deer` for `animal-classifier`)
11568
+ * or draw a step's box (`license_plate` for `plate-detection`). Computed at
11569
+ * import from THIS model's labelmap by `deriveRootSupplies`, never written
11570
+ * by hand and never assumed per family; a row without it is derived on read
11571
+ * (`rootSuppliesOf`). Whether a camera uses it is the step's own
11572
+ * `useRootModel` flag (`resolveRootSupply`).
11573
+ */
11574
+ suppliesSteps: array(RootStepSupplySchema).readonly().optional(),
11575
+ /**
10849
11576
  * The model's CLIP feature-space contract (text encoder, dimension, vector
10850
11577
  * index, token window, search floor) — see `clip-model.ts`. Present ONLY on a
10851
11578
  * CLIP image encoder; its absence is what "not a CLIP model" means to the
@@ -12447,13 +13174,6 @@ var BrokerRtspClientSchema = object({
12447
13174
  lastRtpAt: number(),
12448
13175
  bytesSent: number()
12449
13176
  });
12450
- var BrokerDecodedClientSchema = object({
12451
- tag: string(),
12452
- subscribedAt: number(),
12453
- maxFps: number(),
12454
- framesDelivered: number(),
12455
- framesDropped: number()
12456
- });
12457
13177
  var BrokerAudioClientSchema = object({
12458
13178
  tag: string(),
12459
13179
  subscribedAt: number(),
@@ -12545,7 +13265,6 @@ var BrokerEncodedClientSchema = object({
12545
13265
  });
12546
13266
  var BrokerClientsSchema = object({
12547
13267
  rtsp: array(BrokerRtspClientSchema).readonly(),
12548
- decoded: array(BrokerDecodedClientSchema).readonly(),
12549
13268
  audio: array(BrokerAudioClientSchema).readonly(),
12550
13269
  encoded: array(BrokerEncodedClientSchema).readonly(),
12551
13270
  pipeClients: number(),
@@ -12914,11 +13633,7 @@ method(object({
12914
13633
  auth: "admin"
12915
13634
  }), method(object({ brokerId: string() }), BrokerClientsSchema), method(object({
12916
13635
  brokerId: string(),
12917
- channel: _enum([
12918
- "rtsp",
12919
- "decoded",
12920
- "audio"
12921
- ]),
13636
+ channel: _enum(["rtsp", "audio"]),
12922
13637
  handle: string()
12923
13638
  }), object({ killed: boolean() }), {
12924
13639
  kind: "mutation",
@@ -12953,10 +13668,7 @@ method(object({
12953
13668
  }), method(SubscribeAudioChunksInputSchema, SubscribeAudioChunksResultSchema, { kind: "mutation" }), method(object({
12954
13669
  subscriptionId: string(),
12955
13670
  maxCount: number().int().positive().default(8)
12956
- }), array(DecodedAudioChunkSchema).readonly()), method(object({ subscriptionId: string() }), object({ released: boolean() }), { kind: "mutation" }), method(SubscribeFramesInputSchema, SubscribeFramesResultSchema, { kind: "mutation" }), method(object({
12957
- subscriptionId: string(),
12958
- maxCount: number().int().positive().default(4)
12959
- }), array(FrameHandleSchema).readonly()), method(object({ subscriptionId: string() }), object({ released: boolean() }), { kind: "mutation" }), method(object({
13671
+ }), array(DecodedAudioChunkSchema).readonly()), method(object({ subscriptionId: string() }), object({ released: boolean() }), { kind: "mutation" }), method(object({
12960
13672
  brokerId: string(),
12961
13673
  seconds: number().min(0).max(30)
12962
13674
  }), _void(), {
@@ -14216,53 +14928,219 @@ id: union([string(), number()]) }), {
14216
14928
  auth: "admin"
14217
14929
  });
14218
14930
  /**
14219
- * shm ring usage stats for a `frameSink: 'shm'` decoder session —
14220
- * exposed via `decoder.getShmStats` so downstream consumers can
14221
- * observe ring pressure (slot count, byte budget, hit/miss ratio).
14931
+ * `keychain` — the hub's encrypted store of secret values (D787).
14932
+ *
14933
+ * Settings blobs never hold a secret: they hold a marker
14934
+ * `{"$secret":"secret://<owner>/<scope>/<fieldPath>","v":1}` and the value
14935
+ * lives here, AES-256-GCM under a data key that a master key wraps. This cap
14936
+ * is the OWNER's door to its own values: every method is owner-checked
14937
+ * against the CALLER, never against anything the caller claims.
14938
+ *
14939
+ * Who the caller is: the transport stamps `callerAddonId` into the args of a
14940
+ * call that crossed a process (the parent `LocalChildRegistry` from the UDS
14941
+ * connection, the agent from its child's connection carried in
14942
+ * `$hub-cap-fwd`'s envelope). A stamp always overwrites; a wire call with no
14943
+ * stamp has its claim stripped. Only a hub in-process caller may state its own
14944
+ * identity — `ctx.secrets` does that with the addon id closed over. This is
14945
+ * identity against BUGS and misrouting, not against a malicious addon (every
14946
+ * runner is the same OS user): see the threat model in
14947
+ * `docs/design/2026-10-08-secrets-keychain.md`.
14948
+ *
14949
+ * `callerAddonId` is deliberately NOT in any input schema: the tRPC router
14950
+ * parses the input, so a session can never supply it, and the internal mount
14951
+ * floors every method to admin anyway (D240).
14952
+ *
14953
+ * Internal: addons reach it through `ctx.secrets`, never directly.
14222
14954
  */
14223
- var ShmRingStatsSchema = object({
14224
- sessionId: string(),
14225
- slotCount: number().int(),
14226
- slotByteLength: number().int(),
14227
- segmentBytes: number().int(),
14228
- budgetMb: number().int(),
14229
- framesWritten: number().int(),
14230
- getFrameHits: number().int(),
14231
- getFrameMisses: number().int()
14232
- });
14233
- method(object({ codec: string() }), boolean()), method(_void(), object({
14955
+ /** Why a value cannot be served right now. Never folded into "missing". */
14956
+ var KeychainUnavailableReasonSchema = _enum([
14957
+ "key-missing",
14958
+ "wrong-key",
14959
+ "key-file-unreadable",
14960
+ "store-unavailable",
14961
+ "not-initialized",
14962
+ "decrypt-failed"
14963
+ ]);
14964
+ var KeychainStateSchema = discriminatedUnion("state", [object({ state: literal("ready") }), object({
14965
+ state: literal("locked"),
14966
+ reason: KeychainUnavailableReasonSchema
14967
+ })]);
14968
+ var KeychainResolveEntrySchema = discriminatedUnion("kind", [
14969
+ object({
14970
+ kind: literal("value"),
14971
+ value: string(),
14972
+ version: number().int()
14973
+ }),
14974
+ object({ kind: literal("missing") }),
14975
+ object({
14976
+ kind: literal("unavailable"),
14977
+ reason: KeychainUnavailableReasonSchema
14978
+ }),
14979
+ object({ kind: literal("forbidden") })
14980
+ ]);
14981
+ var KeychainResolveResultSchema = record(string(), KeychainResolveEntrySchema);
14982
+ /** Metadata of one stored value — never the value. */
14983
+ var KeychainSecretMetaSchema = object({
14984
+ ref: string(),
14985
+ owner: string(),
14986
+ /** Formatted scope (`global`, `node/<id>`, `device/<id>`, …). */
14987
+ scope: string(),
14988
+ fieldPath: string(),
14989
+ version: number().int(),
14990
+ createdAt: number(),
14991
+ updatedAt: number(),
14992
+ /** `addon:<id>` | `user:<id>` | `system`. */
14993
+ updatedBy: string(),
14994
+ /** Coarse — written at most once an hour per ref. `null` = never read. */
14995
+ lastReadAt: number().nullable()
14996
+ });
14997
+ var RefListSchema = array(string().min(1)).max(512);
14998
+ method(object({
14999
+ ref: string().min(1),
15000
+ value: string().min(1)
15001
+ }), object({
15002
+ ref: string(),
15003
+ version: number().int()
15004
+ }), {
15005
+ kind: "mutation",
15006
+ auth: "admin"
15007
+ }), method(object({ refs: RefListSchema }), KeychainResolveResultSchema, { auth: "admin" }), method(object({ refs: RefListSchema }), object({ deleted: number().int() }), {
15008
+ kind: "mutation",
15009
+ auth: "admin"
15010
+ }), method(object({}), array(KeychainSecretMetaSchema).readonly(), { auth: "admin" }), method(object({}), KeychainStateSchema, { auth: "admin" });
15011
+ var KeychainKeySourceSchema = object({
15012
+ kind: _enum([
15013
+ "config-file",
15014
+ "previous-config-file",
15015
+ "platform",
15016
+ "default-file"
15017
+ ]),
15018
+ /** File sources only — a path is not a secret. */
15019
+ path: string().optional(),
15020
+ /**
15021
+ * True when a whole-volume backup of the data directory carries this key
15022
+ * next to the database it protects (the default file). The page warns.
15023
+ */
15024
+ sharesBackupWithDatabase: boolean()
15025
+ });
15026
+ var KeychainStatusSchema = object({
15027
+ keychain: KeychainStateSchema,
15028
+ /** The source of the master key in use; `null` while locked. */
15029
+ keySource: KeychainKeySourceSchema.nullable(),
15030
+ /** The source configured to be primary — differs from `keySource` while a rotation is pending. */
15031
+ configuredSource: KeychainKeySourceSchema,
15032
+ /** Short fingerprint of the master key in use (`HMAC(KEK, label)`, 16 hex). */
15033
+ fingerprint: string().nullable(),
15034
+ activeKeyId: string().nullable(),
15035
+ /**
15036
+ * The keychain unlocked with a NON-primary source: the configured primary
15037
+ * key is not the one that wraps the data key yet. `rotateMasterKey` moves it.
15038
+ */
15039
+ rotationPending: boolean(),
15040
+ /** Wrapped copies of the data key under a previous master key, kept until purged. */
15041
+ retiredWraps: number().int(),
15042
+ recoveryKeyDownloadedAt: number().nullable(),
15043
+ secretCount: number().int()
15044
+ });
15045
+ /**
15046
+ * Whether a stored value is still pointed at by its owner's settings.
15047
+ * `unknown` is a first-class answer: a scope whose blob could not be read is
15048
+ * never reported as an orphan (and never deleted as one).
15049
+ */
15050
+ var KeychainSecretUsageSchema = _enum([
15051
+ "in-use",
15052
+ "orphan",
15053
+ "unknown"
15054
+ ]);
15055
+ var KeychainAdminSecretSchema = KeychainSecretMetaSchema.extend({ usage: KeychainSecretUsageSchema });
15056
+ var KeychainAuditActionSchema = _enum([
15057
+ "put",
15058
+ "delete",
15059
+ "reveal",
15060
+ "rotate-master-key",
15061
+ "purge-retired-keys",
15062
+ "download-recovery-key",
15063
+ "delete-orphans",
15064
+ "reset",
15065
+ "initialize"
15066
+ ]);
15067
+ var KeychainAuditEntrySchema = object({
15068
+ id: string(),
15069
+ at: number(),
15070
+ action: KeychainAuditActionSchema,
15071
+ /** `addon:<id>` | `user:<id>` | `system`. */
15072
+ actor: string(),
15073
+ ref: string().nullable(),
15074
+ detail: string().nullable()
15075
+ });
15076
+ var KEYCHAIN_RESET_CONFIRMATION = "RESET KEYCHAIN";
15077
+ /**
15078
+ * Secrets the HUB itself holds (owner `system`) that no settings form shows:
15079
+ * `jwt-signing-key` — the session signing key (D797). It is always "in use",
15080
+ * never an orphan; it is generated on a fresh hub and replaced only through
15081
+ * {@link keychainAdminCapability.methods.setSystemSecret}.
15082
+ */
15083
+ var SystemHeldSecretNameSchema = _enum(["jwt-signing-key"]);
15084
+ method(object({}), KeychainStatusSchema, { auth: "admin" }), method(object({}), array(KeychainAdminSecretSchema).readonly(), { auth: "admin" }), method(object({ limit: number().int().min(1).max(500).optional() }), array(KeychainAuditEntrySchema).readonly(), { auth: "admin" }), method(object({ ref: string().min(1) }), object({
15085
+ value: string(),
15086
+ version: number().int()
15087
+ }), {
15088
+ kind: "mutation",
15089
+ auth: "admin",
15090
+ caller: "required"
15091
+ }), method(object({
15092
+ name: SystemHeldSecretNameSchema,
15093
+ value: string().min(16)
15094
+ }), object({
15095
+ ref: string(),
15096
+ version: number().int()
15097
+ }), {
15098
+ kind: "mutation",
15099
+ auth: "admin",
15100
+ caller: "required"
15101
+ }), method(object({ refs: array(string().min(1)).max(1e3) }), object({
15102
+ deleted: array(string()).readonly(),
15103
+ kept: array(object({
15104
+ ref: string(),
15105
+ usage: KeychainSecretUsageSchema
15106
+ })).readonly()
15107
+ }), {
15108
+ kind: "mutation",
15109
+ auth: "admin",
15110
+ caller: "required"
15111
+ }), method(object({}), object({
15112
+ fingerprint: string(),
15113
+ keySource: KeychainKeySourceSchema
15114
+ }), {
15115
+ kind: "mutation",
15116
+ auth: "admin",
15117
+ caller: "required"
15118
+ }), method(object({}), object({ purged: number().int() }), {
15119
+ kind: "mutation",
15120
+ auth: "admin",
15121
+ caller: "required"
15122
+ }), method(object({}), object({
15123
+ key: string(),
15124
+ fingerprint: string(),
15125
+ keySource: KeychainKeySourceSchema
15126
+ }), {
15127
+ kind: "mutation",
15128
+ auth: "admin",
15129
+ caller: "required"
15130
+ }), method(object({ confirm: literal(KEYCHAIN_RESET_CONFIRMATION) }), object({
15131
+ archivedCollection: string().nullable(),
15132
+ archived: number().int()
15133
+ }), {
15134
+ kind: "mutation",
15135
+ auth: "admin",
15136
+ caller: "required"
15137
+ });
15138
+ method(_void(), object({
14234
15139
  id: string(),
14235
15140
  name: string(),
14236
- isPullMode: boolean().optional(),
14237
- priority: number().optional(),
14238
15141
  hwaccel: string().optional(),
14239
15142
  probedBestHwaccel: string().optional()
14240
- })), method(DecoderSessionConfigSchema, object({
14241
- sessionId: string(),
14242
- nodeId: string()
14243
- })), method(object({ sessionId: string() }), _void()), method(object({
14244
- sessionId: string(),
14245
- packet: EncodedPacketSchema
14246
- }), _void()), method(object({
14247
- sessionId: string(),
14248
- url: string()
14249
- }), _void()), method(object({
14250
- sessionId: string(),
14251
- maxCount: number().default(1),
14252
- waitMs: number().optional()
14253
- }), array(DecodedFrameSchema)), method(object({
14254
- sessionId: string(),
14255
- maxCount: number().default(1),
14256
- waitMs: number().optional()
14257
- }), array(FrameHandleSchema)), method(object({ handle: FrameHandleSchema }), DecodedFrameSchema.nullable()), method(object({ sessionId: string() }), ShmRingStatsSchema.nullable()), method(object({
14258
- sessionId: string(),
14259
- config: DecoderSessionConfigSchema.partial()
14260
- }), _void()), method(object({ sessionId: string() }), DecoderStatsSchema), method(_void(), array(object({
14261
- sessionId: string(),
14262
- codec: string(),
14263
- outputFormat: string(),
14264
- createdAtMs: number()
14265
- })).readonly()), method(_void(), object({ backend: string() }), {
15143
+ })), method(_void(), object({ backend: string() }), {
14266
15144
  kind: "mutation",
14267
15145
  auth: "admin"
14268
15146
  });
@@ -15181,6 +16059,10 @@ var DeviceInfoSchema = object({
15181
16059
  isCamera: boolean(),
15182
16060
  /** Current config values — serializable snapshot */
15183
16061
  config: record(string(), unknown()),
16062
+ /** Paths inside `config` the device's schema declares secret (D783) —
16063
+ * what the non-admin redaction withholds. Paths, never values. Absent
16064
+ * when the owning runner has not reported them. */
16065
+ secretConfigPaths: array(string()).optional(),
15184
16066
  /** Hardware + identity blob (manufacturer / model / firmware / sn /
15185
16067
  * uid / mac / …). Populated by drivers; editable via setMetadata. */
15186
16068
  metadata: record(string(), unknown()).nullable().optional(),
@@ -15386,12 +16268,22 @@ var DeviceRegisterPayloadSchema = object({
15386
16268
  * call (workers re-register on init), so probe-driven updates
15387
16269
  * (e.g. PTZ flag flipping after the abilities probe) propagate. */
15388
16270
  features: array(string()).optional(),
15389
- config: record(string(), unknown())
16271
+ config: record(string(), unknown()),
16272
+ /** Config paths the device's own schema DECLARES secret
16273
+ * (`.meta({ secret: true })` — dotted, `[]` for array items, e.g.
16274
+ * `connection.password`). Re-published on every register; the non-admin
16275
+ * redaction withholds them at any depth (D783). Optional: an older runner
16276
+ * omits it and the keyword backstop applies. */
16277
+ secretConfigPaths: array(string()).optional()
15390
16278
  });
15391
16279
  var DeviceRemovePayloadSchema = object({ deviceId: number() });
15392
16280
  var DevicePersistConfigPayloadSchema = object({
15393
16281
  deviceId: number(),
15394
- data: record(string(), unknown())
16282
+ data: record(string(), unknown()),
16283
+ /** The paths the device's schema declares secret (D783), when the caller
16284
+ * knows them — those values are kept in the keychain, not the blob (D787).
16285
+ * Absent: the paths the device last reported at `registerDevice` apply. */
16286
+ secretConfigPaths: array(string()).optional()
15395
16287
  });
15396
16288
  /** What a migration actually did, per switch. `unreachable` is a first-class
15397
16289
  * answer: a camera that could not be asked is not a camera that was silenced. */
@@ -16213,6 +17105,12 @@ method(_void(), array(string()).readonly(), { auth: "admin" }), method(object({
16213
17105
  kind: "mutation",
16214
17106
  auth: "admin"
16215
17107
  });
17108
+ var HUB_ADDRESS_ROLES = [
17109
+ "appConnections",
17110
+ "notificationTaps",
17111
+ "internal",
17112
+ "publicEndpoint"
17113
+ ];
16216
17114
  /**
16217
17115
  * Transport bounds for AI capability methods.
16218
17116
  *
@@ -17039,7 +17937,14 @@ var LoginMethodContributionSchema = discriminatedUnion("kind", [
17039
17937
  icon: string().optional(),
17040
17938
  /** Addon-owned HTTP route the button navigates to (GET). */
17041
17939
  startUrl: string(),
17042
- stage: LoginStageEnum
17940
+ stage: LoginStageEnum,
17941
+ /**
17942
+ * The provider names an external identity a signed-in user can link to
17943
+ * their own account (D782): `auth.listLinkableProviders` /
17944
+ * `beginExternalLink` offer exactly the redirect methods that set this,
17945
+ * whatever their id. Absent / false — a login button only (magic-link).
17946
+ */
17947
+ linkable: boolean().optional()
17043
17948
  }),
17044
17949
  object({
17045
17950
  kind: literal("widget"),
@@ -18815,6 +19720,8 @@ var NcSystemEventKindSchema = _enum([
18815
19720
  "stream-offline",
18816
19721
  "node-online",
18817
19722
  "node-offline",
19723
+ "node-restarting",
19724
+ "node-updating",
18818
19725
  "node-inference-unavailable",
18819
19726
  "detection-blind",
18820
19727
  "addon-crash-loop",
@@ -20420,7 +21327,13 @@ var NcAlarmModeDelaysListSchema = array(object({
20420
21327
  * Absent = false (logged, not sent). Even when true it is not sent when that same sensor
20421
21328
  * is the one that starts the entry delay — the entry-delay notification already names it.
20422
21329
  */
20423
- notifySensorRewatched: boolean().optional()
21330
+ notifySensorRewatched: boolean().optional(),
21331
+ /**
21332
+ * D735: notify "ignored" when a sensor this arm bypassed really MOVED while armed (its own
21333
+ * change time is after the arm) and set nothing off. Absent = false (logged, not sent). A
21334
+ * re-sync, a recovery from `unavailable` or a duplicate is never announced, switch or not.
21335
+ */
21336
+ notifySensorIgnored: boolean().optional()
20424
21337
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
20425
21338
  var NcAlarmModeDelaysEditListSchema = array(object({
20426
21339
  mode: AlarmArmModeSchema,
@@ -20433,7 +21346,9 @@ var NcAlarmModeDelaysEditListSchema = array(object({
20433
21346
  /** D706: set the switch, or `null` to clear it (back to the default, false). */
20434
21347
  notifyDuringEntryDelay: boolean().nullable().optional(),
20435
21348
  /** D706: set the switch, or `null` to clear it (back to the default, false). */
20436
- notifySensorRewatched: boolean().nullable().optional()
21349
+ notifySensorRewatched: boolean().nullable().optional(),
21350
+ /** D735: set the switch, or `null` to clear it (back to the default, false). */
21351
+ notifySensorIgnored: boolean().nullable().optional()
20437
21352
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
20438
21353
  /** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
20439
21354
  var NcAlarmNonBlockingEditSchema = object({
@@ -21411,8 +22326,8 @@ Object.freeze(Object.fromEntries([{
21411
22326
  var CLUSTER_STEP_SETTING_FIELDS = [{
21412
22327
  stepId: "face-embedding",
21413
22328
  key: "minLandmarkFaceSize",
21414
- label: "Min face size for recognition (detection px)",
21415
- description: "Refuse to embed a face smaller than this IN THE DETECTION FRAME. Applies to every node — the enrolled gallery is one index, and two admission floors would fill it from two policies. 0 disables the gate.",
22329
+ label: "Min face size to embed (detection-frame px)",
22330
+ description: "Faces smaller than this in the small frame the detector sees are not embedded, so they can never be recognised. Below it the face cannot be aligned reliably and a bad embedding matches everyone. Same value on every node. 0 = no limit.",
21416
22331
  type: "slider",
21417
22332
  min: 0,
21418
22333
  max: 64,
@@ -22385,8 +23300,23 @@ var RetrainMacroClassSchema = _enum([
22385
23300
  ]);
22386
23301
  /** A subject to learn, or a phantom to unlearn (taught by OMISSION). */
22387
23302
  var RetrainAnnotationKindSchema = _enum(["subject", "model_error"]);
22388
- /** Did a human draw this box, or did the assist propose it? */
22389
- var RetrainAnnotationSourceSchema = _enum(["operator", "assist"]);
23303
+ /**
23304
+ * Where a box came from. A machine-sourced box is a DRAFT on the page until
23305
+ * the operator confirms it; once saved it keeps its source as provenance.
23306
+ *
23307
+ * - `operator` — drawn by hand.
23308
+ * - `assist` — proposed by a detector run on the copy (the camera's own
23309
+ * model, or a larger teacher model).
23310
+ * - `pipeline` — the box the live pipeline actually drew on that frame, read
23311
+ * back from the track store (never a re-run).
23312
+ * - `llm` — proposed by a vision-capable LLM profile.
23313
+ */
23314
+ var RetrainAnnotationSourceSchema = _enum([
23315
+ "operator",
23316
+ "assist",
23317
+ "pipeline",
23318
+ "llm"
23319
+ ]);
22390
23320
  /** Normalised `[0,1]` rectangle against the FULL frame — the canonical form. */
22391
23321
  var RetrainBboxSchema = object({
22392
23322
  x: number(),
@@ -22404,7 +23334,8 @@ var RetrainBboxSchema = object({
22404
23334
  */
22405
23335
  var RetrainAnnotationSchema = object({
22406
23336
  id: string(),
22407
- trackId: string(),
23337
+ /** The frame's track. Absent on a recording still, which has none. */
23338
+ trackId: string().optional(),
22408
23339
  deviceId: number(),
22409
23340
  /** The COPY in retrain storage — never the source track's media key. */
22410
23341
  mediaKey: string(),
@@ -22453,18 +23384,63 @@ var RetrainFrameCandidateSchema = object({
22453
23384
  * fail, whatever became of the original. */
22454
23385
  copied: boolean()
22455
23386
  });
23387
+ /**
23388
+ * One box the live pipeline drew on a copied frame — what a correction starts
23389
+ * from. Derived when the frame is listed, from the track store: the subject
23390
+ * box recorded on the source media row when there is one, else the track
23391
+ * position nearest the frame's capture time. Never stored as an annotation
23392
+ * until the operator confirms it.
23393
+ */
23394
+ var RetrainPipelineBoxSchema = object({
23395
+ /** Normalised `[0,1]` against the full frame. */
23396
+ bbox: RetrainBboxSchema,
23397
+ macroClass: RetrainMacroClassSchema,
23398
+ label: string().optional(),
23399
+ subLabel: string().optional(),
23400
+ /** The detection score of the position used, when it was recorded. */
23401
+ score: number().optional(),
23402
+ /** The root model that produced the track, when it was recorded. */
23403
+ modelId: string().optional(),
23404
+ trackId: string(),
23405
+ /** `subject-box`: recorded on this very frame. `position`: the track's
23406
+ * position nearest the capture time, `deltaMs` away. */
23407
+ basis: _enum(["subject-box", "position"]),
23408
+ deltaMs: number()
23409
+ });
23410
+ /**
23411
+ * Where a retrain frame came from: a TRACK's stored media (copy-on-select),
23412
+ * or the RECORDING at an instant — the frame a model saw nothing in has no
23413
+ * track at all.
23414
+ */
23415
+ var RetrainFrameOriginSchema = _enum(["track", "recording"]);
22456
23416
  /** A frame the dataset OWNS: bytes copied at selection time. */
22457
23417
  var RetrainFrameSchema = object({
22458
23418
  frameId: string(),
22459
23419
  deviceId: number(),
22460
- trackId: string(),
22461
- /** Provenance only. It may already point at nothing — that is expected. */
22462
- sourceMediaId: number().int(),
23420
+ origin: RetrainFrameOriginSchema,
23421
+ /** The track it was copied from. Absent on a recording still. */
23422
+ trackId: string().optional(),
23423
+ /** Provenance only. It may already point at nothing — that is expected.
23424
+ * Absent on a recording still. */
23425
+ sourceMediaId: number().int().optional(),
23426
+ /** A recording still: the playhead instant it was taken at. */
23427
+ epochMs: number().optional(),
22463
23428
  sourceKind: MediaFileKindEnum,
22464
23429
  sizeBytes: number().int(),
22465
23430
  width: number().int(),
22466
23431
  height: number().int(),
22467
- copiedAt: number()
23432
+ copiedAt: number(),
23433
+ /** When the CAMERA captured the frame. Absent on a copy taken before D785. */
23434
+ capturedAt: number().optional(),
23435
+ /** The operator's "every subject in this frame is boxed". Detector
23436
+ * evaluation keeps only complete frames: an unboxed subject would count as
23437
+ * a false positive. */
23438
+ complete: boolean(),
23439
+ /** The boxes the pipeline drew on this frame. Present on list answers;
23440
+ * empty when nothing the track store holds can be placed on it. */
23441
+ pipelineBoxes: array(RetrainPipelineBoxSchema).readonly().optional(),
23442
+ /** The named collections this frame belongs to. Present on list answers. */
23443
+ collectionIds: array(string()).readonly().optional()
22468
23444
  });
22469
23445
  /** Why a copy-on-select could not be honoured — named, never a silent skip. */
22470
23446
  var RetrainCopyRefusalSchema = _enum([
@@ -22485,6 +23461,56 @@ var RetrainFrameListSchema = object({
22485
23461
  /** What the page pre-selects — the native key frame when one survives. */
22486
23462
  autoPickMediaId: number().int().optional()
22487
23463
  });
23464
+ /**
23465
+ * Which frames an annotated export carries. Every field narrows; an empty
23466
+ * filter is every annotated copy. A dataset VERSION is a frozen `frameIds`
23467
+ * list owned by whoever trains (the training orchestrator), so `frameIds` is
23468
+ * the primitive a version export uses; `batchId` re-selects what an earlier
23469
+ * export carried (`exportedInBatch`).
23470
+ */
23471
+ var RetrainExportFilterSchema = object({
23472
+ deviceIds: array(number()).optional(),
23473
+ frameIds: array(string()).max(2e4).optional(),
23474
+ /** Only frames in at least one of these named collections. */
23475
+ collectionIds: array(string()).max(200).optional(),
23476
+ /** Exactly the frames of this dataset version (and its completeOnly). An
23477
+ * unknown id selects nothing. */
23478
+ datasetVersionId: string().optional(),
23479
+ batchId: string().optional(),
23480
+ /** Only frames the operator marked complete (all subjects boxed). */
23481
+ completeOnly: boolean().optional()
23482
+ });
23483
+ /**
23484
+ * `frames`: the full frame copies, boxes in `annotations.json`.
23485
+ * `crops`: additionally one JPEG per subject, cut with the runtime detail-crop
23486
+ * convention — the classifier layout.
23487
+ */
23488
+ var RetrainExportLayoutSchema = _enum(["frames", "crops"]);
23489
+ /**
23490
+ * Where `writeRetrainArchive` streams the tar: one HTTP request whose body is
23491
+ * the archive, sent chunk by chunk. Covers a presigned PUT and a provider
23492
+ * addon's data-plane upload route alike. Headers are sent verbatim and never
23493
+ * logged.
23494
+ */
23495
+ var RetrainArchiveTargetSchema = object({
23496
+ kind: literal("http"),
23497
+ method: _enum(["PUT", "POST"]),
23498
+ url: string().url(),
23499
+ headers: record(string(), string()).optional()
23500
+ });
23501
+ var RetrainArchiveResultSchema = object({
23502
+ batchId: string(),
23503
+ frames: number().int(),
23504
+ crops: number().int(),
23505
+ annotations: number().int(),
23506
+ /** Frames planned but not shipped (unreadable copy, path too long). */
23507
+ missing: number().int(),
23508
+ bytes: number().int(),
23509
+ /** sha256 of the exact bytes sent. */
23510
+ sha256: string(),
23511
+ /** The target's HTTP status. */
23512
+ status: number().int()
23513
+ });
22488
23514
  /** What the operator asked the assist to look for. */
22489
23515
  var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
22490
23516
  kind: literal("package"),
@@ -22514,6 +23540,170 @@ var RetrainAssistResultSchema = discriminatedUnion("kind", [object({
22514
23540
  reason: string(),
22515
23541
  detail: string().optional()
22516
23542
  })]);
23543
+ /** A named set over the retrain corpus — membership only, it owns no pixels. */
23544
+ var RetrainCollectionSchema = object({
23545
+ id: string(),
23546
+ name: string(),
23547
+ createdAt: number(),
23548
+ frameCount: number().int()
23549
+ });
23550
+ var RetrainCollectionCreateResultSchema = discriminatedUnion("kind", [object({
23551
+ kind: literal("created"),
23552
+ collection: RetrainCollectionSchema
23553
+ }), object({
23554
+ kind: literal("refused"),
23555
+ reason: _enum(["empty-name", "duplicate-name"])
23556
+ })]);
23557
+ var RetrainCollectionRenameResultSchema = discriminatedUnion("kind", [object({
23558
+ kind: literal("renamed"),
23559
+ collection: RetrainCollectionSchema
23560
+ }), object({
23561
+ kind: literal("refused"),
23562
+ reason: _enum([
23563
+ "empty-name",
23564
+ "duplicate-name",
23565
+ "unknown-collection"
23566
+ ])
23567
+ })]);
23568
+ /**
23569
+ * The outcome of adding the frame at a recording instant. Every refusal is
23570
+ * named: `still-unavailable` (not recording, a gap, or an undecodable frame —
23571
+ * `detail` says which), `still-busy` (the recorder is at its decode bound; try
23572
+ * again), `unreadable-image`, `write-failed`, `unknown-collection`.
23573
+ */
23574
+ var AddRetrainFrameFromRecordingResultSchema = discriminatedUnion("kind", [object({
23575
+ kind: literal("added"),
23576
+ collectionId: string(),
23577
+ frame: RetrainFrameSchema,
23578
+ /** The moment was already in the dataset; nothing was decoded. */
23579
+ reused: boolean()
23580
+ }), object({
23581
+ kind: literal("refused"),
23582
+ reason: _enum([
23583
+ "unknown-collection",
23584
+ "still-unavailable",
23585
+ "still-busy",
23586
+ "unreadable-image",
23587
+ "write-failed"
23588
+ ]),
23589
+ detail: string().optional()
23590
+ })]);
23591
+ /** A vision LLM profile the AI assist may use, and where it sends the frame. */
23592
+ var RetrainAssistProfileSchema = object({
23593
+ id: string(),
23594
+ name: string(),
23595
+ model: string(),
23596
+ kind: string(),
23597
+ /** `true` when the frame would leave the house (a cloud vendor, or an
23598
+ * endpoint that is not on the local network). The page confirms each such
23599
+ * run, and the provider refuses one without `allowCloud`. */
23600
+ leavesHouse: boolean()
23601
+ });
23602
+ /** A box the AI assist proposed: a draft, plus the model's own reason. */
23603
+ var RetrainLlmProposalSchema = RetrainAnnotationDraftSchema.extend({ reason: string() });
23604
+ /** What the AI assist said about a box that was already on the canvas. */
23605
+ var RetrainLlmReviewSchema = object({
23606
+ /** The `ref` the page sent the box under. */
23607
+ ref: string(),
23608
+ verdict: _enum([
23609
+ "confirmed",
23610
+ "wrong-class",
23611
+ "not-an-object",
23612
+ "uncertain"
23613
+ ]),
23614
+ suggestedClass: RetrainMacroClassSchema.optional(),
23615
+ reason: string()
23616
+ });
23617
+ /**
23618
+ * The AI assist's answer. `proposed` carries drafts (`source: 'llm'`, the
23619
+ * model and its confidence) and reviews; items the model got wrong (a box off
23620
+ * the frame, an unasked class, an unknown ref) are dropped and counted in
23621
+ * `rejected`. A whole answer that is not the schema is `malformed-answer`.
23622
+ */
23623
+ var RetrainLlmAssistResultSchema = discriminatedUnion("kind", [object({
23624
+ kind: literal("proposed"),
23625
+ profileId: string(),
23626
+ model: string(),
23627
+ /** The frame was sent outside the house for this answer. */
23628
+ leftHouse: boolean(),
23629
+ proposals: array(RetrainLlmProposalSchema).readonly(),
23630
+ reviews: array(RetrainLlmReviewSchema).readonly(),
23631
+ rejected: number().int()
23632
+ }), object({
23633
+ kind: literal("refused"),
23634
+ reason: _enum([
23635
+ "unknown-frame",
23636
+ "unreadable-frame",
23637
+ "unknown-profile",
23638
+ "profile-disabled",
23639
+ "no-vision",
23640
+ "cloud-not-confirmed",
23641
+ "llm-error",
23642
+ "malformed-answer"
23643
+ ]),
23644
+ detail: string().optional()
23645
+ })]);
23646
+ /** What a dataset version is cut from. Every field narrows. */
23647
+ var RetrainDatasetSelectionSchema = object({
23648
+ deviceIds: array(number()).readonly().optional(),
23649
+ collectionIds: array(string()).max(200).readonly().optional(),
23650
+ frameIds: array(string()).max(2e4).readonly().optional()
23651
+ });
23652
+ /** The counts a dataset is judged by. */
23653
+ var RetrainDatasetStatsSchema = object({
23654
+ frames: number().int(),
23655
+ completeFrames: number().int(),
23656
+ /** `completeFrames / frames`, 0..1. */
23657
+ completeness: number(),
23658
+ /** Boxes to learn (model errors excluded). */
23659
+ subjects: number().int(),
23660
+ modelErrors: number().int(),
23661
+ /** Complete frames with no subject. */
23662
+ hardNegatives: number().int(),
23663
+ /** Subjects per class × camera × day/night. */
23664
+ cells: array(object({
23665
+ macroClass: string(),
23666
+ deviceId: number(),
23667
+ lighting: _enum([
23668
+ "day",
23669
+ "night",
23670
+ "unknown"
23671
+ ]),
23672
+ subjects: number().int(),
23673
+ frames: number().int()
23674
+ })).readonly(),
23675
+ cameras: array(object({
23676
+ deviceId: number(),
23677
+ frames: number().int(),
23678
+ completeFrames: number().int()
23679
+ })).readonly()
23680
+ });
23681
+ /** An immutable dataset version, without its frame list. */
23682
+ var RetrainDatasetVersionSchema = object({
23683
+ id: string(),
23684
+ name: string(),
23685
+ createdAt: number(),
23686
+ selection: RetrainDatasetSelectionSchema,
23687
+ layout: RetrainExportLayoutSchema,
23688
+ completeOnly: boolean(),
23689
+ frameCount: number().int(),
23690
+ /** sha256 of what the frames taught when the version was frozen. */
23691
+ annotationHash: string(),
23692
+ /** The export manifest schema the version was cut under. */
23693
+ manifestSchema: string(),
23694
+ stats: RetrainDatasetStatsSchema
23695
+ });
23696
+ var CreateRetrainDatasetVersionResultSchema = discriminatedUnion("kind", [object({
23697
+ kind: literal("created"),
23698
+ version: RetrainDatasetVersionSchema
23699
+ }), object({
23700
+ kind: literal("refused"),
23701
+ reason: _enum([
23702
+ "empty-name",
23703
+ "duplicate-name",
23704
+ "no-frames"
23705
+ ])
23706
+ })]);
22517
23707
  /** The outcome of a lifecycle move owned by the retrain page. */
22518
23708
  var RetrainTransitionResultSchema = object({
22519
23709
  trackId: string(),
@@ -23231,7 +24421,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
23231
24421
  auth: "admin"
23232
24422
  }), method(object({
23233
24423
  deviceId: number(),
23234
- trackId: string(),
24424
+ trackId: string().optional(),
23235
24425
  frameId: string()
23236
24426
  }), object({
23237
24427
  removed: boolean(),
@@ -23248,7 +24438,8 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
23248
24438
  auth: "admin"
23249
24439
  }), method(object({
23250
24440
  deviceId: number(),
23251
- trackId: string(),
24441
+ /** The frame's track, when it has one — checked against the frame. */
24442
+ trackId: string().optional(),
23252
24443
  frameId: string(),
23253
24444
  subject: RetrainAssistSubjectSchema,
23254
24445
  /** Which node runs it. Absent ⇒ wherever an unowned call lands. */
@@ -23256,12 +24447,16 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
23256
24447
  }), RetrainAssistResultSchema, {
23257
24448
  kind: "mutation",
23258
24449
  auth: "admin"
23259
- }), method(object({ trackId: string() }), array(RetrainAnnotationSchema).readonly(), {
24450
+ }), method(object({
24451
+ trackId: string().optional(),
24452
+ frameIds: array(string()).max(2e3).optional()
24453
+ }), array(RetrainAnnotationSchema).readonly(), {
23260
24454
  kind: "query",
23261
24455
  auth: "admin"
23262
24456
  }), method(object({
23263
24457
  deviceId: number(),
23264
- trackId: string(),
24458
+ /** The frame's track, when it has one — checked against the frame. */
24459
+ trackId: string().optional(),
23265
24460
  frameId: string(),
23266
24461
  annotations: array(RetrainAnnotationDraftSchema)
23267
24462
  }), array(RetrainAnnotationSchema).readonly(), {
@@ -23279,7 +24474,121 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
23279
24474
  }), RetrainTransitionResultSchema, {
23280
24475
  kind: "mutation",
23281
24476
  auth: "admin"
23282
- }), method(object({ deviceIds: array(number()).optional() }), object({ url: string() }), {
24477
+ }), method(RetrainExportFilterSchema.extend({ layout: RetrainExportLayoutSchema.optional() }), object({ url: string() }), {
24478
+ kind: "query",
24479
+ auth: "admin"
24480
+ }), method(object({
24481
+ deviceId: number(),
24482
+ trackId: string().optional(),
24483
+ frameId: string(),
24484
+ complete: boolean()
24485
+ }), RetrainFrameSchema, {
24486
+ kind: "mutation",
24487
+ auth: "admin"
24488
+ }), method(object({
24489
+ filter: RetrainExportFilterSchema,
24490
+ layout: RetrainExportLayoutSchema,
24491
+ target: RetrainArchiveTargetSchema
24492
+ }), RetrainArchiveResultSchema, {
24493
+ kind: "mutation",
24494
+ auth: "admin",
24495
+ timeoutMs: 60 * 6e4
24496
+ }), method(object({
24497
+ selection: RetrainDatasetSelectionSchema,
24498
+ completeOnly: boolean().optional()
24499
+ }), RetrainDatasetStatsSchema, {
24500
+ kind: "query",
24501
+ auth: "admin"
24502
+ }), method(object({
24503
+ name: string().max(120),
24504
+ selection: RetrainDatasetSelectionSchema,
24505
+ layout: RetrainExportLayoutSchema,
24506
+ completeOnly: boolean()
24507
+ }), CreateRetrainDatasetVersionResultSchema, {
24508
+ kind: "mutation",
24509
+ auth: "admin",
24510
+ timeoutMs: 12e4
24511
+ }), method(object({}), array(RetrainDatasetVersionSchema).readonly(), {
24512
+ kind: "query",
24513
+ auth: "admin"
24514
+ }), method(object({ id: string() }), object({ deleted: boolean() }), {
24515
+ kind: "mutation",
24516
+ auth: "admin"
24517
+ }), method(object({ id: string() }), object({
24518
+ found: boolean(),
24519
+ annotationHash: string(),
24520
+ matches: boolean(),
24521
+ missingFrames: number().int()
24522
+ }), {
24523
+ kind: "query",
24524
+ auth: "admin",
24525
+ timeoutMs: 12e4
24526
+ }), method(object({}), array(RetrainAssistProfileSchema).readonly(), {
24527
+ kind: "query",
24528
+ auth: "admin"
24529
+ }), method(object({
24530
+ deviceId: number(),
24531
+ trackId: string().optional(),
24532
+ frameId: string(),
24533
+ profileId: string(),
24534
+ classes: array(RetrainMacroClassSchema).min(1),
24535
+ boxes: array(object({
24536
+ ref: string(),
24537
+ bbox: RetrainBboxSchema,
24538
+ macroClass: RetrainMacroClassSchema,
24539
+ label: string().optional()
24540
+ })).max(50),
24541
+ allowCloud: boolean()
24542
+ }), RetrainLlmAssistResultSchema, {
24543
+ kind: "mutation",
24544
+ auth: "admin",
24545
+ timeoutMs: 18e4
24546
+ }), method(object({ name: string().max(120) }), RetrainCollectionCreateResultSchema, {
24547
+ kind: "mutation",
24548
+ auth: "admin"
24549
+ }), method(object({}), array(RetrainCollectionSchema).readonly(), {
24550
+ kind: "query",
24551
+ auth: "admin"
24552
+ }), method(object({
24553
+ id: string(),
24554
+ name: string().max(120)
24555
+ }), RetrainCollectionRenameResultSchema, {
24556
+ kind: "mutation",
24557
+ auth: "admin"
24558
+ }), method(object({ id: string() }), object({
24559
+ deleted: boolean(),
24560
+ removedFrames: number().int()
24561
+ }), {
24562
+ kind: "mutation",
24563
+ auth: "admin"
24564
+ }), method(object({
24565
+ collectionId: string(),
24566
+ deviceId: number(),
24567
+ epochMs: number()
24568
+ }), AddRetrainFrameFromRecordingResultSchema, {
24569
+ kind: "mutation",
24570
+ auth: "admin",
24571
+ timeoutMs: 6e4
24572
+ }), method(object({
24573
+ collectionId: string(),
24574
+ frameIds: array(string()).min(1).max(500)
24575
+ }), object({
24576
+ added: number().int(),
24577
+ alreadyMember: number().int(),
24578
+ unknownFrameIds: array(string()).readonly()
24579
+ }), {
24580
+ kind: "mutation",
24581
+ auth: "admin"
24582
+ }), method(object({
24583
+ collectionId: string(),
24584
+ frameId: string()
24585
+ }), object({
24586
+ removed: boolean(),
24587
+ deletedFrame: boolean()
24588
+ }), {
24589
+ kind: "mutation",
24590
+ auth: "admin"
24591
+ }), method(object({ collectionId: string() }), array(RetrainFrameSchema).readonly(), {
23283
24592
  kind: "query",
23284
24593
  auth: "admin"
23285
24594
  }), method(object({
@@ -23332,6 +24641,34 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
23332
24641
  timestamp: number()
23333
24642
  });
23334
24643
  /**
24644
+ * A root model's boxes standing in for one crop-kind step's own inference
24645
+ * (D730) — see `runPipeline.suppliedRoot`.
24646
+ */
24647
+ var SuppliedRootOutputSchema = object({
24648
+ stepId: string(),
24649
+ /** The root model that drew the boxes — stamped on the result. */
24650
+ modelId: string(),
24651
+ detections: array(object({
24652
+ /** `[x1, y1, x2, y2]` in the pixels of the call's image. */
24653
+ bbox: tuple([
24654
+ number(),
24655
+ number(),
24656
+ number(),
24657
+ number()
24658
+ ]),
24659
+ score: number(),
24660
+ /**
24661
+ * Five facial landmarks in the same pixels — a face the step's own
24662
+ * detector already re-found inside a root face box (D730), so its
24663
+ * embedder child aligns it exactly as it aligns any detected face.
24664
+ */
24665
+ landmarks: array(object({
24666
+ x: number(),
24667
+ y: number()
24668
+ })).optional()
24669
+ })).min(1)
24670
+ });
24671
+ /**
23335
24672
  * Reference to the frame's retained NATIVE surface + the parent crop's placement
23336
24673
  * within the frame, so the executor can re-cut a leaf child ROI at native
23337
24674
  * resolution on the detail plane. See the `runPipeline` `nativeCropRef` field.
@@ -23379,7 +24716,7 @@ var FrameRefSchema = object({
23379
24716
  timestamp: number(),
23380
24717
  capturedAt: number().optional()
23381
24718
  });
23382
- var ModelFormatSchema$1 = _enum([
24719
+ var ModelFormatSchema$2 = _enum([
23383
24720
  "onnx",
23384
24721
  "coreml",
23385
24722
  "openvino",
@@ -23397,7 +24734,7 @@ var PipelineSlotSchema = _enum([
23397
24734
  var PipelineEngineChoiceSchema = object({
23398
24735
  runtime: literal("python"),
23399
24736
  backend: string(),
23400
- format: ModelFormatSchema$1,
24737
+ format: ModelFormatSchema$2,
23401
24738
  device: string().optional()
23402
24739
  });
23403
24740
  /**
@@ -23436,32 +24773,15 @@ var PipelineDefaultStepSchema = lazy(() => object({
23436
24773
  group: string().optional(),
23437
24774
  settings: record(string(), unknown()).optional()
23438
24775
  }));
23439
- var PipelineTemplateStepSchema = lazy(() => object({
23440
- addonId: string(),
23441
- enabled: boolean(),
23442
- modelId: string(),
23443
- children: array(PipelineTemplateStepSchema).readonly(),
23444
- settings: record(string(), unknown()).optional()
23445
- }));
23446
- var PipelineTemplateSchema$1 = object({
23447
- id: string(),
23448
- name: string(),
23449
- createdAt: string(),
23450
- updatedAt: string(),
23451
- engine: PipelineEngineChoiceSchema,
23452
- steps: array(PipelineTemplateStepSchema).readonly()
23453
- });
23454
24776
  var PipelineModelOptionSchema = object({
23455
24777
  id: string(),
23456
24778
  name: string(),
23457
- formats: record(string(), object({
23458
- downloaded: boolean(),
23459
- sizeMB: number()
23460
- })),
24779
+ formats: record(string(), PipelineModelFormatStatusSchema),
23461
24780
  group: ModelVariantGroupSchema.optional(),
23462
24781
  legacy: boolean().optional(),
23463
24782
  provider: ModelProviderIdSchema.optional(),
23464
- defaultConfidence: number().optional()
24783
+ defaultConfidence: number().optional(),
24784
+ suppliesSteps: array(RootStepSupplySchema).readonly().optional()
23465
24785
  });
23466
24786
  var ConfigFieldBridge = custom();
23467
24787
  var PipelineAddonSchemaSchema = object({
@@ -23623,6 +24943,22 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
23623
24943
  }), {
23624
24944
  kind: "mutation",
23625
24945
  auth: "admin"
24946
+ }), method(object({
24947
+ nodeId: string(),
24948
+ deviceIds: array(number()).readonly().optional(),
24949
+ dryRun: boolean()
24950
+ }), object({
24951
+ entries: array(object({
24952
+ deviceId: number(),
24953
+ keys: array(string()).readonly()
24954
+ })).readonly(),
24955
+ refused: array(object({
24956
+ deviceId: number(),
24957
+ reason: string()
24958
+ })).readonly()
24959
+ }), {
24960
+ kind: "mutation",
24961
+ auth: "admin"
23626
24962
  }), method(object({ nodeId: string() }), object({ unhealthy: array(object({
23627
24963
  /** `<backend>:<device>`, e.g. `openvino:gpu`. */
23628
24964
  deviceKey: string(),
@@ -23648,22 +24984,14 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
23648
24984
  }), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(object({
23649
24985
  steps: array(PipelineStepInputSchema),
23650
24986
  deviceKey: string().optional()
23651
- }), PipelineValidationResultSchema), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
23652
- name: string(),
23653
- steps: array(PipelineTemplateStepSchema).readonly(),
23654
- engine: PipelineEngineChoiceSchema
23655
- }), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({
23656
- id: string(),
23657
- name: string().optional(),
23658
- steps: array(PipelineTemplateStepSchema).readonly().optional()
23659
- }), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({ id: string() }), _void(), { kind: "mutation" }), method(_void(), InferenceCapabilitiesBridge), method(object({ addonId: string() }), ModelAvailabilityListBridge), method(object({
24987
+ }), PipelineValidationResultSchema), method(_void(), InferenceCapabilitiesBridge), method(object({ addonId: string() }), ModelAvailabilityListBridge), method(object({
23660
24988
  addonId: string(),
23661
24989
  modelId: string(),
23662
- format: ModelFormatSchema$1
24990
+ format: ModelFormatSchema$2
23663
24991
  }), DownloadModelResultSchema, { kind: "mutation" }), method(object({
23664
24992
  addonId: string(),
23665
24993
  modelId: string(),
23666
- format: ModelFormatSchema$1
24994
+ format: ModelFormatSchema$2
23667
24995
  }), object({ success: literal(true) }), { kind: "mutation" }), method(object({
23668
24996
  steps: array(PipelineStepInputSchema).min(1),
23669
24997
  frame: FrameInputSchema.optional(),
@@ -23729,7 +25057,16 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
23729
25057
  * `device-not-servable`), and nothing reaches the camera's live
23730
25058
  * trace stream. Absent ⇒ unchanged behaviour.
23731
25059
  */
23732
- replay: literal(true).optional()
25060
+ replay: literal(true).optional(),
25061
+ /**
25062
+ * Root-supplied detections for the tree's ROOT step (D730). Set by
25063
+ * `runDetailSubtree` when the camera's root model supplies a crop-kind
25064
+ * step (a Frigate+ `license_plate` for `plate-detection`): the step is
25065
+ * NOT inferred, these boxes are its output, and its children (the plate
25066
+ * reader) run on them through their own crop policy. Boxes are in the
25067
+ * PIXELS of the image this call carries. Absent ⇒ the root step runs.
25068
+ */
25069
+ suppliedRoot: SuppliedRootOutputSchema.optional()
23733
25070
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
23734
25071
  steps: array(PipelineStepInputSchema).min(1),
23735
25072
  frames: array(FrameInputSchema).min(1).max(255),
@@ -23811,6 +25148,763 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
23811
25148
  filename: string().optional(),
23812
25149
  settings: record(string(), unknown()).optional()
23813
25150
  }), AudioTestResultSchema, { kind: "mutation" }), method(_void(), ConfigUISchemaNullableBridge);
25151
+ /**
25152
+ * The wire shapes of the Auto tune plan and apply (D745), used by the
25153
+ * `pipeline-orchestrator` cap. The recommender's own types
25154
+ * (`recommend.ts`) are the TS face of the same data; these
25155
+ * schemas are what crosses the cap boundary.
25156
+ */
25157
+ var ModelFormatSchema$1 = _enum(MODEL_FORMATS);
25158
+ /** The operator's range for it. */
25159
+ var MIN_SESSIONS_PER_DEVICE_RANGE = {
25160
+ min: 1,
25161
+ max: 8
25162
+ };
25163
+ var HardwareClassIdSchema = _enum([
25164
+ "intel-core-ultra",
25165
+ "intel-n-series",
25166
+ "apple-m-base",
25167
+ "apple-m-pro",
25168
+ "unknown"
25169
+ ]);
25170
+ var AcceleratorKindSchema = _enum([
25171
+ "intel-npu",
25172
+ "intel-igpu",
25173
+ "x86-cpu",
25174
+ "arm-cpu",
25175
+ "apple-ane",
25176
+ "coral"
25177
+ ]);
25178
+ var RecommendationReasonSchema = discriminatedUnion("kind", [
25179
+ object({
25180
+ kind: literal("capacity"),
25181
+ sessions: number(),
25182
+ minSessions: number()
25183
+ }),
25184
+ object({
25185
+ kind: literal("tie-faster"),
25186
+ over: string(),
25187
+ recallDelta: number()
25188
+ }),
25189
+ object({
25190
+ kind: literal("stepped-down"),
25191
+ from: string(),
25192
+ to: string(),
25193
+ limitedBy: string()
25194
+ }),
25195
+ object({
25196
+ kind: literal("no-fit-lightest"),
25197
+ sessions: number(),
25198
+ minSessions: number()
25199
+ }),
25200
+ object({
25201
+ kind: literal("derived-latency"),
25202
+ modelId: string()
25203
+ }),
25204
+ object({ kind: literal("cpu-device") }),
25205
+ object({ kind: literal("coral-untouched") }),
25206
+ object({ kind: literal("device-disabled") }),
25207
+ object({ kind: literal("device-unavailable") }),
25208
+ object({ kind: literal("node-offline") }),
25209
+ object({ kind: literal("no-hardware-facts") }),
25210
+ object({
25211
+ kind: literal("no-measured-data"),
25212
+ hardwareClass: HardwareClassIdSchema
25213
+ }),
25214
+ object({
25215
+ kind: literal("no-family-build"),
25216
+ format: ModelFormatSchema$1
25217
+ }),
25218
+ object({
25219
+ kind: literal("kept-custom-pin"),
25220
+ modelId: string(),
25221
+ deviceKey: string()
25222
+ })
25223
+ ]);
25224
+ /** Why a device is left exactly as it is, whatever the plan would pick (D786). */
25225
+ var KeptReasonSchema = _enum(["custom-pin"]);
25226
+ /**
25227
+ * Where a choice's latency came from (D791): this node's own benchmark
25228
+ * result, or the hardware-class table. Measured on the node beats the table.
25229
+ */
25230
+ var LatencySourceSchema = discriminatedUnion("kind", [object({ kind: literal("table") }), object({
25231
+ kind: literal("node-benchmark"),
25232
+ resultId: string(),
25233
+ measuredAtMs: number()
25234
+ })]);
25235
+ var DeviceModelChoiceSchema = object({
25236
+ modelId: string(),
25237
+ p50Ms: number(),
25238
+ fps: number(),
25239
+ measured: boolean(),
25240
+ /** Absent on a plan from before D791 — read as the table. */
25241
+ source: LatencySourceSchema.optional(),
25242
+ /** Parallel sessions at the camera rate with the headroom kept free: ⌊fps × (1 − headroom) / camera fps⌋. */
25243
+ capacitySessions: number(),
25244
+ personRecall: number()
25245
+ });
25246
+ var DeviceRecommendationSchema = object({
25247
+ deviceKey: string(),
25248
+ accelerator: AcceleratorKindSchema.nullable(),
25249
+ enabled: boolean(),
25250
+ available: boolean(),
25251
+ chosen: DeviceModelChoiceSchema.nullable(),
25252
+ /** Present when the device is kept as is: Apply writes nothing on it. */
25253
+ kept: KeptReasonSchema.optional(),
25254
+ reasons: array(RecommendationReasonSchema).readonly()
25255
+ });
25256
+ var NodeRecommendationSchema = object({
25257
+ nodeId: string(),
25258
+ reachable: boolean(),
25259
+ hardwareClass: HardwareClassIdSchema.nullable(),
25260
+ cpuModel: string().nullable(),
25261
+ skipped: boolean(),
25262
+ acceleratorModelId: string().nullable(),
25263
+ devices: array(DeviceRecommendationSchema).readonly(),
25264
+ reasons: array(RecommendationReasonSchema).readonly()
25265
+ });
25266
+ var ModelPinChangeSchema = object({
25267
+ stepId: string(),
25268
+ before: string().nullable(),
25269
+ after: string().nullable()
25270
+ });
25271
+ var NodeDeviceChangeSchema = object({
25272
+ nodeId: string(),
25273
+ deviceKey: string(),
25274
+ madeExplicit: boolean(),
25275
+ modelPins: array(ModelPinChangeSchema).readonly(),
25276
+ thresholds: array(object({
25277
+ stepId: string(),
25278
+ keys: array(string()).readonly()
25279
+ })).readonly()
25280
+ });
25281
+ var CameraOverrideClearSchema = object({
25282
+ deviceId: number(),
25283
+ source: _enum(["step-override", "wholesale"]),
25284
+ nodeId: string(),
25285
+ deviceKey: string().nullable(),
25286
+ stepId: string(),
25287
+ modelIdBefore: string().nullable(),
25288
+ keys: array(string()).readonly()
25289
+ });
25290
+ var DetectorStoreClearSchema = object({
25291
+ deviceId: number(),
25292
+ keys: array(string()).readonly()
25293
+ });
25294
+ var AutoTuneWarningSchema = discriminatedUnion("kind", [
25295
+ object({
25296
+ kind: literal("detector-store-cluster-wide"),
25297
+ skippedNodes: array(string()).readonly()
25298
+ }),
25299
+ object({
25300
+ kind: literal("detector-store-unreadable"),
25301
+ error: string()
25302
+ }),
25303
+ object({
25304
+ kind: literal("coral-enabled"),
25305
+ nodeId: string(),
25306
+ deviceKey: string()
25307
+ }),
25308
+ object({
25309
+ kind: literal("custom-pin-kept"),
25310
+ nodeId: string(),
25311
+ deviceKey: string().nullable(),
25312
+ deviceId: number().nullable(),
25313
+ modelId: string()
25314
+ }),
25315
+ object({
25316
+ kind: literal("custom-registry-unreadable"),
25317
+ error: string()
25318
+ })
25319
+ ]);
25320
+ var AutoTuneOptionsSchema = object({
25321
+ /** Share of each accelerator's throughput kept free (0.3–0.5). */
25322
+ headroom: number().min(.3).max(.5).default(.4),
25323
+ /**
25324
+ * The least number of parallel sessions every device must sustain at the
25325
+ * camera rate with the headroom kept free (D755). The most accurate model
25326
+ * that reaches it wins.
25327
+ */
25328
+ minSessionsPerDevice: number().int().min(MIN_SESSIONS_PER_DEVICE_RANGE.min).max(MIN_SESSIONS_PER_DEVICE_RANGE.max).default(2)
25329
+ });
25330
+ var AutoTunePlanSchema = object({
25331
+ /** Hash of everything the apply will write — the apply refuses a different one. */
25332
+ planHash: string(),
25333
+ family: literal("yolov9"),
25334
+ headroom: number(),
25335
+ minSessionsPerDevice: number(),
25336
+ /** The detection rate a session is counted at (the fastest camera running the pipeline root). */
25337
+ cameraFps: number(),
25338
+ generatedAt: number(),
25339
+ nodes: array(NodeRecommendationSchema).readonly(),
25340
+ nodeChanges: array(NodeDeviceChangeSchema).readonly(),
25341
+ cameraClears: array(CameraOverrideClearSchema).readonly(),
25342
+ detectorStoreClears: array(DetectorStoreClearSchema).readonly(),
25343
+ warnings: array(AutoTuneWarningSchema).readonly()
25344
+ });
25345
+ var AutoTuneApplyInputSchema = object({
25346
+ planHash: string(),
25347
+ headroom: number().min(.3).max(.5).default(.4),
25348
+ minSessionsPerDevice: number().int().min(MIN_SESSIONS_PER_DEVICE_RANGE.min).max(MIN_SESSIONS_PER_DEVICE_RANGE.max).default(2)
25349
+ });
25350
+ var AutoTunePhaseResultSchema = object({
25351
+ phase: _enum([
25352
+ "download",
25353
+ "camera-overrides",
25354
+ "detector-thresholds",
25355
+ "node-models",
25356
+ "redispatch"
25357
+ ]),
25358
+ ok: boolean(),
25359
+ detail: string()
25360
+ });
25361
+ var AutoTuneApplyResultSchema = discriminatedUnion("kind", [
25362
+ object({
25363
+ kind: literal("applied"),
25364
+ planHash: string(),
25365
+ phases: array(AutoTunePhaseResultSchema).readonly()
25366
+ }),
25367
+ object({
25368
+ kind: literal("aborted"),
25369
+ phases: array(AutoTunePhaseResultSchema).readonly()
25370
+ }),
25371
+ object({
25372
+ kind: literal("stale"),
25373
+ plan: AutoTunePlanSchema
25374
+ }),
25375
+ object({
25376
+ kind: literal("refused"),
25377
+ reason: _enum(["apply-in-progress"])
25378
+ })
25379
+ ]);
25380
+ /**
25381
+ * Wire shapes of the Load balancing surface (D748): the session placement
25382
+ * policy, the atomic share write, the observed placement statistics, the
25383
+ * hardware share suggestion and the stale device pins.
25384
+ */
25385
+ /**
25386
+ * How a new detection session picks its node and device.
25387
+ * - `least-loaded` — the historical rule: lowest `attached / weight`, a
25388
+ * constant node order at a tie (D37). An idle node always ties at 0, so a
25389
+ * weight only bites once a node already holds a session.
25390
+ * - `share` — the share of PARALLEL sessions is held to the weights, and a
25391
+ * full or drowning node yields to a free one (D748).
25392
+ */
25393
+ var SessionPlacementPolicySchema = _enum(["least-loaded", "share"]);
25394
+ var LoadBalancingSharesInputSchema = object({
25395
+ /** nodeId → share (> 0). A node left out keeps its weight. */
25396
+ nodes: record(string(), number().positive()).default({}),
25397
+ /** nodeId → deviceKey → share (> 0). A device left out keeps its weight. */
25398
+ devices: record(string(), record(string(), number().positive())).default({})
25399
+ });
25400
+ var PlacementStatsInputSchema = object({
25401
+ /** Window the observed shares are averaged over (default 1 h, max 24 h). */
25402
+ windowMs: number().int().min(6e4).max(1440 * 60 * 1e3).default(3600 * 1e3) });
25403
+ var PlacementDeviceStatsSchema = object({
25404
+ deviceKey: string(),
25405
+ /** Sessions running on this device now. */
25406
+ sessionsNow: number(),
25407
+ /** Sessions started on this device inside the window. */
25408
+ sessionsStarted: number(),
25409
+ /** Time-weighted share of the node's parallel sessions over the window (0–1); null = no session ran. */
25410
+ observedShare: number().nullable()
25411
+ });
25412
+ var PlacementNodeStatsSchema = object({
25413
+ nodeId: string(),
25414
+ sessionsNow: number(),
25415
+ sessionsStarted: number(),
25416
+ /** Time-weighted share of the cluster's parallel sessions over the window (0–1); null = none ran. */
25417
+ observedShare: number().nullable(),
25418
+ /** Most sessions this node ran at once inside the window. */
25419
+ peakSessions: number(),
25420
+ /** Sessions a share election handed elsewhere because this node was full or drowning. */
25421
+ yieldsAway: number(),
25422
+ devices: array(PlacementDeviceStatsSchema).readonly()
25423
+ });
25424
+ /** A camera's device pin that the session path cannot honour. */
25425
+ var StaleDevicePinSchema = object({
25426
+ deviceId: number(),
25427
+ deviceKey: string(),
25428
+ /** `disabled` — the key exists on some node but is not enabled anywhere; `absent` — no node has it. */
25429
+ reason: _enum(["disabled", "absent"])
25430
+ });
25431
+ var PlacementStatsSchema = object({
25432
+ policy: SessionPlacementPolicySchema,
25433
+ windowMs: number(),
25434
+ /** How much of the window was actually observed (the hub keeps it in memory). */
25435
+ observedForMs: number(),
25436
+ sessionsNow: number(),
25437
+ /** Most parallel sessions across the cluster inside the window. */
25438
+ clusterPeakSessions: number(),
25439
+ nodes: array(PlacementNodeStatsSchema).readonly(),
25440
+ stalePins: array(StaleDevicePinSchema).readonly()
25441
+ });
25442
+ /**
25443
+ * Where an automatic share's capacity comes from (D755).
25444
+ * - `hardware` — the static capacity of each device at its Auto tune model:
25445
+ * the measured latency table for the node's hardware class.
25446
+ * - `benchmark` — the node's own measured capacity (node benchmark results).
25447
+ * A node with no results falls back to `hardware`, and says so.
25448
+ */
25449
+ var CapacitySourceSchema = _enum(["hardware", "benchmark"]);
25450
+ /** Why a node under the `benchmark` source was sized from hardware instead. */
25451
+ var CapacityFallbackSchema = _enum(["no-benchmark-results", "partial-benchmark-results"]);
25452
+ var SuggestSharesInputSchema = object({
25453
+ headroom: number().min(.3).max(.5).default(.4),
25454
+ /** Default `hardware`. */
25455
+ source: CapacitySourceSchema.default("hardware")
25456
+ });
25457
+ var SuggestedDeviceShareSchema = object({
25458
+ deviceKey: string(),
25459
+ /** Share of the node (0–100, rounded). */
25460
+ share: number(),
25461
+ modelId: string().nullable(),
25462
+ /** Inferences per second at that model, from the measured table; null when sized by a benchmark. */
25463
+ fps: number().nullable(),
25464
+ /** False for a table cell derived from another chip; true for a measured cell or a benchmark. */
25465
+ measured: boolean(),
25466
+ /**
25467
+ * Advisory session cap: ⌊fps × (1 − headroom) / camera fps⌋ from the table,
25468
+ * or the benchmark's measured max sessions. Never enforced.
25469
+ */
25470
+ suggestedMaxSessions: number().nullable()
25471
+ });
25472
+ var SuggestedNodeShareSchema = object({
25473
+ nodeId: string(),
25474
+ /** Share of the cluster (0–100, rounded); null = node skipped (offline, unknown hardware). */
25475
+ share: number().nullable(),
25476
+ /** Parallel sessions the node sustains at the suggested models with the headroom kept free. */
25477
+ capacitySessions: number(),
25478
+ devices: array(SuggestedDeviceShareSchema).readonly(),
25479
+ skippedReason: string().nullable(),
25480
+ /** What this node's capacity was read from; null when the node is skipped. */
25481
+ capacitySource: CapacitySourceSchema.nullable(),
25482
+ /** Set when the `benchmark` source was asked for and this node used `hardware`. */
25483
+ capacityFallback: CapacityFallbackSchema.nullable()
25484
+ });
25485
+ var SuggestSharesResultSchema = object({
25486
+ headroom: number(),
25487
+ /** The source asked for; each node says what it actually used. */
25488
+ source: CapacitySourceSchema,
25489
+ /** Camera detection rate the capacity is counted in (the fastest camera's fps). */
25490
+ cameraFps: number(),
25491
+ /** The Auto tune capacity floor the models were picked at. */
25492
+ minSessionsPerDevice: number(),
25493
+ nodes: array(SuggestedNodeShareSchema).readonly()
25494
+ });
25495
+ /**
25496
+ * Where the node and device shares come from (D750, D755, D791).
25497
+ * - `benchmark` (default since D791) — each node's capacity as its own node
25498
+ * benchmark measured it; a node without fresh results is sized from
25499
+ * `hardware` and says so. Measured on the node beats the table.
25500
+ * - `hardware` — computed from each node's static capacity only (its
25501
+ * accelerators at their Auto tune models, from the latency table).
25502
+ * Both are recomputed whenever a node or device joins, leaves, is switched
25503
+ * on or off, changes model, or a benchmark result is written. No load input.
25504
+ * - `manual` — the operator's stored sliders.
25505
+ * Either way a node or device that cannot take sessions gives its share to
25506
+ * the others in proportion; the stored values are never rewritten for that.
25507
+ */
25508
+ var ShareSourceSchema = _enum([
25509
+ "hardware",
25510
+ "benchmark",
25511
+ "manual"
25512
+ ]);
25513
+ var SetShareSourceInputSchema = object({ source: ShareSourceSchema });
25514
+ object({
25515
+ nodes: record(string(), number()),
25516
+ devices: record(string(), record(string(), number()))
25517
+ });
25518
+ var EffectiveDeviceShareSchema = object({
25519
+ deviceKey: string(),
25520
+ /** Can take sessions now (enabled, available, can run the root). */
25521
+ eligible: boolean(),
25522
+ /** The share the source assigns, as a percentage of the node's devices. */
25523
+ configuredPct: number(),
25524
+ /** The share actually used, after an ineligible device's share went to the others. */
25525
+ effectivePct: number()
25526
+ });
25527
+ var EffectiveNodeShareSchema = object({
25528
+ nodeId: string(),
25529
+ eligible: boolean(),
25530
+ configuredPct: number(),
25531
+ effectivePct: number(),
25532
+ /** Automatic sources: what this node's share was computed from; null under `manual` or before the first answer. */
25533
+ capacitySource: CapacitySourceSchema.nullable(),
25534
+ /** Automatic `benchmark`: why this node used `hardware` instead. */
25535
+ capacityFallback: CapacityFallbackSchema.nullable(),
25536
+ devices: array(EffectiveDeviceShareSchema).readonly()
25537
+ });
25538
+ var EffectiveSharesSchema = object({
25539
+ source: ShareSourceSchema,
25540
+ policy: SessionPlacementPolicySchema,
25541
+ /** An automatic source with no computed answer yet: the stored sliders are used meanwhile. */
25542
+ autoPending: boolean(),
25543
+ /** When the automatic shares were last computed (ms epoch); null = never. */
25544
+ computedAt: number().nullable(),
25545
+ /** What made the system recompute them last. */
25546
+ trigger: string().nullable(),
25547
+ nodes: array(EffectiveNodeShareSchema).readonly(),
25548
+ /** The relative weights the election reads (before eligibility). */
25549
+ nodeWeights: record(string(), number()),
25550
+ deviceWeights: record(string(), record(string(), number()))
25551
+ });
25552
+ /**
25553
+ * Wire shapes of the node benchmark (D791): the operator's plan, the
25554
+ * preflight the confirmation shows, the run the orchestrator owns, what the
25555
+ * measuring runner reports, and the result rows Auto tune and the automatic
25556
+ * shares read.
25557
+ *
25558
+ * ONE node at a time, owned by the pipeline orchestrator. While a run is
25559
+ * active the cluster is in BENCHMARK MODE for that node: it is not a
25560
+ * detection candidate, camera pins naming it are ignored so its sessions move
25561
+ * elsewhere, and both come back when the mode ends — after a crash or a hub
25562
+ * restart too (a persisted manifest, reconciled against the registry).
25563
+ *
25564
+ * Two measurements, both on the node, driven by its own runner (frames never
25565
+ * cross a process at frame rate, D9):
25566
+ * - `device` — one accelerator × one candidate detector, root only, open
25567
+ * loop: how many streams at the camera rate it carries inside the frame
25568
+ * interval;
25569
+ * - `pipeline` — the chosen detector plus the operator's detail steps
25570
+ * (faces, plates, classifiers, CLIP…), on varied frames from the bench
25571
+ * clip: how many parallel sessions the node carries before latency, drops,
25572
+ * CPU, RAM or the event loop give out.
25573
+ */
25574
+ /** Defaults and bounds, one place. */
25575
+ var NODE_BENCHMARK_DEFAULTS = {
25576
+ fpsPerStream: 10,
25577
+ budgetMinutes: 45,
25578
+ minBudgetMinutes: 10,
25579
+ maxBudgetMinutes: 120,
25580
+ /** A result older than this no longer beats the table. */
25581
+ staleAfterDays: 30,
25582
+ /** Warm-in per ramp step (not accounted). */
25583
+ settleSec: 5,
25584
+ /** Accounted window per ramp step. */
25585
+ measureSec: 20,
25586
+ /** Longest the drain may take before the run is refused. */
25587
+ drainTimeoutSec: 120,
25588
+ /** Candidate models per device in one run. */
25589
+ maxModelsPerDevice: 6
25590
+ };
25591
+ /** The two measurements a run can make. */
25592
+ var NodeBenchmarkPhaseSchema = _enum(["device", "pipeline"]);
25593
+ var NodeBenchmarkDevicePlanSchema = object({
25594
+ /** `<backend>:<device>` — `openvino:npu`, `coreml:all`, `cpu`. */
25595
+ deviceKey: string().min(1),
25596
+ /**
25597
+ * Root detector candidates for the `device` measurement, in the order
25598
+ * measured. The FIRST is the one the `pipeline` measurement runs.
25599
+ */
25600
+ modelIds: array(string().min(1)).min(1).max(NODE_BENCHMARK_DEFAULTS.maxModelsPerDevice)
25601
+ });
25602
+ /** What the operator confirmed. */
25603
+ var NodeBenchmarkPlanSchema = object({
25604
+ nodeId: string().min(1),
25605
+ devices: array(NodeBenchmarkDevicePlanSchema).min(1),
25606
+ /** Which measurements run. At least one. */
25607
+ phases: array(NodeBenchmarkPhaseSchema).min(1).default(["device", "pipeline"]),
25608
+ /**
25609
+ * Detail steps (catalog step ids under the root: faces, plates,
25610
+ * classifiers, CLIP…) the `pipeline` measurement enables. The root
25611
+ * detector always runs.
25612
+ */
25613
+ steps: array(string().min(1)).default([]),
25614
+ /** Offered rate of one stream / session. */
25615
+ fpsPerStream: number().int().min(1).max(30).default(NODE_BENCHMARK_DEFAULTS.fpsPerStream),
25616
+ /** Hard deadline: the mode ends and the cluster is restored when it passes. */
25617
+ budgetMinutes: number().int().min(NODE_BENCHMARK_DEFAULTS.minBudgetMinutes).max(NODE_BENCHMARK_DEFAULTS.maxBudgetMinutes).default(NODE_BENCHMARK_DEFAULTS.budgetMinutes),
25618
+ /** Keep the models the run downloaded (default: delete what was not installed before). */
25619
+ keepDownloadedModels: boolean().default(false),
25620
+ /** Start although the other nodes' capacity does not cover the sessions running now. */
25621
+ acceptInsufficientHeadroom: boolean().default(false)
25622
+ });
25623
+ var NodeBenchmarkRefusalSchema = object({
25624
+ kind: _enum([
25625
+ "benchmark-active",
25626
+ "node-offline",
25627
+ "unknown-device",
25628
+ "no-phase",
25629
+ "insufficient-headroom"
25630
+ ]),
25631
+ detail: string(),
25632
+ overridable: boolean()
25633
+ });
25634
+ /** A camera whose detection leaves the node for the run. */
25635
+ var NodeBenchmarkMovingCameraSchema = object({
25636
+ deviceId: number(),
25637
+ /** Placed on the node now (a standing attach or a running session). */
25638
+ placedOnNode: boolean(),
25639
+ /** A node pin naming this node, ignored for the run. */
25640
+ pinnedToNode: boolean()
25641
+ });
25642
+ /** A camera that cannot leave: no other node can obtain its frames. */
25643
+ var NodeBenchmarkStayingCameraSchema = object({
25644
+ deviceId: number(),
25645
+ reason: literal("only-frame-source")
25646
+ });
25647
+ var NodeBenchmarkPreflightSchema = object({
25648
+ nodeId: string(),
25649
+ online: boolean(),
25650
+ moving: array(NodeBenchmarkMovingCameraSchema).readonly(),
25651
+ /** Keep running on the node during the run; the result is flagged contaminated. */
25652
+ staying: array(NodeBenchmarkStayingCameraSchema).readonly(),
25653
+ estimatedDurationMs: number(),
25654
+ /** The run ends and the cluster is restored at the latest after this long. */
25655
+ budgetMs: number(),
25656
+ phases: array(NodeBenchmarkPhaseSchema).readonly(),
25657
+ steps: array(string()).readonly(),
25658
+ devices: array(NodeBenchmarkDevicePlanSchema).readonly(),
25659
+ /** Parallel sessions the cluster runs now, and what the other nodes can carry. */
25660
+ headroom: object({
25661
+ sessionsNow: number(),
25662
+ otherNodesCapacitySessions: number().nullable()
25663
+ }),
25664
+ refusals: array(NodeBenchmarkRefusalSchema).readonly(),
25665
+ canStart: boolean()
25666
+ });
25667
+ /** Where the measuring runner is. */
25668
+ var NodeBenchProgressSchema = object({
25669
+ phase: _enum([
25670
+ "preparing",
25671
+ "device",
25672
+ "pipeline",
25673
+ "cleanup"
25674
+ ]),
25675
+ deviceKey: string().nullable(),
25676
+ modelId: string().nullable(),
25677
+ /** Streams (device) or sessions (pipeline) of the step running now. */
25678
+ load: number().nullable(),
25679
+ cellsDone: number(),
25680
+ cellsTotal: number(),
25681
+ message: string()
25682
+ });
25683
+ /** What limited a ramp. `none` = the ramp ran out of steps still passing. */
25684
+ var NodeBenchLimitSchema = _enum([
25685
+ "p95",
25686
+ "drops",
25687
+ "errors",
25688
+ "stall",
25689
+ "cpu",
25690
+ "ram",
25691
+ "none"
25692
+ ]);
25693
+ var NodeBenchRampStepSchema = object({
25694
+ load: number(),
25695
+ pass: boolean(),
25696
+ p95Ms: number(),
25697
+ dropRatio: number(),
25698
+ /** Completed frames per second over the accounted window. */
25699
+ fps: number(),
25700
+ cpuPct: number().nullable(),
25701
+ ramFreePct: number().nullable(),
25702
+ failReasons: array(string()).readonly()
25703
+ });
25704
+ var NodeBenchCellSchema = discriminatedUnion("phase", [object({
25705
+ phase: literal("device"),
25706
+ deviceKey: string(),
25707
+ modelId: string(),
25708
+ /** Single-stream latency (the K = 1 step). */
25709
+ p50Ms: number(),
25710
+ p95Ms: number(),
25711
+ p99Ms: number(),
25712
+ /** Frames per second sustained at the last passing step. */
25713
+ fps: number(),
25714
+ /** Streams at the camera rate the device carried inside the frame interval; 0 = not even one. */
25715
+ kMax: number(),
25716
+ limitedBy: NodeBenchLimitSchema,
25717
+ /** First call (model load + warm-up). */
25718
+ warmupMs: number(),
25719
+ ramp: array(NodeBenchRampStepSchema).readonly()
25720
+ }), object({
25721
+ phase: literal("pipeline"),
25722
+ deviceKey: string(),
25723
+ modelId: string(),
25724
+ steps: array(string()).readonly(),
25725
+ /** Parallel sessions at the camera rate the node carried; 0 = not even one. */
25726
+ maxSessions: number(),
25727
+ limitedBy: NodeBenchLimitSchema,
25728
+ /** Detail steps that produced at least one result on the clip — a step that never fired measured nothing. */
25729
+ stepsFired: array(string()).readonly(),
25730
+ warmupMs: number(),
25731
+ ramp: array(NodeBenchRampStepSchema).readonly()
25732
+ })]);
25733
+ /**
25734
+ * What a result was measured ON. A result whose fingerprint no longer matches
25735
+ * the node no longer beats the table.
25736
+ */
25737
+ var NodeBenchFingerprintSchema = object({
25738
+ pipelineVersion: string().nullable(),
25739
+ cpuModel: string().nullable(),
25740
+ ramGb: number().nullable(),
25741
+ os: string().nullable(),
25742
+ /** Per device: the pool the cells ran on (runtime + tuning). */
25743
+ pools: record(string(), object({
25744
+ runtime: string(),
25745
+ numWorkers: number(),
25746
+ concurrency: number(),
25747
+ batchMode: string()
25748
+ })).default({})
25749
+ });
25750
+ /** The frames the run used. */
25751
+ var NodeBenchClipSchema = object({
25752
+ /** `camstack-bench-v1` or `reference-images` (the fallback when the clip is not reachable). */
25753
+ id: string(),
25754
+ revision: string().nullable(),
25755
+ frames: number(),
25756
+ width: number(),
25757
+ height: number()
25758
+ });
25759
+ var NodeBenchRunnerInputSchema = object({
25760
+ runId: string().min(1),
25761
+ devices: array(NodeBenchmarkDevicePlanSchema).min(1),
25762
+ phases: array(NodeBenchmarkPhaseSchema).min(1),
25763
+ /** The full detail step tree root for the `pipeline` measurement is built on the node from these ids. */
25764
+ steps: array(string()).default([]),
25765
+ fpsPerStream: number().int().min(1).max(30),
25766
+ /** The runner stops on its own past this instant, whatever the hub does. */
25767
+ deadlineAtMs: number(),
25768
+ settleSec: number().min(0).max(60).default(NODE_BENCHMARK_DEFAULTS.settleSec),
25769
+ measureSec: number().min(2).max(300).default(NODE_BENCHMARK_DEFAULTS.measureSec),
25770
+ keepDownloadedModels: boolean().default(false)
25771
+ });
25772
+ var NodeBenchRunnerStateSchema = _enum([
25773
+ "running",
25774
+ "done",
25775
+ "failed",
25776
+ "aborted"
25777
+ ]);
25778
+ var NodeBenchRunnerStatusSchema = object({
25779
+ runId: string(),
25780
+ nodeId: string(),
25781
+ state: NodeBenchRunnerStateSchema,
25782
+ progress: NodeBenchProgressSchema.nullable(),
25783
+ /** Every finished cell so far — a run stopped half way keeps what it measured. */
25784
+ cells: array(NodeBenchCellSchema).readonly(),
25785
+ fingerprint: NodeBenchFingerprintSchema.nullable(),
25786
+ clip: NodeBenchClipSchema.nullable(),
25787
+ /** Cameras seen running on the node during the run (they share its pools). */
25788
+ activeCameraIds: array(number()).readonly(),
25789
+ /** Models the run downloaded and then deleted (not installed before). */
25790
+ deletedModels: array(string()).readonly(),
25791
+ error: string().nullable(),
25792
+ startedAtMs: number(),
25793
+ finishedAtMs: number().nullable()
25794
+ });
25795
+ var NodeBenchmarkStateSchema = _enum([
25796
+ "draining",
25797
+ "measuring",
25798
+ "restoring",
25799
+ "done",
25800
+ "failed",
25801
+ "aborted"
25802
+ ]);
25803
+ /** Why a run ended. */
25804
+ var NodeBenchmarkEndReasonSchema = _enum([
25805
+ "completed",
25806
+ "operator-abort",
25807
+ "deadline",
25808
+ "node-lost",
25809
+ "runner-lost",
25810
+ "hub-restart",
25811
+ "drain-timeout",
25812
+ "failed"
25813
+ ]);
25814
+ var NodeBenchmarkRunSchema = object({
25815
+ runId: string(),
25816
+ nodeId: string(),
25817
+ state: NodeBenchmarkStateSchema,
25818
+ plan: NodeBenchmarkPlanSchema,
25819
+ startedAtMs: number(),
25820
+ deadlineAtMs: number(),
25821
+ finishedAtMs: number().nullable(),
25822
+ endReason: NodeBenchmarkEndReasonSchema.nullable(),
25823
+ error: string().nullable(),
25824
+ /** Cameras moved off the node for the run. */
25825
+ moved: array(number()).readonly(),
25826
+ /** Cameras that stayed (their only frame source is the node). */
25827
+ staying: array(number()).readonly(),
25828
+ /** Something else ran on the node's pools during the run. */
25829
+ contaminated: boolean(),
25830
+ progress: NodeBenchProgressSchema.nullable(),
25831
+ resultIds: array(string()).readonly()
25832
+ });
25833
+ /** `getNodeBenchmarkStatus`: the active run (or the last one), and whether benchmark mode is on. */
25834
+ var NodeBenchmarkStatusSchema = object({
25835
+ /** The node the cluster is in benchmark mode for; null = none. */
25836
+ modeNodeId: string().nullable(),
25837
+ run: NodeBenchmarkRunSchema.nullable()
25838
+ });
25839
+ /**
25840
+ * One stored measurement — the only authority Auto tune and the automatic
25841
+ * shares read measured capacity from. Written by the orchestrator when a run
25842
+ * ends, never by anyone else.
25843
+ */
25844
+ var NodeBenchmarkResultSchema = object({
25845
+ id: string(),
25846
+ runId: string(),
25847
+ nodeId: string(),
25848
+ cell: NodeBenchCellSchema,
25849
+ fpsPerStream: number(),
25850
+ fingerprint: NodeBenchFingerprintSchema.nullable(),
25851
+ clip: NodeBenchClipSchema.nullable(),
25852
+ /** Other work shared the node's pools (a camera that could not move, or the hub's own duties). */
25853
+ contaminated: boolean(),
25854
+ stayingCameras: array(number()).readonly(),
25855
+ /** The run ended early; the cell itself completed. */
25856
+ partialRun: boolean(),
25857
+ measuredAtMs: number()
25858
+ });
25859
+ var ListNodeBenchmarkResultsInputSchema = object({
25860
+ nodeId: string().optional(),
25861
+ deviceKey: string().optional()
25862
+ });
25863
+ /** A result with its standing today: does it still beat the table? */
25864
+ var NodeBenchmarkResultViewSchema = NodeBenchmarkResultSchema.extend({
25865
+ /** `fresh` beats the table; `stale` falls back to it; `superseded` = a newer row covers the same cell. */
25866
+ standing: _enum([
25867
+ "fresh",
25868
+ "stale",
25869
+ "superseded"
25870
+ ]),
25871
+ staleWhy: _enum([
25872
+ "age",
25873
+ "runtime",
25874
+ "pool-tuning"
25875
+ ]).nullable()
25876
+ });
25877
+ object({
25878
+ runId: string(),
25879
+ nodeId: string(),
25880
+ startedAtMs: number(),
25881
+ deadlineAtMs: number(),
25882
+ plan: NodeBenchmarkPlanSchema,
25883
+ moved: array(number()).readonly(),
25884
+ staying: array(number()).readonly()
25885
+ });
25886
+ /** Candidate detectors per device, for the plan editor. */
25887
+ var NodeBenchmarkCandidatesSchema = object({
25888
+ nodeId: string(),
25889
+ reachable: boolean(),
25890
+ devices: array(object({
25891
+ deviceKey: string(),
25892
+ enabled: boolean(),
25893
+ available: boolean(),
25894
+ /** Ordered: what Auto tune would pick first, then the rest of the family by accuracy. */
25895
+ suggested: array(string()).readonly(),
25896
+ /** Every root detector the device has a build for. */
25897
+ all: array(string()).readonly(),
25898
+ /** The model the device runs now, when pinned. */
25899
+ currentModelId: string().nullable()
25900
+ })).readonly(),
25901
+ /** Detail steps the node's catalog offers under the root (for the `pipeline` measurement). */
25902
+ steps: array(object({
25903
+ id: string(),
25904
+ name: string(),
25905
+ slot: string()
25906
+ })).readonly()
25907
+ });
23814
25908
  object({
23815
25909
  activeCameras: number(),
23816
25910
  throttledCameras: number(),
@@ -24728,14 +26822,15 @@ var RunnerLocalLoadSchema = object({
24728
26822
  * Per-inference-device live load (multi-device C4). One entry per deviceKey
24729
26823
  * this runner currently has attached cameras on, so the orchestrator's second
24730
26824
  * `balance()` pass (over a node's devices) weights on real per-pool session
24731
- * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
24732
- * per device is 0 until the pool backlog gets a public accessor (follow-up).
26825
+ * counts. Empty on single-device / pre-multi-device runners. There is no
26826
+ * per-device queue depth: the inference pool's backlog has no accessor, and
26827
+ * a field that always read 0 was removed rather than shown as a measurement
26828
+ * (D762).
24733
26829
  */
24734
26830
  devices: array(object({
24735
26831
  deviceKey: string(),
24736
26832
  backend: string(),
24737
- attachedCameras: number(),
24738
- queueDepthTotal: number()
26833
+ attachedCameras: number()
24739
26834
  })).default([]),
24740
26835
  /** Hardware capability flags reported by this node. */
24741
26836
  hardware: object({
@@ -24871,7 +26966,22 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
24871
26966
  */
24872
26967
  cropJpeg: string().optional(),
24873
26968
  parent: DetailParentSchema,
24874
- steps: array(string()).optional()
26969
+ steps: array(string()).optional(),
26970
+ /**
26971
+ * The camera's root model already drew this step's boxes (D730): a
26972
+ * `license_plate` inside the parent vehicle. The named step is NOT
26973
+ * inferred — these FRAME-space boxes are its output — and its
26974
+ * requested children (the plate reader) run on them. Absent ⇒ every
26975
+ * requested step infers as before.
26976
+ */
26977
+ supplied: object({
26978
+ stepId: string(),
26979
+ modelId: string(),
26980
+ boxes: array(object({
26981
+ bbox: NativeCropBboxSchema,
26982
+ score: number()
26983
+ })).min(1)
26984
+ }).optional()
24875
26985
  }), object({ details: array(DetailResultSchema) }).nullable(), { kind: "mutation" }), method(object({
24876
26986
  /** Catalog step id, e.g. `clip-embedding`. */
24877
26987
  stepId: string(),
@@ -24923,7 +27033,16 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
24923
27033
  }), ParkedTrackFrameSchema.nullable()), method(object({
24924
27034
  deviceId: number(),
24925
27035
  trackId: string()
24926
- }), object({ released: number().int().nonnegative() }), { kind: "mutation" });
27036
+ }), object({ released: number().int().nonnegative() }), { kind: "mutation" }), method(NodeBenchRunnerInputSchema, object({
27037
+ accepted: literal(true),
27038
+ runId: string()
27039
+ }), {
27040
+ kind: "mutation",
27041
+ auth: "admin"
27042
+ }), method(object({ runId: string() }), NodeBenchRunnerStatusSchema.nullable(), { auth: "admin" }), method(object({ runId: string() }), object({ aborted: boolean() }), {
27043
+ kind: "mutation",
27044
+ auth: "admin"
27045
+ });
24927
27046
  var CameraPipelineConfigSchema = object({
24928
27047
  engine: PipelineEngineChoiceSchema.optional(),
24929
27048
  steps: array(PipelineStepInputSchema).readonly(),
@@ -24934,14 +27053,6 @@ var CameraPipelineConfigSchema = object({
24934
27053
  settings: record(string(), unknown()).readonly().optional()
24935
27054
  }).nullable().optional()
24936
27055
  });
24937
- var PipelineTemplateSchema = object({
24938
- id: string(),
24939
- name: string(),
24940
- description: string().optional(),
24941
- config: CameraPipelineConfigSchema,
24942
- createdAt: string(),
24943
- updatedAt: string()
24944
- });
24945
27056
  var DeviceStepConfigSchema = object({
24946
27057
  modelId: string().optional(),
24947
27058
  settings: record(string(), unknown()).optional(),
@@ -25015,9 +27126,10 @@ var CameraStepOverridePatchSchema = object({
25015
27126
  modelId: string().optional(),
25016
27127
  settings: record(string(), unknown()).readonly().optional()
25017
27128
  });
27129
+ var CameraStepSettingsSchema = record(string(), record(string(), unknown()).readonly());
25018
27130
  var CameraPipelineSettingsSchema = object({
25019
- pinnedAgentNodeId: string().optional(),
25020
27131
  stepToggles: record(string(), boolean()).optional(),
27132
+ stepSettings: CameraStepSettingsSchema.optional(),
25021
27133
  stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
25022
27134
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
25023
27135
  });
@@ -25050,7 +27162,10 @@ var PipelineAssignmentSchema = object({
25050
27162
  });
25051
27163
  /**
25052
27164
  * Per-agent load summary surfaced to the load balancer + dashboards.
25053
- * Aggregated from each runner's `getLocalLoad` cap call.
27165
+ * Aggregated from each runner's `getLocalLoad` cap call. In the
27166
+ * `getAgentLoad` report, a single-device node's default-pool sessions (no
27167
+ * `deviceKey`) are credited to its one eligible device in `load.devices` —
27168
+ * the default pool IS that device (D745); the balancer's census stays raw.
25054
27169
  */
25055
27170
  var AgentLoadSummarySchema = object({
25056
27171
  nodeId: string(),
@@ -25139,26 +27254,27 @@ var CameraBrokerStatusSchema = object({
25139
27254
  width: number(),
25140
27255
  height: number(),
25141
27256
  subscribers: number(),
25142
- inFps: number(),
25143
- outFps: number()
27257
+ /**
27258
+ * Encoded packets/s the broker receives from the camera. null when the
27259
+ * broker's stats read failed — unknown, never 0 (a 0 here reads as a stall).
27260
+ * There is no output rate: decoding runs in the session decode workers, not
27261
+ * in the broker, so the broker's own decoded-frame rate is 0 on every live
27262
+ * camera and is not served (D758).
27263
+ */
27264
+ inFps: number().nullable()
25144
27265
  })).readonly(),
25145
27266
  webrtcSessions: number(),
25146
27267
  rtspRestream: boolean()
25147
27268
  });
25148
- /** Shared-memory ring statistics within the decoder block. */
25149
- var CameraDecoderShmSchema = object({
25150
- framesWritten: number(),
25151
- getFrameHits: number(),
25152
- getFrameMisses: number(),
25153
- budgetMb: number()
25154
- });
25155
- /** Decoder block — null when the decoder stage is unreachable or inactive. */
25156
- var CameraDecoderStatusSchema = object({
25157
- nodeId: string(),
25158
- formats: array(string()).readonly(),
25159
- sessionCount: number(),
25160
- shm: CameraDecoderShmSchema
25161
- });
27269
+ /**
27270
+ * Decoder block — WHERE decode runs (co-located with the detection node by
27271
+ * construction); null when the camera has no detection node. It carries no
27272
+ * frame counter: the shared-memory plane whose counters it used to serve is
27273
+ * gone, and those fields read 0 forever. Whether frames actually flow is read
27274
+ * from `detection.actualFps` (frames delivered to and processed by the
27275
+ * detector), never from a counter nothing feeds.
27276
+ */
27277
+ var CameraDecoderStatusSchema = object({ nodeId: string() });
25162
27278
  /** Motion block — null when motion detection is not active for this device. */
25163
27279
  var CameraMotionStatusSchema = object({
25164
27280
  enabled: boolean(),
@@ -25515,12 +27631,6 @@ method(object({
25515
27631
  }), object({ success: literal(true) }), {
25516
27632
  kind: "mutation",
25517
27633
  auth: "admin"
25518
- }), method(object({
25519
- agentNodeId: string(),
25520
- detectWeight: number().positive().nullable()
25521
- }), object({ success: literal(true) }), {
25522
- kind: "mutation",
25523
- auth: "admin"
25524
27634
  }), method(object({
25525
27635
  agentNodeId: string(),
25526
27636
  detect: boolean().nullable().optional(),
@@ -25562,6 +27672,30 @@ method(object({
25562
27672
  }), {
25563
27673
  kind: "mutation",
25564
27674
  auth: "admin"
27675
+ }), method(AutoTuneOptionsSchema, AutoTunePlanSchema, { auth: "admin" }), method(AutoTuneApplyInputSchema, AutoTuneApplyResultSchema, {
27676
+ kind: "mutation",
27677
+ auth: "admin"
27678
+ }), method(LoadBalancingSharesInputSchema, object({ success: literal(true) }), {
27679
+ kind: "mutation",
27680
+ auth: "admin"
27681
+ }), method(PlacementStatsInputSchema, PlacementStatsSchema, { auth: "admin" }), method(SuggestSharesInputSchema, SuggestSharesResultSchema, { auth: "admin" }), method(_void(), EffectiveSharesSchema, { auth: "admin" }), method(SetShareSourceInputSchema, object({ success: literal(true) }), {
27682
+ kind: "mutation",
27683
+ auth: "admin"
27684
+ }), method(NodeBenchmarkPlanSchema, NodeBenchmarkPreflightSchema, { auth: "admin" }), method(NodeBenchmarkPlanSchema, discriminatedUnion("kind", [object({
27685
+ kind: literal("started"),
27686
+ runId: string()
27687
+ }), object({
27688
+ kind: literal("refused"),
27689
+ preflight: NodeBenchmarkPreflightSchema
27690
+ })]), {
27691
+ kind: "mutation",
27692
+ auth: "admin"
27693
+ }), method(_void(), NodeBenchmarkStatusSchema, { auth: "admin" }), method(object({ runId: string() }), object({ aborted: boolean() }), {
27694
+ kind: "mutation",
27695
+ auth: "admin"
27696
+ }), method(object({ nodeId: string() }), NodeBenchmarkCandidatesSchema, { auth: "admin" }), method(ListNodeBenchmarkResultsInputSchema, array(NodeBenchmarkResultViewSchema).readonly(), { auth: "admin" }), method(object({ id: string() }), object({ deleted: boolean() }), {
27697
+ kind: "mutation",
27698
+ auth: "admin"
25565
27699
  }), method(object({ deviceId: number() }), CameraPipelineSettingsSchema.nullable()), method(object({
25566
27700
  deviceId: number(),
25567
27701
  addonId: string(),
@@ -25578,6 +27712,13 @@ method(object({
25578
27712
  }), object({ success: literal(true) }), {
25579
27713
  kind: "mutation",
25580
27714
  auth: "admin"
27715
+ }), method(object({
27716
+ deviceId: number(),
27717
+ addonId: string(),
27718
+ settings: record(string(), unknown()).nullable()
27719
+ }), object({ success: literal(true) }), {
27720
+ kind: "mutation",
27721
+ auth: "admin"
25581
27722
  }), method(object({
25582
27723
  deviceId: number(),
25583
27724
  agentNodeId: string(),
@@ -25601,25 +27742,7 @@ method(object({
25601
27742
  }), method(_void(), DiskReconcileJobSchema, {
25602
27743
  kind: "mutation",
25603
27744
  auth: "admin"
25604
- }), method(_void(), DiskReconcileJobSchema, { auth: "admin" }), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
25605
- name: string(),
25606
- description: string().optional(),
25607
- config: CameraPipelineConfigSchema
25608
- }), PipelineTemplateSchema, {
25609
- kind: "mutation",
25610
- auth: "admin"
25611
- }), method(object({
25612
- id: string(),
25613
- name: string().optional(),
25614
- description: string().optional(),
25615
- config: CameraPipelineConfigSchema.optional()
25616
- }), PipelineTemplateSchema, {
25617
- kind: "mutation",
25618
- auth: "admin"
25619
- }), method(object({ id: string() }), object({ success: literal(true) }), {
25620
- kind: "mutation",
25621
- auth: "admin"
25622
- });
27745
+ }), method(_void(), DiskReconcileJobSchema, { auth: "admin" });
25623
27746
  /**
25624
27747
  * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
25625
27748
  * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
@@ -26313,7 +28436,19 @@ var SsoBridgeClaimsSchema = object({
26313
28436
  * editing a descriptor must not retroactively extend or shorten a link the
26314
28437
  * operator already consented to.
26315
28438
  */
26316
- refreshTtl: union([number().int().positive(), literal("never")]).optional()
28439
+ refreshTtl: union([number().int().positive(), literal("never")]).optional(),
28440
+ /**
28441
+ * Opaque handle of a hub-side LINK intent (D782). Set by an OIDC callback
28442
+ * that was started from a signed-in user's "Link" button: the finish then
28443
+ * links this identity to the intent's user instead of signing anyone in.
28444
+ * The handle carries no user id — the hub resolves it, single-use.
28445
+ */
28446
+ linkIntent: string().optional(),
28447
+ /**
28448
+ * Verified `iss` of the id_token behind an OIDC login bridge (D782). A
28449
+ * subject is unique only per issuer, so a link is bound to it.
28450
+ */
28451
+ issuer: string().optional()
26317
28452
  });
26318
28453
  method(object({
26319
28454
  claims: SsoBridgeClaimsSchema,
@@ -28016,8 +30151,6 @@ var WebrtcStreamChoiceSchema = object({
28016
30151
  ]).nullable(),
28017
30152
  /** Input FPS from the camera (null for adaptive). */
28018
30153
  inputFps: number().nullable(),
28019
- /** Decoded FPS (null for adaptive). */
28020
- decodeFps: number().nullable(),
28021
30154
  /** Bitrate in kbps (null for adaptive). */
28022
30155
  bitrateKbps: number().nullable()
28023
30156
  });
@@ -28294,7 +30427,22 @@ DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), ar
28294
30427
  * field their hints were silently stripped by the schema and the
28295
30428
  * adaptive ladder started blind on the client's display size.
28296
30429
  */
28297
- hints: webrtcClientHintsSchema.optional()
30430
+ hints: webrtcClientHintsSchema.optional(),
30431
+ /**
30432
+ * Talk-back (D771): relay the PEER's audio to the device's speaker
30433
+ * through the `intercom` cap's push-talk triple. When `true` and the
30434
+ * offer lets the peer send audio, the answer's audio is `sendrecv`
30435
+ * and every inbound frame goes to `intercom.pushTalkAudio` for this
30436
+ * `deviceId`; absent/false answers `sendonly`, as for every viewer.
30437
+ *
30438
+ * TRUSTED-CALLER ONLY. Set by the Alexa export for an Echo on a camera
30439
+ * with `intercom`, when the linked user may control that camera. The
30440
+ * hub STRIPS it from every request-bound call (a browser), exactly as
30441
+ * it overwrites `relayOnly` — a viewer's talk permission is the
30442
+ * intercom cap's own surface (share tokens gate it there), and this
30443
+ * flag must not become a second door to the camera's speaker.
30444
+ */
30445
+ talkback: boolean().optional()
28298
30446
  }), object({
28299
30447
  sessionId: string(),
28300
30448
  sdpAnswer: string()
@@ -28349,6 +30497,14 @@ DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), ar
28349
30497
  */
28350
30498
  alive: boolean()
28351
30499
  })), method(object({
30500
+ deviceId: number().int().nonnegative(),
30501
+ sessionId: string(),
30502
+ downlinkMbps: number().nonnegative()
30503
+ }), object({
30504
+ outcome: _enum(["switching", "held"]),
30505
+ tier: CamProfileSchema.nullable(),
30506
+ reason: string()
30507
+ }), { kind: "mutation" }), method(object({
28352
30508
  deviceId: number().int().nonnegative(),
28353
30509
  sessionId: string()
28354
30510
  }), WebrtcSessionDebugSchema, { kind: "query" });
@@ -28588,6 +30744,59 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
28588
30744
  var RestartAddonResultSchema = unknown();
28589
30745
  var InstallPackageResultSchema = unknown();
28590
30746
  var ReloadPackagesResultSchema = unknown();
30747
+ /**
30748
+ * What an uninstall did on ONE node.
30749
+ * - `uninstalled` — removed: runner(s) stopped, caps gone, code deleted.
30750
+ * - `not-installed` — the node did not have it (nothing to do).
30751
+ * - `refused` — protected / bootstrap package; nothing touched.
30752
+ * - `failed` — attempted and did not complete; `error` says why.
30753
+ * - `pending-reconnect` — the node is OFFLINE: its record was pruned so the
30754
+ * back-fill never returns the package, and the stale-addon sweep removes the
30755
+ * copy when the node registers again (only once the hub no longer has it).
30756
+ */
30757
+ var UninstallNodeOutcomeSchema = _enum([
30758
+ "uninstalled",
30759
+ "not-installed",
30760
+ "refused",
30761
+ "failed",
30762
+ "pending-reconnect"
30763
+ ]);
30764
+ var UninstallNodeResultSchema = object({
30765
+ nodeId: string(),
30766
+ outcome: UninstallNodeOutcomeSchema,
30767
+ error: string().optional(),
30768
+ addonIds: array(string()).readonly(),
30769
+ runnersStopped: array(string()).readonly(),
30770
+ warnings: array(string()).readonly()
30771
+ });
30772
+ var UninstallPackageResultSchema = object({
30773
+ /** Every targeted node ended `uninstalled`, `not-installed` or `pending-reconnect`. */
30774
+ success: boolean(),
30775
+ packageName: string(),
30776
+ results: array(UninstallNodeResultSchema).readonly()
30777
+ });
30778
+ /**
30779
+ * `unreachable` lists online agents whose status read FAILED — absence from
30780
+ * `installations` is only "not installed" for a node that answered (D315).
30781
+ */
30782
+ var PackageInstallationsSchema = object({
30783
+ installations: array(object({
30784
+ packageName: string(),
30785
+ nodeId: string(),
30786
+ nodeName: string(),
30787
+ isHub: boolean(),
30788
+ isOnline: boolean(),
30789
+ version: string().nullable(),
30790
+ addonIds: array(string()).readonly(),
30791
+ source: _enum(["live", "recorded"]),
30792
+ /** The hub installs this package too. `false` on an agent row = an orphan. */
30793
+ onHub: boolean()
30794
+ })).readonly(),
30795
+ unreachable: array(object({
30796
+ nodeId: string(),
30797
+ error: string()
30798
+ })).readonly()
30799
+ });
28591
30800
  var BulkUpdateItemStatusSchema = _enum([
28592
30801
  "queued",
28593
30802
  "updating",
@@ -28670,10 +30879,14 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
28670
30879
  }), method(object({ packageName: string() }), InstallPackageResultSchema, {
28671
30880
  kind: "mutation",
28672
30881
  auth: "admin"
28673
- }), method(_void(), boolean()), method(_void(), array(unknown()).readonly()), method(object({ packageName: string() }), unknown(), {
30882
+ }), method(_void(), boolean()), method(_void(), array(unknown()).readonly()), method(object({
30883
+ packageName: string().min(1),
30884
+ nodeId: string().optional(),
30885
+ everywhere: boolean().optional()
30886
+ }), UninstallPackageResultSchema, {
28674
30887
  kind: "mutation",
28675
30888
  auth: "admin"
28676
- }), method(_void(), ReloadPackagesResultSchema, {
30889
+ }), method(object({ packageName: string().optional() }), PackageInstallationsSchema, { auth: "admin" }), method(_void(), ReloadPackagesResultSchema, {
28677
30890
  kind: "mutation",
28678
30891
  auth: "admin"
28679
30892
  }), method(object({ query: string().optional() }), array(SearchResultSchema)), method(object({ nodeId: string().optional() }), array(PackageUpdateSchema).readonly(), { auth: "admin" }), method(object({
@@ -31738,6 +33951,9 @@ var CreateIntegrationInputSchema = object({
31738
33951
  enabled: boolean().optional(),
31739
33952
  info: record(string(), unknown()).optional(),
31740
33953
  settings: record(string(), unknown()).optional(),
33954
+ /** Keys of `settings` the form's schema declares secret (by type) — kept in
33955
+ * the keychain, not the table (D787). */
33956
+ secretKeys: array(string()).optional(),
31741
33957
  skipRestart: boolean().optional()
31742
33958
  });
31743
33959
  var UpdateIntegrationInputSchema = object({
@@ -31761,7 +33977,9 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
31761
33977
  auth: "admin"
31762
33978
  }), method(object({ id: string() }), record(string(), unknown()), { auth: "admin" }), method(object({
31763
33979
  id: string(),
31764
- settings: record(string(), unknown())
33980
+ settings: record(string(), unknown()),
33981
+ /** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
33982
+ secretKeys: array(string()).optional()
31765
33983
  }), object({ success: boolean() }), {
31766
33984
  kind: "mutation",
31767
33985
  auth: "admin"
@@ -32160,7 +34378,8 @@ var ConnectionEndpointSchema = object({
32160
34378
  "lan-ipv6",
32161
34379
  "public",
32162
34380
  "loopback",
32163
- "app-link"
34381
+ "app-link",
34382
+ "custom"
32164
34383
  ]),
32165
34384
  /**
32166
34385
  * Originating-interface kind — `lan`/`wifi`/`docker`/`vpn`/`other`/
@@ -32229,7 +34448,7 @@ var GetConnectionEndpointsResultSchema = object({
32229
34448
  localPort: AdvertisedLocalPortSchema
32230
34449
  });
32231
34450
  /**
32232
- * The address flagged for notification links. `baseUrl: null` = AUTO, which
34451
+ * The address flagged for notification taps (`notificationTaps`). `baseUrl: null` = AUTO, which
32233
34452
  * since D709 is the CamStack app link; `resolved` reports the effective value,
32234
34453
  * so the UI can show it either way.
32235
34454
  */
@@ -32262,6 +34481,72 @@ var ViewerEndpointsSchema = object({
32262
34481
  /** What the ranking currently resolves to (may be empty if nothing is up). */
32263
34482
  resolved: array(string()).readonly()
32264
34483
  });
34484
+ /**
34485
+ * DEPRECATED compatibility shape of `getHubAddresses` (D729 amendment, kept
34486
+ * for addons built against it): `cluster` is the `internal` role, `browser`
34487
+ * the `publicEndpoint` role; `override` is the flagged address.
34488
+ */
34489
+ var HubAddressSnapshotSchema = object({
34490
+ override: string().nullable(),
34491
+ resolved: string().nullable(),
34492
+ source: _enum([
34493
+ "override",
34494
+ "auto",
34495
+ "none"
34496
+ ])
34497
+ });
34498
+ var HubAddressesSchema = object({
34499
+ cluster: HubAddressSnapshotSchema,
34500
+ browser: HubAddressSnapshotSchema
34501
+ });
34502
+ /**
34503
+ * What an address is used for (`hub-address.ts`, D734). `appConnections` is
34504
+ * many-valued; `notificationTaps`, `publicEndpoint` and `internal` are carried
34505
+ * by at most one address.
34506
+ */
34507
+ var HubAddressRoleSchema = _enum(HUB_ADDRESS_ROLES);
34508
+ var RoleSourceSchema = _enum([
34509
+ "flagged",
34510
+ "auto",
34511
+ "none"
34512
+ ]);
34513
+ /** One single-valued role: the flagged address, the one in use, and why. */
34514
+ var HubAddressRoleResolutionSchema = object({
34515
+ /** The address carrying the flag, or null = automatic. */
34516
+ flagged: string().nullable(),
34517
+ /** The address in use, or null when nothing qualifies (never loopback). */
34518
+ resolved: string().nullable(),
34519
+ source: RoleSourceSchema
34520
+ });
34521
+ var HubAppConnectionsResolutionSchema = object({
34522
+ /** The addresses flagged `appConnections`, or empty = automatic. */
34523
+ flagged: array(string()).readonly(),
34524
+ /** What the apps race right now. */
34525
+ resolved: array(string()).readonly(),
34526
+ source: RoleSourceSchema
34527
+ });
34528
+ var HubAddressBookSchema = object({
34529
+ rows: array(ConnectionEndpointSchema.extend({
34530
+ /** In the hub's detected palette right now. */
34531
+ detected: boolean(),
34532
+ /** Entered by the operator (removable). */
34533
+ custom: boolean(),
34534
+ /** The roles explicitly flagged on this address. */
34535
+ flags: array(HubAddressRoleSchema).readonly(),
34536
+ /** The roles this address serves right now — flagged or automatic. */
34537
+ inUse: array(HubAddressRoleSchema).readonly(),
34538
+ /** The roles this address may be flagged with. */
34539
+ eligible: array(HubAddressRoleSchema).readonly()
34540
+ })).readonly(),
34541
+ roles: object({
34542
+ appConnections: HubAppConnectionsResolutionSchema,
34543
+ notificationTaps: HubAddressRoleResolutionSchema,
34544
+ internal: HubAddressRoleResolutionSchema,
34545
+ publicEndpoint: HubAddressRoleResolutionSchema
34546
+ }),
34547
+ /** The `hub=` an app link carries (D709), or null. */
34548
+ appLinkHub: string().nullable()
34549
+ });
32265
34550
  var AllowedAddressesSchema = object({
32266
34551
  /**
32267
34552
  * Allowlist of interface addresses operators have explicitly opted
@@ -32305,10 +34590,27 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
32305
34590
  * tunnel always emits `https://` regardless. */
32306
34591
  scheme: _enum(["http", "https"]).optional(),
32307
34592
  /** Append the CamStack app link entry (`kind: 'app-link'`, D709) —
32308
- * an address notification links can be flagged with, not a hub
34593
+ * an address notification taps can be flagged with, not a hub
32309
34594
  * route. Default `false`: racers and rankers never see it. */
32310
34595
  includeAppLink: boolean().optional()
32311
- }), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), ViewerEndpointsSchema), method(object({ baseUrls: array(string()).readonly() }), ViewerEndpointsSchema, { kind: "mutation" }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" }), method(_void(), TlsStatusSchema), method(object({ reason: string().optional() }), TlsStatusSchema, {
34596
+ }), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), ViewerEndpointsSchema), method(object({ baseUrls: array(string()).readonly() }), ViewerEndpointsSchema, { kind: "mutation" }), method(object({ scheme: _enum(["http", "https"]).optional() }).optional(), HubAddressBookSchema), method(object({
34597
+ baseUrl: string(),
34598
+ label: string().max(80)
34599
+ }), HubAddressBookSchema, {
34600
+ kind: "mutation",
34601
+ auth: "admin"
34602
+ }), method(object({ baseUrl: string() }), HubAddressBookSchema, {
34603
+ kind: "mutation",
34604
+ auth: "admin"
34605
+ }), method(object({
34606
+ role: HubAddressRoleSchema,
34607
+ baseUrl: string().nullable(),
34608
+ enabled: boolean(),
34609
+ acceptInsecureHttp: boolean().optional()
34610
+ }), HubAddressBookSchema, {
34611
+ kind: "mutation",
34612
+ auth: "admin"
34613
+ }), method(_void(), HubAddressesSchema), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" }), method(_void(), TlsStatusSchema), method(object({ reason: string().optional() }), TlsStatusSchema, {
32312
34614
  kind: "mutation",
32313
34615
  auth: "admin"
32314
34616
  }), method(object({
@@ -32766,7 +35068,8 @@ var MotionStatusSchema = object({
32766
35068
  * writes this slice (not even a boot seed), and neither does the runner's
32767
35069
  * phase machine: its phase is "a session is attached" for every on-motion
32768
35070
  * session, not motion, and it now has its own `DetectionPhaseTransition`.
32769
- * A provider's `isDetected` answers from the hub's slice.
35071
+ * A provider's `isDetected` and `getStatus` answer from the hub's slice
35072
+ * (`hubMotionNativeProvider`).
32770
35073
  * - **Sensors** (Home Assistant binary sensors, Homematic, Matter, Alexa
32771
35074
  * imports) — the provider pushes both edges itself, from the upstream
32772
35075
  * system's own state change. That is the sensor's own fact.
@@ -38498,6 +40801,32 @@ var TotpStatusSchema = object({
38498
40801
  /** Null when no row exists OR the row is still pending confirmation. */
38499
40802
  confirmedAt: number().nullable()
38500
40803
  });
40804
+ /**
40805
+ * user-management — singleton capability for user accounts, API keys,
40806
+ * and scoped tokens.
40807
+ *
40808
+ * v2 model: every method gated by `auth: 'admin'`. Admin-only delegation
40809
+ * — the subset check on `setUserScopes` / `createScopedToken` is moot
40810
+ * because non-admins never reach these. If/when we open non-admin
40811
+ * delegation, structural caller-scope subset check goes inside the
40812
+ * provider.
40813
+ */
40814
+ var ExternalIdentitySchema = object({
40815
+ /** Login-method id of the provider instance, e.g. `auth-oidc/authentik`. */
40816
+ provider: string(),
40817
+ /** The IdP's stable subject (`sub`) for this account. */
40818
+ subject: string(),
40819
+ /** The verified `iss` of the IdP the identity was linked under — `sub` is unique only per issuer. */
40820
+ issuer: string(),
40821
+ userId: string(),
40822
+ /** What the IdP called the account at link time (username / email) — display only. */
40823
+ label: string().optional(),
40824
+ linkedAt: number()
40825
+ });
40826
+ var ExternalIdentityKeySchema = object({
40827
+ provider: string().min(1),
40828
+ subject: string().min(1)
40829
+ });
38501
40830
  method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserInputSchema, UserSummarySchema, {
38502
40831
  kind: "mutation",
38503
40832
  auth: "admin",
@@ -38611,7 +40940,22 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserI
38611
40940
  userId: string(),
38612
40941
  username: string(),
38613
40942
  scopes: array(TokenScopeSchema)
38614
- }).nullable(), { access: "view" }), method(_void(), array(OauthSessionSummarySchema), { auth: "admin" }), method(object({ id: string() }), object({ success: boolean() }), {
40943
+ }).nullable(), { access: "view" }), method(object({ userId: string() }), array(ExternalIdentitySchema), { auth: "admin" }), method(ExternalIdentityKeySchema, object({
40944
+ userId: string(),
40945
+ issuer: string()
40946
+ }).nullable(), { auth: "admin" }), method(ExternalIdentityKeySchema.extend({
40947
+ userId: string(),
40948
+ issuer: string().min(1),
40949
+ label: string().optional()
40950
+ }), object({ success: literal(true) }), {
40951
+ kind: "mutation",
40952
+ auth: "admin",
40953
+ access: "create"
40954
+ }), method(ExternalIdentityKeySchema.extend({ userId: string() }), object({ success: boolean() }), {
40955
+ kind: "mutation",
40956
+ auth: "admin",
40957
+ access: "delete"
40958
+ }), method(_void(), array(OauthSessionSummarySchema), { auth: "admin" }), method(object({ id: string() }), object({ success: boolean() }), {
38615
40959
  kind: "mutation",
38616
40960
  auth: "admin",
38617
40961
  access: "delete"
@@ -39313,6 +41657,16 @@ var zoneRulesCapability = {
39313
41657
  durability: "restored"
39314
41658
  };
39315
41659
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
41660
+ Object.freeze({
41661
+ person: .5,
41662
+ vehicle: .5,
41663
+ animal: .5
41664
+ });
41665
+ Object.freeze({
41666
+ person: .65,
41667
+ vehicle: .65,
41668
+ animal: .65
41669
+ });
39316
41670
  /**
39317
41671
  * Accessory device helpers — shared across drivers.
39318
41672
  *
@@ -39788,7 +42142,8 @@ var BaseDevice = class {
39788
42142
  this.role = options.role;
39789
42143
  this.parentDeviceId = ctx.parentDeviceId;
39790
42144
  const seedData = ctx.persistedConfig ?? {};
39791
- this.config = DeviceConfig.fromSchema(schema, (data) => ctx.persistConfig(data), seedData, ({ droppedKeys, issues }) => {
42145
+ const declaredSecretPaths = collectZodSecretPaths(schema);
42146
+ this.config = DeviceConfig.fromSchema(schema, (data) => ctx.persistConfig(data, { secretConfigPaths: declaredSecretPaths }), seedData, ({ droppedKeys, issues }) => {
39792
42147
  ctx.logger.warn("Device config recovery: dropping invalid persisted fields", {
39793
42148
  tags: {
39794
42149
  deviceId: ctx.id,
@@ -40534,6 +42889,12 @@ Object.freeze({
40534
42889
  addonId: null,
40535
42890
  access: "view"
40536
42891
  },
42892
+ "addons.listInstallations": {
42893
+ capName: "addons",
42894
+ capScope: "system",
42895
+ addonId: null,
42896
+ access: "view"
42897
+ },
40537
42898
  "addons.listJobs": {
40538
42899
  capName: "addons",
40539
42900
  capScope: "system",
@@ -41716,90 +44077,18 @@ Object.freeze({
41716
44077
  addonId: null,
41717
44078
  access: "create"
41718
44079
  },
41719
- "decoder.createSession": {
41720
- capName: "decoder",
41721
- capScope: "system",
41722
- addonId: null,
41723
- access: "view"
41724
- },
41725
- "decoder.destroySession": {
41726
- capName: "decoder",
41727
- capScope: "system",
41728
- addonId: null,
41729
- access: "delete"
41730
- },
41731
- "decoder.getFrame": {
41732
- capName: "decoder",
41733
- capScope: "system",
41734
- addonId: null,
41735
- access: "view"
41736
- },
41737
44080
  "decoder.getInfo": {
41738
44081
  capName: "decoder",
41739
44082
  capScope: "system",
41740
44083
  addonId: null,
41741
44084
  access: "view"
41742
44085
  },
41743
- "decoder.getShmStats": {
41744
- capName: "decoder",
41745
- capScope: "system",
41746
- addonId: null,
41747
- access: "view"
41748
- },
41749
- "decoder.getStats": {
41750
- capName: "decoder",
41751
- capScope: "system",
41752
- addonId: null,
41753
- access: "view"
41754
- },
41755
- "decoder.listActiveSessions": {
41756
- capName: "decoder",
41757
- capScope: "system",
41758
- addonId: null,
41759
- access: "view"
41760
- },
41761
- "decoder.openStream": {
41762
- capName: "decoder",
41763
- capScope: "system",
41764
- addonId: null,
41765
- access: "view"
41766
- },
41767
- "decoder.pullFrames": {
41768
- capName: "decoder",
41769
- capScope: "system",
41770
- addonId: null,
41771
- access: "view"
41772
- },
41773
- "decoder.pullHandles": {
41774
- capName: "decoder",
41775
- capScope: "system",
41776
- addonId: null,
41777
- access: "view"
41778
- },
41779
- "decoder.pushPacket": {
41780
- capName: "decoder",
41781
- capScope: "system",
41782
- addonId: null,
41783
- access: "view"
41784
- },
41785
44086
  "decoder.reprobeHwaccel": {
41786
44087
  capName: "decoder",
41787
44088
  capScope: "system",
41788
44089
  addonId: null,
41789
44090
  access: "create"
41790
44091
  },
41791
- "decoder.supportsCodec": {
41792
- capName: "decoder",
41793
- capScope: "system",
41794
- addonId: null,
41795
- access: "view"
41796
- },
41797
- "decoder.updateConfig": {
41798
- capName: "decoder",
41799
- capScope: "system",
41800
- addonId: null,
41801
- access: "view"
41802
- },
41803
44092
  "detectionPipeline.applyDeviceSettingsPatch": {
41804
44093
  capName: "detection-pipeline",
41805
44094
  capScope: "device",
@@ -43066,6 +45355,96 @@ Object.freeze({
43066
45355
  addonId: null,
43067
45356
  access: "create"
43068
45357
  },
45358
+ "keychain.delete": {
45359
+ capName: "keychain",
45360
+ capScope: "system",
45361
+ addonId: null,
45362
+ access: "delete"
45363
+ },
45364
+ "keychain.getState": {
45365
+ capName: "keychain",
45366
+ capScope: "system",
45367
+ addonId: null,
45368
+ access: "view"
45369
+ },
45370
+ "keychain.listMeta": {
45371
+ capName: "keychain",
45372
+ capScope: "system",
45373
+ addonId: null,
45374
+ access: "view"
45375
+ },
45376
+ "keychain.put": {
45377
+ capName: "keychain",
45378
+ capScope: "system",
45379
+ addonId: null,
45380
+ access: "create"
45381
+ },
45382
+ "keychain.resolve": {
45383
+ capName: "keychain",
45384
+ capScope: "system",
45385
+ addonId: null,
45386
+ access: "view"
45387
+ },
45388
+ "keychainAdmin.deleteOrphans": {
45389
+ capName: "keychain-admin",
45390
+ capScope: "system",
45391
+ addonId: null,
45392
+ access: "delete"
45393
+ },
45394
+ "keychainAdmin.downloadRecoveryKey": {
45395
+ capName: "keychain-admin",
45396
+ capScope: "system",
45397
+ addonId: null,
45398
+ access: "create"
45399
+ },
45400
+ "keychainAdmin.listAudit": {
45401
+ capName: "keychain-admin",
45402
+ capScope: "system",
45403
+ addonId: null,
45404
+ access: "view"
45405
+ },
45406
+ "keychainAdmin.listSecrets": {
45407
+ capName: "keychain-admin",
45408
+ capScope: "system",
45409
+ addonId: null,
45410
+ access: "view"
45411
+ },
45412
+ "keychainAdmin.purgeRetiredKeys": {
45413
+ capName: "keychain-admin",
45414
+ capScope: "system",
45415
+ addonId: null,
45416
+ access: "delete"
45417
+ },
45418
+ "keychainAdmin.resetKeychain": {
45419
+ capName: "keychain-admin",
45420
+ capScope: "system",
45421
+ addonId: null,
45422
+ access: "delete"
45423
+ },
45424
+ "keychainAdmin.reveal": {
45425
+ capName: "keychain-admin",
45426
+ capScope: "system",
45427
+ addonId: null,
45428
+ access: "create"
45429
+ },
45430
+ "keychainAdmin.rotateMasterKey": {
45431
+ capName: "keychain-admin",
45432
+ capScope: "system",
45433
+ addonId: null,
45434
+ access: "create"
45435
+ },
45436
+ "keychainAdmin.setSystemSecret": {
45437
+ capName: "keychain-admin",
45438
+ capScope: "system",
45439
+ addonId: null,
45440
+ access: "create"
45441
+ },
45442
+ "keychainAdmin.status": {
45443
+ capName: "keychain-admin",
45444
+ capScope: "system",
45445
+ addonId: null,
45446
+ access: "view"
45447
+ },
43069
45448
  "lawnMowerControl.dock": {
43070
45449
  capName: "lawn-mower-control",
43071
45450
  capScope: "device",
@@ -43288,12 +45667,24 @@ Object.freeze({
43288
45667
  addonId: null,
43289
45668
  access: "view"
43290
45669
  },
45670
+ "localNetwork.addCustomAddress": {
45671
+ capName: "local-network",
45672
+ capScope: "system",
45673
+ addonId: null,
45674
+ access: "create"
45675
+ },
43291
45676
  "localNetwork.downloadCa": {
43292
45677
  capName: "local-network",
43293
45678
  capScope: "system",
43294
45679
  addonId: null,
43295
45680
  access: "view"
43296
45681
  },
45682
+ "localNetwork.getAddresses": {
45683
+ capName: "local-network",
45684
+ capScope: "system",
45685
+ addonId: null,
45686
+ access: "view"
45687
+ },
43297
45688
  "localNetwork.getAllowedAddresses": {
43298
45689
  capName: "local-network",
43299
45690
  capScope: "system",
@@ -43306,6 +45697,12 @@ Object.freeze({
43306
45697
  addonId: null,
43307
45698
  access: "view"
43308
45699
  },
45700
+ "localNetwork.getHubAddresses": {
45701
+ capName: "local-network",
45702
+ capScope: "system",
45703
+ addonId: null,
45704
+ access: "view"
45705
+ },
43309
45706
  "localNetwork.getNotificationEndpoint": {
43310
45707
  capName: "local-network",
43311
45708
  capScope: "system",
@@ -43342,6 +45739,12 @@ Object.freeze({
43342
45739
  addonId: null,
43343
45740
  access: "create"
43344
45741
  },
45742
+ "localNetwork.removeCustomAddress": {
45743
+ capName: "local-network",
45744
+ capScope: "system",
45745
+ addonId: null,
45746
+ access: "delete"
45747
+ },
43345
45748
  "localNetwork.resetAllowlistToBestMatch": {
43346
45749
  capName: "local-network",
43347
45750
  capScope: "system",
@@ -43354,6 +45757,12 @@ Object.freeze({
43354
45757
  addonId: null,
43355
45758
  access: "create"
43356
45759
  },
45760
+ "localNetwork.setAddressRole": {
45761
+ capName: "local-network",
45762
+ capScope: "system",
45763
+ addonId: null,
45764
+ access: "create"
45765
+ },
43357
45766
  "localNetwork.setAllowedAddresses": {
43358
45767
  capName: "local-network",
43359
45768
  capScope: "system",
@@ -44410,6 +46819,18 @@ Object.freeze({
44410
46819
  addonId: null,
44411
46820
  access: "create"
44412
46821
  },
46822
+ "pipelineAnalytics.addRetrainFrameFromRecording": {
46823
+ capName: "pipeline-analytics",
46824
+ capScope: "device",
46825
+ addonId: null,
46826
+ access: "create"
46827
+ },
46828
+ "pipelineAnalytics.addRetrainFramesToCollection": {
46829
+ capName: "pipeline-analytics",
46830
+ capScope: "device",
46831
+ addonId: null,
46832
+ access: "create"
46833
+ },
44413
46834
  "pipelineAnalytics.applyDeviceSettingsPatch": {
44414
46835
  capName: "pipeline-analytics",
44415
46836
  capScope: "device",
@@ -44458,12 +46879,36 @@ Object.freeze({
44458
46879
  addonId: null,
44459
46880
  access: "view"
44460
46881
  },
46882
+ "pipelineAnalytics.createRetrainCollection": {
46883
+ capName: "pipeline-analytics",
46884
+ capScope: "device",
46885
+ addonId: null,
46886
+ access: "create"
46887
+ },
46888
+ "pipelineAnalytics.createRetrainDatasetVersion": {
46889
+ capName: "pipeline-analytics",
46890
+ capScope: "device",
46891
+ addonId: null,
46892
+ access: "create"
46893
+ },
44461
46894
  "pipelineAnalytics.deleteDeviceEvents": {
44462
46895
  capName: "pipeline-analytics",
44463
46896
  capScope: "device",
44464
46897
  addonId: null,
44465
46898
  access: "delete"
44466
46899
  },
46900
+ "pipelineAnalytics.deleteRetrainCollection": {
46901
+ capName: "pipeline-analytics",
46902
+ capScope: "device",
46903
+ addonId: null,
46904
+ access: "delete"
46905
+ },
46906
+ "pipelineAnalytics.deleteRetrainDatasetVersion": {
46907
+ capName: "pipeline-analytics",
46908
+ capScope: "device",
46909
+ addonId: null,
46910
+ access: "delete"
46911
+ },
44467
46912
  "pipelineAnalytics.deleteTracks": {
44468
46913
  capName: "pipeline-analytics",
44469
46914
  capScope: "device",
@@ -44668,6 +47113,30 @@ Object.freeze({
44668
47113
  addonId: null,
44669
47114
  access: "view"
44670
47115
  },
47116
+ "pipelineAnalytics.listRetrainAssistProfiles": {
47117
+ capName: "pipeline-analytics",
47118
+ capScope: "device",
47119
+ addonId: null,
47120
+ access: "view"
47121
+ },
47122
+ "pipelineAnalytics.listRetrainCollectionFrames": {
47123
+ capName: "pipeline-analytics",
47124
+ capScope: "device",
47125
+ addonId: null,
47126
+ access: "view"
47127
+ },
47128
+ "pipelineAnalytics.listRetrainCollections": {
47129
+ capName: "pipeline-analytics",
47130
+ capScope: "device",
47131
+ addonId: null,
47132
+ access: "view"
47133
+ },
47134
+ "pipelineAnalytics.listRetrainDatasetVersions": {
47135
+ capName: "pipeline-analytics",
47136
+ capScope: "device",
47137
+ addonId: null,
47138
+ access: "view"
47139
+ },
44671
47140
  "pipelineAnalytics.listRetrainFrames": {
44672
47141
  capName: "pipeline-analytics",
44673
47142
  capScope: "device",
@@ -44710,12 +47179,24 @@ Object.freeze({
44710
47179
  addonId: null,
44711
47180
  access: "create"
44712
47181
  },
47182
+ "pipelineAnalytics.previewRetrainDataset": {
47183
+ capName: "pipeline-analytics",
47184
+ capScope: "device",
47185
+ addonId: null,
47186
+ access: "view"
47187
+ },
44713
47188
  "pipelineAnalytics.proposeRetrainAnnotations": {
44714
47189
  capName: "pipeline-analytics",
44715
47190
  capScope: "device",
44716
47191
  addonId: null,
44717
47192
  access: "create"
44718
47193
  },
47194
+ "pipelineAnalytics.proposeRetrainAnnotationsWithLlm": {
47195
+ capName: "pipeline-analytics",
47196
+ capScope: "device",
47197
+ addonId: null,
47198
+ access: "create"
47199
+ },
44719
47200
  "pipelineAnalytics.pruneEvents": {
44720
47201
  capName: "pipeline-analytics",
44721
47202
  capScope: "device",
@@ -44770,6 +47251,18 @@ Object.freeze({
44770
47251
  addonId: null,
44771
47252
  access: "create"
44772
47253
  },
47254
+ "pipelineAnalytics.removeRetrainFrameFromCollection": {
47255
+ capName: "pipeline-analytics",
47256
+ capScope: "device",
47257
+ addonId: null,
47258
+ access: "delete"
47259
+ },
47260
+ "pipelineAnalytics.renameRetrainCollection": {
47261
+ capName: "pipeline-analytics",
47262
+ capScope: "device",
47263
+ addonId: null,
47264
+ access: "create"
47265
+ },
44773
47266
  "pipelineAnalytics.restageRetrainTrack": {
44774
47267
  capName: "pipeline-analytics",
44775
47268
  capScope: "device",
@@ -44800,6 +47293,12 @@ Object.freeze({
44800
47293
  addonId: null,
44801
47294
  access: "create"
44802
47295
  },
47296
+ "pipelineAnalytics.setRetrainFrameComplete": {
47297
+ capName: "pipeline-analytics",
47298
+ capScope: "device",
47299
+ addonId: null,
47300
+ access: "create"
47301
+ },
44803
47302
  "pipelineAnalytics.setTrackFlags": {
44804
47303
  capName: "pipeline-analytics",
44805
47304
  capScope: "device",
@@ -44824,6 +47323,12 @@ Object.freeze({
44824
47323
  addonId: null,
44825
47324
  access: "create"
44826
47325
  },
47326
+ "pipelineAnalytics.verifyRetrainDatasetVersion": {
47327
+ capName: "pipeline-analytics",
47328
+ capScope: "device",
47329
+ addonId: null,
47330
+ access: "view"
47331
+ },
44827
47332
  "pipelineAnalytics.wipeAllAnalytics": {
44828
47333
  capName: "pipeline-analytics",
44829
47334
  capScope: "device",
@@ -44836,6 +47341,12 @@ Object.freeze({
44836
47341
  addonId: null,
44837
47342
  access: "delete"
44838
47343
  },
47344
+ "pipelineAnalytics.writeRetrainArchive": {
47345
+ capName: "pipeline-analytics",
47346
+ capScope: "device",
47347
+ addonId: null,
47348
+ access: "create"
47349
+ },
44839
47350
  "pipelineExecutor.cacheFrameInPool": {
44840
47351
  capName: "pipeline-executor",
44841
47352
  capScope: "system",
@@ -44848,13 +47359,13 @@ Object.freeze({
44848
47359
  addonId: null,
44849
47360
  access: "delete"
44850
47361
  },
44851
- "pipelineExecutor.deleteModel": {
47362
+ "pipelineExecutor.clearModelBoundDeviceOverrides": {
44852
47363
  capName: "pipeline-executor",
44853
47364
  capScope: "system",
44854
47365
  addonId: null,
44855
47366
  access: "delete"
44856
47367
  },
44857
- "pipelineExecutor.deleteTemplate": {
47368
+ "pipelineExecutor.deleteModel": {
44858
47369
  capName: "pipeline-executor",
44859
47370
  capScope: "system",
44860
47371
  addonId: null,
@@ -44998,12 +47509,6 @@ Object.freeze({
44998
47509
  addonId: null,
44999
47510
  access: "view"
45000
47511
  },
45001
- "pipelineExecutor.listTemplates": {
45002
- capName: "pipeline-executor",
45003
- capScope: "system",
45004
- addonId: null,
45005
- access: "view"
45006
- },
45007
47512
  "pipelineExecutor.rearmInferenceDevice": {
45008
47513
  capName: "pipeline-executor",
45009
47514
  capScope: "system",
@@ -45028,12 +47533,6 @@ Object.freeze({
45028
47533
  addonId: null,
45029
47534
  access: "create"
45030
47535
  },
45031
- "pipelineExecutor.saveTemplate": {
45032
- capName: "pipeline-executor",
45033
- capScope: "system",
45034
- addonId: null,
45035
- access: "create"
45036
- },
45037
47536
  "pipelineExecutor.setVideoPipelineSteps": {
45038
47537
  capName: "pipeline-executor",
45039
47538
  capScope: "system",
@@ -45052,17 +47551,23 @@ Object.freeze({
45052
47551
  addonId: null,
45053
47552
  access: "create"
45054
47553
  },
45055
- "pipelineExecutor.updateTemplate": {
47554
+ "pipelineExecutor.validatePipeline": {
45056
47555
  capName: "pipeline-executor",
45057
47556
  capScope: "system",
45058
47557
  addonId: null,
47558
+ access: "view"
47559
+ },
47560
+ "pipelineOrchestrator.abortNodeBenchmark": {
47561
+ capName: "pipeline-orchestrator",
47562
+ capScope: "system",
47563
+ addonId: null,
45059
47564
  access: "create"
45060
47565
  },
45061
- "pipelineExecutor.validatePipeline": {
45062
- capName: "pipeline-executor",
47566
+ "pipelineOrchestrator.applyAutoTune": {
47567
+ capName: "pipeline-orchestrator",
45063
47568
  capScope: "system",
45064
47569
  addonId: null,
45065
- access: "view"
47570
+ access: "create"
45066
47571
  },
45067
47572
  "pipelineOrchestrator.applyDeviceSettingsPatch": {
45068
47573
  capName: "pipeline-orchestrator",
@@ -45082,7 +47587,7 @@ Object.freeze({
45082
47587
  addonId: null,
45083
47588
  access: "create"
45084
47589
  },
45085
- "pipelineOrchestrator.deleteTemplate": {
47590
+ "pipelineOrchestrator.deleteNodeBenchmarkResult": {
45086
47591
  capName: "pipeline-orchestrator",
45087
47592
  capScope: "system",
45088
47593
  addonId: null,
@@ -45178,6 +47683,12 @@ Object.freeze({
45178
47683
  addonId: null,
45179
47684
  access: "view"
45180
47685
  },
47686
+ "pipelineOrchestrator.getEffectiveShares": {
47687
+ capName: "pipeline-orchestrator",
47688
+ capScope: "system",
47689
+ addonId: null,
47690
+ access: "view"
47691
+ },
45181
47692
  "pipelineOrchestrator.getGlobalMetrics": {
45182
47693
  capName: "pipeline-orchestrator",
45183
47694
  capScope: "system",
@@ -45190,6 +47701,24 @@ Object.freeze({
45190
47701
  addonId: null,
45191
47702
  access: "view"
45192
47703
  },
47704
+ "pipelineOrchestrator.getNodeBenchmarkCandidates": {
47705
+ capName: "pipeline-orchestrator",
47706
+ capScope: "system",
47707
+ addonId: null,
47708
+ access: "view"
47709
+ },
47710
+ "pipelineOrchestrator.getNodeBenchmarkPreflight": {
47711
+ capName: "pipeline-orchestrator",
47712
+ capScope: "system",
47713
+ addonId: null,
47714
+ access: "view"
47715
+ },
47716
+ "pipelineOrchestrator.getNodeBenchmarkStatus": {
47717
+ capName: "pipeline-orchestrator",
47718
+ capScope: "system",
47719
+ addonId: null,
47720
+ access: "view"
47721
+ },
45193
47722
  "pipelineOrchestrator.getNodeInferenceDevices": {
45194
47723
  capName: "pipeline-orchestrator",
45195
47724
  capScope: "system",
@@ -45214,6 +47743,12 @@ Object.freeze({
45214
47743
  addonId: null,
45215
47744
  access: "view"
45216
47745
  },
47746
+ "pipelineOrchestrator.getPlacementStats": {
47747
+ capName: "pipeline-orchestrator",
47748
+ capScope: "system",
47749
+ addonId: null,
47750
+ access: "view"
47751
+ },
45217
47752
  "pipelineOrchestrator.getReconcileFromDiskStatus": {
45218
47753
  capName: "pipeline-orchestrator",
45219
47754
  capScope: "system",
@@ -45226,7 +47761,7 @@ Object.freeze({
45226
47761
  addonId: null,
45227
47762
  access: "view"
45228
47763
  },
45229
- "pipelineOrchestrator.listTemplates": {
47764
+ "pipelineOrchestrator.listNodeBenchmarkResults": {
45230
47765
  capName: "pipeline-orchestrator",
45231
47766
  capScope: "system",
45232
47767
  addonId: null,
@@ -45238,6 +47773,12 @@ Object.freeze({
45238
47773
  addonId: null,
45239
47774
  access: "create"
45240
47775
  },
47776
+ "pipelineOrchestrator.planAutoTune": {
47777
+ capName: "pipeline-orchestrator",
47778
+ capScope: "system",
47779
+ addonId: null,
47780
+ access: "view"
47781
+ },
45241
47782
  "pipelineOrchestrator.rebalance": {
45242
47783
  capName: "pipeline-orchestrator",
45243
47784
  capScope: "system",
@@ -45280,12 +47821,6 @@ Object.freeze({
45280
47821
  addonId: null,
45281
47822
  access: "create"
45282
47823
  },
45283
- "pipelineOrchestrator.saveTemplate": {
45284
- capName: "pipeline-orchestrator",
45285
- capScope: "system",
45286
- addonId: null,
45287
- access: "create"
45288
- },
45289
47824
  "pipelineOrchestrator.setAgentCapabilities": {
45290
47825
  capName: "pipeline-orchestrator",
45291
47826
  capScope: "system",
@@ -45298,12 +47833,6 @@ Object.freeze({
45298
47833
  addonId: null,
45299
47834
  access: "create"
45300
47835
  },
45301
- "pipelineOrchestrator.setAgentDetectWeight": {
45302
- capName: "pipeline-orchestrator",
45303
- capScope: "system",
45304
- addonId: null,
45305
- access: "create"
45306
- },
45307
47836
  "pipelineOrchestrator.setAgentInferenceDevices": {
45308
47837
  capName: "pipeline-orchestrator",
45309
47838
  capScope: "system",
@@ -45340,6 +47869,12 @@ Object.freeze({
45340
47869
  addonId: null,
45341
47870
  access: "create"
45342
47871
  },
47872
+ "pipelineOrchestrator.setCameraStepSettings": {
47873
+ capName: "pipeline-orchestrator",
47874
+ capScope: "system",
47875
+ addonId: null,
47876
+ access: "create"
47877
+ },
45343
47878
  "pipelineOrchestrator.setCameraStepToggle": {
45344
47879
  capName: "pipeline-orchestrator",
45345
47880
  capScope: "system",
@@ -45358,12 +47893,36 @@ Object.freeze({
45358
47893
  addonId: null,
45359
47894
  access: "create"
45360
47895
  },
47896
+ "pipelineOrchestrator.setLoadBalancingShares": {
47897
+ capName: "pipeline-orchestrator",
47898
+ capScope: "system",
47899
+ addonId: null,
47900
+ access: "create"
47901
+ },
45361
47902
  "pipelineOrchestrator.setPipelineDevicePin": {
45362
47903
  capName: "pipeline-orchestrator",
45363
47904
  capScope: "system",
45364
47905
  addonId: null,
45365
47906
  access: "create"
45366
47907
  },
47908
+ "pipelineOrchestrator.setShareSource": {
47909
+ capName: "pipeline-orchestrator",
47910
+ capScope: "system",
47911
+ addonId: null,
47912
+ access: "create"
47913
+ },
47914
+ "pipelineOrchestrator.startNodeBenchmark": {
47915
+ capName: "pipeline-orchestrator",
47916
+ capScope: "system",
47917
+ addonId: null,
47918
+ access: "create"
47919
+ },
47920
+ "pipelineOrchestrator.suggestShares": {
47921
+ capName: "pipeline-orchestrator",
47922
+ capScope: "system",
47923
+ addonId: null,
47924
+ access: "view"
47925
+ },
45367
47926
  "pipelineOrchestrator.unassignAudio": {
45368
47927
  capName: "pipeline-orchestrator",
45369
47928
  capScope: "system",
@@ -45376,8 +47935,8 @@ Object.freeze({
45376
47935
  addonId: null,
45377
47936
  access: "create"
45378
47937
  },
45379
- "pipelineOrchestrator.updateTemplate": {
45380
- capName: "pipeline-orchestrator",
47938
+ "pipelineRunner.abortNodeBench": {
47939
+ capName: "pipeline-runner",
45381
47940
  capScope: "system",
45382
47941
  addonId: null,
45383
47942
  access: "create"
@@ -45430,6 +47989,12 @@ Object.freeze({
45430
47989
  addonId: null,
45431
47990
  access: "view"
45432
47991
  },
47992
+ "pipelineRunner.getNodeBenchStatus": {
47993
+ capName: "pipeline-runner",
47994
+ capScope: "system",
47995
+ addonId: null,
47996
+ access: "view"
47997
+ },
45433
47998
  "pipelineRunner.getParkedTrackFrame": {
45434
47999
  capName: "pipeline-runner",
45435
48000
  capScope: "system",
@@ -45472,6 +48037,12 @@ Object.freeze({
45472
48037
  addonId: null,
45473
48038
  access: "create"
45474
48039
  },
48040
+ "pipelineRunner.startNodeBench": {
48041
+ capName: "pipeline-runner",
48042
+ capScope: "system",
48043
+ addonId: null,
48044
+ access: "create"
48045
+ },
45475
48046
  "plateGallery.assignPlate": {
45476
48047
  capName: "plate-gallery",
45477
48048
  capScope: "system",
@@ -46816,12 +49387,6 @@ Object.freeze({
46816
49387
  addonId: null,
46817
49388
  access: "view"
46818
49389
  },
46819
- "streamBroker.pullFrameHandles": {
46820
- capName: "stream-broker",
46821
- capScope: "system",
46822
- addonId: null,
46823
- access: "view"
46824
- },
46825
49390
  "streamBroker.regenerateRtspToken": {
46826
49391
  capName: "stream-broker",
46827
49392
  capScope: "system",
@@ -46888,12 +49453,6 @@ Object.freeze({
46888
49453
  addonId: null,
46889
49454
  access: "create"
46890
49455
  },
46891
- "streamBroker.subscribeFrames": {
46892
- capName: "stream-broker",
46893
- capScope: "system",
46894
- addonId: null,
46895
- access: "create"
46896
- },
46897
49456
  "streamBroker.unassignProfile": {
46898
49457
  capName: "stream-broker",
46899
49458
  capScope: "system",
@@ -46906,12 +49465,6 @@ Object.freeze({
46906
49465
  addonId: null,
46907
49466
  access: "create"
46908
49467
  },
46909
- "streamBroker.unsubscribeFrames": {
46910
- capName: "stream-broker",
46911
- capScope: "system",
46912
- addonId: null,
46913
- access: "create"
46914
- },
46915
49468
  "streamCatalog.getCatalog": {
46916
49469
  capName: "stream-catalog",
46917
49470
  capScope: "device",
@@ -47206,18 +49759,36 @@ Object.freeze({
47206
49759
  addonId: null,
47207
49760
  access: "delete"
47208
49761
  },
49762
+ "userManagement.findUserByExternalIdentity": {
49763
+ capName: "user-management",
49764
+ capScope: "system",
49765
+ addonId: null,
49766
+ access: "view"
49767
+ },
47209
49768
  "userManagement.getTotpStatus": {
47210
49769
  capName: "user-management",
47211
49770
  capScope: "system",
47212
49771
  addonId: null,
47213
49772
  access: "view"
47214
49773
  },
49774
+ "userManagement.linkExternalIdentity": {
49775
+ capName: "user-management",
49776
+ capScope: "system",
49777
+ addonId: null,
49778
+ access: "create"
49779
+ },
47215
49780
  "userManagement.listApiKeys": {
47216
49781
  capName: "user-management",
47217
49782
  capScope: "system",
47218
49783
  addonId: null,
47219
49784
  access: "view"
47220
49785
  },
49786
+ "userManagement.listExternalIdentities": {
49787
+ capName: "user-management",
49788
+ capScope: "system",
49789
+ addonId: null,
49790
+ access: "view"
49791
+ },
47221
49792
  "userManagement.listOauthSessions": {
47222
49793
  capName: "user-management",
47223
49794
  capScope: "system",
@@ -47296,6 +49867,12 @@ Object.freeze({
47296
49867
  addonId: null,
47297
49868
  access: "create"
47298
49869
  },
49870
+ "userManagement.unlinkExternalIdentity": {
49871
+ capName: "user-management",
49872
+ capScope: "system",
49873
+ addonId: null,
49874
+ access: "delete"
49875
+ },
47299
49876
  "userManagement.updateUser": {
47300
49877
  capName: "user-management",
47301
49878
  capScope: "system",
@@ -47608,6 +50185,12 @@ Object.freeze({
47608
50185
  addonId: null,
47609
50186
  access: "create"
47610
50187
  },
50188
+ "webrtcSession.applyDownlinkProbe": {
50189
+ capName: "webrtc-session",
50190
+ capScope: "device",
50191
+ addonId: null,
50192
+ access: "create"
50193
+ },
47611
50194
  "webrtcSession.closeSession": {
47612
50195
  capName: "webrtc-session",
47613
50196
  capScope: "device",
@@ -49419,11 +52002,6 @@ Object.freeze({
49419
52002
  form: "single",
49420
52003
  optional: false
49421
52004
  }],
49422
- "decoder.createSession": [{
49423
- name: "deviceId",
49424
- form: "single",
49425
- optional: true
49426
- }],
49427
52005
  "detectionPipeline.applyDeviceSettingsPatch": [{
49428
52006
  name: "deviceId",
49429
52007
  form: "single",
@@ -50394,6 +52972,11 @@ Object.freeze({
50394
52972
  form: "single",
50395
52973
  optional: false
50396
52974
  }],
52975
+ "pipelineAnalytics.addRetrainFrameFromRecording": [{
52976
+ name: "deviceId",
52977
+ form: "single",
52978
+ optional: false
52979
+ }],
50397
52980
  "pipelineAnalytics.applyDeviceSettingsPatch": [{
50398
52981
  name: "deviceId",
50399
52982
  form: "single",
@@ -50584,6 +53167,11 @@ Object.freeze({
50584
53167
  form: "single",
50585
53168
  optional: false
50586
53169
  }],
53170
+ "pipelineAnalytics.proposeRetrainAnnotationsWithLlm": [{
53171
+ name: "deviceId",
53172
+ form: "single",
53173
+ optional: false
53174
+ }],
50587
53175
  "pipelineAnalytics.pruneEventsBefore": [{
50588
53176
  name: "deviceId",
50589
53177
  form: "single",
@@ -50634,6 +53222,11 @@ Object.freeze({
50634
53222
  form: "single",
50635
53223
  optional: false
50636
53224
  }],
53225
+ "pipelineAnalytics.setRetrainFrameComplete": [{
53226
+ name: "deviceId",
53227
+ form: "single",
53228
+ optional: false
53229
+ }],
50637
53230
  "pipelineAnalytics.setTrackFlags": [{
50638
53231
  name: "deviceId",
50639
53232
  form: "single",
@@ -50654,6 +53247,11 @@ Object.freeze({
50654
53247
  form: "single",
50655
53248
  optional: false
50656
53249
  }],
53250
+ "pipelineExecutor.clearModelBoundDeviceOverrides": [{
53251
+ name: "deviceIds",
53252
+ form: "array",
53253
+ optional: true
53254
+ }],
50657
53255
  "pipelineExecutor.runPipeline": [{
50658
53256
  name: "deviceId",
50659
53257
  form: "single",
@@ -50759,6 +53357,11 @@ Object.freeze({
50759
53357
  form: "single",
50760
53358
  optional: false
50761
53359
  }],
53360
+ "pipelineOrchestrator.setCameraStepSettings": [{
53361
+ name: "deviceId",
53362
+ form: "single",
53363
+ optional: false
53364
+ }],
50762
53365
  "pipelineOrchestrator.setCameraStepToggle": [{
50763
53366
  name: "deviceId",
50764
53367
  form: "single",
@@ -51530,6 +54133,11 @@ Object.freeze({
51530
54133
  form: "single",
51531
54134
  optional: false
51532
54135
  }],
54136
+ "webrtcSession.applyDownlinkProbe": [{
54137
+ name: "deviceId",
54138
+ form: "single",
54139
+ optional: false
54140
+ }],
51533
54141
  "webrtcSession.closeSession": [{
51534
54142
  name: "deviceId",
51535
54143
  form: "single",
@@ -51759,6 +54367,8 @@ new Map(Object.entries({
51759
54367
  "detection-blind": "device-events",
51760
54368
  "node-online": "system",
51761
54369
  "node-offline": "system",
54370
+ "node-restarting": "system",
54371
+ "node-updating": "system",
51762
54372
  "node-inference-unavailable": "system",
51763
54373
  "addon-crash-loop": "system",
51764
54374
  "addon-update-available": "system",
@@ -51836,6 +54446,7 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
51836
54446
  kinds: [
51837
54447
  "node-offline",
51838
54448
  "node-online",
54449
+ "node-restarting",
51839
54450
  "node-inference-unavailable"
51840
54451
  ]
51841
54452
  },
@@ -51857,6 +54468,7 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
51857
54468
  label: "Server & container updates",
51858
54469
  kinds: [
51859
54470
  "server-update-available",
54471
+ "node-updating",
51860
54472
  "server-updated",
51861
54473
  "wrapper-update-available"
51862
54474
  ]
@@ -52004,6 +54616,7 @@ var STATE_SUMMARY_SECTION_VARS = [
52004
54616
  kinds: [
52005
54617
  "node-offline",
52006
54618
  "node-online",
54619
+ "node-restarting",
52007
54620
  "node-inference-unavailable"
52008
54621
  ]
52009
54622
  },
@@ -52025,6 +54638,7 @@ var STATE_SUMMARY_SECTION_VARS = [
52025
54638
  example: "server 1.2.377 → 1.2.378",
52026
54639
  kinds: [
52027
54640
  "server-update-available",
54641
+ "node-updating",
52028
54642
  "server-updated",
52029
54643
  "wrapper-update-available"
52030
54644
  ]
@@ -52057,7 +54671,7 @@ var STATE_SUMMARY_VARS = [
52057
54671
  {
52058
54672
  name: "summarySection",
52059
54673
  label: "Summary: this section",
52060
- description: "The section a per-section summary row is about (D698); on a summary sent as one message, every section it reports, comma-separated.",
54674
+ description: "The section a per-section summary row is about; on a summary sent as one message, every section it reports, comma-separated.",
52061
54675
  example: "Batterie scariche",
52062
54676
  group: "system",
52063
54677
  families: ["rule"],