@camstack/addon-decoder-ffmpeg 1.2.25 → 1.2.26
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/index.js +252 -119
- package/dist/index.mjs +252 -119
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8021,6 +8021,15 @@ var LabelDefinitionSchema = object({
|
|
|
8021
8021
|
description: string().optional(),
|
|
8022
8022
|
icon: string().optional()
|
|
8023
8023
|
});
|
|
8024
|
+
var ClassMapDefinitionSchema = object({
|
|
8025
|
+
mapping: record(string(), _enum([
|
|
8026
|
+
"person",
|
|
8027
|
+
"vehicle",
|
|
8028
|
+
"animal",
|
|
8029
|
+
"package"
|
|
8030
|
+
])),
|
|
8031
|
+
preserveOriginal: boolean()
|
|
8032
|
+
});
|
|
8024
8033
|
var MODEL_FORMATS = [
|
|
8025
8034
|
"onnx",
|
|
8026
8035
|
"coreml",
|
|
@@ -8199,7 +8208,13 @@ var ModelCatalogEntrySchema = object({
|
|
|
8199
8208
|
* `id` stays the source of truth for resolution/download/persistence; grouping
|
|
8200
8209
|
* is a presentation overlay resolved back to an `id`.
|
|
8201
8210
|
*/
|
|
8202
|
-
group: ModelVariantGroupSchema.optional()
|
|
8211
|
+
group: ModelVariantGroupSchema.optional(),
|
|
8212
|
+
/**
|
|
8213
|
+
* Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
|
|
8214
|
+
* applies (Frigate / COCO public catalog). Set on a custom model whose raw
|
|
8215
|
+
* labels already ARE the CamStack macros (Scrypted identity map).
|
|
8216
|
+
*/
|
|
8217
|
+
classMap: ClassMapDefinitionSchema.optional()
|
|
8203
8218
|
});
|
|
8204
8219
|
var ConvertTargetSchema = discriminatedUnion("format", [object({
|
|
8205
8220
|
format: literal("openvino"),
|
|
@@ -8228,7 +8243,8 @@ var ModelConvertMetadataSchema = object({
|
|
|
8228
8243
|
"ocr",
|
|
8229
8244
|
"segmentation"
|
|
8230
8245
|
]),
|
|
8231
|
-
faceAlignment: boolean().optional()
|
|
8246
|
+
faceAlignment: boolean().optional(),
|
|
8247
|
+
classMap: ClassMapDefinitionSchema.optional()
|
|
8232
8248
|
});
|
|
8233
8249
|
var ConvertResultSchema = object({
|
|
8234
8250
|
entry: ModelCatalogEntrySchema,
|
|
@@ -17349,6 +17365,33 @@ var NativeCropRefSchema = object({
|
|
|
17349
17365
|
h: number()
|
|
17350
17366
|
})
|
|
17351
17367
|
});
|
|
17368
|
+
object({
|
|
17369
|
+
crop: object({
|
|
17370
|
+
left: number(),
|
|
17371
|
+
top: number(),
|
|
17372
|
+
width: number().positive(),
|
|
17373
|
+
height: number().positive()
|
|
17374
|
+
}).optional(),
|
|
17375
|
+
content: object({
|
|
17376
|
+
width: number().int().positive(),
|
|
17377
|
+
height: number().int().positive()
|
|
17378
|
+
}),
|
|
17379
|
+
fit: _enum(["stretch", "contain"]),
|
|
17380
|
+
format: _enum([
|
|
17381
|
+
"rgb",
|
|
17382
|
+
"gray",
|
|
17383
|
+
"jpeg"
|
|
17384
|
+
])
|
|
17385
|
+
});
|
|
17386
|
+
var FrameRefSchema = object({
|
|
17387
|
+
registryId: string().min(1),
|
|
17388
|
+
id: string().min(1),
|
|
17389
|
+
width: number().int().positive(),
|
|
17390
|
+
height: number().int().positive(),
|
|
17391
|
+
format: _enum(["rgb", "gray"]),
|
|
17392
|
+
timestamp: number(),
|
|
17393
|
+
capturedAt: number().optional()
|
|
17394
|
+
});
|
|
17352
17395
|
var ModelFormatSchema$1 = _enum([
|
|
17353
17396
|
"onnx",
|
|
17354
17397
|
"coreml",
|
|
@@ -17593,6 +17636,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
|
|
|
17593
17636
|
steps: array(PipelineStepInputSchema).min(1),
|
|
17594
17637
|
frame: FrameInputSchema.optional(),
|
|
17595
17638
|
/**
|
|
17639
|
+
* Process-local lazy frame. Valid only when caller and provider resolve
|
|
17640
|
+
* in the same execution-group process; split/cross-node callers use
|
|
17641
|
+
* `frame`/`image` inline compatibility instead.
|
|
17642
|
+
*/
|
|
17643
|
+
frameRef: FrameRefSchema.optional(),
|
|
17644
|
+
/**
|
|
17596
17645
|
* CB5 shm passthrough — a `FrameHandle` naming the same ring slot
|
|
17597
17646
|
* the decoded pixels live in. One more member of the one-of
|
|
17598
17647
|
* frame/frameHandle/image/imageBase64/referenceImage group.
|
|
@@ -17848,7 +17897,10 @@ var NativeCropResultSchema = object({
|
|
|
17848
17897
|
* Which source served this crop, so a quality-sensitive consumer (the native
|
|
17849
17898
|
* `keyFrame`) can reject a degraded fallback:
|
|
17850
17899
|
* - `native` — cut from the decode worker's retained NATIVE surface (the
|
|
17851
|
-
* quality path).
|
|
17900
|
+
* quality path). A subject-tile serve is also native-resolution and stays
|
|
17901
|
+
* `native` here: the public enum cannot name `tile` without a breaking cap
|
|
17902
|
+
* change. Runner telemetry distinguishes lease vs tile via `source` on the
|
|
17903
|
+
* internal crop result (`nativeHits` vs `tileHits`).
|
|
17852
17904
|
* - `ram-fullframe` — the native surface MISSED but the request was full-frame,
|
|
17853
17905
|
* so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
|
|
17854
17906
|
* the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
|
|
@@ -18339,12 +18391,41 @@ var RunnerLocalLoadSchema = object({
|
|
|
18339
18391
|
* legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
|
|
18340
18392
|
* working unchanged when they switch to reading from the runner cap.
|
|
18341
18393
|
*/
|
|
18394
|
+
var FrameLazyCountersSchema = object({
|
|
18395
|
+
framesDecoded: number(),
|
|
18396
|
+
framesAdmitted: number(),
|
|
18397
|
+
framesDroppedPixelFree: number(),
|
|
18398
|
+
viewsMaterialized: number(),
|
|
18399
|
+
viewsSkipped: number(),
|
|
18400
|
+
workerToRunnerBytes: number(),
|
|
18401
|
+
runnerToPoolRawBytes: number(),
|
|
18402
|
+
runnerToPoolJpegBytes: number(),
|
|
18403
|
+
onDemandFullFrameRequests: number(),
|
|
18404
|
+
onDemandCropRequests: number(),
|
|
18405
|
+
nativeHits: number(),
|
|
18406
|
+
nativeMisses: number(),
|
|
18407
|
+
tileHits: number(),
|
|
18408
|
+
tileMisses: number(),
|
|
18409
|
+
fallbackHits: number(),
|
|
18410
|
+
fallbackMisses: number(),
|
|
18411
|
+
retainedWritesAvoided: number(),
|
|
18412
|
+
residentRefs: number(),
|
|
18413
|
+
residentBytes: number(),
|
|
18414
|
+
releases: number(),
|
|
18415
|
+
evictions: number(),
|
|
18416
|
+
staleMisses: number()
|
|
18417
|
+
});
|
|
18418
|
+
var FrameLazyMetricsSchema = object({
|
|
18419
|
+
node: FrameLazyCountersSchema,
|
|
18420
|
+
cameras: array(FrameLazyCountersSchema.extend({ deviceId: number() }))
|
|
18421
|
+
});
|
|
18342
18422
|
var RunnerLocalMetricsSchema = object({
|
|
18343
18423
|
nodeId: string(),
|
|
18344
18424
|
activeCameras: number(),
|
|
18345
18425
|
throttledCameras: number(),
|
|
18346
18426
|
avgInferenceTimeMs: number(),
|
|
18347
|
-
queueDepth: number()
|
|
18427
|
+
queueDepth: number(),
|
|
18428
|
+
frameLazy: FrameLazyMetricsSchema.optional()
|
|
18348
18429
|
});
|
|
18349
18430
|
method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mutation" }), method(object({ deviceId: number() }), object({ success: literal(true) }), { kind: "mutation" }), method(ReportMotionInputSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), RunnerLocalLoadSchema), method(_void(), RunnerLocalMetricsSchema), method(object({ deviceId: number() }), CameraMetricsSchema.nullable()), method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()), method(_void(), array(number()).readonly()), method(object({
|
|
18350
18431
|
handle: FrameHandleSchema,
|
|
@@ -19644,6 +19725,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
|
|
|
19644
19725
|
location: StorageLocationSchema,
|
|
19645
19726
|
relativePath: string()
|
|
19646
19727
|
}), _void(), { kind: "mutation" }), method(object({ location: StorageLocationSchema }), number().nullable()), method(BeginUploadInputSchema, BeginUploadResultSchema, { kind: "mutation" }), method(WriteChunkInputSchema, _void(), { kind: "mutation" }), method(FinalizeUploadInputSchema, _void(), { kind: "mutation" }), method(AbortUploadInputSchema, _void(), { kind: "mutation" }), method(BeginDownloadInputSchema, BeginDownloadResultSchema, { kind: "mutation" }), method(ReadChunkInputSchema, _instanceof(Uint8Array)), method(EndDownloadInputSchema, _void(), { kind: "mutation" });
|
|
19728
|
+
/** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
|
|
19729
|
+
var ProfileSettingsSchemaBridge = unknown().nullable();
|
|
19730
|
+
var ProfileSettingsBagSchema = record(string(), unknown());
|
|
19647
19731
|
/**
|
|
19648
19732
|
* A live terminal session hosted by the provider addon. Output and input do
|
|
19649
19733
|
* NOT flow through the capability — they use the addon data plane
|
|
@@ -19673,7 +19757,14 @@ var TerminalSessionInfoSchema = object({
|
|
|
19673
19757
|
var TerminalProfileInfoSchema = object({
|
|
19674
19758
|
profileId: string(),
|
|
19675
19759
|
label: string(),
|
|
19676
|
-
description: string().optional()
|
|
19760
|
+
description: string().optional(),
|
|
19761
|
+
/** Spawn defaults the instance form copies on create. */
|
|
19762
|
+
executable: string().optional(),
|
|
19763
|
+
args: array(string()).readonly().optional(),
|
|
19764
|
+
cwd: string().optional(),
|
|
19765
|
+
environment: array(string()).readonly().optional(),
|
|
19766
|
+
/** ConfigUISchema for instance knobs, or null when the profile has none. */
|
|
19767
|
+
settingsSchema: ProfileSettingsSchemaBridge.optional()
|
|
19677
19768
|
});
|
|
19678
19769
|
/**
|
|
19679
19770
|
* A durable operator-created Terminal instance. Profiles are templates; only
|
|
@@ -19686,7 +19777,12 @@ var TerminalInstanceInfoSchema = object({
|
|
|
19686
19777
|
profileId: string(),
|
|
19687
19778
|
profileLabel: string(),
|
|
19688
19779
|
name: string(),
|
|
19689
|
-
enabled: boolean()
|
|
19780
|
+
enabled: boolean(),
|
|
19781
|
+
executable: string(),
|
|
19782
|
+
args: array(string()).readonly(),
|
|
19783
|
+
cwd: string(),
|
|
19784
|
+
environment: array(string()).readonly(),
|
|
19785
|
+
profileSettings: ProfileSettingsBagSchema
|
|
19690
19786
|
});
|
|
19691
19787
|
var TerminalLegacyCameraSchema = object({
|
|
19692
19788
|
stableId: string(),
|
|
@@ -19716,7 +19812,23 @@ var TerminalOutputBatchSchema = object({
|
|
|
19716
19812
|
method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
|
|
19717
19813
|
targetNodeId: string().min(1),
|
|
19718
19814
|
profileId: string().min(1),
|
|
19719
|
-
name: string().trim().min(1).max(160).optional()
|
|
19815
|
+
name: string().trim().min(1).max(160).optional(),
|
|
19816
|
+
executable: string().max(1024).optional(),
|
|
19817
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
19818
|
+
cwd: string().max(1024).optional(),
|
|
19819
|
+
environment: array(string().max(4096)).max(64).optional(),
|
|
19820
|
+
profileSettings: ProfileSettingsBagSchema.optional()
|
|
19821
|
+
}), TerminalInstanceInfoSchema, {
|
|
19822
|
+
kind: "mutation",
|
|
19823
|
+
auth: "admin"
|
|
19824
|
+
}), method(object({
|
|
19825
|
+
instanceId: string().min(1),
|
|
19826
|
+
name: string().trim().min(1).max(160).optional(),
|
|
19827
|
+
executable: string().max(1024).optional(),
|
|
19828
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
19829
|
+
cwd: string().max(1024).optional(),
|
|
19830
|
+
environment: array(string().max(4096)).max(64).optional(),
|
|
19831
|
+
profileSettings: ProfileSettingsBagSchema.optional()
|
|
19720
19832
|
}), TerminalInstanceInfoSchema, {
|
|
19721
19833
|
kind: "mutation",
|
|
19722
19834
|
auth: "admin"
|
|
@@ -19738,7 +19850,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
|
|
|
19738
19850
|
}), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
|
|
19739
19851
|
profileId: string(),
|
|
19740
19852
|
cols: number().int().positive(),
|
|
19741
|
-
rows: number().int().positive()
|
|
19853
|
+
rows: number().int().positive(),
|
|
19854
|
+
executable: string().max(1024).optional(),
|
|
19855
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
19856
|
+
cwd: string().max(1024).optional(),
|
|
19857
|
+
environment: array(string().max(4096)).max(64).optional()
|
|
19742
19858
|
}), TerminalSessionInfoSchema, {
|
|
19743
19859
|
kind: "mutation",
|
|
19744
19860
|
auth: "admin"
|
|
@@ -22520,10 +22636,10 @@ DeviceType.LawnMower, method(object({ deviceId: number().int().nonnegative() }),
|
|
|
22520
22636
|
*
|
|
22521
22637
|
* • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
|
|
22522
22638
|
* to receive an ordered list of candidate base URLs it should race
|
|
22523
|
-
* on connect — LAN IPv4 first (lowest latency
|
|
22524
|
-
* then public hostname (if a tunnel is
|
|
22525
|
-
* race them with short timeouts and stick with the
|
|
22526
|
-
* session.
|
|
22639
|
+
* on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
|
|
22640
|
+
* when on the same network), then public hostname (if a tunnel is
|
|
22641
|
+
* up). The SDK can race them with short timeouts and stick with the
|
|
22642
|
+
* winner for the session.
|
|
22527
22643
|
*
|
|
22528
22644
|
* Why hub-only: agents are not directly addressable by the operator's
|
|
22529
22645
|
* clients — they reverse-connect to the hub. Exposing their interfaces
|
|
@@ -22678,6 +22794,17 @@ var NotificationEndpointSchema = object({
|
|
|
22678
22794
|
/** What the ranking currently resolves to (null when nothing is reachable). */
|
|
22679
22795
|
resolved: string().nullable()
|
|
22680
22796
|
});
|
|
22797
|
+
/**
|
|
22798
|
+
* The URLs the SDK / viewer should race for API access. `baseUrls` empty =
|
|
22799
|
+
* AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
|
|
22800
|
+
* currently expands to, so the UI can show the effective set either way.
|
|
22801
|
+
*/
|
|
22802
|
+
var ViewerEndpointsSchema = object({
|
|
22803
|
+
/** The operator's explicit race set, or empty for AUTO. */
|
|
22804
|
+
baseUrls: array(string()).readonly(),
|
|
22805
|
+
/** What the ranking currently resolves to (may be empty if nothing is up). */
|
|
22806
|
+
resolved: array(string()).readonly()
|
|
22807
|
+
});
|
|
22681
22808
|
var AllowedAddressesSchema = object({
|
|
22682
22809
|
/**
|
|
22683
22810
|
* Allowlist of interface addresses operators have explicitly opted
|
|
@@ -22686,6 +22813,20 @@ var AllowedAddressesSchema = object({
|
|
|
22686
22813
|
* Network Addresses admin page and persisted by the addon.
|
|
22687
22814
|
*/
|
|
22688
22815
|
addresses: array(string()).readonly() });
|
|
22816
|
+
var TlsStatusSchema = object({
|
|
22817
|
+
mode: _enum([
|
|
22818
|
+
"generated",
|
|
22819
|
+
"uploaded",
|
|
22820
|
+
"disabled"
|
|
22821
|
+
]),
|
|
22822
|
+
leafFingerprintSha256: string().nullable(),
|
|
22823
|
+
caFingerprintSha256: string().nullable(),
|
|
22824
|
+
validTo: string().nullable(),
|
|
22825
|
+
sans: array(string()),
|
|
22826
|
+
caCertPem: string().nullable(),
|
|
22827
|
+
reissueError: string().nullable(),
|
|
22828
|
+
restartRequired: boolean()
|
|
22829
|
+
});
|
|
22689
22830
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
22690
22831
|
/**
|
|
22691
22832
|
* LEGACY HINT — do not send from new code. Kept optional so clients
|
|
@@ -22695,17 +22836,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
22695
22836
|
*/
|
|
22696
22837
|
port: number().int().min(1).max(65535).optional(),
|
|
22697
22838
|
/** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
|
|
22698
|
-
* candidate. Default `
|
|
22839
|
+
* candidate. Default `false` — loopback is not a client route. */
|
|
22699
22840
|
includeLoopback: boolean().optional(),
|
|
22700
|
-
/** Skip IPv6 entries.
|
|
22701
|
-
*
|
|
22841
|
+
/** Skip IPv6 entries. Default `false` — the palette includes stable
|
|
22842
|
+
* LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
|
|
22843
|
+
* hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
|
|
22702
22844
|
ipv4Only: boolean().optional(),
|
|
22703
22845
|
/** Scheme to emit for LAN/loopback URLs. Default `'http'`.
|
|
22704
22846
|
* Pass `'https'` when the caller is itself loaded over HTTPS
|
|
22705
22847
|
* to avoid mixed-content blocks in the browser. The public
|
|
22706
22848
|
* tunnel always emits `https://` regardless. */
|
|
22707
22849
|
scheme: _enum(["http", "https"]).optional()
|
|
22708
|
-
}), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" })
|
|
22850
|
+
}), 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, {
|
|
22851
|
+
kind: "mutation",
|
|
22852
|
+
auth: "admin"
|
|
22853
|
+
}), method(object({
|
|
22854
|
+
certPem: string().min(1),
|
|
22855
|
+
keyPem: string().min(1),
|
|
22856
|
+
caPem: string().optional()
|
|
22857
|
+
}), TlsStatusSchema, {
|
|
22858
|
+
kind: "mutation",
|
|
22859
|
+
auth: "admin"
|
|
22860
|
+
}), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
|
|
22861
|
+
kind: "mutation",
|
|
22862
|
+
auth: "admin"
|
|
22863
|
+
});
|
|
22709
22864
|
object({
|
|
22710
22865
|
/** Lifecycle state of the lock. `jammed` means the motor reported
|
|
22711
22866
|
* failure to reach the target — operator intervention required. */
|
|
@@ -28468,6 +28623,12 @@ Object.freeze({
|
|
|
28468
28623
|
addonId: null,
|
|
28469
28624
|
access: "create"
|
|
28470
28625
|
},
|
|
28626
|
+
"localNetwork.downloadCa": {
|
|
28627
|
+
capName: "local-network",
|
|
28628
|
+
capScope: "system",
|
|
28629
|
+
addonId: null,
|
|
28630
|
+
access: "view"
|
|
28631
|
+
},
|
|
28471
28632
|
"localNetwork.getAllowedAddresses": {
|
|
28472
28633
|
capName: "local-network",
|
|
28473
28634
|
capScope: "system",
|
|
@@ -28492,18 +28653,42 @@ Object.freeze({
|
|
|
28492
28653
|
addonId: null,
|
|
28493
28654
|
access: "view"
|
|
28494
28655
|
},
|
|
28656
|
+
"localNetwork.getTlsStatus": {
|
|
28657
|
+
capName: "local-network",
|
|
28658
|
+
capScope: "system",
|
|
28659
|
+
addonId: null,
|
|
28660
|
+
access: "view"
|
|
28661
|
+
},
|
|
28662
|
+
"localNetwork.getViewerEndpoints": {
|
|
28663
|
+
capName: "local-network",
|
|
28664
|
+
capScope: "system",
|
|
28665
|
+
addonId: null,
|
|
28666
|
+
access: "view"
|
|
28667
|
+
},
|
|
28495
28668
|
"localNetwork.list": {
|
|
28496
28669
|
capName: "local-network",
|
|
28497
28670
|
capScope: "system",
|
|
28498
28671
|
addonId: null,
|
|
28499
28672
|
access: "view"
|
|
28500
28673
|
},
|
|
28674
|
+
"localNetwork.regenerateCertificate": {
|
|
28675
|
+
capName: "local-network",
|
|
28676
|
+
capScope: "system",
|
|
28677
|
+
addonId: null,
|
|
28678
|
+
access: "create"
|
|
28679
|
+
},
|
|
28501
28680
|
"localNetwork.resetAllowlistToBestMatch": {
|
|
28502
28681
|
capName: "local-network",
|
|
28503
28682
|
capScope: "system",
|
|
28504
28683
|
addonId: null,
|
|
28505
28684
|
access: "delete"
|
|
28506
28685
|
},
|
|
28686
|
+
"localNetwork.revertToGeneratedCertificate": {
|
|
28687
|
+
capName: "local-network",
|
|
28688
|
+
capScope: "system",
|
|
28689
|
+
addonId: null,
|
|
28690
|
+
access: "create"
|
|
28691
|
+
},
|
|
28507
28692
|
"localNetwork.setAllowedAddresses": {
|
|
28508
28693
|
capName: "local-network",
|
|
28509
28694
|
capScope: "system",
|
|
@@ -28516,6 +28701,18 @@ Object.freeze({
|
|
|
28516
28701
|
addonId: null,
|
|
28517
28702
|
access: "create"
|
|
28518
28703
|
},
|
|
28704
|
+
"localNetwork.setViewerEndpoints": {
|
|
28705
|
+
capName: "local-network",
|
|
28706
|
+
capScope: "system",
|
|
28707
|
+
addonId: null,
|
|
28708
|
+
access: "create"
|
|
28709
|
+
},
|
|
28710
|
+
"localNetwork.uploadCertificate": {
|
|
28711
|
+
capName: "local-network",
|
|
28712
|
+
capScope: "system",
|
|
28713
|
+
addonId: null,
|
|
28714
|
+
access: "create"
|
|
28715
|
+
},
|
|
28519
28716
|
"lockControl.lock": {
|
|
28520
28717
|
capName: "lock-control",
|
|
28521
28718
|
capScope: "device",
|
|
@@ -31396,6 +31593,12 @@ Object.freeze({
|
|
|
31396
31593
|
addonId: null,
|
|
31397
31594
|
access: "create"
|
|
31398
31595
|
},
|
|
31596
|
+
"terminalSession.updateInstance": {
|
|
31597
|
+
capName: "terminal-session",
|
|
31598
|
+
capScope: "system",
|
|
31599
|
+
addonId: null,
|
|
31600
|
+
access: "create"
|
|
31601
|
+
},
|
|
31399
31602
|
"terminalSession.writeInput": {
|
|
31400
31603
|
capName: "terminal-session",
|
|
31401
31604
|
capScope: "system",
|
|
@@ -33883,6 +34086,35 @@ Object.freeze(Object.fromEntries([{
|
|
|
33883
34086
|
}]
|
|
33884
34087
|
}].map((s) => [s.stepId, s.defaultModelId])));
|
|
33885
34088
|
string().min(1);
|
|
34089
|
+
var CLUSTER_STEP_SETTING_FIELDS = [{
|
|
34090
|
+
stepId: "face-embedding",
|
|
34091
|
+
key: "minLandmarkFaceSize",
|
|
34092
|
+
label: "Min face size for recognition (detection px)",
|
|
34093
|
+
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.",
|
|
34094
|
+
type: "slider",
|
|
34095
|
+
min: 0,
|
|
34096
|
+
max: 64,
|
|
34097
|
+
step: 2,
|
|
34098
|
+
default: 24
|
|
34099
|
+
}];
|
|
34100
|
+
function clusterStepSettingKey(stepId, fieldKey) {
|
|
34101
|
+
return `clusterStepSetting:${stepId}:${fieldKey}`;
|
|
34102
|
+
}
|
|
34103
|
+
var ClusterSettingNumberSchema = number().finite();
|
|
34104
|
+
function readClusterStepSettings(config) {
|
|
34105
|
+
const out = {};
|
|
34106
|
+
for (const field of CLUSTER_STEP_SETTING_FIELDS) {
|
|
34107
|
+
const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
|
|
34108
|
+
const value = parsed.success ? parsed.data : field.default;
|
|
34109
|
+
const existing = out[field.stepId] ?? {};
|
|
34110
|
+
out[field.stepId] = {
|
|
34111
|
+
...existing,
|
|
34112
|
+
[field.key]: value
|
|
34113
|
+
};
|
|
34114
|
+
}
|
|
34115
|
+
return out;
|
|
34116
|
+
}
|
|
34117
|
+
readClusterStepSettings({});
|
|
33886
34118
|
object({
|
|
33887
34119
|
/**
|
|
33888
34120
|
* Fraction of the box's own size added on EACH side before cutting.
|
|
@@ -34074,113 +34306,14 @@ var RingBuffer = class {
|
|
|
34074
34306
|
* decoder is an explicit opt-in fallback only. A stale/failed settings read at
|
|
34075
34307
|
* boot therefore resolves to node-av (never leaves the node with no decoder). */
|
|
34076
34308
|
var DEFAULT_DECODER_BACKEND = "nodeav";
|
|
34077
|
-
/** Narrow an unknown settings value to a {@link DecoderBackend}, else `null`. */
|
|
34078
|
-
function parseDecoderBackend(value) {
|
|
34079
|
-
return value === "ffmpeg" || value === "nodeav" ? value : null;
|
|
34080
|
-
}
|
|
34081
|
-
/**
|
|
34082
|
-
* Normalise a raw kernel node id to the bare node id used for scoping.
|
|
34083
|
-
* `localNodeId` can carry a `<node>/<addon>` suffix; the decoder selection is
|
|
34084
|
-
* per-NODE, so strip the addon segment. Falls back to `hub`.
|
|
34085
|
-
*/
|
|
34086
|
-
function normalizeDecoderNodeId(rawNodeId) {
|
|
34087
|
-
const raw = rawNodeId ?? "hub";
|
|
34088
|
-
return raw.includes("/") ? raw.split("/")[0] ?? "hub" : raw;
|
|
34089
|
-
}
|
|
34090
34309
|
//#endregion
|
|
34091
34310
|
//#region src/shared/decoder-backend.ts
|
|
34092
|
-
/** The NEUTRAL addon (pipeline-orchestrator, hub-resident) whose global
|
|
34093
|
-
* settings OWN the per-node `backend` selector. Neither decoder addon owns
|
|
34094
|
-
* it, so decoder-nodeav / decoder-ffmpeg stay fully independent. */
|
|
34095
|
-
var DECODER_OWNER_ADDON_ID = "pipeline-orchestrator";
|
|
34096
|
-
function isHydratedField(entry) {
|
|
34097
|
-
return typeof entry === "object" && entry !== null && "key" in entry;
|
|
34098
|
-
}
|
|
34099
34311
|
/**
|
|
34100
|
-
*
|
|
34101
|
-
*
|
|
34102
|
-
*
|
|
34103
|
-
* resolves to {@link DEFAULT_DECODER_BACKEND} — never a bare store key.
|
|
34312
|
+
* Resolve the decoder backend this addon should run. Owner settings are
|
|
34313
|
+
* ignored: persisted `backend@<nodeId>` rows stay in the store and are not
|
|
34314
|
+
* consulted. The runtime default is always {@link DEFAULT_DECODER_BACKEND}.
|
|
34104
34315
|
*/
|
|
34105
|
-
function
|
|
34106
|
-
if (view === null) return DEFAULT_DECODER_BACKEND;
|
|
34107
|
-
for (const section of view.sections) for (const entry of section.fields) {
|
|
34108
|
-
if (!isHydratedField(entry) || entry.key !== "backend") continue;
|
|
34109
|
-
return parseDecoderBackend(entry.value) ?? "nodeav";
|
|
34110
|
-
}
|
|
34111
|
-
return DEFAULT_DECODER_BACKEND;
|
|
34112
|
-
}
|
|
34113
|
-
/** Missing optional owner fingerprints: ffmpeg may be uninstalled. */
|
|
34114
|
-
function isMissingOwnerSettingsError(message) {
|
|
34115
|
-
return /not routable/i.test(message) || /provider not available/i.test(message);
|
|
34116
|
-
}
|
|
34117
|
-
/** Transient transport/settings-store fingerprints worth retrying on. */
|
|
34118
|
-
function isTransientSettingsError(message) {
|
|
34119
|
-
return /not loaded/i.test(message) || /transport-failed/i.test(message) || /not connected/i.test(message) || /SqliteSettingsBackend not initialized/i.test(message);
|
|
34120
|
-
}
|
|
34121
|
-
/**
|
|
34122
|
-
* Resolve the decoder backend a NON-OWNER addon (`decoder-nodeav`) should run,
|
|
34123
|
-
* by reading the OWNER's (`decoder-ffmpeg`) hub-central per-node `backend`.
|
|
34124
|
-
*
|
|
34125
|
-
* The read routes to the owner addon's child runner; during a simultaneous
|
|
34126
|
-
* (re)start the owner may not be up yet → a transient `transport-failed (addon
|
|
34127
|
-
* not loaded)`. Immediately defaulting here would ignore an explicit `ffmpeg`
|
|
34128
|
-
* selection (the node would silently run the node-av default instead). So retry
|
|
34129
|
-
* on the transient fingerprints with a bounded budget (mirrors
|
|
34130
|
-
* `BaseAddon.readAddonStoreWithRetry`) until the owner answers; only a
|
|
34131
|
-
* persistent failure falls back to {@link DEFAULT_DECODER_BACKEND}.
|
|
34132
|
-
*
|
|
34133
|
-
* The OWNER addon must NOT call this (it would self-route + deadlock) — it uses
|
|
34134
|
-
* {@link resolveOwnDecoderBackend} against its own store instead.
|
|
34135
|
-
*/
|
|
34136
|
-
async function resolveDecoderBackend(api, nodeId, logger) {
|
|
34137
|
-
if (!api) {
|
|
34138
|
-
logger.warn("decoder-backend: no api surface — using default backend", { meta: { default: DEFAULT_DECODER_BACKEND } });
|
|
34139
|
-
return DEFAULT_DECODER_BACKEND;
|
|
34140
|
-
}
|
|
34141
|
-
const normalized = normalizeDecoderNodeId(nodeId);
|
|
34142
|
-
const delaysMs = [
|
|
34143
|
-
150,
|
|
34144
|
-
350,
|
|
34145
|
-
600,
|
|
34146
|
-
900,
|
|
34147
|
-
1200
|
|
34148
|
-
];
|
|
34149
|
-
let lastErr;
|
|
34150
|
-
for (let attempt = 0; attempt <= delaysMs.length; attempt++) {
|
|
34151
|
-
try {
|
|
34152
|
-
const view = await api.addonSettings.getGlobalSettings.query({
|
|
34153
|
-
addonId: DECODER_OWNER_ADDON_ID,
|
|
34154
|
-
nodeId: normalized
|
|
34155
|
-
});
|
|
34156
|
-
if (view !== null) return pickDecoderBackendFromSettings(view);
|
|
34157
|
-
lastErr = /* @__PURE__ */ new Error("owner settings unavailable (null)");
|
|
34158
|
-
} catch (err) {
|
|
34159
|
-
lastErr = err;
|
|
34160
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
34161
|
-
if (isTransientSettingsError(msg)) {} else if (isMissingOwnerSettingsError(msg)) {
|
|
34162
|
-
logger.warn("decoder-backend: optional owner unavailable — using default backend", { meta: {
|
|
34163
|
-
default: DEFAULT_DECODER_BACKEND,
|
|
34164
|
-
owner: DECODER_OWNER_ADDON_ID,
|
|
34165
|
-
error: msg
|
|
34166
|
-
} });
|
|
34167
|
-
return DEFAULT_DECODER_BACKEND;
|
|
34168
|
-
} else {
|
|
34169
|
-
logger.warn("decoder-backend: settings read failed — using default backend", { meta: {
|
|
34170
|
-
default: DEFAULT_DECODER_BACKEND,
|
|
34171
|
-
error: msg
|
|
34172
|
-
} });
|
|
34173
|
-
return DEFAULT_DECODER_BACKEND;
|
|
34174
|
-
}
|
|
34175
|
-
}
|
|
34176
|
-
if (attempt === delaysMs.length) break;
|
|
34177
|
-
await new Promise((resolve) => setTimeout(resolve, delaysMs[attempt]));
|
|
34178
|
-
}
|
|
34179
|
-
logger.warn("decoder-backend: owner settings unavailable after retries — using default backend", { meta: {
|
|
34180
|
-
default: DEFAULT_DECODER_BACKEND,
|
|
34181
|
-
owner: DECODER_OWNER_ADDON_ID,
|
|
34182
|
-
error: lastErr instanceof Error ? lastErr.message : String(lastErr)
|
|
34183
|
-
} });
|
|
34316
|
+
async function resolveDecoderBackend(_api, _nodeId, _logger) {
|
|
34184
34317
|
return DEFAULT_DECODER_BACKEND;
|
|
34185
34318
|
}
|
|
34186
34319
|
//#endregion
|
package/dist/index.mjs
CHANGED
|
@@ -8017,6 +8017,15 @@ var LabelDefinitionSchema = object({
|
|
|
8017
8017
|
description: string().optional(),
|
|
8018
8018
|
icon: string().optional()
|
|
8019
8019
|
});
|
|
8020
|
+
var ClassMapDefinitionSchema = object({
|
|
8021
|
+
mapping: record(string(), _enum([
|
|
8022
|
+
"person",
|
|
8023
|
+
"vehicle",
|
|
8024
|
+
"animal",
|
|
8025
|
+
"package"
|
|
8026
|
+
])),
|
|
8027
|
+
preserveOriginal: boolean()
|
|
8028
|
+
});
|
|
8020
8029
|
var MODEL_FORMATS = [
|
|
8021
8030
|
"onnx",
|
|
8022
8031
|
"coreml",
|
|
@@ -8195,7 +8204,13 @@ var ModelCatalogEntrySchema = object({
|
|
|
8195
8204
|
* `id` stays the source of truth for resolution/download/persistence; grouping
|
|
8196
8205
|
* is a presentation overlay resolved back to an `id`.
|
|
8197
8206
|
*/
|
|
8198
|
-
group: ModelVariantGroupSchema.optional()
|
|
8207
|
+
group: ModelVariantGroupSchema.optional(),
|
|
8208
|
+
/**
|
|
8209
|
+
* Per-MODEL class map override. Absent ⇒ the step's `StepDefinition.classMap`
|
|
8210
|
+
* applies (Frigate / COCO public catalog). Set on a custom model whose raw
|
|
8211
|
+
* labels already ARE the CamStack macros (Scrypted identity map).
|
|
8212
|
+
*/
|
|
8213
|
+
classMap: ClassMapDefinitionSchema.optional()
|
|
8199
8214
|
});
|
|
8200
8215
|
var ConvertTargetSchema = discriminatedUnion("format", [object({
|
|
8201
8216
|
format: literal("openvino"),
|
|
@@ -8224,7 +8239,8 @@ var ModelConvertMetadataSchema = object({
|
|
|
8224
8239
|
"ocr",
|
|
8225
8240
|
"segmentation"
|
|
8226
8241
|
]),
|
|
8227
|
-
faceAlignment: boolean().optional()
|
|
8242
|
+
faceAlignment: boolean().optional(),
|
|
8243
|
+
classMap: ClassMapDefinitionSchema.optional()
|
|
8228
8244
|
});
|
|
8229
8245
|
var ConvertResultSchema = object({
|
|
8230
8246
|
entry: ModelCatalogEntrySchema,
|
|
@@ -17345,6 +17361,33 @@ var NativeCropRefSchema = object({
|
|
|
17345
17361
|
h: number()
|
|
17346
17362
|
})
|
|
17347
17363
|
});
|
|
17364
|
+
object({
|
|
17365
|
+
crop: object({
|
|
17366
|
+
left: number(),
|
|
17367
|
+
top: number(),
|
|
17368
|
+
width: number().positive(),
|
|
17369
|
+
height: number().positive()
|
|
17370
|
+
}).optional(),
|
|
17371
|
+
content: object({
|
|
17372
|
+
width: number().int().positive(),
|
|
17373
|
+
height: number().int().positive()
|
|
17374
|
+
}),
|
|
17375
|
+
fit: _enum(["stretch", "contain"]),
|
|
17376
|
+
format: _enum([
|
|
17377
|
+
"rgb",
|
|
17378
|
+
"gray",
|
|
17379
|
+
"jpeg"
|
|
17380
|
+
])
|
|
17381
|
+
});
|
|
17382
|
+
var FrameRefSchema = object({
|
|
17383
|
+
registryId: string().min(1),
|
|
17384
|
+
id: string().min(1),
|
|
17385
|
+
width: number().int().positive(),
|
|
17386
|
+
height: number().int().positive(),
|
|
17387
|
+
format: _enum(["rgb", "gray"]),
|
|
17388
|
+
timestamp: number(),
|
|
17389
|
+
capturedAt: number().optional()
|
|
17390
|
+
});
|
|
17348
17391
|
var ModelFormatSchema$1 = _enum([
|
|
17349
17392
|
"onnx",
|
|
17350
17393
|
"coreml",
|
|
@@ -17589,6 +17632,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
|
|
|
17589
17632
|
steps: array(PipelineStepInputSchema).min(1),
|
|
17590
17633
|
frame: FrameInputSchema.optional(),
|
|
17591
17634
|
/**
|
|
17635
|
+
* Process-local lazy frame. Valid only when caller and provider resolve
|
|
17636
|
+
* in the same execution-group process; split/cross-node callers use
|
|
17637
|
+
* `frame`/`image` inline compatibility instead.
|
|
17638
|
+
*/
|
|
17639
|
+
frameRef: FrameRefSchema.optional(),
|
|
17640
|
+
/**
|
|
17592
17641
|
* CB5 shm passthrough — a `FrameHandle` naming the same ring slot
|
|
17593
17642
|
* the decoded pixels live in. One more member of the one-of
|
|
17594
17643
|
* frame/frameHandle/image/imageBase64/referenceImage group.
|
|
@@ -17844,7 +17893,10 @@ var NativeCropResultSchema = object({
|
|
|
17844
17893
|
* Which source served this crop, so a quality-sensitive consumer (the native
|
|
17845
17894
|
* `keyFrame`) can reject a degraded fallback:
|
|
17846
17895
|
* - `native` — cut from the decode worker's retained NATIVE surface (the
|
|
17847
|
-
* quality path).
|
|
17896
|
+
* quality path). A subject-tile serve is also native-resolution and stays
|
|
17897
|
+
* `native` here: the public enum cannot name `tile` without a breaking cap
|
|
17898
|
+
* change. Runner telemetry distinguishes lease vs tile via `source` on the
|
|
17899
|
+
* internal crop result (`nativeHits` vs `tileHits`).
|
|
17848
17900
|
* - `ram-fullframe` — the native surface MISSED but the request was full-frame,
|
|
17849
17901
|
* so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
|
|
17850
17902
|
* the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
|
|
@@ -18335,12 +18387,41 @@ var RunnerLocalLoadSchema = object({
|
|
|
18335
18387
|
* legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
|
|
18336
18388
|
* working unchanged when they switch to reading from the runner cap.
|
|
18337
18389
|
*/
|
|
18390
|
+
var FrameLazyCountersSchema = object({
|
|
18391
|
+
framesDecoded: number(),
|
|
18392
|
+
framesAdmitted: number(),
|
|
18393
|
+
framesDroppedPixelFree: number(),
|
|
18394
|
+
viewsMaterialized: number(),
|
|
18395
|
+
viewsSkipped: number(),
|
|
18396
|
+
workerToRunnerBytes: number(),
|
|
18397
|
+
runnerToPoolRawBytes: number(),
|
|
18398
|
+
runnerToPoolJpegBytes: number(),
|
|
18399
|
+
onDemandFullFrameRequests: number(),
|
|
18400
|
+
onDemandCropRequests: number(),
|
|
18401
|
+
nativeHits: number(),
|
|
18402
|
+
nativeMisses: number(),
|
|
18403
|
+
tileHits: number(),
|
|
18404
|
+
tileMisses: number(),
|
|
18405
|
+
fallbackHits: number(),
|
|
18406
|
+
fallbackMisses: number(),
|
|
18407
|
+
retainedWritesAvoided: number(),
|
|
18408
|
+
residentRefs: number(),
|
|
18409
|
+
residentBytes: number(),
|
|
18410
|
+
releases: number(),
|
|
18411
|
+
evictions: number(),
|
|
18412
|
+
staleMisses: number()
|
|
18413
|
+
});
|
|
18414
|
+
var FrameLazyMetricsSchema = object({
|
|
18415
|
+
node: FrameLazyCountersSchema,
|
|
18416
|
+
cameras: array(FrameLazyCountersSchema.extend({ deviceId: number() }))
|
|
18417
|
+
});
|
|
18338
18418
|
var RunnerLocalMetricsSchema = object({
|
|
18339
18419
|
nodeId: string(),
|
|
18340
18420
|
activeCameras: number(),
|
|
18341
18421
|
throttledCameras: number(),
|
|
18342
18422
|
avgInferenceTimeMs: number(),
|
|
18343
|
-
queueDepth: number()
|
|
18423
|
+
queueDepth: number(),
|
|
18424
|
+
frameLazy: FrameLazyMetricsSchema.optional()
|
|
18344
18425
|
});
|
|
18345
18426
|
method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mutation" }), method(object({ deviceId: number() }), object({ success: literal(true) }), { kind: "mutation" }), method(ReportMotionInputSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), RunnerLocalLoadSchema), method(_void(), RunnerLocalMetricsSchema), method(object({ deviceId: number() }), CameraMetricsSchema.nullable()), method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()), method(_void(), array(number()).readonly()), method(object({
|
|
18346
18427
|
handle: FrameHandleSchema,
|
|
@@ -19640,6 +19721,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
|
|
|
19640
19721
|
location: StorageLocationSchema,
|
|
19641
19722
|
relativePath: string()
|
|
19642
19723
|
}), _void(), { kind: "mutation" }), method(object({ location: StorageLocationSchema }), number().nullable()), method(BeginUploadInputSchema, BeginUploadResultSchema, { kind: "mutation" }), method(WriteChunkInputSchema, _void(), { kind: "mutation" }), method(FinalizeUploadInputSchema, _void(), { kind: "mutation" }), method(AbortUploadInputSchema, _void(), { kind: "mutation" }), method(BeginDownloadInputSchema, BeginDownloadResultSchema, { kind: "mutation" }), method(ReadChunkInputSchema, _instanceof(Uint8Array)), method(EndDownloadInputSchema, _void(), { kind: "mutation" });
|
|
19724
|
+
/** Profile-exported FormBuilder schema. Shape is ConfigUISchema at the UI. */
|
|
19725
|
+
var ProfileSettingsSchemaBridge = unknown().nullable();
|
|
19726
|
+
var ProfileSettingsBagSchema = record(string(), unknown());
|
|
19643
19727
|
/**
|
|
19644
19728
|
* A live terminal session hosted by the provider addon. Output and input do
|
|
19645
19729
|
* NOT flow through the capability — they use the addon data plane
|
|
@@ -19669,7 +19753,14 @@ var TerminalSessionInfoSchema = object({
|
|
|
19669
19753
|
var TerminalProfileInfoSchema = object({
|
|
19670
19754
|
profileId: string(),
|
|
19671
19755
|
label: string(),
|
|
19672
|
-
description: string().optional()
|
|
19756
|
+
description: string().optional(),
|
|
19757
|
+
/** Spawn defaults the instance form copies on create. */
|
|
19758
|
+
executable: string().optional(),
|
|
19759
|
+
args: array(string()).readonly().optional(),
|
|
19760
|
+
cwd: string().optional(),
|
|
19761
|
+
environment: array(string()).readonly().optional(),
|
|
19762
|
+
/** ConfigUISchema for instance knobs, or null when the profile has none. */
|
|
19763
|
+
settingsSchema: ProfileSettingsSchemaBridge.optional()
|
|
19673
19764
|
});
|
|
19674
19765
|
/**
|
|
19675
19766
|
* A durable operator-created Terminal instance. Profiles are templates; only
|
|
@@ -19682,7 +19773,12 @@ var TerminalInstanceInfoSchema = object({
|
|
|
19682
19773
|
profileId: string(),
|
|
19683
19774
|
profileLabel: string(),
|
|
19684
19775
|
name: string(),
|
|
19685
|
-
enabled: boolean()
|
|
19776
|
+
enabled: boolean(),
|
|
19777
|
+
executable: string(),
|
|
19778
|
+
args: array(string()).readonly(),
|
|
19779
|
+
cwd: string(),
|
|
19780
|
+
environment: array(string()).readonly(),
|
|
19781
|
+
profileSettings: ProfileSettingsBagSchema
|
|
19686
19782
|
});
|
|
19687
19783
|
var TerminalLegacyCameraSchema = object({
|
|
19688
19784
|
stableId: string(),
|
|
@@ -19712,7 +19808,23 @@ var TerminalOutputBatchSchema = object({
|
|
|
19712
19808
|
method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
|
|
19713
19809
|
targetNodeId: string().min(1),
|
|
19714
19810
|
profileId: string().min(1),
|
|
19715
|
-
name: string().trim().min(1).max(160).optional()
|
|
19811
|
+
name: string().trim().min(1).max(160).optional(),
|
|
19812
|
+
executable: string().max(1024).optional(),
|
|
19813
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
19814
|
+
cwd: string().max(1024).optional(),
|
|
19815
|
+
environment: array(string().max(4096)).max(64).optional(),
|
|
19816
|
+
profileSettings: ProfileSettingsBagSchema.optional()
|
|
19817
|
+
}), TerminalInstanceInfoSchema, {
|
|
19818
|
+
kind: "mutation",
|
|
19819
|
+
auth: "admin"
|
|
19820
|
+
}), method(object({
|
|
19821
|
+
instanceId: string().min(1),
|
|
19822
|
+
name: string().trim().min(1).max(160).optional(),
|
|
19823
|
+
executable: string().max(1024).optional(),
|
|
19824
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
19825
|
+
cwd: string().max(1024).optional(),
|
|
19826
|
+
environment: array(string().max(4096)).max(64).optional(),
|
|
19827
|
+
profileSettings: ProfileSettingsBagSchema.optional()
|
|
19716
19828
|
}), TerminalInstanceInfoSchema, {
|
|
19717
19829
|
kind: "mutation",
|
|
19718
19830
|
auth: "admin"
|
|
@@ -19734,7 +19846,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
|
|
|
19734
19846
|
}), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
|
|
19735
19847
|
profileId: string(),
|
|
19736
19848
|
cols: number().int().positive(),
|
|
19737
|
-
rows: number().int().positive()
|
|
19849
|
+
rows: number().int().positive(),
|
|
19850
|
+
executable: string().max(1024).optional(),
|
|
19851
|
+
args: array(string().max(2048)).max(64).optional(),
|
|
19852
|
+
cwd: string().max(1024).optional(),
|
|
19853
|
+
environment: array(string().max(4096)).max(64).optional()
|
|
19738
19854
|
}), TerminalSessionInfoSchema, {
|
|
19739
19855
|
kind: "mutation",
|
|
19740
19856
|
auth: "admin"
|
|
@@ -22516,10 +22632,10 @@ DeviceType.LawnMower, method(object({ deviceId: number().int().nonnegative() }),
|
|
|
22516
22632
|
*
|
|
22517
22633
|
* • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
|
|
22518
22634
|
* to receive an ordered list of candidate base URLs it should race
|
|
22519
|
-
* on connect — LAN IPv4 first (lowest latency
|
|
22520
|
-
* then public hostname (if a tunnel is
|
|
22521
|
-
* race them with short timeouts and stick with the
|
|
22522
|
-
* session.
|
|
22635
|
+
* on connect — LAN IPv4 and stable LAN IPv6 first (lowest latency
|
|
22636
|
+
* when on the same network), then public hostname (if a tunnel is
|
|
22637
|
+
* up). The SDK can race them with short timeouts and stick with the
|
|
22638
|
+
* winner for the session.
|
|
22523
22639
|
*
|
|
22524
22640
|
* Why hub-only: agents are not directly addressable by the operator's
|
|
22525
22641
|
* clients — they reverse-connect to the hub. Exposing their interfaces
|
|
@@ -22674,6 +22790,17 @@ var NotificationEndpointSchema = object({
|
|
|
22674
22790
|
/** What the ranking currently resolves to (null when nothing is reachable). */
|
|
22675
22791
|
resolved: string().nullable()
|
|
22676
22792
|
});
|
|
22793
|
+
/**
|
|
22794
|
+
* The URLs the SDK / viewer should race for API access. `baseUrls` empty =
|
|
22795
|
+
* AUTO (every LAN IPv4 + the public tunnel). `resolved` is what that choice
|
|
22796
|
+
* currently expands to, so the UI can show the effective set either way.
|
|
22797
|
+
*/
|
|
22798
|
+
var ViewerEndpointsSchema = object({
|
|
22799
|
+
/** The operator's explicit race set, or empty for AUTO. */
|
|
22800
|
+
baseUrls: array(string()).readonly(),
|
|
22801
|
+
/** What the ranking currently resolves to (may be empty if nothing is up). */
|
|
22802
|
+
resolved: array(string()).readonly()
|
|
22803
|
+
});
|
|
22677
22804
|
var AllowedAddressesSchema = object({
|
|
22678
22805
|
/**
|
|
22679
22806
|
* Allowlist of interface addresses operators have explicitly opted
|
|
@@ -22682,6 +22809,20 @@ var AllowedAddressesSchema = object({
|
|
|
22682
22809
|
* Network Addresses admin page and persisted by the addon.
|
|
22683
22810
|
*/
|
|
22684
22811
|
addresses: array(string()).readonly() });
|
|
22812
|
+
var TlsStatusSchema = object({
|
|
22813
|
+
mode: _enum([
|
|
22814
|
+
"generated",
|
|
22815
|
+
"uploaded",
|
|
22816
|
+
"disabled"
|
|
22817
|
+
]),
|
|
22818
|
+
leafFingerprintSha256: string().nullable(),
|
|
22819
|
+
caFingerprintSha256: string().nullable(),
|
|
22820
|
+
validTo: string().nullable(),
|
|
22821
|
+
sans: array(string()),
|
|
22822
|
+
caCertPem: string().nullable(),
|
|
22823
|
+
reissueError: string().nullable(),
|
|
22824
|
+
restartRequired: boolean()
|
|
22825
|
+
});
|
|
22685
22826
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
22686
22827
|
/**
|
|
22687
22828
|
* LEGACY HINT — do not send from new code. Kept optional so clients
|
|
@@ -22691,17 +22832,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
22691
22832
|
*/
|
|
22692
22833
|
port: number().int().min(1).max(65535).optional(),
|
|
22693
22834
|
/** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
|
|
22694
|
-
* candidate. Default `
|
|
22835
|
+
* candidate. Default `false` — loopback is not a client route. */
|
|
22695
22836
|
includeLoopback: boolean().optional(),
|
|
22696
|
-
/** Skip IPv6 entries.
|
|
22697
|
-
*
|
|
22837
|
+
/** Skip IPv6 entries. Default `false` — the palette includes stable
|
|
22838
|
+
* LAN IPv6 (ICE/WebRTC uses dual-stack regardless). Pass `true` to
|
|
22839
|
+
* hide them. The viewer HTTP/WS race is `getViewerEndpoints`. */
|
|
22698
22840
|
ipv4Only: boolean().optional(),
|
|
22699
22841
|
/** Scheme to emit for LAN/loopback URLs. Default `'http'`.
|
|
22700
22842
|
* Pass `'https'` when the caller is itself loaded over HTTPS
|
|
22701
22843
|
* to avoid mixed-content blocks in the browser. The public
|
|
22702
22844
|
* tunnel always emits `https://` regardless. */
|
|
22703
22845
|
scheme: _enum(["http", "https"]).optional()
|
|
22704
|
-
}), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" })
|
|
22846
|
+
}), 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, {
|
|
22847
|
+
kind: "mutation",
|
|
22848
|
+
auth: "admin"
|
|
22849
|
+
}), method(object({
|
|
22850
|
+
certPem: string().min(1),
|
|
22851
|
+
keyPem: string().min(1),
|
|
22852
|
+
caPem: string().optional()
|
|
22853
|
+
}), TlsStatusSchema, {
|
|
22854
|
+
kind: "mutation",
|
|
22855
|
+
auth: "admin"
|
|
22856
|
+
}), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
|
|
22857
|
+
kind: "mutation",
|
|
22858
|
+
auth: "admin"
|
|
22859
|
+
});
|
|
22705
22860
|
object({
|
|
22706
22861
|
/** Lifecycle state of the lock. `jammed` means the motor reported
|
|
22707
22862
|
* failure to reach the target — operator intervention required. */
|
|
@@ -28464,6 +28619,12 @@ Object.freeze({
|
|
|
28464
28619
|
addonId: null,
|
|
28465
28620
|
access: "create"
|
|
28466
28621
|
},
|
|
28622
|
+
"localNetwork.downloadCa": {
|
|
28623
|
+
capName: "local-network",
|
|
28624
|
+
capScope: "system",
|
|
28625
|
+
addonId: null,
|
|
28626
|
+
access: "view"
|
|
28627
|
+
},
|
|
28467
28628
|
"localNetwork.getAllowedAddresses": {
|
|
28468
28629
|
capName: "local-network",
|
|
28469
28630
|
capScope: "system",
|
|
@@ -28488,18 +28649,42 @@ Object.freeze({
|
|
|
28488
28649
|
addonId: null,
|
|
28489
28650
|
access: "view"
|
|
28490
28651
|
},
|
|
28652
|
+
"localNetwork.getTlsStatus": {
|
|
28653
|
+
capName: "local-network",
|
|
28654
|
+
capScope: "system",
|
|
28655
|
+
addonId: null,
|
|
28656
|
+
access: "view"
|
|
28657
|
+
},
|
|
28658
|
+
"localNetwork.getViewerEndpoints": {
|
|
28659
|
+
capName: "local-network",
|
|
28660
|
+
capScope: "system",
|
|
28661
|
+
addonId: null,
|
|
28662
|
+
access: "view"
|
|
28663
|
+
},
|
|
28491
28664
|
"localNetwork.list": {
|
|
28492
28665
|
capName: "local-network",
|
|
28493
28666
|
capScope: "system",
|
|
28494
28667
|
addonId: null,
|
|
28495
28668
|
access: "view"
|
|
28496
28669
|
},
|
|
28670
|
+
"localNetwork.regenerateCertificate": {
|
|
28671
|
+
capName: "local-network",
|
|
28672
|
+
capScope: "system",
|
|
28673
|
+
addonId: null,
|
|
28674
|
+
access: "create"
|
|
28675
|
+
},
|
|
28497
28676
|
"localNetwork.resetAllowlistToBestMatch": {
|
|
28498
28677
|
capName: "local-network",
|
|
28499
28678
|
capScope: "system",
|
|
28500
28679
|
addonId: null,
|
|
28501
28680
|
access: "delete"
|
|
28502
28681
|
},
|
|
28682
|
+
"localNetwork.revertToGeneratedCertificate": {
|
|
28683
|
+
capName: "local-network",
|
|
28684
|
+
capScope: "system",
|
|
28685
|
+
addonId: null,
|
|
28686
|
+
access: "create"
|
|
28687
|
+
},
|
|
28503
28688
|
"localNetwork.setAllowedAddresses": {
|
|
28504
28689
|
capName: "local-network",
|
|
28505
28690
|
capScope: "system",
|
|
@@ -28512,6 +28697,18 @@ Object.freeze({
|
|
|
28512
28697
|
addonId: null,
|
|
28513
28698
|
access: "create"
|
|
28514
28699
|
},
|
|
28700
|
+
"localNetwork.setViewerEndpoints": {
|
|
28701
|
+
capName: "local-network",
|
|
28702
|
+
capScope: "system",
|
|
28703
|
+
addonId: null,
|
|
28704
|
+
access: "create"
|
|
28705
|
+
},
|
|
28706
|
+
"localNetwork.uploadCertificate": {
|
|
28707
|
+
capName: "local-network",
|
|
28708
|
+
capScope: "system",
|
|
28709
|
+
addonId: null,
|
|
28710
|
+
access: "create"
|
|
28711
|
+
},
|
|
28515
28712
|
"lockControl.lock": {
|
|
28516
28713
|
capName: "lock-control",
|
|
28517
28714
|
capScope: "device",
|
|
@@ -31392,6 +31589,12 @@ Object.freeze({
|
|
|
31392
31589
|
addonId: null,
|
|
31393
31590
|
access: "create"
|
|
31394
31591
|
},
|
|
31592
|
+
"terminalSession.updateInstance": {
|
|
31593
|
+
capName: "terminal-session",
|
|
31594
|
+
capScope: "system",
|
|
31595
|
+
addonId: null,
|
|
31596
|
+
access: "create"
|
|
31597
|
+
},
|
|
31395
31598
|
"terminalSession.writeInput": {
|
|
31396
31599
|
capName: "terminal-session",
|
|
31397
31600
|
capScope: "system",
|
|
@@ -33879,6 +34082,35 @@ Object.freeze(Object.fromEntries([{
|
|
|
33879
34082
|
}]
|
|
33880
34083
|
}].map((s) => [s.stepId, s.defaultModelId])));
|
|
33881
34084
|
string().min(1);
|
|
34085
|
+
var CLUSTER_STEP_SETTING_FIELDS = [{
|
|
34086
|
+
stepId: "face-embedding",
|
|
34087
|
+
key: "minLandmarkFaceSize",
|
|
34088
|
+
label: "Min face size for recognition (detection px)",
|
|
34089
|
+
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.",
|
|
34090
|
+
type: "slider",
|
|
34091
|
+
min: 0,
|
|
34092
|
+
max: 64,
|
|
34093
|
+
step: 2,
|
|
34094
|
+
default: 24
|
|
34095
|
+
}];
|
|
34096
|
+
function clusterStepSettingKey(stepId, fieldKey) {
|
|
34097
|
+
return `clusterStepSetting:${stepId}:${fieldKey}`;
|
|
34098
|
+
}
|
|
34099
|
+
var ClusterSettingNumberSchema = number().finite();
|
|
34100
|
+
function readClusterStepSettings(config) {
|
|
34101
|
+
const out = {};
|
|
34102
|
+
for (const field of CLUSTER_STEP_SETTING_FIELDS) {
|
|
34103
|
+
const parsed = ClusterSettingNumberSchema.safeParse(config[clusterStepSettingKey(field.stepId, field.key)]);
|
|
34104
|
+
const value = parsed.success ? parsed.data : field.default;
|
|
34105
|
+
const existing = out[field.stepId] ?? {};
|
|
34106
|
+
out[field.stepId] = {
|
|
34107
|
+
...existing,
|
|
34108
|
+
[field.key]: value
|
|
34109
|
+
};
|
|
34110
|
+
}
|
|
34111
|
+
return out;
|
|
34112
|
+
}
|
|
34113
|
+
readClusterStepSettings({});
|
|
33882
34114
|
object({
|
|
33883
34115
|
/**
|
|
33884
34116
|
* Fraction of the box's own size added on EACH side before cutting.
|
|
@@ -34070,113 +34302,14 @@ var RingBuffer = class {
|
|
|
34070
34302
|
* decoder is an explicit opt-in fallback only. A stale/failed settings read at
|
|
34071
34303
|
* boot therefore resolves to node-av (never leaves the node with no decoder). */
|
|
34072
34304
|
var DEFAULT_DECODER_BACKEND = "nodeav";
|
|
34073
|
-
/** Narrow an unknown settings value to a {@link DecoderBackend}, else `null`. */
|
|
34074
|
-
function parseDecoderBackend(value) {
|
|
34075
|
-
return value === "ffmpeg" || value === "nodeav" ? value : null;
|
|
34076
|
-
}
|
|
34077
|
-
/**
|
|
34078
|
-
* Normalise a raw kernel node id to the bare node id used for scoping.
|
|
34079
|
-
* `localNodeId` can carry a `<node>/<addon>` suffix; the decoder selection is
|
|
34080
|
-
* per-NODE, so strip the addon segment. Falls back to `hub`.
|
|
34081
|
-
*/
|
|
34082
|
-
function normalizeDecoderNodeId(rawNodeId) {
|
|
34083
|
-
const raw = rawNodeId ?? "hub";
|
|
34084
|
-
return raw.includes("/") ? raw.split("/")[0] ?? "hub" : raw;
|
|
34085
|
-
}
|
|
34086
34305
|
//#endregion
|
|
34087
34306
|
//#region src/shared/decoder-backend.ts
|
|
34088
|
-
/** The NEUTRAL addon (pipeline-orchestrator, hub-resident) whose global
|
|
34089
|
-
* settings OWN the per-node `backend` selector. Neither decoder addon owns
|
|
34090
|
-
* it, so decoder-nodeav / decoder-ffmpeg stay fully independent. */
|
|
34091
|
-
var DECODER_OWNER_ADDON_ID = "pipeline-orchestrator";
|
|
34092
|
-
function isHydratedField(entry) {
|
|
34093
|
-
return typeof entry === "object" && entry !== null && "key" in entry;
|
|
34094
|
-
}
|
|
34095
34307
|
/**
|
|
34096
|
-
*
|
|
34097
|
-
*
|
|
34098
|
-
*
|
|
34099
|
-
* resolves to {@link DEFAULT_DECODER_BACKEND} — never a bare store key.
|
|
34308
|
+
* Resolve the decoder backend this addon should run. Owner settings are
|
|
34309
|
+
* ignored: persisted `backend@<nodeId>` rows stay in the store and are not
|
|
34310
|
+
* consulted. The runtime default is always {@link DEFAULT_DECODER_BACKEND}.
|
|
34100
34311
|
*/
|
|
34101
|
-
function
|
|
34102
|
-
if (view === null) return DEFAULT_DECODER_BACKEND;
|
|
34103
|
-
for (const section of view.sections) for (const entry of section.fields) {
|
|
34104
|
-
if (!isHydratedField(entry) || entry.key !== "backend") continue;
|
|
34105
|
-
return parseDecoderBackend(entry.value) ?? "nodeav";
|
|
34106
|
-
}
|
|
34107
|
-
return DEFAULT_DECODER_BACKEND;
|
|
34108
|
-
}
|
|
34109
|
-
/** Missing optional owner fingerprints: ffmpeg may be uninstalled. */
|
|
34110
|
-
function isMissingOwnerSettingsError(message) {
|
|
34111
|
-
return /not routable/i.test(message) || /provider not available/i.test(message);
|
|
34112
|
-
}
|
|
34113
|
-
/** Transient transport/settings-store fingerprints worth retrying on. */
|
|
34114
|
-
function isTransientSettingsError(message) {
|
|
34115
|
-
return /not loaded/i.test(message) || /transport-failed/i.test(message) || /not connected/i.test(message) || /SqliteSettingsBackend not initialized/i.test(message);
|
|
34116
|
-
}
|
|
34117
|
-
/**
|
|
34118
|
-
* Resolve the decoder backend a NON-OWNER addon (`decoder-nodeav`) should run,
|
|
34119
|
-
* by reading the OWNER's (`decoder-ffmpeg`) hub-central per-node `backend`.
|
|
34120
|
-
*
|
|
34121
|
-
* The read routes to the owner addon's child runner; during a simultaneous
|
|
34122
|
-
* (re)start the owner may not be up yet → a transient `transport-failed (addon
|
|
34123
|
-
* not loaded)`. Immediately defaulting here would ignore an explicit `ffmpeg`
|
|
34124
|
-
* selection (the node would silently run the node-av default instead). So retry
|
|
34125
|
-
* on the transient fingerprints with a bounded budget (mirrors
|
|
34126
|
-
* `BaseAddon.readAddonStoreWithRetry`) until the owner answers; only a
|
|
34127
|
-
* persistent failure falls back to {@link DEFAULT_DECODER_BACKEND}.
|
|
34128
|
-
*
|
|
34129
|
-
* The OWNER addon must NOT call this (it would self-route + deadlock) — it uses
|
|
34130
|
-
* {@link resolveOwnDecoderBackend} against its own store instead.
|
|
34131
|
-
*/
|
|
34132
|
-
async function resolveDecoderBackend(api, nodeId, logger) {
|
|
34133
|
-
if (!api) {
|
|
34134
|
-
logger.warn("decoder-backend: no api surface — using default backend", { meta: { default: DEFAULT_DECODER_BACKEND } });
|
|
34135
|
-
return DEFAULT_DECODER_BACKEND;
|
|
34136
|
-
}
|
|
34137
|
-
const normalized = normalizeDecoderNodeId(nodeId);
|
|
34138
|
-
const delaysMs = [
|
|
34139
|
-
150,
|
|
34140
|
-
350,
|
|
34141
|
-
600,
|
|
34142
|
-
900,
|
|
34143
|
-
1200
|
|
34144
|
-
];
|
|
34145
|
-
let lastErr;
|
|
34146
|
-
for (let attempt = 0; attempt <= delaysMs.length; attempt++) {
|
|
34147
|
-
try {
|
|
34148
|
-
const view = await api.addonSettings.getGlobalSettings.query({
|
|
34149
|
-
addonId: DECODER_OWNER_ADDON_ID,
|
|
34150
|
-
nodeId: normalized
|
|
34151
|
-
});
|
|
34152
|
-
if (view !== null) return pickDecoderBackendFromSettings(view);
|
|
34153
|
-
lastErr = /* @__PURE__ */ new Error("owner settings unavailable (null)");
|
|
34154
|
-
} catch (err) {
|
|
34155
|
-
lastErr = err;
|
|
34156
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
34157
|
-
if (isTransientSettingsError(msg)) {} else if (isMissingOwnerSettingsError(msg)) {
|
|
34158
|
-
logger.warn("decoder-backend: optional owner unavailable — using default backend", { meta: {
|
|
34159
|
-
default: DEFAULT_DECODER_BACKEND,
|
|
34160
|
-
owner: DECODER_OWNER_ADDON_ID,
|
|
34161
|
-
error: msg
|
|
34162
|
-
} });
|
|
34163
|
-
return DEFAULT_DECODER_BACKEND;
|
|
34164
|
-
} else {
|
|
34165
|
-
logger.warn("decoder-backend: settings read failed — using default backend", { meta: {
|
|
34166
|
-
default: DEFAULT_DECODER_BACKEND,
|
|
34167
|
-
error: msg
|
|
34168
|
-
} });
|
|
34169
|
-
return DEFAULT_DECODER_BACKEND;
|
|
34170
|
-
}
|
|
34171
|
-
}
|
|
34172
|
-
if (attempt === delaysMs.length) break;
|
|
34173
|
-
await new Promise((resolve) => setTimeout(resolve, delaysMs[attempt]));
|
|
34174
|
-
}
|
|
34175
|
-
logger.warn("decoder-backend: owner settings unavailable after retries — using default backend", { meta: {
|
|
34176
|
-
default: DEFAULT_DECODER_BACKEND,
|
|
34177
|
-
owner: DECODER_OWNER_ADDON_ID,
|
|
34178
|
-
error: lastErr instanceof Error ? lastErr.message : String(lastErr)
|
|
34179
|
-
} });
|
|
34312
|
+
async function resolveDecoderBackend(_api, _nodeId, _logger) {
|
|
34180
34313
|
return DEFAULT_DECODER_BACKEND;
|
|
34181
34314
|
}
|
|
34182
34315
|
//#endregion
|