@camstack/addon-post-analysis 1.2.283 → 1.2.285
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/THIRD_PARTY_MODELS.md +233 -0
- package/dist/{dist-zp1v7cCp.mjs → dist-D2tXUMfE.mjs} +1035 -716
- package/dist/{dist-CT5xOY86.js → dist-ITESpHou.js} +1070 -715
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +2 -2
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-9UxXz_Yw.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-7oasHfhD.mjs} +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CjeM5Bph.mjs +26 -0
- package/dist/pipeline-analytics/{_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DWPDYuJS.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BidXbeas.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit-27I-ZEiW.mjs → hostInit-B6m75Rnz.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +10141 -5360
- package/dist/pipeline-analytics/index.mjs +10132 -5351
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +3 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CZkZhroP.mjs +0 -26
|
@@ -5391,7 +5391,7 @@ var ZodIssueCode = {
|
|
|
5391
5391
|
var ZodFirstPartyTypeKind;
|
|
5392
5392
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5393
5393
|
//#endregion
|
|
5394
|
-
//#region ../types/dist/sleep-
|
|
5394
|
+
//#region ../types/dist/sleep-BaRu0bS5.mjs
|
|
5395
5395
|
/**
|
|
5396
5396
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5397
5397
|
* window to float samples (D455).
|
|
@@ -10055,7 +10055,7 @@ var ModelFormatEntrySchema = object({
|
|
|
10055
10055
|
/** Multi-file payload (directory members or sibling files). */
|
|
10056
10056
|
files: array(string()).readonly().optional(),
|
|
10057
10057
|
/** Runtime(s) that can use this format. If omitted, inferred from ModelFormat key */
|
|
10058
|
-
runtimes: array(_enum(["
|
|
10058
|
+
runtimes: array(_enum(["python"])).readonly().optional()
|
|
10059
10059
|
});
|
|
10060
10060
|
/**
|
|
10061
10061
|
* Extra file that must be downloaded alongside the model (e.g., labels JSON, dict.txt).
|
|
@@ -10136,10 +10136,17 @@ var ModelCatalogEntrySchema = object({
|
|
|
10136
10136
|
inputChannels: number().int().positive().optional(),
|
|
10137
10137
|
labels: array(LabelDefinitionSchema).readonly(),
|
|
10138
10138
|
inputLayout: _enum(["nchw", "nhwc"]).optional(),
|
|
10139
|
+
/**
|
|
10140
|
+
* `'scrfd'` applies InsightFace's SCRFD input contract, `(x - 127.5) / 128`
|
|
10141
|
+
* (upstream `insightface/model_zoo/scrfd.py`), instead of the historical
|
|
10142
|
+
* plain `/255`. Measured against COCO GT: 188 → 196 faces found on the
|
|
10143
|
+
* `scrfd-2.5g` catalog entry (2026-09-26 model-replacement spike, §2.4-2).
|
|
10144
|
+
*/
|
|
10139
10145
|
inputNormalization: _enum([
|
|
10140
10146
|
"zero-one",
|
|
10141
10147
|
"imagenet",
|
|
10142
|
-
"none"
|
|
10148
|
+
"none",
|
|
10149
|
+
"scrfd"
|
|
10143
10150
|
]).optional(),
|
|
10144
10151
|
/**
|
|
10145
10152
|
* The model already applies softmax IN-GRAPH — its raw output is a
|
|
@@ -10241,7 +10248,8 @@ var ModelConvertMetadataSchema = object({
|
|
|
10241
10248
|
inputNormalization: _enum([
|
|
10242
10249
|
"zero-one",
|
|
10243
10250
|
"imagenet",
|
|
10244
|
-
"none"
|
|
10251
|
+
"none",
|
|
10252
|
+
"scrfd"
|
|
10245
10253
|
]).optional(),
|
|
10246
10254
|
preprocessMode: _enum(["letterbox", "resize"]).optional(),
|
|
10247
10255
|
outputFormat: _enum([
|
|
@@ -18118,6 +18126,25 @@ var NcScheduleSchema = object({
|
|
|
18118
18126
|
/** Active OUTSIDE the windows (e.g. "only outside business hours"). */
|
|
18119
18127
|
invert: boolean().optional()
|
|
18120
18128
|
});
|
|
18129
|
+
/**
|
|
18130
|
+
* How a system-event rule of the `device-events` or `system` family speaks
|
|
18131
|
+
* (D647): at once, per event (`instant`, the only behaviour before D647), or
|
|
18132
|
+
* as a STATE SUMMARY at the end of each window of `schedule`.
|
|
18133
|
+
*/
|
|
18134
|
+
var NcSystemEventDeliveryModeSchema = _enum(["instant", "summary"]);
|
|
18135
|
+
/**
|
|
18136
|
+
* `summary`: the rule sends NOTHING per event. When a window of `schedule`
|
|
18137
|
+
* CLOSES, it sends one notification with the current state of its kinds
|
|
18138
|
+
* (low batteries, consumables to replace, devices offline, updates
|
|
18139
|
+
* available, ...), after its own filters. Nothing to report sends nothing.
|
|
18140
|
+
* The schedule is the ordinary `NcSchedule` (weekly windows, IANA timezone,
|
|
18141
|
+
* midnight crossing); the send time is each window's END. Refused on the
|
|
18142
|
+
* `alarm` family, which stays instant (`systemEventDeliveryProblems`).
|
|
18143
|
+
*/
|
|
18144
|
+
var NcSystemEventDeliverySchema = discriminatedUnion("mode", [object({ mode: literal("instant") }), object({
|
|
18145
|
+
mode: literal("summary"),
|
|
18146
|
+
schedule: NcScheduleSchema
|
|
18147
|
+
})]);
|
|
18121
18148
|
/** Fuzzy plate matcher — OCR noise makes exact match useless (spec row 12/13). */
|
|
18122
18149
|
var NcPlateMatcherSchema = object({
|
|
18123
18150
|
/**
|
|
@@ -18853,6 +18880,16 @@ var NcRuleInputSchema = object({
|
|
|
18853
18880
|
delivery: NcDeliverySchema,
|
|
18854
18881
|
conditions: NcConditionsSchema.default({}),
|
|
18855
18882
|
schedule: NcScheduleSchema.optional(),
|
|
18883
|
+
/**
|
|
18884
|
+
* A `device-events` / `system` rule's delivery (D647): absent or `instant`
|
|
18885
|
+
* is a notification per event; `summary` is one state summary per closed
|
|
18886
|
+
* window of its schedule. `.optional()` and never defaulted: absent must
|
|
18887
|
+
* keep meaning what every rule authored before D647 meant, with no
|
|
18888
|
+
* migration. Top-level rather than inside `conditions.systemEvent`, so a
|
|
18889
|
+
* client that does not know it (an older viewer re-sending `conditions`
|
|
18890
|
+
* whole) leaves it standing through the store's patch merge.
|
|
18891
|
+
*/
|
|
18892
|
+
systemEventDelivery: NcSystemEventDeliverySchema.optional(),
|
|
18856
18893
|
/** May be empty when `targetUsers` addresses at least one user — the
|
|
18857
18894
|
* "at least one addressee" invariant is enforced by the provider, because
|
|
18858
18895
|
* a cross-field refine here would break `NcRulePatchSchema.partial()`. */
|
|
@@ -19081,6 +19118,28 @@ var NcRuleSchema = NcRuleInputSchema.extend({
|
|
|
19081
19118
|
*/
|
|
19082
19119
|
disabledTargetIds: array(string()).default([])
|
|
19083
19120
|
});
|
|
19121
|
+
/** One section of a previewed state summary. `unavailable` = its state could not be read. */
|
|
19122
|
+
var NcStateSummaryPreviewSectionSchema = object({
|
|
19123
|
+
id: string(),
|
|
19124
|
+
status: _enum(["read", "unavailable"]),
|
|
19125
|
+
items: number().int().nonnegative(),
|
|
19126
|
+
reason: string().optional()
|
|
19127
|
+
});
|
|
19128
|
+
/**
|
|
19129
|
+
* The summary a rule would send now. `empty` = nothing to report and every
|
|
19130
|
+
* section read: such a summary is NOT sent. `dropped` counts the open states
|
|
19131
|
+
* the rule's own filters (or a camera mute) left out, by reason.
|
|
19132
|
+
* `excludedGroups` names the kind groups selected that have no state to report
|
|
19133
|
+
* (backups & exports).
|
|
19134
|
+
*/
|
|
19135
|
+
var NcStateSummaryPreviewSchema = object({
|
|
19136
|
+
empty: boolean(),
|
|
19137
|
+
count: number().int().nonnegative(),
|
|
19138
|
+
text: string(),
|
|
19139
|
+
sections: array(NcStateSummaryPreviewSectionSchema),
|
|
19140
|
+
dropped: record(string(), number().int().nonnegative()),
|
|
19141
|
+
excludedGroups: array(string())
|
|
19142
|
+
});
|
|
19084
19143
|
var NcTestResultSchema = object({
|
|
19085
19144
|
recordId: string(),
|
|
19086
19145
|
recordKind: _enum([
|
|
@@ -19172,6 +19231,13 @@ var NcTemplateVarDescriptorSchema = object({
|
|
|
19172
19231
|
kinds: array(NcTemplateVarKindSchema).optional(),
|
|
19173
19232
|
deliveries: array(NcDeliverySchema).optional(),
|
|
19174
19233
|
systemEventKinds: array(NcSystemEventKindSchema).optional(),
|
|
19234
|
+
/**
|
|
19235
|
+
* Which system-event DELIVERY has a value for it (D647). Absent = an
|
|
19236
|
+
* instant row only when the descriptor names `systemEventKinds` (a
|
|
19237
|
+
* per-event fact: a summary row has no single device), and both otherwise.
|
|
19238
|
+
* `['summary']` marks the state-summary vocabulary (`{{summaryText}}`, ...).
|
|
19239
|
+
*/
|
|
19240
|
+
systemEventDeliveries: array(NcSystemEventDeliveryModeSchema).optional(),
|
|
19175
19241
|
dynamic: object({
|
|
19176
19242
|
prefix: literal("count_"),
|
|
19177
19243
|
from: literal("classes")
|
|
@@ -19184,7 +19250,9 @@ var NcTemplatePreviewInputSchema = object({
|
|
|
19184
19250
|
field: NcTemplateFieldSchema,
|
|
19185
19251
|
kind: NcRuleKindSchema.optional(),
|
|
19186
19252
|
delivery: NcDeliverySchema.optional(),
|
|
19187
|
-
systemEventKinds: array(NcSystemEventKindSchema).optional()
|
|
19253
|
+
systemEventKinds: array(NcSystemEventKindSchema).optional(),
|
|
19254
|
+
/** The rule's system-event delivery (D647); absent = instant. */
|
|
19255
|
+
systemEventDelivery: NcSystemEventDeliveryModeSchema.optional()
|
|
19188
19256
|
}),
|
|
19189
19257
|
template: object({
|
|
19190
19258
|
title: string().max(500).optional(),
|
|
@@ -20318,6 +20386,17 @@ var notificationRulesCapability = {
|
|
|
20318
20386
|
auth: "admin"
|
|
20319
20387
|
}),
|
|
20320
20388
|
/**
|
|
20389
|
+
* The STATE SUMMARY a system rule would send now (D647) — for a saved rule
|
|
20390
|
+
* or an unsaved draft. Pure: sends nothing, stamps nothing. A MUTATION
|
|
20391
|
+
* only for transport (a whole rule does not belong in a GET URL). Admin,
|
|
20392
|
+
* like `testRule`: it names devices and nodes across the installation.
|
|
20393
|
+
*/
|
|
20394
|
+
previewStateSummary: method(object({ rule: NcRuleInputSchema }), NcStateSummaryPreviewSchema, {
|
|
20395
|
+
kind: "mutation",
|
|
20396
|
+
auth: "admin",
|
|
20397
|
+
access: "view"
|
|
20398
|
+
}),
|
|
20399
|
+
/**
|
|
20321
20400
|
* The machine-readable condition surface a rule editor renders from, plus
|
|
20322
20401
|
* the picker VOCABULARY those conditions draw on.
|
|
20323
20402
|
*
|
|
@@ -20762,646 +20841,6 @@ var OauthIntegrationDescriptorSchema = object({
|
|
|
20762
20841
|
});
|
|
20763
20842
|
method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
|
|
20764
20843
|
/**
|
|
20765
|
-
* Reference to the frame's retained NATIVE surface + the parent crop's placement
|
|
20766
|
-
* within the frame, so the executor can re-cut a leaf child ROI at native
|
|
20767
|
-
* resolution on the detail plane. See the `runPipeline` `nativeCropRef` field.
|
|
20768
|
-
*/
|
|
20769
|
-
var NativeCropRefSchema = object({
|
|
20770
|
-
/** Handle keying the retained native surface (node-pinned to its owner). */
|
|
20771
|
-
handle: FrameHandleSchema,
|
|
20772
|
-
/** The parent crop's padded/clamped rectangle in FRAME-space pixels. */
|
|
20773
|
-
cropFrameSpace: object({
|
|
20774
|
-
x: number(),
|
|
20775
|
-
y: number(),
|
|
20776
|
-
w: number(),
|
|
20777
|
-
h: number()
|
|
20778
|
-
})
|
|
20779
|
-
});
|
|
20780
|
-
object({
|
|
20781
|
-
crop: object({
|
|
20782
|
-
left: number(),
|
|
20783
|
-
top: number(),
|
|
20784
|
-
width: number().positive(),
|
|
20785
|
-
height: number().positive()
|
|
20786
|
-
}).optional(),
|
|
20787
|
-
content: object({
|
|
20788
|
-
width: number().int().positive(),
|
|
20789
|
-
height: number().int().positive()
|
|
20790
|
-
}),
|
|
20791
|
-
fit: _enum(["stretch", "contain"]),
|
|
20792
|
-
format: _enum([
|
|
20793
|
-
"rgb",
|
|
20794
|
-
"gray",
|
|
20795
|
-
"jpeg"
|
|
20796
|
-
])
|
|
20797
|
-
});
|
|
20798
|
-
/**
|
|
20799
|
-
* Process-local frame identity. It is serializable so it can ride an in-process
|
|
20800
|
-
* capability call, but `registryId` deliberately prevents resolution in any
|
|
20801
|
-
* other process or execution group.
|
|
20802
|
-
*/
|
|
20803
|
-
var FrameRefSchema = object({
|
|
20804
|
-
registryId: string().min(1),
|
|
20805
|
-
id: string().min(1),
|
|
20806
|
-
width: number().int().positive(),
|
|
20807
|
-
height: number().int().positive(),
|
|
20808
|
-
format: _enum(["rgb", "gray"]),
|
|
20809
|
-
timestamp: number(),
|
|
20810
|
-
capturedAt: number().optional()
|
|
20811
|
-
});
|
|
20812
|
-
var ModelFormatSchema$1 = _enum([
|
|
20813
|
-
"onnx",
|
|
20814
|
-
"coreml",
|
|
20815
|
-
"openvino",
|
|
20816
|
-
"tflite",
|
|
20817
|
-
"pt",
|
|
20818
|
-
"gguf"
|
|
20819
|
-
]);
|
|
20820
|
-
var PipelineSlotSchema = _enum([
|
|
20821
|
-
"detector",
|
|
20822
|
-
"cropper",
|
|
20823
|
-
"classifier",
|
|
20824
|
-
"refiner",
|
|
20825
|
-
"audio-classifier"
|
|
20826
|
-
]);
|
|
20827
|
-
var PipelineEngineChoiceSchema = object({
|
|
20828
|
-
runtime: _enum(["node", "python"]),
|
|
20829
|
-
backend: string(),
|
|
20830
|
-
format: ModelFormatSchema$1,
|
|
20831
|
-
device: string().optional()
|
|
20832
|
-
});
|
|
20833
|
-
var AvailableEngineSchema = object({
|
|
20834
|
-
engine: PipelineEngineChoiceSchema,
|
|
20835
|
-
devices: array(object({
|
|
20836
|
-
id: string(),
|
|
20837
|
-
label: string(),
|
|
20838
|
-
description: string().optional()
|
|
20839
|
-
})).readonly(),
|
|
20840
|
-
defaultDevice: string()
|
|
20841
|
-
});
|
|
20842
|
-
var PipelineDefaultStepSchema = lazy(() => object({
|
|
20843
|
-
addonId: string(),
|
|
20844
|
-
addonName: string(),
|
|
20845
|
-
slot: PipelineSlotSchema,
|
|
20846
|
-
inputClasses: array(string()).readonly(),
|
|
20847
|
-
outputClasses: array(string()).readonly(),
|
|
20848
|
-
enabled: boolean(),
|
|
20849
|
-
modelId: string(),
|
|
20850
|
-
children: array(PipelineDefaultStepSchema).readonly(),
|
|
20851
|
-
group: string().optional(),
|
|
20852
|
-
settings: record(string(), unknown()).optional()
|
|
20853
|
-
}));
|
|
20854
|
-
var PipelineTemplateStepSchema = lazy(() => object({
|
|
20855
|
-
addonId: string(),
|
|
20856
|
-
enabled: boolean(),
|
|
20857
|
-
modelId: string(),
|
|
20858
|
-
children: array(PipelineTemplateStepSchema).readonly(),
|
|
20859
|
-
settings: record(string(), unknown()).optional()
|
|
20860
|
-
}));
|
|
20861
|
-
var PipelineTemplateSchema$1 = object({
|
|
20862
|
-
id: string(),
|
|
20863
|
-
name: string(),
|
|
20864
|
-
createdAt: string(),
|
|
20865
|
-
updatedAt: string(),
|
|
20866
|
-
engine: PipelineEngineChoiceSchema,
|
|
20867
|
-
steps: array(PipelineTemplateStepSchema).readonly()
|
|
20868
|
-
});
|
|
20869
|
-
var PipelineModelOptionSchema = object({
|
|
20870
|
-
id: string(),
|
|
20871
|
-
name: string(),
|
|
20872
|
-
formats: record(string(), object({
|
|
20873
|
-
downloaded: boolean(),
|
|
20874
|
-
sizeMB: number()
|
|
20875
|
-
})),
|
|
20876
|
-
group: ModelVariantGroupSchema.optional(),
|
|
20877
|
-
legacy: boolean().optional(),
|
|
20878
|
-
provider: ModelProviderIdSchema.optional()
|
|
20879
|
-
});
|
|
20880
|
-
var ConfigFieldBridge = custom();
|
|
20881
|
-
var PipelineAddonSchemaSchema = object({
|
|
20882
|
-
id: string(),
|
|
20883
|
-
name: string(),
|
|
20884
|
-
slot: PipelineSlotSchema,
|
|
20885
|
-
inputClasses: array(string()).readonly(),
|
|
20886
|
-
outputClasses: array(string()).readonly(),
|
|
20887
|
-
childSlots: array(PipelineSlotSchema).readonly(),
|
|
20888
|
-
models: array(PipelineModelOptionSchema).readonly(),
|
|
20889
|
-
defaultModelId: string(),
|
|
20890
|
-
defaultModelIdByFormat: record(string(), string()).optional(),
|
|
20891
|
-
enabledByDefault: boolean().optional(),
|
|
20892
|
-
backfillIntoExistingOverrides: boolean().optional(),
|
|
20893
|
-
defaultConfidence: number(),
|
|
20894
|
-
group: string().optional(),
|
|
20895
|
-
configSchema: array(ConfigFieldBridge).readonly().optional()
|
|
20896
|
-
});
|
|
20897
|
-
var PipelineSlotSchemaSchema = object({
|
|
20898
|
-
id: PipelineSlotSchema,
|
|
20899
|
-
label: string(),
|
|
20900
|
-
priority: number(),
|
|
20901
|
-
parentSlot: PipelineSlotSchema.nullable(),
|
|
20902
|
-
addons: array(PipelineAddonSchemaSchema).readonly()
|
|
20903
|
-
});
|
|
20904
|
-
var PipelineSchemaSchema = object({
|
|
20905
|
-
availableEngines: array(AvailableEngineSchema).readonly(),
|
|
20906
|
-
selectedEngine: PipelineEngineChoiceSchema,
|
|
20907
|
-
slots: array(PipelineSlotSchemaSchema).readonly()
|
|
20908
|
-
});
|
|
20909
|
-
var EngineProvisioningSchema = object({
|
|
20910
|
-
runtimeId: _enum([
|
|
20911
|
-
"onnx",
|
|
20912
|
-
"openvino",
|
|
20913
|
-
"coreml",
|
|
20914
|
-
"edgetpu"
|
|
20915
|
-
]).nullable(),
|
|
20916
|
-
device: string().nullable(),
|
|
20917
|
-
state: _enum([
|
|
20918
|
-
"idle",
|
|
20919
|
-
"installing",
|
|
20920
|
-
"verifying",
|
|
20921
|
-
"ready",
|
|
20922
|
-
"failed"
|
|
20923
|
-
]),
|
|
20924
|
-
progress: number().optional(),
|
|
20925
|
-
error: string().optional(),
|
|
20926
|
-
nextRetryAt: number().optional(),
|
|
20927
|
-
/**
|
|
20928
|
-
* Gate A (config-correctness gate at engine change): human-readable
|
|
20929
|
-
* config issues surfaced EAGERLY when the node's engine changes — model
|
|
20930
|
-
* substitutions ("chose X, running Y") and zero-build steps ("no model
|
|
20931
|
-
* has a <format> build"). Additive/optional: informational only, never
|
|
20932
|
-
* enforced here — `assertEngineReady` (readiness) still gates inference.
|
|
20933
|
-
* Absent/empty when the node-default tree resolves cleanly.
|
|
20934
|
-
*/
|
|
20935
|
-
configIssues: array(string()).optional()
|
|
20936
|
-
});
|
|
20937
|
-
var PipelineStepInputSchema = lazy(() => object({
|
|
20938
|
-
addonId: string(),
|
|
20939
|
-
modelId: string().optional(),
|
|
20940
|
-
enabled: boolean().default(true),
|
|
20941
|
-
children: array(PipelineStepInputSchema).optional(),
|
|
20942
|
-
settings: record(string(), unknown()).optional(),
|
|
20943
|
-
jumpDeviceKey: string().optional()
|
|
20944
|
-
}));
|
|
20945
|
-
var ModelSubstitutionSchema = object({
|
|
20946
|
-
addonId: string(),
|
|
20947
|
-
chosen: string(),
|
|
20948
|
-
running: string(),
|
|
20949
|
-
format: string()
|
|
20950
|
-
});
|
|
20951
|
-
var PipelineValidationIssueSchema = object({
|
|
20952
|
-
addonId: string(),
|
|
20953
|
-
kind: _enum(["unknown-addon", "no-format-build"]),
|
|
20954
|
-
detail: string()
|
|
20955
|
-
});
|
|
20956
|
-
var PipelineValidationResultSchema = object({
|
|
20957
|
-
ok: boolean(),
|
|
20958
|
-
issues: array(PipelineValidationIssueSchema).readonly(),
|
|
20959
|
-
substitutions: array(ModelSubstitutionSchema).readonly(),
|
|
20960
|
-
/** The node's `currentEngine.format` this validation ran against. */
|
|
20961
|
-
format: string()
|
|
20962
|
-
});
|
|
20963
|
-
var ReferenceImageEntrySchema = object({
|
|
20964
|
-
filename: string(),
|
|
20965
|
-
stepIds: array(string()).readonly().optional()
|
|
20966
|
-
});
|
|
20967
|
-
var ReferenceImageBodySchema = object({
|
|
20968
|
-
base64: string(),
|
|
20969
|
-
filename: string()
|
|
20970
|
-
});
|
|
20971
|
-
var ReferenceAudioEntrySchema = object({
|
|
20972
|
-
filename: string(),
|
|
20973
|
-
sizeKb: number()
|
|
20974
|
-
});
|
|
20975
|
-
var ReferenceAudioBodySchema = object({ base64: string() });
|
|
20976
|
-
var AudioBackendSchema = object({
|
|
20977
|
-
id: string(),
|
|
20978
|
-
name: string(),
|
|
20979
|
-
description: string(),
|
|
20980
|
-
available: boolean(),
|
|
20981
|
-
/**
|
|
20982
|
-
* Raw classifier labels this backend can emit (e.g. YAMNet's
|
|
20983
|
-
* 521-class set or Apple SoundAnalysis's 303-class set). Used by
|
|
20984
|
-
* the benchmark UI to populate the `enabledMicroClasses` filter
|
|
20985
|
-
* specific to the selected backend without a separate fetch.
|
|
20986
|
-
*/
|
|
20987
|
-
rawLabels: array(string()).readonly().optional()
|
|
20988
|
-
});
|
|
20989
|
-
var AudioCapabilitiesSchema = object({
|
|
20990
|
-
activeBackend: string(),
|
|
20991
|
-
availableBackends: array(AudioBackendSchema).readonly(),
|
|
20992
|
-
sampleRate: number(),
|
|
20993
|
-
chunkDurationMs: number()
|
|
20994
|
-
});
|
|
20995
|
-
var DownloadModelResultSchema = object({
|
|
20996
|
-
filePath: string(),
|
|
20997
|
-
sizeMB: number(),
|
|
20998
|
-
durationMs: number()
|
|
20999
|
-
});
|
|
21000
|
-
/**
|
|
21001
|
-
* Wrapper carrying a single test run's result. Replaces the legacy
|
|
21002
|
-
* ad-hoc `{labels: [{className, originalClass, score}]}` shape with the
|
|
21003
|
-
* canonical `AudioResult` from the Phase 6 output rework: one
|
|
21004
|
-
* `AudioDetection` per class above `minScore`, top-N candidates in
|
|
21005
|
-
* `debug.alternateLabels['audio-classifier']`, per-source timings in
|
|
21006
|
-
* `debug.stepTimings`. The outer `success`/`error` fields stay so the
|
|
21007
|
-
* benchmark UI can still report a clean failure when the classifier
|
|
21008
|
-
* cap isn't available.
|
|
21009
|
-
*/
|
|
21010
|
-
var AudioTestResultSchema = object({
|
|
21011
|
-
success: boolean(),
|
|
21012
|
-
error: string().optional(),
|
|
21013
|
-
frame: custom().optional()
|
|
21014
|
-
});
|
|
21015
|
-
var PipelineConfigBridge = custom();
|
|
21016
|
-
var ConfigUISchemaBridge = custom();
|
|
21017
|
-
var ConfigUISchemaNullableBridge = custom();
|
|
21018
|
-
var InferenceCapabilitiesBridge = custom();
|
|
21019
|
-
var ModelAvailabilityListBridge = custom();
|
|
21020
|
-
var PipelineRunResultBridge = custom();
|
|
21021
|
-
method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
|
|
21022
|
-
modelId: string(),
|
|
21023
|
-
settings: record(string(), unknown()).readonly()
|
|
21024
|
-
}))), method(object({ steps: record(string(), object({
|
|
21025
|
-
modelId: string(),
|
|
21026
|
-
settings: record(string(), unknown()).readonly()
|
|
21027
|
-
})) }), object({ success: literal(true) }), {
|
|
21028
|
-
kind: "mutation",
|
|
21029
|
-
auth: "admin"
|
|
21030
|
-
}), method(object({ nodeId: string() }), object({
|
|
21031
|
-
success: literal(true),
|
|
21032
|
-
clearedDevices: number()
|
|
21033
|
-
}), {
|
|
21034
|
-
kind: "mutation",
|
|
21035
|
-
auth: "admin"
|
|
21036
|
-
}), method(object({ nodeId: string() }), object({ unhealthy: array(object({
|
|
21037
|
-
/** `<backend>:<device>`, e.g. `openvino:gpu`. */
|
|
21038
|
-
deviceKey: string(),
|
|
21039
|
-
/**
|
|
21040
|
-
* `failed` — the per-device restart budget is exhausted; no pool
|
|
21041
|
-
* will be spawned until an operator re-arms it or the runner
|
|
21042
|
-
* respawns. `backoff` — under budget, waiting out the backoff (or
|
|
21043
|
-
* a cached pool observed dead and not yet condemned).
|
|
21044
|
-
*/
|
|
21045
|
-
state: _enum(["failed", "backoff"]),
|
|
21046
|
-
/** Epoch ms of the death that produced this state. */
|
|
21047
|
-
since: number(),
|
|
21048
|
-
/** Pool deaths inside the current window. */
|
|
21049
|
-
deaths: number(),
|
|
21050
|
-
/** The last death's message. */
|
|
21051
|
-
lastError: string()
|
|
21052
|
-
})).readonly() })), method(object({
|
|
21053
|
-
nodeId: string(),
|
|
21054
|
-
deviceKey: string()
|
|
21055
|
-
}), object({ rearmed: boolean() }), {
|
|
21056
|
-
kind: "mutation",
|
|
21057
|
-
auth: "admin"
|
|
21058
|
-
}), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(object({ steps: array(PipelineStepInputSchema) }), PipelineValidationResultSchema), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
|
|
21059
|
-
name: string(),
|
|
21060
|
-
steps: array(PipelineTemplateStepSchema).readonly(),
|
|
21061
|
-
engine: PipelineEngineChoiceSchema
|
|
21062
|
-
}), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({
|
|
21063
|
-
id: string(),
|
|
21064
|
-
name: string().optional(),
|
|
21065
|
-
steps: array(PipelineTemplateStepSchema).readonly().optional()
|
|
21066
|
-
}), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({ id: string() }), _void(), { kind: "mutation" }), method(_void(), InferenceCapabilitiesBridge), method(object({ addonId: string() }), ModelAvailabilityListBridge), method(object({
|
|
21067
|
-
addonId: string(),
|
|
21068
|
-
modelId: string(),
|
|
21069
|
-
format: ModelFormatSchema$1
|
|
21070
|
-
}), DownloadModelResultSchema, { kind: "mutation" }), method(object({
|
|
21071
|
-
addonId: string(),
|
|
21072
|
-
modelId: string(),
|
|
21073
|
-
format: ModelFormatSchema$1
|
|
21074
|
-
}), object({ success: literal(true) }), { kind: "mutation" }), method(object({
|
|
21075
|
-
engine: PipelineEngineChoiceSchema.optional(),
|
|
21076
|
-
steps: array(PipelineStepInputSchema).min(1),
|
|
21077
|
-
frame: FrameInputSchema.optional(),
|
|
21078
|
-
/**
|
|
21079
|
-
* Process-local lazy frame. Valid only when caller and provider resolve
|
|
21080
|
-
* in the same execution-group process; split/cross-node callers use
|
|
21081
|
-
* `frame`/`image` inline compatibility instead.
|
|
21082
|
-
*/
|
|
21083
|
-
frameRef: FrameRefSchema.optional(),
|
|
21084
|
-
/**
|
|
21085
|
-
* CB5 shm passthrough — a `FrameHandle` naming the same ring slot
|
|
21086
|
-
* the decoded pixels live in. One more member of the one-of
|
|
21087
|
-
* frame/frameHandle/image/imageBase64/referenceImage group.
|
|
21088
|
-
*/
|
|
21089
|
-
frameHandle: FrameHandleSchema.optional(),
|
|
21090
|
-
imageBase64: string().optional(),
|
|
21091
|
-
/**
|
|
21092
|
-
* Binary JPEG bytes — preferred over `imageBase64` on internal
|
|
21093
|
-
* hops (hub → forked worker via Moleculer MsgPack) because it
|
|
21094
|
-
* skips the 33% base64 overhead + the per-call base64 decode on
|
|
21095
|
-
* the detection-pipeline worker. Callers can pass either; exactly
|
|
21096
|
-
* one of `frame`/`image`/`imageBase64`/`referenceImage` is required.
|
|
21097
|
-
*/
|
|
21098
|
-
image: _instanceof(Uint8Array).optional(),
|
|
21099
|
-
referenceImage: string().optional(),
|
|
21100
|
-
deviceId: number().optional(),
|
|
21101
|
-
sessionId: string().optional(),
|
|
21102
|
-
/**
|
|
21103
|
-
* Execution plane. 'full' (default) runs the whole tree — benchmark,
|
|
21104
|
-
* reference-image, and detail-subtree calls. 'frame' is the live
|
|
21105
|
-
* per-frame dispatch: ONLY root-plane steps run; crop children
|
|
21106
|
-
* (inputClasses ≠ null) are skipped and served per-track via
|
|
21107
|
-
* pipelineRunner.runDetailSubtree (two-plane design).
|
|
21108
|
-
*/
|
|
21109
|
-
plane: _enum(["full", "frame"]).optional(),
|
|
21110
|
-
/**
|
|
21111
|
-
* Inference-device selector (Phase 2 multi-device). Format
|
|
21112
|
-
* `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
|
|
21113
|
-
* Omitted ⇒ the runner's default device (current single-engine
|
|
21114
|
-
* behaviour). Selects WHICH device pool of the node runs the call.
|
|
21115
|
-
*/
|
|
21116
|
-
deviceKey: string().optional(),
|
|
21117
|
-
/**
|
|
21118
|
-
* Two-plane NATIVE child-crop reference. Set by `runDetailSubtree` ONLY
|
|
21119
|
-
* when the parent crop was resolved from the frame's retained NATIVE
|
|
21120
|
-
* surface (a frameHandle HIT). Lets the executor re-cut a LEAF crop
|
|
21121
|
-
* child's ROI (plate-ocr, face-embedding, leaf classifiers) at native
|
|
21122
|
-
* resolution from that surface — the SAME quality path faces already
|
|
21123
|
-
* had — instead of the downscaled parent tile. `handle` keys the native
|
|
21124
|
-
* surface (node-pinned to its owner); `cropFrameSpace` is the parent
|
|
21125
|
-
* crop's padded/clamped rectangle in FRAME-space pixels, used to compose
|
|
21126
|
-
* the executor's crop-normalized child ROI back into frame-normalized
|
|
21127
|
-
* coordinates. Auxiliary to the image source (`image`/`frame`/…), NOT one
|
|
21128
|
-
* of the mutually-exclusive image inputs. Absent ⇒ tile-crop children
|
|
21129
|
-
* (today's behaviour on the fallback path).
|
|
21130
|
-
*/
|
|
21131
|
-
nativeCropRef: NativeCropRefSchema.optional()
|
|
21132
|
-
}), PipelineRunResultBridge, { kind: "mutation" }), method(object({
|
|
21133
|
-
engine: PipelineEngineChoiceSchema.optional(),
|
|
21134
|
-
steps: array(PipelineStepInputSchema).min(1),
|
|
21135
|
-
frames: array(FrameInputSchema).min(1).max(255),
|
|
21136
|
-
deviceId: number().optional(),
|
|
21137
|
-
sessionId: string().optional(),
|
|
21138
|
-
/**
|
|
21139
|
-
* Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
|
|
21140
|
-
* the batch to the Python pool's bench preprocess cache
|
|
21141
|
-
* (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
|
|
21142
|
-
* preprocessed ONCE and every later inference is a pure-inference cache
|
|
21143
|
-
* hit — the sustained-throughput run measures inference, not
|
|
21144
|
-
* decode+preprocess+infer. Omitted/0 for live frames (all different →
|
|
21145
|
-
* full preprocess every call, correct). Fresh per sustained run;
|
|
21146
|
-
* released via `uncacheFrame`.
|
|
21147
|
-
*/
|
|
21148
|
-
frameId: number().int().nonnegative().optional(),
|
|
21149
|
-
/** Inference-device selector (Phase 2 multi-device); see runPipeline. */
|
|
21150
|
-
deviceKey: string().optional()
|
|
21151
|
-
}), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
|
|
21152
|
-
data: _instanceof(Uint8Array),
|
|
21153
|
-
width: number().int().positive(),
|
|
21154
|
-
height: number().int().positive(),
|
|
21155
|
-
format: _enum([
|
|
21156
|
-
"rgb",
|
|
21157
|
-
"bgr",
|
|
21158
|
-
"gray"
|
|
21159
|
-
])
|
|
21160
|
-
}), object({
|
|
21161
|
-
frameId: number(),
|
|
21162
|
-
width: number(),
|
|
21163
|
-
height: number()
|
|
21164
|
-
}), { kind: "mutation" }), method(object({
|
|
21165
|
-
stepId: string(),
|
|
21166
|
-
frameId: number().int()
|
|
21167
|
-
}), record(string(), unknown()), { kind: "mutation" }), method(object({ frameId: number().int() }), _void(), { kind: "mutation" }), method(_void(), object({
|
|
21168
|
-
batchMode: string(),
|
|
21169
|
-
windowMs: number(),
|
|
21170
|
-
maxBatchSize: number(),
|
|
21171
|
-
concurrency: number()
|
|
21172
|
-
})), method(_void(), array(object({
|
|
21173
|
-
engineKey: string(),
|
|
21174
|
-
engine: PipelineEngineChoiceSchema,
|
|
21175
|
-
modelsLoaded: array(string()).readonly(),
|
|
21176
|
-
inUseByCameras: array(number()).readonly(),
|
|
21177
|
-
/**
|
|
21178
|
-
* Origin of this resident factory.
|
|
21179
|
-
* - `runtime` — main camera-serving engine (no idle TTL).
|
|
21180
|
-
* - `warm-override` — benchmark/test override held in the warm
|
|
21181
|
-
* cache; auto-disposed after the idle TTL.
|
|
21182
|
-
* - `device-pool` — a concurrent per-device pool (Phase 2
|
|
21183
|
-
* multi-device, keyed by `deviceKey`) resolved
|
|
21184
|
-
* via `resolveDeviceFactory`. Runs alongside the
|
|
21185
|
-
* `runtime` engine on a DIFFERENT accelerator
|
|
21186
|
-
* (NPU / iGPU / Coral) — this is how the
|
|
21187
|
-
* Engines tab shows all pools running at once.
|
|
21188
|
-
*/
|
|
21189
|
-
kind: _enum([
|
|
21190
|
-
"runtime",
|
|
21191
|
-
"warm-override",
|
|
21192
|
-
"device-pool"
|
|
21193
|
-
]),
|
|
21194
|
-
/** Native pid of the underlying Python pool (null when no pool). */
|
|
21195
|
-
poolPid: number().nullable(),
|
|
21196
|
-
/** ms since this factory was last used (null when not warm-tracked). */
|
|
21197
|
-
idleMs: number().nullable(),
|
|
21198
|
-
/** Idle TTL after which `warm-override` factories self-evict (null when not applicable). */
|
|
21199
|
-
idleTtlMs: number().nullable()
|
|
21200
|
-
})).readonly()), method(object({ engine: PipelineEngineChoiceSchema }), object({ success: literal(true) }), {
|
|
21201
|
-
kind: "mutation",
|
|
21202
|
-
auth: "admin"
|
|
21203
|
-
}), method(object({
|
|
21204
|
-
engine: PipelineEngineChoiceSchema,
|
|
21205
|
-
force: boolean().optional()
|
|
21206
|
-
}), object({
|
|
21207
|
-
success: boolean(),
|
|
21208
|
-
reason: string().optional()
|
|
21209
|
-
}), {
|
|
21210
|
-
kind: "mutation",
|
|
21211
|
-
auth: "admin"
|
|
21212
|
-
}), method(_void(), array(ReferenceImageEntrySchema).readonly()), method(object({ filename: string() }), ReferenceImageBodySchema.nullable()), method(_void(), array(ReferenceAudioEntrySchema).readonly()), method(object({ filename: string() }), ReferenceAudioBodySchema.nullable()), method(_void(), AudioCapabilitiesSchema), method(object({
|
|
21213
|
-
addonId: string(),
|
|
21214
|
-
modelId: string(),
|
|
21215
|
-
filename: string().optional(),
|
|
21216
|
-
settings: record(string(), unknown()).optional()
|
|
21217
|
-
}), AudioTestResultSchema, { kind: "mutation" }), method(_void(), ConfigUISchemaNullableBridge);
|
|
21218
|
-
/**
|
|
21219
|
-
* Per-stage gating mode applied to the zones a rule references.
|
|
21220
|
-
*
|
|
21221
|
-
* - `include`: the rule contributes to a **whitelist** for its stage.
|
|
21222
|
-
* When at least one `include` rule fires for a stage, only entities
|
|
21223
|
-
* inside one of those zones pass that stage.
|
|
21224
|
-
* - `exclude`: the rule contributes to a **blacklist** for its stage.
|
|
21225
|
-
* Entities inside one of those zones are dropped at that stage.
|
|
21226
|
-
*
|
|
21227
|
-
* `monitor`-style observation (count without filtering) is not a rule
|
|
21228
|
-
* mode — zones without any matching rule are observed naturally by
|
|
21229
|
-
* `zone-analytics` (live snapshot + history), so an "I just want to
|
|
21230
|
-
* count, not filter" use case needs no rule at all.
|
|
21231
|
-
*/
|
|
21232
|
-
var ZoneRuleModeEnum = _enum(["include", "exclude"]);
|
|
21233
|
-
/**
|
|
21234
|
-
* Per-consumer rule that references existing zones (geometry) and
|
|
21235
|
-
* defines how a specific pipeline stage should treat them. Each
|
|
21236
|
-
* consumer addon owns its own `ZoneRule[]` array in its per-device
|
|
21237
|
-
* settings:
|
|
21238
|
-
*
|
|
21239
|
-
* - `addon-motion-wasm` → `motionZoneRules: ZoneRule[]` (motion stage)
|
|
21240
|
-
* - `addon-detection-pipeline` → `detectionZoneRules: ZoneRule[]` (detection stage)
|
|
21241
|
-
* - future: notification rules, audio gating, etc.
|
|
21242
|
-
*
|
|
21243
|
-
* One rule applies to N zones (`zoneIds[]`) so the operator can
|
|
21244
|
-
* express "ignore motion in ALL of {garden, street}" with a single
|
|
21245
|
-
* rule. `classFilter` narrows the rule to specific object classes —
|
|
21246
|
-
* "drop person detections in the street, but keep cars" is one
|
|
21247
|
-
* `exclude` rule with `classFilter: ['person']`.
|
|
21248
|
-
*
|
|
21249
|
-
* `enabled` is a soft toggle — the operator can keep the rule
|
|
21250
|
-
* configured but inert without deleting it.
|
|
21251
|
-
*/
|
|
21252
|
-
var ZoneRuleSchema = object({
|
|
21253
|
-
/** Stable rule id — survives edits, used by the UI for diffing. */
|
|
21254
|
-
id: string(),
|
|
21255
|
-
/** Optional human-readable label rendered in the rule editor. */
|
|
21256
|
-
name: string().optional(),
|
|
21257
|
-
/** Zones this rule targets. The rule's `mode` applies to ALL
|
|
21258
|
-
* listed zones (OR-set: a detection in any one of them counts).
|
|
21259
|
-
* At least one zone id required — a rule with no targets is a
|
|
21260
|
-
* configuration mistake and the form validator rejects it. */
|
|
21261
|
-
zoneIds: array(string()).min(1).readonly(),
|
|
21262
|
-
mode: ZoneRuleModeEnum,
|
|
21263
|
-
/**
|
|
21264
|
-
* Class names this rule applies to. Empty / undefined ⇒ rule
|
|
21265
|
-
* applies to every class. Class strings match the `macroClass`
|
|
21266
|
-
* field on detections (e.g. `person`, `car`, `dog`).
|
|
21267
|
-
*/
|
|
21268
|
-
classFilter: array(string()).readonly().optional(),
|
|
21269
|
-
/**
|
|
21270
|
-
* Minimum bbox/mask overlap (0–1) with any of the rule's zones
|
|
21271
|
-
* required to consider an entity "in the zone". Defaults to the
|
|
21272
|
-
* consumer's stage default when omitted. Kept for back-compat with
|
|
21273
|
-
* existing per-rule overrides; new operators pick the value via
|
|
21274
|
-
* `bboxInclusionPct` (operator-friendly 0–100). Whichever field is
|
|
21275
|
-
* set, the lower-level engine reads it as a 0–1 fraction.
|
|
21276
|
-
*/
|
|
21277
|
-
overlapThreshold: number().min(0).max(1).optional(),
|
|
21278
|
-
/**
|
|
21279
|
-
* Operator-friendly version of `overlapThreshold` — the percentage
|
|
21280
|
-
* of the detection's bbox that must lie inside the zone for the
|
|
21281
|
-
* rule to match. Documented default is 85%; the engine substitutes
|
|
21282
|
-
* that when the field is omitted (kept optional so existing rules
|
|
21283
|
-
* stored without it stay valid).
|
|
21284
|
-
*
|
|
21285
|
-
* When BOTH `overlapThreshold` and `bboxInclusionPct` are set on a
|
|
21286
|
-
* rule, the engine prefers `bboxInclusionPct` because it's the
|
|
21287
|
-
* field exposed in the UI. Internally both feed the same gate.
|
|
21288
|
-
*/
|
|
21289
|
-
bboxInclusionPct: number().min(0).max(100).optional(),
|
|
21290
|
-
/**
|
|
21291
|
-
* When `true` and a detection has a segmentation mask, use the
|
|
21292
|
-
* mask for overlap instead of the bbox. Detection-stage only;
|
|
21293
|
-
* motion rules ignore this field.
|
|
21294
|
-
*/
|
|
21295
|
-
preferMask: boolean().optional(),
|
|
21296
|
-
/**
|
|
21297
|
-
* Soft-toggle: `false` disables the rule without deleting it.
|
|
21298
|
-
* Defaults to `true` so operators creating a rule via the UI
|
|
21299
|
-
* see it active immediately.
|
|
21300
|
-
*/
|
|
21301
|
-
enabled: boolean().default(true)
|
|
21302
|
-
});
|
|
21303
|
-
array(ZoneRuleSchema).readonly();
|
|
21304
|
-
/**
|
|
21305
|
-
* Zone — pure geometry + identity. NO filtering behaviour.
|
|
21306
|
-
*
|
|
21307
|
-
* Zones describe **where** in the frame the operator wants to flag
|
|
21308
|
-
* something; consumer-owned {@link ZoneRule} arrays describe **how**
|
|
21309
|
-
* each pipeline stage uses them. Splitting the two means a single
|
|
21310
|
-
* polygon "Driveway" can simultaneously back a motion-exclude rule,
|
|
21311
|
-
* a detection-include rule on `['car']`, and an occupancy aggregate
|
|
21312
|
-
* — without three duplicated polygons.
|
|
21313
|
-
*
|
|
21314
|
-
* Owned by the orchestrator addon (provider) and mirrored into the
|
|
21315
|
-
* `zones` device-state slice on every mutation. Consumers
|
|
21316
|
-
* (motion-wasm, pipeline-executor, analytics, admin UI) read either
|
|
21317
|
-
* via `api.zones.listZones` (one-shot) or via `dev.state.zones` (live
|
|
21318
|
-
* mirror with `onChanged`).
|
|
21319
|
-
*
|
|
21320
|
-
* Coordinates are normalised fractions of the frame (0–1) so zones
|
|
21321
|
-
* survive resolution changes and stream profile switches.
|
|
21322
|
-
*
|
|
21323
|
-
* `kind` discriminates between full polygons (closed regions used
|
|
21324
|
-
* for intrusion / occupancy filters) and tripwires (open 2-point
|
|
21325
|
-
* line segments used for cross events). Onboard / firmware-reported
|
|
21326
|
-
* zones (Reolink, ONVIF) are out of scope for now — see the deferred
|
|
21327
|
-
* task list.
|
|
21328
|
-
*/
|
|
21329
|
-
var ZoneKindEnum = _enum(["polygon", "tripwire"]);
|
|
21330
|
-
/** Polygon vertex in fraction-of-frame coordinates (0–1). */
|
|
21331
|
-
var PolygonPointSchema = object({
|
|
21332
|
-
x: number(),
|
|
21333
|
-
y: number()
|
|
21334
|
-
});
|
|
21335
|
-
/** A camera detection zone — pure geometry/identity. */
|
|
21336
|
-
var ZoneSchema = object({
|
|
21337
|
-
id: string(),
|
|
21338
|
-
name: string(),
|
|
21339
|
-
kind: ZoneKindEnum.default("polygon"),
|
|
21340
|
-
/** Polygon vertices, fraction of frame (0–1). */
|
|
21341
|
-
polygon: array(PolygonPointSchema).readonly(),
|
|
21342
|
-
/** Visual color for UI rendering. */
|
|
21343
|
-
color: string().default("#3b82f6")
|
|
21344
|
-
});
|
|
21345
|
-
/**
|
|
21346
|
-
* Zones capability — per-camera CRUD over polygon detection zones.
|
|
21347
|
-
*
|
|
21348
|
-
* Provider lives in `addon-pipeline-orchestrator` (hub-only). Persists
|
|
21349
|
-
* to per-device settings and mirrors into the `zones` device-state
|
|
21350
|
-
* slice on every mutation, so downstream consumers can subscribe via
|
|
21351
|
-
* `dev.state.zones.onChanged`.
|
|
21352
|
-
*
|
|
21353
|
-
* The cap surface only handles geometry + identity; filtering
|
|
21354
|
-
* behaviour (per-class, include/exclude, threshold) lives in the
|
|
21355
|
-
* consumer addons' rule arrays — see `ZoneRuleSchema` exported from
|
|
21356
|
-
* `capabilities/schemas/zone-rule.js`.
|
|
21357
|
-
*/
|
|
21358
|
-
var zonesCapability = {
|
|
21359
|
-
name: "zones",
|
|
21360
|
-
scope: "device",
|
|
21361
|
-
mode: "singleton",
|
|
21362
|
-
deviceTypes: [DeviceType.Camera],
|
|
21363
|
-
methods: {
|
|
21364
|
-
listZones: method(object({ deviceId: number() }), array(ZoneSchema).readonly()),
|
|
21365
|
-
addZone: method(object({
|
|
21366
|
-
deviceId: number(),
|
|
21367
|
-
zone: ZoneSchema
|
|
21368
|
-
}), _void(), {
|
|
21369
|
-
kind: "mutation",
|
|
21370
|
-
auth: "admin"
|
|
21371
|
-
}),
|
|
21372
|
-
removeZone: method(object({
|
|
21373
|
-
deviceId: number(),
|
|
21374
|
-
zoneId: string()
|
|
21375
|
-
}), _void(), {
|
|
21376
|
-
kind: "mutation",
|
|
21377
|
-
auth: "admin"
|
|
21378
|
-
}),
|
|
21379
|
-
updateZone: method(object({
|
|
21380
|
-
deviceId: number(),
|
|
21381
|
-
zone: ZoneSchema
|
|
21382
|
-
}), _void(), {
|
|
21383
|
-
kind: "mutation",
|
|
21384
|
-
auth: "admin"
|
|
21385
|
-
})
|
|
21386
|
-
},
|
|
21387
|
-
/**
|
|
21388
|
-
* Runtime-state slice — the live zone catalogue mirrored by the
|
|
21389
|
-
* orchestrator on every CRUD mutation. Consumers read via
|
|
21390
|
-
* `device.state.zones.value` / `.watch(...)` without round-tripping
|
|
21391
|
-
* the cap, and the codegen DeviceProxy auto-wires the reactive
|
|
21392
|
-
* handle. Slice shape is `{ zones: Zone[] }` so future extensions
|
|
21393
|
-
* (e.g. zone groupings) can sit alongside the polygon list.
|
|
21394
|
-
*/
|
|
21395
|
-
runtimeState: object({ zones: array(ZoneSchema).readonly() }),
|
|
21396
|
-
/**
|
|
21397
|
-
* Runtime-state durability: **restored** — written only on operator mutation, so a camera that never had one has nothing to re-derive from. This is the slice `zone-mirror-hydration.ts` exists to paper over.
|
|
21398
|
-
*
|
|
21399
|
-
* See `RuntimeStateDurability`. Enforced by
|
|
21400
|
-
* `scripts/check-runtime-state-durability.ts`.
|
|
21401
|
-
*/
|
|
21402
|
-
durability: "restored"
|
|
21403
|
-
};
|
|
21404
|
-
/**
|
|
21405
20844
|
* pipeline-analytics — device-scoped wrapper cap. Refines raw
|
|
21406
20845
|
* per-frame detections emitted by the pipeline runner into tracked
|
|
21407
20846
|
* objects, per-kind event collections (motion / object / audio), and
|
|
@@ -21972,6 +21411,22 @@ var TrackSchema = object({
|
|
|
21972
21411
|
* omit it. Render nothing when absent.
|
|
21973
21412
|
*/
|
|
21974
21413
|
previewMissReason: string().optional(),
|
|
21414
|
+
/**
|
|
21415
|
+
* The ROOT model that produced this track — the `FrameResult.rootModelId` of
|
|
21416
|
+
* the frame it was BORN on (D646). What a later replay compares against, so
|
|
21417
|
+
* "the original model" is named rather than assumed. A birth fact: never
|
|
21418
|
+
* rewritten by a later frame. **Absent ≠ unknown-model**: a row from before
|
|
21419
|
+
* the column, and a track the camera's own detector produced, carry none.
|
|
21420
|
+
*/
|
|
21421
|
+
modelId: string().optional(),
|
|
21422
|
+
/**
|
|
21423
|
+
* Size (px) of the detection frame this track's `positions[].bbox` live in —
|
|
21424
|
+
* the frame it was born on, else the camera's latest analysis frame at
|
|
21425
|
+
* persist time. Divide a position by these to normalise it. Absent when the
|
|
21426
|
+
* size was never known.
|
|
21427
|
+
*/
|
|
21428
|
+
frameWidth: number().optional(),
|
|
21429
|
+
frameHeight: number().optional(),
|
|
21975
21430
|
...TrackFlagFields,
|
|
21976
21431
|
...TrackRetrainFields
|
|
21977
21432
|
});
|
|
@@ -22978,24 +22433,6 @@ var MediaReclaimStatusSchema = object({
|
|
|
22978
22433
|
finishedAtMs: number().nullable(),
|
|
22979
22434
|
error: string().nullable()
|
|
22980
22435
|
});
|
|
22981
|
-
var ReplayFrameInputSchema = object({
|
|
22982
|
-
timestamp: number(),
|
|
22983
|
-
frame: PipelineRunResultBridge
|
|
22984
|
-
});
|
|
22985
|
-
var RunReplayFrameProcessorResultSchema = object({ tracks: array(object({
|
|
22986
|
-
className: string(),
|
|
22987
|
-
firstSeenMs: number(),
|
|
22988
|
-
lastSeenMs: number(),
|
|
22989
|
-
/** Bbox of the track's FIRST matched detection, pixel-space in the clip's
|
|
22990
|
-
* frame — a representative box for the diff's `(className, window, IoU)`
|
|
22991
|
-
* pairing (`replay-diff.ts`). A replay does not need the full per-frame
|
|
22992
|
-
* trajectory production's `Track.positions` keeps. */
|
|
22993
|
-
bbox: BoundingBoxSchema,
|
|
22994
|
-
/** How many of the input frames this track matched a real detection on
|
|
22995
|
-
* (never a coasted/extrapolated frame) — the replay's own signal for "how
|
|
22996
|
-
* solid is this track", cheaper than re-deriving it from a trajectory. */
|
|
22997
|
-
framesMatched: number().int()
|
|
22998
|
-
})).readonly() });
|
|
22999
22436
|
/**
|
|
23000
22437
|
* The long-term series a camera produces. A closed union so a typo cannot
|
|
23001
22438
|
* invent one, and the ONE declaration of it — the addon's `AnalyticsLtsSeries`
|
|
@@ -23722,32 +23159,6 @@ var pipelineAnalyticsCapability = {
|
|
|
23722
23159
|
kind: "mutation",
|
|
23723
23160
|
auth: "admin"
|
|
23724
23161
|
}),
|
|
23725
|
-
/**
|
|
23726
|
-
* The FrameProcessor pass of a replay run — see the `Replay` section
|
|
23727
|
-
* above this capability's definition for why this is not the
|
|
23728
|
-
* `processFrame` method this file's header says pipeline-analytics does
|
|
23729
|
-
* not have.
|
|
23730
|
-
*
|
|
23731
|
-
* Constructs a FRESH `FrameProcessor` for `(deviceId, source)`, feeds it
|
|
23732
|
-
* `frames` IN THE ORDER GIVEN (the caller is responsible for time
|
|
23733
|
-
* ordering — this method does not sort), and returns the tracks it
|
|
23734
|
-
* produced. Zero persistence: no `TrackStore`, no event bus, no media
|
|
23735
|
-
* capture. `zones` / `detectionRules` are the run's OWN zone set —
|
|
23736
|
-
* typically the camera's real zones plus an ephemeral overlay
|
|
23737
|
-
* (`addon-benchmark`'s `replay-plan.ts`), never read from or written to
|
|
23738
|
-
* the `zones` capability by this method itself.
|
|
23739
|
-
*/
|
|
23740
|
-
runReplayFrameProcessor: method(object({
|
|
23741
|
-
deviceId: number(),
|
|
23742
|
-
source: DetectionSourceSchema,
|
|
23743
|
-
zones: array(ZoneSchema).readonly().optional(),
|
|
23744
|
-
detectionRules: array(ZoneRuleSchema).readonly().optional(),
|
|
23745
|
-
zoneMembershipMinOverlap: number().min(0).max(1).optional(),
|
|
23746
|
-
frames: array(ReplayFrameInputSchema).min(1)
|
|
23747
|
-
}), RunReplayFrameProcessorResultSchema, {
|
|
23748
|
-
kind: "mutation",
|
|
23749
|
-
auth: "admin"
|
|
23750
|
-
}),
|
|
23751
23162
|
/** Every annotation on a track, oldest first. */
|
|
23752
23163
|
listRetrainAnnotations: method(object({ trackId: string() }), array(RetrainAnnotationSchema).readonly(), {
|
|
23753
23164
|
kind: "query",
|
|
@@ -23987,6 +23398,478 @@ var pipelineAnalyticsCapability = {
|
|
|
23987
23398
|
}) }
|
|
23988
23399
|
}
|
|
23989
23400
|
};
|
|
23401
|
+
/**
|
|
23402
|
+
* Reference to the frame's retained NATIVE surface + the parent crop's placement
|
|
23403
|
+
* within the frame, so the executor can re-cut a leaf child ROI at native
|
|
23404
|
+
* resolution on the detail plane. See the `runPipeline` `nativeCropRef` field.
|
|
23405
|
+
*/
|
|
23406
|
+
var NativeCropRefSchema = object({
|
|
23407
|
+
/** Handle keying the retained native surface (node-pinned to its owner). */
|
|
23408
|
+
handle: FrameHandleSchema,
|
|
23409
|
+
/** The parent crop's padded/clamped rectangle in FRAME-space pixels. */
|
|
23410
|
+
cropFrameSpace: object({
|
|
23411
|
+
x: number(),
|
|
23412
|
+
y: number(),
|
|
23413
|
+
w: number(),
|
|
23414
|
+
h: number()
|
|
23415
|
+
})
|
|
23416
|
+
});
|
|
23417
|
+
object({
|
|
23418
|
+
crop: object({
|
|
23419
|
+
left: number(),
|
|
23420
|
+
top: number(),
|
|
23421
|
+
width: number().positive(),
|
|
23422
|
+
height: number().positive()
|
|
23423
|
+
}).optional(),
|
|
23424
|
+
content: object({
|
|
23425
|
+
width: number().int().positive(),
|
|
23426
|
+
height: number().int().positive()
|
|
23427
|
+
}),
|
|
23428
|
+
fit: _enum(["stretch", "contain"]),
|
|
23429
|
+
format: _enum([
|
|
23430
|
+
"rgb",
|
|
23431
|
+
"gray",
|
|
23432
|
+
"jpeg"
|
|
23433
|
+
])
|
|
23434
|
+
});
|
|
23435
|
+
/**
|
|
23436
|
+
* Process-local frame identity. It is serializable so it can ride an in-process
|
|
23437
|
+
* capability call, but `registryId` deliberately prevents resolution in any
|
|
23438
|
+
* other process or execution group.
|
|
23439
|
+
*/
|
|
23440
|
+
var FrameRefSchema = object({
|
|
23441
|
+
registryId: string().min(1),
|
|
23442
|
+
id: string().min(1),
|
|
23443
|
+
width: number().int().positive(),
|
|
23444
|
+
height: number().int().positive(),
|
|
23445
|
+
format: _enum(["rgb", "gray"]),
|
|
23446
|
+
timestamp: number(),
|
|
23447
|
+
capturedAt: number().optional()
|
|
23448
|
+
});
|
|
23449
|
+
var ModelFormatSchema$1 = _enum([
|
|
23450
|
+
"onnx",
|
|
23451
|
+
"coreml",
|
|
23452
|
+
"openvino",
|
|
23453
|
+
"tflite",
|
|
23454
|
+
"pt",
|
|
23455
|
+
"gguf"
|
|
23456
|
+
]);
|
|
23457
|
+
var PipelineSlotSchema = _enum([
|
|
23458
|
+
"detector",
|
|
23459
|
+
"cropper",
|
|
23460
|
+
"classifier",
|
|
23461
|
+
"refiner",
|
|
23462
|
+
"audio-classifier"
|
|
23463
|
+
]);
|
|
23464
|
+
var PipelineEngineChoiceSchema = object({
|
|
23465
|
+
runtime: literal("python"),
|
|
23466
|
+
backend: string(),
|
|
23467
|
+
format: ModelFormatSchema$1,
|
|
23468
|
+
device: string().optional()
|
|
23469
|
+
});
|
|
23470
|
+
/**
|
|
23471
|
+
* One pool's resolved provisioning (`getEffectiveTuning`). `numWorkers` is the
|
|
23472
|
+
* knob that lifts a saturated pool (`inference-devices.md`); `concurrency` is
|
|
23473
|
+
* per-worker in-flight requests. `deviceKey` echoes the pool asked about —
|
|
23474
|
+
* `null` for the node-default pool.
|
|
23475
|
+
*/
|
|
23476
|
+
var EffectiveTuningSchema = object({
|
|
23477
|
+
deviceKey: string().nullable(),
|
|
23478
|
+
runtime: string(),
|
|
23479
|
+
batchMode: string(),
|
|
23480
|
+
windowMs: number(),
|
|
23481
|
+
maxBatchSize: number(),
|
|
23482
|
+
concurrency: number(),
|
|
23483
|
+
numWorkers: number()
|
|
23484
|
+
});
|
|
23485
|
+
var AvailableEngineSchema = object({
|
|
23486
|
+
engine: PipelineEngineChoiceSchema,
|
|
23487
|
+
devices: array(object({
|
|
23488
|
+
id: string(),
|
|
23489
|
+
label: string(),
|
|
23490
|
+
description: string().optional()
|
|
23491
|
+
})).readonly(),
|
|
23492
|
+
defaultDevice: string()
|
|
23493
|
+
});
|
|
23494
|
+
var PipelineDefaultStepSchema = lazy(() => object({
|
|
23495
|
+
addonId: string(),
|
|
23496
|
+
addonName: string(),
|
|
23497
|
+
slot: PipelineSlotSchema,
|
|
23498
|
+
inputClasses: array(string()).readonly(),
|
|
23499
|
+
outputClasses: array(string()).readonly(),
|
|
23500
|
+
enabled: boolean(),
|
|
23501
|
+
modelId: string(),
|
|
23502
|
+
children: array(PipelineDefaultStepSchema).readonly(),
|
|
23503
|
+
group: string().optional(),
|
|
23504
|
+
settings: record(string(), unknown()).optional()
|
|
23505
|
+
}));
|
|
23506
|
+
var PipelineTemplateStepSchema = lazy(() => object({
|
|
23507
|
+
addonId: string(),
|
|
23508
|
+
enabled: boolean(),
|
|
23509
|
+
modelId: string(),
|
|
23510
|
+
children: array(PipelineTemplateStepSchema).readonly(),
|
|
23511
|
+
settings: record(string(), unknown()).optional()
|
|
23512
|
+
}));
|
|
23513
|
+
var PipelineTemplateSchema$1 = object({
|
|
23514
|
+
id: string(),
|
|
23515
|
+
name: string(),
|
|
23516
|
+
createdAt: string(),
|
|
23517
|
+
updatedAt: string(),
|
|
23518
|
+
engine: PipelineEngineChoiceSchema,
|
|
23519
|
+
steps: array(PipelineTemplateStepSchema).readonly()
|
|
23520
|
+
});
|
|
23521
|
+
var PipelineModelOptionSchema = object({
|
|
23522
|
+
id: string(),
|
|
23523
|
+
name: string(),
|
|
23524
|
+
formats: record(string(), object({
|
|
23525
|
+
downloaded: boolean(),
|
|
23526
|
+
sizeMB: number()
|
|
23527
|
+
})),
|
|
23528
|
+
group: ModelVariantGroupSchema.optional(),
|
|
23529
|
+
legacy: boolean().optional(),
|
|
23530
|
+
provider: ModelProviderIdSchema.optional()
|
|
23531
|
+
});
|
|
23532
|
+
var ConfigFieldBridge = custom();
|
|
23533
|
+
var PipelineAddonSchemaSchema = object({
|
|
23534
|
+
id: string(),
|
|
23535
|
+
name: string(),
|
|
23536
|
+
slot: PipelineSlotSchema,
|
|
23537
|
+
inputClasses: array(string()).readonly(),
|
|
23538
|
+
outputClasses: array(string()).readonly(),
|
|
23539
|
+
childSlots: array(PipelineSlotSchema).readonly(),
|
|
23540
|
+
models: array(PipelineModelOptionSchema).readonly(),
|
|
23541
|
+
defaultModelId: string(),
|
|
23542
|
+
defaultModelIdByFormat: record(string(), string()).optional(),
|
|
23543
|
+
enabledByDefault: boolean().optional(),
|
|
23544
|
+
backfillIntoExistingOverrides: boolean().optional(),
|
|
23545
|
+
defaultConfidence: number(),
|
|
23546
|
+
group: string().optional(),
|
|
23547
|
+
configSchema: array(ConfigFieldBridge).readonly().optional()
|
|
23548
|
+
});
|
|
23549
|
+
var PipelineSlotSchemaSchema = object({
|
|
23550
|
+
id: PipelineSlotSchema,
|
|
23551
|
+
label: string(),
|
|
23552
|
+
priority: number(),
|
|
23553
|
+
parentSlot: PipelineSlotSchema.nullable(),
|
|
23554
|
+
addons: array(PipelineAddonSchemaSchema).readonly()
|
|
23555
|
+
});
|
|
23556
|
+
var PipelineSchemaSchema = object({
|
|
23557
|
+
availableEngines: array(AvailableEngineSchema).readonly(),
|
|
23558
|
+
selectedEngine: PipelineEngineChoiceSchema,
|
|
23559
|
+
slots: array(PipelineSlotSchemaSchema).readonly()
|
|
23560
|
+
});
|
|
23561
|
+
var EngineProvisioningSchema = object({
|
|
23562
|
+
runtimeId: _enum([
|
|
23563
|
+
"onnx",
|
|
23564
|
+
"openvino",
|
|
23565
|
+
"coreml",
|
|
23566
|
+
"edgetpu"
|
|
23567
|
+
]).nullable(),
|
|
23568
|
+
device: string().nullable(),
|
|
23569
|
+
state: _enum([
|
|
23570
|
+
"idle",
|
|
23571
|
+
"installing",
|
|
23572
|
+
"verifying",
|
|
23573
|
+
"ready",
|
|
23574
|
+
"failed"
|
|
23575
|
+
]),
|
|
23576
|
+
progress: number().optional(),
|
|
23577
|
+
error: string().optional(),
|
|
23578
|
+
nextRetryAt: number().optional(),
|
|
23579
|
+
/**
|
|
23580
|
+
* Gate A (config-correctness gate at engine change): human-readable
|
|
23581
|
+
* config issues surfaced EAGERLY when the node's engine changes — model
|
|
23582
|
+
* substitutions ("chose X, running Y") and zero-build steps ("no model
|
|
23583
|
+
* has a <format> build"). Additive/optional: informational only, never
|
|
23584
|
+
* enforced here — `assertEngineReady` (readiness) still gates inference.
|
|
23585
|
+
* Absent/empty when the node-default tree resolves cleanly.
|
|
23586
|
+
*/
|
|
23587
|
+
configIssues: array(string()).optional()
|
|
23588
|
+
});
|
|
23589
|
+
var PipelineStepInputSchema = lazy(() => object({
|
|
23590
|
+
addonId: string(),
|
|
23591
|
+
modelId: string().optional(),
|
|
23592
|
+
enabled: boolean().default(true),
|
|
23593
|
+
children: array(PipelineStepInputSchema).optional(),
|
|
23594
|
+
settings: record(string(), unknown()).optional(),
|
|
23595
|
+
jumpDeviceKey: string().optional()
|
|
23596
|
+
}));
|
|
23597
|
+
var ModelSubstitutionSchema = object({
|
|
23598
|
+
addonId: string(),
|
|
23599
|
+
chosen: string(),
|
|
23600
|
+
running: string(),
|
|
23601
|
+
format: string()
|
|
23602
|
+
});
|
|
23603
|
+
var PipelineValidationIssueSchema = object({
|
|
23604
|
+
addonId: string(),
|
|
23605
|
+
kind: _enum([
|
|
23606
|
+
"unknown-addon",
|
|
23607
|
+
"no-format-build",
|
|
23608
|
+
"unknown-device"
|
|
23609
|
+
]),
|
|
23610
|
+
detail: string()
|
|
23611
|
+
});
|
|
23612
|
+
var PipelineValidationResultSchema = object({
|
|
23613
|
+
ok: boolean(),
|
|
23614
|
+
issues: array(PipelineValidationIssueSchema).readonly(),
|
|
23615
|
+
substitutions: array(ModelSubstitutionSchema).readonly(),
|
|
23616
|
+
/** The node's `currentEngine.format` this validation ran against. */
|
|
23617
|
+
format: string()
|
|
23618
|
+
});
|
|
23619
|
+
var ReferenceImageEntrySchema = object({
|
|
23620
|
+
filename: string(),
|
|
23621
|
+
stepIds: array(string()).readonly().optional()
|
|
23622
|
+
});
|
|
23623
|
+
var ReferenceImageBodySchema = object({
|
|
23624
|
+
base64: string(),
|
|
23625
|
+
filename: string()
|
|
23626
|
+
});
|
|
23627
|
+
var ReferenceAudioEntrySchema = object({
|
|
23628
|
+
filename: string(),
|
|
23629
|
+
sizeKb: number()
|
|
23630
|
+
});
|
|
23631
|
+
var ReferenceAudioBodySchema = object({ base64: string() });
|
|
23632
|
+
var AudioBackendSchema = object({
|
|
23633
|
+
id: string(),
|
|
23634
|
+
name: string(),
|
|
23635
|
+
description: string(),
|
|
23636
|
+
available: boolean(),
|
|
23637
|
+
/**
|
|
23638
|
+
* Raw classifier labels this backend can emit (e.g. YAMNet's
|
|
23639
|
+
* 521-class set or Apple SoundAnalysis's 303-class set). Used by
|
|
23640
|
+
* the benchmark UI to populate the `enabledMicroClasses` filter
|
|
23641
|
+
* specific to the selected backend without a separate fetch.
|
|
23642
|
+
*/
|
|
23643
|
+
rawLabels: array(string()).readonly().optional()
|
|
23644
|
+
});
|
|
23645
|
+
var AudioCapabilitiesSchema = object({
|
|
23646
|
+
activeBackend: string(),
|
|
23647
|
+
availableBackends: array(AudioBackendSchema).readonly(),
|
|
23648
|
+
sampleRate: number(),
|
|
23649
|
+
chunkDurationMs: number()
|
|
23650
|
+
});
|
|
23651
|
+
var DownloadModelResultSchema = object({
|
|
23652
|
+
filePath: string(),
|
|
23653
|
+
sizeMB: number(),
|
|
23654
|
+
durationMs: number()
|
|
23655
|
+
});
|
|
23656
|
+
/**
|
|
23657
|
+
* Wrapper carrying a single test run's result. Replaces the legacy
|
|
23658
|
+
* ad-hoc `{labels: [{className, originalClass, score}]}` shape with the
|
|
23659
|
+
* canonical `AudioResult` from the Phase 6 output rework: one
|
|
23660
|
+
* `AudioDetection` per class above `minScore`, top-N candidates in
|
|
23661
|
+
* `debug.alternateLabels['audio-classifier']`, per-source timings in
|
|
23662
|
+
* `debug.stepTimings`. The outer `success`/`error` fields stay so the
|
|
23663
|
+
* benchmark UI can still report a clean failure when the classifier
|
|
23664
|
+
* cap isn't available.
|
|
23665
|
+
*/
|
|
23666
|
+
var AudioTestResultSchema = object({
|
|
23667
|
+
success: boolean(),
|
|
23668
|
+
error: string().optional(),
|
|
23669
|
+
frame: custom().optional()
|
|
23670
|
+
});
|
|
23671
|
+
var PipelineConfigBridge = custom();
|
|
23672
|
+
var ConfigUISchemaBridge = custom();
|
|
23673
|
+
var ConfigUISchemaNullableBridge = custom();
|
|
23674
|
+
var InferenceCapabilitiesBridge = custom();
|
|
23675
|
+
var ModelAvailabilityListBridge = custom();
|
|
23676
|
+
var PipelineRunResultBridge = custom();
|
|
23677
|
+
method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
|
|
23678
|
+
modelId: string(),
|
|
23679
|
+
settings: record(string(), unknown()).readonly()
|
|
23680
|
+
}))), method(object({ steps: record(string(), object({
|
|
23681
|
+
modelId: string(),
|
|
23682
|
+
settings: record(string(), unknown()).readonly()
|
|
23683
|
+
})) }), object({ success: literal(true) }), {
|
|
23684
|
+
kind: "mutation",
|
|
23685
|
+
auth: "admin"
|
|
23686
|
+
}), method(object({ nodeId: string() }), object({
|
|
23687
|
+
success: literal(true),
|
|
23688
|
+
clearedDevices: number()
|
|
23689
|
+
}), {
|
|
23690
|
+
kind: "mutation",
|
|
23691
|
+
auth: "admin"
|
|
23692
|
+
}), method(object({ nodeId: string() }), object({ unhealthy: array(object({
|
|
23693
|
+
/** `<backend>:<device>`, e.g. `openvino:gpu`. */
|
|
23694
|
+
deviceKey: string(),
|
|
23695
|
+
/**
|
|
23696
|
+
* `failed` — the per-device restart budget is exhausted; no pool
|
|
23697
|
+
* will be spawned until an operator re-arms it or the runner
|
|
23698
|
+
* respawns. `backoff` — under budget, waiting out the backoff (or
|
|
23699
|
+
* a cached pool observed dead and not yet condemned).
|
|
23700
|
+
*/
|
|
23701
|
+
state: _enum(["failed", "backoff"]),
|
|
23702
|
+
/** Epoch ms of the death that produced this state. */
|
|
23703
|
+
since: number(),
|
|
23704
|
+
/** Pool deaths inside the current window. */
|
|
23705
|
+
deaths: number(),
|
|
23706
|
+
/** The last death's message. */
|
|
23707
|
+
lastError: string()
|
|
23708
|
+
})).readonly() })), method(object({
|
|
23709
|
+
nodeId: string(),
|
|
23710
|
+
deviceKey: string()
|
|
23711
|
+
}), object({ rearmed: boolean() }), {
|
|
23712
|
+
kind: "mutation",
|
|
23713
|
+
auth: "admin"
|
|
23714
|
+
}), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(object({
|
|
23715
|
+
steps: array(PipelineStepInputSchema),
|
|
23716
|
+
deviceKey: string().optional()
|
|
23717
|
+
}), PipelineValidationResultSchema), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
|
|
23718
|
+
name: string(),
|
|
23719
|
+
steps: array(PipelineTemplateStepSchema).readonly(),
|
|
23720
|
+
engine: PipelineEngineChoiceSchema
|
|
23721
|
+
}), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({
|
|
23722
|
+
id: string(),
|
|
23723
|
+
name: string().optional(),
|
|
23724
|
+
steps: array(PipelineTemplateStepSchema).readonly().optional()
|
|
23725
|
+
}), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({ id: string() }), _void(), { kind: "mutation" }), method(_void(), InferenceCapabilitiesBridge), method(object({ addonId: string() }), ModelAvailabilityListBridge), method(object({
|
|
23726
|
+
addonId: string(),
|
|
23727
|
+
modelId: string(),
|
|
23728
|
+
format: ModelFormatSchema$1
|
|
23729
|
+
}), DownloadModelResultSchema, { kind: "mutation" }), method(object({
|
|
23730
|
+
addonId: string(),
|
|
23731
|
+
modelId: string(),
|
|
23732
|
+
format: ModelFormatSchema$1
|
|
23733
|
+
}), object({ success: literal(true) }), { kind: "mutation" }), method(object({
|
|
23734
|
+
steps: array(PipelineStepInputSchema).min(1),
|
|
23735
|
+
frame: FrameInputSchema.optional(),
|
|
23736
|
+
/**
|
|
23737
|
+
* Process-local lazy frame. Valid only when caller and provider resolve
|
|
23738
|
+
* in the same execution-group process; split/cross-node callers use
|
|
23739
|
+
* `frame`/`image` inline compatibility instead.
|
|
23740
|
+
*/
|
|
23741
|
+
frameRef: FrameRefSchema.optional(),
|
|
23742
|
+
/**
|
|
23743
|
+
* CB5 shm passthrough — a `FrameHandle` naming the same ring slot
|
|
23744
|
+
* the decoded pixels live in. One more member of the one-of
|
|
23745
|
+
* frame/frameHandle/image/imageBase64/referenceImage group.
|
|
23746
|
+
*/
|
|
23747
|
+
frameHandle: FrameHandleSchema.optional(),
|
|
23748
|
+
imageBase64: string().optional(),
|
|
23749
|
+
/**
|
|
23750
|
+
* Binary JPEG bytes — preferred over `imageBase64` on internal
|
|
23751
|
+
* hops (hub → forked worker via Moleculer MsgPack) because it
|
|
23752
|
+
* skips the 33% base64 overhead + the per-call base64 decode on
|
|
23753
|
+
* the detection-pipeline worker. Callers can pass either; exactly
|
|
23754
|
+
* one of `frame`/`image`/`imageBase64`/`referenceImage` is required.
|
|
23755
|
+
*/
|
|
23756
|
+
image: _instanceof(Uint8Array).optional(),
|
|
23757
|
+
referenceImage: string().optional(),
|
|
23758
|
+
deviceId: number().optional(),
|
|
23759
|
+
sessionId: string().optional(),
|
|
23760
|
+
/**
|
|
23761
|
+
* Execution plane. 'full' (default) runs the whole tree — benchmark,
|
|
23762
|
+
* reference-image, and detail-subtree calls. 'frame' is the live
|
|
23763
|
+
* per-frame dispatch: ONLY root-plane steps run; crop children
|
|
23764
|
+
* (inputClasses ≠ null) are skipped and served per-track via
|
|
23765
|
+
* pipelineRunner.runDetailSubtree (two-plane design).
|
|
23766
|
+
*/
|
|
23767
|
+
plane: _enum(["full", "frame"]).optional(),
|
|
23768
|
+
/**
|
|
23769
|
+
* Inference-device selector (Phase 2 multi-device). Format
|
|
23770
|
+
* `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
|
|
23771
|
+
* Omitted ⇒ the runner's default device (current single-engine
|
|
23772
|
+
* behaviour). Selects WHICH device pool of the node runs the call.
|
|
23773
|
+
*/
|
|
23774
|
+
deviceKey: string().optional(),
|
|
23775
|
+
/**
|
|
23776
|
+
* Two-plane NATIVE child-crop reference. Set by `runDetailSubtree` ONLY
|
|
23777
|
+
* when the parent crop was resolved from the frame's retained NATIVE
|
|
23778
|
+
* surface (a frameHandle HIT). Lets the executor re-cut a LEAF crop
|
|
23779
|
+
* child's ROI (plate-ocr, face-embedding, leaf classifiers) at native
|
|
23780
|
+
* resolution from that surface — the SAME quality path faces already
|
|
23781
|
+
* had — instead of the downscaled parent tile. `handle` keys the native
|
|
23782
|
+
* surface (node-pinned to its owner); `cropFrameSpace` is the parent
|
|
23783
|
+
* crop's padded/clamped rectangle in FRAME-space pixels, used to compose
|
|
23784
|
+
* the executor's crop-normalized child ROI back into frame-normalized
|
|
23785
|
+
* coordinates. Auxiliary to the image source (`image`/`frame`/…), NOT one
|
|
23786
|
+
* of the mutually-exclusive image inputs. Absent ⇒ tile-crop children
|
|
23787
|
+
* (today's behaviour on the fallback path).
|
|
23788
|
+
*/
|
|
23789
|
+
nativeCropRef: NativeCropRefSchema.optional(),
|
|
23790
|
+
/**
|
|
23791
|
+
* Offline REPLAY of stored media for `deviceId` (D56, D646): the
|
|
23792
|
+
* camera's zone gate and overrides apply as live, every executing
|
|
23793
|
+
* step runs EXACTLY the model it names on the `deviceKey` pool it
|
|
23794
|
+
* names or the call refuses (`model-not-servable` /
|
|
23795
|
+
* `device-not-servable`), and nothing reaches the camera's live
|
|
23796
|
+
* trace stream. Absent ⇒ unchanged behaviour.
|
|
23797
|
+
*/
|
|
23798
|
+
replay: literal(true).optional()
|
|
23799
|
+
}), PipelineRunResultBridge, { kind: "mutation" }), method(object({
|
|
23800
|
+
steps: array(PipelineStepInputSchema).min(1),
|
|
23801
|
+
frames: array(FrameInputSchema).min(1).max(255),
|
|
23802
|
+
deviceId: number().optional(),
|
|
23803
|
+
sessionId: string().optional(),
|
|
23804
|
+
/**
|
|
23805
|
+
* Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
|
|
23806
|
+
* the batch to the Python pool's bench preprocess cache
|
|
23807
|
+
* (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
|
|
23808
|
+
* preprocessed ONCE and every later inference is a pure-inference cache
|
|
23809
|
+
* hit — the sustained-throughput run measures inference, not
|
|
23810
|
+
* decode+preprocess+infer. Omitted/0 for live frames (all different →
|
|
23811
|
+
* full preprocess every call, correct). Fresh per sustained run;
|
|
23812
|
+
* released via `uncacheFrame`.
|
|
23813
|
+
*/
|
|
23814
|
+
frameId: number().int().nonnegative().optional(),
|
|
23815
|
+
/** Inference-device selector (Phase 2 multi-device); see runPipeline. */
|
|
23816
|
+
deviceKey: string().optional()
|
|
23817
|
+
}), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
|
|
23818
|
+
data: _instanceof(Uint8Array),
|
|
23819
|
+
width: number().int().positive(),
|
|
23820
|
+
height: number().int().positive(),
|
|
23821
|
+
format: _enum([
|
|
23822
|
+
"rgb",
|
|
23823
|
+
"bgr",
|
|
23824
|
+
"gray"
|
|
23825
|
+
])
|
|
23826
|
+
}), object({
|
|
23827
|
+
frameId: number(),
|
|
23828
|
+
width: number(),
|
|
23829
|
+
height: number()
|
|
23830
|
+
}), { kind: "mutation" }), method(object({
|
|
23831
|
+
stepId: string(),
|
|
23832
|
+
frameId: number().int()
|
|
23833
|
+
}), record(string(), unknown()), { kind: "mutation" }), method(object({ frameId: number().int() }), _void(), { kind: "mutation" }), method(object({ deviceKey: string().optional() }), EffectiveTuningSchema), method(_void(), array(object({
|
|
23834
|
+
engineKey: string(),
|
|
23835
|
+
engine: PipelineEngineChoiceSchema,
|
|
23836
|
+
modelsLoaded: array(string()).readonly(),
|
|
23837
|
+
inUseByCameras: array(number()).readonly(),
|
|
23838
|
+
/**
|
|
23839
|
+
* Origin of this resident factory.
|
|
23840
|
+
* - `runtime` — main camera-serving engine (no idle TTL).
|
|
23841
|
+
* - `device-pool` — a concurrent per-device pool (Phase 2
|
|
23842
|
+
* multi-device, keyed by `deviceKey`) resolved
|
|
23843
|
+
* via `resolveDeviceFactory`. Runs alongside the
|
|
23844
|
+
* `runtime` engine on a DIFFERENT accelerator
|
|
23845
|
+
* (NPU / iGPU / Coral) — this is how the
|
|
23846
|
+
* Engines tab shows all pools running at once.
|
|
23847
|
+
*/
|
|
23848
|
+
kind: _enum(["runtime", "device-pool"]),
|
|
23849
|
+
/** Native pid of the underlying Python pool (null when no pool). */
|
|
23850
|
+
poolPid: number().nullable(),
|
|
23851
|
+
/** ms since this factory was last used (null when not warm-tracked). */
|
|
23852
|
+
idleMs: number().nullable(),
|
|
23853
|
+
/** Idle TTL after which the factory self-evicts (null when not applicable). */
|
|
23854
|
+
idleTtlMs: number().nullable()
|
|
23855
|
+
})).readonly()), method(object({ engine: PipelineEngineChoiceSchema }), object({ success: literal(true) }), {
|
|
23856
|
+
kind: "mutation",
|
|
23857
|
+
auth: "admin"
|
|
23858
|
+
}), method(object({
|
|
23859
|
+
engine: PipelineEngineChoiceSchema,
|
|
23860
|
+
force: boolean().optional()
|
|
23861
|
+
}), object({
|
|
23862
|
+
success: boolean(),
|
|
23863
|
+
reason: string().optional()
|
|
23864
|
+
}), {
|
|
23865
|
+
kind: "mutation",
|
|
23866
|
+
auth: "admin"
|
|
23867
|
+
}), method(_void(), array(ReferenceImageEntrySchema).readonly()), method(object({ filename: string() }), ReferenceImageBodySchema.nullable()), method(_void(), array(ReferenceAudioEntrySchema).readonly()), method(object({ filename: string() }), ReferenceAudioBodySchema.nullable()), method(_void(), AudioCapabilitiesSchema), method(object({
|
|
23868
|
+
addonId: string(),
|
|
23869
|
+
modelId: string(),
|
|
23870
|
+
filename: string().optional(),
|
|
23871
|
+
settings: record(string(), unknown()).optional()
|
|
23872
|
+
}), AudioTestResultSchema, { kind: "mutation" }), method(_void(), ConfigUISchemaNullableBridge);
|
|
23990
23873
|
object({
|
|
23991
23874
|
activeCameras: number(),
|
|
23992
23875
|
throttledCameras: number(),
|
|
@@ -24012,6 +23895,106 @@ var CameraMetricsSchema = object({
|
|
|
24012
23895
|
});
|
|
24013
23896
|
var CameraMetricsWithDeviceIdSchema = CameraMetricsSchema.extend({ deviceId: number() });
|
|
24014
23897
|
/**
|
|
23898
|
+
* Zone — pure geometry + identity. NO filtering behaviour.
|
|
23899
|
+
*
|
|
23900
|
+
* Zones describe **where** in the frame the operator wants to flag
|
|
23901
|
+
* something; consumer-owned {@link ZoneRule} arrays describe **how**
|
|
23902
|
+
* each pipeline stage uses them. Splitting the two means a single
|
|
23903
|
+
* polygon "Driveway" can simultaneously back a motion-exclude rule,
|
|
23904
|
+
* a detection-include rule on `['car']`, and an occupancy aggregate
|
|
23905
|
+
* — without three duplicated polygons.
|
|
23906
|
+
*
|
|
23907
|
+
* Owned by the orchestrator addon (provider) and mirrored into the
|
|
23908
|
+
* `zones` device-state slice on every mutation. Consumers
|
|
23909
|
+
* (motion-wasm, pipeline-executor, analytics, admin UI) read either
|
|
23910
|
+
* via `api.zones.listZones` (one-shot) or via `dev.state.zones` (live
|
|
23911
|
+
* mirror with `onChanged`).
|
|
23912
|
+
*
|
|
23913
|
+
* Coordinates are normalised fractions of the frame (0–1) so zones
|
|
23914
|
+
* survive resolution changes and stream profile switches.
|
|
23915
|
+
*
|
|
23916
|
+
* `kind` discriminates between full polygons (closed regions used
|
|
23917
|
+
* for intrusion / occupancy filters) and tripwires (open 2-point
|
|
23918
|
+
* line segments used for cross events). Onboard / firmware-reported
|
|
23919
|
+
* zones (Reolink, ONVIF) are out of scope for now — see the deferred
|
|
23920
|
+
* task list.
|
|
23921
|
+
*/
|
|
23922
|
+
var ZoneKindEnum = _enum(["polygon", "tripwire"]);
|
|
23923
|
+
/** Polygon vertex in fraction-of-frame coordinates (0–1). */
|
|
23924
|
+
var PolygonPointSchema = object({
|
|
23925
|
+
x: number(),
|
|
23926
|
+
y: number()
|
|
23927
|
+
});
|
|
23928
|
+
/** A camera detection zone — pure geometry/identity. */
|
|
23929
|
+
var ZoneSchema = object({
|
|
23930
|
+
id: string(),
|
|
23931
|
+
name: string(),
|
|
23932
|
+
kind: ZoneKindEnum.default("polygon"),
|
|
23933
|
+
/** Polygon vertices, fraction of frame (0–1). */
|
|
23934
|
+
polygon: array(PolygonPointSchema).readonly(),
|
|
23935
|
+
/** Visual color for UI rendering. */
|
|
23936
|
+
color: string().default("#3b82f6")
|
|
23937
|
+
});
|
|
23938
|
+
/**
|
|
23939
|
+
* Zones capability — per-camera CRUD over polygon detection zones.
|
|
23940
|
+
*
|
|
23941
|
+
* Provider lives in `addon-pipeline-orchestrator` (hub-only). Persists
|
|
23942
|
+
* to per-device settings and mirrors into the `zones` device-state
|
|
23943
|
+
* slice on every mutation, so downstream consumers can subscribe via
|
|
23944
|
+
* `dev.state.zones.onChanged`.
|
|
23945
|
+
*
|
|
23946
|
+
* The cap surface only handles geometry + identity; filtering
|
|
23947
|
+
* behaviour (per-class, include/exclude, threshold) lives in the
|
|
23948
|
+
* consumer addons' rule arrays — see `ZoneRuleSchema` exported from
|
|
23949
|
+
* `capabilities/schemas/zone-rule.js`.
|
|
23950
|
+
*/
|
|
23951
|
+
var zonesCapability = {
|
|
23952
|
+
name: "zones",
|
|
23953
|
+
scope: "device",
|
|
23954
|
+
mode: "singleton",
|
|
23955
|
+
deviceTypes: [DeviceType.Camera],
|
|
23956
|
+
methods: {
|
|
23957
|
+
listZones: method(object({ deviceId: number() }), array(ZoneSchema).readonly()),
|
|
23958
|
+
addZone: method(object({
|
|
23959
|
+
deviceId: number(),
|
|
23960
|
+
zone: ZoneSchema
|
|
23961
|
+
}), _void(), {
|
|
23962
|
+
kind: "mutation",
|
|
23963
|
+
auth: "admin"
|
|
23964
|
+
}),
|
|
23965
|
+
removeZone: method(object({
|
|
23966
|
+
deviceId: number(),
|
|
23967
|
+
zoneId: string()
|
|
23968
|
+
}), _void(), {
|
|
23969
|
+
kind: "mutation",
|
|
23970
|
+
auth: "admin"
|
|
23971
|
+
}),
|
|
23972
|
+
updateZone: method(object({
|
|
23973
|
+
deviceId: number(),
|
|
23974
|
+
zone: ZoneSchema
|
|
23975
|
+
}), _void(), {
|
|
23976
|
+
kind: "mutation",
|
|
23977
|
+
auth: "admin"
|
|
23978
|
+
})
|
|
23979
|
+
},
|
|
23980
|
+
/**
|
|
23981
|
+
* Runtime-state slice — the live zone catalogue mirrored by the
|
|
23982
|
+
* orchestrator on every CRUD mutation. Consumers read via
|
|
23983
|
+
* `device.state.zones.value` / `.watch(...)` without round-tripping
|
|
23984
|
+
* the cap, and the codegen DeviceProxy auto-wires the reactive
|
|
23985
|
+
* handle. Slice shape is `{ zones: Zone[] }` so future extensions
|
|
23986
|
+
* (e.g. zone groupings) can sit alongside the polygon list.
|
|
23987
|
+
*/
|
|
23988
|
+
runtimeState: object({ zones: array(ZoneSchema).readonly() }),
|
|
23989
|
+
/**
|
|
23990
|
+
* Runtime-state durability: **restored** — written only on operator mutation, so a camera that never had one has nothing to re-derive from. This is the slice `zone-mirror-hydration.ts` exists to paper over.
|
|
23991
|
+
*
|
|
23992
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
23993
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
23994
|
+
*/
|
|
23995
|
+
durability: "restored"
|
|
23996
|
+
};
|
|
23997
|
+
/**
|
|
24015
23998
|
* A bounding box in NORMALIZED [0,1] frame coordinates for `getNativeCrop`. The
|
|
24016
23999
|
* decode worker resolves it against the RETAINED native frame's real pixel dims,
|
|
24017
24000
|
* so the caller supplies only the detection-res bbox divided by the detection
|
|
@@ -24198,7 +24181,17 @@ var DetailResultSchema = object({
|
|
|
24198
24181
|
/** Face short side (px) measured on the NATIVE crop surface. The `bbox`
|
|
24199
24182
|
* above is detection-frame px (≈6× smaller on a 4K camera) — min-face-size
|
|
24200
24183
|
* consumers MUST prefer this when present (2026-07-22 native-gate fix). */
|
|
24201
|
-
nativeFaceShortSidePx: number().optional()
|
|
24184
|
+
nativeFaceShortSidePx: number().optional(),
|
|
24185
|
+
/**
|
|
24186
|
+
* The five facial landmarks of a `face` detail, in the SAME frame space as
|
|
24187
|
+
* `bbox` (D644). Additive and optional: post-analysis stores them beside the
|
|
24188
|
+
* face's source frame so a future alignment can be re-derived exactly; a
|
|
24189
|
+
* runner that predates the field sends none and the face is stored without.
|
|
24190
|
+
*/
|
|
24191
|
+
landmarks: array(object({
|
|
24192
|
+
x: number(),
|
|
24193
|
+
y: number()
|
|
24194
|
+
})).optional()
|
|
24202
24195
|
});
|
|
24203
24196
|
/**
|
|
24204
24197
|
* Why an executing node REFUSED a stateless step run (`runStatelessStep`).
|
|
@@ -34375,7 +34368,7 @@ var HardwareInfoSchema = object({
|
|
|
34375
34368
|
coral: CoralInfoSchema.nullable().optional()
|
|
34376
34369
|
});
|
|
34377
34370
|
var PlatformScoreSchema = object({
|
|
34378
|
-
runtime:
|
|
34371
|
+
runtime: literal("python"),
|
|
34379
34372
|
backend: string(),
|
|
34380
34373
|
format: _enum([
|
|
34381
34374
|
"onnx",
|
|
@@ -34412,7 +34405,7 @@ var ModelRequirementSchema = object({
|
|
|
34412
34405
|
});
|
|
34413
34406
|
var ResolvedInferenceConfigSchema = object({
|
|
34414
34407
|
modelId: string(),
|
|
34415
|
-
runtime:
|
|
34408
|
+
runtime: literal("python"),
|
|
34416
34409
|
backend: string(),
|
|
34417
34410
|
format: ModelFormatSchema,
|
|
34418
34411
|
reason: string()
|
|
@@ -37007,6 +37000,92 @@ var sceneMonitorCapability = {
|
|
|
37007
37000
|
durability: "session"
|
|
37008
37001
|
};
|
|
37009
37002
|
/**
|
|
37003
|
+
* Per-stage gating mode applied to the zones a rule references.
|
|
37004
|
+
*
|
|
37005
|
+
* - `include`: the rule contributes to a **whitelist** for its stage.
|
|
37006
|
+
* When at least one `include` rule fires for a stage, only entities
|
|
37007
|
+
* inside one of those zones pass that stage.
|
|
37008
|
+
* - `exclude`: the rule contributes to a **blacklist** for its stage.
|
|
37009
|
+
* Entities inside one of those zones are dropped at that stage.
|
|
37010
|
+
*
|
|
37011
|
+
* `monitor`-style observation (count without filtering) is not a rule
|
|
37012
|
+
* mode — zones without any matching rule are observed naturally by
|
|
37013
|
+
* `zone-analytics` (live snapshot + history), so an "I just want to
|
|
37014
|
+
* count, not filter" use case needs no rule at all.
|
|
37015
|
+
*/
|
|
37016
|
+
var ZoneRuleModeEnum = _enum(["include", "exclude"]);
|
|
37017
|
+
/**
|
|
37018
|
+
* Per-consumer rule that references existing zones (geometry) and
|
|
37019
|
+
* defines how a specific pipeline stage should treat them. Each
|
|
37020
|
+
* consumer addon owns its own `ZoneRule[]` array in its per-device
|
|
37021
|
+
* settings:
|
|
37022
|
+
*
|
|
37023
|
+
* - `addon-motion-wasm` → `motionZoneRules: ZoneRule[]` (motion stage)
|
|
37024
|
+
* - `addon-detection-pipeline` → `detectionZoneRules: ZoneRule[]` (detection stage)
|
|
37025
|
+
* - future: notification rules, audio gating, etc.
|
|
37026
|
+
*
|
|
37027
|
+
* One rule applies to N zones (`zoneIds[]`) so the operator can
|
|
37028
|
+
* express "ignore motion in ALL of {garden, street}" with a single
|
|
37029
|
+
* rule. `classFilter` narrows the rule to specific object classes —
|
|
37030
|
+
* "drop person detections in the street, but keep cars" is one
|
|
37031
|
+
* `exclude` rule with `classFilter: ['person']`.
|
|
37032
|
+
*
|
|
37033
|
+
* `enabled` is a soft toggle — the operator can keep the rule
|
|
37034
|
+
* configured but inert without deleting it.
|
|
37035
|
+
*/
|
|
37036
|
+
var ZoneRuleSchema = object({
|
|
37037
|
+
/** Stable rule id — survives edits, used by the UI for diffing. */
|
|
37038
|
+
id: string(),
|
|
37039
|
+
/** Optional human-readable label rendered in the rule editor. */
|
|
37040
|
+
name: string().optional(),
|
|
37041
|
+
/** Zones this rule targets. The rule's `mode` applies to ALL
|
|
37042
|
+
* listed zones (OR-set: a detection in any one of them counts).
|
|
37043
|
+
* At least one zone id required — a rule with no targets is a
|
|
37044
|
+
* configuration mistake and the form validator rejects it. */
|
|
37045
|
+
zoneIds: array(string()).min(1).readonly(),
|
|
37046
|
+
mode: ZoneRuleModeEnum,
|
|
37047
|
+
/**
|
|
37048
|
+
* Class names this rule applies to. Empty / undefined ⇒ rule
|
|
37049
|
+
* applies to every class. Class strings match the `macroClass`
|
|
37050
|
+
* field on detections (e.g. `person`, `car`, `dog`).
|
|
37051
|
+
*/
|
|
37052
|
+
classFilter: array(string()).readonly().optional(),
|
|
37053
|
+
/**
|
|
37054
|
+
* Minimum bbox/mask overlap (0–1) with any of the rule's zones
|
|
37055
|
+
* required to consider an entity "in the zone". Defaults to the
|
|
37056
|
+
* consumer's stage default when omitted. Kept for back-compat with
|
|
37057
|
+
* existing per-rule overrides; new operators pick the value via
|
|
37058
|
+
* `bboxInclusionPct` (operator-friendly 0–100). Whichever field is
|
|
37059
|
+
* set, the lower-level engine reads it as a 0–1 fraction.
|
|
37060
|
+
*/
|
|
37061
|
+
overlapThreshold: number().min(0).max(1).optional(),
|
|
37062
|
+
/**
|
|
37063
|
+
* Operator-friendly version of `overlapThreshold` — the percentage
|
|
37064
|
+
* of the detection's bbox that must lie inside the zone for the
|
|
37065
|
+
* rule to match. Documented default is 85%; the engine substitutes
|
|
37066
|
+
* that when the field is omitted (kept optional so existing rules
|
|
37067
|
+
* stored without it stay valid).
|
|
37068
|
+
*
|
|
37069
|
+
* When BOTH `overlapThreshold` and `bboxInclusionPct` are set on a
|
|
37070
|
+
* rule, the engine prefers `bboxInclusionPct` because it's the
|
|
37071
|
+
* field exposed in the UI. Internally both feed the same gate.
|
|
37072
|
+
*/
|
|
37073
|
+
bboxInclusionPct: number().min(0).max(100).optional(),
|
|
37074
|
+
/**
|
|
37075
|
+
* When `true` and a detection has a segmentation mask, use the
|
|
37076
|
+
* mask for overlap instead of the bbox. Detection-stage only;
|
|
37077
|
+
* motion rules ignore this field.
|
|
37078
|
+
*/
|
|
37079
|
+
preferMask: boolean().optional(),
|
|
37080
|
+
/**
|
|
37081
|
+
* Soft-toggle: `false` disables the rule without deleting it.
|
|
37082
|
+
* Defaults to `true` so operators creating a rule via the UI
|
|
37083
|
+
* see it active immediately.
|
|
37084
|
+
*/
|
|
37085
|
+
enabled: boolean().default(true)
|
|
37086
|
+
});
|
|
37087
|
+
array(ZoneRuleSchema).readonly();
|
|
37088
|
+
/**
|
|
37010
37089
|
* Script-runner cap. Models HA `script.*` entities on
|
|
37011
37090
|
* `DeviceType.Script`. A Script is a pre-recorded action sequence
|
|
37012
37091
|
* that can be invoked imperatively — optionally with a variables
|
|
@@ -45178,6 +45257,12 @@ Object.freeze({
|
|
|
45178
45257
|
addonId: null,
|
|
45179
45258
|
access: "view"
|
|
45180
45259
|
},
|
|
45260
|
+
"notificationRules.previewStateSummary": {
|
|
45261
|
+
capName: "notification-rules",
|
|
45262
|
+
capScope: "system",
|
|
45263
|
+
addonId: null,
|
|
45264
|
+
access: "view"
|
|
45265
|
+
},
|
|
45181
45266
|
"notificationRules.previewTemplate": {
|
|
45182
45267
|
capName: "notification-rules",
|
|
45183
45268
|
capScope: "system",
|
|
@@ -45754,12 +45839,6 @@ Object.freeze({
|
|
|
45754
45839
|
addonId: null,
|
|
45755
45840
|
access: "create"
|
|
45756
45841
|
},
|
|
45757
|
-
"pipelineAnalytics.runReplayFrameProcessor": {
|
|
45758
|
-
capName: "pipeline-analytics",
|
|
45759
|
-
capScope: "device",
|
|
45760
|
-
addonId: null,
|
|
45761
|
-
access: "create"
|
|
45762
|
-
},
|
|
45763
45842
|
"pipelineAnalytics.saveRetrainAnnotations": {
|
|
45764
45843
|
capName: "pipeline-analytics",
|
|
45765
45844
|
capScope: "device",
|
|
@@ -49857,11 +49936,6 @@ Object.freeze({
|
|
|
49857
49936
|
form: "single",
|
|
49858
49937
|
optional: false
|
|
49859
49938
|
}],
|
|
49860
|
-
"pipelineAnalytics.runReplayFrameProcessor": [{
|
|
49861
|
-
name: "deviceId",
|
|
49862
|
-
form: "single",
|
|
49863
|
-
optional: false
|
|
49864
|
-
}],
|
|
49865
49939
|
"pipelineAnalytics.saveRetrainAnnotations": [{
|
|
49866
49940
|
name: "deviceId",
|
|
49867
49941
|
form: "single",
|
|
@@ -50899,7 +50973,8 @@ var NC_SYSTEM_EVENT_FAMILY = {
|
|
|
50899
50973
|
"camera-disabled": null,
|
|
50900
50974
|
"camera-enabled": null
|
|
50901
50975
|
};
|
|
50902
|
-
|
|
50976
|
+
/** Read by NAME, for kinds that arrive as plain strings (a newer hub, the viewer's mirror). */
|
|
50977
|
+
var FAMILY_BY_NAME = new Map(Object.entries(NC_SYSTEM_EVENT_FAMILY));
|
|
50903
50978
|
var NC_SYSTEM_EVENT_CONDITION_FILTER_KEYS = [...NC_SYSTEM_EVENT_FILTER_KEYS, "excludeDeviceIds"];
|
|
50904
50979
|
/**
|
|
50905
50980
|
* The filters a family may carry. The base filters are the union of
|
|
@@ -51010,6 +51085,24 @@ function systemEventKindsOfFamily(family) {
|
|
|
51010
51085
|
return NC_SYSTEM_EVENT_KIND_GROUP_SPECS.filter((g) => g.family === family).flatMap((g) => g.kinds);
|
|
51011
51086
|
}
|
|
51012
51087
|
/**
|
|
51088
|
+
* One family, or `mixed`. Mixed covers kinds that span families, a legacy kind,
|
|
51089
|
+
* a kind this build does not know (it fails toward read-only and never toward
|
|
51090
|
+
* editable), and an empty selection (nothing to decide on).
|
|
51091
|
+
*/
|
|
51092
|
+
function systemEventFamilyOf(kindNames) {
|
|
51093
|
+
const families = /* @__PURE__ */ new Set();
|
|
51094
|
+
for (const name of kindNames) {
|
|
51095
|
+
const family = FAMILY_BY_NAME.get(name);
|
|
51096
|
+
if (family === void 0 || family === null) return { kind: "mixed" };
|
|
51097
|
+
families.add(family);
|
|
51098
|
+
}
|
|
51099
|
+
const [only] = [...families];
|
|
51100
|
+
return families.size === 1 && only !== void 0 ? {
|
|
51101
|
+
kind: "family",
|
|
51102
|
+
family: only
|
|
51103
|
+
} : { kind: "mixed" };
|
|
51104
|
+
}
|
|
51105
|
+
/**
|
|
51013
51106
|
* Why the hub refuses this condition on a new or updated rule. Empty = accepted.
|
|
51014
51107
|
* Families are reported in `NC_SYSTEM_EVENT_FAMILIES` order, and filters in
|
|
51015
51108
|
* `NC_SYSTEM_EVENT_CONDITION_FILTER_KEYS` order, so a refusal reads the same
|
|
@@ -51085,18 +51178,38 @@ var WEEKDAY_TO_DAY = {
|
|
|
51085
51178
|
Fri: 5,
|
|
51086
51179
|
Sat: 6
|
|
51087
51180
|
};
|
|
51088
|
-
/**
|
|
51089
|
-
*
|
|
51090
|
-
function
|
|
51091
|
-
|
|
51092
|
-
|
|
51093
|
-
|
|
51181
|
+
/**
|
|
51182
|
+
* One formatter per timezone, built once (D647 review I4). Constructing an
|
|
51183
|
+
* `Intl.DateTimeFormat` is the expensive part of this function, and a caller
|
|
51184
|
+
* that asks about many instants — a window scan — used to build one per
|
|
51185
|
+
* instant. `null` = an invalid IANA name (the host timezone is used).
|
|
51186
|
+
*/
|
|
51187
|
+
var FORMATTERS = /* @__PURE__ */ new Map();
|
|
51188
|
+
function formatterFor(timezone) {
|
|
51189
|
+
const held = FORMATTERS.get(timezone);
|
|
51190
|
+
if (held !== void 0) return held;
|
|
51191
|
+
let made;
|
|
51192
|
+
try {
|
|
51193
|
+
made = new Intl.DateTimeFormat("en-US", {
|
|
51094
51194
|
timeZone: timezone,
|
|
51095
51195
|
weekday: "short",
|
|
51096
51196
|
hour: "numeric",
|
|
51097
51197
|
minute: "numeric",
|
|
51098
51198
|
hourCycle: "h23"
|
|
51099
|
-
})
|
|
51199
|
+
});
|
|
51200
|
+
} catch {
|
|
51201
|
+
made = null;
|
|
51202
|
+
}
|
|
51203
|
+
FORMATTERS.set(timezone, made);
|
|
51204
|
+
return made;
|
|
51205
|
+
}
|
|
51206
|
+
/** Resolve (weekday, minute-of-day) of `atMs` in the schedule's timezone.
|
|
51207
|
+
* An invalid/unknown IANA name falls back to the host timezone. */
|
|
51208
|
+
function localDayMinute(atMs, timezone) {
|
|
51209
|
+
const d = new Date(atMs);
|
|
51210
|
+
const formatter = timezone !== void 0 ? formatterFor(timezone) : null;
|
|
51211
|
+
if (formatter !== null) try {
|
|
51212
|
+
const parts = formatter.formatToParts(d);
|
|
51100
51213
|
let weekday;
|
|
51101
51214
|
let hour;
|
|
51102
51215
|
let minute;
|
|
@@ -51236,6 +51349,119 @@ var DEVICE_EVENTS = [
|
|
|
51236
51349
|
"detection-blind",
|
|
51237
51350
|
...ALARM
|
|
51238
51351
|
];
|
|
51352
|
+
/** Every kind a summary may speak about — the two summarisable families. */
|
|
51353
|
+
var SUMMARISABLE = [...systemEventKindsOfFamily("device-events"), ...systemEventKindsOfFamily("system")];
|
|
51354
|
+
/** One per summary SECTION (`NC_STATE_SUMMARY_SECTIONS` in the notification
|
|
51355
|
+
* centre): the section's lines, offered when one of its kinds is selected. */
|
|
51356
|
+
var STATE_SUMMARY_SECTION_VARS = [
|
|
51357
|
+
{
|
|
51358
|
+
name: "summaryDevices",
|
|
51359
|
+
label: "Summary: devices offline / switched off",
|
|
51360
|
+
example: "Garage — offline since 07:12",
|
|
51361
|
+
kinds: [
|
|
51362
|
+
"device-offline",
|
|
51363
|
+
"device-online",
|
|
51364
|
+
"device-disabled",
|
|
51365
|
+
"device-enabled"
|
|
51366
|
+
]
|
|
51367
|
+
},
|
|
51368
|
+
{
|
|
51369
|
+
name: "summaryStreams",
|
|
51370
|
+
label: "Summary: streams offline",
|
|
51371
|
+
example: "Ingresso (high) — since 06:40",
|
|
51372
|
+
kinds: ["stream-offline", "stream-online"]
|
|
51373
|
+
},
|
|
51374
|
+
{
|
|
51375
|
+
name: "summaryBattery",
|
|
51376
|
+
label: "Summary: low batteries",
|
|
51377
|
+
example: "Sensore porta — 8 %",
|
|
51378
|
+
kinds: BATTERY
|
|
51379
|
+
},
|
|
51380
|
+
{
|
|
51381
|
+
name: "summaryConsumables",
|
|
51382
|
+
label: "Summary: consumables",
|
|
51383
|
+
example: "Robot — filter 10 %",
|
|
51384
|
+
kinds: CONSUMABLE
|
|
51385
|
+
},
|
|
51386
|
+
{
|
|
51387
|
+
name: "summaryTanks",
|
|
51388
|
+
label: "Summary: tanks",
|
|
51389
|
+
example: "Robot — clean water low",
|
|
51390
|
+
kinds: TANK
|
|
51391
|
+
},
|
|
51392
|
+
{
|
|
51393
|
+
name: "summaryDetection",
|
|
51394
|
+
label: "Summary: cameras detecting nothing",
|
|
51395
|
+
example: "Ingresso — since 05:10",
|
|
51396
|
+
kinds: ["detection-blind"]
|
|
51397
|
+
},
|
|
51398
|
+
{
|
|
51399
|
+
name: "summaryNodes",
|
|
51400
|
+
label: "Summary: nodes",
|
|
51401
|
+
example: "little-unraid — offline since 03:00",
|
|
51402
|
+
kinds: [
|
|
51403
|
+
"node-offline",
|
|
51404
|
+
"node-online",
|
|
51405
|
+
"node-inference-unavailable"
|
|
51406
|
+
]
|
|
51407
|
+
},
|
|
51408
|
+
{
|
|
51409
|
+
name: "summaryAddons",
|
|
51410
|
+
label: "Summary: addons",
|
|
51411
|
+
example: "@camstack/addon-recorder 1.2.3 → 1.2.4",
|
|
51412
|
+
kinds: [
|
|
51413
|
+
"addon-crash-loop",
|
|
51414
|
+
"addon-update-available",
|
|
51415
|
+
"addon-updated"
|
|
51416
|
+
]
|
|
51417
|
+
},
|
|
51418
|
+
{
|
|
51419
|
+
name: "summaryUpdates",
|
|
51420
|
+
label: "Summary: server & container updates",
|
|
51421
|
+
example: "server 1.2.377 → 1.2.378",
|
|
51422
|
+
kinds: [
|
|
51423
|
+
"server-update-available",
|
|
51424
|
+
"server-updated",
|
|
51425
|
+
"wrapper-update-available"
|
|
51426
|
+
]
|
|
51427
|
+
}
|
|
51428
|
+
];
|
|
51429
|
+
var STATE_SUMMARY_VARS = [
|
|
51430
|
+
{
|
|
51431
|
+
name: "summaryText",
|
|
51432
|
+
label: "Summary: every section",
|
|
51433
|
+
description: "One block per section with something to report, one line per item.",
|
|
51434
|
+
example: "Batterie scariche: Sensore porta — 8 %",
|
|
51435
|
+
group: "system",
|
|
51436
|
+
families: ["rule"],
|
|
51437
|
+
fields: [...RULE_TEXT],
|
|
51438
|
+
kinds: ["system"],
|
|
51439
|
+
systemEventKinds: [...SUMMARISABLE],
|
|
51440
|
+
systemEventDeliveries: ["summary"]
|
|
51441
|
+
},
|
|
51442
|
+
{
|
|
51443
|
+
name: "summaryCount",
|
|
51444
|
+
label: "Summary: items reported",
|
|
51445
|
+
example: "3",
|
|
51446
|
+
group: "system",
|
|
51447
|
+
families: ["rule"],
|
|
51448
|
+
fields: [...RULE_TEXT],
|
|
51449
|
+
kinds: ["system"],
|
|
51450
|
+
systemEventKinds: [...SUMMARISABLE],
|
|
51451
|
+
systemEventDeliveries: ["summary"]
|
|
51452
|
+
},
|
|
51453
|
+
...STATE_SUMMARY_SECTION_VARS.map((v) => ({
|
|
51454
|
+
name: v.name,
|
|
51455
|
+
label: v.label,
|
|
51456
|
+
example: v.example,
|
|
51457
|
+
group: "system",
|
|
51458
|
+
families: ["rule"],
|
|
51459
|
+
fields: [...RULE_TEXT],
|
|
51460
|
+
kinds: ["system"],
|
|
51461
|
+
systemEventKinds: [...v.kinds],
|
|
51462
|
+
systemEventDeliveries: ["summary"]
|
|
51463
|
+
}))
|
|
51464
|
+
];
|
|
51239
51465
|
var NC_TEMPLATE_VARS = [
|
|
51240
51466
|
{
|
|
51241
51467
|
name: "camera",
|
|
@@ -51486,7 +51712,8 @@ var NC_TEMPLATE_VARS = [
|
|
|
51486
51712
|
group: "system",
|
|
51487
51713
|
families: ["rule"],
|
|
51488
51714
|
fields: [...RULE_TEXT],
|
|
51489
|
-
kinds: ["system"]
|
|
51715
|
+
kinds: ["system"],
|
|
51716
|
+
systemEventDeliveries: ["instant"]
|
|
51490
51717
|
},
|
|
51491
51718
|
{
|
|
51492
51719
|
name: "deviceName",
|
|
@@ -51929,7 +52156,8 @@ var NC_TEMPLATE_VARS = [
|
|
|
51929
52156
|
group: "digest",
|
|
51930
52157
|
families: ["summary"],
|
|
51931
52158
|
fields: [...RULE_TEXT]
|
|
51932
|
-
}
|
|
52159
|
+
},
|
|
52160
|
+
...STATE_SUMMARY_VARS
|
|
51933
52161
|
];
|
|
51934
52162
|
/**
|
|
51935
52163
|
* System-event kinds the SERVED catalog holds back (D642): the kinds the hub
|
|
@@ -51958,11 +52186,32 @@ var NC_TEMPLATE_HELD_BACK_SYSTEM_EVENT_KINDS = [
|
|
|
51958
52186
|
* rule an installed viewer can author, which is exactly true.
|
|
51959
52187
|
*/
|
|
51960
52188
|
function servedTemplateVars() {
|
|
51961
|
-
return NC_TEMPLATE_VARS.map((d) => d.systemEventKinds === void 0 ? d : {
|
|
52189
|
+
return NC_TEMPLATE_VARS.filter((d) => !isHeldBackTemplateVar(d)).map((d) => d.systemEventKinds === void 0 ? d : {
|
|
51962
52190
|
...d,
|
|
51963
52191
|
systemEventKinds: d.systemEventKinds.filter((kind) => !NC_TEMPLATE_HELD_BACK_SYSTEM_EVENT_KINDS.includes(kind))
|
|
51964
52192
|
});
|
|
51965
52193
|
}
|
|
52194
|
+
/**
|
|
52195
|
+
* Is this descriptor held back from the SERVED catalog as a whole (D647)?
|
|
52196
|
+
* A summary-only variable is: an installed viewer does not read the delivery
|
|
52197
|
+
* axis, and would offer it on every instant system rule. The admin, built
|
|
52198
|
+
* from the workspace, restores it from its own `NC_TEMPLATE_VARS`.
|
|
52199
|
+
*/
|
|
52200
|
+
function isHeldBackTemplateVar(d) {
|
|
52201
|
+
return d.systemEventDeliveries !== void 0 && !d.systemEventDeliveries.includes("instant");
|
|
52202
|
+
}
|
|
52203
|
+
/**
|
|
52204
|
+
* Does a descriptor have a value on a system rule of this DELIVERY (D647)?
|
|
52205
|
+
* Read only for a system rule kind. An absent axis is `instant` for a
|
|
52206
|
+
* descriptor that names `systemEventKinds` (a per-event fact: a summary row
|
|
52207
|
+
* has no one device, node or package) and BOTH for one that does not
|
|
52208
|
+
* (`{{rule}}`, `{{time}}`).
|
|
52209
|
+
*/
|
|
52210
|
+
function templateVarDeliveryApplies(d, ctx) {
|
|
52211
|
+
if (!isSystemRuleKind(ctx.kind)) return true;
|
|
52212
|
+
const mode = ctx.systemEventDelivery ?? "instant";
|
|
52213
|
+
return (d.systemEventDeliveries ?? (d.systemEventKinds !== void 0 ? ["instant"] : [mode])).includes(mode);
|
|
52214
|
+
}
|
|
51966
52215
|
/** The caption fields see a fixed, smaller vocabulary (`timelapsePreviewText`,
|
|
51967
52216
|
* `summaryCaptionText`). Declared here rather than as `fields` on every
|
|
51968
52217
|
* descriptor so the whole rule stays in one line of code. */
|
|
@@ -52027,12 +52276,82 @@ function templateVarsFor(ctx) {
|
|
|
52027
52276
|
const allowed = CAPTION_VARS[ctx.field];
|
|
52028
52277
|
return NC_TEMPLATE_VARS.filter((d) => d.families.includes(ctx.family) && allowed.includes(d.name) && d.dynamic === void 0);
|
|
52029
52278
|
}
|
|
52030
|
-
return NC_TEMPLATE_VARS.filter((d) => d.families.includes(ctx.family) && onAxis(d.fields, ctx.field) && templateVarKindApplies(d.kinds, ctx.kind) && onAxis(d.deliveries, ctx.delivery) && templateVarSystemEventApplies(d, ctx));
|
|
52279
|
+
return NC_TEMPLATE_VARS.filter((d) => d.families.includes(ctx.family) && onAxis(d.fields, ctx.field) && templateVarKindApplies(d.kinds, ctx.kind) && onAxis(d.deliveries, ctx.delivery) && templateVarSystemEventApplies(d, ctx) && templateVarDeliveryApplies(d, ctx));
|
|
52031
52280
|
}
|
|
52032
52281
|
/** True when `name` is supplied in `ctx`, dynamic families (`count_<class>`) included. */
|
|
52033
52282
|
function isKnownTemplateVar(name, ctx) {
|
|
52034
52283
|
return templateVarsFor(ctx).some((d) => d.dynamic !== void 0 ? name.startsWith(d.dynamic.prefix) && name.length > d.dynamic.prefix.length : d.name === name);
|
|
52035
52284
|
}
|
|
52285
|
+
/** The families a rule may summarise. The alarm is not one of them. */
|
|
52286
|
+
var NC_SYSTEM_EVENT_SUMMARY_FAMILIES = ["device-events", "system"];
|
|
52287
|
+
/** The summary's schedule, or `null` for any rule that is not a summary system rule. */
|
|
52288
|
+
function systemEventSummaryScheduleOf(rule) {
|
|
52289
|
+
if (!isSystemDelivery(rule.delivery)) return null;
|
|
52290
|
+
const delivery = rule.systemEventDelivery;
|
|
52291
|
+
return delivery?.mode === "summary" ? delivery.schedule : null;
|
|
52292
|
+
}
|
|
52293
|
+
/** Absent and `instant` are the same answer: a notification per event. */
|
|
52294
|
+
function isSystemEventSummaryRule(rule) {
|
|
52295
|
+
return systemEventSummaryScheduleOf(rule) !== null;
|
|
52296
|
+
}
|
|
52297
|
+
function actionsAreSet(actions) {
|
|
52298
|
+
return (actions?.onTrigger?.length ?? 0) > 0 || (actions?.buttons?.length ?? 0) > 0;
|
|
52299
|
+
}
|
|
52300
|
+
/**
|
|
52301
|
+
* Why the hub refuses this rule's delivery. Empty = accepted. An instant rule
|
|
52302
|
+
* (absent or `{ mode: 'instant' }`) is never refused here.
|
|
52303
|
+
*
|
|
52304
|
+
* Beside a summary, two fields are refused because they would do nothing:
|
|
52305
|
+
* the rule's own `schedule` (the summary's schedule is the send time; a second
|
|
52306
|
+
* active-hours gate would silently empty it) and `actions` (a summary is not a
|
|
52307
|
+
* match, so nothing would ever run them).
|
|
52308
|
+
*/
|
|
52309
|
+
function systemEventDeliveryProblems(rule) {
|
|
52310
|
+
if (rule.systemEventDelivery?.mode !== "summary") return [];
|
|
52311
|
+
if (!isSystemDelivery(rule.delivery)) return [{
|
|
52312
|
+
code: "not-a-system-rule",
|
|
52313
|
+
delivery: rule.delivery
|
|
52314
|
+
}];
|
|
52315
|
+
const problems = [];
|
|
52316
|
+
const verdict = systemEventFamilyOf(rule.conditions.systemEvent?.kinds ?? []);
|
|
52317
|
+
const family = verdict.kind === "family" ? verdict.family : "mixed";
|
|
52318
|
+
if (family === "mixed" || !NC_SYSTEM_EVENT_SUMMARY_FAMILIES.includes(family)) problems.push({
|
|
52319
|
+
code: "family-not-summarisable",
|
|
52320
|
+
family
|
|
52321
|
+
});
|
|
52322
|
+
const windows = rule.systemEventDelivery.schedule.windows;
|
|
52323
|
+
if (windows.length === 0) problems.push({ code: "empty-schedule" });
|
|
52324
|
+
windows.forEach((window, index) => {
|
|
52325
|
+
if (window.days.length === 0 || window.startMinute === window.endMinute) problems.push({
|
|
52326
|
+
code: "degenerate-window",
|
|
52327
|
+
index
|
|
52328
|
+
});
|
|
52329
|
+
});
|
|
52330
|
+
if (rule.schedule !== void 0) problems.push({ code: "schedule-beside-summary" });
|
|
52331
|
+
if (actionsAreSet(rule.actions)) problems.push({ code: "actions-beside-summary" });
|
|
52332
|
+
return problems;
|
|
52333
|
+
}
|
|
52334
|
+
function describeSystemEventDeliveryProblem(problem) {
|
|
52335
|
+
switch (problem.code) {
|
|
52336
|
+
case "not-a-system-rule": return `a '${problem.delivery}' rule has no summary delivery — only device-events and system rules do`;
|
|
52337
|
+
case "family-not-summarisable": return problem.family === "alarm" ? "an alarm rule cannot be a scheduled summary — the alarm stays instant" : `a ${problem.family} rule cannot be a scheduled summary — only device-events and system rules`;
|
|
52338
|
+
case "degenerate-window": return `send time ${String(problem.index + 1)} has no day, or starts and ends on the same minute — it never closes, so nothing would be sent`;
|
|
52339
|
+
case "empty-schedule": return "a summary rule needs at least one send time — its schedule has no window";
|
|
52340
|
+
case "schedule-beside-summary": return "a summary rule is sent on its summary schedule — remove the active-hours schedule";
|
|
52341
|
+
case "actions-beside-summary": return "a summary rule never matches an event, so its actions would never run — remove them";
|
|
52342
|
+
}
|
|
52343
|
+
}
|
|
52344
|
+
var SUMMARY_CONTENT_VAR = /\{\{\s*summary(?:Text|Devices|Streams|Battery|Consumables|Tanks|Detection|Nodes|Addons|Updates)\s*\}\}/;
|
|
52345
|
+
/**
|
|
52346
|
+
* Does a custom BODY carry the summary's content (`{{summaryText}}` or a
|
|
52347
|
+
* section variable)? A body that does not would send a summary with nothing
|
|
52348
|
+
* in it; the hub then appends `{{summaryText}}` and the admin warns (review
|
|
52349
|
+
* M3). An absent or blank body uses the hub's own wording, which does.
|
|
52350
|
+
*/
|
|
52351
|
+
function templateCarriesSummary(body) {
|
|
52352
|
+
if (body === void 0 || body.trim().length === 0) return true;
|
|
52353
|
+
return SUMMARY_CONTENT_VAR.test(body);
|
|
52354
|
+
}
|
|
52036
52355
|
/**
|
|
52037
52356
|
* TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
|
|
52038
52357
|
*
|
|
@@ -53253,6 +53572,12 @@ Object.defineProperty(exports, "NC_SNOOZE_MAX_MINUTES", {
|
|
|
53253
53572
|
return NC_SNOOZE_MAX_MINUTES;
|
|
53254
53573
|
}
|
|
53255
53574
|
});
|
|
53575
|
+
Object.defineProperty(exports, "NC_SYSTEM_EVENT_KIND_GROUP_SPECS", {
|
|
53576
|
+
enumerable: true,
|
|
53577
|
+
get: function() {
|
|
53578
|
+
return NC_SYSTEM_EVENT_KIND_GROUP_SPECS;
|
|
53579
|
+
}
|
|
53580
|
+
});
|
|
53256
53581
|
Object.defineProperty(exports, "NC_TANK_SYSTEM_EVENT_KINDS", {
|
|
53257
53582
|
enumerable: true,
|
|
53258
53583
|
get: function() {
|
|
@@ -53577,6 +53902,12 @@ Object.defineProperty(exports, "deriveRecordingMode", {
|
|
|
53577
53902
|
return deriveRecordingMode;
|
|
53578
53903
|
}
|
|
53579
53904
|
});
|
|
53905
|
+
Object.defineProperty(exports, "describeSystemEventDeliveryProblem", {
|
|
53906
|
+
enumerable: true,
|
|
53907
|
+
get: function() {
|
|
53908
|
+
return describeSystemEventDeliveryProblem;
|
|
53909
|
+
}
|
|
53910
|
+
});
|
|
53580
53911
|
Object.defineProperty(exports, "describeSystemEventFamilyProblem", {
|
|
53581
53912
|
enumerable: true,
|
|
53582
53913
|
get: function() {
|
|
@@ -53691,6 +54022,12 @@ Object.defineProperty(exports, "isSystemDelivery", {
|
|
|
53691
54022
|
return isSystemDelivery;
|
|
53692
54023
|
}
|
|
53693
54024
|
});
|
|
54025
|
+
Object.defineProperty(exports, "isSystemEventSummaryRule", {
|
|
54026
|
+
enumerable: true,
|
|
54027
|
+
get: function() {
|
|
54028
|
+
return isSystemEventSummaryRule;
|
|
54029
|
+
}
|
|
54030
|
+
});
|
|
53694
54031
|
Object.defineProperty(exports, "kebabToCamel", {
|
|
53695
54032
|
enumerable: true,
|
|
53696
54033
|
get: function() {
|
|
@@ -53847,6 +54184,12 @@ Object.defineProperty(exports, "subKindsOf", {
|
|
|
53847
54184
|
return subKindsOf;
|
|
53848
54185
|
}
|
|
53849
54186
|
});
|
|
54187
|
+
Object.defineProperty(exports, "systemEventDeliveryProblems", {
|
|
54188
|
+
enumerable: true,
|
|
54189
|
+
get: function() {
|
|
54190
|
+
return systemEventDeliveryProblems;
|
|
54191
|
+
}
|
|
54192
|
+
});
|
|
53850
54193
|
Object.defineProperty(exports, "systemEventFamilyProblems", {
|
|
53851
54194
|
enumerable: true,
|
|
53852
54195
|
get: function() {
|
|
@@ -53859,6 +54202,18 @@ Object.defineProperty(exports, "systemEventFilterApplies", {
|
|
|
53859
54202
|
return systemEventFilterApplies;
|
|
53860
54203
|
}
|
|
53861
54204
|
});
|
|
54205
|
+
Object.defineProperty(exports, "systemEventSummaryScheduleOf", {
|
|
54206
|
+
enumerable: true,
|
|
54207
|
+
get: function() {
|
|
54208
|
+
return systemEventSummaryScheduleOf;
|
|
54209
|
+
}
|
|
54210
|
+
});
|
|
54211
|
+
Object.defineProperty(exports, "templateCarriesSummary", {
|
|
54212
|
+
enumerable: true,
|
|
54213
|
+
get: function() {
|
|
54214
|
+
return templateCarriesSummary;
|
|
54215
|
+
}
|
|
54216
|
+
});
|
|
53862
54217
|
Object.defineProperty(exports, "templateVarsFor", {
|
|
53863
54218
|
enumerable: true,
|
|
53864
54219
|
get: function() {
|