@camstack/types 1.1.23 → 1.1.25

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.
@@ -148,6 +148,7 @@ declare const AgentLoadSummarySchema: z.ZodObject<{
148
148
  }, z.core.$strip>;
149
149
  }, z.core.$strip>;
150
150
  score: z.ZodNumber;
151
+ decodeHwaccel: z.ZodNullable<z.ZodString>;
151
152
  }, z.core.$strip>;
152
153
  /**
153
154
  * Aggregate metrics across the whole detection cluster. Replaces the legacy
@@ -543,6 +544,7 @@ export declare const pipelineOrchestratorCapability: {
543
544
  }, z.core.$strip>;
544
545
  }, z.core.$strip>;
545
546
  score: z.ZodNumber;
547
+ decodeHwaccel: z.ZodNullable<z.ZodString>;
546
548
  }, z.core.$strip>>>, import("./capability-definition.js").CapabilityMethodKind>;
547
549
  /** Aggregate global metrics across the whole detection cluster. */
548
550
  readonly getGlobalMetrics: import("./capability-definition.js").CapabilityMethodSchema<z.ZodVoid, z.ZodObject<{
@@ -685,6 +687,11 @@ export declare const pipelineOrchestratorCapability: {
685
687
  settings: z.ZodReadonly<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
686
688
  }, z.core.$strip>>>;
687
689
  maxCameras: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
690
+ detectWeight: z.ZodOptional<z.ZodNumber>;
691
+ detect: z.ZodOptional<z.ZodBoolean>;
692
+ decode: z.ZodOptional<z.ZodBoolean>;
693
+ audio: z.ZodOptional<z.ZodBoolean>;
694
+ ingest: z.ZodOptional<z.ZodBoolean>;
688
695
  }, z.core.$strip>>, import("./capability-definition.js").CapabilityMethodKind>;
689
696
  /** Enumerate every agent's settings (hub + remote runners). */
690
697
  readonly listAgentSettings: import("./capability-definition.js").CapabilityMethodSchema<z.ZodVoid, z.ZodReadonly<z.ZodArray<z.ZodObject<{
@@ -696,6 +703,11 @@ export declare const pipelineOrchestratorCapability: {
696
703
  settings: z.ZodReadonly<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
697
704
  }, z.core.$strip>>>;
698
705
  maxCameras: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
706
+ detectWeight: z.ZodOptional<z.ZodNumber>;
707
+ detect: z.ZodOptional<z.ZodBoolean>;
708
+ decode: z.ZodOptional<z.ZodBoolean>;
709
+ audio: z.ZodOptional<z.ZodBoolean>;
710
+ ingest: z.ZodOptional<z.ZodBoolean>;
699
711
  }, z.core.$strip>;
700
712
  }, z.core.$strip>>>, import("./capability-definition.js").CapabilityMethodKind>;
701
713
  /** Bulk-replace ALL addon configs on one agent. Used by the agent-level PipelineEditor which emits the full map per edit. Merges on top of existing entries — omitted addonIds are left alone. */
@@ -743,6 +755,36 @@ export declare const pipelineOrchestratorCapability: {
743
755
  }, z.core.$strip>, z.ZodObject<{
744
756
  success: z.ZodLiteral<true>;
745
757
  }, z.core.$strip>, "mutation">;
758
+ /**
759
+ * Set a node's detection WEIGHT (relative share for the quota balancer).
760
+ * `null` clears it (back to the implicit weight 1). Unpinned cameras
761
+ * distribute proportionally to weight; `maxCameras` still clamps on top.
762
+ */
763
+ readonly setAgentDetectWeight: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
764
+ agentNodeId: z.ZodString;
765
+ detectWeight: z.ZodNullable<z.ZodNumber>;
766
+ }, z.core.$strip>, z.ZodObject<{
767
+ success: z.ZodLiteral<true>;
768
+ }, z.core.$strip>, "mutation">;
769
+ /**
770
+ * Set one node's placement CAPABILITIES — the per-node record that
771
+ * replaced the four flat orchestrator lists (`enabledNodes`,
772
+ * `enabledDecoderNodes`, `enabledAudioNodes`, `remoteSourcingNodes`).
773
+ * Each flag is optional in the patch: omit a flag to leave it unchanged,
774
+ * pass `null` to reset it to the node default (`hub` → capable, every
775
+ * other node → not). Detection/decode/audio eligibility is derived from
776
+ * these flags across the cluster; changing them re-derives the enabled
777
+ * sets and reconciles dispatch immediately.
778
+ */
779
+ readonly setAgentCapabilities: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
780
+ agentNodeId: z.ZodString;
781
+ detect: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
782
+ decode: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
783
+ audio: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
784
+ ingest: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
785
+ }, z.core.$strip>, z.ZodObject<{
786
+ success: z.ZodLiteral<true>;
787
+ }, z.core.$strip>, "mutation">;
746
788
  /** Read one camera's settings. Null when never touched (inherits agent defaults fully). */
747
789
  readonly getCameraSettings: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
748
790
  deviceId: z.ZodNumber;
@@ -268,143 +268,6 @@ export declare const platformProbeCapability: {
268
268
  preferred: z.ZodReadonly<z.ZodArray<z.ZodString>>;
269
269
  rationale: z.ZodString;
270
270
  }, z.core.$strip>, import("./capability-definition.js").CapabilityMethodKind>;
271
- /**
272
- * Hardware-encoder probe — see Task #185. Cached after first call.
273
- */
274
- readonly getHardwareEncoders: import("./capability-definition.js").CapabilityMethodSchema<z.ZodVoid, z.ZodObject<{
275
- encoders: z.ZodReadonly<z.ZodArray<z.ZodObject<{
276
- encoder: z.ZodEnum<{
277
- h264_videotoolbox: "h264_videotoolbox";
278
- hevc_videotoolbox: "hevc_videotoolbox";
279
- h264_vaapi: "h264_vaapi";
280
- hevc_vaapi: "hevc_vaapi";
281
- h264_nvenc: "h264_nvenc";
282
- hevc_nvenc: "hevc_nvenc";
283
- h264_qsv: "h264_qsv";
284
- hevc_qsv: "hevc_qsv";
285
- h264_amf: "h264_amf";
286
- hevc_amf: "hevc_amf";
287
- libx264: "libx264";
288
- libx265: "libx265";
289
- }>;
290
- codec: z.ZodEnum<{
291
- H264: "H264";
292
- H265: "H265";
293
- }>;
294
- family: z.ZodEnum<{
295
- videotoolbox: "videotoolbox";
296
- vaapi: "vaapi";
297
- qsv: "qsv";
298
- amf: "amf";
299
- nvenc: "nvenc";
300
- software: "software";
301
- }>;
302
- available: z.ZodBoolean;
303
- reason: z.ZodOptional<z.ZodString>;
304
- }, z.core.$strip>>>;
305
- defaultH264: z.ZodEnum<{
306
- h264_videotoolbox: "h264_videotoolbox";
307
- hevc_videotoolbox: "hevc_videotoolbox";
308
- h264_vaapi: "h264_vaapi";
309
- hevc_vaapi: "hevc_vaapi";
310
- h264_nvenc: "h264_nvenc";
311
- hevc_nvenc: "hevc_nvenc";
312
- h264_qsv: "h264_qsv";
313
- hevc_qsv: "hevc_qsv";
314
- h264_amf: "h264_amf";
315
- hevc_amf: "hevc_amf";
316
- libx264: "libx264";
317
- libx265: "libx265";
318
- }>;
319
- defaultH265: z.ZodEnum<{
320
- h264_videotoolbox: "h264_videotoolbox";
321
- hevc_videotoolbox: "hevc_videotoolbox";
322
- h264_vaapi: "h264_vaapi";
323
- hevc_vaapi: "hevc_vaapi";
324
- h264_nvenc: "h264_nvenc";
325
- hevc_nvenc: "hevc_nvenc";
326
- h264_qsv: "h264_qsv";
327
- hevc_qsv: "hevc_qsv";
328
- h264_amf: "h264_amf";
329
- hevc_amf: "hevc_amf";
330
- libx264: "libx264";
331
- libx265: "libx265";
332
- }>;
333
- probedAt: z.ZodNumber;
334
- }, z.core.$strip>, import("./capability-definition.js").CapabilityMethodKind>;
335
- readonly refreshHardwareEncoders: import("./capability-definition.js").CapabilityMethodSchema<z.ZodVoid, z.ZodObject<{
336
- encoders: z.ZodReadonly<z.ZodArray<z.ZodObject<{
337
- encoder: z.ZodEnum<{
338
- h264_videotoolbox: "h264_videotoolbox";
339
- hevc_videotoolbox: "hevc_videotoolbox";
340
- h264_vaapi: "h264_vaapi";
341
- hevc_vaapi: "hevc_vaapi";
342
- h264_nvenc: "h264_nvenc";
343
- hevc_nvenc: "hevc_nvenc";
344
- h264_qsv: "h264_qsv";
345
- hevc_qsv: "hevc_qsv";
346
- h264_amf: "h264_amf";
347
- hevc_amf: "hevc_amf";
348
- libx264: "libx264";
349
- libx265: "libx265";
350
- }>;
351
- codec: z.ZodEnum<{
352
- H264: "H264";
353
- H265: "H265";
354
- }>;
355
- family: z.ZodEnum<{
356
- videotoolbox: "videotoolbox";
357
- vaapi: "vaapi";
358
- qsv: "qsv";
359
- amf: "amf";
360
- nvenc: "nvenc";
361
- software: "software";
362
- }>;
363
- available: z.ZodBoolean;
364
- reason: z.ZodOptional<z.ZodString>;
365
- }, z.core.$strip>>>;
366
- defaultH264: z.ZodEnum<{
367
- h264_videotoolbox: "h264_videotoolbox";
368
- hevc_videotoolbox: "hevc_videotoolbox";
369
- h264_vaapi: "h264_vaapi";
370
- hevc_vaapi: "hevc_vaapi";
371
- h264_nvenc: "h264_nvenc";
372
- hevc_nvenc: "hevc_nvenc";
373
- h264_qsv: "h264_qsv";
374
- hevc_qsv: "hevc_qsv";
375
- h264_amf: "h264_amf";
376
- hevc_amf: "hevc_amf";
377
- libx264: "libx264";
378
- libx265: "libx265";
379
- }>;
380
- defaultH265: z.ZodEnum<{
381
- h264_videotoolbox: "h264_videotoolbox";
382
- hevc_videotoolbox: "hevc_videotoolbox";
383
- h264_vaapi: "h264_vaapi";
384
- hevc_vaapi: "hevc_vaapi";
385
- h264_nvenc: "h264_nvenc";
386
- hevc_nvenc: "hevc_nvenc";
387
- h264_qsv: "h264_qsv";
388
- hevc_qsv: "hevc_qsv";
389
- h264_amf: "h264_amf";
390
- hevc_amf: "hevc_amf";
391
- libx264: "libx264";
392
- libx265: "libx265";
393
- }>;
394
- probedAt: z.ZodNumber;
395
- }, z.core.$strip>, "mutation">;
396
- /**
397
- * Decode-side hw-accel probe — the `-hwaccel` methods the configured ffmpeg
398
- * binary supports (via `ffmpeg -hwaccels`). Cached after first call.
399
- */
400
- readonly getHardwareDecodeAccels: import("./capability-definition.js").CapabilityMethodSchema<z.ZodVoid, z.ZodObject<{
401
- methods: z.ZodReadonly<z.ZodArray<z.ZodString>>;
402
- probedAt: z.ZodNumber;
403
- }, z.core.$strip>, import("./capability-definition.js").CapabilityMethodKind>;
404
- readonly refreshHardwareDecodeAccels: import("./capability-definition.js").CapabilityMethodSchema<z.ZodVoid, z.ZodObject<{
405
- methods: z.ZodReadonly<z.ZodArray<z.ZodString>>;
406
- probedAt: z.ZodNumber;
407
- }, z.core.$strip>, "mutation">;
408
271
  };
409
272
  };
410
273
  export type IPlatformProbeProvider = InferProvider<typeof platformProbeCapability>;
@@ -1,4 +1,11 @@
1
1
  import type { IScopedLogger } from '../interfaces/logging.js';
2
+ /**
3
+ * Recursively find the first file named exactly `name` under `dir`. Used as the
4
+ * archive-extraction fallback when the declared inner path misses (the archive's
5
+ * top-level dir carries an unpredictable version/arch, e.g. johnvansickle's
6
+ * `ffmpeg-<version>-<arch>-static/`). Returns the absolute path, or `null`.
7
+ */
8
+ export declare function findFileByName(dir: string, name: string): string | null;
2
9
  export interface PlatformInfo {
3
10
  readonly platform: NodeJS.Platform;
4
11
  readonly arch: NodeJS.Architecture;
@@ -9665,6 +9665,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
9665
9665
  };
9666
9666
  };
9667
9667
  score: number;
9668
+ decodeHwaccel: string | null;
9668
9669
  }[];
9669
9670
  meta: object;
9670
9671
  }>;
@@ -9886,6 +9887,22 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
9886
9887
  };
9887
9888
  meta: object;
9888
9889
  }>;
9890
+ setAgentDetectWeight: import("@trpc/server").TRPCMutationProcedure<{
9891
+ input: {
9892
+ [x: string]: unknown;
9893
+ agentNodeId: string;
9894
+ detectWeight: number | null;
9895
+ };
9896
+ output: {
9897
+ success: true;
9898
+ };
9899
+ meta: object;
9900
+ }>;
9901
+ setAgentCapabilities: import("@trpc/server").TRPCMutationProcedure<{
9902
+ input: any;
9903
+ output: any;
9904
+ meta: object;
9905
+ }>;
9889
9906
  getCameraSettings: import("@trpc/server").TRPCQueryProcedure<{
9890
9907
  input: {
9891
9908
  [x: string]: unknown;
@@ -10704,62 +10721,6 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
10704
10721
  };
10705
10722
  meta: object;
10706
10723
  }>;
10707
- getHardwareEncoders: import("@trpc/server").TRPCQueryProcedure<{
10708
- input: {
10709
- nodeId?: string | undefined;
10710
- } | undefined;
10711
- output: {
10712
- encoders: readonly {
10713
- encoder: "h264_videotoolbox" | "hevc_videotoolbox" | "h264_vaapi" | "hevc_vaapi" | "h264_nvenc" | "hevc_nvenc" | "h264_qsv" | "hevc_qsv" | "h264_amf" | "hevc_amf" | "libx264" | "libx265";
10714
- codec: "H264" | "H265";
10715
- family: "videotoolbox" | "vaapi" | "qsv" | "amf" | "nvenc" | "software";
10716
- available: boolean;
10717
- reason?: string | undefined;
10718
- }[];
10719
- defaultH264: "h264_videotoolbox" | "hevc_videotoolbox" | "h264_vaapi" | "hevc_vaapi" | "h264_nvenc" | "hevc_nvenc" | "h264_qsv" | "hevc_qsv" | "h264_amf" | "hevc_amf" | "libx264" | "libx265";
10720
- defaultH265: "h264_videotoolbox" | "hevc_videotoolbox" | "h264_vaapi" | "hevc_vaapi" | "h264_nvenc" | "hevc_nvenc" | "h264_qsv" | "hevc_qsv" | "h264_amf" | "hevc_amf" | "libx264" | "libx265";
10721
- probedAt: number;
10722
- };
10723
- meta: object;
10724
- }>;
10725
- refreshHardwareEncoders: import("@trpc/server").TRPCMutationProcedure<{
10726
- input: {
10727
- nodeId?: string | undefined;
10728
- } | undefined;
10729
- output: {
10730
- encoders: readonly {
10731
- encoder: "h264_videotoolbox" | "hevc_videotoolbox" | "h264_vaapi" | "hevc_vaapi" | "h264_nvenc" | "hevc_nvenc" | "h264_qsv" | "hevc_qsv" | "h264_amf" | "hevc_amf" | "libx264" | "libx265";
10732
- codec: "H264" | "H265";
10733
- family: "videotoolbox" | "vaapi" | "qsv" | "amf" | "nvenc" | "software";
10734
- available: boolean;
10735
- reason?: string | undefined;
10736
- }[];
10737
- defaultH264: "h264_videotoolbox" | "hevc_videotoolbox" | "h264_vaapi" | "hevc_vaapi" | "h264_nvenc" | "hevc_nvenc" | "h264_qsv" | "hevc_qsv" | "h264_amf" | "hevc_amf" | "libx264" | "libx265";
10738
- defaultH265: "h264_videotoolbox" | "hevc_videotoolbox" | "h264_vaapi" | "hevc_vaapi" | "h264_nvenc" | "hevc_nvenc" | "h264_qsv" | "hevc_qsv" | "h264_amf" | "hevc_amf" | "libx264" | "libx265";
10739
- probedAt: number;
10740
- };
10741
- meta: object;
10742
- }>;
10743
- getHardwareDecodeAccels: import("@trpc/server").TRPCQueryProcedure<{
10744
- input: {
10745
- nodeId?: string | undefined;
10746
- } | undefined;
10747
- output: {
10748
- methods: readonly string[];
10749
- probedAt: number;
10750
- };
10751
- meta: object;
10752
- }>;
10753
- refreshHardwareDecodeAccels: import("@trpc/server").TRPCMutationProcedure<{
10754
- input: {
10755
- nodeId?: string | undefined;
10756
- } | undefined;
10757
- output: {
10758
- methods: readonly string[];
10759
- probedAt: number;
10760
- };
10761
- meta: object;
10762
- }>;
10763
10724
  }>>;
10764
10725
  powerMeter: import("@trpc/server").TRPCBuiltRouter<{
10765
10726
  ctx: TrpcContext;
@@ -24058,6 +24019,7 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
24058
24019
  };
24059
24020
  };
24060
24021
  score: number;
24022
+ decodeHwaccel: string | null;
24061
24023
  }[];
24062
24024
  meta: object;
24063
24025
  }>;
@@ -24279,6 +24241,22 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
24279
24241
  };
24280
24242
  meta: object;
24281
24243
  }>;
24244
+ setAgentDetectWeight: import("@trpc/server").TRPCMutationProcedure<{
24245
+ input: {
24246
+ [x: string]: unknown;
24247
+ agentNodeId: string;
24248
+ detectWeight: number | null;
24249
+ };
24250
+ output: {
24251
+ success: true;
24252
+ };
24253
+ meta: object;
24254
+ }>;
24255
+ setAgentCapabilities: import("@trpc/server").TRPCMutationProcedure<{
24256
+ input: any;
24257
+ output: any;
24258
+ meta: object;
24259
+ }>;
24282
24260
  getCameraSettings: import("@trpc/server").TRPCQueryProcedure<{
24283
24261
  input: {
24284
24262
  [x: string]: unknown;
@@ -25097,62 +25075,6 @@ export type AppRouter = import("@trpc/server/unstable-core-do-not-import").Route
25097
25075
  };
25098
25076
  meta: object;
25099
25077
  }>;
25100
- getHardwareEncoders: import("@trpc/server").TRPCQueryProcedure<{
25101
- input: {
25102
- nodeId?: string | undefined;
25103
- } | undefined;
25104
- output: {
25105
- encoders: readonly {
25106
- encoder: "h264_videotoolbox" | "hevc_videotoolbox" | "h264_vaapi" | "hevc_vaapi" | "h264_nvenc" | "hevc_nvenc" | "h264_qsv" | "hevc_qsv" | "h264_amf" | "hevc_amf" | "libx264" | "libx265";
25107
- codec: "H264" | "H265";
25108
- family: "videotoolbox" | "vaapi" | "qsv" | "amf" | "nvenc" | "software";
25109
- available: boolean;
25110
- reason?: string | undefined;
25111
- }[];
25112
- defaultH264: "h264_videotoolbox" | "hevc_videotoolbox" | "h264_vaapi" | "hevc_vaapi" | "h264_nvenc" | "hevc_nvenc" | "h264_qsv" | "hevc_qsv" | "h264_amf" | "hevc_amf" | "libx264" | "libx265";
25113
- defaultH265: "h264_videotoolbox" | "hevc_videotoolbox" | "h264_vaapi" | "hevc_vaapi" | "h264_nvenc" | "hevc_nvenc" | "h264_qsv" | "hevc_qsv" | "h264_amf" | "hevc_amf" | "libx264" | "libx265";
25114
- probedAt: number;
25115
- };
25116
- meta: object;
25117
- }>;
25118
- refreshHardwareEncoders: import("@trpc/server").TRPCMutationProcedure<{
25119
- input: {
25120
- nodeId?: string | undefined;
25121
- } | undefined;
25122
- output: {
25123
- encoders: readonly {
25124
- encoder: "h264_videotoolbox" | "hevc_videotoolbox" | "h264_vaapi" | "hevc_vaapi" | "h264_nvenc" | "hevc_nvenc" | "h264_qsv" | "hevc_qsv" | "h264_amf" | "hevc_amf" | "libx264" | "libx265";
25125
- codec: "H264" | "H265";
25126
- family: "videotoolbox" | "vaapi" | "qsv" | "amf" | "nvenc" | "software";
25127
- available: boolean;
25128
- reason?: string | undefined;
25129
- }[];
25130
- defaultH264: "h264_videotoolbox" | "hevc_videotoolbox" | "h264_vaapi" | "hevc_vaapi" | "h264_nvenc" | "hevc_nvenc" | "h264_qsv" | "hevc_qsv" | "h264_amf" | "hevc_amf" | "libx264" | "libx265";
25131
- defaultH265: "h264_videotoolbox" | "hevc_videotoolbox" | "h264_vaapi" | "hevc_vaapi" | "h264_nvenc" | "hevc_nvenc" | "h264_qsv" | "hevc_qsv" | "h264_amf" | "hevc_amf" | "libx264" | "libx265";
25132
- probedAt: number;
25133
- };
25134
- meta: object;
25135
- }>;
25136
- getHardwareDecodeAccels: import("@trpc/server").TRPCQueryProcedure<{
25137
- input: {
25138
- nodeId?: string | undefined;
25139
- } | undefined;
25140
- output: {
25141
- methods: readonly string[];
25142
- probedAt: number;
25143
- };
25144
- meta: object;
25145
- }>;
25146
- refreshHardwareDecodeAccels: import("@trpc/server").TRPCMutationProcedure<{
25147
- input: {
25148
- nodeId?: string | undefined;
25149
- } | undefined;
25150
- output: {
25151
- methods: readonly string[];
25152
- probedAt: number;
25153
- };
25154
- meta: object;
25155
- }>;
25156
25078
  }>>;
25157
25079
  powerMeter: import("@trpc/server").TRPCBuiltRouter<{
25158
25080
  ctx: TrpcContext;
@@ -6,7 +6,7 @@
6
6
  * scope+access check inside `protectedProcedure` (see
7
7
  * `server/backend/src/api/trpc/trpc.middleware.ts`).
8
8
  *
9
- * Coverage: 737 method paths across 111 capabilities.
9
+ * Coverage: 735 method paths across 111 capabilities.
10
10
  */
11
11
  import type { CapabilityMethodAccess } from '../capabilities/capability-definition.js';
12
12
  export interface MethodAccessRecord {
@@ -72,7 +72,7 @@ export interface SystemProxy {
72
72
  readonly nodes: Pick<InferProvider<typeof nodesCapability>, 'topology' | 'deployAddon' | 'undeployAddon' | 'restartAddon' | 'restartProcess' | 'restartNode' | 'shutdownNode' | 'renameNode' | 'clusterAddonStatus' | 'getCapUsageGraph' | 'getNodeAddons' | 'setProcessLogLevel' | 'executeQuery'>;
73
73
  readonly notificationOutput: Pick<InferProvider<typeof notificationOutputCapability>, 'listTargetKinds' | 'listTargets' | 'discoverTargets' | 'send' | 'testTarget' | 'upsertTarget' | 'deleteTarget' | 'setTargetEnabled'>;
74
74
  readonly pipelineExecutor: Pick<InferProvider<typeof pipelineExecutorCapability>, 'getAvailableEngines' | 'getSelectedEngine' | 'getDefaultSteps' | 'reprobeEngine' | 'getEngineProvisioning' | 'getVideoPipelineSteps' | 'setVideoPipelineSteps' | 'getSchema' | 'getGlobalSteps' | 'getGlobalPipelineConfig' | 'getOrchestratorConfigSchema' | 'listTemplates' | 'saveTemplate' | 'updateTemplate' | 'deleteTemplate' | 'getCapabilities' | 'getAddonModels' | 'downloadModel' | 'deleteModel' | 'detect' | 'cacheFrameInPool' | 'inferCached' | 'uncacheFrame' | 'getEffectiveTuning' | 'listLoadedEngines' | 'spinEngine' | 'killEngine' | 'listReferenceImages' | 'getReferenceImage' | 'getReferenceAudioFiles' | 'getReferenceAudio' | 'getAudioCapabilities' | 'runAudioTest' | 'getDetectionConfigSchema'>;
75
- readonly pipelineOrchestrator: Pick<InferProvider<typeof pipelineOrchestratorCapability>, 'rebalance' | 'getPipelineAssignments' | 'getAgentLoad' | 'getGlobalMetrics' | 'getCapabilityBindings' | 'setCapabilityBinding' | 'getDecoderAssignments' | 'getAudioNodeLoad' | 'getAgentSettings' | 'listAgentSettings' | 'setAgentAddonDefaults' | 'removeAgentSettings' | 'setAgentMaxCameras' | 'getCameraStatuses' | 'listTemplates' | 'saveTemplate' | 'updateTemplate' | 'deleteTemplate'>;
75
+ readonly pipelineOrchestrator: Pick<InferProvider<typeof pipelineOrchestratorCapability>, 'rebalance' | 'getPipelineAssignments' | 'getAgentLoad' | 'getGlobalMetrics' | 'getCapabilityBindings' | 'setCapabilityBinding' | 'getDecoderAssignments' | 'getAudioNodeLoad' | 'getAgentSettings' | 'listAgentSettings' | 'setAgentAddonDefaults' | 'removeAgentSettings' | 'setAgentMaxCameras' | 'setAgentDetectWeight' | 'setAgentCapabilities' | 'getCameraStatuses' | 'listTemplates' | 'saveTemplate' | 'updateTemplate' | 'deleteTemplate'>;
76
76
  readonly pipelineRunner: Pick<InferProvider<typeof pipelineRunnerCapability>, 'attachCamera' | 'reportMotion' | 'getLocalLoad' | 'getLocalMetrics' | 'getAllCameraMetrics' | 'getLocalCameras'>;
77
77
  readonly plateGallery: Pick<InferProvider<typeof plateGalleryCapability>, 'getPlateMedia' | 'searchPlates' | 'suggestPlateClusters' | 'correctPlateText' | 'deletePlate'>;
78
78
  readonly recording: Pick<InferProvider<typeof recordingCapability>, 'getStorageUsage'>;
package/dist/index.js CHANGED
@@ -11998,6 +11998,8 @@ function createSystemProxy(api) {
11998
11998
  setAgentAddonDefaults: (input) => dispatch("pipelineOrchestrator", "setAgentAddonDefaults", "mutation", input),
11999
11999
  removeAgentSettings: (input) => dispatch("pipelineOrchestrator", "removeAgentSettings", "mutation", input),
12000
12000
  setAgentMaxCameras: (input) => dispatch("pipelineOrchestrator", "setAgentMaxCameras", "mutation", input),
12001
+ setAgentDetectWeight: (input) => dispatch("pipelineOrchestrator", "setAgentDetectWeight", "mutation", input),
12002
+ setAgentCapabilities: (input) => dispatch("pipelineOrchestrator", "setAgentCapabilities", "mutation", input),
12001
12003
  getCameraStatuses: (input) => dispatch("pipelineOrchestrator", "getCameraStatuses", "query", input),
12002
12004
  listTemplates: (input) => dispatch("pipelineOrchestrator", "listTemplates", "query", input),
12003
12005
  saveTemplate: (input) => dispatch("pipelineOrchestrator", "saveTemplate", "mutation", input),
@@ -16920,7 +16922,17 @@ var AgentAddonConfigSchema = zod.z.object({
16920
16922
  });
16921
16923
  var AgentPipelineSettingsSchema = zod.z.object({
16922
16924
  addonDefaults: zod.z.record(zod.z.string(), AgentAddonConfigSchema).readonly(),
16923
- maxCameras: zod.z.number().int().nonnegative().nullable().default(null)
16925
+ maxCameras: zod.z.number().int().nonnegative().nullable().default(null),
16926
+ /** Per-node detection weight (relative share for the quota balancer). */
16927
+ detectWeight: zod.z.number().positive().optional(),
16928
+ /** Node is eligible to run the detection pipeline (decode + inference). */
16929
+ detect: zod.z.boolean().optional(),
16930
+ /** Node is eligible to host decoder sessions. */
16931
+ decode: zod.z.boolean().optional(),
16932
+ /** Node is eligible to run audio-analyzer sessions. */
16933
+ audio: zod.z.boolean().optional(),
16934
+ /** Node is eligible to be the ingest / source-owner (serve the restream). */
16935
+ ingest: zod.z.boolean().optional()
16924
16936
  });
16925
16937
  var CameraPipelineForAgentSchema = zod.z.object({
16926
16938
  steps: zod.z.array(PipelineStepInputSchema).readonly(),
@@ -16995,7 +17007,10 @@ var AgentLoadSummarySchema = zod.z.object({
16995
17007
  online: zod.z.boolean(),
16996
17008
  load: RunnerLocalLoadSchema,
16997
17009
  /** Computed score used by the L2 capacity balancer (lower = less loaded). */
16998
- score: zod.z.number()
17010
+ score: zod.z.number(),
17011
+ /** This node's decode hwaccel backend (per-node `probedBestHwaccel`), or null
17012
+ * when not yet probed — for the cluster Pipeline table UI (P0.2). */
17013
+ decodeHwaccel: zod.z.string().nullable()
16999
17014
  });
17000
17015
  /**
17001
17016
  * Aggregate metrics across the whole detection cluster. Replaces the legacy
@@ -17359,6 +17374,38 @@ var pipelineOrchestratorCapability = {
17359
17374
  kind: "mutation",
17360
17375
  auth: "admin"
17361
17376
  }),
17377
+ /**
17378
+ * Set a node's detection WEIGHT (relative share for the quota balancer).
17379
+ * `null` clears it (back to the implicit weight 1). Unpinned cameras
17380
+ * distribute proportionally to weight; `maxCameras` still clamps on top.
17381
+ */
17382
+ setAgentDetectWeight: require_sleep.method(zod.z.object({
17383
+ agentNodeId: zod.z.string(),
17384
+ detectWeight: zod.z.number().positive().nullable()
17385
+ }), zod.z.object({ success: zod.z.literal(true) }), {
17386
+ kind: "mutation",
17387
+ auth: "admin"
17388
+ }),
17389
+ /**
17390
+ * Set one node's placement CAPABILITIES — the per-node record that
17391
+ * replaced the four flat orchestrator lists (`enabledNodes`,
17392
+ * `enabledDecoderNodes`, `enabledAudioNodes`, `remoteSourcingNodes`).
17393
+ * Each flag is optional in the patch: omit a flag to leave it unchanged,
17394
+ * pass `null` to reset it to the node default (`hub` → capable, every
17395
+ * other node → not). Detection/decode/audio eligibility is derived from
17396
+ * these flags across the cluster; changing them re-derives the enabled
17397
+ * sets and reconciles dispatch immediately.
17398
+ */
17399
+ setAgentCapabilities: require_sleep.method(zod.z.object({
17400
+ agentNodeId: zod.z.string(),
17401
+ detect: zod.z.boolean().nullable().optional(),
17402
+ decode: zod.z.boolean().nullable().optional(),
17403
+ audio: zod.z.boolean().nullable().optional(),
17404
+ ingest: zod.z.boolean().nullable().optional()
17405
+ }), zod.z.object({ success: zod.z.literal(true) }), {
17406
+ kind: "mutation",
17407
+ auth: "admin"
17408
+ }),
17362
17409
  /** Read one camera's settings. Null when never touched (inherits agent defaults fully). */
17363
17410
  getCameraSettings: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), CameraPipelineSettingsSchema.nullable()),
17364
17411
  /** Set or clear the 3-state toggle for one (camera, addonId). Pass `enabled: null` to clear and revert to agent default. */
@@ -20845,21 +20892,13 @@ var HardwareEncoderProbeSchema = zod.z.object({
20845
20892
  available: zod.z.boolean(),
20846
20893
  reason: zod.z.string().optional()
20847
20894
  });
20848
- var HardwareEncodersSchema = zod.z.object({
20895
+ zod.z.object({
20849
20896
  encoders: zod.z.array(HardwareEncoderProbeSchema).readonly(),
20850
20897
  defaultH264: HardwareEncoderIdSchema,
20851
20898
  defaultH265: HardwareEncoderIdSchema,
20852
20899
  probedAt: zod.z.number()
20853
20900
  });
20854
- /**
20855
- * Decode-side companion to {@link HardwareEncodersSchema}: the `-hwaccel`
20856
- * methods the configured ffmpeg binary actually supports (parsed from
20857
- * `ffmpeg -hwaccels`). Used to gate decode-hwaccel attempts so the transcode
20858
- * egress never spends a spawn on a backend this build cannot offer. Per-stream
20859
- * decode fragility (e.g. VideoToolbox HEVC) is still handled by the egress's
20860
- * software fallback — this only filters out wholly-unsupported backends.
20861
- */
20862
- var HardwareDecodeAccelsSchema = zod.z.object({
20901
+ zod.z.object({
20863
20902
  methods: zod.z.array(zod.z.string()).readonly(),
20864
20903
  probedAt: zod.z.number()
20865
20904
  });
@@ -20930,24 +20969,7 @@ var platformProbeCapability = {
20930
20969
  getCapabilities: require_sleep.method(zod.z.void(), PlatformCapabilitiesSchema),
20931
20970
  getHardware: require_sleep.method(zod.z.void(), HardwareInfoSchema),
20932
20971
  resolveInferenceConfig: require_sleep.method(zod.z.object({ requirements: zod.z.array(ModelRequirementSchema).readonly() }), ResolvedInferenceConfigSchema),
20933
- resolveHwAccel: require_sleep.method(zod.z.object({ prefer: HwAccelBackendInputSchema }), HwAccelResolutionSchema),
20934
- /**
20935
- * Hardware-encoder probe — see Task #185. Cached after first call.
20936
- */
20937
- getHardwareEncoders: require_sleep.method(zod.z.void(), HardwareEncodersSchema),
20938
- refreshHardwareEncoders: require_sleep.method(zod.z.void(), HardwareEncodersSchema, {
20939
- kind: "mutation",
20940
- auth: "admin"
20941
- }),
20942
- /**
20943
- * Decode-side hw-accel probe — the `-hwaccel` methods the configured ffmpeg
20944
- * binary supports (via `ffmpeg -hwaccels`). Cached after first call.
20945
- */
20946
- getHardwareDecodeAccels: require_sleep.method(zod.z.void(), HardwareDecodeAccelsSchema),
20947
- refreshHardwareDecodeAccels: require_sleep.method(zod.z.void(), HardwareDecodeAccelsSchema, {
20948
- kind: "mutation",
20949
- auth: "admin"
20950
- })
20972
+ resolveHwAccel: require_sleep.method(zod.z.object({ prefer: HwAccelBackendInputSchema }), HwAccelResolutionSchema)
20951
20973
  }
20952
20974
  };
20953
20975
  //#endregion
@@ -25811,6 +25833,18 @@ var METHOD_ACCESS_MAP = Object.freeze({
25811
25833
  addonId: null,
25812
25834
  access: "create"
25813
25835
  },
25836
+ "pipelineOrchestrator.setAgentCapabilities": {
25837
+ capName: "pipeline-orchestrator",
25838
+ capScope: "system",
25839
+ addonId: null,
25840
+ access: "create"
25841
+ },
25842
+ "pipelineOrchestrator.setAgentDetectWeight": {
25843
+ capName: "pipeline-orchestrator",
25844
+ capScope: "system",
25845
+ addonId: null,
25846
+ access: "create"
25847
+ },
25814
25848
  "pipelineOrchestrator.setAgentMaxCameras": {
25815
25849
  capName: "pipeline-orchestrator",
25816
25850
  capScope: "system",
@@ -25967,30 +26001,6 @@ var METHOD_ACCESS_MAP = Object.freeze({
25967
26001
  addonId: null,
25968
26002
  access: "view"
25969
26003
  },
25970
- "platformProbe.getHardwareDecodeAccels": {
25971
- capName: "platform-probe",
25972
- capScope: "system",
25973
- addonId: null,
25974
- access: "view"
25975
- },
25976
- "platformProbe.getHardwareEncoders": {
25977
- capName: "platform-probe",
25978
- capScope: "system",
25979
- addonId: null,
25980
- access: "view"
25981
- },
25982
- "platformProbe.refreshHardwareDecodeAccels": {
25983
- capName: "platform-probe",
25984
- capScope: "system",
25985
- addonId: null,
25986
- access: "create"
25987
- },
25988
- "platformProbe.refreshHardwareEncoders": {
25989
- capName: "platform-probe",
25990
- capScope: "system",
25991
- addonId: null,
25992
- access: "create"
25993
- },
25994
26004
  "platformProbe.resolveHwAccel": {
25995
26005
  capName: "platform-probe",
25996
26006
  capScope: "system",
package/dist/index.mjs CHANGED
@@ -11997,6 +11997,8 @@ function createSystemProxy(api) {
11997
11997
  setAgentAddonDefaults: (input) => dispatch("pipelineOrchestrator", "setAgentAddonDefaults", "mutation", input),
11998
11998
  removeAgentSettings: (input) => dispatch("pipelineOrchestrator", "removeAgentSettings", "mutation", input),
11999
11999
  setAgentMaxCameras: (input) => dispatch("pipelineOrchestrator", "setAgentMaxCameras", "mutation", input),
12000
+ setAgentDetectWeight: (input) => dispatch("pipelineOrchestrator", "setAgentDetectWeight", "mutation", input),
12001
+ setAgentCapabilities: (input) => dispatch("pipelineOrchestrator", "setAgentCapabilities", "mutation", input),
12000
12002
  getCameraStatuses: (input) => dispatch("pipelineOrchestrator", "getCameraStatuses", "query", input),
12001
12003
  listTemplates: (input) => dispatch("pipelineOrchestrator", "listTemplates", "query", input),
12002
12004
  saveTemplate: (input) => dispatch("pipelineOrchestrator", "saveTemplate", "mutation", input),
@@ -16919,7 +16921,17 @@ var AgentAddonConfigSchema = z.object({
16919
16921
  });
16920
16922
  var AgentPipelineSettingsSchema = z.object({
16921
16923
  addonDefaults: z.record(z.string(), AgentAddonConfigSchema).readonly(),
16922
- maxCameras: z.number().int().nonnegative().nullable().default(null)
16924
+ maxCameras: z.number().int().nonnegative().nullable().default(null),
16925
+ /** Per-node detection weight (relative share for the quota balancer). */
16926
+ detectWeight: z.number().positive().optional(),
16927
+ /** Node is eligible to run the detection pipeline (decode + inference). */
16928
+ detect: z.boolean().optional(),
16929
+ /** Node is eligible to host decoder sessions. */
16930
+ decode: z.boolean().optional(),
16931
+ /** Node is eligible to run audio-analyzer sessions. */
16932
+ audio: z.boolean().optional(),
16933
+ /** Node is eligible to be the ingest / source-owner (serve the restream). */
16934
+ ingest: z.boolean().optional()
16923
16935
  });
16924
16936
  var CameraPipelineForAgentSchema = z.object({
16925
16937
  steps: z.array(PipelineStepInputSchema).readonly(),
@@ -16994,7 +17006,10 @@ var AgentLoadSummarySchema = z.object({
16994
17006
  online: z.boolean(),
16995
17007
  load: RunnerLocalLoadSchema,
16996
17008
  /** Computed score used by the L2 capacity balancer (lower = less loaded). */
16997
- score: z.number()
17009
+ score: z.number(),
17010
+ /** This node's decode hwaccel backend (per-node `probedBestHwaccel`), or null
17011
+ * when not yet probed — for the cluster Pipeline table UI (P0.2). */
17012
+ decodeHwaccel: z.string().nullable()
16998
17013
  });
16999
17014
  /**
17000
17015
  * Aggregate metrics across the whole detection cluster. Replaces the legacy
@@ -17358,6 +17373,38 @@ var pipelineOrchestratorCapability = {
17358
17373
  kind: "mutation",
17359
17374
  auth: "admin"
17360
17375
  }),
17376
+ /**
17377
+ * Set a node's detection WEIGHT (relative share for the quota balancer).
17378
+ * `null` clears it (back to the implicit weight 1). Unpinned cameras
17379
+ * distribute proportionally to weight; `maxCameras` still clamps on top.
17380
+ */
17381
+ setAgentDetectWeight: method(z.object({
17382
+ agentNodeId: z.string(),
17383
+ detectWeight: z.number().positive().nullable()
17384
+ }), z.object({ success: z.literal(true) }), {
17385
+ kind: "mutation",
17386
+ auth: "admin"
17387
+ }),
17388
+ /**
17389
+ * Set one node's placement CAPABILITIES — the per-node record that
17390
+ * replaced the four flat orchestrator lists (`enabledNodes`,
17391
+ * `enabledDecoderNodes`, `enabledAudioNodes`, `remoteSourcingNodes`).
17392
+ * Each flag is optional in the patch: omit a flag to leave it unchanged,
17393
+ * pass `null` to reset it to the node default (`hub` → capable, every
17394
+ * other node → not). Detection/decode/audio eligibility is derived from
17395
+ * these flags across the cluster; changing them re-derives the enabled
17396
+ * sets and reconciles dispatch immediately.
17397
+ */
17398
+ setAgentCapabilities: method(z.object({
17399
+ agentNodeId: z.string(),
17400
+ detect: z.boolean().nullable().optional(),
17401
+ decode: z.boolean().nullable().optional(),
17402
+ audio: z.boolean().nullable().optional(),
17403
+ ingest: z.boolean().nullable().optional()
17404
+ }), z.object({ success: z.literal(true) }), {
17405
+ kind: "mutation",
17406
+ auth: "admin"
17407
+ }),
17361
17408
  /** Read one camera's settings. Null when never touched (inherits agent defaults fully). */
17362
17409
  getCameraSettings: method(z.object({ deviceId: z.number() }), CameraPipelineSettingsSchema.nullable()),
17363
17410
  /** Set or clear the 3-state toggle for one (camera, addonId). Pass `enabled: null` to clear and revert to agent default. */
@@ -20844,21 +20891,13 @@ var HardwareEncoderProbeSchema = z.object({
20844
20891
  available: z.boolean(),
20845
20892
  reason: z.string().optional()
20846
20893
  });
20847
- var HardwareEncodersSchema = z.object({
20894
+ z.object({
20848
20895
  encoders: z.array(HardwareEncoderProbeSchema).readonly(),
20849
20896
  defaultH264: HardwareEncoderIdSchema,
20850
20897
  defaultH265: HardwareEncoderIdSchema,
20851
20898
  probedAt: z.number()
20852
20899
  });
20853
- /**
20854
- * Decode-side companion to {@link HardwareEncodersSchema}: the `-hwaccel`
20855
- * methods the configured ffmpeg binary actually supports (parsed from
20856
- * `ffmpeg -hwaccels`). Used to gate decode-hwaccel attempts so the transcode
20857
- * egress never spends a spawn on a backend this build cannot offer. Per-stream
20858
- * decode fragility (e.g. VideoToolbox HEVC) is still handled by the egress's
20859
- * software fallback — this only filters out wholly-unsupported backends.
20860
- */
20861
- var HardwareDecodeAccelsSchema = z.object({
20900
+ z.object({
20862
20901
  methods: z.array(z.string()).readonly(),
20863
20902
  probedAt: z.number()
20864
20903
  });
@@ -20929,24 +20968,7 @@ var platformProbeCapability = {
20929
20968
  getCapabilities: method(z.void(), PlatformCapabilitiesSchema),
20930
20969
  getHardware: method(z.void(), HardwareInfoSchema),
20931
20970
  resolveInferenceConfig: method(z.object({ requirements: z.array(ModelRequirementSchema).readonly() }), ResolvedInferenceConfigSchema),
20932
- resolveHwAccel: method(z.object({ prefer: HwAccelBackendInputSchema }), HwAccelResolutionSchema),
20933
- /**
20934
- * Hardware-encoder probe — see Task #185. Cached after first call.
20935
- */
20936
- getHardwareEncoders: method(z.void(), HardwareEncodersSchema),
20937
- refreshHardwareEncoders: method(z.void(), HardwareEncodersSchema, {
20938
- kind: "mutation",
20939
- auth: "admin"
20940
- }),
20941
- /**
20942
- * Decode-side hw-accel probe — the `-hwaccel` methods the configured ffmpeg
20943
- * binary supports (via `ffmpeg -hwaccels`). Cached after first call.
20944
- */
20945
- getHardwareDecodeAccels: method(z.void(), HardwareDecodeAccelsSchema),
20946
- refreshHardwareDecodeAccels: method(z.void(), HardwareDecodeAccelsSchema, {
20947
- kind: "mutation",
20948
- auth: "admin"
20949
- })
20971
+ resolveHwAccel: method(z.object({ prefer: HwAccelBackendInputSchema }), HwAccelResolutionSchema)
20950
20972
  }
20951
20973
  };
20952
20974
  //#endregion
@@ -25810,6 +25832,18 @@ var METHOD_ACCESS_MAP = Object.freeze({
25810
25832
  addonId: null,
25811
25833
  access: "create"
25812
25834
  },
25835
+ "pipelineOrchestrator.setAgentCapabilities": {
25836
+ capName: "pipeline-orchestrator",
25837
+ capScope: "system",
25838
+ addonId: null,
25839
+ access: "create"
25840
+ },
25841
+ "pipelineOrchestrator.setAgentDetectWeight": {
25842
+ capName: "pipeline-orchestrator",
25843
+ capScope: "system",
25844
+ addonId: null,
25845
+ access: "create"
25846
+ },
25813
25847
  "pipelineOrchestrator.setAgentMaxCameras": {
25814
25848
  capName: "pipeline-orchestrator",
25815
25849
  capScope: "system",
@@ -25966,30 +26000,6 @@ var METHOD_ACCESS_MAP = Object.freeze({
25966
26000
  addonId: null,
25967
26001
  access: "view"
25968
26002
  },
25969
- "platformProbe.getHardwareDecodeAccels": {
25970
- capName: "platform-probe",
25971
- capScope: "system",
25972
- addonId: null,
25973
- access: "view"
25974
- },
25975
- "platformProbe.getHardwareEncoders": {
25976
- capName: "platform-probe",
25977
- capScope: "system",
25978
- addonId: null,
25979
- access: "view"
25980
- },
25981
- "platformProbe.refreshHardwareDecodeAccels": {
25982
- capName: "platform-probe",
25983
- capScope: "system",
25984
- addonId: null,
25985
- access: "create"
25986
- },
25987
- "platformProbe.refreshHardwareEncoders": {
25988
- capName: "platform-probe",
25989
- capScope: "system",
25990
- addonId: null,
25991
- access: "create"
25992
- },
25993
26003
  "platformProbe.resolveHwAccel": {
25994
26004
  capName: "platform-probe",
25995
26005
  capScope: "system",
@@ -266,6 +266,15 @@ export interface IKernelServices {
266
266
  * (`resolveHwAccel`), not a bespoke Moleculer service.
267
267
  */
268
268
  readonly hwaccel?: IKernelHwAccel;
269
+ /**
270
+ * Kernel inference-runtime hardware resolver — the node's own inference
271
+ * accelerators (gpu/npu), probed LOCALLY in-process. The exact model as
272
+ * {@link hwaccel}: every node (hub + forked worker + remote agent) resolves
273
+ * its OWN hardware, NEVER the `platform-probe` singleton (which returns the
274
+ * hub's hardware to a forked agent child). The detection-pipeline maps this to
275
+ * a runtime via the shared `scoreRuntimes` / `pickBestRuntime`.
276
+ */
277
+ readonly inferenceEngine?: IKernelInferenceEngine;
269
278
  /**
270
279
  * Capability registry — provider lookups, native-cap resolution, wrapper listing.
271
280
  * Hub-only: forked workers don't see this (they use `ctx.api` for cross-process calls).
@@ -343,6 +352,24 @@ export interface IKernelHwAccel {
343
352
  */
344
353
  readonly resolve: (prefer?: HwAccelBackend | 'none' | null) => Promise<HwAccelResolution>;
345
354
  }
355
+ /** THIS node's inference accelerators (gpu/npu), probed locally in-process. */
356
+ export interface KernelInferenceHardware {
357
+ readonly gpu: {
358
+ readonly type: string;
359
+ } | null;
360
+ readonly npu: {
361
+ readonly type: string;
362
+ } | null;
363
+ }
364
+ export interface IKernelInferenceEngine {
365
+ /**
366
+ * Probe the LOCAL host and return its inference accelerators. Deterministic
367
+ * per host — safe to call repeatedly. The exact model as {@link IKernelHwAccel}:
368
+ * every node resolves its OWN hardware, so the detection-pipeline never adopts
369
+ * a remote node's runtime from the `platform-probe` singleton.
370
+ */
371
+ readonly resolveHardware: () => Promise<KernelInferenceHardware>;
372
+ }
346
373
  export interface IKernelStreamProbe {
347
374
  /**
348
375
  * Run ffprobe on an RTSP / HTTP stream URL and return the raw
@@ -54,6 +54,15 @@ export interface AgentLoadSummary {
54
54
  readonly load: RunnerLocalLoad;
55
55
  /** Computed score used by the L2 capacity balancer (lower = less loaded). */
56
56
  readonly score: number;
57
+ /**
58
+ * The decode hwaccel backend this node runs on (e.g. `videotoolbox`, `vaapi`,
59
+ * `qsv`), from the decoder addon's per-node `probedBestHwaccel@<node>` setting,
60
+ * or `null` when not yet probed. For the cluster Pipeline table UI's per-node
61
+ * capability column (P0.2). Sourced from a node-LOCAL probe published to a
62
+ * per-node setting — never the `platform-probe` singleton (whose per-node pin
63
+ * answers with the hub's hardware).
64
+ */
65
+ readonly decodeHwaccel: string | null;
57
66
  }
58
67
  /**
59
68
  * Aggregate metrics across the whole detection cluster.
package/dist/node.js CHANGED
@@ -31,6 +31,24 @@ let node_stream = require("node:stream");
31
31
  let node_child_process = require("node:child_process");
32
32
  let node_crypto = require("node:crypto");
33
33
  //#region src/deps/binary-downloader.ts
34
+ /**
35
+ * Recursively find the first file named exactly `name` under `dir`. Used as the
36
+ * archive-extraction fallback when the declared inner path misses (the archive's
37
+ * top-level dir carries an unpredictable version/arch, e.g. johnvansickle's
38
+ * `ffmpeg-<version>-<arch>-static/`). Returns the absolute path, or `null`.
39
+ */
40
+ function findFileByName(dir, name) {
41
+ try {
42
+ for (const entry of (0, node_fs.readdirSync)(dir, { withFileTypes: true })) {
43
+ const full = (0, node_path.join)(dir, entry.name);
44
+ if (entry.isDirectory()) {
45
+ const nested = findFileByName(full, name);
46
+ if (nested !== null) return nested;
47
+ } else if (entry.isFile() && entry.name === name) return full;
48
+ }
49
+ } catch {}
50
+ return null;
51
+ }
34
52
  function getPlatformInfo() {
35
53
  return {
36
54
  platform: process.platform,
@@ -109,8 +127,12 @@ async function downloadBinary(opts) {
109
127
  ], { stdio: "pipe" });
110
128
  if (archiveInnerPath) {
111
129
  const { copyFileSync } = await import("node:fs");
112
- const sourcePath = (0, node_path.join)(tmpExtractDir, archiveInnerPath);
113
- if (!(0, node_fs.existsSync)(sourcePath)) throw new Error(`Binary not found in archive at ${archiveInnerPath}`);
130
+ let sourcePath = (0, node_path.join)(tmpExtractDir, archiveInnerPath);
131
+ if (!(0, node_fs.existsSync)(sourcePath)) {
132
+ const found = findFileByName(tmpExtractDir, name);
133
+ if (found === null) throw new Error(`Binary "${name}" not found in archive (tried inner path "${archiveInnerPath}" and a recursive search)`);
134
+ sourcePath = found;
135
+ }
114
136
  copyFileSync(sourcePath, targetPath);
115
137
  }
116
138
  (0, node_fs.unlinkSync)(tmpArchive);
package/dist/node.mjs CHANGED
@@ -8,6 +8,24 @@ import { Readable } from "node:stream";
8
8
  import { execFileSync } from "node:child_process";
9
9
  import { createHash } from "node:crypto";
10
10
  //#region src/deps/binary-downloader.ts
11
+ /**
12
+ * Recursively find the first file named exactly `name` under `dir`. Used as the
13
+ * archive-extraction fallback when the declared inner path misses (the archive's
14
+ * top-level dir carries an unpredictable version/arch, e.g. johnvansickle's
15
+ * `ffmpeg-<version>-<arch>-static/`). Returns the absolute path, or `null`.
16
+ */
17
+ function findFileByName(dir, name) {
18
+ try {
19
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
20
+ const full = join(dir, entry.name);
21
+ if (entry.isDirectory()) {
22
+ const nested = findFileByName(full, name);
23
+ if (nested !== null) return nested;
24
+ } else if (entry.isFile() && entry.name === name) return full;
25
+ }
26
+ } catch {}
27
+ return null;
28
+ }
11
29
  function getPlatformInfo() {
12
30
  return {
13
31
  platform: process.platform,
@@ -86,8 +104,12 @@ async function downloadBinary(opts) {
86
104
  ], { stdio: "pipe" });
87
105
  if (archiveInnerPath) {
88
106
  const { copyFileSync } = await import("node:fs");
89
- const sourcePath = join(tmpExtractDir, archiveInnerPath);
90
- if (!existsSync(sourcePath)) throw new Error(`Binary not found in archive at ${archiveInnerPath}`);
107
+ let sourcePath = join(tmpExtractDir, archiveInnerPath);
108
+ if (!existsSync(sourcePath)) {
109
+ const found = findFileByName(tmpExtractDir, name);
110
+ if (found === null) throw new Error(`Binary "${name}" not found in archive (tried inner path "${archiveInnerPath}" and a recursive search)`);
111
+ sourcePath = found;
112
+ }
91
113
  copyFileSync(sourcePath, targetPath);
92
114
  }
93
115
  unlinkSync(tmpArchive);
@@ -61,6 +61,26 @@ export interface AgentPipelineSettings {
61
61
  * without migration.
62
62
  */
63
63
  readonly maxCameras?: number | null;
64
+ /**
65
+ * Optional per-node detection WEIGHT (relative share). Unpinned cameras
66
+ * distribute proportionally to weight across detect-capable nodes (e.g.
67
+ * Mac 60 / hub 30 / N100 10). `undefined`/absent = weight 1 (equal share,
68
+ * back-compat: identical to score-only balancing). Ceiling (`maxCameras`)
69
+ * still clamps on top of the weight share.
70
+ */
71
+ readonly detectWeight?: number;
72
+ /**
73
+ * Placement capabilities (placement-model-design §6) — the per-node record
74
+ * that replaced the four flat orchestrator lists (`enabledNodes`,
75
+ * `enabledDecoderNodes`, `enabledAudioNodes`, `remoteSourcingNodes`). Each
76
+ * flag is optional; an absent flag inherits the node default (`hub` →
77
+ * capable, every other node → not). The orchestrator derives its
78
+ * enabled-node sets from these flags across the cluster.
79
+ */
80
+ readonly detect?: boolean;
81
+ readonly decode?: boolean;
82
+ readonly audio?: boolean;
83
+ readonly ingest?: boolean;
64
84
  }
65
85
  /**
66
86
  * Per-camera pipeline settings. Sparse by design — a camera that has
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/types",
3
- "version": "1.1.23",
3
+ "version": "1.1.25",
4
4
  "description": "Shared types, interfaces, and model catalogs for the CamStack detection ecosystem",
5
5
  "keywords": [
6
6
  "camstack",