@camstack/addon-decoder-ffmpeg 1.2.24 → 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 +262 -120
- package/dist/index.mjs +262 -120
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ Object.defineProperties(exports, {
|
|
|
4
4
|
});
|
|
5
5
|
let node_crypto = require("node:crypto");
|
|
6
6
|
let node_child_process = require("node:child_process");
|
|
7
|
-
//#region ../types/dist/event-category-
|
|
7
|
+
//#region ../types/dist/event-category-CIa_iT6b.mjs
|
|
8
8
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
9
9
|
EventCategory["SystemBoot"] = "system.boot";
|
|
10
10
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -20,6 +20,15 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
20
20
|
*/
|
|
21
21
|
EventCategory["SystemRestartCompleted"] = "system.restart-completed";
|
|
22
22
|
/**
|
|
23
|
+
* The hub reissued its own TLS certificate at boot (`ensureTlsCert`).
|
|
24
|
+
* Emitted only when the material on disk actually changed, so an
|
|
25
|
+
* operator who trusted the old certificate by hand is told rather than
|
|
26
|
+
* discovering it as a browser error. Payload `TlsCertChangedPayload`.
|
|
27
|
+
*
|
|
28
|
+
* Rule: docs/decisions/adr-0227-*.md
|
|
29
|
+
*/
|
|
30
|
+
EventCategory["SystemTlsCertChanged"] = "system.tls-cert-changed";
|
|
31
|
+
/**
|
|
23
32
|
* A newer addon or server-root package version was found by the
|
|
24
33
|
* authoritative registry check. Emitted once when any observed
|
|
25
34
|
* `latestVersion` changes (or a package/node first appears behind);
|
|
@@ -8012,6 +8021,15 @@ var LabelDefinitionSchema = object({
|
|
|
8012
8021
|
description: string().optional(),
|
|
8013
8022
|
icon: string().optional()
|
|
8014
8023
|
});
|
|
8024
|
+
var ClassMapDefinitionSchema = object({
|
|
8025
|
+
mapping: record(string(), _enum([
|
|
8026
|
+
"person",
|
|
8027
|
+
"vehicle",
|
|
8028
|
+
"animal",
|
|
8029
|
+
"package"
|
|
8030
|
+
])),
|
|
8031
|
+
preserveOriginal: boolean()
|
|
8032
|
+
});
|
|
8015
8033
|
var MODEL_FORMATS = [
|
|
8016
8034
|
"onnx",
|
|
8017
8035
|
"coreml",
|
|
@@ -8190,7 +8208,13 @@ var ModelCatalogEntrySchema = object({
|
|
|
8190
8208
|
* `id` stays the source of truth for resolution/download/persistence; grouping
|
|
8191
8209
|
* is a presentation overlay resolved back to an `id`.
|
|
8192
8210
|
*/
|
|
8193
|
-
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()
|
|
8194
8218
|
});
|
|
8195
8219
|
var ConvertTargetSchema = discriminatedUnion("format", [object({
|
|
8196
8220
|
format: literal("openvino"),
|
|
@@ -8219,7 +8243,8 @@ var ModelConvertMetadataSchema = object({
|
|
|
8219
8243
|
"ocr",
|
|
8220
8244
|
"segmentation"
|
|
8221
8245
|
]),
|
|
8222
|
-
faceAlignment: boolean().optional()
|
|
8246
|
+
faceAlignment: boolean().optional(),
|
|
8247
|
+
classMap: ClassMapDefinitionSchema.optional()
|
|
8223
8248
|
});
|
|
8224
8249
|
var ConvertResultSchema = object({
|
|
8225
8250
|
entry: ModelCatalogEntrySchema,
|
|
@@ -17340,6 +17365,33 @@ var NativeCropRefSchema = object({
|
|
|
17340
17365
|
h: number()
|
|
17341
17366
|
})
|
|
17342
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
|
+
});
|
|
17343
17395
|
var ModelFormatSchema$1 = _enum([
|
|
17344
17396
|
"onnx",
|
|
17345
17397
|
"coreml",
|
|
@@ -17584,6 +17636,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
|
|
|
17584
17636
|
steps: array(PipelineStepInputSchema).min(1),
|
|
17585
17637
|
frame: FrameInputSchema.optional(),
|
|
17586
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
|
+
/**
|
|
17587
17645
|
* CB5 shm passthrough — a `FrameHandle` naming the same ring slot
|
|
17588
17646
|
* the decoded pixels live in. One more member of the one-of
|
|
17589
17647
|
* frame/frameHandle/image/imageBase64/referenceImage group.
|
|
@@ -17839,7 +17897,10 @@ var NativeCropResultSchema = object({
|
|
|
17839
17897
|
* Which source served this crop, so a quality-sensitive consumer (the native
|
|
17840
17898
|
* `keyFrame`) can reject a degraded fallback:
|
|
17841
17899
|
* - `native` — cut from the decode worker's retained NATIVE surface (the
|
|
17842
|
-
* 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`).
|
|
17843
17904
|
* - `ram-fullframe` — the native surface MISSED but the request was full-frame,
|
|
17844
17905
|
* so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
|
|
17845
17906
|
* the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
|
|
@@ -18330,12 +18391,41 @@ var RunnerLocalLoadSchema = object({
|
|
|
18330
18391
|
* legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
|
|
18331
18392
|
* working unchanged when they switch to reading from the runner cap.
|
|
18332
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
|
+
});
|
|
18333
18422
|
var RunnerLocalMetricsSchema = object({
|
|
18334
18423
|
nodeId: string(),
|
|
18335
18424
|
activeCameras: number(),
|
|
18336
18425
|
throttledCameras: number(),
|
|
18337
18426
|
avgInferenceTimeMs: number(),
|
|
18338
|
-
queueDepth: number()
|
|
18427
|
+
queueDepth: number(),
|
|
18428
|
+
frameLazy: FrameLazyMetricsSchema.optional()
|
|
18339
18429
|
});
|
|
18340
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({
|
|
18341
18431
|
handle: FrameHandleSchema,
|
|
@@ -19635,6 +19725,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
|
|
|
19635
19725
|
location: StorageLocationSchema,
|
|
19636
19726
|
relativePath: string()
|
|
19637
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());
|
|
19638
19731
|
/**
|
|
19639
19732
|
* A live terminal session hosted by the provider addon. Output and input do
|
|
19640
19733
|
* NOT flow through the capability — they use the addon data plane
|
|
@@ -19664,7 +19757,14 @@ var TerminalSessionInfoSchema = object({
|
|
|
19664
19757
|
var TerminalProfileInfoSchema = object({
|
|
19665
19758
|
profileId: string(),
|
|
19666
19759
|
label: string(),
|
|
19667
|
-
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()
|
|
19668
19768
|
});
|
|
19669
19769
|
/**
|
|
19670
19770
|
* A durable operator-created Terminal instance. Profiles are templates; only
|
|
@@ -19677,7 +19777,12 @@ var TerminalInstanceInfoSchema = object({
|
|
|
19677
19777
|
profileId: string(),
|
|
19678
19778
|
profileLabel: string(),
|
|
19679
19779
|
name: string(),
|
|
19680
|
-
enabled: boolean()
|
|
19780
|
+
enabled: boolean(),
|
|
19781
|
+
executable: string(),
|
|
19782
|
+
args: array(string()).readonly(),
|
|
19783
|
+
cwd: string(),
|
|
19784
|
+
environment: array(string()).readonly(),
|
|
19785
|
+
profileSettings: ProfileSettingsBagSchema
|
|
19681
19786
|
});
|
|
19682
19787
|
var TerminalLegacyCameraSchema = object({
|
|
19683
19788
|
stableId: string(),
|
|
@@ -19707,7 +19812,23 @@ var TerminalOutputBatchSchema = object({
|
|
|
19707
19812
|
method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
|
|
19708
19813
|
targetNodeId: string().min(1),
|
|
19709
19814
|
profileId: string().min(1),
|
|
19710
|
-
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()
|
|
19711
19832
|
}), TerminalInstanceInfoSchema, {
|
|
19712
19833
|
kind: "mutation",
|
|
19713
19834
|
auth: "admin"
|
|
@@ -19729,7 +19850,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
|
|
|
19729
19850
|
}), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
|
|
19730
19851
|
profileId: string(),
|
|
19731
19852
|
cols: number().int().positive(),
|
|
19732
|
-
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()
|
|
19733
19858
|
}), TerminalSessionInfoSchema, {
|
|
19734
19859
|
kind: "mutation",
|
|
19735
19860
|
auth: "admin"
|
|
@@ -22511,10 +22636,10 @@ DeviceType.LawnMower, method(object({ deviceId: number().int().nonnegative() }),
|
|
|
22511
22636
|
*
|
|
22512
22637
|
* • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
|
|
22513
22638
|
* to receive an ordered list of candidate base URLs it should race
|
|
22514
|
-
* on connect — LAN IPv4 first (lowest latency
|
|
22515
|
-
* then public hostname (if a tunnel is
|
|
22516
|
-
* race them with short timeouts and stick with the
|
|
22517
|
-
* 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.
|
|
22518
22643
|
*
|
|
22519
22644
|
* Why hub-only: agents are not directly addressable by the operator's
|
|
22520
22645
|
* clients — they reverse-connect to the hub. Exposing their interfaces
|
|
@@ -22669,6 +22794,17 @@ var NotificationEndpointSchema = object({
|
|
|
22669
22794
|
/** What the ranking currently resolves to (null when nothing is reachable). */
|
|
22670
22795
|
resolved: string().nullable()
|
|
22671
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
|
+
});
|
|
22672
22808
|
var AllowedAddressesSchema = object({
|
|
22673
22809
|
/**
|
|
22674
22810
|
* Allowlist of interface addresses operators have explicitly opted
|
|
@@ -22677,6 +22813,20 @@ var AllowedAddressesSchema = object({
|
|
|
22677
22813
|
* Network Addresses admin page and persisted by the addon.
|
|
22678
22814
|
*/
|
|
22679
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
|
+
});
|
|
22680
22830
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
22681
22831
|
/**
|
|
22682
22832
|
* LEGACY HINT — do not send from new code. Kept optional so clients
|
|
@@ -22686,17 +22836,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
22686
22836
|
*/
|
|
22687
22837
|
port: number().int().min(1).max(65535).optional(),
|
|
22688
22838
|
/** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
|
|
22689
|
-
* candidate. Default `
|
|
22839
|
+
* candidate. Default `false` — loopback is not a client route. */
|
|
22690
22840
|
includeLoopback: boolean().optional(),
|
|
22691
|
-
/** Skip IPv6 entries.
|
|
22692
|
-
*
|
|
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`. */
|
|
22693
22844
|
ipv4Only: boolean().optional(),
|
|
22694
22845
|
/** Scheme to emit for LAN/loopback URLs. Default `'http'`.
|
|
22695
22846
|
* Pass `'https'` when the caller is itself loaded over HTTPS
|
|
22696
22847
|
* to avoid mixed-content blocks in the browser. The public
|
|
22697
22848
|
* tunnel always emits `https://` regardless. */
|
|
22698
22849
|
scheme: _enum(["http", "https"]).optional()
|
|
22699
|
-
}), 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
|
+
});
|
|
22700
22864
|
object({
|
|
22701
22865
|
/** Lifecycle state of the lock. `jammed` means the motor reported
|
|
22702
22866
|
* failure to reach the target — operator intervention required. */
|
|
@@ -28459,6 +28623,12 @@ Object.freeze({
|
|
|
28459
28623
|
addonId: null,
|
|
28460
28624
|
access: "create"
|
|
28461
28625
|
},
|
|
28626
|
+
"localNetwork.downloadCa": {
|
|
28627
|
+
capName: "local-network",
|
|
28628
|
+
capScope: "system",
|
|
28629
|
+
addonId: null,
|
|
28630
|
+
access: "view"
|
|
28631
|
+
},
|
|
28462
28632
|
"localNetwork.getAllowedAddresses": {
|
|
28463
28633
|
capName: "local-network",
|
|
28464
28634
|
capScope: "system",
|
|
@@ -28483,18 +28653,42 @@ Object.freeze({
|
|
|
28483
28653
|
addonId: null,
|
|
28484
28654
|
access: "view"
|
|
28485
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
|
+
},
|
|
28486
28668
|
"localNetwork.list": {
|
|
28487
28669
|
capName: "local-network",
|
|
28488
28670
|
capScope: "system",
|
|
28489
28671
|
addonId: null,
|
|
28490
28672
|
access: "view"
|
|
28491
28673
|
},
|
|
28674
|
+
"localNetwork.regenerateCertificate": {
|
|
28675
|
+
capName: "local-network",
|
|
28676
|
+
capScope: "system",
|
|
28677
|
+
addonId: null,
|
|
28678
|
+
access: "create"
|
|
28679
|
+
},
|
|
28492
28680
|
"localNetwork.resetAllowlistToBestMatch": {
|
|
28493
28681
|
capName: "local-network",
|
|
28494
28682
|
capScope: "system",
|
|
28495
28683
|
addonId: null,
|
|
28496
28684
|
access: "delete"
|
|
28497
28685
|
},
|
|
28686
|
+
"localNetwork.revertToGeneratedCertificate": {
|
|
28687
|
+
capName: "local-network",
|
|
28688
|
+
capScope: "system",
|
|
28689
|
+
addonId: null,
|
|
28690
|
+
access: "create"
|
|
28691
|
+
},
|
|
28498
28692
|
"localNetwork.setAllowedAddresses": {
|
|
28499
28693
|
capName: "local-network",
|
|
28500
28694
|
capScope: "system",
|
|
@@ -28507,6 +28701,18 @@ Object.freeze({
|
|
|
28507
28701
|
addonId: null,
|
|
28508
28702
|
access: "create"
|
|
28509
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
|
+
},
|
|
28510
28716
|
"lockControl.lock": {
|
|
28511
28717
|
capName: "lock-control",
|
|
28512
28718
|
capScope: "device",
|
|
@@ -31387,6 +31593,12 @@ Object.freeze({
|
|
|
31387
31593
|
addonId: null,
|
|
31388
31594
|
access: "create"
|
|
31389
31595
|
},
|
|
31596
|
+
"terminalSession.updateInstance": {
|
|
31597
|
+
capName: "terminal-session",
|
|
31598
|
+
capScope: "system",
|
|
31599
|
+
addonId: null,
|
|
31600
|
+
access: "create"
|
|
31601
|
+
},
|
|
31390
31602
|
"terminalSession.writeInput": {
|
|
31391
31603
|
capName: "terminal-session",
|
|
31392
31604
|
capScope: "system",
|
|
@@ -33874,6 +34086,35 @@ Object.freeze(Object.fromEntries([{
|
|
|
33874
34086
|
}]
|
|
33875
34087
|
}].map((s) => [s.stepId, s.defaultModelId])));
|
|
33876
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({});
|
|
33877
34118
|
object({
|
|
33878
34119
|
/**
|
|
33879
34120
|
* Fraction of the box's own size added on EACH side before cutting.
|
|
@@ -34065,113 +34306,14 @@ var RingBuffer = class {
|
|
|
34065
34306
|
* decoder is an explicit opt-in fallback only. A stale/failed settings read at
|
|
34066
34307
|
* boot therefore resolves to node-av (never leaves the node with no decoder). */
|
|
34067
34308
|
var DEFAULT_DECODER_BACKEND = "nodeav";
|
|
34068
|
-
/** Narrow an unknown settings value to a {@link DecoderBackend}, else `null`. */
|
|
34069
|
-
function parseDecoderBackend(value) {
|
|
34070
|
-
return value === "ffmpeg" || value === "nodeav" ? value : null;
|
|
34071
|
-
}
|
|
34072
|
-
/**
|
|
34073
|
-
* Normalise a raw kernel node id to the bare node id used for scoping.
|
|
34074
|
-
* `localNodeId` can carry a `<node>/<addon>` suffix; the decoder selection is
|
|
34075
|
-
* per-NODE, so strip the addon segment. Falls back to `hub`.
|
|
34076
|
-
*/
|
|
34077
|
-
function normalizeDecoderNodeId(rawNodeId) {
|
|
34078
|
-
const raw = rawNodeId ?? "hub";
|
|
34079
|
-
return raw.includes("/") ? raw.split("/")[0] ?? "hub" : raw;
|
|
34080
|
-
}
|
|
34081
34309
|
//#endregion
|
|
34082
34310
|
//#region src/shared/decoder-backend.ts
|
|
34083
|
-
/** The NEUTRAL addon (pipeline-orchestrator, hub-resident) whose global
|
|
34084
|
-
* settings OWN the per-node `backend` selector. Neither decoder addon owns
|
|
34085
|
-
* it, so decoder-nodeav / decoder-ffmpeg stay fully independent. */
|
|
34086
|
-
var DECODER_OWNER_ADDON_ID = "pipeline-orchestrator";
|
|
34087
|
-
function isHydratedField(entry) {
|
|
34088
|
-
return typeof entry === "object" && entry !== null && "key" in entry;
|
|
34089
|
-
}
|
|
34090
|
-
/**
|
|
34091
|
-
* Pure selection from an already-read hydrated settings payload: extract the
|
|
34092
|
-
* owner's `backend` field value (which the owner projected per-node from its
|
|
34093
|
-
* scoped store key) and narrow it. A missing/invalid field or a null payload
|
|
34094
|
-
* resolves to {@link DEFAULT_DECODER_BACKEND} — never a bare store key.
|
|
34095
|
-
*/
|
|
34096
|
-
function pickDecoderBackendFromSettings(view) {
|
|
34097
|
-
if (view === null) return DEFAULT_DECODER_BACKEND;
|
|
34098
|
-
for (const section of view.sections) for (const entry of section.fields) {
|
|
34099
|
-
if (!isHydratedField(entry) || entry.key !== "backend") continue;
|
|
34100
|
-
return parseDecoderBackend(entry.value) ?? "nodeav";
|
|
34101
|
-
}
|
|
34102
|
-
return DEFAULT_DECODER_BACKEND;
|
|
34103
|
-
}
|
|
34104
|
-
/** Missing optional owner fingerprints: ffmpeg may be uninstalled. */
|
|
34105
|
-
function isMissingOwnerSettingsError(message) {
|
|
34106
|
-
return /not routable/i.test(message) || /provider not available/i.test(message);
|
|
34107
|
-
}
|
|
34108
|
-
/** Transient transport/settings-store fingerprints worth retrying on. */
|
|
34109
|
-
function isTransientSettingsError(message) {
|
|
34110
|
-
return /not loaded/i.test(message) || /transport-failed/i.test(message) || /not connected/i.test(message) || /SqliteSettingsBackend not initialized/i.test(message);
|
|
34111
|
-
}
|
|
34112
34311
|
/**
|
|
34113
|
-
* Resolve the decoder backend
|
|
34114
|
-
*
|
|
34115
|
-
*
|
|
34116
|
-
* The read routes to the owner addon's child runner; during a simultaneous
|
|
34117
|
-
* (re)start the owner may not be up yet → a transient `transport-failed (addon
|
|
34118
|
-
* not loaded)`. Immediately defaulting here would ignore an explicit `ffmpeg`
|
|
34119
|
-
* selection (the node would silently run the node-av default instead). So retry
|
|
34120
|
-
* on the transient fingerprints with a bounded budget (mirrors
|
|
34121
|
-
* `BaseAddon.readAddonStoreWithRetry`) until the owner answers; only a
|
|
34122
|
-
* persistent failure falls back to {@link DEFAULT_DECODER_BACKEND}.
|
|
34123
|
-
*
|
|
34124
|
-
* The OWNER addon must NOT call this (it would self-route + deadlock) — it uses
|
|
34125
|
-
* {@link resolveOwnDecoderBackend} against its own store instead.
|
|
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}.
|
|
34126
34315
|
*/
|
|
34127
|
-
async function resolveDecoderBackend(
|
|
34128
|
-
if (!api) {
|
|
34129
|
-
logger.warn("decoder-backend: no api surface — using default backend", { meta: { default: DEFAULT_DECODER_BACKEND } });
|
|
34130
|
-
return DEFAULT_DECODER_BACKEND;
|
|
34131
|
-
}
|
|
34132
|
-
const normalized = normalizeDecoderNodeId(nodeId);
|
|
34133
|
-
const delaysMs = [
|
|
34134
|
-
150,
|
|
34135
|
-
350,
|
|
34136
|
-
600,
|
|
34137
|
-
900,
|
|
34138
|
-
1200
|
|
34139
|
-
];
|
|
34140
|
-
let lastErr;
|
|
34141
|
-
for (let attempt = 0; attempt <= delaysMs.length; attempt++) {
|
|
34142
|
-
try {
|
|
34143
|
-
const view = await api.addonSettings.getGlobalSettings.query({
|
|
34144
|
-
addonId: DECODER_OWNER_ADDON_ID,
|
|
34145
|
-
nodeId: normalized
|
|
34146
|
-
});
|
|
34147
|
-
if (view !== null) return pickDecoderBackendFromSettings(view);
|
|
34148
|
-
lastErr = /* @__PURE__ */ new Error("owner settings unavailable (null)");
|
|
34149
|
-
} catch (err) {
|
|
34150
|
-
lastErr = err;
|
|
34151
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
34152
|
-
if (isTransientSettingsError(msg)) {} else if (isMissingOwnerSettingsError(msg)) {
|
|
34153
|
-
logger.warn("decoder-backend: optional owner unavailable — using default backend", { meta: {
|
|
34154
|
-
default: DEFAULT_DECODER_BACKEND,
|
|
34155
|
-
owner: DECODER_OWNER_ADDON_ID,
|
|
34156
|
-
error: msg
|
|
34157
|
-
} });
|
|
34158
|
-
return DEFAULT_DECODER_BACKEND;
|
|
34159
|
-
} else {
|
|
34160
|
-
logger.warn("decoder-backend: settings read failed — using default backend", { meta: {
|
|
34161
|
-
default: DEFAULT_DECODER_BACKEND,
|
|
34162
|
-
error: msg
|
|
34163
|
-
} });
|
|
34164
|
-
return DEFAULT_DECODER_BACKEND;
|
|
34165
|
-
}
|
|
34166
|
-
}
|
|
34167
|
-
if (attempt === delaysMs.length) break;
|
|
34168
|
-
await new Promise((resolve) => setTimeout(resolve, delaysMs[attempt]));
|
|
34169
|
-
}
|
|
34170
|
-
logger.warn("decoder-backend: owner settings unavailable after retries — using default backend", { meta: {
|
|
34171
|
-
default: DEFAULT_DECODER_BACKEND,
|
|
34172
|
-
owner: DECODER_OWNER_ADDON_ID,
|
|
34173
|
-
error: lastErr instanceof Error ? lastErr.message : String(lastErr)
|
|
34174
|
-
} });
|
|
34316
|
+
async function resolveDecoderBackend(_api, _nodeId, _logger) {
|
|
34175
34317
|
return DEFAULT_DECODER_BACKEND;
|
|
34176
34318
|
}
|
|
34177
34319
|
//#endregion
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
|
-
//#region ../types/dist/event-category-
|
|
3
|
+
//#region ../types/dist/event-category-CIa_iT6b.mjs
|
|
4
4
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
5
5
|
EventCategory["SystemBoot"] = "system.boot";
|
|
6
6
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -16,6 +16,15 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
16
16
|
*/
|
|
17
17
|
EventCategory["SystemRestartCompleted"] = "system.restart-completed";
|
|
18
18
|
/**
|
|
19
|
+
* The hub reissued its own TLS certificate at boot (`ensureTlsCert`).
|
|
20
|
+
* Emitted only when the material on disk actually changed, so an
|
|
21
|
+
* operator who trusted the old certificate by hand is told rather than
|
|
22
|
+
* discovering it as a browser error. Payload `TlsCertChangedPayload`.
|
|
23
|
+
*
|
|
24
|
+
* Rule: docs/decisions/adr-0227-*.md
|
|
25
|
+
*/
|
|
26
|
+
EventCategory["SystemTlsCertChanged"] = "system.tls-cert-changed";
|
|
27
|
+
/**
|
|
19
28
|
* A newer addon or server-root package version was found by the
|
|
20
29
|
* authoritative registry check. Emitted once when any observed
|
|
21
30
|
* `latestVersion` changes (or a package/node first appears behind);
|
|
@@ -8008,6 +8017,15 @@ var LabelDefinitionSchema = object({
|
|
|
8008
8017
|
description: string().optional(),
|
|
8009
8018
|
icon: string().optional()
|
|
8010
8019
|
});
|
|
8020
|
+
var ClassMapDefinitionSchema = object({
|
|
8021
|
+
mapping: record(string(), _enum([
|
|
8022
|
+
"person",
|
|
8023
|
+
"vehicle",
|
|
8024
|
+
"animal",
|
|
8025
|
+
"package"
|
|
8026
|
+
])),
|
|
8027
|
+
preserveOriginal: boolean()
|
|
8028
|
+
});
|
|
8011
8029
|
var MODEL_FORMATS = [
|
|
8012
8030
|
"onnx",
|
|
8013
8031
|
"coreml",
|
|
@@ -8186,7 +8204,13 @@ var ModelCatalogEntrySchema = object({
|
|
|
8186
8204
|
* `id` stays the source of truth for resolution/download/persistence; grouping
|
|
8187
8205
|
* is a presentation overlay resolved back to an `id`.
|
|
8188
8206
|
*/
|
|
8189
|
-
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()
|
|
8190
8214
|
});
|
|
8191
8215
|
var ConvertTargetSchema = discriminatedUnion("format", [object({
|
|
8192
8216
|
format: literal("openvino"),
|
|
@@ -8215,7 +8239,8 @@ var ModelConvertMetadataSchema = object({
|
|
|
8215
8239
|
"ocr",
|
|
8216
8240
|
"segmentation"
|
|
8217
8241
|
]),
|
|
8218
|
-
faceAlignment: boolean().optional()
|
|
8242
|
+
faceAlignment: boolean().optional(),
|
|
8243
|
+
classMap: ClassMapDefinitionSchema.optional()
|
|
8219
8244
|
});
|
|
8220
8245
|
var ConvertResultSchema = object({
|
|
8221
8246
|
entry: ModelCatalogEntrySchema,
|
|
@@ -17336,6 +17361,33 @@ var NativeCropRefSchema = object({
|
|
|
17336
17361
|
h: number()
|
|
17337
17362
|
})
|
|
17338
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
|
+
});
|
|
17339
17391
|
var ModelFormatSchema$1 = _enum([
|
|
17340
17392
|
"onnx",
|
|
17341
17393
|
"coreml",
|
|
@@ -17580,6 +17632,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
|
|
|
17580
17632
|
steps: array(PipelineStepInputSchema).min(1),
|
|
17581
17633
|
frame: FrameInputSchema.optional(),
|
|
17582
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
|
+
/**
|
|
17583
17641
|
* CB5 shm passthrough — a `FrameHandle` naming the same ring slot
|
|
17584
17642
|
* the decoded pixels live in. One more member of the one-of
|
|
17585
17643
|
* frame/frameHandle/image/imageBase64/referenceImage group.
|
|
@@ -17835,7 +17893,10 @@ var NativeCropResultSchema = object({
|
|
|
17835
17893
|
* Which source served this crop, so a quality-sensitive consumer (the native
|
|
17836
17894
|
* `keyFrame`) can reject a degraded fallback:
|
|
17837
17895
|
* - `native` — cut from the decode worker's retained NATIVE surface (the
|
|
17838
|
-
* 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`).
|
|
17839
17900
|
* - `ram-fullframe` — the native surface MISSED but the request was full-frame,
|
|
17840
17901
|
* so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
|
|
17841
17902
|
* the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
|
|
@@ -18326,12 +18387,41 @@ var RunnerLocalLoadSchema = object({
|
|
|
18326
18387
|
* legacy `OrchestratorMetricsSchema` shape so existing dashboards keep
|
|
18327
18388
|
* working unchanged when they switch to reading from the runner cap.
|
|
18328
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
|
+
});
|
|
18329
18418
|
var RunnerLocalMetricsSchema = object({
|
|
18330
18419
|
nodeId: string(),
|
|
18331
18420
|
activeCameras: number(),
|
|
18332
18421
|
throttledCameras: number(),
|
|
18333
18422
|
avgInferenceTimeMs: number(),
|
|
18334
|
-
queueDepth: number()
|
|
18423
|
+
queueDepth: number(),
|
|
18424
|
+
frameLazy: FrameLazyMetricsSchema.optional()
|
|
18335
18425
|
});
|
|
18336
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({
|
|
18337
18427
|
handle: FrameHandleSchema,
|
|
@@ -19631,6 +19721,9 @@ method(_void(), ProviderInfoSchema), method(object({ config: record(string(), un
|
|
|
19631
19721
|
location: StorageLocationSchema,
|
|
19632
19722
|
relativePath: string()
|
|
19633
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());
|
|
19634
19727
|
/**
|
|
19635
19728
|
* A live terminal session hosted by the provider addon. Output and input do
|
|
19636
19729
|
* NOT flow through the capability — they use the addon data plane
|
|
@@ -19660,7 +19753,14 @@ var TerminalSessionInfoSchema = object({
|
|
|
19660
19753
|
var TerminalProfileInfoSchema = object({
|
|
19661
19754
|
profileId: string(),
|
|
19662
19755
|
label: string(),
|
|
19663
|
-
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()
|
|
19664
19764
|
});
|
|
19665
19765
|
/**
|
|
19666
19766
|
* A durable operator-created Terminal instance. Profiles are templates; only
|
|
@@ -19673,7 +19773,12 @@ var TerminalInstanceInfoSchema = object({
|
|
|
19673
19773
|
profileId: string(),
|
|
19674
19774
|
profileLabel: string(),
|
|
19675
19775
|
name: string(),
|
|
19676
|
-
enabled: boolean()
|
|
19776
|
+
enabled: boolean(),
|
|
19777
|
+
executable: string(),
|
|
19778
|
+
args: array(string()).readonly(),
|
|
19779
|
+
cwd: string(),
|
|
19780
|
+
environment: array(string()).readonly(),
|
|
19781
|
+
profileSettings: ProfileSettingsBagSchema
|
|
19677
19782
|
});
|
|
19678
19783
|
var TerminalLegacyCameraSchema = object({
|
|
19679
19784
|
stableId: string(),
|
|
@@ -19703,7 +19808,23 @@ var TerminalOutputBatchSchema = object({
|
|
|
19703
19808
|
method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }), method(_void(), array(TerminalInstanceInfoSchema).readonly(), { auth: "admin" }), method(object({
|
|
19704
19809
|
targetNodeId: string().min(1),
|
|
19705
19810
|
profileId: string().min(1),
|
|
19706
|
-
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()
|
|
19707
19828
|
}), TerminalInstanceInfoSchema, {
|
|
19708
19829
|
kind: "mutation",
|
|
19709
19830
|
auth: "admin"
|
|
@@ -19725,7 +19846,11 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
|
|
|
19725
19846
|
}), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
|
|
19726
19847
|
profileId: string(),
|
|
19727
19848
|
cols: number().int().positive(),
|
|
19728
|
-
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()
|
|
19729
19854
|
}), TerminalSessionInfoSchema, {
|
|
19730
19855
|
kind: "mutation",
|
|
19731
19856
|
auth: "admin"
|
|
@@ -22507,10 +22632,10 @@ DeviceType.LawnMower, method(object({ deviceId: number().int().nonnegative() }),
|
|
|
22507
22632
|
*
|
|
22508
22633
|
* • The SDK (mobile / web client) consumes `getConnectionEndpoints()`
|
|
22509
22634
|
* to receive an ordered list of candidate base URLs it should race
|
|
22510
|
-
* on connect — LAN IPv4 first (lowest latency
|
|
22511
|
-
* then public hostname (if a tunnel is
|
|
22512
|
-
* race them with short timeouts and stick with the
|
|
22513
|
-
* 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.
|
|
22514
22639
|
*
|
|
22515
22640
|
* Why hub-only: agents are not directly addressable by the operator's
|
|
22516
22641
|
* clients — they reverse-connect to the hub. Exposing their interfaces
|
|
@@ -22665,6 +22790,17 @@ var NotificationEndpointSchema = object({
|
|
|
22665
22790
|
/** What the ranking currently resolves to (null when nothing is reachable). */
|
|
22666
22791
|
resolved: string().nullable()
|
|
22667
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
|
+
});
|
|
22668
22804
|
var AllowedAddressesSchema = object({
|
|
22669
22805
|
/**
|
|
22670
22806
|
* Allowlist of interface addresses operators have explicitly opted
|
|
@@ -22673,6 +22809,20 @@ var AllowedAddressesSchema = object({
|
|
|
22673
22809
|
* Network Addresses admin page and persisted by the addon.
|
|
22674
22810
|
*/
|
|
22675
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
|
+
});
|
|
22676
22826
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
22677
22827
|
/**
|
|
22678
22828
|
* LEGACY HINT — do not send from new code. Kept optional so clients
|
|
@@ -22682,17 +22832,31 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
22682
22832
|
*/
|
|
22683
22833
|
port: number().int().min(1).max(65535).optional(),
|
|
22684
22834
|
/** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
|
|
22685
|
-
* candidate. Default `
|
|
22835
|
+
* candidate. Default `false` — loopback is not a client route. */
|
|
22686
22836
|
includeLoopback: boolean().optional(),
|
|
22687
|
-
/** Skip IPv6 entries.
|
|
22688
|
-
*
|
|
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`. */
|
|
22689
22840
|
ipv4Only: boolean().optional(),
|
|
22690
22841
|
/** Scheme to emit for LAN/loopback URLs. Default `'http'`.
|
|
22691
22842
|
* Pass `'https'` when the caller is itself loaded over HTTPS
|
|
22692
22843
|
* to avoid mixed-content blocks in the browser. The public
|
|
22693
22844
|
* tunnel always emits `https://` regardless. */
|
|
22694
22845
|
scheme: _enum(["http", "https"]).optional()
|
|
22695
|
-
}), 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
|
+
});
|
|
22696
22860
|
object({
|
|
22697
22861
|
/** Lifecycle state of the lock. `jammed` means the motor reported
|
|
22698
22862
|
* failure to reach the target — operator intervention required. */
|
|
@@ -28455,6 +28619,12 @@ Object.freeze({
|
|
|
28455
28619
|
addonId: null,
|
|
28456
28620
|
access: "create"
|
|
28457
28621
|
},
|
|
28622
|
+
"localNetwork.downloadCa": {
|
|
28623
|
+
capName: "local-network",
|
|
28624
|
+
capScope: "system",
|
|
28625
|
+
addonId: null,
|
|
28626
|
+
access: "view"
|
|
28627
|
+
},
|
|
28458
28628
|
"localNetwork.getAllowedAddresses": {
|
|
28459
28629
|
capName: "local-network",
|
|
28460
28630
|
capScope: "system",
|
|
@@ -28479,18 +28649,42 @@ Object.freeze({
|
|
|
28479
28649
|
addonId: null,
|
|
28480
28650
|
access: "view"
|
|
28481
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
|
+
},
|
|
28482
28664
|
"localNetwork.list": {
|
|
28483
28665
|
capName: "local-network",
|
|
28484
28666
|
capScope: "system",
|
|
28485
28667
|
addonId: null,
|
|
28486
28668
|
access: "view"
|
|
28487
28669
|
},
|
|
28670
|
+
"localNetwork.regenerateCertificate": {
|
|
28671
|
+
capName: "local-network",
|
|
28672
|
+
capScope: "system",
|
|
28673
|
+
addonId: null,
|
|
28674
|
+
access: "create"
|
|
28675
|
+
},
|
|
28488
28676
|
"localNetwork.resetAllowlistToBestMatch": {
|
|
28489
28677
|
capName: "local-network",
|
|
28490
28678
|
capScope: "system",
|
|
28491
28679
|
addonId: null,
|
|
28492
28680
|
access: "delete"
|
|
28493
28681
|
},
|
|
28682
|
+
"localNetwork.revertToGeneratedCertificate": {
|
|
28683
|
+
capName: "local-network",
|
|
28684
|
+
capScope: "system",
|
|
28685
|
+
addonId: null,
|
|
28686
|
+
access: "create"
|
|
28687
|
+
},
|
|
28494
28688
|
"localNetwork.setAllowedAddresses": {
|
|
28495
28689
|
capName: "local-network",
|
|
28496
28690
|
capScope: "system",
|
|
@@ -28503,6 +28697,18 @@ Object.freeze({
|
|
|
28503
28697
|
addonId: null,
|
|
28504
28698
|
access: "create"
|
|
28505
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
|
+
},
|
|
28506
28712
|
"lockControl.lock": {
|
|
28507
28713
|
capName: "lock-control",
|
|
28508
28714
|
capScope: "device",
|
|
@@ -31383,6 +31589,12 @@ Object.freeze({
|
|
|
31383
31589
|
addonId: null,
|
|
31384
31590
|
access: "create"
|
|
31385
31591
|
},
|
|
31592
|
+
"terminalSession.updateInstance": {
|
|
31593
|
+
capName: "terminal-session",
|
|
31594
|
+
capScope: "system",
|
|
31595
|
+
addonId: null,
|
|
31596
|
+
access: "create"
|
|
31597
|
+
},
|
|
31386
31598
|
"terminalSession.writeInput": {
|
|
31387
31599
|
capName: "terminal-session",
|
|
31388
31600
|
capScope: "system",
|
|
@@ -33870,6 +34082,35 @@ Object.freeze(Object.fromEntries([{
|
|
|
33870
34082
|
}]
|
|
33871
34083
|
}].map((s) => [s.stepId, s.defaultModelId])));
|
|
33872
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({});
|
|
33873
34114
|
object({
|
|
33874
34115
|
/**
|
|
33875
34116
|
* Fraction of the box's own size added on EACH side before cutting.
|
|
@@ -34061,113 +34302,14 @@ var RingBuffer = class {
|
|
|
34061
34302
|
* decoder is an explicit opt-in fallback only. A stale/failed settings read at
|
|
34062
34303
|
* boot therefore resolves to node-av (never leaves the node with no decoder). */
|
|
34063
34304
|
var DEFAULT_DECODER_BACKEND = "nodeav";
|
|
34064
|
-
/** Narrow an unknown settings value to a {@link DecoderBackend}, else `null`. */
|
|
34065
|
-
function parseDecoderBackend(value) {
|
|
34066
|
-
return value === "ffmpeg" || value === "nodeav" ? value : null;
|
|
34067
|
-
}
|
|
34068
|
-
/**
|
|
34069
|
-
* Normalise a raw kernel node id to the bare node id used for scoping.
|
|
34070
|
-
* `localNodeId` can carry a `<node>/<addon>` suffix; the decoder selection is
|
|
34071
|
-
* per-NODE, so strip the addon segment. Falls back to `hub`.
|
|
34072
|
-
*/
|
|
34073
|
-
function normalizeDecoderNodeId(rawNodeId) {
|
|
34074
|
-
const raw = rawNodeId ?? "hub";
|
|
34075
|
-
return raw.includes("/") ? raw.split("/")[0] ?? "hub" : raw;
|
|
34076
|
-
}
|
|
34077
34305
|
//#endregion
|
|
34078
34306
|
//#region src/shared/decoder-backend.ts
|
|
34079
|
-
/** The NEUTRAL addon (pipeline-orchestrator, hub-resident) whose global
|
|
34080
|
-
* settings OWN the per-node `backend` selector. Neither decoder addon owns
|
|
34081
|
-
* it, so decoder-nodeav / decoder-ffmpeg stay fully independent. */
|
|
34082
|
-
var DECODER_OWNER_ADDON_ID = "pipeline-orchestrator";
|
|
34083
|
-
function isHydratedField(entry) {
|
|
34084
|
-
return typeof entry === "object" && entry !== null && "key" in entry;
|
|
34085
|
-
}
|
|
34086
|
-
/**
|
|
34087
|
-
* Pure selection from an already-read hydrated settings payload: extract the
|
|
34088
|
-
* owner's `backend` field value (which the owner projected per-node from its
|
|
34089
|
-
* scoped store key) and narrow it. A missing/invalid field or a null payload
|
|
34090
|
-
* resolves to {@link DEFAULT_DECODER_BACKEND} — never a bare store key.
|
|
34091
|
-
*/
|
|
34092
|
-
function pickDecoderBackendFromSettings(view) {
|
|
34093
|
-
if (view === null) return DEFAULT_DECODER_BACKEND;
|
|
34094
|
-
for (const section of view.sections) for (const entry of section.fields) {
|
|
34095
|
-
if (!isHydratedField(entry) || entry.key !== "backend") continue;
|
|
34096
|
-
return parseDecoderBackend(entry.value) ?? "nodeav";
|
|
34097
|
-
}
|
|
34098
|
-
return DEFAULT_DECODER_BACKEND;
|
|
34099
|
-
}
|
|
34100
|
-
/** Missing optional owner fingerprints: ffmpeg may be uninstalled. */
|
|
34101
|
-
function isMissingOwnerSettingsError(message) {
|
|
34102
|
-
return /not routable/i.test(message) || /provider not available/i.test(message);
|
|
34103
|
-
}
|
|
34104
|
-
/** Transient transport/settings-store fingerprints worth retrying on. */
|
|
34105
|
-
function isTransientSettingsError(message) {
|
|
34106
|
-
return /not loaded/i.test(message) || /transport-failed/i.test(message) || /not connected/i.test(message) || /SqliteSettingsBackend not initialized/i.test(message);
|
|
34107
|
-
}
|
|
34108
34307
|
/**
|
|
34109
|
-
* Resolve the decoder backend
|
|
34110
|
-
*
|
|
34111
|
-
*
|
|
34112
|
-
* The read routes to the owner addon's child runner; during a simultaneous
|
|
34113
|
-
* (re)start the owner may not be up yet → a transient `transport-failed (addon
|
|
34114
|
-
* not loaded)`. Immediately defaulting here would ignore an explicit `ffmpeg`
|
|
34115
|
-
* selection (the node would silently run the node-av default instead). So retry
|
|
34116
|
-
* on the transient fingerprints with a bounded budget (mirrors
|
|
34117
|
-
* `BaseAddon.readAddonStoreWithRetry`) until the owner answers; only a
|
|
34118
|
-
* persistent failure falls back to {@link DEFAULT_DECODER_BACKEND}.
|
|
34119
|
-
*
|
|
34120
|
-
* The OWNER addon must NOT call this (it would self-route + deadlock) — it uses
|
|
34121
|
-
* {@link resolveOwnDecoderBackend} against its own store instead.
|
|
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}.
|
|
34122
34311
|
*/
|
|
34123
|
-
async function resolveDecoderBackend(
|
|
34124
|
-
if (!api) {
|
|
34125
|
-
logger.warn("decoder-backend: no api surface — using default backend", { meta: { default: DEFAULT_DECODER_BACKEND } });
|
|
34126
|
-
return DEFAULT_DECODER_BACKEND;
|
|
34127
|
-
}
|
|
34128
|
-
const normalized = normalizeDecoderNodeId(nodeId);
|
|
34129
|
-
const delaysMs = [
|
|
34130
|
-
150,
|
|
34131
|
-
350,
|
|
34132
|
-
600,
|
|
34133
|
-
900,
|
|
34134
|
-
1200
|
|
34135
|
-
];
|
|
34136
|
-
let lastErr;
|
|
34137
|
-
for (let attempt = 0; attempt <= delaysMs.length; attempt++) {
|
|
34138
|
-
try {
|
|
34139
|
-
const view = await api.addonSettings.getGlobalSettings.query({
|
|
34140
|
-
addonId: DECODER_OWNER_ADDON_ID,
|
|
34141
|
-
nodeId: normalized
|
|
34142
|
-
});
|
|
34143
|
-
if (view !== null) return pickDecoderBackendFromSettings(view);
|
|
34144
|
-
lastErr = /* @__PURE__ */ new Error("owner settings unavailable (null)");
|
|
34145
|
-
} catch (err) {
|
|
34146
|
-
lastErr = err;
|
|
34147
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
34148
|
-
if (isTransientSettingsError(msg)) {} else if (isMissingOwnerSettingsError(msg)) {
|
|
34149
|
-
logger.warn("decoder-backend: optional owner unavailable — using default backend", { meta: {
|
|
34150
|
-
default: DEFAULT_DECODER_BACKEND,
|
|
34151
|
-
owner: DECODER_OWNER_ADDON_ID,
|
|
34152
|
-
error: msg
|
|
34153
|
-
} });
|
|
34154
|
-
return DEFAULT_DECODER_BACKEND;
|
|
34155
|
-
} else {
|
|
34156
|
-
logger.warn("decoder-backend: settings read failed — using default backend", { meta: {
|
|
34157
|
-
default: DEFAULT_DECODER_BACKEND,
|
|
34158
|
-
error: msg
|
|
34159
|
-
} });
|
|
34160
|
-
return DEFAULT_DECODER_BACKEND;
|
|
34161
|
-
}
|
|
34162
|
-
}
|
|
34163
|
-
if (attempt === delaysMs.length) break;
|
|
34164
|
-
await new Promise((resolve) => setTimeout(resolve, delaysMs[attempt]));
|
|
34165
|
-
}
|
|
34166
|
-
logger.warn("decoder-backend: owner settings unavailable after retries — using default backend", { meta: {
|
|
34167
|
-
default: DEFAULT_DECODER_BACKEND,
|
|
34168
|
-
owner: DECODER_OWNER_ADDON_ID,
|
|
34169
|
-
error: lastErr instanceof Error ? lastErr.message : String(lastErr)
|
|
34170
|
-
} });
|
|
34312
|
+
async function resolveDecoderBackend(_api, _nodeId, _logger) {
|
|
34171
34313
|
return DEFAULT_DECODER_BACKEND;
|
|
34172
34314
|
}
|
|
34173
34315
|
//#endregion
|