@camstack/addon-pipeline-orchestrator 1.2.638 → 1.2.639

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/assets/viewer/.viewer-current-files.json +2 -2
  3. package/assets/viewer/.viewer-version +1 -1
  4. package/assets/viewer/_expo/static/js/web/{__common-6206f99c82ca608b8620797fed9685e4.js → __common-987325b06b59d95030dfaa48ed6559b0.js} +43 -43
  5. package/assets/viewer/_expo/static/js/web/{index-7e427fd375381d1d43da28a24697dd03.js → index-0134483fbf5fd90d22b3febd6bd0cc64.js} +2 -2
  6. package/assets/viewer/index.html +1 -1
  7. package/dist/_stub.js +2 -2
  8. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-Hya6JCSL.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-CpDS4Ilu.mjs} +2 -2
  9. package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CYNe1S9x.mjs +26 -0
  10. package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CMKrpGjd.mjs +26 -0
  11. package/dist/{hostInit-BWrAZ8UU.mjs → hostInit-DY3NVnRS.mjs} +2 -2
  12. package/dist/index.js +507 -24
  13. package/dist/index.mjs +507 -24
  14. package/dist/remoteEntry.js +1 -1
  15. package/package.json +1 -1
  16. package/assets/viewer/_expo/static/js/web/[id]-d3b1d7a725fa2a1af0839ac46985eb3d.js +0 -7
  17. package/assets/viewer/_expo/static/js/web/[id]-ed4f5ea7c5329aa67a79c94da84ba024.js +0 -5
  18. package/assets/viewer/_expo/static/js/web/[section]-0eef3af480ef1910eed719cf82530d92.js +0 -31
  19. package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BlL4i123.mjs +0 -26
  20. package/dist/_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DW9I9T44.mjs +0 -26
package/dist/index.js CHANGED
@@ -5611,7 +5611,7 @@ var ZodIssueCode = {
5611
5611
  var ZodFirstPartyTypeKind;
5612
5612
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5613
5613
  //#endregion
5614
- //#region ../types/dist/sleep-BAvUCi_2.mjs
5614
+ //#region ../types/dist/sleep-C_u6cEAJ.mjs
5615
5615
  /**
5616
5616
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5617
5617
  * window to float samples (D455).
@@ -9559,6 +9559,7 @@ var CAP_NAMES = Object.freeze([
9559
9559
  "network-link",
9560
9560
  "network-quality",
9561
9561
  "node-certificates",
9562
+ "node-removal-hook",
9562
9563
  "nodes",
9563
9564
  "notification-output",
9564
9565
  "notification-rules",
@@ -9589,6 +9590,7 @@ var CAP_NAMES = Object.freeze([
9589
9590
  "recording-onboard",
9590
9591
  "recording-signal",
9591
9592
  "records",
9593
+ "remote",
9592
9594
  "running-state",
9593
9595
  "scene-monitor",
9594
9596
  "script-runner",
@@ -9612,6 +9614,7 @@ var CAP_NAMES = Object.freeze([
9612
9614
  "system",
9613
9615
  "tamper",
9614
9616
  "temperature-sensor",
9617
+ "template",
9615
9618
  "terminal-session",
9616
9619
  "toast",
9617
9620
  "training",
@@ -10552,6 +10555,11 @@ Object.freeze({
10552
10555
  "standby"
10553
10556
  ]
10554
10557
  },
10558
+ {
10559
+ "path": "stateSource",
10560
+ "valueType": "enum",
10561
+ "enumValues": ["reported", "assumed"]
10562
+ },
10555
10563
  {
10556
10564
  "path": "volumeLevel",
10557
10565
  "valueType": "number"
@@ -10827,6 +10835,18 @@ Object.freeze({
10827
10835
  {
10828
10836
  "path": "volume",
10829
10837
  "valueType": "number"
10838
+ },
10839
+ {
10840
+ "path": "lastFeedGrams",
10841
+ "valueType": "number"
10842
+ },
10843
+ {
10844
+ "path": "lastFeedHopper1",
10845
+ "valueType": "number"
10846
+ },
10847
+ {
10848
+ "path": "lastFeedHopper2",
10849
+ "valueType": "number"
10830
10850
  }
10831
10851
  ],
10832
10852
  "power-meter": [
@@ -11752,7 +11772,7 @@ function errMsg$2(err) {
11752
11772
  return String(err);
11753
11773
  }
11754
11774
  //#endregion
11755
- //#region ../types/dist/composition-DpFv_J6D.mjs
11775
+ //#region ../types/dist/composition-bIBW5_13.mjs
11756
11776
  /**
11757
11777
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11758
11778
  *
@@ -43852,6 +43872,14 @@ method(object({
43852
43872
  kind: "mutation",
43853
43873
  auth: "admin"
43854
43874
  });
43875
+ method(object({
43876
+ nodeId: string().min(1),
43877
+ /** Epoch ms the node was forgotten (the tombstone's `removedAt`). */
43878
+ removedAt: number()
43879
+ }), object({ counts: record(string(), number().int().nonnegative()) }), {
43880
+ kind: "mutation",
43881
+ auth: "admin"
43882
+ });
43855
43883
  /**
43856
43884
  * `device-removal-hook` — the reliable way an addon learns a device was DELETED,
43857
43885
  * so it can delete what it keeps for that device (D865).
@@ -56095,8 +56123,7 @@ var pipelineOrchestratorCapability = {
56095
56123
  * replaced the four flat orchestrator lists (`enabledNodes`,
56096
56124
  * `enabledDecoderNodes`, `enabledAudioNodes`, `remoteSourcingNodes`).
56097
56125
  * Each flag is optional in the patch: omit a flag to leave it unchanged,
56098
- * pass `null` to reset it to the node default (`hub` → capable, every
56099
- * other node → not). Detection/audio eligibility is derived from these
56126
+ * pass `null` to reset it to the default (ON for every node). Detection/audio eligibility is derived from these
56100
56127
  * flags across the cluster; changing them re-derives the enabled sets
56101
56128
  * and reconciles dispatch immediately. There is NO separate decode flag:
56102
56129
  * decode is always co-located with its frame consumer, so decode
@@ -60055,6 +60082,318 @@ DeviceType.Light, method(object({
60055
60082
  level: number().min(0).max(100),
60056
60083
  lastChangedAt: number()
60057
60084
  });
60085
+ /**
60086
+ * The role vocabulary of a remote (D963): what a button MEANS, independent of
60087
+ * how it is wired. A layout template places buttons by role; a device binds a
60088
+ * role to an action; an exporter asks for a role by name. Roles are shared so
60089
+ * one layout fits every appliance and one voice command fits every binding.
60090
+ *
60091
+ * A role outside this list is `custom:<key>` — the operator's own button (a
60092
+ * SmartIR source, a channel preset). The key is a lowercase slug.
60093
+ */
60094
+ var REMOTE_KNOWN_ROLES = [
60095
+ "power",
60096
+ "power_on",
60097
+ "power_off",
60098
+ "volume_up",
60099
+ "volume_down",
60100
+ "mute",
60101
+ "channel_up",
60102
+ "channel_down",
60103
+ "up",
60104
+ "down",
60105
+ "left",
60106
+ "right",
60107
+ "ok",
60108
+ "back",
60109
+ "home",
60110
+ "menu",
60111
+ "info",
60112
+ "guide",
60113
+ "exit",
60114
+ "input",
60115
+ "play",
60116
+ "pause",
60117
+ "play_pause",
60118
+ "stop",
60119
+ "next",
60120
+ "previous",
60121
+ "fast_forward",
60122
+ "rewind",
60123
+ "digit_0",
60124
+ "digit_1",
60125
+ "digit_2",
60126
+ "digit_3",
60127
+ "digit_4",
60128
+ "digit_5",
60129
+ "digit_6",
60130
+ "digit_7",
60131
+ "digit_8",
60132
+ "digit_9"
60133
+ ];
60134
+ var CUSTOM_KEY_RE = /^[a-z0-9][a-z0-9_]{0,47}$/;
60135
+ var KNOWN_ROLES = new Set(REMOTE_KNOWN_ROLES);
60136
+ function isRemoteKnownRole(value) {
60137
+ return KNOWN_ROLES.has(value);
60138
+ }
60139
+ function isRemoteCustomKey(key) {
60140
+ return CUSTOM_KEY_RE.test(key);
60141
+ }
60142
+ /** The key of a `custom:<key>` role, or null for any other string. */
60143
+ function customKeyOfRole(role) {
60144
+ if (!role.startsWith("custom:")) return null;
60145
+ const key = role.slice(7);
60146
+ return isRemoteCustomKey(key) ? key : null;
60147
+ }
60148
+ function isRemoteRole(value) {
60149
+ return isRemoteKnownRole(value) || customKeyOfRole(value) !== null;
60150
+ }
60151
+ var RemoteRoleSchema = string().refine(isRemoteRole, { message: "a role is one of the known roles or custom:<key> (lowercase letters, digits, _)" });
60152
+ var RemoteBindingSchema = object({ action: AutomationCapStepSchema.extend({ deviceId: number().int().nonnegative() }) });
60153
+ var RemoteCustomButtonSchema = object({
60154
+ /** The slug after `custom:` in the role. */
60155
+ key: string().refine(isRemoteCustomKey, { message: "a lowercase slug" }),
60156
+ /** What the operator calls it, and what a voice command may name. */
60157
+ name: string().min(1).max(60)
60158
+ });
60159
+ var RemotePowerModeSchema = _enum(["toggle", "discrete"]);
60160
+ var RemoteConfigSchema = object({
60161
+ layoutTemplateId: string().min(1).nullable(),
60162
+ /**
60163
+ * The transmitter device new bindings and an import send through. A binding
60164
+ * names its own device, so one command may override it.
60165
+ */
60166
+ defaultTransmitterId: number().int().nonnegative().nullable().default(null),
60167
+ /** Keyed by role. A role with no entry falls back to the native cap. */
60168
+ bindings: record(string(), RemoteBindingSchema),
60169
+ customButtons: array(RemoteCustomButtonSchema).max(100),
60170
+ power: object({ mode: RemotePowerModeSchema })
60171
+ }).superRefine((config, ctx) => {
60172
+ const roles = Object.keys(config.bindings);
60173
+ if (roles.length > 200) ctx.addIssue({
60174
+ code: "custom",
60175
+ message: "too many bindings",
60176
+ path: ["bindings"]
60177
+ });
60178
+ for (const role of roles) if (!isRemoteRole(role)) ctx.addIssue({
60179
+ code: "custom",
60180
+ message: `"${role}" is not a role`,
60181
+ path: ["bindings", role]
60182
+ });
60183
+ const keys = /* @__PURE__ */ new Set();
60184
+ config.customButtons.forEach((button, index) => {
60185
+ if (keys.has(button.key)) ctx.addIssue({
60186
+ code: "custom",
60187
+ message: `custom button "${button.key}" is listed twice`,
60188
+ path: [
60189
+ "customButtons",
60190
+ index,
60191
+ "key"
60192
+ ]
60193
+ });
60194
+ keys.add(button.key);
60195
+ });
60196
+ for (const role of roles) {
60197
+ const key = customKeyOfRole(role);
60198
+ if (key !== null && !keys.has(key)) ctx.addIssue({
60199
+ code: "custom",
60200
+ message: `${role} is bound but has no entry in customButtons`,
60201
+ path: ["bindings", role]
60202
+ });
60203
+ }
60204
+ });
60205
+ object({
60206
+ power: object({
60207
+ state: _enum(["on", "off"]),
60208
+ at: number()
60209
+ }).nullable(),
60210
+ muted: object({
60211
+ value: boolean(),
60212
+ at: number()
60213
+ }).nullable()
60214
+ });
60215
+ var RemoteLayoutButtonSchema = object({
60216
+ id: string().min(1).max(40),
60217
+ /** 0-based grid position. */
60218
+ row: number().int().min(0).max(24),
60219
+ col: number().int().min(0).max(24),
60220
+ rowSpan: number().int().min(1).max(24),
60221
+ colSpan: number().int().min(1).max(24),
60222
+ icon: string().min(1).max(60).optional(),
60223
+ label: string().min(1).max(40).optional(),
60224
+ role: RemoteRoleSchema
60225
+ });
60226
+ object({
60227
+ columns: number().int().min(1).max(24),
60228
+ rows: number().int().min(1).max(24),
60229
+ buttons: array(RemoteLayoutButtonSchema).max(200)
60230
+ }).superRefine((grid, ctx) => {
60231
+ const ids = /* @__PURE__ */ new Set();
60232
+ const taken = /* @__PURE__ */ new Map();
60233
+ grid.buttons.forEach((button, index) => {
60234
+ if (ids.has(button.id)) ctx.addIssue({
60235
+ code: "custom",
60236
+ message: `button id "${button.id}" is used twice`,
60237
+ path: [
60238
+ "buttons",
60239
+ index,
60240
+ "id"
60241
+ ]
60242
+ });
60243
+ ids.add(button.id);
60244
+ if (button.row + button.rowSpan > grid.rows || button.col + button.colSpan > grid.columns) {
60245
+ ctx.addIssue({
60246
+ code: "custom",
60247
+ message: `button "${button.id}" leaves the grid`,
60248
+ path: ["buttons", index]
60249
+ });
60250
+ return;
60251
+ }
60252
+ for (let r = button.row; r < button.row + button.rowSpan; r += 1) for (let c = button.col; c < button.col + button.colSpan; c += 1) {
60253
+ const cell = `${r}:${c}`;
60254
+ const other = taken.get(cell);
60255
+ if (other !== void 0) {
60256
+ ctx.addIssue({
60257
+ code: "custom",
60258
+ message: `buttons "${other}" and "${button.id}" overlap`,
60259
+ path: ["buttons", index]
60260
+ });
60261
+ return;
60262
+ }
60263
+ taken.set(cell, button.id);
60264
+ }
60265
+ });
60266
+ });
60267
+ var RemoteBoundCommandSchema = object({
60268
+ role: RemoteRoleSchema,
60269
+ /** The operator's name for a custom button; absent for a known role. */
60270
+ label: string().optional(),
60271
+ custom: boolean()
60272
+ });
60273
+ var RemoteCommandListSchema = object({
60274
+ layoutTemplateId: string().nullable(),
60275
+ power: object({ mode: RemotePowerModeSchema }),
60276
+ /** Only the roles that are BOUND — the ones a press can reach. */
60277
+ commands: array(RemoteBoundCommandSchema)
60278
+ });
60279
+ var RemoteRefusalReasonSchema = _enum([
60280
+ "no-remote",
60281
+ "unbound-role",
60282
+ "action-failed"
60283
+ ]);
60284
+ var RemoteSendResultSchema = discriminatedUnion("status", [
60285
+ object({
60286
+ status: literal("sent"),
60287
+ /** How many times the action ran. */
60288
+ sends: number().int().min(1)
60289
+ }),
60290
+ object({
60291
+ status: literal("skipped"),
60292
+ reason: literal("already-in-state")
60293
+ }),
60294
+ object({
60295
+ status: literal("refused"),
60296
+ reason: RemoteRefusalReasonSchema,
60297
+ detail: string().optional()
60298
+ })
60299
+ ]);
60300
+ var RemoteMediaCommandSchema = _enum([
60301
+ "play",
60302
+ "pause",
60303
+ "stop",
60304
+ "next",
60305
+ "previous",
60306
+ "setMute",
60307
+ "selectSource"
60308
+ ]);
60309
+ /** A send result, or `not-routed`: the role is not bound, so the native cap answers. */
60310
+ var RemoteMediaCommandResultSchema = discriminatedUnion("status", [...RemoteSendResultSchema.options, object({ status: literal("not-routed") })]);
60311
+ var RemoteSetConfigResultSchema = discriminatedUnion("status", [object({ status: literal("saved") }), object({
60312
+ status: literal("refused"),
60313
+ reason: _enum(["unknown-template", "not-a-media-player"]),
60314
+ detail: string()
60315
+ })]);
60316
+ var deviceInput = object({ deviceId: number().int().nonnegative() });
60317
+ DeviceType.MediaPlayer;
60318
+ method(deviceInput, RemoteConfigSchema, { auth: "admin" }), method(deviceInput.extend({ config: RemoteConfigSchema }), RemoteSetConfigResultSchema, {
60319
+ kind: "mutation",
60320
+ auth: "admin"
60321
+ }), method(deviceInput, RemoteCommandListSchema, { auth: "protected" }), method(deviceInput.extend({
60322
+ role: RemoteRoleSchema,
60323
+ /** Sends in a row (volume steps). */
60324
+ repeat: number().int().min(1).max(50).default(1)
60325
+ }), RemoteSendResultSchema, {
60326
+ kind: "mutation",
60327
+ auth: "protected"
60328
+ }), method(deviceInput.extend({
60329
+ command: RemoteMediaCommandSchema,
60330
+ muted: boolean().optional(),
60331
+ source: string().optional(),
60332
+ repeat: number().int().min(1).max(50).default(1)
60333
+ }), RemoteMediaCommandResultSchema, {
60334
+ kind: "mutation",
60335
+ auth: "protected"
60336
+ });
60337
+ var TemplateKindSchema = _enum(["remote"]);
60338
+ /**
60339
+ * template — the generic store of reusable, named operator data (D963). A
60340
+ * record is `{ id, kind, name, payload }`; the kinds (today only `remote`, the
60341
+ * button grid of a remote) and the schema each payload must satisfy are
60342
+ * declared by the system in `template-kinds.ts`. Nothing here is specific to a
60343
+ * kind: the engine validates a payload against its kind's schema and, on a
60344
+ * delete, asks the kind's owner whether anything still points at the record.
60345
+ *
60346
+ * Every refusal is a typed result — a UI says "unknown kind" or names the
60347
+ * devices that still use a template, without parsing text.
60348
+ */
60349
+ var TemplateRecordSchema = object({
60350
+ id: string().min(1),
60351
+ kind: TemplateKindSchema,
60352
+ name: string().min(1).max(120),
60353
+ /** Shaped by the kind; `checkTemplatePayload(kind, payload)` reads it. */
60354
+ payload: unknown(),
60355
+ createdAt: number(),
60356
+ updatedAt: number()
60357
+ });
60358
+ var TemplateWriteResultSchema = discriminatedUnion("status", [object({
60359
+ status: literal("saved"),
60360
+ template: TemplateRecordSchema
60361
+ }), object({
60362
+ status: literal("refused"),
60363
+ reason: _enum(["invalid-payload", "not-found"]),
60364
+ detail: string()
60365
+ })]);
60366
+ /** A device still pointing at a template. */
60367
+ var TemplateUserSchema = object({
60368
+ deviceId: number().int().nonnegative(),
60369
+ name: string()
60370
+ });
60371
+ var TemplateDeleteResultSchema = discriminatedUnion("status", [
60372
+ object({ status: literal("deleted") }),
60373
+ object({ status: literal("not-found") }),
60374
+ object({
60375
+ status: literal("in-use"),
60376
+ devices: array(TemplateUserSchema).min(1)
60377
+ })
60378
+ ]);
60379
+ method(object({ kind: TemplateKindSchema.optional() }), array(TemplateRecordSchema), { auth: "protected" }), method(object({ id: string().min(1) }), TemplateRecordSchema.nullable(), { auth: "protected" }), method(object({
60380
+ kind: TemplateKindSchema,
60381
+ name: string().min(1).max(120),
60382
+ payload: unknown()
60383
+ }), TemplateWriteResultSchema, {
60384
+ kind: "mutation",
60385
+ auth: "admin"
60386
+ }), method(object({
60387
+ id: string().min(1),
60388
+ name: string().min(1).max(120).optional(),
60389
+ payload: unknown().optional()
60390
+ }), TemplateWriteResultSchema, {
60391
+ kind: "mutation",
60392
+ auth: "admin"
60393
+ }), method(object({ id: string().min(1) }), TemplateDeleteResultSchema, {
60394
+ kind: "mutation",
60395
+ auth: "admin"
60396
+ });
60058
60397
  DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _void(), {
60059
60398
  kind: "mutation",
60060
60399
  auth: "protected"
@@ -62813,9 +63152,35 @@ var MediaInfoSchema = object({
62813
63152
  /** Optional cover-art / thumbnail URL. */
62814
63153
  imageUrl: string().optional()
62815
63154
  });
63155
+ /**
63156
+ * Where `state`, `volumeLevel`, `isMuted` and `source` come from. `reported`:
63157
+ * the device said so. `assumed`: the driver has no feedback (infrared) and
63158
+ * tracks what it last SENT; it moves only after a send was accepted, and a
63159
+ * human at the real remote makes it wrong. A UI shows an assumed value as a
63160
+ * guess, never as a reading.
63161
+ */
63162
+ var MediaPlayerStateSourceSchema = _enum(["reported", "assumed"]);
63163
+ /** The fields of a media-player status a driver can only guess. */
63164
+ var MediaPlayerAssumedFieldSchema = _enum([
63165
+ "state",
63166
+ "volumeLevel",
63167
+ "isMuted",
63168
+ "source"
63169
+ ]);
62816
63170
  object({
62817
63171
  /** Playback lifecycle state. */
62818
63172
  state: MediaPlayerStateSchema,
63173
+ /**
63174
+ * Whether the state fields are readings or the driver's own bookkeeping.
63175
+ * A slice written before this field existed parses as `reported`.
63176
+ */
63177
+ stateSource: MediaPlayerStateSourceSchema.default("reported"),
63178
+ /**
63179
+ * Which fields are the driver's own guess. `stateSource` is `assumed` when
63180
+ * this is not empty; a field not listed here is a reading even then (power
63181
+ * from a running-state source, mute guessed from the last send).
63182
+ */
63183
+ assumedFields: array(MediaPlayerAssumedFieldSchema).default([]),
62819
63184
  /** Volume as 0..100 inclusive. Null when the device has no volume
62820
63185
  * surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
62821
63186
  volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
@@ -64281,6 +64646,14 @@ object({
64281
64646
  feedSound: boolean(),
64282
64647
  /** Speaker / prompt volume level (device-scaled integer). */
64283
64648
  volume: number().meta(readingMeta("none")),
64649
+ /** Portion (grams) of the last manual feed sent from a single-hopper
64650
+ * stepper — the default the next manual feed starts from. Persisted per
64651
+ * device by the provider; null until a manual feed has been sent. */
64652
+ lastFeedGrams: gramsPortion.nullable(),
64653
+ /** Hopper-1 portion of the last manual feed (dual-hopper); null until sent. */
64654
+ lastFeedHopper1: gramsPortion.nullable(),
64655
+ /** Hopper-2 portion of the last manual feed (dual-hopper); null until sent. */
64656
+ lastFeedHopper2: gramsPortion.nullable(),
64284
64657
  /** Ms epoch when the slice was last refreshed from the cloud. */
64285
64658
  lastFetchedAt: number()
64286
64659
  });
@@ -69732,9 +70105,17 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
69732
70105
  kind: "mutation",
69733
70106
  auth: "admin"
69734
70107
  });
70108
+ discriminatedUnion("kind", [object({
70109
+ kind: literal("raw"),
70110
+ timings: array(number().int()),
70111
+ settings: TransmitSettingsSchema.optional()
70112
+ }), object({
70113
+ kind: literal("pronto"),
70114
+ code: string()
70115
+ })]);
69735
70116
  DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
69736
70117
  SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
69737
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
70118
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
69738
70119
  String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
69739
70120
  DeviceRole.BinarySensor, DeviceRole.MotionSensor;
69740
70121
  new Set(CAP_NAMES);
@@ -74585,6 +74966,13 @@ Object.freeze({
74585
74966
  access: "create",
74586
74967
  role: "admin"
74587
74968
  },
74969
+ "nodeRemovalHook.onNodeRemoved": {
74970
+ capName: "node-removal-hook",
74971
+ capScope: "system",
74972
+ addonId: null,
74973
+ access: "create",
74974
+ role: "admin"
74975
+ },
74588
74976
  "nodes.clusterAddonStatus": {
74589
74977
  capName: "nodes",
74590
74978
  capScope: "system",
@@ -77476,6 +77864,41 @@ Object.freeze({
77476
77864
  access: "create",
77477
77865
  role: "admin"
77478
77866
  },
77867
+ "remote.getConfig": {
77868
+ capName: "remote",
77869
+ capScope: "system",
77870
+ addonId: null,
77871
+ access: "view",
77872
+ role: "admin"
77873
+ },
77874
+ "remote.listCommands": {
77875
+ capName: "remote",
77876
+ capScope: "system",
77877
+ addonId: null,
77878
+ access: "view",
77879
+ role: "admin"
77880
+ },
77881
+ "remote.sendCommand": {
77882
+ capName: "remote",
77883
+ capScope: "system",
77884
+ addonId: null,
77885
+ access: "create",
77886
+ role: "admin"
77887
+ },
77888
+ "remote.sendMediaCommand": {
77889
+ capName: "remote",
77890
+ capScope: "system",
77891
+ addonId: null,
77892
+ access: "create",
77893
+ role: "admin"
77894
+ },
77895
+ "remote.setConfig": {
77896
+ capName: "remote",
77897
+ capScope: "system",
77898
+ addonId: null,
77899
+ access: "create",
77900
+ role: "admin"
77901
+ },
77479
77902
  "runningState.getStatus": {
77480
77903
  capName: "running-state",
77481
77904
  capScope: "device",
@@ -78666,6 +79089,41 @@ Object.freeze({
78666
79089
  access: "view",
78667
79090
  role: "viewer"
78668
79091
  },
79092
+ "template.create": {
79093
+ capName: "template",
79094
+ capScope: "system",
79095
+ addonId: null,
79096
+ access: "create",
79097
+ role: "admin"
79098
+ },
79099
+ "template.delete": {
79100
+ capName: "template",
79101
+ capScope: "system",
79102
+ addonId: null,
79103
+ access: "delete",
79104
+ role: "admin"
79105
+ },
79106
+ "template.get": {
79107
+ capName: "template",
79108
+ capScope: "system",
79109
+ addonId: null,
79110
+ access: "view",
79111
+ role: "admin"
79112
+ },
79113
+ "template.list": {
79114
+ capName: "template",
79115
+ capScope: "system",
79116
+ addonId: null,
79117
+ access: "view",
79118
+ role: "admin"
79119
+ },
79120
+ "template.update": {
79121
+ capName: "template",
79122
+ capScope: "system",
79123
+ addonId: null,
79124
+ access: "create",
79125
+ role: "admin"
79126
+ },
78669
79127
  "terminalSession.adoptLegacyMonitor": {
78670
79128
  capName: "terminal-session",
78671
79129
  capScope: "system",
@@ -83458,6 +83916,7 @@ var SYSTEM_CAP_VOCABULARY = {
83458
83916
  "device-manager": system("the device registry itself — rename, move and remove are administration"),
83459
83917
  "device-provider": system(INTERNAL),
83460
83918
  "device-removal-hook": system(INTERNAL),
83919
+ "node-removal-hook": system(INTERNAL),
83461
83920
  "device-state": system("the runtime-state mirror every trigger already reads through"),
83462
83921
  "display-palette": system("the cluster display palette — one colour table for every client, not a device action"),
83463
83922
  "embedding-encoder": system(ML),
@@ -83497,6 +83956,7 @@ var SYSTEM_CAP_VOCABULARY = {
83497
83956
  "recording-archive": system(STORAGE),
83498
83957
  "recording-export": system("recording exports — requested from the timeline, not an automation step"),
83499
83958
  records: system(INTERNAL),
83959
+ remote: system("the per-device remote setup — authored in the Customize remote editor, sent through the remote UI"),
83500
83960
  "server-management": system(CLUSTER),
83501
83961
  "settings-store": system(INTERNAL),
83502
83962
  "smtp-provider": system(INTERNAL),
@@ -83509,6 +83969,7 @@ var SYSTEM_CAP_VOCABULARY = {
83509
83969
  "storage-provider": system(STORAGE),
83510
83970
  "stream-broker": system(PIPELINE),
83511
83971
  system: system(CLUSTER),
83972
+ template: system("operator-authored templates — edited in their own editor, never actuated"),
83512
83973
  "terminal-session": system(CLUSTER),
83513
83974
  toast: system(UI_PLUMBING),
83514
83975
  training: system(ML),
@@ -85963,6 +86424,31 @@ Object.freeze({
85963
86424
  form: "single",
85964
86425
  optional: true
85965
86426
  }],
86427
+ "remote.getConfig": [{
86428
+ name: "deviceId",
86429
+ form: "single",
86430
+ optional: false
86431
+ }],
86432
+ "remote.listCommands": [{
86433
+ name: "deviceId",
86434
+ form: "single",
86435
+ optional: false
86436
+ }],
86437
+ "remote.sendCommand": [{
86438
+ name: "deviceId",
86439
+ form: "single",
86440
+ optional: false
86441
+ }],
86442
+ "remote.sendMediaCommand": [{
86443
+ name: "deviceId",
86444
+ form: "single",
86445
+ optional: false
86446
+ }],
86447
+ "remote.setConfig": [{
86448
+ name: "deviceId",
86449
+ form: "single",
86450
+ optional: false
86451
+ }],
85966
86452
  "runningState.getStatus": [{
85967
86453
  name: "deviceId",
85968
86454
  form: "single",
@@ -89103,14 +89589,6 @@ var DEVICE_BACKEND_TO_FORMAT = {
89103
89589
  function deviceBackendToFormat(backend) {
89104
89590
  return DEVICE_BACKEND_TO_FORMAT[backend] ?? "onnx";
89105
89591
  }
89106
- discriminatedUnion("kind", [object({
89107
- kind: literal("raw"),
89108
- timings: array(number().int()),
89109
- settings: TransmitSettingsSchema.optional()
89110
- }), object({
89111
- kind: literal("pronto"),
89112
- code: string()
89113
- })]);
89114
89592
  object({
89115
89593
  manufacturer: string().optional(),
89116
89594
  supportedModels: array(string()).optional(),
@@ -100796,12 +101274,12 @@ function computeFrameSourceNodes(input) {
100796
101274
  //#endregion
100797
101275
  //#region src/node-topology.ts
100798
101276
  /**
100799
- * Default placement capability for a node when its stored flag is unset:
100800
- * the hub is capable of every role out of the box; every other node must be
100801
- * explicitly enabled.
101277
+ * A placement flag (detect, audio, ingest) is ON unless the operator stored an
101278
+ * explicit `false`. A newly paired agent has no stored settings and must still
101279
+ * join balancing (D965).
100802
101280
  */
100803
- function nodeCapabilityDefault(nodeId) {
100804
- return nodeId === "hub";
101281
+ function isRoleOn(flag) {
101282
+ return flag !== false;
100805
101283
  }
100806
101284
  var NodeTopologyService = class {
100807
101285
  deps;
@@ -100932,17 +101410,16 @@ var NodeTopologyService = class {
100932
101410
  this.deps.logger.warn("refreshNodeCapabilities: agent settings read failed", { meta: { error: errMsg$2(err) } });
100933
101411
  }
100934
101412
  const nodeIds = new Set(["hub"]);
100935
- for (const nodeId of Object.keys(blob)) if (typeof nodeId === "string" && nodeId.length > 0 && !nodeId.includes("/")) nodeIds.add(nodeId);
101413
+ for (const nodeId of [...Object.keys(blob), ...this.knownRunnerNodes]) if (typeof nodeId === "string" && nodeId.length > 0 && !nodeId.includes("/")) nodeIds.add(nodeId);
100936
101414
  const detect = [];
100937
101415
  const audio = [];
100938
101416
  const ingest = [];
100939
101417
  const reachableHosts = /* @__PURE__ */ new Map();
100940
- const capable = (flag, nodeId) => typeof flag === "boolean" ? flag : nodeCapabilityDefault(nodeId);
100941
101418
  for (const nodeId of nodeIds) {
100942
101419
  const settings = blob[nodeId];
100943
- if (capable(settings?.detect, nodeId)) detect.push(nodeId);
100944
- if (capable(settings?.audio, nodeId)) audio.push(nodeId);
100945
- if (capable(settings?.ingest, nodeId)) ingest.push(nodeId);
101420
+ if (isRoleOn(settings?.detect)) detect.push(nodeId);
101421
+ if (isRoleOn(settings?.audio)) audio.push(nodeId);
101422
+ if (isRoleOn(settings?.ingest)) ingest.push(nodeId);
100946
101423
  const host = settings?.reachableHost?.trim();
100947
101424
  if (host !== void 0 && host.length > 0) reachableHosts.set(nodeId, host);
100948
101425
  }
@@ -102320,7 +102797,7 @@ var PipelineSettingsStore = class PipelineSettingsStore {
102320
102797
  const deviceFormat = deviceKey ? parseDeviceKey(deviceKey).format : catalog.selectedEngine.format;
102321
102798
  const jumpFormats = [...new Set(jumpDeviceKeys.map((k) => parseDeviceKey(k).format))];
102322
102799
  const { config: resolvedBase, skipped, droppedRoots } = resolvePipelineWithSkips(cam, deviceFormat, catalog, jumpFormats);
102323
- if (skipped.length > 0) this.deps.logger.warn("pipeline steps gated on but not provisioned on this node — skipped", {
102800
+ if (skipped.length > 0) this.deps.logger.warn(`pipeline steps gated on but not provisioned on this node — skipped (node ${agentNodeId}, device ${deviceId})`, {
102324
102801
  tags: {
102325
102802
  deviceId,
102326
102803
  nodeId: agentNodeId
@@ -106681,6 +107158,12 @@ async function buildOrchestratorControllers(deps) {
106681
107158
  const unsubAgentOnlineEvent = deps.ctx().eventBus.subscribe({ category: EventCategory.AgentOnline }, (event) => {
106682
107159
  const nodeId = event.data.agentId;
106683
107160
  if (!nodeId.includes("/")) topology.addKnownRunnerNode(nodeId);
107161
+ if (!nodeId.includes("/")) topology.refreshNodeCapabilities().catch((err) => {
107162
+ deps.ctx().logger.warn("refreshNodeCapabilities after agent online failed", {
107163
+ tags: { nodeId },
107164
+ meta: { error: errMsg$2(err) }
107165
+ });
107166
+ });
106684
107167
  sliceGate.forgetNode(nodeId);
106685
107168
  sliceGate.refresh("agent-online");
106686
107169
  providerGate.forgetProcess(nodeId);